Skip to content

Address duplicate import issue found in pypi-installed cuda-core packages - #2386

Open
acosmicflamingo wants to merge 5 commits into
NVIDIA:mainfrom
acosmicflamingo:prevent-duplicate-imports
Open

Address duplicate import issue found in pypi-installed cuda-core packages#2386
acosmicflamingo wants to merge 5 commits into
NVIDIA:mainfrom
acosmicflamingo:prevent-duplicate-imports

Conversation

@acosmicflamingo

@acosmicflamingo acosmicflamingo commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes #2023

Right now, importing certain modules also imports a duplicate version where the cuda major version number resides in the name (e.g. importing cuda.core.system will mean both cuda.core.system and cuda.core.cu13.system are now in sys.modules).

It's also difficult to reproduce the problem locally. Running pip install . was not giving me the file path I was expecting to write a failing test, which would allow me to assert that the solution I come up with actually works (assuming that the fix involves recompiling cython modules):

# typing.py location from remote pip install
lib/python3.13/site-packages/cuda/core/cu13/system/typing.py

# typing.py location from remote conda install and local pip install
lib/python3.13/site-packages/cuda/core/system/typing.py

Although the issue is exacerbated by relative imports, the underlying issue comes from how cuda.core.__init__.py handles importing the modules from the cuda/core/cu13 path, so using absolute imports everywhere will not get rid of cuda.core.cu13 from sys.modules.

The PR's approach is to simply append the cuda/core/cu<cuda_major> absolute path (if it exists) to cuda.core.__path__ attribute. This will mean users do not have to change relative imports to absolute imports.

Checklist

  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

@copy-pr-bot

copy-pr-bot Bot commented Jul 17, 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.core Everything related to the cuda.core module label Jul 17, 2026
@acosmicflamingo

Copy link
Copy Markdown
Contributor Author

@mdboom when you have the chance, can you please run /ok to test for me?

@mdboom

mdboom commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

/ok to test

@copy-pr-bot

copy-pr-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

/ok to test

@mdboom, there was an error processing your request: E1

See the following link for more information: https://docs.gha-runners.nvidia.com/cpr/e/1/

@acosmicflamingo

Copy link
Copy Markdown
Contributor Author

Hm, I've seen this E1 error appear before in another PR (not in this repo though, was numba-cuda-mlir). Is there something additional I need to do before opening a PR to not encounter the issue the first time around?

@mdboom

mdboom commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

/ok to test 0e12b48

@mdboom

mdboom commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Hm, I've seen this E1 error appear before in another PR (not in this repo though, was numba-cuda-mlir). Is there something additional I need to do before opening a PR to not encounter the issue the first time around?

Sorry, that was my bad. I have to provide the commit hash in my comment.

@github-actions

Copy link
Copy Markdown

@acosmicflamingo

Copy link
Copy Markdown
Contributor Author

Sorry, that was my bad. I have to provide the commit hash in my comment.

It's all good; happy to finally know why that happens ;)

Alright, test failed as I wanted:

=================================== FAILURES ===================================
__________________________ test_typing_module_imports __________________________

    def test_typing_module_imports():
        """
        Importing cuda.core.system should not also import cuda.core.cuXX.system
        """
    
        assert "cuda.core.system" in sys.modules
