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