From e621cbffa0e54f47ce3e1c6a114fa4cf34494469 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 19 Mar 2026 17:02:52 -0700 Subject: [PATCH] docs(pathfinder): clarify Windows DLL search semantics Document that Python 3.8+ excludes PATH from the native Windows DLL search used by cuda.pathfinder, so CTK installs are typically found via CUDA_HOME/CUDA_PATH or other explicit locations instead. Made-with: Cursor --- .../pathfinder/_dynamic_libs/load_dl_linux.py | 4 +-- .../_dynamic_libs/load_dl_windows.py | 10 ++++-- .../_dynamic_libs/load_nvidia_dynamic_lib.py | 33 +++++++++++-------- .../pathfinder/_dynamic_libs/search_steps.py | 8 ++++- 4 files changed, 36 insertions(+), 19 deletions(-) diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_dl_linux.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_dl_linux.py index e4f2d0d817..4bce75d109 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_dl_linux.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_dl_linux.py @@ -156,10 +156,10 @@ def _load_lib(desc: LibDescriptor, filename: str) -> ctypes.CDLL: def load_with_system_search(desc: LibDescriptor) -> LoadedDL | None: - """Try to load a library using system search paths. + """Try to load a library using the native Linux dynamic-loader search path. Args: - libname: The name of the library to load + desc: Descriptor for the library to load Returns: A LoadedDL object if successful, None if the library cannot be loaded diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_dl_windows.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_dl_windows.py index cf4e32d0d8..a296813aa2 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_dl_windows.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_dl_windows.py @@ -116,10 +116,16 @@ def check_if_already_loaded_from_elsewhere(desc: LibDescriptor, have_abs_path: b def load_with_system_search(desc: LibDescriptor) -> LoadedDL | None: - """Try to load a DLL using system search paths. + """Try to load a DLL using the native Windows process DLL search path. + + This calls ``LoadLibraryExW(dll_name, NULL, 0)`` directly. Under Python + 3.8+, CPython configures the process with + ``SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)``, so this + search does **not** include the system ``PATH``. Directories added via + ``AddDllDirectory()`` still participate. Args: - libname: The name of the library to load + desc: Descriptor for the library to load Returns: A LoadedDL object if successful, None if the library cannot be loaded diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py index b137fc9249..acf9bd62d7 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py @@ -58,9 +58,9 @@ def _load_driver_lib_no_cache(desc: LibDescriptor) -> LoadedDL: """Load an NVIDIA driver library (system-search only). Driver libs (libcuda, libnvidia-ml) are part of the display driver, not - the CUDA Toolkit. They are always on the system linker path, so the - full CTK search cascade (site-packages, conda, CUDA_HOME, canary) is - unnecessary. + the CUDA Toolkit. They are expected to be discoverable via the platform's + native loader mechanisms, so the full CTK search cascade (site-packages, + conda, CUDA_HOME, canary) is unnecessary. """ loaded = LOADER.check_if_already_loaded_from_elsewhere(desc, False) if loaded is not None: @@ -234,35 +234,40 @@ def load_nvidia_dynamic_lib(libname: str) -> LoadedDL: - Linux: ``dlopen()`` - - Windows: ``LoadLibraryW()`` + - Windows: ``LoadLibraryExW()`` - - CUDA Toolkit (CTK) system installs with system config updates are often - discovered via: + On Linux, CUDA Toolkit (CTK) system installs with system config updates are + usually discovered via ``/etc/ld.so.conf.d/*cuda*.conf``. - - Linux: ``/etc/ld.so.conf.d/*cuda*.conf`` - - - Windows: ``C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\vX.Y\\bin`` - on the system ``PATH``. + On Windows, under Python 3.8+, CPython configures the process with + ``SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)``. + As a result, the native DLL search used here does **not** include + the system ``PATH``. 4. **Environment variables** - If set, use ``CUDA_HOME`` or ``CUDA_PATH`` (in that order). + On Windows, this is the typical way system-installed CTK DLLs are + located. Note that the NVIDIA CTK installer automatically + adds ``CUDA_PATH`` to the system-wide environment. 5. **CTK root canary probe (discoverable libs only)** - For selected libraries whose shared object doesn't reside on the standard linker path (currently ``nvvm``), attempt to derive CTK root by system-loading a well-known CTK canary library in a - subprocess and then searching relative to that root. + subprocess and then searching relative to that root. On Windows, + the canary uses the same native ``LoadLibraryExW`` semantics as + step 3, so there is also no ``PATH``-based discovery. **Driver libraries** (``"cuda"``, ``"nvml"``): These are part of the NVIDIA display driver (not the CUDA Toolkit) and - are always on the system linker path. For these libraries the search - is simplified to: + are expected to be reachable via the native OS loader path. For these + libraries the search is simplified to: 0. Already loaded in the current process - 1. OS default mechanisms (``dlopen`` / ``LoadLibraryW``) + 1. OS default mechanisms (``dlopen`` / ``LoadLibraryExW``) The CTK-specific steps (site-packages, conda, ``CUDA_HOME``, canary probe) are skipped entirely. diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/search_steps.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/search_steps.py index 7c7e36f70d..216c4e1a63 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/search_steps.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/search_steps.py @@ -183,7 +183,13 @@ def find_in_conda(ctx: SearchContext) -> FindResult | None: def find_in_cuda_home(ctx: SearchContext) -> FindResult | None: - """Search ``$CUDA_HOME`` / ``$CUDA_PATH``.""" + """Search ``$CUDA_HOME`` / ``$CUDA_PATH``. + + On Windows, this is the normal fallback for system-installed CTK DLLs when + they are not already discoverable via the native ``LoadLibraryExW(..., 0)`` + path used by :func:`cuda.pathfinder._dynamic_libs.load_dl_windows.load_with_system_search`. + Python 3.8+ does not include ``PATH`` in that native DLL search. + """ cuda_home = get_cuda_home_or_path() if cuda_home is None: return None