>       assert f"cuda.core.cu{cuda_major}.system" not in sys.modules
E       AssertionError: assert 'cuda.core.cu13.system' not in {'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, '_frozen_importlib': <module '_frozen_importlib' (frozen)>, '_imp': <module '_imp' (built-in)>, ...}
E        +  where {'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, '_frozen_importlib': <module '_frozen_importlib' (frozen)>, '_imp': <module '_imp' (built-in)>, ...} = sys.modules


tests/test_duplicate_imports.py:20: AssertionError

Although I've struggled to find relative imports to change to absolute imports within cython files, I did find some in cuda.core.system.__init__.py to change. I confirmed locally that making the change did fix many of those modules!

However, I'm struggling with how to handle cuda.core.cu13 itself. While experimenting, I did find a potential fix that might both address this issue and not require developers to change their importing behavior.

diff --git a/cuda_core/cuda/core/__init__.py b/cuda_core/cuda/core/__init__.py
index dc6fefdffe..94f7959f5d 100644
--- a/cuda_core/cuda/core/__init__.py
+++ b/cuda_core/cuda/core/__init__.py
@@ -5,8 +5,11 @@
 from cuda.core._version import __version__
 
 
+# TODO: remove this function altogether after wheel-variants become mainstream
 def _import_versioned_module() -> None:
     import importlib
+    import pathlib
+    import sys
 
     from cuda import bindings
 
@@ -15,13 +18,13 @@ def _import_versioned_module() -> None:
         raise ImportError("cuda.bindings 12.x or 13.x must be installed")
 
     subdir = f"cu{cuda_major}"
-    try:
-        versioned_mod = importlib.import_module(f".{subdir}", __package__)
-        # Import all symbols from the module
-        globals().update(versioned_mod.__dict__)
-    except ImportError:
-        # This is not a wheel build, but a conda or local build, do nothing
-        pass
+    versioned_dir = pathlib.Path(__file__).parent / subdir
+    # This is a wheel build with relevant modules in cuda/core/cu<cuda_major>
+    # directory. Let's add it to module path so imports work as expected.
+    # cuda.core.cu<cuda_major> is not meant to behave as a module itself, and
+    # does not belong in sys.modules
+    if versioned_dir.is_dir():
+        __path__.append(str(versioned_dir))
 
 
 _import_versioned_module()

One other approach could be manipulating __dict__ values too:

diff --git a/cuda_core/cuda/core/__init__.py b/cuda_core/cuda/core/__init__.py
index dc6fefdffe..d536fd39a8 100644
--- a/cuda_core/cuda/core/__init__.py
+++ b/cuda_core/cuda/core/__init__.py
@@ -7,6 +7,7 @@ from cuda.core._version import __version__
 
 def _import_versioned_module() -> None:
     import importlib
+    import sys
 
     from cuda import bindings
 
@@ -18,7 +19,13 @@ def _import_versioned_module() -> None:
     try:
         versioned_mod = importlib.import_module(f".{subdir}", __package__)
         # Import all symbols from the module
+        core_mod_name = __name__
+        versioned_mod_name = versioned_mod.__name__
         globals().update(versioned_mod.__dict__)
+        globals()["__name__"] = core_mod_name
+
+        if versioned_mod_name in sys.modules:
+            del sys.modules[versioned_mod_name]
     except ImportError:
         # This is not a wheel build, but a conda or local build, do nothing
         pass

@acosmicflamingo

Copy link
Copy Markdown
Contributor Author

@mdboom I opted for the cleaner approach, but I do have a solution that mutates globals() I can push if it's preferred. Can you please run /ok to test b3717d4fc7ad97e513d6f433a6e77bf1ee047a50 when you have a chance?

@rparolin
rparolin requested a review from mdboom July 23, 2026 23:59
@acosmicflamingo acosmicflamingo changed the title [WIP] Address duplicate import issue found in pypi-installed cuda-core packages Address duplicate import issue found in pypi-installed cuda-core packages Jul 24, 2026
@acosmicflamingo
acosmicflamingo marked this pull request as ready for review July 24, 2026 18:08
@mdboom

mdboom commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

/ok to test b3717d4

@acosmicflamingo

Copy link
Copy Markdown
Contributor Author

Whoops, getting this error:

E   SyntaxError: import * only allowed at module level

Don't know why I didn't see it locally, I'll push a fix in a few hours when I am in front of my keyboard

@acosmicflamingo

Copy link
Copy Markdown
Contributor Author

Given the context of the test and issue, seems like it would be acceptable to just move the import to module level instead of emulate the same behavior within the specific test.

@mdboom could you now please run /ok to test 3922d6d37b92f5d747a66f0d917ac97869bd9a61 for me?

@mdboom

mdboom commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

/ok to test 3922d6d

@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.

I understand the challenge with reproducing locally.

Unfortunately, the test currently passes without your changes to __init__.py applied, so we will need some better way to detect the error condition and confirm this change is actually working.

@acosmicflamingo

Copy link
Copy Markdown
Contributor Author

I understand the challenge with reproducing locally.

Unfortunately, the test currently passes without your changes to __init__.py applied, so we will need some better way to detect the error condition and confirm this change is actually working.

Sounds good! This bug is certainly a slippery one compared to others; I'll try and figure out how to go about it locally and not waste more CI resources.

@acosmicflamingo

acosmicflamingo commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

Trying to take a crack at this again. I am able to reproduce the issue locally using a pip-installed cuda-python wheel package, and just pushed a change that reverts all changes and ensures the test only runs when a module like cu13 exists (and moved importing cuda.core to the tested function itself in the off-chance pytest was doing something wonky importing at module level the last time CI was run). I expect to see the wheel runs fail in the same way that it fails for me locally because the __init__.py fix isn't present, but we will see...

@mdboom mind running /ok to test 5d2520545ee6d312f358ac991fe81d21fa7e437b for me please?

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

Labels

cuda.core Everything related to the cuda.core module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Relative imports from Cython files in cuda_core cause duplicate imports

2 participants