From 3fa11ebe8edac571f46f67c6acf21a331b07dcc5 Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Thu, 2 Apr 2026 18:42:05 +0200 Subject: [PATCH] [CPyCppyy] Remove `Eval()` and classes redundant with TPython The `TPython::Eval()` method was removed some time ago, and replaced with the `Exec()` overload that uses `std::any` for data transfer. So for ROOT, there is no reason to keep the fragile `Eval()` functionality in the future CppJIT. If CppJIT users would need such functionality, we should instead port the `std::any` mechanism to the CPyCppyy API. Remove PyResult, TPyArg and TPyClassGenerator in CPyCppyy, as they are copies of TPython classes. The corresponding TPython class for PyResult is the TPyReturn, and to implement it without forwarding to the former PyResult class, the CPyCppyy API needs to be extended with setters for ownership flags. --- bindings/pyroot/cppyy/CPyCppyy/CMakeLists.txt | 2 - .../cppyy/CPyCppyy/include/CPyCppyy/API.h | 8 +- .../CPyCppyy/include/CPyCppyy/PyResult.h | 65 ---- .../cppyy/CPyCppyy/include/CPyCppyy/TPyArg.h | 52 --- bindings/pyroot/cppyy/CPyCppyy/src/API.cxx | 85 ++--- .../pyroot/cppyy/CPyCppyy/src/PyResult.cxx | 166 ---------- bindings/pyroot/cppyy/CPyCppyy/src/TPyArg.cxx | 131 -------- .../cppyy/CPyCppyy/src/TPyClassGenerator.cxx | 301 ------------------ .../cppyy/CPyCppyy/src/TPyClassGenerator.h | 21 -- bindings/tpython/src/TPyReturn.cxx | 10 +- 10 files changed, 37 insertions(+), 804 deletions(-) delete mode 100644 bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/PyResult.h delete mode 100644 bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/TPyArg.h delete mode 100644 bindings/pyroot/cppyy/CPyCppyy/src/PyResult.cxx delete mode 100644 bindings/pyroot/cppyy/CPyCppyy/src/TPyArg.cxx delete mode 100644 bindings/pyroot/cppyy/CPyCppyy/src/TPyClassGenerator.cxx delete mode 100644 bindings/pyroot/cppyy/CPyCppyy/src/TPyClassGenerator.h diff --git a/bindings/pyroot/cppyy/CPyCppyy/CMakeLists.txt b/bindings/pyroot/cppyy/CPyCppyy/CMakeLists.txt index 065a89348ff4a..c21fb317f8cb6 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/CMakeLists.txt +++ b/bindings/pyroot/cppyy/CPyCppyy/CMakeLists.txt @@ -7,7 +7,6 @@ set(headers include/CPyCppyy/API.h include/CPyCppyy/Reflex.h - include/CPyCppyy/PyResult.h include/CPyCppyy/CommonDefs.h include/CPyCppyy/PyException.h include/CPyCppyy/DispatchPtr.h @@ -38,7 +37,6 @@ set(sources src/MemoryRegulator.cxx src/ProxyWrappers.cxx src/PyException.cxx - src/PyResult.cxx src/PyStrings.cxx src/Pythonize.cxx src/TemplateProxy.cxx diff --git a/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/API.h b/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/API.h index 05bd9f13937a8..e619348e11c0d 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/API.h +++ b/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/API.h @@ -40,7 +40,6 @@ namespace Cppyy { } // namespace Cppyy // Bindings -#include "CPyCppyy/PyResult.h" #include "CPyCppyy/CommonDefs.h" // Standard @@ -193,6 +192,10 @@ CPYCPPYY_EXTERN bool Scope_CheckExact(PyObject* pyobject); CPYCPPYY_EXTERN bool Instance_Check(PyObject* pyobject); CPYCPPYY_EXTERN bool Instance_CheckExact(PyObject* pyobject); +// memory management: ownership of the underlying C++ object +CPYCPPYY_EXTERN void Instance_SetPythonOwns(PyObject* pyobject); +CPYCPPYY_EXTERN void Instance_SetCppOwns(PyObject* pyobject); + // type verifier for sequences CPYCPPYY_EXTERN bool Sequence_Check(PyObject* pyobject); @@ -217,9 +220,6 @@ CPYCPPYY_EXTERN bool Import(const std::string& name); // execute a python statement (e.g. "import sys") CPYCPPYY_EXTERN bool Exec(const std::string& cmd); -// evaluate a python expression (e.g. "1+1") -CPYCPPYY_EXTERN const PyResult Eval(const std::string& expr); - // execute a python stand-alone script, with argv CLI arguments CPYCPPYY_EXTERN void ExecScript(const std::string& name, const std::vector& args); diff --git a/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/PyResult.h b/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/PyResult.h deleted file mode 100644 index 7e43dffc9fa09..0000000000000 --- a/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/PyResult.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef CPYCPPYY_PYRESULT_H -#define CPYCPPYY_PYRESULT_H - -////////////////////////////////////////////////////////////////////////////// -// // -// TPyResult // -// // -// Morphing return type from evaluating python expressions. // -// // -////////////////////////////////////////////////////////////////////////////// - -// Python -struct _object; -typedef _object PyObject; - -// Bindings -#include "CPyCppyy/CommonDefs.h" - - -namespace CPyCppyy { - -class CPYCPPYY_CLASS_EXTERN PyResult { -public: - PyResult(); - PyResult(PyObject* pyobject); - PyResult(const PyResult&); - PyResult& operator=(const PyResult&); - virtual ~PyResult(); - -// conversions to standard types, may fail if unconvertible - operator char*() const; - operator const char*() const; - operator char() const; - - operator long() const; - operator int() const { return (int)operator long(); } - operator short() const { return (short)operator long(); } - - operator unsigned long() const; - operator unsigned int() const { - return (unsigned int)operator unsigned long(); - } - operator unsigned short() const { - return (unsigned short)operator unsigned long(); - } - - operator double() const; - operator float() const { return (float)operator double(); } - -// used for both general object type and PyObject conversions - operator void*() const; - - template - operator T*() const { return (T*)(void*)*this; } - -// used strictly for PyObject conversions - operator PyObject*() const; - -private: - PyObject* fPyObject; /// - -// Bindings -#include "CPyCppyy/CommonDefs.h" - - -class CPYCPPYY_CLASS_EXTERN TPyArg { -public: -// converting constructors - TPyArg(PyObject*); - TPyArg(int); - TPyArg(long); - TPyArg(double); - TPyArg(const char*); - - TPyArg(const TPyArg&); - TPyArg& operator=(const TPyArg&); - virtual ~TPyArg(); - -// "extractor" - operator PyObject*() const; - -// constructor and generic dispatch - static void CallConstructor( - PyObject*& pyself, PyObject* pyclass, const std::vector& args); - static void CallConstructor(PyObject*& pyself, PyObject* pyclass); // default ctor - static PyObject* CallMethod(PyObject* pymeth, const std::vector& args); - static void CallDestructor( - PyObject*& pyself, PyObject* pymeth, const std::vector& args); - static void CallDestructor(PyObject*& pyself); - -private: - mutable PyObject* fPyObject; ///PythonOwns(); +} + +//----------------------------------------------------------------------------- +void CPyCppyy::Instance_SetCppOwns(PyObject* pyobject) +{ + if (!Initialize()) + return; + +// check validity of cast + if (!CPPInstance_Check(pyobject)) + return; + + ((CPPInstance *)pyobject)->CppOwns(); +} + //----------------------------------------------------------------------------- bool CPyCppyy::Sequence_Check(PyObject* pyobject) { @@ -313,10 +339,6 @@ bool CPyCppyy::Import(const std::string& mod_name) fullname += "."; fullname += CPyCppyy_PyText_AsString(pyClName); - // force class creation (this will eventually call TPyClassGenerator) - // TODO: the following is broken (and should live in Cppyy.cxx) to - // TClass::GetClass(fullname.c_str(), true); - Py_XDECREF(pyClName); } @@ -430,61 +452,6 @@ bool CPyCppyy::Exec(const std::string& cmd) return false; } -//----------------------------------------------------------------------------- -const CPyCppyy::PyResult CPyCppyy::Eval(const std::string& expr) -{ -// Evaluate a python expression. -// -// Caution: do not hold on to the return value: either store it in a builtin -// type (implicit casting will work), or in a pointer to a cppyy object (explicit -// casting to a void* is required). - if (!Initialize()) - return PyResult(); - -// evaluate the expression - PyObject* result = - PyRun_String(const_cast(expr.c_str()), Py_eval_input, gMainDict, gMainDict); - -// report errors as appropriate; return void - if (!result) { - PyErr_Print(); - return PyResult(); - } - -// results that require no conversion - if (result == Py_None || CPPInstance_Check(result) || - PyBytes_Check(result) || - PyFloat_Check(result) || PyLong_Check(result) || PyInt_Check(result)) - return PyResult(result); - -// explicit conversion for python type required - PyObject* pyclass = (PyObject*)Py_TYPE(result); - -// retrieve class name and the module in which it resides - PyObject* name = PyObject_GetAttr(pyclass, PyStrings::gName); - PyObject* module = PyObject_GetAttr(pyclass, PyStrings::gModule); - - // concat name - std::string qname = - std::string(CPyCppyy_PyText_AsString(module)) + \ - '.' + CPyCppyy_PyText_AsString(name); - Py_DECREF(module); - Py_DECREF(name); - -// locate cppyy style class with this name - // TODO: use Cppyy.cxx ... - //TClass* klass = TClass::GetClass(qname.c_str()); - void* klass = nullptr; - -// construct general cppyy python object that pretends to be of class 'klass' - if (klass) - return PyResult(result); - -// no conversion, return null pointer object - Py_DECREF(result); - return PyResult(); -} - //----------------------------------------------------------------------------- void CPyCppyy::Prompt() { // Enter an interactive python session (exit with ^D). State is preserved diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/PyResult.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/PyResult.cxx deleted file mode 100644 index 08775bd0dfa70..0000000000000 --- a/bindings/pyroot/cppyy/CPyCppyy/src/PyResult.cxx +++ /dev/null @@ -1,166 +0,0 @@ -// Bindings -#include "CPyCppyy.h" -#define CPYCPPYY_INTERNAL 1 -#include "CPyCppyy/PyResult.h" -#undef CPYCPPYY_INTERNAL - -#include "CPPInstance.h" - -// Standard -#include - - -//______________________________________________________________________________ -// Python expression eval result -// ============================= -// -// Transport class for bringing objects from python (dynamically typed) to Cling -// (statically typed). It is best to immediately cast a PyResult to the real -// type, either implicitly (for builtin types) or explicitly (through a void* -// cast for pointers to C++ objects). - - -//- constructors/destructor -------------------------------------------------- -CPyCppyy::PyResult::PyResult() -{ -// Construct a PyResult object from Py_None. - Py_INCREF(Py_None); - fPyObject = Py_None; -} - -//---------------------------------------------------------------------------- -CPyCppyy::PyResult::PyResult(PyObject* pyobject) -{ -// Construct a PyResult from a python object. The python object may represent -// a C++ object. Steals reference to given python object. - if (!pyobject) { - Py_INCREF(Py_None); - fPyObject = Py_None; - } else - fPyObject = pyobject; // steals reference -} - -//---------------------------------------------------------------------------- -CPyCppyy::PyResult::PyResult(const PyResult& other) -{ -// Copy constructor. Applies python object reference counting. - Py_INCREF(other.fPyObject); - fPyObject = other.fPyObject; -} - -//---------------------------------------------------------------------------- -CPyCppyy::PyResult& CPyCppyy::PyResult::operator=(const PyResult& other) -{ -// Assignment operator. Applies python object reference counting. - if (this != &other) { - Py_INCREF(other.fPyObject); - Py_DECREF(fPyObject); - fPyObject = other.fPyObject; - } - - return *this; -} - -//---------------------------------------------------------------------------- -CPyCppyy::PyResult::~PyResult() -{ -// Destructor. Reference counting for the held python object is in effect. - Py_DECREF(fPyObject); -} - - -//- public members ----------------------------------------------------------- -CPyCppyy::PyResult::operator char*() const -{ -// Cast python return value to C-style string (may fail). - return (char*)((const char*)*this); -} - -//---------------------------------------------------------------------------- -CPyCppyy::PyResult::operator const char*() const -{ -// Cast python return value to C-style string (may fail). - if (fPyObject == Py_None) // for void returns - return nullptr; - - const char* s = CPyCppyy_PyText_AsString(fPyObject); - if (PyErr_Occurred()) { - PyErr_Print(); - return nullptr; - } - - return s; -} - -//---------------------------------------------------------------------------- -CPyCppyy::PyResult::operator char() const -{ -// Cast python return value to C++ char (may fail). - std::string s = operator const char*(); - if (s.size()) - return s[0]; - - return '\0'; -} - -//---------------------------------------------------------------------------- -CPyCppyy::PyResult::operator long() const -{ -// Cast python return value to C++ long (may fail). - long l = PyLong_AsLong(fPyObject); - - if (PyErr_Occurred()) - PyErr_Print(); - - return l; -} - -//---------------------------------------------------------------------------- -CPyCppyy::PyResult::operator unsigned long() const -{ -// Cast python return value to C++ unsigned long (may fail). - unsigned long ul = PyLong_AsUnsignedLong(fPyObject); - - if (PyErr_Occurred()) - PyErr_Print(); - - return ul; -} - -//---------------------------------------------------------------------------- -CPyCppyy::PyResult::operator double() const -{ -// Cast python return value to C++ double (may fail). - double d = PyFloat_AsDouble(fPyObject); - - if (PyErr_Occurred()) - PyErr_Print(); - - return d; -} - -//---------------------------------------------------------------------------- -CPyCppyy::PyResult::operator void*() const -{ -// Cast python return value to C++ object with dictionary (may fail; note that -// you have to use the void* converter, as Cling will not call any other). - if (fPyObject == Py_None) - return nullptr; - - if (CPyCppyy::CPPInstance_Check(fPyObject)) { - ((CPyCppyy::CPPInstance*)fPyObject)->CppOwns(); - return ((CPyCppyy::CPPInstance*)fPyObject)->GetObject(); - } else - return fPyObject; // borrows reference -} - -//---------------------------------------------------------------------------- -CPyCppyy::PyResult::operator PyObject*() const -{ -// Direct return of the held PyObject; note the new reference. - if (fPyObject == Py_None) - return nullptr; - - Py_INCREF(fPyObject); - return fPyObject; -} diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/TPyArg.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/TPyArg.cxx deleted file mode 100644 index 9906ae87493ff..0000000000000 --- a/bindings/pyroot/cppyy/CPyCppyy/src/TPyArg.cxx +++ /dev/null @@ -1,131 +0,0 @@ -// Bindings -#include "CPyCppyy.h" -#define CPYCPPYY_INTERNAL 1 -#include "CPyCppyy/TPyArg.h" -#undef CPYCPPYY_INTERNAL - - -//______________________________________________________________________________ -// Generic wrapper for arguments -// ============================= -// -// Transport class for bringing C++ values and objects from Cling to Python. It -// provides, from the selected constructor, the proper conversion to a PyObject. -// In principle, there should be no need to use this class directly: it relies -// on implicit conversions. - - -//- constructor dispatcher --------------------------------------------------- -void TPyArg::CallConstructor( - PyObject*& pyself, PyObject* pyclass, const std::vector& args) -{ - int nArgs = (int)args.size(); - PyObject* pyargs = PyTuple_New(nArgs); - for (int i = 0; i < nArgs; ++i) - PyTuple_SET_ITEM(pyargs, i, (PyObject*)args[i]); - pyself = PyObject_Call(pyclass, pyargs, nullptr); - Py_DECREF(pyargs); -} - -//---------------------------------------------------------------------------- -void CallConstructor(PyObject*& pyself, PyObject* pyclass) -{ - PyObject* pyargs = PyTuple_New(0); - pyself = PyObject_Call(pyclass, pyargs, nullptr); - Py_DECREF(pyargs); -} - -//- generic dispatcher ------------------------------------------------------- -PyObject* TPyArg::CallMethod(PyObject* pymeth, const std::vector& args) -{ - int nArgs = (int)args.size(); - PyObject* pyargs = PyTuple_New(nArgs); - for (int i = 0; i < nArgs; ++i) - PyTuple_SET_ITEM(pyargs, i, (PyObject*)args[i]); - PyObject* result = PyObject_Call(pymeth, pyargs, nullptr); - Py_DECREF(pyargs); - return result; -} - -//- destructor dispatcher ---------------------------------------------------- -void TPyArg::CallDestructor(PyObject*& pyself, PyObject*, const std::vector&) -{ - Py_XDECREF(pyself); // calls actual dtor if ref-count down to 0 -} - -//---------------------------------------------------------------------------- -void TPyArg::CallDestructor(PyObject*& pyself) -{ - Py_XDECREF(pyself); -} - -//- constructors/destructor -------------------------------------------------- -TPyArg::TPyArg(PyObject* pyobject) -{ -// Construct a TPyArg from a python object. - Py_XINCREF(pyobject); - fPyObject = pyobject; -} - -//---------------------------------------------------------------------------- -TPyArg::TPyArg(int value) -{ -// Construct a TPyArg from an integer value. - fPyObject = PyInt_FromLong(value); -} - -//---------------------------------------------------------------------------- -TPyArg::TPyArg(long value) -{ -// Construct a TPyArg from an integer value. - fPyObject = PyLong_FromLong(value); -} - -//---------------------------------------------------------------------------- -TPyArg::TPyArg(double value) -{ -// Construct a TPyArg from a double value. - fPyObject = PyFloat_FromDouble(value); -} - -//---------------------------------------------------------------------------- -TPyArg::TPyArg(const char* value) -{ -// Construct a TPyArg from a C-string. - fPyObject = CPyCppyy_PyText_FromString(value); -} - -//---------------------------------------------------------------------------- -TPyArg::TPyArg(const TPyArg& s) -{ -// Copy constructor. - Py_XINCREF(s.fPyObject); - fPyObject = s.fPyObject; -} - -//---------------------------------------------------------------------------- -TPyArg& TPyArg::operator=(const TPyArg& s) -{ -// Assignment operator. - if (this != &s) { - Py_XINCREF(s.fPyObject); - fPyObject = s.fPyObject; - } - return *this; -} - -//---------------------------------------------------------------------------- -TPyArg::~TPyArg() -{ -// Done with held PyObject. - Py_XDECREF(fPyObject); - fPyObject = nullptr; -} - -//- public members ----------------------------------------------------------- -TPyArg::operator PyObject*() const -{ -// Extract the python object. - Py_XINCREF(fPyObject); - return fPyObject; -} diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/TPyClassGenerator.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/TPyClassGenerator.cxx deleted file mode 100644 index 5d7990ac08dff..0000000000000 --- a/bindings/pyroot/cppyy/CPyCppyy/src/TPyClassGenerator.cxx +++ /dev/null @@ -1,301 +0,0 @@ -// Bindings -#include "CPyCppyy.h" -#include "PyStrings.h" -#include "TPyClassGenerator.h" -#include "Utility.h" - -#include "CPyCppyy/PyResult.h" - -// TODO: not sure if any of this still makes sense ... -#if 0 - -// ROOT -#include "TClass.h" -#include "TInterpreter.h" -#include "TROOT.h" - -// Standard -#include -#include -#include - - -//= helper ================================================================== -namespace { - - class PyGILRAII { - PyGILState_STATE m_GILState; - public: - PyGILRAII() : m_GILState(PyGILState_Ensure()) {} - ~PyGILRAII() { PyGILState_Release(m_GILState); } - }; - -} // unnamed namespace - - -//- public members ----------------------------------------------------------- -TClass* TPyClassGenerator::GetClass( const char* name, bool load ) -{ -// Just forward. - return GetClass( name, load, false ); -} - -//- public members ----------------------------------------------------------- -TClass* TPyClassGenerator::GetClass( const char* name, bool load, bool silent ) -{ -// Class generator to make python classes available to Cling - -// called if all other class generators failed, attempt to build from python class - if ( PyROOT::gDictLookupActive == true ) - return 0; // call originated from python - - if ( ! load || ! name ) - return 0; - - PyGILRAII thePyGILRAII; - -// first, check whether the name is of a module - PyObject* modules = PySys_GetObject( const_cast("modules") ); - PyObject* pyname = PyROOT_PyUnicode_FromString( name ); - PyObject* keys = PyDict_Keys( modules ); - bool isModule = PySequence_Contains( keys, pyname ); - Py_DECREF( keys ); - Py_DECREF( pyname ); - - if ( isModule ) { - // the normal TClass::GetClass mechanism doesn't allow direct returns, so - // do our own check - TClass* cl = (TClass*)gROOT->GetListOfClasses()->FindObject( name ); - if ( cl ) return cl; - - std::ostringstream nsCode; - nsCode << "namespace " << name << " {\n"; - - // add all free functions - PyObject* mod = PyDict_GetItemString( modules, const_cast(name) ); - PyObject* dct = PyModule_GetDict( mod ); - keys = PyDict_Keys( dct ); - - for ( int i = 0; i < PyList_GET_SIZE( keys ); ++i ) { - PyObject* key = PyList_GET_ITEM( keys, i ); - Py_INCREF( key ); - - PyObject* attr = PyDict_GetItem( dct, key ); - Py_INCREF( attr ); - - // TODO: refactor the code below with the class method code - if ( PyCallable_Check( attr ) && \ - ! (PyClass_Check( attr ) || PyObject_HasAttr( attr, PyROOT::PyStrings::gBases )) ) { - std::string func_name = PyROOT_PyUnicode_AsString( key ); - - // figure out number of variables required - PyObject* func_code = PyObject_GetAttrString( attr, (char*)"func_code" ); - PyObject* var_names = - func_code ? PyObject_GetAttrString( func_code, (char*)"co_varnames" ) : NULL; - int nVars = var_names ? PyTuple_GET_SIZE( var_names ) : 0 /* TODO: probably large number, all default? */; - if ( nVars < 0 ) nVars = 0; - Py_XDECREF( var_names ); - Py_XDECREF( func_code ); - - nsCode << " TPyReturn " << func_name << "("; - for ( int ivar = 0; ivar < nVars; ++ivar ) { - nsCode << "const TPyArg& a" << ivar; - if ( ivar != nVars-1 ) nsCode << ", "; - } - nsCode << ") {\n"; - nsCode << " std::vector v; v.reserve(" << nVars << ");\n"; - - // add the variables - for ( int ivar = 0; ivar < nVars; ++ ivar ) - nsCode << " v.push_back(a" << ivar << ");\n"; - - // call dispatch (method or class pointer hard-wired) - nsCode << " return TPyReturn(TPyArg::CallMethod((PyObject*)" << (void*)attr << ", v)); }\n"; - } - - Py_DECREF( attr ); - Py_DECREF( key ); - } - - Py_DECREF( keys ); - - nsCode << " }"; - - if ( gInterpreter->LoadText( nsCode.str().c_str() ) ) { - TClass* klass = new TClass( name, silent ); - TClass::AddClass( klass ); - return klass; - } - - return nullptr; - } - -// determine module and class name part - std::string clName = name; - std::string::size_type pos = clName.rfind( '.' ); - - if ( pos == std::string::npos ) - return 0; // this isn't a python style class - - std::string mdName = clName.substr( 0, pos ); - clName = clName.substr( pos+1, std::string::npos ); - -// create class in namespace, if it exists (no load, silent) - bool useNS = gROOT->GetListOfClasses()->FindObject( mdName.c_str() ) != 0; - if ( ! useNS ) { - // the class itself may exist if we're using the global scope - TClass* cl = (TClass*)gROOT->GetListOfClasses()->FindObject( clName.c_str() ); - if ( cl ) return cl; - } - -// locate and get class - PyObject* mod = PyImport_AddModule( const_cast< char* >( mdName.c_str() ) ); - if ( ! mod ) { - PyErr_Clear(); - return 0; // module apparently disappeared - } - - Py_INCREF( mod ); - PyObject* pyclass = - PyDict_GetItemString( PyModule_GetDict( mod ), const_cast< char* >( clName.c_str() ) ); - Py_XINCREF( pyclass ); - Py_DECREF( mod ); - - if ( ! pyclass ) { - PyErr_Clear(); // the class is no longer available?! - return 0; - } - -// get a listing of all python-side members - PyObject* attrs = PyObject_Dir( pyclass ); - if ( ! attrs ) { - PyErr_Clear(); - Py_DECREF( pyclass ); - return 0; - } - -// pre-amble Cling proxy class - std::ostringstream proxyCode; - if ( useNS ) proxyCode << "namespace " << mdName << " { "; - proxyCode << "class " << clName << " {\nprivate:\n PyObject* fPyObject;\npublic:\n"; - -// loop over and add member functions - bool hasConstructor = false, hasDestructor = false; - for ( int i = 0; i < PyList_GET_SIZE( attrs ); ++i ) { - PyObject* label = PyList_GET_ITEM( attrs, i ); - Py_INCREF( label ); - PyObject* attr = PyObject_GetAttr( pyclass, label ); - - // collect only member functions (i.e. callable elements in __dict__) - if ( PyCallable_Check( attr ) ) { - std::string mtName = PyROOT_PyUnicode_AsString( label ); - - if ( mtName == "__del__" ) { - hasDestructor = true; - proxyCode << " ~" << clName << "() { TPyArg::CallDestructor(fPyObject); }\n"; - continue; - } - - bool isConstructor = mtName == "__init__"; - if ( !isConstructor && mtName.find("__", 0, 2) == 0 ) - continue; // skip all other python special funcs - - // figure out number of variables required -#if PY_VERSION_HEX < 0x03000000 - PyObject* im_func = PyObject_GetAttrString( attr, (char*)"im_func" ); - PyObject* func_code = - im_func ? PyObject_GetAttrString( im_func, (char*)"func_code" ) : NULL; -#else - PyObject* func_code = PyObject_GetAttrString( attr, "__code__" ); -#endif - PyObject* var_names = - func_code ? PyObject_GetAttrString( func_code, (char*)"co_varnames" ) : NULL; - if (PyErr_Occurred()) PyErr_Clear(); // happens for slots; default to 0 arguments - - int nVars = var_names ? PyTuple_GET_SIZE( var_names ) - 1 /* self */ : 0 /* TODO: probably large number, all default? */; - if ( nVars < 0 ) nVars = 0; - Py_XDECREF( var_names ); - Py_XDECREF( func_code ); -#if PY_VERSION_HEX < 0x03000000 - Py_XDECREF( im_func ); -#endif - - // method declaration as appropriate - if ( isConstructor ) { - hasConstructor = true; - proxyCode << " " << clName << "("; - } else // normal method - proxyCode << " TPyReturn " << mtName << "("; - for ( int ivar = 0; ivar < nVars; ++ivar ) { - proxyCode << "const TPyArg& a" << ivar; - if ( ivar != nVars-1 ) proxyCode << ", "; - } - proxyCode << ") {\n"; - proxyCode << " std::vector v; v.reserve(" << nVars+(isConstructor ? 0 : 1) << ");\n"; - - // add the 'self' argument as appropriate - if ( ! isConstructor ) - proxyCode << " v.push_back(fPyObject);\n"; - - // then add the remaining variables - for ( int ivar = 0; ivar < nVars; ++ ivar ) - proxyCode << " v.push_back(a" << ivar << ");\n"; - - // call dispatch (method or class pointer hard-wired) - if ( ! isConstructor ) - proxyCode << " return TPyReturn(TPyArg::CallMethod((PyObject*)" << (void*)attr << ", v))"; - else - proxyCode << " TPyArg::CallConstructor(fPyObject, (PyObject*)" << (void*)pyclass << ", v)"; - proxyCode << ";\n }\n"; - } - - // no decref of attr for now (b/c of hard-wired ptr); need cleanup somehow - Py_DECREF( label ); - } - -// special case if no constructor or destructor - if ( ! hasConstructor ) - proxyCode << " " << clName << "() {\n TPyArg::CallConstructor(fPyObject, (PyObject*)" << (void*)pyclass << "); }\n"; - - if ( ! hasDestructor ) - proxyCode << " ~" << clName << "() { TPyArg::CallDestructor(fPyObject); }\n"; - -// for now, don't allow copying (ref-counting wouldn't work as expected anyway) - proxyCode << " " << clName << "(const " << clName << "&) = delete;\n"; - proxyCode << " " << clName << "& operator=(const " << clName << "&) = delete;\n"; - -// closing and building of Cling proxy class - proxyCode << "};"; - if ( useNS ) proxyCode << " }"; - - Py_DECREF( attrs ); -// done with pyclass, decref here, assuming module is kept - Py_DECREF( pyclass ); - -// body compilation - if ( ! gInterpreter->LoadText( proxyCode.str().c_str() ) ) - return nullptr; - -// done, let ROOT manage the new class - TClass* klass = new TClass( useNS ? (mdName+"::"+clName).c_str() : clName.c_str(), silent ); - TClass::AddClass( klass ); - - return klass; -} - -//////////////////////////////////////////////////////////////////////////////// -/// Just forward; based on type name only. - -TClass* TPyClassGenerator::GetClass( const std::type_info& typeinfo, bool load, bool silent ) -{ - return GetClass( typeinfo.name(), load, silent ); -} - -//////////////////////////////////////////////////////////////////////////////// -/// Just forward; based on type name only - -TClass* TPyClassGenerator::GetClass( const std::type_info& typeinfo, bool load ) -{ - return GetClass( typeinfo.name(), load ); -} -#endif diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/TPyClassGenerator.h b/bindings/pyroot/cppyy/CPyCppyy/src/TPyClassGenerator.h deleted file mode 100644 index ed55fa27fd6fa..0000000000000 --- a/bindings/pyroot/cppyy/CPyCppyy/src/TPyClassGenerator.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef CPYCPPYY_TPYCLASSGENERATOR -#define CPYCPPYY_TPYCLASSGENERATOR - -// TODO: not sure if any of this still makes sense ... -#if 0 - -// ROOT -#include "TClassGenerator.h" - - -class TPyClassGenerator : public TClassGenerator { -public: - virtual TClass* GetClass(const char* name, bool load); - virtual TClass* GetClass(const std::type_info& typeinfo, bool load); - virtual TClass* GetClass(const char* name, bool load, bool silent); - virtual TClass* GetClass(const std::type_info& typeinfo, bool load, bool silent); -}; - -#endif - -#endif // !CPYCPPYY_TPYCLASSGENERATOR diff --git a/bindings/tpython/src/TPyReturn.cxx b/bindings/tpython/src/TPyReturn.cxx index b4dd5016a930f..ea62bde3cb98a 100644 --- a/bindings/tpython/src/TPyReturn.cxx +++ b/bindings/tpython/src/TPyReturn.cxx @@ -203,16 +203,20 @@ TPyReturn::operator Double_t() const //////////////////////////////////////////////////////////////////////////////// /// Cast python return value to ROOT object with dictionary (may fail; note that -/// you have to use the void* converter, as CINT will not call any other). +/// you have to use the void* converter, as Cling will not call any other). TPyReturn::operator void *() const { PyGILRAII gilRaii; if (fPyObject == Py_None) - return 0; + return nullptr; - return static_cast(CPyCppyy::PyResult{fPyObject}); + if (CPyCppyy::Instance_Check(fPyObject)) { + CPyCppyy::Instance_SetCppOwns(fPyObject); + return CPyCppyy::Instance_AsVoidPtr(fPyObject); + } else + return fPyObject; // borrows reference } ////////////////////////////////////////////////////////////////////////////////