Skip to content

Commit bd21d3c

Browse files
committed
Merge remote-tracking branch 'origin/main' into ajost/closed-resource-validation
2 parents 615d7d8 + 749e104 commit bd21d3c

10 files changed

Lines changed: 143 additions & 49 deletions

File tree

‎.github/workflows/pr-metadata-check.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ on:
1717
- reopened
1818
- ready_for_review
1919

20+
permissions:
21+
pull-requests: read
22+
2023
jobs:
2124
check-metadata:
2225
name: PR has assignee, labels, and milestone

‎AGENTS.md‎

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,30 @@ guide for package-specific conventions and workflows.
1414

1515
# Pull requests
1616

17-
**Never push branches or commits to the canonical upstream repository. Treat
18-
it as read-only.** Branch creation and pushes for pull-request work must go to
19-
an approved fork associated with the contributor. The fork may be owned by the
20-
contributor's personal account or by an organization.
21-
22-
Before pushing, run `git remote -v` and confirm that the intended push remote
23-
points to a fork of the pull-request base, not to the base repository itself.
24-
Compare complete `OWNER/REPOSITORY` names; do not rely on remote names such as
25-
`origin` or `upstream`, or on the owner alone. Do not use `git push upstream`
26-
or any command that writes to the upstream remote.
17+
Treat the canonical upstream repository as read-only by default. For normal
18+
pull-request work, push branches and commits to an approved fork associated
19+
with the contributor. The fork may be owned by the contributor's personal
20+
account or by an organization.
21+
22+
Before any push, run `git remote -v` and verify the complete
23+
`OWNER/REPOSITORY` of the intended destination. For normal pull-request work,
24+
confirm that the destination is a fork of the pull-request base. Do not rely
25+
on remote names such as `origin` or `upstream`, or on the owner alone.
26+
27+
An upstream push is allowed when the user explicitly requests it and provides
28+
a rationale for why the upstream repository is needed, such as testing
29+
`.github/workflows`, triggering CI from a designated upstream ref, or other
30+
infrastructure work.
31+
32+
For an authorized upstream push, verify the exact source and destination refs
33+
against the user's request. If the repository and refspec are unambiguous,
34+
proceed; do not require the user to perform the push manually solely because
35+
the destination is upstream.
36+
37+
Authorization is limited to the requested ref update. It does not authorize
38+
pushing to a default or protected branch, force-pushing, creating tags, or
39+
deleting refs unless the user separately and explicitly requests those
40+
operations.
2741

2842

2943
# General

‎cuda_bindings/tests/cython/build_tests.bat‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ REM SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIA
44
REM SPDX-License-Identifier: Apache-2.0
55

66
setlocal
7-
set CL=%CL% /I"%CUDA_HOME%\include"
8-
REM Use -j 1 to side-step any process-pool issues and ensure deterministic single-threaded builds
9-
cythonize -3 -j 1 -i -Xfreethreading_compatible=True %~dp0test_*.pyx
10-
endlocal
7+
set CL=%CL% /I"%CUDA_HOME%\include"
8+
REM The Python driver provides Cython's .pxd include path and builds in this
9+
REM directory so Windows does not duplicate the checkout path in link outputs.
10+
python "%~dp0build_tests.py"
11+
set "BUILD_RESULT=%ERRORLEVEL%"
12+
endlocal & exit /b %BUILD_RESULT%

‎cuda_bindings/tests/cython/build_tests.py‎

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ def _bindings_source_root() -> Path:
3434

3535
def main() -> None:
3636
script_dir = Path(__file__).resolve().parent
37-
pyx_files = sorted(str(p) for p in script_dir.glob("test_*.pyx"))
37+
# Avoid appending the absolute checkout path under build/temp: the
38+
# concatenated path can exceed Windows' path limit. These files are siblings.
39+
os.chdir(script_dir)
40+
pyx_files = sorted(p.name for p in script_dir.glob("test_*.pyx"))
3841
if not pyx_files:
3942
raise SystemExit(f"no test_*.pyx files under {script_dir}")
4043

@@ -46,13 +49,8 @@ def main() -> None:
4649
compiler_directives={"freethreading_compatible": True},
4750
)
4851

