Skip to content

Commit af88337

Browse files
committed
cuda.core: regenerate stubs and drop exec from the floor tool and tests (#2783)
stubgen-pyx regenerated version.pyi (BUILD_CUDA_MAJOR), system/_system.pyi (the constant is now True) and system/_device.pyi (NVLink 6.0 mapping is unconditional). ci/tools/cuda_core_bindings_floor.py reads the CUDA_BINDINGS_FLOOR literal with ast instead of executing the module; its tests and test_build_hooks.py load modules with importlib instead of exec, and the stubbed configuration check takes any arguments.
1 parent cd1c42d commit af88337

6 files changed

Lines changed: 37 additions & 25 deletions

File tree

‎ci/tools/cuda_core_bindings_floor.py‎

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,40 @@
1616
1717
The wheel carries the import-free module cuda/core/_bindings_floor.py, at top
1818
level in a single-major build and under cuda/core/cu<major>/ in the merged
19-
wheel; this script evaluates it and prints CUDA_BINDINGS_FLOOR[major].
19+
wheel; this script reads the CUDA_BINDINGS_FLOOR literal out of it (without
20+
running the module) and prints the entry for `major` as a dotted version.
2021
"""
2122

2223
from __future__ import annotations
2324

2425
import argparse
26+
import ast
2527
import sys
2628
import zipfile
2729
from pathlib import Path
2830

2931
MODULE = "_bindings_floor.py"
3032

3133

34+
def floors_from_source(source: str) -> dict[int, tuple[int, int, int]]:
35+
"""The CUDA_BINDINGS_FLOOR literal of _bindings_floor.py, parsed without executing it."""
36+
for node in ast.parse(source, MODULE).body:
37+
if isinstance(node, ast.AnnAssign):
38+
targets = [node.target]
39+
elif isinstance(node, ast.Assign):
40+
targets = node.targets
41+
else:
42+
continue
43+
if node.value is not None and any(isinstance(t, ast.Name) and t.id == "CUDA_BINDINGS_FLOOR" for t in targets):
44+
return ast.literal_eval(node.value)
45+
raise SystemExit(f"{MODULE} does not assign CUDA_BINDINGS_FLOOR")
46+
47+
3248
def floor_from_source(source: str, major: int) -> str:
33-
namespace: dict = {}
34-
exec(compile(source, MODULE, "exec"), namespace)
35-
floors = namespace["CUDA_BINDINGS_FLOOR"]
49+
floors = floors_from_source(source)
3650
if major not in floors:
3751
raise SystemExit(f"CUDA {major} is not a supported major (floors: {sorted(floors)})")
38-
return namespace["format_version"](floors[major])
52+
return ".".join(str(part) for part in floors[major])
3953

4054

4155
def floor_from_wheel(wheel: Path, major: int) -> str:

‎ci/tools/tests/test_cuda_core_bindings_floor.py‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,19 @@
1313
FLOOR_MODULE = REPO / "cuda_core" / "cuda" / "core" / "_bindings_floor.py"
1414

1515

16-
def _load_tool():
17-
spec = importlib.util.spec_from_file_location("cuda_core_bindings_floor", TOOLS / "cuda_core_bindings_floor.py")
16+
def _load(name, path):
17+
spec = importlib.util.spec_from_file_location(name, path)
1818
module = importlib.util.module_from_spec(spec)
1919
spec.loader.exec_module(module)
2020
return module
2121

2222

23-
tool = _load_tool()
23+
tool = _load("cuda_core_bindings_floor", TOOLS / "cuda_core_bindings_floor.py")
24+
floor_module = _load("cuda_core_bindings_floor_module", FLOOR_MODULE)
2425

2526

2627
def _expected(major):
27-
namespace = {}
28-
exec(FLOOR_MODULE.read_text(encoding="utf-8"), namespace)
29-
return namespace["format_version"](namespace["CUDA_BINDINGS_FLOOR"][major])
28+
return floor_module.format_version(floor_module.CUDA_BINDINGS_FLOOR[major])
3029

3130

3231
def _wheel(tmp_path, entries):

‎cuda_core/cuda/core/_utils/version.pyi‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import functools
44

5+
BUILD_CUDA_MAJOR: int = ...
56

67
def _parse_version_triple(version_str: str) -> tuple[int, int, int]:
78
"""Parse a PEP 440 version string into a (major, minor, patch) triple.
@@ -10,8 +11,6 @@ def _parse_version_triple(version_str: str) -> tuple[int, int, int]:
1011
``0b1`` or ``0rc1`` by extracting only the leading integer from each
1112
release segment.
1213
"""
13-
BUILD_CUDA_MAJOR: int
14-
1514
@functools.cache
1615
def binding_version() -> tuple[int, int, int]:
1716
"""Return the cuda-bindings version as a (major, minor, patch) triple."""

‎cuda_core/cuda/core/system/_device.pyi‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ _EVENT_TYPE_MAPPING = {nvml.EventType.NONE: EventType.NONE, nvml.EventType.SINGL
2222
_EVENT_TYPE_INV_MAPPING = {v: k for k, v in _EVENT_TYPE_MAPPING.items()}
2323
_FAN_CONTROL_POLICY_MAPPING = {nvml.FanControlPolicy.TEMPERATURE_CONTINUOUS_SW: FanControlPolicy.TEMPERATURE_CONTROLLED, nvml.FanControlPolicy.MANUAL: FanControlPolicy.MANUAL}
2424
_INFOROM_OBJECT_MAPPING = {InforomObject.OEM: nvml.InforomObject.INFOROM_OEM, InforomObject.ECC: nvml.InforomObject.INFOROM_ECC, InforomObject.POWER: nvml.InforomObject.INFOROM_POWER, InforomObject.DEN: nvml.InforomObject.INFOROM_DEN}
25-
_NVLINK_VERSION_MAPPING = {nvml.NvlinkVersion.VERSION_1_0: (1, 0), nvml.NvlinkVersion.VERSION_2_0: (2, 0), nvml.NvlinkVersion.VERSION_2_2: (2, 2), nvml.NvlinkVersion.VERSION_3_0: (3, 0), nvml.NvlinkVersion.VERSION_3_1: (3, 1), nvml.NvlinkVersion.VERSION_4_0: (4, 0), nvml.NvlinkVersion.VERSION_5_0: (5, 0)}
26-
_NVLINK_VERSION_6_0 = getattr(nvml.NvlinkVersion, 'VERSION_6_0', None)
25+
_NVLINK_VERSION_MAPPING = {nvml.NvlinkVersion.VERSION_1_0: (1, 0), nvml.NvlinkVersion.VERSION_2_0: (2, 0), nvml.NvlinkVersion.VERSION_2_2: (2, 2), nvml.NvlinkVersion.VERSION_3_0: (3, 0), nvml.NvlinkVersion.VERSION_3_1: (3, 1), nvml.NvlinkVersion.VERSION_4_0: (4, 0), nvml.NvlinkVersion.VERSION_5_0: (5, 0), nvml.NvlinkVersion.VERSION_6_0: (6, 0)}
2726
_TEMPERATURE_THRESHOLD_MAPPING = {TemperatureThresholds.SHUTDOWN: nvml.TemperatureThresholds.TEMPERATURE_THRESHOLD_SHUTDOWN, TemperatureThresholds.SLOWDOWN: nvml.TemperatureThresholds.TEMPERATURE_THRESHOLD_SLOWDOWN, TemperatureThresholds.MEM_MAX: nvml.TemperatureThresholds.TEMPERATURE_THRESHOLD_MEM_MAX, TemperatureThresholds.GPU_MAX: nvml.TemperatureThresholds.TEMPERATURE_THRESHOLD_GPU_MAX, TemperatureThresholds.ACOUSTIC_MIN: nvml.TemperatureThresholds.TEMPERATURE_THRESHOLD_ACOUSTIC_MIN, TemperatureThresholds.ACOUSTIC_CURR: nvml.TemperatureThresholds.TEMPERATURE_THRESHOLD_ACOUSTIC_CURR, TemperatureThresholds.ACOUSTIC_MAX: nvml.TemperatureThresholds.TEMPERATURE_THRESHOLD_ACOUSTIC_MAX, TemperatureThresholds.GPS_CURR: nvml.TemperatureThresholds.TEMPERATURE_THRESHOLD_GPS_CURR}
2827
_THERMAL_CONTROLLER_MAPPING = {nvml.ThermalController.GPU_INTERNAL: ThermalController.GPU_INTERNAL, nvml.ThermalController.ADM1032: ThermalController.ADM1032, nvml.ThermalController.ADT7461: ThermalController.ADT7461, nvml.ThermalController.MAX6649: ThermalController.MAX6649, nvml.ThermalController.MAX1617: ThermalController.MAX1617, nvml.ThermalController.LM99: ThermalController.LM99, nvml.ThermalController.LM89: ThermalController.LM89, nvml.ThermalController.LM64: ThermalController.LM64, nvml.ThermalController.G781: ThermalController.G781, nvml.ThermalController.ADT7473: ThermalController.ADT7473, nvml.ThermalController.SBMAX6649: ThermalController.SBMAX6649, nvml.ThermalController.VBIOSEVT: ThermalController.VBIOSEVT, nvml.ThermalController.OS: ThermalController.OS, nvml.ThermalController.NVSYSCON_CANOAS: ThermalController.NVSYSCON_CANOAS, nvml.ThermalController.NVSYSCON_E551: ThermalController.NVSYSCON_E551, nvml.ThermalController.MAX6649R: ThermalController.MAX6649R, nvml.ThermalController.ADT7473S: ThermalController.ADT7473S, nvml.ThermalController.UNKNOWN: ThermalController.UNKNOWN}
2928
_THERMAL_TARGET_MAPPING = {nvml.ThermalTarget.NONE: ThermalTarget.NONE, nvml.ThermalTarget.GPU: ThermalTarget.GPU, nvml.ThermalTarget.MEMORY: ThermalTarget.MEMORY, nvml.ThermalTarget.POWER_SUPPLY: ThermalTarget.POWER_SUPPLY, nvml.ThermalTarget.BOARD: ThermalTarget.BOARD, nvml.ThermalTarget.VCD_BOARD: ThermalTarget.VCD_BOARD, nvml.ThermalTarget.VCD_INLET: ThermalTarget.VCD_INLET, nvml.ThermalTarget.VCD_OUTLET: ThermalTarget.VCD_OUTLET, nvml.ThermalTarget.ALL: ThermalTarget.ALL}

‎cuda_core/cuda/core/system/_system.pyi‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# This file was generated by stubgen-pyx v0.2.22 from cuda_core/cuda/core/system/_system.pyx
22

3-
CUDA_BINDINGS_NVML_IS_COMPATIBLE: bool
3+
CUDA_BINDINGS_NVML_IS_COMPATIBLE: bool = True
44
__all__ = ['get_driver_branch', 'get_kernel_mode_driver_version', 'get_user_mode_driver_version', 'get_nvml_version', 'get_num_devices', 'get_process_name', 'CUDA_BINDINGS_NVML_IS_COMPATIBLE']
55

66
def get_user_mode_driver_version() -> tuple[int, ...]:
77
"""
88
Get the user-mode (UMD / CUDA) driver version.
99
1010
This is the most commonly needed version when checking CUDA driver
11-
compatibility. It works with all ``cuda-bindings`` versions.
11+
compatibility.
1212
1313
Returns
1414
-------

‎cuda_core/tests/test_build_hooks.py‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ def fake_cythonize(ext_modules, **kwargs):
234234
monkeypatch.setattr(build_hooks, "_get_cuda_path", lambda: "/nonexistent-cuda")
235235
# The configuration check reads that header and the installed cuda-bindings;
236236
# it has its own tests (TestBuildConfigurationCheck).
237-
monkeypatch.setattr(build_hooks, "_check_build_configuration", lambda cuda_path, cuda_major: None)
237+
monkeypatch.setattr(build_hooks, "_check_build_configuration", lambda *_: None)
238238
monkeypatch.setattr(build_hooks, "cythonize", fake_cythonize)
239239
monkeypatch.setenv("CUDA_CORE_BUILD_MAJOR", cuda_major)
240240
build_hooks._determine_cuda_major_version.cache_clear()
@@ -494,12 +494,13 @@ def test_floor_bindings_and_matching_header_pass_and_are_recorded(self, tmp_path
494494

495495
build_hooks._check_build_configuration(cuda_path, str(major))
496496

497-
info = {}
498-
exec(build_hooks._BUILD_INFO_PATH.read_text(), info)
499-
assert info["CUDA_MAJOR"] == major
500-
assert info["CUDA_VERSION"] == floor[0] * 1000 + floor[1] * 10
501-
assert info["CUDA_BINDINGS_FLOOR"] == floor
502-
assert info["CUDA_BINDINGS_BUILD_VERSION"] == version
497+
spec = importlib.util.spec_from_file_location("_build_info_under_test", build_hooks._BUILD_INFO_PATH)
498+
info = importlib.util.module_from_spec(spec)
499+
spec.loader.exec_module(info)
500+
assert major == info.CUDA_MAJOR
501+
assert floor[0] * 1000 + floor[1] * 10 == info.CUDA_VERSION
502+
assert floor == info.CUDA_BINDINGS_FLOOR
503+
assert version == info.CUDA_BINDINGS_BUILD_VERSION
503504

504505
@pytest.mark.agent_authored(model="claude-fable-5-1")
505506
def test_bindings_below_the_floor_fail(self, tmp_path, monkeypatch):
@@ -597,7 +598,7 @@ def fake_cythonize(ext_modules, **kwargs):
597598
return []
598599

599600
monkeypatch.setattr(build_hooks, "_get_cuda_path", lambda: "/nonexistent-cuda")
600-
monkeypatch.setattr(build_hooks, "_check_build_configuration", lambda cuda_path, cuda_major: None)
601+
monkeypatch.setattr(build_hooks, "_check_build_configuration", lambda *_: None)
601602
monkeypatch.setattr(build_hooks, "cythonize", fake_cythonize)
602603
monkeypatch.setenv("CUDA_CORE_BUILD_MAJOR", "13")
603604
build_hooks._determine_cuda_major_version.cache_clear()

0 commit comments

Comments
 (0)