Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
PyRootType.cxx
Go to the documentation of this file.
1 // @(#)root/pyroot:$Id$
2 // Author: Wim Lavrijsen, Jan 2005
3 
4 // Bindings
5 #include "PyROOT.h"
6 #include "PyRootType.h"
7 #include "MethodProxy.h"
8 #include "PropertyProxy.h"
9 #include "RootWrapper.h"
10 #include "TFunctionHolder.h"
11 #include "TemplateProxy.h"
12 #include "PyStrings.h"
13 
14 // ROOT
15 #include "TClass.h" // for method and enum finding
16 #include "TList.h" // id.
17 
18 // Standard
19 #include <string.h>
20 #include <string>
21 #include <vector>
22 
23 namespace PyROOT {
24 
25 namespace {
26 
27 //= PyROOT type proxy construction/destruction ===============================
28  PyObject* meta_alloc( PyTypeObject* metatype, Py_ssize_t nitems )
29  {
30  // specialized allocator, fitting in a few extra bytes for a TClassRef
31  PyObject* pyclass = PyType_Type.tp_alloc( metatype, nitems );
32 
33  return pyclass;
34  }
35 
36 ////////////////////////////////////////////////////////////////////////////////
37 
38  void meta_dealloc( PyRootClass* pytype )
39  {
40  return PyType_Type.tp_dealloc( (PyObject*)pytype );
41  }
42 
43 ////////////////////////////////////////////////////////////////////////////////
44 /// Called when PyRootType acts as a metaclass; since type_new always resets
45 /// tp_alloc, and since it does not call tp_init on types, the metaclass is
46 /// being fixed up here, and the class is initialized here as well.
47 
48  PyObject* pt_new( PyTypeObject* subtype, PyObject* args, PyObject* kwds )
49  {
50  // fixup of metaclass (left permanent, and in principle only called once b/c
51  // PyROOT caches python classes)
52  subtype->tp_alloc = (allocfunc)meta_alloc;
53  subtype->tp_dealloc = (destructor)meta_dealloc;
54 
55  // creation of the python-side class
56  PyRootClass* result = (PyRootClass*)PyType_Type.tp_new( subtype, args, kwds );
57 
58  // initialization of class (based on name only, initially, which is lazy)
59 
60  // there's a snag here: if a python class is derived from the bound class,
61  // the name will not be known by TClassRef, hence we'll use the meta class
62  // name from the subtype, rather than given class name
63 
64  const char* mp = strstr( subtype->tp_name, "_meta" );
65  if ( ! mp ) {
66  // there has been a user meta class override in a derived class, so do
67  // the consistent thing, thus allowing user control over naming
68  result->fCppType = Cppyy::GetScope(
69  PyROOT_PyUnicode_AsString( PyTuple_GET_ITEM( args, 0 ) ) );
70  } else {
71  // coming here from PyROOT, use meta class name instead of given name,
72  // so that it is safe to inherit python classes from the bound class
73  result->fCppType = Cppyy::GetScope(
74  std::string( subtype->tp_name ).substr( 0, mp-subtype->tp_name ).c_str() );
75  }
76 
77  return (PyObject*)result;
78  }
79 
80 
81 //= PyROOT type metaclass behavior ===========================================
82  PyObject* pt_getattro( PyObject* pyclass, PyObject* pyname )
83  {
84  // normal type lookup
85  PyObject* attr = PyType_Type.tp_getattro( pyclass, pyname );
86 
87  // extra ROOT lookup in case of failure (e.g. for inner classes on demand)
88  if ( ! attr && PyROOT_PyUnicode_CheckExact( pyname ) ) {
89  PyObject *etype, *value, *trace;
90  PyErr_Fetch( &etype, &value, &trace ); // clears current exception
91 
92  // filter for python specials and lookup qualified class or function
93  std::string name = PyROOT_PyUnicode_AsString( pyname );
94 
95  if ( name.size() <= 2 || name.substr( 0, 2 ) != "__" ) {
96  attr = CreateScopeProxy( name, pyclass );
97 
98  // namespaces may have seen updates in their list of global functions, which
99  // are available as "methods" even though they're not really that
100  if ( ! attr && ! PyRootType_CheckExact( pyclass ) && PyType_Check( pyclass ) ) {
101  PyErr_Clear();
102  PyObject* pycppname = PyObject_GetAttr( pyclass, PyStrings::gCppName );
103  const char* cppname = PyROOT_PyUnicode_AsString(pycppname);
104  Py_DECREF(pycppname);
105  Cppyy::TCppScope_t scope = Cppyy::GetScope( cppname );
106  TClass* klass = TClass::GetClass( cppname );
107  if ( Cppyy::IsNamespace( scope ) ) {
108  // tickle lazy lookup of functions
109  if ( ! attr ) {
110  TObject *methObj = nullptr;
111  if ( (methObj = klass->GetListOfMethods()->FindObject(name.c_str())) ) {
112  // function exists, now collect overloads
113  auto completeName = methObj->GetName();
114  std::vector< PyCallable* > overloads;
115  const size_t nmeth = Cppyy::GetNumMethods( scope );
116  for ( size_t imeth = 0; imeth < nmeth; ++imeth ) {
117  Cppyy::TCppMethod_t method = Cppyy::GetMethod( scope, imeth );
118  auto currentName = Cppyy::GetMethodName(method);
119  // We need to compare with a final complete name, where:
120  // - Typedefs are resolved (e.g. Float_t -> float)
121  // - Namespaces are added (e.g. vector<float> -> std::vector<float>
122  // - All template types are included (e.g. std::vector<float> ->
123  // std::vector<float, std::allocator<float>>)
124  if (currentName == completeName)
125  overloads.push_back( new TFunctionHolder( scope, method ) );
126  }
127 
128  // Note: can't re-use Utility::AddClass here, as there's the risk of
129  // a recursive call. Simply add method directly, as we're guaranteed
130  // that it doesn't exist yet.
131  attr = (PyObject*)MethodProxy_New( name.c_str(), overloads );
132  }
133  }
134 
135  // tickle lazy lookup of data members
136  if ( ! attr ) {
137  Cppyy::TCppIndex_t dmi = Cppyy::GetDatamemberIndex( scope, name );
138  if ( 0 <= dmi ) attr = (PyObject*)PropertyProxy_New( scope, dmi );
139  }
140  }
141 
142  // function templates that have not been instantiated
143  if ( ! attr && klass ) {
144  TFunctionTemplate* tmpl = klass->GetFunctionTemplate( name.c_str() );
145  if ( tmpl )
146  attr = (PyObject*)TemplateProxy_New( name, pyclass );
147  }
148 
149  // enums types requested as type (rather than the constants)
150  if (!attr) {
151  if (Cppyy::IsEnum(Cppyy::GetScopedFinalName(scope)+"::"+name)) {
152  // enum types (incl. named and class enums)
153  Cppyy::TCppEnum_t enumtype = Cppyy::GetEnum(scope, name);
154  if (enumtype) {
155  // collect the enum values
156  Cppyy::TCppIndex_t ndata = Cppyy::GetNumEnumData(enumtype);
157  PyObject* dct = PyDict_New();
158  for (Cppyy::TCppIndex_t idata = 0; idata < ndata; ++idata) {
159  PyObject* val = PyLong_FromLongLong(Cppyy::GetEnumDataValue(enumtype, idata));
160  PyDict_SetItemString(dct, Cppyy::GetEnumDataName(enumtype, idata).c_str(), val);
161  Py_DECREF(val);
162  }
163 
164  // add the __cppname__ for templates
165  PyObject* cppnamepy = PyROOT_PyUnicode_FromString((Cppyy::GetScopedFinalName(scope)+"::"+name).c_str());
166  PyDict_SetItem(dct, PyStrings::gCppName, cppnamepy);
167  // add also __cpp_name__ for forward compatibility
168  PyDict_SetItem(dct, PyStrings::gCppNameNew, cppnamepy);
169  Py_DECREF(cppnamepy);
170 
171  // create new type with labeled values in place
172  PyObject* pybases = PyTuple_New(1);
173  Py_INCREF(&PyInt_Type);
174  PyTuple_SET_ITEM(pybases, 0, (PyObject*)&PyInt_Type);
175  PyObject* args = Py_BuildValue((char*)"sOO", name.c_str(), pybases, dct);
176  attr = Py_TYPE(&PyInt_Type)->tp_new(Py_TYPE(&PyInt_Type), args, nullptr);
177  Py_DECREF(args);
178  Py_DECREF(pybases);
179  Py_DECREF(dct);
180  } else {
181  // presumably not a class enum; simply pretend int
182  Py_INCREF(&PyInt_Type);
183  attr = (PyObject*)&PyInt_Type;
184  }
185  }
186  }
187 
188  if ( attr ) {
189  PyObject_SetAttr( pyclass, pyname, attr );
190  Py_DECREF( attr );
191  attr = PyType_Type.tp_getattro( pyclass, pyname );
192  }
193  }
194 
195  if ( ! attr && ! PyRootType_Check( pyclass ) /* at global or module-level only */ ) {
196  PyErr_Clear();
197  // get class name to look up CINT tag info ...
198  attr = GetCppGlobal( name /*, tag */ );
199  if ( PropertyProxy_Check( attr ) ) {
200  PyObject_SetAttr( (PyObject*)Py_TYPE(pyclass), pyname, attr );
201  Py_DECREF( attr );
202  attr = PyType_Type.tp_getattro( pyclass, pyname );
203  } else if ( attr )
204  PyObject_SetAttr( pyclass, pyname, attr );
205  }
206 
207  }
208 
209  // if failed, then the original error is likely to be more instructive
210  if ( ! attr && etype )
211  PyErr_Restore( etype, value, trace );
212  else if ( ! attr ) {
213  PyObject* sklass = PyObject_Str( pyclass );
214  PyErr_Format( PyExc_AttributeError, "%s has no attribute \'%s\'",
215  PyROOT_PyUnicode_AsString( sklass ), PyROOT_PyUnicode_AsString( pyname ) );
216  Py_DECREF( sklass );
217  }
218 
219  // attribute is cached, if found
220  }
221 
222  return attr;
223  }
224 
225 } // unnamed namespace
226 
227 
228 //= PyROOT object proxy type type ============================================
229 PyTypeObject PyRootType_Type = {
230  PyVarObject_HEAD_INIT( &PyType_Type, 0 )
231  (char*)"ROOT.PyRootType", // tp_name
232  sizeof(PyROOT::PyRootClass),// tp_basicsize
233  0, // tp_itemsize
234  0, // tp_dealloc
235  0, // tp_print
236  0, // tp_getattr
237  0, // tp_setattr
238  0, // tp_compare
239  0, // tp_repr
240  0, // tp_as_number
241  0, // tp_as_sequence
242  0, // tp_as_mapping
243  0, // tp_hash
244  0, // tp_call
245  0, // tp_str
246  (getattrofunc)pt_getattro, // tp_getattro
247  0, // tp_setattro
248  0, // tp_as_buffer
249  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags
250  (char*)"PyROOT metatype (internal)", // tp_doc
251  0, // tp_traverse
252  0, // tp_clear
253  0, // tp_richcompare
254  0, // tp_weaklistoffset
255  0, // tp_iter
256  0, // tp_iternext
257  0, // tp_methods
258  0, // tp_members
259  0, // tp_getset
260  &PyType_Type, // tp_base
261  0, // tp_dict
262  0, // tp_descr_get
263  0, // tp_descr_set
264  0, // tp_dictoffset
265  0, // tp_init
266  0, // tp_alloc
267  (newfunc)pt_new, // tp_new
268  0, // tp_free
269  0, // tp_is_gc
270  0, // tp_bases
271  0, // tp_mro
272  0, // tp_cache
273  0, // tp_subclasses
274  0 // tp_weaklist
275 #if PY_VERSION_HEX >= 0x02030000
276  , 0 // tp_del
277 #endif
278 #if PY_VERSION_HEX >= 0x02060000
279  , 0 // tp_version_tag
280 #endif
281 #if PY_VERSION_HEX >= 0x03040000
282  , 0 // tp_finalize
283 #endif
284 };
285 
286 } // namespace PyROOT