Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 75 additions & 7 deletions bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
'include', # load and jit a header file
'c_include', # load and jit a C header file
'load_library', # load a shared library
'nullptr', # unique pointer representing NULL

Check failure on line 43 in bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (F405)

bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py:43:5: F405 `nullptr` may be undefined, or defined from star imports
'sizeof', # size of a C++ type
'typeid', # typeid of a C++ type
'multi', # helper for multiple inheritance
Expand All @@ -64,27 +64,27 @@
ispypy = False

from . import _typemap
from ._version import __version__

Check failure on line 67 in bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (F401)

bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py:67:23: F401 `._version.__version__` imported but unused; consider removing, adding to `__all__`, or using a redundant alias help: Add unused import `__version__` to __all__

# import separately instead of in the above try/except block for easier to
# understand tracebacks
if ispypy:
from ._pypy_cppyy import *

Check failure on line 72 in bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (F403)

bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py:72:5: F403 `from ._pypy_cppyy import *` used; unable to detect undefined names
else:
from ._cpython_cppyy import *

Check failure on line 74 in bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (F403)

bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py:74:5: F403 `from ._cpython_cppyy import *` used; unable to detect undefined names


#- allow importing from gbl --------------------------------------------------
sys.modules['cppyy.gbl'] = gbl

Check failure on line 78 in bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (F405)

bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py:78:28: F405 `gbl` may be undefined, or defined from star imports
sys.modules['cppyy.gbl.std'] = gbl.std

Check failure on line 79 in bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (F405)

bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py:79:32: F405 `gbl` may be undefined, or defined from star imports


#- external typemap ----------------------------------------------------------
_typemap.initialize(_backend) # also creates (u)int8_t mapper

Check failure on line 83 in bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (F405)

bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py:83:21: F405 `_backend` may be undefined, or defined from star imports

try:
gbl.std.int8_t = gbl.int8_t # ensures same _integer_ type

Check failure on line 86 in bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (F405)

bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py:86:23: F405 `gbl` may be undefined, or defined from star imports

Check failure on line 86 in bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (F405)

bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py:86:5: F405 `gbl` may be undefined, or defined from star imports
gbl.std.uint8_t = gbl.uint8_t

Check failure on line 87 in bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (F405)

bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py:87:5: F405 `gbl` may be undefined, or defined from star imports
except (AttributeError, TypeError):
pass

Expand Down Expand Up @@ -145,25 +145,93 @@
obj = self.cls(*args)
return self.ptrcls[self.cls](obj) # C++ takes ownership

# The introspection trick above only works for class types the smart pointer can
# adopt from a Python-owned object. An arithmetic element type (int, double, ...)
# has no such object, so it needs a heap-allocated scalar. We test for *arithmetic*
# rather than *fundamental* types because std::is_fundamental also matches 'void'
# and 'std::nullptr_t', neither of which can back a 'new T{}' scalar.
#
# _py_arithmetic maps the Python numeric builtins onto a C++ spelling by convention
# (Python's int is arbitrary-precision, bool subclasses int); they carry no
# __cpp_name__ to query, so the mapping has to be explicit.
_py_arithmetic = {int: 'int', float: 'float', bool: 'bool'}

def _as_arithmetic(cls):
"""Return the C++ type name if 'cls' denotes an arithmetic type, else None.

'cls' may be a Python builtin type (int/float/bool), a string naming a C++
type, or a cppyy type proxy (e.g. cppyy.gbl.int)."""
name = _py_arithmetic.get(cls)
if name is not None:
return name
name = cls if isinstance(cls, str) else getattr(cls, '__cpp_name__', None)
if name is None:
return None
# Let C++ be the source of truth: std::is_arithmetic resolves canonical
# spellings and typedefs (size_t, Int_t, ...) alike, with no hand-kept list to
# drift out of sync. cppyy caches the trait instantiation itself.
#
# A name that is no resolvable type (e.g. a typo) makes the trait lookup raise;
# treat that as "not arithmetic" so it falls through to the class path, which
# reports the bad name itself instead of leaking std::is_arithmetic<...>.
try:
is_arith = bool(gbl.std.is_arithmetic[name].value)
except Exception:
is_arith = False
return name if is_arith else None

