Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump up CPyCppyy to latest upstream status #73

Merged
merged 27 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
61ce4ac
Initialize new PyTypeObject fields introduced in Python 3.13
guitargeek Oct 29, 2024
2b6fb00
Implement out-of-bounds handling for small char-based enums
aaronj0 Jan 20, 2025
af49626
No static gPythonizations variable to avoid initialization order fiasco
aaronj0 Jan 20, 2025
689c21e
recent numpy passes kwargs given to `np.array` to `__array__`, so sup…
wlav Nov 5, 2024
78471a7
remove Python C API methods deprecated by py3.13
wlav Nov 5, 2024
2de6e5f
remove spurious patch numbers in hex version checking
wlav Nov 5, 2024
9bfd6c9
recognize ctypes' types for py3.13
wlav Nov 7, 2024
4b83035
reduce state struct copied from ctypes to the types used only
wlav Nov 7, 2024
5366d28
handle "copy" argument if passed as a keyword to std::vector or llvie…
wlav Nov 7, 2024
7e6f932
fix clang warning due to changes in PyObject
silverweed Oct 4, 2024
c346752
look for types of default values both in local and global scope
wlav Nov 25, 2024
099c7fc
remove "TODO" as exceptions are already handled through a magic return
wlav Nov 26, 2024
d67ea5e
consistency in validity checking of pointer result
wlav Nov 26, 2024
35139d9
improve comment explaining the use of IsSTLIterator
wlav Nov 26, 2024
078561d
iterator executors need not set lifelines b/c the STLSequenceIter fro…
wlav Nov 26, 2024
7a19fd6
proper template scoping for shadowed names
aaronj0 Jan 20, 2025
70d2477
Represent arrays of ``signed char`` as low level views returning bytes
wlav Nov 26, 2024
c10cdee
add clarifying comments to instance flags
wlav Nov 29, 2024
cc9f17e
don't wrap or register iterators
wlav Nov 29, 2024
c9d330c
memory regulated smart pointer should match to smart pointer, not poi…
wlav Nov 29, 2024
443b7e0
set PyArg_ParseTupleAndKeywords inputs to nullptr as it won't initial…
wlav Dec 13, 2024
435bda0
update version to 1.13.0 and backend requirements to 1.15.3/6.32.8
wlav Dec 17, 2024
6dfffc6
Remove unused variable in CPPMethod.cxx
guitargeek Dec 17, 2024
7072bfb
Avoid `nullptr` dereferencing in CPyCppyy::BindCppObjectNoCast
guitargeek Dec 17, 2024
cb78daf
don't just rely on __cplusplus, but also check _MSVC_LANG if on Windows
wlav Dec 17, 2024
086da46
update versions in .toml to match setup.py
wlav Jan 16, 2025
5a9f910
fix type in requirements version: 6.38.2 -> 6.32.8
wlav Jan 17, 2025
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
2 changes: 1 addition & 1 deletion include/CPyCppyy/DispatchPtr.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class CPYCPPYY_CLASS_EXTERN DispatchPtr {
}

private:
PyObject* Get() const;
PyObject* Get(bool borrowed=true) const;

private:
PyObject* fPyHardRef;
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[build-system]
requires = ["cppyy-cling==6.28.0", "cppyy-backend==1.14.11", "setuptools", "wheel"]
requires = ["cppyy-cling==6.32.8", "cppyy-backend==1.15.3", "setuptools", "wheel"]
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
except ImportError:
has_wheel = False

requirements = ['cppyy-cling==6.28.0', 'cppyy-backend==1.14.11']
requirements = ['cppyy-cling==6.32.8', 'cppyy-backend==1.15.3']
setup_requirements = ['wheel']
if 'build' in sys.argv or 'install' in sys.argv:
setup_requirements += requirements
Expand Down Expand Up @@ -80,7 +80,7 @@ def build_extension(self, ext):

