| |
| |
|
|
| """Centralized CUDA environment variable handling. |
| |
| This module defines the canonical search order for CUDA Toolkit environment variables |
| used throughout cuda-python packages (cuda.pathfinder, cuda.core, cuda.bindings). |
| |
| Search Order Priority: |
| 1. CUDA_PATH (higher priority) |
| 2. CUDA_HOME (lower priority) |
| |
| If both are set and differ, CUDA_PATH takes precedence and a warning is issued. |
| |
| Important Note on Caching: |
| The result of get_cuda_path_or_home() is cached for the process lifetime. The first |
| call determines the CUDA Toolkit path, and all subsequent calls return the cached |
| value, even if environment variables change later. This ensures consistent behavior |
| throughout the application lifecycle. |
| """ |
|
|
| import functools |
| import os |
| import warnings |
|
|
| _CUDA_PATH_ENV_VARS_ORDERED = ("CUDA_PATH", "CUDA_HOME") |
|
|
|
|
| def _paths_differ(a: str, b: str) -> bool: |
| """ |
| Return True if paths are observably different. |
| |
| Strategy: |
| 1) Compare os.path.normcase(os.path.normpath(...)) for quick, robust textual equality. |
| - Handles trailing slashes and case-insensitivity on Windows. |
| 2) If still different AND both exist, use os.path.samefile to resolve symlinks/junctions. |
| 3) Otherwise (nonexistent paths or samefile unavailable), treat as different. |
| """ |
| norm_a = os.path.normcase(os.path.normpath(a)) |
| norm_b = os.path.normcase(os.path.normpath(b)) |
| if norm_a == norm_b: |
| return False |
|
|
| try: |
| if os.path.exists(a) and os.path.exists(b): |
| |
| return not os.path.samefile(a, b) |
| except OSError: |
| |
| pass |
|
|
| |
| return True |
|
|
|
|
| @functools.cache |
| def get_cuda_path_or_home() -> str | None: |
| """Get CUDA Toolkit path from environment variables. |
| |
| Returns the value of CUDA_PATH or CUDA_HOME. If both are set and differ, |
| CUDA_PATH takes precedence and a warning is issued. |
| |
| The result is cached for the process lifetime. The first call determines the CUDA |
| Toolkit path, and subsequent calls return the cached value. |
| |
| Returns: |
| Path to CUDA Toolkit, or None if neither variable is set or all are empty. |
| |
| Warnings: |
| UserWarning: If multiple CUDA environment variables are set but point to |
| different locations (only on the first call). |
| |
| """ |
| |
| |
| set_vars = {} |
| for var in _CUDA_PATH_ENV_VARS_ORDERED: |
| val = os.environ.get(var) |
| if val: |
| set_vars[var] = val |
|
|
| if not set_vars: |
| return None |
|
|
| |
| if len(set_vars) > 1: |
| values = list(set_vars.items()) |
| values_differ = False |
| for i in range(len(values) - 1): |
| if _paths_differ(values[i][1], values[i + 1][1]): |
| values_differ = True |
| break |
|
|
| if values_differ: |
| var_list = "\n".join(f" {var}={val}" for var, val in set_vars.items()) |
| warnings.warn( |
| f"Multiple CUDA environment variables are set but differ:\n" |
| f"{var_list}\n" |
| f"Using {_CUDA_PATH_ENV_VARS_ORDERED[0]} (highest priority).", |
| UserWarning, |
| stacklevel=2, |
| ) |
|
|
| |
| return next(iter(set_vars.values())) |
|
|