49-
# `build_ext --inplace` places the compiled .so relative to the current
50-
# working directory, but pixi runs this task from the project root. pytest
51-
# imports each extension by bare module name (see test_cython.py), which
52-
# only resolves when the .so sits in tests/cython (the dir pytest puts on
53-
# sys.path). chdir here so the .so lands next to its .pyx regardless of the
54-
# invoking cwd.
55-
os.chdir(script_dir)
52+
# pytest imports each extension by bare module name (see test_cython.py),
53+
# so build in-place next to its .pyx regardless of the invoking cwd.
5654
sys.argv = [sys.argv[0], "build_ext", "--inplace"]
5755
setup(name="cuda_bindings_cython_tests", ext_modules=ext_modules)
5856

‎cuda_bindings/tests/nvml/test_pynvml.py‎

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
# A set of tests ported from https://github.com/gpuopenanalytics/pynvml/blob/11.5.3/pynvml/tests/test_nvml.py
55

66
import os
7-
import time
87

98
import pytest
109

@@ -150,23 +149,6 @@ def test_device_get_power_usage(ngpus, handles, subtests):
150149
assert power_mwatts >= 0.0
151150

152151

153-
def test_device_get_total_energy_consumption(ngpus, handles, subtests):
154-
for i in range(ngpus):
155-
with subtests.test(device_index=i):
156-
with unsupported_before(handles[i], None):
157-
energy_mjoules1 = nvml.device_get_total_energy_consumption(handles[i])
158-
159-
for _ in range(10): # idle for 150 ms
160-
time.sleep(0.015) # and check for increase every 15 ms
161-
with unsupported_before(handles[i], None):
162-
energy_mjoules2 = nvml.device_get_total_energy_consumption(handles[i])
163-
assert energy_mjoules2 >= energy_mjoules1
164-
if energy_mjoules2 > energy_mjoules1:
165-
break
166-
else:
167-
raise AssertionError("energy did not increase across 150 ms interval")
168-
169-
170152
# [Skipping] pynvml.nvmlDeviceGetGpuOperationMode
171153
# [Skipping] pynvml.nvmlDeviceGetCurrentGpuOperationMode
172154
# [Skipping] pynvml.nvmlDeviceGetPendingGpuOperationMode

