Logo ROOT   6.18/05
Reference Guide
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
23namespace PyROOT {
24
25namespace {
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 ) {
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) {
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
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 Py_DECREF(cppnamepy);
168
169 // create new type with labeled values in place
170 PyObject* pybases = PyTuple_New(1);
171 Py_INCREF(&PyInt_Type);
172 PyTuple_SET_ITEM(pybases, 0, (PyObject*)&PyInt_Type);
173 PyObject* args = Py_BuildValue((char*)"sOO", name.c_str(), pybases, dct);
174 attr = Py_TYPE(&PyInt_Type)->tp_new(Py_TYPE(&PyInt_Type), args, nullptr);
175 Py_DECREF(args);
176 Py_DECREF(pybases);
177 Py_DECREF(dct);
178 } else {
179 // presumably not a class enum; simply pretend int
180 Py_INCREF(&PyInt_Type);
181 attr = (PyObject*)&PyInt_Type;
182 }
183 }
184 }
185
186 if ( attr ) {
187 PyObject_SetAttr( pyclass, pyname, attr );
188 Py_DECREF( attr );
189 attr = PyType_Type.tp_getattro( pyclass, pyname );
190 }
191 }
192
193 if ( ! attr && ! PyRootType_Check( pyclass ) /* at global or module-level only */ ) {
194 PyErr_Clear();
195 // get class name to look up CINT tag info ...
196 attr = GetCppGlobal( name /*, tag */ );
197 if ( PropertyProxy_Check( attr ) ) {
198 PyObject_SetAttr( (PyObject*)Py_TYPE(pyclass), pyname, attr );
199 Py_DECREF( attr );
200 attr = PyType_Type.tp_getattro( pyclass, pyname );
201 } else if ( attr )
202 PyObject_SetAttr( pyclass, pyname, attr );
203 }
204
205 }
206
207 // if failed, then the original error is likely to be more instructive
208 if ( ! attr && etype )
209 PyErr_Restore( etype, value, trace );
210 else if ( ! attr ) {
211 PyObject* sklass = PyObject_Str( pyclass );
212 PyErr_Format( PyExc_AttributeError, "%s has no attribute \'%s\'",
214 Py_DECREF( sklass );
215 }
216
217 // attribute is cached, if found
218 }
219
220 return attr;
221 }
222
223} // unnamed namespace
224
225#if !defined(_MSC_VER)
226#pragma GCC diagnostic push
227#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
228#endif
229
230//= PyROOT object proxy type type ============================================
231PyTypeObject PyRootType_Type = {
232 PyVarObject_HEAD_INIT( &PyType_Type, 0 )
233 (char*)"ROOT.PyRootType", // tp_name
234 sizeof(PyROOT::PyRootClass),// tp_basicsize
235 0, // tp_itemsize
236 0, // tp_dealloc
237 0, // tp_print (python < 3.8)
238 // tp_vectorcall_offset (python >= 3.8)
239 0, // tp_getattr
240 0, // tp_setattr
241 0, // tp_compare
242 0, // tp_repr
243 0, // tp_as_number
244 0, // tp_as_sequence
245 0, // tp_as_mapping
246 0, // tp_hash
247 0, // tp_call
248 0, // tp_str
249 (getattrofunc)pt_getattro, // tp_getattro
250 0, // tp_setattro
251 0, // tp_as_buffer
252 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags
253 (char*)"PyROOT metatype (internal)", // tp_doc
254 0, // tp_traverse
255 0, // tp_clear
256 0, // tp_richcompare
257 0, // tp_weaklistoffset
258 0, // tp_iter
259 0, // tp_iternext
260 0, // tp_methods
261 0, // tp_members
262 0, // tp_getset
263 &PyType_Type, // tp_base
264 0, // tp_dict
265 0, // tp_descr_get
266 0, // tp_descr_set
267 0, // tp_dictoffset
268 0, // tp_init
269 0, // tp_alloc
270 (newfunc)pt_new, // tp_new
271 0, // tp_free
272 0, // tp_is_gc
273 0, // tp_bases
274 0, // tp_mro
275 0, // tp_cache
276 0, // tp_subclasses
277 0 // tp_weaklist
278#if PY_VERSION_HEX >= 0x02030000
279 , 0 // tp_del
280#endif
281#if PY_VERSION_HEX >= 0x02060000
282 , 0 // tp_version_tag
283#endif
284#if PY_VERSION_HEX >= 0x03040000
285 , 0 // tp_finalize
286#endif
287#if PY_VERSION_HEX >= 0x03080000
288 , 0 // tp_vectorcall
289#if PY_VERSION_HEX < 0x03090000
290 , 0 // tp_print (python 3.8 only)
291#endif
292#endif
293};
294
295#if !defined(_MSC_VER)
296#pragma GCC diagnostic pop
297#endif
298
299} // namespace PyROOT
#define Py_TYPE(ob)
Definition: PyROOT.h:161
int Py_ssize_t
Definition: PyROOT.h:166
#define PyROOT_PyUnicode_AsString
Definition: PyROOT.h:78
#define PyROOT_PyUnicode_FromString
Definition: PyROOT.h:82
#define PyROOT_PyUnicode_CheckExact
Definition: PyROOT.h:77
#define PyVarObject_HEAD_INIT(type, size)
Definition: PyROOT.h:159
char name[80]
Definition: TGX11.cxx:109
#define pyname
Definition: TMCParticle.cxx:19
_object PyObject
Definition: TPyArg.h:20
Type object to hold TClassRef instance (this is only semantically a presentation of PyRootType instan...
Definition: PyRootType.h:37
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition: TClass.h:75
TList * GetListOfMethods(Bool_t load=kTRUE)
Return list containing the TMethods of a class.
Definition: TClass.cxx:3684
TFunctionTemplate * GetFunctionTemplate(const char *name)
Definition: TClass.cxx:3496
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition: TClass.cxx:2895
Dictionary for function template This class describes one single function template.
virtual TObject * FindObject(const char *name) const
Find an object in this list using its name.
Definition: TList.cxx:575
Mother of all ROOT objects.
Definition: TObject.h:37
virtual const char * GetName() const
Returns name of object.
Definition: TObject.cxx:357
ptrdiff_t TCppScope_t
Definition: Cppyy.h:15
Bool_t IsNamespace(TCppScope_t scope)
Definition: Cppyy.cxx:556
std::string GetMethodName(TCppMethod_t)
Definition: Cppyy.cxx:753
TCppIndex_t GetNumMethods(TCppScope_t scope)
Definition: Cppyy.cxx:679
long long GetEnumDataValue(TCppEnum_t, TCppIndex_t idata)
Definition: Cppyy.cxx:1120
std::string GetScopedFinalName(TCppType_t type)
Definition: Cppyy.cxx:590
Bool_t IsEnum(const std::string &type_name)
Definition: Cppyy.cxx:575
std::string GetEnumDataName(TCppEnum_t, TCppIndex_t idata)
Definition: Cppyy.cxx:1115
void * TCppEnum_t
Definition: Cppyy.h:19
TCppIndex_t GetDatamemberIndex(TCppScope_t scope, const std::string &name)
Definition: Cppyy.cxx:1009
Long_t TCppIndex_t
Definition: Cppyy.h:21
TCppScope_t GetScope(const std::string &scope_name)
Definition: Cppyy.cxx:193
TCppEnum_t GetEnum(TCppScope_t scope, const std::string &enum_name)
Definition: Cppyy.cxx:1098
ptrdiff_t TCppMethod_t
Definition: Cppyy.h:18
TCppMethod_t GetMethod(TCppScope_t scope, TCppIndex_t imeth)
Definition: Cppyy.cxx:747
TCppIndex_t GetNumEnumData(TCppEnum_t)
Definition: Cppyy.cxx:1110
R__EXTERN PyObject * gCppName
Definition: PyStrings.h:34
PyTypeObject PyRootType_Type
Definition: PyRootType.cxx:231
Bool_t PyRootType_CheckExact(T *object)
Definition: PyRootType.h:56
Bool_t PyRootType_Check(T *object)
Definition: PyRootType.h:50
TemplateProxy * TemplateProxy_New(const std::string &name, PyObject *pyclass)
Definition: TemplateProxy.h:62
MethodProxy * MethodProxy_New(const std::string &name, std::vector< PyCallable * > &methods)
Definition: MethodProxy.h:75
PyObject * CreateScopeProxy(Cppyy::TCppScope_t)
Convenience function with a lookup first through the known existing proxies.
PropertyProxy * PropertyProxy_New(Cppyy::TCppScope_t scope, Cppyy::TCppIndex_t idata)
Definition: PropertyProxy.h:62
PyObject * GetCppGlobal(const std::string &name)
try named global variable/enum (first ROOT, then Cling: sync is too slow)
Bool_t PropertyProxy_Check(T *object)
Definition: PropertyProxy.h:50