Skip to content

fix(bindings): CUDA_PYTHON_DISABLE_MAJOR_VERSION_WARNING=0 must not disable it - #2581

Open
LeSingh1 wants to merge 2 commits into
NVIDIA:mainfrom
LeSingh1:version-check-disable-flag
Open

fix(bindings): CUDA_PYTHON_DISABLE_MAJOR_VERSION_WARNING=0 must not disable it#2581
LeSingh1 wants to merge 2 commits into
NVIDIA:mainfrom
LeSingh1:version-check-disable-flag

Conversation

@LeSingh1

@LeSingh1 LeSingh1 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Problem

The suppression check tests the raw environment string for truthiness (_version_check.py:35):

    # Allow users to suppress the warning
    if os.environ.get("CUDA_PYTHON_DISABLE_MAJOR_VERSION_WARNING"):
        return

so CUDA_PYTHON_DISABLE_MAJOR_VERSION_WARNING=0 — the obvious way to spell "no, keep warning me" — suppresses the warning exactly as effectively as =1.

Two things make that a trap rather than a convention:

  1. The warning's own text reads (Set CUDA_PYTHON_DISABLE_MAJOR_VERSION_WARNING=1 to suppress this warning.), which implies 0 is the off value.
  2. The other two boolean knobs in this repository disagree with it, and both parse with int():
# cuda_bindings/cuda/bindings/_internal/runtime_linux.pyx:29 (and _windows)
__usePTDS = bool(int(os.getenv('CUDA_PYTHON_CUDA_PER_THREAD_DEFAULT_STREAM', default=0)))

# cuda_core/cuda/core/__init__.py:44
if int(os.environ.get("CUDA_CORE_DONT_FIX_TAB_COMPLETION", "0")):

A user (or a CI job template, or a Dockerfile) that sets all three to 0 gets the behaviour they asked for from two of them and the opposite from this one — silently losing a compatibility warning whose whole purpose is to explain why their driver is too old for the wheel they installed.

Fix

Parse the value with int() here too, in a small _warning_disabled() helper.

Compatibility: unset and empty still mean "not disabled", every non-zero integer still disables, and a value that is not an integer keeps the old set-means-disabled behaviour so nobody relying on a spelling like =true starts seeing the warning again. 0 is the only input whose meaning changes — which is the point.

Also uses the env-var name constant in the warning text so the message and the lookup cannot drift apart.

Tests

Added to cuda_bindings/tests/test_version_check.py:

  • test_disable_flag_parsing — parametrised over 0, 0, empty, blank, 1, 2, true, yes.
  • test_disable_flag_unset.
  • test_warning_not_suppressed_when_env_var_is_zero — end-to-end through warn_if_cuda_major_version_mismatch, mirroring the existing test_warning_suppressed_by_env_var it sits next to.

The existing test_warning_suppressed_by_env_var (=1) is unchanged and still passes.

What I ran

Environment: macOS, no CUDA driver and no CUDA toolkit, so cuda.bindings is not built here.

  • Did not run: cuda_bindings/tests/test_version_check.py itself — it imports cuda.bindings.driver at module level.
  • Ran (teeth check): _version_check.py imports only os / threading / warnings at module level and pulls in cuda.bindings.driver inside the function, so I loaded the real module with a stubbed driver (CUDA_VERSION=13000, cuDriverGetVersion -> 12080) and counted emitted warnings for each env value:
                       main    this PR
  unset                  1        1
  ""                     1        1
  "   "                  0        1   <-- fixed
  "0"                    0        1   <-- the defect
  " 0 "                  0        1   <-- fixed
  "1"                    0        0
  "2"                    0        0
  "true"                 0        0   <-- deliberately unchanged
  "yes"                  0        0   <-- deliberately unchanged
  • Ran: python -m py_compile, ruff check, ruff format --check on both changed files — clean, no new findings against a main baseline.
  • Checked: warn_if_cuda_major_version_mismatch has exactly one non-test caller, Device_ensure_cuda_initialized in cuda_core/cuda/core/_device.pyx:1613-1625, which calls it immediately after a successful cuInit(0) and does not inspect the env var itself.

…isable it

The suppression check tests the raw environment string for truthiness:

    if os.environ.get("CUDA_PYTHON_DISABLE_MAJOR_VERSION_WARNING"):
        return

so `=0` -- the obvious way to spell "no, keep warning me" -- suppresses the
warning just as effectively as `=1`. The warning's own text says
"(Set CUDA_PYTHON_DISABLE_MAJOR_VERSION_WARNING=1 to suppress this warning.)",
which reads as though 0 is the off value, and the other two boolean knobs in
this repository disagree with it:

    cuda_bindings/.../_internal/runtime_linux.pyx:29
        bool(int(os.getenv('CUDA_PYTHON_CUDA_PER_THREAD_DEFAULT_STREAM', default=0)))
    cuda_core/cuda/core/__init__.py:44
        if int(os.environ.get("CUDA_CORE_DONT_FIX_TAB_COMPLETION", "0")):

Both parse with int(), so `=0` is off for them. A user who sets all three to
0 gets the behaviour they asked for from two of them and the opposite from
this one, silently losing a compatibility warning that exists to explain why
their driver is too old.

Parse the value with int() here too. Unset and empty still mean "not
disabled". A value that is not an integer keeps the old set-means-disabled
behaviour, so anyone currently relying on a spelling like `=true` does not
start seeing the warning again -- `0` is the only input whose meaning
changes.
@copy-pr-bot

