Logo ROOT  
Reference Guide
TPyClassGenerator.cxx
Go to the documentation of this file.
1// Bindings
2#include "CPyCppyy.h"
3#include "PyStrings.h"
4#include "TPyClassGenerator.h"
5#include "Utility.h"
6
7#include "CPyCppyy/PyResult.h"
8
9// TODO: not sure if any of this still makes sense ...
10#if 0
11
12// ROOT
13#include "TClass.h"
14#include "TInterpreter.h"
15#include "TROOT.h"
16
17// Standard
18#include <sstream>
19#include <string>
20#include <typeinfo>
21
22
23//= helper ==================================================================
24namespace {
25
26 class PyGILRAII {
27 PyGILState_STATE m_GILState;
28 public:
29 PyGILRAII() : m_GILState(PyGILState_Ensure()) {}
30 ~PyGILRAII() { PyGILState_Release(m_GILState); }
31 };
32
33} // unnamed namespace
34
35
36//- public members -----------------------------------------------------------
37TClass* TPyClassGenerator::GetClass( const char* name, bool load )
38{
39// Just forward.
40 return GetClass( name, load, false );
41}
42
43//- public members -----------------------------------------------------------
44TClass* TPyClassGenerator::GetClass( const char* name, bool load, bool silent )
45{
46// Class generator to make python classes available to Cling
47
48// called if all other class generators failed, attempt to build from python class
49 if ( PyROOT::gDictLookupActive == true )
50 return 0; // call originated from python
51
52 if ( ! load || ! name )
53 return 0;
54
55 PyGILRAII thePyGILRAII;
56
57// first, check whether the name is of a module
58 PyObject* modules = PySys_GetObject( const_cast<char*>("modules") );
59 PyObject* pyname = PyROOT_PyUnicode_FromString( name );
60 PyObject* keys = PyDict_Keys( modules );
61 bool isModule = PySequence_Contains( keys, pyname );
62 Py_DECREF( keys );
63 Py_DECREF( pyname );
64
65 if ( isModule ) {
66 // the normal TClass::GetClass mechanism doesn't allow direct returns, so
67 // do our own check
68 TClass* cl = (TClass*)gROOT->GetListOfClasses()->FindObject( name );
69 if ( cl ) return cl;
70
71 std::ostringstream nsCode;
72 nsCode << "namespace " << name << " {\n";
73
74 // add all free functions
75 PyObject* mod = PyDict_GetItemString( modules, const_cast<char*>(name) );
76 PyObject* dct = PyModule_GetDict( mod );
77 keys = PyDict_Keys( dct );
78
79 for ( int i = 0; i < PyList_GET_SIZE( keys ); ++i ) {
80 PyObject* key = PyList_GET_ITEM( keys, i );
81 Py_INCREF( key );
82
83 PyObject* attr = PyDict_GetItem( dct, key );
84 Py_INCREF( attr );
85
86 // TODO: refactor the code below with the class method code
87 if ( PyCallable_Check( attr ) && \
88 ! (PyClass_Check( attr ) || PyObject_HasAttr( attr, PyROOT::PyStrings::gBases )) ) {
89 std::string func_name = PyROOT_PyUnicode_AsString( key );
90
91 // figure out number of variables required
92 PyObject* func_code = PyObject_GetAttrString( attr, (char*)"func_code" );
93 PyObject* var_names =
94 func_code ? PyObject_GetAttrString( func_code, (char*)"co_varnames" ) : NULL;
95 int nVars = var_names ? PyTuple_GET_SIZE( var_names ) : 0 /* TODO: probably large number, all default? */;
96 if ( nVars < 0 ) nVars = 0;
97 Py_XDECREF( var_names );
98 Py_XDECREF( func_code );
99
100 nsCode << " TPyReturn " << func_name << "(";
101 for ( int ivar = 0; ivar < nVars; ++ivar ) {
102 nsCode << "const TPyArg& a" << ivar;
103 if ( ivar != nVars-1 ) nsCode << ", ";
104 }
105 nsCode << ") {\n";
106 nsCode << " std::vector<TPyArg> v; v.reserve(" << nVars << ");\n";
107
108 // add the variables
109 for ( int ivar = 0; ivar < nVars; ++ ivar )
110 nsCode << " v.push_back(a" << ivar << ");\n";
111
112 // call dispatch (method or class pointer hard-wired)
113 nsCode << " return TPyReturn(TPyArg::CallMethod((PyObject*)" << (void*)attr << ", v)); }\n";
114 }
115
116 Py_DECREF( attr );
117 Py_DECREF( key );
118 }
119
120 Py_DECREF( keys );
121
122 nsCode << " }";
123
124 if ( gInterpreter->LoadText( nsCode.str().c_str() ) ) {
125 TClass* klass = new TClass( name, silent );
126 TClass::AddClass( klass );
127 return klass;
128 }
129
130 return nullptr;
131 }
132
133// determine module and class name part
134 std::string clName = name;
135 std::string::size_type pos = clName.rfind( '.' );
136
137 if ( pos == std::string::npos )
138 return 0; // this isn't a python style class
139
140 std::string mdName = clName.substr( 0, pos );
141 clName = clName.substr( pos+1, std::string::npos );
142
143// create class in namespace, if it exists (no load, silent)
144 bool useNS = gROOT->GetListOfClasses()->FindObject( mdName.c_str() ) != 0;
145 if ( ! useNS ) {
146 // the class itself may exist if we're using the global scope
147 TClass* cl = (TClass*)gROOT->GetListOfClasses()->FindObject( clName.c_str() );
148 if ( cl ) return cl;
149 }
150
151// locate and get class
152 PyObject* mod = PyImport_AddModule( const_cast< char* >( mdName.c_str() ) );
153 if ( ! mod ) {
154 PyErr_Clear();
155 return 0; // module apparently disappeared
156 }
157
158 Py_INCREF( mod );
159 PyObject* pyclass =
160 PyDict_GetItemString( PyModule_GetDict( mod ), const_cast< char* >( clName.c_str() ) );
161 Py_XINCREF( pyclass );
162 Py_DECREF( mod );
163
164 if ( ! pyclass ) {
165 PyErr_Clear(); // the class is no longer available?!
166 return 0;
167 }
168
169// get a listing of all python-side members
170 PyObject* attrs = PyObject_Dir( pyclass );
171 if ( ! attrs ) {
172 PyErr_Clear();
173 Py_DECREF( pyclass );
174 return 0;
175 }
176
177// pre-amble Cling proxy class
178 std::ostringstream proxyCode;
179 if ( useNS ) proxyCode << "namespace " << mdName << " { ";
180 proxyCode << "class " << clName << " {\nprivate:\n PyObject* fPyObject;\npublic:\n";
181
182// loop over and add member functions
183 bool hasConstructor = false, hasDestructor = false;
184 for ( int i = 0; i < PyList_GET_SIZE( attrs ); ++i ) {
185 PyObject* label = PyList_GET_ITEM( attrs, i );
186 Py_INCREF( label );
187 PyObject* attr = PyObject_GetAttr( pyclass, label );
188
189 // collect only member functions (i.e. callable elements in __dict__)
190 if ( PyCallable_Check( attr ) ) {
191 std::string mtName = PyROOT_PyUnicode_AsString( label );
192
193 if ( mtName == "__del__" ) {
194 hasDestructor = true;
195 proxyCode << " ~" << clName << "() { TPyArg::CallDestructor(fPyObject); }\n";
196 continue;
197 }
198
199 bool isConstructor = mtName == "__init__";
200 if ( !isConstructor && mtName.find("__", 0, 2) == 0 )
201 continue; // skip all other python special funcs
202
203 // figure out number of variables required
204#if PY_VERSION_HEX < 0x03000000
205 PyObject* im_func = PyObject_GetAttrString( attr, (char*)"im_func" );
206 PyObject* func_code =
207 im_func ? PyObject_GetAttrString( im_func, (char*)"func_code" ) : NULL;
208#else
209 PyObject* func_code = PyObject_GetAttrString( attr, "__code__" );
210#endif
211 PyObject* var_names =
212 func_code ? PyObject_GetAttrString( func_code, (char*)"co_varnames" ) : NULL;
213 if (PyErr_Occurred()) PyErr_Clear(); // happens for slots; default to 0 arguments
214
215 int nVars = var_names ? PyTuple_GET_SIZE( var_names ) - 1 /* self */ : 0 /* TODO: probably large number, all default? */;
216 if ( nVars < 0 ) nVars = 0;
217 Py_XDECREF( var_names );
218 Py_XDECREF( func_code );
219#if PY_VERSION_HEX < 0x03000000
220 Py_XDECREF( im_func );
221#endif
222
223 // method declaration as appropriate
224 if ( isConstructor ) {
225 hasConstructor = true;
226 proxyCode << " " << clName << "(";
227 } else // normal method
228 proxyCode << " TPyReturn " << mtName << "(";
229 for ( int ivar = 0; ivar < nVars; ++ivar ) {
230 proxyCode << "const TPyArg& a" << ivar;
231 if ( ivar != nVars-1 ) proxyCode << ", ";
232 }
233 proxyCode << ") {\n";
234 proxyCode << " std::vector<TPyArg> v; v.reserve(" << nVars+(isConstructor ? 0 : 1) << ");\n";
235
236 // add the 'self' argument as appropriate
237 if ( ! isConstructor )
238 proxyCode << " v.push_back(fPyObject);\n";
239
240 // then add the remaining variables
241 for ( int ivar = 0; ivar < nVars; ++ ivar )
242 proxyCode << " v.push_back(a" << ivar << ");\n";
243
244 // call dispatch (method or class pointer hard-wired)
245 if ( ! isConstructor )
246 proxyCode << " return TPyReturn(TPyArg::CallMethod((PyObject*)" << (void*)attr << ", v))";
247 else
248 proxyCode << " TPyArg::CallConstructor(fPyObject, (PyObject*)" << (void*)pyclass << ", v)";
249 proxyCode << ";\n }\n";
250 }
251
252 // no decref of attr for now (b/c of hard-wired ptr); need cleanup somehow
253 Py_DECREF( label );
254 }
255
256// special case if no constructor or destructor
257 if ( ! hasConstructor )
258 proxyCode << " " << clName << "() {\n TPyArg::CallConstructor(fPyObject, (PyObject*)" << (void*)pyclass << "); }\n";
259
260 if ( ! hasDestructor )
261 proxyCode << " ~" << clName << "() { TPyArg::CallDestructor(fPyObject); }\n";
262
263// for now, don't allow copying (ref-counting wouldn't work as expected anyway)
264 proxyCode << " " << clName << "(const " << clName << "&) = delete;\n";
265 proxyCode << " " << clName << "& operator=(const " << clName << "&) = delete;\n";
266
267// closing and building of Cling proxy class
268 proxyCode << "};";
269 if ( useNS ) proxyCode << " }";
270
271 Py_DECREF( attrs );
272// done with pyclass, decref here, assuming module is kept
273 Py_DECREF( pyclass );
274
275// body compilation
276 if ( ! gInterpreter->LoadText( proxyCode.str().c_str() ) )
277 return nullptr;
278
279// done, let ROOT manage the new class
280 TClass* klass = new TClass( useNS ? (mdName+"::"+clName).c_str() : clName.c_str(), silent );
281 TClass::AddClass( klass );
282
283 return klass;
284}
285
286////////////////////////////////////////////////////////////////////////////////
287/// Just forward; based on type name only.
288
289TClass* TPyClassGenerator::GetClass( const std::type_info& typeinfo, bool load, bool silent )
290{
291 return GetClass( typeinfo.name(), load, silent );
292}
293
294////////////////////////////////////////////////////////////////////////////////
295/// Just forward; based on type name only
296
297TClass* TPyClassGenerator::GetClass( const std::type_info& typeinfo, bool load )
298{
299 return GetClass( typeinfo.name(), load );
300}
301#endif
_object PyObject
Definition: PyMethodBase.h:41
char name[80]
Definition: TGX11.cxx:109
#define gInterpreter
Definition: TInterpreter.h:556
#define pyname
Definition: TMCParticle.cxx:19
#define gROOT
Definition: TROOT.h:406
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition: TClass.h:80
static void AddClass(TClass *cl)
static: Add a class to the list and map of classes.
Definition: TClass.cxx:492
virtual TClass * GetClass(const char *name, Bool_t load)
PyObject * gBases
Definition: PyStrings.cxx:8
bool gDictLookupActive
Definition: Utility.cxx:27