Skip to content

Commit a61cb05

Browse files
cpcloudleofang
andauthored
Make Linker.backend a classmethod (#1910)
* feat(core): convert Linker.backend from property to classmethod Allows querying the linking backend without constructing a Linker instance — useful for dispatching on input format (PTX vs. LTOIR) before linking. Updates existing call sites (Program init, test_linker) to use the new invocation form Linker.backend(). * test(core): add tests for Linker.backend classmethod Covers classmethod invocation (Linker.backend() without an instance), memoisation flag handling, probe-on-first-use, and non-property attribute semantics. * docs(core): add 1.0.0 release notes for Linker.backend change Breaking change: Linker.backend is now a classmethod, so call sites must use parens: Linker.backend(). --------- Co-authored-by: Leo Fang <leof@nvidia.com>
1 parent 2a2186c commit a61cb05

4 files changed

Lines changed: 63 additions & 6 deletions

File tree

cuda_core/cuda/core/_linker.pyx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,23 @@ cdef class Linker:
168168
else:
169169
return as_py(self._culink_handle)
170170

171-
@property
172-
def backend(self) -> CompilerBackendType:
173-
"""Return this Linker instance's underlying :class:`CompilerBackendType`."""
174-
return CompilerBackendType.NVJITLINK if self._use_nvjitlink else CompilerBackendType.DRIVER
171+
@classmethod
172+
def which_backend(cls) -> CompilerBackendType:
173+
"""Return which linking backend will be used.
174+
175+
Returns :attr:`~CompilerBackendType.NVJITLINK` when the nvJitLink
176+
library is available and meets the minimum version requirement,
177+
otherwise :attr:`~CompilerBackendType.DRIVER`.
178+
179+
.. note::
180+
181+
Prefer letting :class:`Linker` decide. Query ``which_backend()``
182+
only when you need to dispatch based on input format (for
183+
example: choose PTX vs. LTOIR before constructing a
184+
``Linker``). The returned value names an implementation
185+
detail whose support matrix may shift across CTK releases.
186+
"""
187+
return CompilerBackendType.DRIVER if _decide_nvjitlink_or_driver() else CompilerBackendType.NVJITLINK
175188

176189

177190
# =============================================================================

cuda_core/cuda/core/_program.pyx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,7 @@ cdef inline int Program_init(Program self, object code, str code_type, object op
785785
self._linker = Linker(
786786
ObjectCode._init(code_bytes, code_type), options=_translate_program_options(options)
787787
)
788-
self._backend = str(self._linker.backend)
788+
self._backend = str(Linker.which_backend())
789789

790790
elif code_type == "nvvm":
791791
_get_nvvm_module() # Validate NVVM availability

cuda_core/docs/source/release/1.0.0-notes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ Breaking changes
120120
``CUgraphConditionalHandle`` value. Previously, ``.handle`` had to be
121121
extracted explicitly.
122122

123+
- :meth:`Linker.which_backend` is now a classmethod instead of the former
124+
``backend`` instance property. Call sites must use ``Linker.which_backend()``
125+
(with parentheses) instead of ``linker.backend``. This allows querying the
126+
linking backend without constructing a ``Linker`` instance — for example, to
127+
choose between PTX and LTOIR input before linking.
128+
123129
- :attr:`DeviceMemoryResource.peer_accessible_by` now returns a
124130
:class:`collections.abc.MutableSet` of :obj:`~_device.Device` objects instead
125131
of a sorted ``tuple[int, ...]``. The property setter is unchanged.

cuda_core/tests/test_linker.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#
33
# SPDX-License-Identifier: Apache-2.0
44

5+
import inspect
6+
57
import pytest
68

79
from cuda.core import Device, Linker, LinkerOptions, Program, ProgramOptions, _linker
@@ -92,7 +94,7 @@ def test_linker_init(compile_ptx_functions, options):
9294
linker = Linker(*compile_ptx_functions, options=options)
9395
object_code = linker.link("cubin")
9496
assert isinstance(object_code, ObjectCode)
95-
assert linker.backend == ("driver" if is_culink_backend else "nvJitLink")
97+
assert Linker.which_backend() == ("driver" if is_culink_backend else "nvJitLink")
9698

9799

98100
def test_linker_init_invalid_arch(compile_ptx_functions):
@@ -242,3 +244,39 @@ def test_linker_options_nvjitlink_options_as_str():
242244
assert f"-arch={ARCH}" in options
243245
assert "-g" in options
244246
assert "-lineinfo" in options
247+
248+
249+
class TestWhichBackendClassmethod:
250+
def test_which_backend_returns_nvjitlink(self, monkeypatch):
251+
monkeypatch.setattr(_linker, "_use_nvjitlink_backend", True)
252+
assert Linker.which_backend() == "nvJitLink"
253+
254+
def test_which_backend_returns_driver(self, monkeypatch):
255+
monkeypatch.setattr(_linker, "_use_nvjitlink_backend", False)
256+
assert Linker.which_backend() == "driver"
257+
258+
def test_which_backend_invokes_probe_when_not_memoised(self, monkeypatch):
259+
monkeypatch.setattr(_linker, "_use_nvjitlink_backend", None)
260+
called = []
261+
262+
def fake_decide():
263+
called.append(True)
264+
return False # False = not falling back to driver = nvJitLink
265+
266+
monkeypatch.setattr(_linker, "_decide_nvjitlink_or_driver", fake_decide)
267+
result = Linker.which_backend()
268+
assert result == "nvJitLink"
269+
assert called, "_decide_nvjitlink_or_driver was not called"
270+
271+
def test_which_backend_is_classmethod(self):
272+
attr = inspect.getattr_static(Linker, "which_backend")
273+
assert isinstance(attr, classmethod)
274+
275+
def test_which_backend_is_not_property(self):
276+
"""which_backend is a classmethod, not a property.
277+
278+
This is an intentional breaking change from the prior ``backend`` property API.
279+
All call sites must use parens: ``Linker.which_backend()``.
280+
"""
281+
attr = inspect.getattr_static(Linker, "which_backend")
282+
assert not isinstance(attr, property)

0 commit comments

Comments
 (0)