copy-pr-bot Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions github-actions Bot added the cuda.bindings Everything related to the cuda.bindings module label Aug 9, 2026
Comment on lines +16 to +36
"""Whether the user asked to suppress the major-version warning.

``=0`` means "do not suppress". A bare truthiness test on the raw string
made ``CUDA_PYTHON_DISABLE_MAJOR_VERSION_WARNING=0`` suppress the warning
-- the exact opposite of what the warning itself tells the user to type,
and the opposite of the other boolean knobs in this repository
(``CUDA_PYTHON_CUDA_PER_THREAD_DEFAULT_STREAM`` and
``CUDA_CORE_DONT_FIX_TAB_COMPLETION``), which both parse their value with
``int()``.

Unset and empty still mean "not disabled". A value that is not an integer
keeps the old set-means-disabled behaviour, so anyone currently relying on
a spelling like ``=true`` does not silently start seeing the warning again.
"""
raw = os.environ.get(_DISABLE_WARNING_ENV_VAR, "").strip()
if not raw:
return False
try:
return int(raw) != 0
except ValueError:
return True

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make a helper function for parsing a bool-like value from an environment variable. Call it envvar_bool and put it in utils/__init__.py. Then just use that, passing in the environment variable name, from warn_if_cuda_major_version_mismatch.

There are other instances of parsing bool-like envvars throughout the codebase that could then be updated to use this new helper function.

Comment on lines +87 to +111
@pytest.mark.agent_authored(model="claude-opus-5")
@pytest.mark.parametrize(
("raw", "expected"),
[
pytest.param("0", False, id="zero"),
pytest.param(" 0 ", False, id="zero-padded"),
pytest.param("", False, id="empty"),
pytest.param(" ", False, id="blank"),
pytest.param("1", True, id="one"),
pytest.param("2", True, id="two"),
# Not an integer: keep the old set-means-disabled behaviour so no
# one relying on a spelling like `=true` starts seeing the warning
# again.
pytest.param("true", True, id="true"),
pytest.param("yes", True, id="yes"),
],
)
def test_disable_flag_parsing(self, monkeypatch, raw, expected):
monkeypatch.setenv("CUDA_PYTHON_DISABLE_MAJOR_VERSION_WARNING", raw)
assert _version_check._warning_disabled() is expected

@pytest.mark.agent_authored(model="claude-opus-5")
def test_disable_flag_unset(self, monkeypatch):
monkeypatch.delenv("CUDA_PYTHON_DISABLE_MAJOR_VERSION_WARNING", raising=False)
assert _version_check._warning_disabled() is False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given my other suggestion of a generic helper function, the test should be also made generic (and not tied to a specific environment variable).

Replaces the flag-specific _warning_disabled with a shared envvar_bool,
exported from cuda.bindings.utils. It recognises 1/true/yes/on and
0/false/no/off case-insensitively, falls back to C truthiness for other
integers, and treats unset, empty, and whitespace-only as the caller's
default.

The helper lives in utils/_envvar.py rather than directly in
utils/__init__.py because __init__ already imports _version_check, which
needs the helper -- defining it in __init__ would make that import
circular. It is re-exported so the public name is cuda.bindings.utils.envvar_bool.

The parsing test moves to test_envvar.py and drives an arbitrary variable
name instead of the version-check one.
@LeSingh1

Copy link
Copy Markdown
Contributor Author

Done — added envvar_bool and switched the version-check flag to it.

One deviation from your suggestion, and I want to flag it rather than bury it: I put the function in utils/_envvar.py and re-exported it, instead of defining it in utils/__init__.py directly. __init__.py already imports _version_check, and _version_check is the caller, so defining it in __init__ makes that import circular. The public name is still cuda.bindings.utils.envvar_bool. Happy to inline it if you'd rather restructure the imports.

It recognises 1/true/yes/on and 0/false/no/off case-insensitively, falls back to int(raw, 0) truthiness for anything else numeric, and treats unset, empty and whitespace-only as the caller's default. The parsing test moved to tests/test_envvar.py and drives an arbitrary variable name.

On the other bool-like envvars: I surveyed them and most can't actually use this helper.

  • CUDA_PYTHON_CUDA_PER_THREAD_DEFAULT_STREAM in _internal/{driver,runtime}_{linux,windows}.pyx — these are generated ("Do not modify it directly") and sealed by the check-generated-file-seals hook. They'd have to change in the generator.
  • CUDA_PYTHON_COVERAGE in setup.py / build_hooks.py (both packages) — build-time, runs before the package is importable.
  • CUDA_PYTHON_UNDER_SANITIZER and SETUP_SANITIZER in cuda_python_test_helpers — separate package, currently == "1".
  • CUDA_CORE_DONT_FIX_TAB_COMPLETION in cuda_core/cuda/core/__init__.py — different package; usable only if you want cuda_core importing this helper from cuda_bindings.

So the reachable conversions are cuda_core's tab-completion flag and the two test-helper flags, both cross-package. Say the word and I'll do either or both, in this PR or a follow-up — I left them out since they widen the blast radius past the bug this PR fixes.

One thing that's yours to decide: an unrecognised value like =banana currently returns True, preserving the historical set-means-on behaviour. Raising would be cleaner but these are read at import time, so a typo would become an import failure. I kept the lenient behaviour; tell me if you'd prefer strict.

@mdboom

mdboom commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

/ok to test 75efd63

@mdboom mdboom self-assigned this Aug 27, 2026
@mdboom mdboom added this to the cuda.bindings next milestone Aug 27, 2026
@mdboom mdboom added the P2 Low priority - Nice to have label Aug 27, 2026

@mdboom mdboom left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Looks in good shape now.

@mdboom
mdboom enabled auto-merge (squash) August 27, 2026 17:50
@github-actions

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cuda.bindings Everything related to the cuda.bindings module P2 Low priority - Nice to have

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants