Coverage for cuda / pathfinder / _dynamic_libs / search_platform.py: 81.73%
104 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-25 01:07 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-25 01:07 +0000
1# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2# SPDX-License-Identifier: Apache-2.0
4"""Platform abstraction for filesystem search steps.
6The goal is to keep :mod:`search_steps` platform-agnostic: it should not branch
7on OS flags like ``IS_WINDOWS``. Instead, it calls through the single
8``PLATFORM`` instance exported here.
9"""
11from __future__ import annotations
13import glob
14import os
15from collections.abc import Sequence
16from dataclasses import dataclass
17from typing import Protocol, cast
19from cuda.pathfinder._dynamic_libs.lib_descriptor import LibDescriptor
20from cuda.pathfinder._dynamic_libs.supported_nvidia_libs import is_suppressed_dll_file
21from cuda.pathfinder._utils.find_sub_dirs import find_sub_dirs_all_sitepackages
22from cuda.pathfinder._utils.platform_aware import IS_WINDOWS
25def _no_such_file_in_sub_dirs(
26 sub_dirs: Sequence[str], file_wild: str, error_messages: list[str], attachments: list[str]
27) -> None:
28 error_messages.append(f"No such file: {file_wild}") 1abecd
29 for sub_dir in find_sub_dirs_all_sitepackages(sub_dirs): 1abecd
30 attachments.append(f' listdir("{sub_dir}"):') 1d
31 for node in sorted(os.listdir(sub_dir)): 1d
32 attachments.append(f" {node}")
35def _find_so_in_rel_dirs(
36 rel_dirs: tuple[str, ...],
37 so_basename: str,
38 error_messages: list[str],
39 attachments: list[str],
40) -> str | None:
41 sub_dirs_searched: list[tuple[str, ...]] = [] 1abeld
42 file_wild = so_basename + "*" 1abeld
43 for rel_dir in rel_dirs: 1abeld
44 sub_dir = tuple(rel_dir.split(os.path.sep)) 1abeld
45 for abs_dir in find_sub_dirs_all_sitepackages(sub_dir): 1abeld
46 so_name = os.path.join(abs_dir, so_basename) 1ld
47 if os.path.isfile(so_name): 1ld
48 return so_name 1l
49 for so_name in sorted(glob.glob(os.path.join(abs_dir, file_wild))): 1d
50 if os.path.isfile(so_name):
51 return so_name
52 sub_dirs_searched.append(sub_dir) 1abed
53 for sub_dir in sub_dirs_searched: 1abed
54 _no_such_file_in_sub_dirs(sub_dir, file_wild, error_messages, attachments) 1abed
55 return None 1abed
58def _find_dll_under_dir(dirpath: str, file_wild: str) -> str | None:
59 for path in sorted(glob.glob(os.path.join(dirpath, file_wild))): 1afghijcmnk
60 if not os.path.isfile(path): 1afghijcmnk
61 continue
62 if not is_suppressed_dll_file(os.path.basename(path)): 1afghijcmnk
63 return path 1afghijcmnk
64 return None
67def _find_dll_in_rel_dirs(
68 rel_dirs: tuple[str, ...],
69 lib_searched_for: str,
70 error_messages: list[str],
71 attachments: list[str],
72) -> str | None:
73 sub_dirs_searched: list[tuple[str, ...]] = [] 1abck
74 for rel_dir in rel_dirs: 1abck
75 sub_dir = tuple(rel_dir.split(os.path.sep)) 1abck
76 for abs_dir in find_sub_dirs_all_sitepackages(sub_dir): 1abck
77 dll_name = _find_dll_under_dir(abs_dir, lib_searched_for) 1k
78 if dll_name is not None: 1k
79 return dll_name 1k
80 sub_dirs_searched.append(sub_dir) 1abc
81 for sub_dir in sub_dirs_searched: 1abc
82 _no_such_file_in_sub_dirs(sub_dir, lib_searched_for, error_messages, attachments) 1abc
83 return None 1abc
86class SearchPlatform(Protocol):
87 def lib_searched_for(self, libname: str) -> str: ...
89 def site_packages_rel_dirs(self, desc: LibDescriptor) -> tuple[str, ...]: ...
91 def conda_anchor_point(self, conda_prefix: str) -> str: ...
93 def anchor_rel_dirs(self, desc: LibDescriptor) -> tuple[str, ...]: ...
95 def find_in_site_packages(
96 self,
97 rel_dirs: tuple[str, ...],
98 lib_searched_for: str,
99 error_messages: list[str],
100 attachments: list[str],
101 ) -> str | None: ...
103 def find_in_lib_dir(
104 self,
105 lib_dir: str,
106 libname: str,
107 lib_searched_for: str,
108 error_messages: list[str],
109 attachments: list[str],
110 ) -> str | None: ...
113@dataclass(frozen=True, slots=True)
114class LinuxSearchPlatform:
115 def lib_searched_for(self, libname: str) -> str:
116 return f"lib{libname}.so" 1afghtijbowperqsldxyz
118 def site_packages_rel_dirs(self, desc: LibDescriptor) -> tuple[str, ...]:
119 return cast(tuple[str, ...], desc.site_packages_linux) 1abeldA
121 def conda_anchor_point(self, conda_prefix: str) -> str:
122 return conda_prefix 1opq
124 def anchor_rel_dirs(self, desc: LibDescriptor) -> tuple[str, ...]:
125 return cast(tuple[str, ...], desc.anchor_rel_dirs_linux) 1afguhijvopeBCrqs
127 def find_in_site_packages(
128 self,
129 rel_dirs: tuple[str, ...],
130 lib_searched_for: str,
131 error_messages: list[str],
132 attachments: list[str],
133 ) -> str | None:
134 return _find_so_in_rel_dirs(rel_dirs, lib_searched_for, error_messages, attachments) 1abeld
136 def find_in_lib_dir(
137 self,
138 lib_dir: str,
139 _libname: str,
140 lib_searched_for: str,
141 error_messages: list[str],
142 attachments: list[str],
143 ) -> str | None:
144 # Most libraries have both unversioned and versioned files/symlinks (exact match first)
145 so_name = os.path.join(lib_dir, lib_searched_for) 1afghijoperqs
146 if os.path.isfile(so_name): 1afghijoperqs
147 return so_name 1afghijerqs
148 # Some libraries only exist as versioned files (e.g., libcupti.so.13 in conda),
149 # so the glob fallback is needed
150 file_wild = lib_searched_for + "*" 1op
151 # Only one match is expected, but to ensure deterministic behavior in unexpected
152 # situations, and to be internally consistent, we sort in reverse order with the
153 # intent to return the newest version first.
154 for so_name in sorted(glob.glob(os.path.join(lib_dir, file_wild)), reverse=True): 1op
155 if os.path.isfile(so_name): 1op
156 return so_name 1op
157 error_messages.append(f"No such file: {file_wild}")
158 attachments.append(f' listdir("{lib_dir}"):')
159 if not os.path.isdir(lib_dir):
160 attachments.append(" DIRECTORY DOES NOT EXIST")
161 else:
162 for node in sorted(os.listdir(lib_dir)):
163 attachments.append(f" {node}")
164 return None
167@dataclass(frozen=True, slots=True)
168class WindowsSearchPlatform:
169 def lib_searched_for(self, libname: str) -> str:
170 return f"{libname}*.dll" 1afghtijbcmnkD
172 def site_packages_rel_dirs(self, desc: LibDescriptor) -> tuple[str, ...]:
173 return cast(tuple[str, ...], desc.site_packages_windows) 1abck
175 def conda_anchor_point(self, conda_prefix: str) -> str:
176 return os.path.join(conda_prefix, "Library") 1m
178 def anchor_rel_dirs(self, desc: LibDescriptor) -> tuple[str, ...]:
179 return cast(tuple[str, ...], desc.anchor_rel_dirs_windows) 1afguhijvcEmn
181 def find_in_site_packages(
182 self,
183 rel_dirs: tuple[str, ...],
184 lib_searched_for: str,
185 error_messages: list[str],
186 attachments: list[str],
187 ) -> str | None:
188 return _find_dll_in_rel_dirs(rel_dirs, lib_searched_for, error_messages, attachments) 1abck
190 def find_in_lib_dir(
191 self,
192 lib_dir: str,
193 libname: str,
194 _lib_searched_for: str,
195 error_messages: list[str],
196 attachments: list[str],
197 ) -> str | None:
198 file_wild = libname + "*.dll" 1afghijcmn
199 dll_name = _find_dll_under_dir(lib_dir, file_wild) 1afghijcmn
200 if dll_name is not None: 1afghijcmn
201 return dll_name 1afghijcmn
202 error_messages.append(f"No such file: {file_wild}")
203 attachments.append(f' listdir("{lib_dir}"):')
204 if not os.path.isdir(lib_dir):
205 attachments.append(" DIRECTORY DOES NOT EXIST")
206 else:
207 for node in sorted(os.listdir(lib_dir)):
208 attachments.append(f" {node}")
209 return None
212PLATFORM: SearchPlatform = WindowsSearchPlatform() if IS_WINDOWS else LinuxSearchPlatform()