setup(
name='CPyCppyy',
version='1.12.13',
version='1.13.0',
description='Cling-based Python-C++ bindings for CPython',
long_description=long_description,

Expand Down
9 changes: 9 additions & 0 deletions src/API.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,16 @@ void CPyCppyy::ExecScript(const std::string& name, const std::vector<std::string
<< std::endl;
}
};

#if PY_VERSION_HEX < 0x30d00f0
WideStringListAppendHelper(&fConfig.argv, Py_GetProgramName());
#else
PyObject* progname = PySys_GetObject("executable"); // borrowed
wchar_t buf[4096];
Py_ssize_t sz = CPyCppyy_PyUnicode_AsWideChar(progname, buf, 4095);
if (0 < sz)
WideStringListAppendHelper(&fConfig.argv, buf);
#endif
for (const auto &iarg : argv2) {
WideStringListAppendHelper(&fConfig.argv, iarg.c_str());
}
Expand Down
3 changes: 3 additions & 0 deletions src/CPPDataMember.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,9 @@ PyTypeObject CPPDataMember_Type = {
#if PY_VERSION_HEX >= 0x030c0000
, 0 // tp_watched
#endif
#if PY_VERSION_HEX >= 0x030d0000
, 0 // tp_versions_used
#endif
};

} // namespace CPyCppyy
Expand Down
21 changes: 20 additions & 1 deletion src/CPPEnum.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,19 @@ PyObject* CPyCppyy::pyval_from_enum(const std::string& enum_type, PyObject* pyty
PyObject* bval;
if (enum_type == "char") {
char val = (char)llval;
#if PY_VERSION_HEX < 0x03000000
bval = CPyCppyy_PyText_FromStringAndSize(&val, 1);
#else
bval = PyUnicode_FromOrdinal((int)val);
#endif
} else if (enum_type == "int" || enum_type == "unsigned int")
bval = PyInt_FromLong((long)llval);
else
bval = PyLong_FromLongLong(llval);

if (!bval)
return nullptr; // e.g. when out of range for small integers

if (pytype && btype) {
PyObject* args = PyTuple_New(1);
PyTuple_SET_ITEM(args, 0, bval);
Expand Down Expand Up @@ -136,7 +143,7 @@ static PyObject* enum_ctype(PyObject* cls, PyObject* args, PyObject* kwds)
CPyCppyy::CPPEnum* CPyCppyy::CPPEnum_New(const std::string& name, Cppyy::TCppScope_t scope)
{
// Create a new enum type based on the actual C++ type. Enum values are added to
// the type by may also live in the enclosing scope.
// the type but may also live in the enclosing scope.

CPPEnum* pyenum = nullptr;

Expand Down Expand Up @@ -195,8 +202,13 @@ CPyCppyy::CPPEnum* CPyCppyy::CPPEnum_New(const std::string& name, Cppyy::TCppSco

// collect the enum values
std::vector<Cppyy::TCppScope_t> econstants = Cppyy::GetEnumConstants(etype);
bool values_ok = true;
for (auto *econstant : econstants) {
PyObject* val = pyval_from_enum(resolved, pyenum, pyside_type, econstant);
if (!val) {
values_ok = false;
break;
}
PyObject* pydname = CPyCppyy_PyText_FromString(Cppyy::GetFinalName(econstant).c_str());
PyObject_SetAttr(pyenum, pydname, val);
PyObject_SetAttr(val, PyStrings::gCppName, pydname);
Expand All @@ -211,6 +223,13 @@ CPyCppyy::CPPEnum* CPyCppyy::CPPEnum_New(const std::string& name, Cppyy::TCppSco
Py_DECREF(args);
Py_DECREF(pymeta);

if (!values_ok) {
if (!PyErr_Occurred())
PyErr_SetString(PyExc_ValueError, "could not set some of the enum values");
Py_DECREF(pyenum);
return nullptr;
}

} else {
// presumably not a class enum; simply pretend int
Py_INCREF(&PyInt_Type);
Expand Down
3 changes: 3 additions & 0 deletions src/CPPExcInstance.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,9 @@ PyTypeObject CPPExcInstance_Type = {
#if PY_VERSION_HEX >= 0x030c0000
, 0 // tp_watched
#endif
#if PY_VERSION_HEX >= 0x030d0000
, 0 // tp_versions_used
#endif
};

} // namespace CPyCppyy
5 changes: 4 additions & 1 deletion src/CPPInstance.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ static int op_clear(CPPInstance* pyobj)
// Garbage collector clear of held python member objects; this is a good time
// to safely remove this object from the memory regulator.
if (pyobj->fFlags & CPPInstance::kIsRegulated)
MemoryRegulator::UnregisterPyObject(pyobj, (PyObject*)Py_TYPE((PyObject*)pyobj));;
MemoryRegulator::UnregisterPyObject(pyobj, (PyObject*)Py_TYPE((PyObject*)pyobj));

return 0;
}
Expand Down Expand Up @@ -1102,6 +1102,9 @@ PyTypeObject CPPInstance_Type = {
#if PY_VERSION_HEX >= 0x030c0000
, 0 // tp_watched
#endif
#if PY_VERSION_HEX >= 0x030d0000
, 0 // tp_versions_used
#endif
};

} // namespace CPyCppyy
29 changes: 15 additions & 14 deletions src/CPPInstance.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,21 @@ class CPPInstance {
public:
enum EFlags {
kDefault = 0x0000,
kNoWrapConv = 0x0001,
kIsOwner = 0x0002,
kIsExtended = 0x0004,
kIsReference = 0x0008,
kIsRValue = 0x0010,
kIsLValue = 0x0020,
kIsValue = 0x0040,
kIsPtrPtr = 0x0080,
kIsArray = 0x0100,
kIsSmartPtr = 0x0200,
kNoMemReg = 0x0400,
kHasLifeLine = 0x0800,
kIsRegulated = 0x1000,
kIsActual = 0x2000 };
kNoWrapConv = 0x0001, // use type as-is (eg. no smart ptr wrap)
kIsOwner = 0x0002, // Python instance owns C++ object/memory
kIsExtended = 0x0004, // has extended data
kIsValue = 0x0008, // was created from a by-value return
kIsReference = 0x0010, // represents one indirection
kIsArray = 0x0020, // represents an array of objects
kIsSmartPtr = 0x0040, // is or embeds a smart pointer
kIsPtrPtr = 0x0080, // represents two indirections
kIsRValue = 0x0100, // can be used as an r-value
kIsLValue = 0x0200, // can be used as an l-value
kNoMemReg = 0x0400, // do not register with memory regulator
kIsRegulated = 0x0800, // is registered with memory regulator
kIsActual = 0x1000, // has been downcasted to actual type
kHasLifeLine = 0x2000, // has a life line set
};

public: // public, as the python C-API works with C structs
PyObject_HEAD
Expand Down
36 changes: 22 additions & 14 deletions src/CPPMethod.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -615,15 +615,30 @@ PyObject* CPyCppyy::CPPMethod::GetArgDefault(int iarg, bool silent)
PyObject* gdct = *dctptr;
PyObject* scope = nullptr;

if (defvalue.find("::") != std::string::npos) {
// try to tickle scope creation, just in case
scope = CreateScopeProxy(defvalue.substr(0, defvalue.rfind('(')));
if (!scope) PyErr_Clear();

// rename '::' -> '.'
TypeManip::cppscope_to_pyscope(defvalue);
if (defvalue.rfind('(') != std::string::npos) { // constructor-style call
// try to tickle scope creation, just in case, first look in the scope where
// the function lives, then in the global scope
std::string possible_scope = defvalue.substr(0, defvalue.rfind('('));
if (!Cppyy::IsBuiltin(possible_scope)) {
std::string cand_scope = Cppyy::GetScopedFinalName(fScope)+"::"+possible_scope;
scope = CreateScopeProxy(cand_scope);
if (!scope) {
PyErr_Clear();
// search within the global scope instead
scope = CreateScopeProxy(possible_scope);
if (!scope) PyErr_Clear();
} else {
// re-scope the scope; alternatively, the expression could be
// compiled in the dictionary of the function's namespace, but
// that would affect arguments passed to the constructor, too
defvalue = cand_scope + defvalue.substr(defvalue.rfind('('), std::string::npos);
}
}
}

// replace '::' -> '.'
TypeManip::cppscope_to_pyscope(defvalue);

if (!scope) {
// a couple of common cases that python doesn't like (technically, 'L' is okay with older
// pythons, but C long will always fit in Python int, so no need to bother)
Expand Down Expand Up @@ -960,13 +975,6 @@ PyObject* CPyCppyy::CPPMethod::Execute(void* self, ptrdiff_t offset, CallContext
result = ExecuteProtected(self, offset, ctxt);
}

// TODO: the following is dreadfully slow and dead-locks on Apache: revisit
// raising exceptions through callbacks by using magic returns
// if (result && Utility::PyErr_Occurred_WithGIL()) {
// // can happen in the case of a CINT error: trigger exception processing
// Py_DECREF(result);
// result = 0;
// } else if (!result && PyErr_Occurred())
if (!result && PyErr_Occurred())
SetPyError_(0);

Expand Down
5 changes: 4 additions & 1 deletion src/CPPOverload.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,7 @@ static PyObject* mp_call(CPPOverload* pymeth, PyObject* args, PyObject* kwds)
continue; // did not set implicit conversion, so don't try again

PyObject* result = methods[i]->Call(im_self, args, nargsf, kwds, &ctxt);
if (result != 0) {
if (result) {
// success: update the dispatch map for subsequent calls
if (!memoized_pc)
dispatchMap.push_back(std::make_pair(sighash, methods[i]));
Expand Down Expand Up @@ -1055,6 +1055,9 @@ PyTypeObject CPPOverload_Type = {
#if PY_VERSION_HEX >= 0x030c0000
, 0 // tp_watched
#endif
#if PY_VERSION_HEX >= 0x030d0000
, 0 // tp_versions_used
#endif
};

} // namespace CPyCppyy
Expand Down
11 changes: 8 additions & 3 deletions src/CPPScope.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -474,12 +474,14 @@ static PyObject* meta_getattro(PyObject* pyclass, PyObject* pyname)
// try all outstanding using namespaces in turn to find the attribute (will cache
// locally later; TODO: doing so may cause pathological cases)
for (auto pyref : *klass->fImp.fUsing) {
PyObject* pyuscope = PyWeakref_GetObject(pyref);
PyObject* pyuscope = CPyCppyy_GetWeakRef(pyref);
if (pyuscope) {
attr = PyObject_GetAttr(pyuscope, pyname);
if (attr) break;
PyErr_Clear();
if (!attr) PyErr_Clear();
Py_DECREF(pyuscope);
}
if (attr)
break;
}
}

Expand Down Expand Up @@ -702,6 +704,9 @@ PyTypeObject CPPScope_Type = {
#if PY_VERSION_HEX >= 0x030c0000
, 0 // tp_watched
#endif
#if PY_VERSION_HEX >= 0x030d0000
, 0 // tp_versions_used
#endif
};

} // namespace CPyCppyy
20 changes: 19 additions & 1 deletion src/CPyCppyy.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,26 @@ inline PyObject* CPyCppyy_tp_call(PyObject* cb, PyObject* args, size_t, PyObject
}
#endif

// weakref forced strong reference
#if PY_VERSION_HEX < 0x30d0000
static inline PyObject* CPyCppyy_GetWeakRef(PyObject* ref) {
PyObject* pyobject = PyWeakref_GetObject(ref);
if (!pyobject || pyobject == Py_None)
return nullptr;
Py_INCREF(pyobject);
return pyobject;
}
#else
static inline PyObject* CPyCppyy_GetWeakRef(PyObject* ref) {
PyObject* pyobject = nullptr;
if (PyWeakref_GetRef(ref, &pyobject) != -1)
return pyobject;
return nullptr;
}
#endif

// Py_TYPE as inline function
#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE)
#if PY_VERSION_HEX < 0x03090000 && !defined(Py_SET_TYPE)
static inline
void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) { ob->ob_type = type; }
#define Py_SET_TYPE(ob, type) _Py_SET_TYPE((PyObject*)(ob), type)
Expand Down
46 changes: 33 additions & 13 deletions src/CPyCppyyModule.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ static PyTypeObject PyNullPtr_t_Type = {
#if PY_VERSION_HEX >= 0x030c0000
, 0 // tp_watched
#endif
#if PY_VERSION_HEX >= 0x030d0000
, 0 // tp_versions_used
#endif
};


Expand Down Expand Up @@ -204,19 +207,30 @@ static PyTypeObject PyDefault_t_Type = {
#if PY_VERSION_HEX >= 0x030c0000
, 0 // tp_watched
#endif
#if PY_VERSION_HEX >= 0x030d0000
, 0 // tp_versions_used
#endif
};

namespace {

PyObject _CPyCppyy_NullPtrStruct = {
_PyObject_EXTRA_INIT
1, &PyNullPtr_t_Type
};
PyObject _CPyCppyy_NullPtrStruct = {_PyObject_EXTRA_INIT
// In 3.12.0-beta this field was changed from a ssize_t to a union
#if PY_VERSION_HEX >= 0x30c00b1
{1},
#else
1,
#endif
&PyNullPtr_t_Type};

PyObject _CPyCppyy_DefaultStruct = {
_PyObject_EXTRA_INIT
1, &PyDefault_t_Type
};
PyObject _CPyCppyy_DefaultStruct = {_PyObject_EXTRA_INIT
// In 3.12.0-beta this field was changed from a ssize_t to a union
#if PY_VERSION_HEX >= 0x30c00b1
{1},
#else
1,
#endif
&PyDefault_t_Type};

// TODO: refactor with Converters.cxx
struct CPyCppyy_tagCDataObject { // non-public (but stable)
Expand All @@ -236,10 +250,15 @@ namespace CPyCppyy {
PyObject* gSegvException = nullptr;
PyObject* gIllException = nullptr;
PyObject* gAbrtException = nullptr;
std::map<std::string, std::vector<PyObject*>> gPythonizations;
std::set<Cppyy::TCppScope_t> gPinnedTypes;
std::set<Cppyy::TCppType_t> gPinnedTypes;
std::ostringstream gCapturedError;
std::streambuf* gOldErrorBuffer = nullptr;

std::map<std::string, std::vector<PyObject*>> &pythonizations()
{
static std::map<std::string, std::vector<PyObject*>> pyzMap;
return pyzMap;
}
}


Expand Down Expand Up @@ -837,7 +856,7 @@ static PyObject* AddPythonization(PyObject*, PyObject* args)
}

Py_INCREF(pythonizor);
gPythonizations[scope].push_back(pythonizor);
pythonizations()[scope].push_back(pythonizor);

Py_RETURN_NONE;
}
Expand All @@ -851,8 +870,9 @@ static PyObject* RemovePythonization(PyObject*, PyObject* args)
if (!PyArg_ParseTuple(args, const_cast<char*>("Os"), &pythonizor, &scope))
return nullptr;

auto p1 = gPythonizations.find(scope);
if (p1 != gPythonizations.end()) {
auto &pyzMap = pythonizations();
auto p1 = pyzMap.find(scope);
if (p1 != pyzMap.end()) {
auto p2 = std::find(p1->second.begin(), p1->second.end(), pythonizor);
if (p2 != p1->second.end()) {
p1->second.erase(p2);
Expand Down
Loading