# Helpers that build a smart pointer to a heap-allocated scalar. They are
# deliberately *non-variadic*: instantiating std::make_unique/make_shared for an
# arithmetic type makes the interpreter JIT a call wrapper whose variadic Args...
# pack is mis-desugared on some platforms (macOS with C++ modules) into invalid
# code like 'make_unique<int, 0>'; these single-argument templates avoid it.
# See https://github.com/root-project/root/issues/19122.
#
# Each body static_asserts T is arithmetic, mirroring the Python-side
# _as_arithmetic() gate (class types take the adopt path instead). The assert is
# in the body, not the signature: an extra template parameter is exactly what
# triggers the mis-desugaring above.
_scalar_makers = {}
def _scalar_maker(name):
maker = _scalar_makers.get(name)
if maker is None:
if not _scalar_makers:
cppdef("""namespace __cppyy_internal {
template <class T> std::unique_ptr<T> make_unique_scalar() { static_assert(std::is_arithmetic_v<T>, "scalar maker requires an arithmetic type"); return std::unique_ptr<T>(new T{}); }
template <class T> std::unique_ptr<T> make_unique_scalar(T v) { static_assert(std::is_arithmetic_v<T>, "scalar maker requires an arithmetic type"); return std::unique_ptr<T>(new T(v)); }
template <class T> std::shared_ptr<T> make_shared_scalar() { static_assert(std::is_arithmetic_v<T>, "scalar maker requires an arithmetic type"); return std::shared_ptr<T>(new T{}); }
template <class T> std::shared_ptr<T> make_shared_scalar(T v) { static_assert(std::is_arithmetic_v<T>, "scalar maker requires an arithmetic type"); return std::shared_ptr<T>(new T(v)); }
}""")
Comment thread
vepadulano marked this conversation as resolved.
maker = getattr(gbl.__cppyy_internal, name)
_scalar_makers[name] = maker
return maker

class make_smartptr(object):
__slots__ = ['ptrcls', 'maker']
def __init__(self, ptrcls, maker):
self.ptrcls = ptrcls
self.maker = maker
__slots__ = ['ptrcls', 'maker', 'scalarname']
def __init__(self, ptrcls, maker, scalarname):
self.ptrcls = ptrcls
self.maker = maker
self.scalarname = scalarname
def __call__(self, ptr):
return py_make_smartptr(type(ptr), self.ptrcls)(ptr)
def __getitem__(self, cls):
# Arithmetic element types: build from a heap scalar (see _scalar_maker).
name = _as_arithmetic(cls)
if name is not None:
return _scalar_maker(self.scalarname)[name]
# Class types (cppyy proxies or Python classes, incl. Python-derived):
# construct in Python and let the smart pointer adopt the object.
try:
if not cls.__module__ == int.__module__:
return py_make_smartptr(cls, self.ptrcls)
except AttributeError:
pass
if isinstance(cls, str) and not cls in ('int', 'float'):
if isinstance(cls, str):
return py_make_smartptr(getattr(gbl, cls), self.ptrcls)
return self.maker[cls]

gbl.std.make_shared = make_smartptr(gbl.std.shared_ptr, gbl.std.make_shared)
gbl.std.make_unique = make_smartptr(gbl.std.unique_ptr, gbl.std.make_unique)
gbl.std.make_shared = make_smartptr(gbl.std.shared_ptr, gbl.std.make_shared, 'make_shared_scalar')
gbl.std.make_unique = make_smartptr(gbl.std.unique_ptr, gbl.std.make_unique, 'make_unique_scalar')
del make_smartptr


Expand Down
4 changes: 1 addition & 3 deletions bindings/pyroot/cppyy/cppyy/test/test_cpp11features.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sys, pytest, os
from pytest import mark, raises
from support import setup_make, ispypy, IS_MAC_ARM, IS_WINDOWS
from support import setup_make, ispypy


test_dct = "cpp11features_cxx"
Expand Down Expand Up @@ -438,7 +438,6 @@ def test14_shared_ptr_passing(self):
gc.collect()
assert TestSmartPtr.s_counter == 0

@mark.xfail(strict=True, condition=IS_WINDOWS | IS_MAC_ARM, reason='ValueError: Could not find "make_unique<int>"')
def test15_unique_ptr_template_deduction(self):
"""Argument type deduction with std::unique_ptr"""

Expand All @@ -458,7 +457,6 @@ def test15_unique_ptr_template_deduction(self):
with raises(ValueError): # not an RValue
cppyy.gbl.UniqueTempl.returnptr[int](uptr_in)

@mark.xfail(strict=True, condition=IS_WINDOWS | IS_MAC_ARM, reason='TypeError: Could not find "make_unique<int>"')
def test16_unique_ptr_moves(self):
"""std::unique_ptr requires moves"""

Expand Down
Loading