‎cuda_core/cuda/core/_program.pyi‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,11 @@ class ProgramOptions:
317317
Load NVIDIA's `libdevice <https://docs.nvidia.com/cuda/libdevice-users-guide/>`_
318318
math builtins library. Only supported for the NVVM backend.
319319
Default: False
320+
numba_debug : bool, optional
321+
Emit the debug information layout expected by Numba. Recognized only by
322+
newer toolkits; compilers that do not support it reject the option with
323+
an error.
324+
Default: False
320325
"""
321326
name: str | None = 'default_program'
322327
arch: str | None = None
@@ -428,6 +433,17 @@ _nvvm_import_attempted = False
428433
def _can_load_generated_ptx() -> bool:
429434
"""Check if the driver can load PTX generated by the current NVRTC version."""
430435

436+
def _assert_single_dashed_nvvm_options(options: list[str]) -> None:
437+
"""Guard against emitting a double-dashed option to libNVVM.
438+
439+
libNVVM's parser accepts only single-dashed options and rejects the
440+
double-dashed spelling of every option with NVVM_ERROR_INVALID_OPTION
441+
(see #2570). Every option on this path is generated from typed fields, so
442+
a double dash can only mean a bug in ``cuda.core`` rather than bad user
443+
input. Fail here, naming the option, instead of leaving the user with
444+
libNVVM's opaque error.
445+
"""
446+
431447
def _program_compile_uncached(program, target_type, name_expressions, logs):
432448
"""Run ``Program_compile`` without the cache wrapper.
433449

‎cuda_core/cuda/core/_program.pyx‎

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,11 @@ class ProgramOptions:
477477
Load NVIDIA's `libdevice <https://docs.nvidia.com/cuda/libdevice-users-guide/>`_
478478
math builtins library. Only supported for the NVVM backend.
479479
Default: False
480+
numba_debug : bool, optional
481+
Emit the debug information layout expected by Numba. Recognized only by
482+
newer toolkits; compilers that do not support it reject the option with
483+
an error.
484+
Default: False
480485
"""
481486

482487
name: str | None = "default_program"
@@ -1231,6 +1236,24 @@ cdef inline list _prepare_nvrtc_options_impl(object opts):
12311236
return [o.encode() for o in options]
12321237

12331238

1239+
cpdef void _assert_single_dashed_nvvm_options(options: list[str]) except *:
1240+
"""Guard against emitting a double-dashed option to libNVVM.
1241+
1242+
libNVVM's parser accepts only single-dashed options and rejects the
1243+
double-dashed spelling of every option with NVVM_ERROR_INVALID_OPTION
1244+
(see #2570). Every option on this path is generated from typed fields, so
1245+
a double dash can only mean a bug in ``cuda.core`` rather than bad user
1246+
input. Fail here, naming the option, instead of leaving the user with
1247+
libNVVM's opaque error.
1248+
"""
1249+
for option in options:
1250+
if option.startswith("--"):
1251+
raise RuntimeError(
1252+
f"Internal error: NVVM option {option!r} is double-dashed. libNVVM accepts "
1253+
f"only single-dashed options; emit {option[1:]!r} instead."
1254+
)
1255+
1256+
12341257
cdef inline object _prepare_nvvm_options_impl(object opts, bint as_bytes):
12351258
"""Build NVVM-specific compiler options."""
12361259
options = []
@@ -1243,8 +1266,10 @@ cdef inline object _prepare_nvvm_options_impl(object opts, bint as_bytes):
12431266
options.append(f"-arch={arch}")
12441267
if opts.debug is not None and opts.debug:
12451268
options.append("-g")
1269+
# libNVVM only accepts single-dashed options; the double-dashed spelling
1270+
# accepted by NVRTC is rejected with NVVM_ERROR_INVALID_OPTION.
12461271
if opts.numba_debug:
1247-
options.append("--numba-debug")
1272+
options.append("-numba-debug")
12481273
if opts.device_code_optimize is False:
12491274
options.append("-opt=0")
12501275
elif opts.device_code_optimize is True:
@@ -1324,6 +1349,8 @@ cdef inline object _prepare_nvvm_options_impl(object opts, bint as_bytes):
13241349
if unsupported:
13251350
raise CUDAError(f"The following options are not supported by NVVM backend: {', '.join(unsupported)}")
13261351

1352+
_assert_single_dashed_nvvm_options(options)
1353+
13271354
if as_bytes:
13281355
return [o.encode() for o in options]
13291356
else:

‎cuda_core/docs/source/release/1.2.0-notes.rst‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ Fixes and enhancements
127127
``coscheduled_sm_count`` explicitly when an aligned result is required.
128128
(`#2389 <https://github.com/NVIDIA/cuda-python/pull/2389>`__)
129129

130+
- ``ProgramOptions(numba_debug=True)`` now works on the NVVM backend. The
131+
option was emitted to libNVVM as ``--numba-debug``, but libNVVM accepts only
132+
single-dashed options, so every such compile failed with
133+
``NVVM_ERROR_INVALID_OPTION``. It is now emitted as ``-numba-debug``, matching
134+
what numba-cuda passes on the NVVM path. The NVRTC backend accepts both
135+
spellings and was unaffected. The option itself is only recognized by newer
136+
toolkits; libNVVM from CUDA 12.x does not support it under either spelling and
137+
still reports ``NVVM_ERROR_INVALID_OPTION``.
138+
(closes `#2570 <https://github.com/NVIDIA/cuda-python/issues/2570>`__)
139+
130140
Deprecation Notices
131141
-------------------
132142

‎cuda_core/tests/system/test_system_device.py‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,10 @@ def test_clock(subtests):
590590
with unsupported_before(device, None):
591591
pstate = device.performance_state
592592

593-
min_, max_ = clock.get_min_max_clock_of_pstate_mhz(pstate)
593+
# Individual queries may be unsupported for a clock domain even
594+
# on newer devices.
595+
with unsupported_before(device, None):
596+
min_, max_ = clock.get_min_max_clock_of_pstate_mhz(pstate)
594597
assert isinstance(min_, int)
595598
assert min_ >= 0
596599
assert isinstance(max_, int)
@@ -601,7 +604,7 @@ def test_clock(subtests):
601604
assert isinstance(max_mhz, int)
602605
assert max_mhz >= 0
603606

604-
with unsupported_before(device, DeviceArch.KEPLER):
607+
with unsupported_before(device, None):
605608
current_mhz = clock.get_current_mhz()
606609
assert isinstance(current_mhz, int)
607610
assert current_mhz >= 0

‎cuda_core/tests/test_program.py‎

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,16 @@ def _check_nvvm_arch(arch: str) -> bool:
9393

9494

9595
def _check_nvvm_supports_numba_debug() -> bool:
96-
"""Check if the installed libNVVM recognizes --numba-debug (CTK 13.2+)."""
96+
"""Check if the installed libNVVM recognizes -numba-debug.
97+
98+
libNVVM only accepts single-dashed options, so the double-dashed spelling
99+
used by NVRTC is rejected by every libNVVM version.
100+
"""
97101
if not _has_check_nvvm_compiler_options():
98102
return False
99103
from cuda.bindings.utils import check_nvvm_compiler_options
100104

101-
return check_nvvm_compiler_options(["--numba-debug"])
105+
return check_nvvm_compiler_options(["-numba-debug"])
102106

103107

104108
@pytest.fixture(scope="session")
@@ -773,18 +777,53 @@ def test_program_options_as_bytes_nvvm_unsupported_option():
773777

774778
@nvvm_available
775779
def test_nvvm_program_options_as_bytes_numba_debug():
776-
"""numba_debug must be plumbed through to libNVVM as --numba-debug
777-
(see #1287)."""
780+
"""numba_debug must be plumbed through to libNVVM as -numba-debug
781+
(see #1287, #2570). libNVVM rejects the double-dashed spelling."""
778782
options = ProgramOptions(arch="sm_80", debug=True, numba_debug=True)
779783
nvvm_bytes = options.as_bytes("nvvm")
780-
assert b"--numba-debug" in nvvm_bytes
784+
assert b"-numba-debug" in nvvm_bytes
785+
assert b"--numba-debug" not in nvvm_bytes
781786
assert b"-g" in nvvm_bytes
782787

783788

789+
@pytest.mark.agent_authored(model="claude-opus-5[1m]")
790+
def test_nvvm_options_reject_double_dash():
791+
"""The guard must name a double-dashed option rather than let libNVVM
792+
reject it with an opaque error (see #2570)."""
793+
from cuda.core._program import _assert_single_dashed_nvvm_options
794+
795+
_assert_single_dashed_nvvm_options(["-arch=compute_80", "-g", "-numba-debug"])
796+
797+
with pytest.raises(RuntimeError, match=r"--numba-debug.*double-dashed"):
798+
_assert_single_dashed_nvvm_options(["-arch=compute_80", "--numba-debug"])
799+
800+
801+
@nvvm_available
802+
@pytest.mark.agent_authored(model="claude-opus-5[1m]")
803+
def test_nvvm_program_options_as_bytes_all_single_dashed():
804+
"""Every option cuda.core emits to libNVVM must be single-dashed, because
805+
libNVVM rejects the double-dashed spelling of all of them (see #2570).
806+
This covers every NVVM-supported field of ProgramOptions."""
807+
options = ProgramOptions(
808+
arch="sm_80",
809+
debug=True,
810+
numba_debug=True,
811+
device_code_optimize=True,
812+
ftz=True,
813+
prec_sqrt=True,
814+
prec_div=True,
815+
fma=True,
816+
)
817+
nvvm_bytes = options.as_bytes("nvvm")
818+
assert nvvm_bytes, "expected at least one emitted option"
819+
offenders = [o for o in nvvm_bytes if o.startswith(b"--")]
820+
assert not offenders, f"double-dashed options are rejected by libNVVM: {offenders}"
821+
822+
784823
@nvvm_available
785824
@pytest.mark.skipif(
786825
not _check_nvvm_supports_numba_debug(),
787-
reason="installed libNVVM does not recognize --numba-debug (needs CTK 13.2+)",
826+
reason="installed libNVVM does not recognize -numba-debug",
788827
)
789828
def test_nvvm_program_numba_debug(init_cuda, nvvm_ir):
790829
options = ProgramOptions(arch="sm_80", debug=True, numba_debug=True)

0 commit comments

Comments
 (0)