Skip to content

Commit 043669b

Browse files
authored
Merge pull request #484 from rwgk/uniform_init_disables
Apply `__new__` approach to disabling `__init__`
2 parents a52e562 + 201d3fc commit 043669b

File tree

10 files changed

+110
-59
lines changed

10 files changed

+110
-59
lines changed

cuda_core/cuda/core/experimental/_context.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ class ContextOptions:
1515
class Context:
1616
__slots__ = ("_handle", "_id")
1717

18-
def __init__(self):
19-
raise NotImplementedError("TODO")
18+
def __new__(self, *args, **kwargs):
19+
raise RuntimeError("Context objects cannot be instantiated directly. Please use Device or Stream APIs.")
2020

21-
@staticmethod
22-
def _from_ctx(obj, dev_id):
21+
@classmethod
22+
def _from_ctx(cls, obj, dev_id):
2323
assert isinstance(obj, driver.CUcontext)
24-
ctx = Context.__new__(Context)
24+
ctx = super().__new__(cls)
2525
ctx._handle = obj
2626
ctx._id = dev_id
2727
return ctx

cuda_core/cuda/core/experimental/_device.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ class DeviceProperties:
2222
Attributes are read-only and provide information about the device.
2323
"""
2424

25-
def __init__(self):
26-
raise RuntimeError("DeviceProperties should not be instantiated directly")
25+
def __new__(self, *args, **kwargs):
26+
raise RuntimeError("DeviceProperties cannot be instantiated directly. Please use Device APIs.")
2727

2828
__slots__ = ("_handle", "_cache")
2929

30-
def _init(handle):
31-
self = DeviceProperties.__new__(DeviceProperties)
30+
@classmethod
31+
def _init(cls, handle):
32+
self = super().__new__(cls)
3233
self._handle = handle
3334
self._cache = {}
3435
return self

cuda_core/cuda/core/experimental/_event.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,14 @@ def close(self):
6767
handle_return(driver.cuEventDestroy(self.handle))
6868
self.handle = None
6969

70-
__slots__ = ("__weakref__", "_mnff", "_timing_disabled", "_busy_waited")
70+
def __new__(self, *args, **kwargs):
71+
raise RuntimeError("Event objects cannot be instantiated directly. Please use Stream APIs (record).")
7172

72-
def __init__(self):
73-
raise NotImplementedError("directly creating an Event object can be ambiguous. Please call Stream.record().")
73+
__slots__ = ("__weakref__", "_mnff", "_timing_disabled", "_busy_waited")
7474

75-
@staticmethod
76-
def _init(options: Optional[EventOptions] = None):
77-
self = Event.__new__(Event)
75+
@classmethod
76+
def _init(cls, options: Optional[EventOptions] = None):
77+
self = super().__new__(cls)
7878
self._mnff = Event._MembersNeededForFinalize(self, None)
7979

8080
options = check_or_create_options(EventOptions, options, "Event options")

cuda_core/cuda/core/experimental/_module.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,14 @@ def _lazy_init():
4747

4848

4949
class KernelAttributes:
50-
def __init__(self):
51-
raise RuntimeError("KernelAttributes should not be instantiated directly")
50+
def __new__(self, *args, **kwargs):
51+
raise RuntimeError("KernelAttributes cannot be instantiated directly. Please use Kernel APIs.")
5252

5353
slots = ("_handle", "_cache", "_backend_version", "_loader")
5454

55-
def _init(handle):
56-
self = KernelAttributes.__new__(KernelAttributes)
55+
@classmethod
56+
def _init(cls, handle):
57+
self = super().__new__(cls)
5758
self._handle = handle
5859
self._cache = {}
5960

@@ -189,14 +190,14 @@ class Kernel:
189190

190191
__slots__ = ("_handle", "_module", "_attributes")
191192

192-
def __init__(self):
193-
raise RuntimeError("directly constructing a Kernel instance is not supported")
193+
def __new__(self, *args, **kwargs):
194+
raise RuntimeError("Kernel objects cannot be instantiated directly. Please use ObjectCode APIs.")
194195

195-
@staticmethod
196-
def _from_obj(obj, mod):
196+
@classmethod
197+
def _from_obj(cls, obj, mod):
197198
assert isinstance(obj, _kernel_ctypes)
198199
assert isinstance(mod, ObjectCode)
199-
ker = Kernel.__new__(Kernel)
200+
ker = super().__new__(cls)
200201
ker._handle = obj
201202
ker._module = mod
202203
ker._attributes = None
@@ -237,15 +238,15 @@ class ObjectCode:
237238
__slots__ = ("_handle", "_backend_version", "_code_type", "_module", "_loader", "_sym_map")
238239
_supported_code_type = ("cubin", "ptx", "ltoir", "fatbin")
239240

240-
def __init__(self):
241-
raise NotImplementedError(
242-
"directly creating an ObjectCode object can be ambiguous. Please either call Program.compile() "
243-
"or one of the ObjectCode.from_*() constructors"
241+
def __new__(self, *args, **kwargs):
242+
raise RuntimeError(
243+
"ObjectCode objects cannot be instantiated directly. "
244+
"Please use ObjectCode APIs (from_cubin, from_ptx) or Program APIs (compile)."
244245
)
245246

246-
@staticmethod
247-
def _init(module, code_type, *, symbol_mapping: Optional[dict] = None):
248-
self = ObjectCode.__new__(ObjectCode)
247+
@classmethod
248+
def _init(cls, module, code_type, *, symbol_mapping: Optional[dict] = None):
249+
self = super().__new__(cls)
249250
assert code_type in self._supported_code_type, f"{code_type=} is not supported"
250251
_lazy_init()
251252

cuda_core/cuda/core/experimental/_stream.py

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,37 @@ def close(self):
7171
self.owner = None
7272
self.handle = None
7373

74+
def __new__(self, *args, **kwargs):
75+
raise RuntimeError(
76+
"Stream objects cannot be instantiated directly. "
77+
"Please use Device APIs (create_stream) or other Stream APIs (from_handle)."
78+
)
79+
7480
__slots__ = ("__weakref__", "_mnff", "_nonblocking", "_priority", "_device_id", "_ctx_handle")
7581

76-
def __init__(self):
77-
raise NotImplementedError(
78-
"directly creating a Stream object can be ambiguous. Please either "
79-
"call Device.create_stream() or, if a stream pointer is already "
80-
"available from somewhere else, Stream.from_handle()"
81-
)
82+
@classmethod
83+
def _legacy_default(cls):
84+
self = super().__new__(cls)
85+
self._mnff = Stream._MembersNeededForFinalize(self, driver.CUstream(driver.CU_STREAM_LEGACY), None, True)
86+
self._nonblocking = None # delayed
87+
self._priority = None # delayed
88+
self._device_id = None # delayed
89+
self._ctx_handle = None # delayed
90+
return self
8291

83-
@staticmethod
84-
def _init(obj=None, *, options: Optional[StreamOptions] = None):
85-
self = Stream.__new__(Stream)
92+
@classmethod
93+
def _per_thread_default(cls):
94+
self = super().__new__(cls)
95+
self._mnff = Stream._MembersNeededForFinalize(self, driver.CUstream(driver.CU_STREAM_PER_THREAD), None, True)
96+
self._nonblocking = None # delayed
97+
self._priority = None # delayed
98+
self._device_id = None # delayed
99+
self._ctx_handle = None # delayed
100+
return self
101+
102+
@classmethod
103+
def _init(cls, obj=None, *, options: Optional[StreamOptions] = None):
104+
self = super().__new__(cls)
86105
self._mnff = Stream._MembersNeededForFinalize(self, None, None, False)
87106

88107
if obj is not None and options is not None:
@@ -295,22 +314,8 @@ def __cuda_stream__(self):
295314
return Stream._init(obj=_stream_holder())
296315

297316

298-
class _LegacyDefaultStream(Stream):
299-
def __init__(self):
300-
self._mnff = Stream._MembersNeededForFinalize(self, driver.CUstream(driver.CU_STREAM_LEGACY), None, True)
301-
self._nonblocking = None # delayed
302-
self._priority = None # delayed
303-
304-
305-
class _PerThreadDefaultStream(Stream):
306-
def __init__(self):
307-
self._mnff = Stream._MembersNeededForFinalize(self, driver.CUstream(driver.CU_STREAM_PER_THREAD), None, True)
308-
self._nonblocking = None # delayed
309-
self._priority = None # delayed
310-
311-
312-
LEGACY_DEFAULT_STREAM = _LegacyDefaultStream()
313-
PER_THREAD_DEFAULT_STREAM = _PerThreadDefaultStream()
317+
LEGACY_DEFAULT_STREAM = Stream._legacy_default()
318+
PER_THREAD_DEFAULT_STREAM = Stream._per_thread_default()
314319

315320

316321
def default_stream():

cuda_core/tests/test_context.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Copyright 2025 NVIDIA Corporation. All rights reserved.
2+
#
3+
# Please refer to the NVIDIA end user license agreement (EULA) associated
4+
# with this source code for terms and conditions that govern your use of
5+
# this software. Any use, reproduction, disclosure, or distribution of
6+
# this software and related documentation outside the terms of the EULA
7+
# is strictly prohibited.
8+
9+
import pytest
10+
11+
import cuda.core.experimental
12+
13+
14+
def test_context_init_disabled():
15+
with pytest.raises(RuntimeError, match=r"^Context objects cannot be instantiated directly\."):
16+
cuda.core.experimental._context.Context() # Ensure back door is locked.

cuda_core/tests/test_device.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,16 @@
1313
from cuda import cudart as runtime
1414
import pytest
1515

16+
import cuda.core.experimental
1617
from cuda.core.experimental import Device
1718
from cuda.core.experimental._utils import ComputeCapability, get_binding_version, handle_return
1819

1920

21+
def test_device_init_disabled():
22+
with pytest.raises(RuntimeError, match=r"^DeviceProperties cannot be instantiated directly\."):
23+
cuda.core.experimental._device.DeviceProperties() # Ensure back door is locked.
24+
25+
2026
@pytest.fixture(scope="module")
2127
def cuda_version():
2228
# binding availability depends on cuda-python version

cuda_core/tests/test_event.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,15 @@
88

99
import pytest
1010

11+
import cuda.core.experimental
1112
from cuda.core.experimental import Device, EventOptions
1213

1314

15+
def test_event_init_disabled():
16+
with pytest.raises(RuntimeError, match=r"^Event objects cannot be instantiated directly\."):
17+
cuda.core.experimental._event.Event() # Ensure back door is locked.
18+
19+
1420
@pytest.mark.parametrize("enable_timing", [True, False, None])
1521
def test_timing(init_cuda, enable_timing):
1622
options = EventOptions(enable_timing=enable_timing)

cuda_core/tests/test_module.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import pytest
1313

14+
import cuda.core.experimental
1415
from cuda.core.experimental import ObjectCode, Program, ProgramOptions, system
1516

1617
SAXPY_KERNEL = """
@@ -28,6 +29,21 @@
2829
"""
2930

3031

32+
def test_kernel_attributes_init_disabled():
33+
with pytest.raises(RuntimeError, match=r"^KernelAttributes cannot be instantiated directly\."):
34+
cuda.core.experimental._module.KernelAttributes() # Ensure back door is locked.
35+
36+
37+
def test_kernel_init_disabled():
38+
with pytest.raises(RuntimeError, match=r"^Kernel objects cannot be instantiated directly\."):
39+
cuda.core.experimental._module.Kernel() # Ensure back door is locked.
40+
41+
42+
def test_object_code_init_disabled():
43+
with pytest.raises(RuntimeError, match=r"^ObjectCode objects cannot be instantiated directly\."):
44+
ObjectCode() # Reject at front door.
45+
46+
3147
@pytest.fixture(scope="function")
3248
def get_saxpy_kernel(init_cuda):
3349
# prepare program

cuda_core/tests/test_stream.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
from cuda.core.experimental._utils import driver
1515

1616

17-
def test_stream_init():
18-
with pytest.raises(NotImplementedError):
19-
Stream()
17+
def test_stream_init_disabled():
18+
with pytest.raises(RuntimeError, match=r"^Stream objects cannot be instantiated directly\."):
19+
Stream() # Reject at front door.
2020

2121

2222
def test_stream_init_with_options(init_cuda):

0 commit comments

Comments
 (0)