Skip to content

[BUG]: Tab completion with DeviceMemoryResource is broken #2053

Closed

Description

@leofang

Is this a duplicate?

Type of Bug

Runtime Error

Component

cuda.core

Describe the bug

Tab completion is not prompting anything for the MR's method:

mr.<tab>

git bisect tells me the offending commit is 8b55a6c

How to Reproduce

  • build cuda-core locally
  • run this script via python -I test_mr.py to enter the Python REPL:
from cuda.core import Device
from cuda.core import DeviceMemoryResource
dev = Device(0)
dev.set_current()
mr = DeviceMemoryResource(dev)
  • hit tab completion for mr.

Expected behavior

it should just work

Operating System

No response

nvidia-smi output

No response

Activity

  1. added this to the cuda.core v1.0.0 milestone on May 8, 2026
  2. added
    bugSomething isn't working
    P0High priority - Must do!
    cuda.coreEverything related to the cuda.core module
    on May 8, 2026
  3. leofang commented on May 8, 2026

    @leofang
    MemberAuthor

    Tab completion for cuda.core.system.Device is broken too.

  4. leofang commented on May 8, 2026

    @leofang
    MemberAuthor

    Root cause analysis

    In CPython 3.12+, rlcompleter.Completer.attr_matches() has a guard to skip evaluating Python properties (introduced by python/cpython#88915 / python/cpython#27401, for bpo-44752):

    if isinstance(getattr(type(thisobject), word, None), property):
        matches.append(match)   # skip getattr(obj, word)
        continue
    if (value := getattr(thisobject, word, None)) is not None:
        ...

    However, Cython cdef class properties generate getset_descriptor objects, not Python property objects. The isinstance(..., property) guard does not fire for them. When getattr(obj, word, None) is called, it only catches AttributeError — any other exception propagates uncaught, crashing attr_matches() entirely and producing zero completions.

    This was flagged as a concern before the naming audit landed — see #1945 (comment):

    [methods] are side-effect free with getattr/hasattr (properties are not)

    The offending commit (8b55a6c) converted get_allocation_handle() from a method to a @property. As a method, getattr(mr, 'get_allocation_handle') returned the bound method object (no side effects). As a property, getattr(mr, 'allocation_handle') invokes the getter, which raises RuntimeError for non-IPC resources — and crashes the completer.

    Note

    This is a known upstream CPython issue: python/cpython#112821 ("Reconsider BPO 44752 changes in rlcompleter since exceptions in descriptors cause auto completion to fail"). Filed Dec 2023, still open.

    Impact audit

    I audited every Cython @property in cuda/core/ that can raise a non-AttributeError exception. Only Cython cdef class properties (getset_descriptor) are affected — pure Python property descriptors are handled correctly by the rlcompleter guard.

    Tab completion is effectively broken for all classes listed below. The triggering conditions are not edge cases — they are the default or common states for these objects (non-IPC is the default for memory resources and events; NVML queries fail on many GPU models; contexts get closed during normal lifecycle).

    IPC-related properties (non-IPC is the default — affects most users):

    Class Property Exception Condition
    DeviceMemoryResource allocation_handle RuntimeError non-IPC MR (the default)
    PinnedMemoryResource allocation_handle RuntimeError non-IPC MR (the default)
    Event ipc_descriptor RuntimeError non-IPC event (the default)
    Buffer ipc_descriptor RuntimeError buffer from non-IPC MR

    NVML-backed properties (raises on GPUs that don't support the query):

    Class Property Exception Condition
    system.Device attributes NotSupportedError GPU doesn't support NVML query
    system.Device is_auto_boosted_clocks_enabled NotSupportedError GPU doesn't support feature
    system.Device is_c2c_enabled NotSupportedError GPU doesn't support C2C
    system.Device numa_node_id NotSupportedError GPU doesn't expose NUMA node

    Other properties:

    Class Property Exception Condition
    Context resources RuntimeError closed context
    DeviceResources workqueue RuntimeError hardware-dependent
    GraphBuilder is_building NotImplementedError unknown capture status
    system.Device current_clock_event_reasons ValueError unknown clock event bit
    system.Device supported_clock_event_reasons ValueError unknown clock event bit

    Not affected (abstract base, never instantiated directly):
    MemoryResource.{is_device_accessible, is_host_accessible, device_id} — TypeError, but only hit on bare MemoryResource instances.

    Not affected (pure Python class, rlcompleter guard works):
    LegacyPinnedMemoryResource.device_id — RuntimeError, but isinstance(desc, property) is True, so rlcompleter skips it.

    Possible solutions

    1. Upstream CPython fix (Reconsider gh-88915 changes rlcompleter since exceptions in descriptors cause auto completion to fail python/cpython#112821): Extend the rlcompleter guard to handle getset_descriptor (or restore the old try/except Exception around getattr). This would fix all Cython extension types, not just cuda.core.

    2. cuda-python workaround — raise AttributeError instead:
      getattr(obj, name, None) catches AttributeError, so changing raise RuntimeError(...) to raise AttributeError(...) in the affected property getters is a minimal one-line-per-property fix. Downside: semantically misleading (the attribute exists, it's just unusable in the current state), and CPython appends a "Did you mean: ...?" suggestion that can be confusing.

    3. cuda-python workaround — return None instead of raising:
      Properties like allocation_handle and ipc_descriptor could return None when the feature is not enabled. Callers already need to check is_ipc_enabled first.

    4. cuda-python workaround — revert to methods:
      Keep get_allocation_handle() / get_ipc_descriptor() as methods. Methods are never evaluated by getattr() during tab completion.

    5. cuda-python workaround — catch NVML errors in system.Device:
      Wrap NVML-backed properties in try/except and return a sentinel (e.g., None or a "not supported" marker) instead of letting NotSupportedError propagate.

    -- Leo's bot

  5. mdboom commented on May 8, 2026

    @mdboom
    Contributor

    Confirming that tab completion is /not/ broken in IPython:

    Image

    It is also /not/ broken in Jupyter:

    Image

    EDIT: And everything is available in this list if you scroll down, just not visible in the screenshot.

    So it seems confined to vanilla Python REPL tab completion specifically (which one could argue is the least important).

  6. mdboom commented on May 8, 2026

    @mdboom
    Contributor

    Confirmed this affects Python 3.10 -> 3.14, so it's not just the "new" REPL introduced in 3.13.

  7. self-assigned this
    on May 8, 2026
  8. mdboom commented on May 8, 2026

    @mdboom
    Contributor

    My assessment of the agent's suggestions:

    Upstream CPython fix (python/cpython#112821): Extend the rlcompleter guard to handle getset_descriptor (or restore the old try/except Exception around getattr). This would fix all Cython extension types, not just cuda.core.

    This is a non-starter. Even if we got the bugfix in, it would be ~5 years until we could rely on it.

    cuda-python workaround — raise AttributeError instead:
    getattr(obj, name, None) catches AttributeError, so changing raise RuntimeError(...) to raise AttributeError(...) in the affected property getters is a minimal one-line-per-property fix. Downside: semantically misleading (the attribute exists, it's just unusable in the current state), and CPython appends a "Did you mean: ...?" suggestion that can be confusing.

    This would change the semantics of the property, and the "Did you mean" would be incredibly misleading to users. They didn't misspell -- they used a feature that semantically should raise some sort of runtime error. It should be fine for us to design that way.

    cuda-python workaround — return None instead of raising:
    Properties like allocation_handle and ipc_descriptor could return None when the feature is not enabled. Callers already need to check is_ipc_enabled first.

    This places the burden of error checking on our users. It's deeply un-Pythonic to require error code checks. We are even trying to move away from that in cuda_bindings.

    cuda-python workaround — revert to methods:
    Keep get_allocation_handle() / get_ipc_descriptor() as methods. Methods are never evaluated by getattr() during tab completion.

    This is probably the most reasonable thing to do on this list. If we went this way, we should add some sort of a test harness that makes sure we stick to the convention that "any property that can raise an exception must be a getter instead", because we will forget. It seems frustrating to design an API around the limitations of tab completion, though, so I'm still not a huge fan.

    cuda-python workaround — catch NVML errors in system.Device:
    Wrap NVML-backed properties in try/except and return a sentinel (e.g., None or a "not supported" marker) instead of letting NotSupportedError propagate.

    This is basically the same as #3 above.

  9. linked a pull request that will close this issueFix tab completion #2055on May 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P0High priority - Must do!bugSomething isn't workingcuda.coreEverything related to the cuda.core moduletriageNeeds the team's attention

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions