Skip to content

Commit 2801e98

Browse files
committed
Use Python properties for Cython descriptors
1 parent 9cc3420 commit 2801e98

10 files changed

Lines changed: 98 additions & 29 deletions

File tree

cuda_core/cuda/core/_context.pyx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,7 @@ cdef class Context:
7777
return False
7878
return get_context_green_ctx(self._h_context).get() != NULL
7979

80-
@property
81-
def resources(self) -> DeviceResources:
80+
def _get_resources(self) -> DeviceResources:
8281
"""Query the hardware resources provisioned for this context.
8382

8483
For green contexts, returns the resources this context was created
@@ -91,6 +90,8 @@ cdef class Context:
9190
raise RuntimeError("Cannot query resources on a closed context")
9291
return DeviceResources._init_from_ctx(self._h_context, self._device_id)
9392

93+
resources = property(_get_resources)
94+
9495
def create_stream(self, options: StreamOptions | None = None):
9596
"""Create a new stream bound to this green context.
9697

cuda_core/cuda/core/_device_resources.pyx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -622,8 +622,7 @@ cdef class DeviceResources:
622622
self._query_sm(&res)
623623
return SMResource._from_dev_resource(res, self._device_id)
624624

625-
@property
626-
def workqueue(self) -> WorkqueueResource:
625+
def _get_workqueue(self) -> WorkqueueResource:
627626
"""Return the :obj:`WorkqueueResource` for this device or context."""
628627
_check_green_ctx_support()
629628
_check_workqueue_support()
@@ -678,3 +677,5 @@ cdef class DeviceResources:
678677
raise RuntimeError(
679678
"WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings"
680679
)
680+
681+
workqueue = property(_get_workqueue)

cuda_core/cuda/core/_event.pyx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,7 @@ cdef class Event:
197197
def __repr__(self) -> str:
198198
return f"<Event handle={as_intptr(self._h_event):#x}>"
199199

200-
@property
201-
def ipc_descriptor(self) -> IPCEventDescriptor:
200+
def _get_ipc_descriptor(self) -> IPCEventDescriptor:
202201
"""Descriptor for sharing this event with other processes."""
203202
if self._ipc_descriptor is not None:
204203
return self._ipc_descriptor
@@ -211,6 +210,8 @@ cdef class Event:
211210
self._ipc_descriptor = IPCEventDescriptor._init(data_b, get_event_is_blocking_sync(self._h_event))
212211
return self._ipc_descriptor
213212

213+
ipc_descriptor = property(_get_ipc_descriptor)
214+
214215
@classmethod
215216
def from_ipc_descriptor(cls, ipc_descriptor: IPCEventDescriptor) -> Event:
216217
"""Import an event that was exported from another process.

cuda_core/cuda/core/_memory/_buffer.pyx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,13 +190,14 @@ cdef class Buffer:
190190
"""
191191
return _ipc.Buffer_from_ipc_descriptor(cls, mr, ipc_descriptor, stream)
192192

193-
@property
194-
def ipc_descriptor(self) -> IPCBufferDescriptor:
193+
def _get_ipc_descriptor(self) -> IPCBufferDescriptor:
195194
"""Descriptor for sharing this buffer with other processes."""
196195
if self._ipc_data is None:
197196
self._ipc_data = IPCDataForBuffer(_ipc.Buffer_get_ipc_descriptor(self), False)
198197
return self._ipc_data.ipc_descriptor
199198

199+
ipc_descriptor = property(_get_ipc_descriptor)
200+
200201
def close(self, stream: Stream | GraphBuilder | None = None):
201202
"""Deallocate this buffer asynchronously on the given stream.
202203

cuda_core/cuda/core/_memory/_device_memory_resource.pyx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,7 @@ cdef class DeviceMemoryResource(_MemPool):
190190
mr._dev_id = Device(device_id).device_id
191191
return mr
192192

193-
@property
194-
def allocation_handle(self) -> IPCAllocationHandle:
193+
def _get_allocation_handle(self) -> IPCAllocationHandle:
195194
"""Shareable handle for this memory pool (requires IPC).
196195

197196
The handle can be used to share the memory pool with other processes.
@@ -201,6 +200,8 @@ cdef class DeviceMemoryResource(_MemPool):
201200
raise RuntimeError("Memory resource is not IPC-enabled")
202201
return self._ipc_data._alloc_handle
203202

203+
allocation_handle = property(_get_allocation_handle)
204+
204205
@property
205206
def device_id(self) -> int:
206207
"""The associated device ordinal."""

cuda_core/cuda/core/_memory/_pinned_memory_resource.pyx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,7 @@ cdef class PinnedMemoryResource(_MemPool):
148148
_ipc.MP_from_allocation_handle(cls, alloc_handle))
149149
return mr
150150

151-
@property
152-
def allocation_handle(self) -> IPCAllocationHandle:
151+
def _get_allocation_handle(self) -> IPCAllocationHandle:
153152
"""Shareable handle for this memory pool (requires IPC).
154153

155154
The handle can be used to share the memory pool with other processes.
@@ -159,6 +158,8 @@ cdef class PinnedMemoryResource(_MemPool):
159158
raise RuntimeError("Memory resource is not IPC-enabled")
160159
return self._ipc_data._alloc_handle
161160

161+
allocation_handle = property(_get_allocation_handle)
162+
162163
@property
163164
def device_id(self) -> int:
164165
"""Return -1. Pinned memory is host memory and is not associated with a specific device."""

cuda_core/cuda/core/graph/_graph_builder.pyx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,7 @@ class GraphBuilder:
300300
handle_return(driver.cuStreamBeginCapture(self._mnff.stream.handle, capture_mode))
301301
return self
302302

303-
@property
304-
def is_building(self) -> bool:
303+
def _get_is_building(self) -> bool:
305304
"""Returns True if the graph builder is currently building."""
306305
capture_status = handle_return(driver.cuStreamGetCaptureInfo(self._mnff.stream.handle))[0]
307306
if capture_status == driver.CUstreamCaptureStatus.CU_STREAM_CAPTURE_STATUS_NONE:
@@ -315,6 +314,8 @@ class GraphBuilder:
315314
else:
316315
raise NotImplementedError(f"Unsupported capture status type received: {capture_status}")
317316

317+
is_building = property(_get_is_building)
318+
318319
def end_building(self) -> GraphBuilder:
319320
"""Ends the building process."""
320321
if not self.is_building:

cuda_core/cuda/core/system/_device.pyx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -259,15 +259,16 @@ cdef class Device:
259259
"""
260260
return self.pci_info.bus_id
261261

262-
@property
263-
def numa_node_id(self) -> int:
262+
def _get_numa_node_id(self) -> int:
264263
"""
265264
The NUMA node of the given GPU device.
266265

267266
This only applies to platforms where the GPUs are NUMA nodes.
268267
"""
269268
return nvml.device_get_numa_node_id(self._handle)
270269

270+
numa_node_id = property(_get_numa_node_id)
271+
271272
@property
272273
def arch(self) -> DeviceArch:
273274
"""
@@ -332,13 +333,14 @@ cdef class Device:
332333
"""
333334
return nvml.device_get_minor_number(self._handle)
334335

335-
@property
336-
def is_c2c_enabled(self) -> bool:
336+
def _get_is_c2c_enabled(self) -> bool:
337337
"""
338338
Whether the C2C (Chip-to-Chip) mode is enabled for this device.
339339
"""
340340
return bool(nvml.device_get_c2c_mode_info_v(self._handle).is_c2c_enabled)
341341

342+
is_c2c_enabled = property(_get_is_c2c_enabled)
343+
342344
@property
343345
def is_persistence_mode_enabled(self) -> bool:
344346
"""
@@ -575,8 +577,7 @@ cdef class Device:
575577
"""
576578
return ClockInfo(self._handle, clock_type)
577579

578-
@property
579-
def is_auto_boosted_clocks_enabled(self) -> tuple[bool, bool]:
580+
def _get_is_auto_boosted_clocks_enabled(self) -> tuple[bool, bool]:
580581
"""
581582
Retrieve the current state of auto boosted clocks on a device.
582583

@@ -601,8 +602,9 @@ cdef class Device:
601602
current, default = nvml.device_get_auto_boosted_clocks_enabled(self._handle)
602603
return current == nvml.EnableState.FEATURE_ENABLED, default == nvml.EnableState.FEATURE_ENABLED
603604

604-
@property
605-
def current_clock_event_reasons(self) -> list[ClocksEventReasons]:
605+
is_auto_boosted_clocks_enabled = property(_get_is_auto_boosted_clocks_enabled)
606+
607+
def _get_current_clock_event_reasons(self) -> list[ClocksEventReasons]:
606608
"""
607609
Retrieves the current :obj:`~ClocksEventReasons`.
608610

@@ -619,8 +621,9 @@ cdef class Device:
619621
output_reasons.append(output_reason)
620622
return output_reasons
621623

622-
@property
623-
def supported_clock_event_reasons(self) -> list[ClocksEventReasons]:
624+
current_clock_event_reasons = property(_get_current_clock_event_reasons)
625+
626+
def _get_supported_clock_event_reasons(self) -> list[ClocksEventReasons]:
624627
"""
625628
Retrieves supported :obj:`~ClocksEventReasons` that can be returned by
626629
:meth:`get_current_clock_event_reasons`.
@@ -640,6 +643,8 @@ cdef class Device:
640643
output_reasons.append(output_reason)
641644
return output_reasons
642645

646+
supported_clock_event_reasons = property(_get_supported_clock_event_reasons)
647+
643648
##########################################################################
644649
# COOLER
645650
# See external class definitions in _cooler.pxi
@@ -655,8 +660,7 @@ cdef class Device:
655660
# DEVICE ATTRIBUTES
656661
# See external class definitions in _device_attributes.pxi
657662

658-
@property
659-
def attributes(self) -> DeviceAttributes:
663+
def _get_attributes(self) -> DeviceAttributes:
660664
"""
661665
:obj:`~_device.DeviceAttributes` object with various device attributes.
662666

@@ -665,6 +669,8 @@ cdef class Device:
665669
"""
666670
return DeviceAttributes(nvml.device_get_attributes_v2(self._handle))
667671

672+
attributes = property(_get_attributes)
673+
668674
#########################################################################
669675
# DISPLAY
670676

cuda_core/pixi.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,6 @@ numpy = "*"
186186
cuda-bindings = "*"
187187
cuda-pathfinder = "*"
188188

189-
[package.target.'python_version < "3.11"'.run-dependencies]
190-
"backports.strenum" = "*"
191-
192189
[target.linux.tasks.build-cython-tests]
193190
cmd = ["$PIXI_PROJECT_ROOT/tests/cython/build_tests.sh"]
194191

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
import inspect
6+
import types
7+
8+
import pytest
9+
10+
from cuda.core import (
11+
Buffer,
12+
Context,
13+
DeviceMemoryResource,
14+
DeviceResources,
15+
Event,
16+
PinnedMemoryResource,
17+
)
18+
from cuda.core.graph import GraphBuilder
19+
from cuda.core.system import CUDA_BINDINGS_NVML_IS_COMPATIBLE
20+
21+
_PYTHON_PROPERTIES = [
22+
pytest.param(DeviceMemoryResource, "allocation_handle", id="DeviceMemoryResource.allocation_handle"),
23+
pytest.param(PinnedMemoryResource, "allocation_handle", id="PinnedMemoryResource.allocation_handle"),
24+
pytest.param(Event, "ipc_descriptor", id="Event.ipc_descriptor"),
25+
pytest.param(Buffer, "ipc_descriptor", id="Buffer.ipc_descriptor"),
26+
pytest.param(Context, "resources", id="Context.resources"),
27+
pytest.param(DeviceResources, "workqueue", id="DeviceResources.workqueue"),
28+
pytest.param(GraphBuilder, "is_building", id="GraphBuilder.is_building"),
29+
]
30+
31+
if CUDA_BINDINGS_NVML_IS_COMPATIBLE:
32+
from cuda.core.system import Device as SystemDevice
33+
34+
_PYTHON_PROPERTIES.extend(
35+
[
36+
pytest.param(SystemDevice, "attributes", id="system.Device.attributes"),
37+
pytest.param(
38+
SystemDevice,
39+
"is_auto_boosted_clocks_enabled",
40+
id="system.Device.is_auto_boosted_clocks_enabled",
41+
),
42+
pytest.param(SystemDevice, "is_c2c_enabled", id="system.Device.is_c2c_enabled"),
43+
pytest.param(SystemDevice, "numa_node_id", id="system.Device.numa_node_id"),
44+
pytest.param(SystemDevice, "current_clock_event_reasons", id="system.Device.current_clock_event_reasons"),
45+
pytest.param(
46+
SystemDevice,
47+
"supported_clock_event_reasons",
48+
id="system.Device.supported_clock_event_reasons",
49+
),
50+
]
51+
)
52+
53+
54+
@pytest.mark.parametrize("cls, name", _PYTHON_PROPERTIES)
55+
def test_known_public_cython_properties_are_python_properties(cls, name):
56+
descriptor = inspect.getattr_static(cls, name)
57+
58+
assert isinstance(descriptor, property)
59+
assert not isinstance(descriptor, types.GetSetDescriptorType)

0 commit comments

Comments
 (0)