Logo ROOT  
Reference Guide
ObjectProxy.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 "PyStrings.h"
7#include "ObjectProxy.h"
8#include "RootWrapper.h"
9#include "Utility.h"
10
11// ROOT
12#include "TBufferFile.h" // for pickling
13#include "TClass.h" // id.
14#include "TObject.h" // for gROOT life-check
15#include "TROOT.h" // id.
16
17// Standard
18#include <algorithm>
19
20
21//______________________________________________________________________________
22// Python-side proxy objects
23// =========================
24//
25// C++ objects are represented in Python by ObjectProxy's, which encapsulate
26// them using either a pointer (normal), pointer-to-pointer (kIsReference set),
27// or as an owned value (kIsValue set). Objects held as reference are never
28// owned, otherwise the object is owned if kIsOwner is set.
29//
30// In addition to encapsulation, ObjectProxy offers pickling (using TBufferFile
31// with a copy into a Python string); rudimentary comparison operators (based on
32// pointer value and class comparisons); stubs for numeric operators; and a
33// representation that prints the C++ pointer values, rather than the PyObject*
34// ones as is the default.
35
36
37//- data _______________________________________________________________________
38namespace PyROOT {
39 R__EXTERN PyObject* gRootModule; // needed for pickling
40}
41
42
43////////////////////////////////////////////////////////////////////////////////
44/// Destroy the held C++ object, if owned; does not deallocate the proxy.
45
47 if ( gROOT && !gROOT->TestBit( TObject::kInvalidObject ) ) {
48 if ( pyobj->fFlags & ObjectProxy::kIsValue ) {
49 if ( ! (pyobj->fFlags & ObjectProxy::kIsSmartPtr) ) {
50 Cppyy::CallDestructor( pyobj->ObjectIsA(), pyobj->GetObject() );
51 Cppyy::Deallocate( pyobj->ObjectIsA(), pyobj->GetObject() );
52 } else {
55 }
56 }
57 else if ( pyobj->fObject && ( pyobj->fFlags & ObjectProxy::kIsOwner ) ) {
58 if ( ! (pyobj->fFlags & ObjectProxy::kIsSmartPtr) ) {
59 Cppyy::Destruct( pyobj->ObjectIsA(), pyobj->GetObject() );
60 } else {
61 Cppyy::Destruct( pyobj->fSmartPtrType, pyobj->fSmartPtr );
62 }
63 }
64 }
65 pyobj->fObject = NULL;
66}
67
68
69////////////////////////////////////////////////////////////////////////////////
70
71namespace PyROOT {
72namespace {
73
74//= PyROOT object proxy null-ness checking ===================================
75 PyObject* op_nonzero( ObjectProxy* self )
76 {
77 // Null of the proxy is determined by null-ness of the held C++ object.
78 PyObject* result = self->GetObject() ? Py_True : Py_False;
79 Py_INCREF( result );
80 return result;
81 }
82
83//= PyROOT object explicit destruction =======================================
84 PyObject* op_destruct( ObjectProxy* self )
85 {
86 // User access to force deletion of the object. Needed in case of a true
87 // garbage collector (like in PyPy), to allow the user control over when
88 // the C++ destructor is called. This method requires that the C++ object
89 // is owned (no-op otherwise).
90 op_dealloc_nofree( self );
91 Py_INCREF( Py_None );
92 return Py_None;
93 }
94
95//= PyROOT object proxy pickle support =======================================
96 PyObject* op_reduce( ObjectProxy* self )
97 {
98 // Turn the object proxy instance into a character stream and return for
99 // pickle, together with the callable object that can restore the stream
100 // into the object proxy instance.
101
102 // keep a borrowed reference around to the callable function for expanding;
103 // because it is borrowed, it means that there can be no pickling during the
104 // shutdown of the libPyROOT module
105 static PyObject* s_expand = PyDict_GetItemString(
106 PyModule_GetDict( gRootModule ), const_cast< char* >( "_ObjectProxy__expand__" ) );
107
108 // TBuffer and its derived classes can't write themselves, but can be created
109 // directly from the buffer, so handle them in a special case
110 static Cppyy::TCppType_t s_bfClass = Cppyy::GetScope( "TBufferFile" );
111
112 TBufferFile* buff = 0;
113 if ( s_bfClass == self->ObjectIsA() ) {
114 buff = (TBufferFile*)self->GetObject();
115 } else {
116 // no cast is needed, but WriteObject taking a TClass argument is protected,
117 // so use WriteObjectAny()
118 static TBufferFile s_buff( TBuffer::kWrite );
119 s_buff.Reset();
120 if ( s_buff.WriteObjectAny( self->GetObject(),
121 TClass::GetClass( Cppyy::GetFinalName( self->ObjectIsA() ).c_str() ) ) != 1 ) {
122 PyErr_Format( PyExc_IOError,
123 "could not stream object of type %s", Cppyy::GetFinalName( self->ObjectIsA() ).c_str() );
124 return 0;
125 }
126 buff = &s_buff;
127 }
128
129 // use a string for the serialized result, as a python buffer will not copy
130 // the buffer contents; use a string for the class name, used when casting
131 // on reading back in (see RootModule.cxx:TObjectExpand)
132 PyObject* res2 = PyTuple_New( 2 );
133 PyTuple_SET_ITEM( res2, 0, PyBytes_FromStringAndSize( buff->Buffer(), buff->Length() ) );
134 PyTuple_SET_ITEM( res2, 1, PyBytes_FromString( Cppyy::GetFinalName( self->ObjectIsA() ).c_str() ) );
135
136 PyObject* result = PyTuple_New( 2 );
137 Py_INCREF( s_expand );
138 PyTuple_SET_ITEM( result, 0, s_expand );
139 PyTuple_SET_ITEM( result, 1, res2 );
140
141 return result;
142 }
143
144//= PyROOT object dispatch support ===========================================
145 PyObject* op_dispatch( PyObject* self, PyObject* args, PyObject* /* kdws */ )
146 {
147 // User-side __dispatch__ method to allow selection of a specific overloaded
148 // method. The actual selection is in the disp() method of MethodProxy.
149 PyObject *mname = 0, *sigarg = 0;
150 if ( ! PyArg_ParseTuple( args, const_cast< char* >( "O!O!:__dispatch__" ),
151 &PyROOT_PyUnicode_Type, &mname, &PyROOT_PyUnicode_Type, &sigarg ) )
152 return 0;
153
154 // get the named overload
155 PyObject* pymeth = PyObject_GetAttr( self, mname );
156 if ( ! pymeth )
157 return 0;
158
159 // get the 'disp' method to allow overload selection
160 PyObject* pydisp = PyObject_GetAttrString( pymeth, const_cast<char*>( "disp" ) );
161 if ( ! pydisp ) {
162 Py_DECREF( pymeth );
163 return 0;
164 }
165
166 // finally, call dispatch to get the specific overload
167 PyObject* oload = PyObject_CallFunctionObjArgs( pydisp, sigarg, NULL );
168 Py_DECREF( pydisp );
169 Py_DECREF( pymeth );
170 return oload;
171 }
172
173//= PyROOT smart pointer support =============================================
174 PyObject* op_get_smart_ptr( ObjectProxy* self )
175 {
176 if ( !( self->fFlags & ObjectProxy::kIsSmartPtr ) ) {
177 Py_RETURN_NONE;
178 }
179
180 return (PyObject*)PyROOT::BindCppObject( self->fSmartPtr, self->fSmartPtrType );
181 }
182
183////////////////////////////////////////////////////////////////////////////////
184
185 PyMethodDef op_methods[] = {
186 { (char*)"__nonzero__", (PyCFunction)op_nonzero, METH_NOARGS, NULL },
187 { (char*)"__bool__", (PyCFunction)op_nonzero, METH_NOARGS, NULL }, // for p3
188 { (char*)"__destruct__", (PyCFunction)op_destruct, METH_NOARGS, NULL },
189 { (char*)"__reduce__", (PyCFunction)op_reduce, METH_NOARGS, NULL },
190 { (char*)"__dispatch__", (PyCFunction)op_dispatch, METH_VARARGS, (char*)"dispatch to selected overload" },
191 { (char*)"_get_smart_ptr", (PyCFunction)op_get_smart_ptr, METH_NOARGS, (char*)"get associated smart pointer, if any" },
192 { (char*)"__smartptr__", (PyCFunction)op_get_smart_ptr, METH_NOARGS, (char*)"get associated smart pointer, if any" },
193 { (char*)NULL, NULL, 0, NULL }
194 };
195
196
197//= PyROOT object proxy construction/destruction =============================
198 ObjectProxy* op_new( PyTypeObject* subtype, PyObject*, PyObject* )
199 {
200 // Create a new object proxy (holder only).
201 ObjectProxy* pyobj = (ObjectProxy*)subtype->tp_alloc( subtype, 0 );
202 pyobj->fObject = NULL;
203 pyobj->fFlags = 0;
204
205 return pyobj;
206 }
207
208////////////////////////////////////////////////////////////////////////////////
209/// Remove (Python-side) memory held by the object proxy.
210
211 void op_dealloc( ObjectProxy* pyobj )
212 {
213 op_dealloc_nofree( pyobj );
214 Py_TYPE(pyobj)->tp_free( (PyObject*)pyobj );
215 }
216
217////////////////////////////////////////////////////////////////////////////////
218/// Rich set of comparison objects; only equals and not-equals are defined.
219
220 PyObject* op_richcompare( ObjectProxy* self, ObjectProxy* other, int op )
221 {
222 if ( op != Py_EQ && op != Py_NE ) {
223 Py_INCREF( Py_NotImplemented );
224 return Py_NotImplemented;
225 }
226
227 Bool_t bIsEq = false;
228
229 // special case for None to compare True to a null-pointer
230 if ( (PyObject*)other == Py_None && ! self->fObject )
231 bIsEq = true;
232
233 // type + held pointer value defines identity (will cover if other is not
234 // actually an ObjectProxy, as ob_type will be unequal)
235 else if ( Py_TYPE(self) == Py_TYPE(other) && self->GetObject() == other->GetObject() )
236 bIsEq = true;
237
238 if ( ( op == Py_EQ && bIsEq ) || ( op == Py_NE && ! bIsEq ) ) {
239 Py_INCREF( Py_True );
240 return Py_True;
241 }
242
243 Py_INCREF( Py_False );
244 return Py_False;
245 }
246
247////////////////////////////////////////////////////////////////////////////////
248/// Build a representation string of the object proxy that shows the address
249/// of the C++ object that is held, as well as its type.
250
251 PyObject* op_repr( ObjectProxy* pyobj )
252 {
253 Cppyy::TCppType_t klass = pyobj->ObjectIsA();
254 std::string clName = klass ? Cppyy::GetFinalName( klass ) : "<unknown>";
255 if ( pyobj->fFlags & ObjectProxy::kIsReference )
256 clName.append( "*" );
257
258 std::string smartPtrName;
259 if ( pyobj->fFlags & ObjectProxy::kIsSmartPtr ) {
260 Cppyy::TCppType_t smartPtrType = pyobj->fSmartPtrType;
261 smartPtrName = smartPtrType ? Cppyy::GetFinalName( smartPtrType ) : "unknown smart pointer";
262 }
263
264 // need to prevent accidental derefs when just printing (usually unsafe)
265 if ( ! PyObject_HasAttr( (PyObject*)pyobj, PyStrings::gDeref ) ) {
266 PyObject* name = PyObject_CallMethod( (PyObject*)pyobj,
267 const_cast< char* >( "GetName" ), const_cast< char* >( "" ) );
268
269 if ( name ) {
270 if ( PyROOT_PyUnicode_GET_SIZE( name ) != 0 ) {
271 if ( pyobj->fFlags & ObjectProxy::kIsSmartPtr ) {
272 PyObject* repr = PyROOT_PyUnicode_FromFormat( "<ROOT.%s object (\"%s\") at %p held by %s at %p>",
273 clName.c_str(), PyROOT_PyUnicode_AsString( name ), pyobj->GetObject(), smartPtrName.c_str(), pyobj->fSmartPtr );
274 Py_DECREF( name );
275 return repr;
276 } else {
277 PyObject* repr = PyROOT_PyUnicode_FromFormat( "<ROOT.%s object (\"%s\") at %p>",
278 clName.c_str(), PyROOT_PyUnicode_AsString( name ), pyobj->GetObject() );
279 Py_DECREF( name );
280 return repr;
281 }
282 }
283 Py_DECREF( name );
284 } else
285 PyErr_Clear();
286 }
287
288 // get here if object has no method GetName() or name = ""
289 if ( pyobj->fFlags & ObjectProxy::kIsSmartPtr ) {
290 return PyROOT_PyUnicode_FromFormat( const_cast< char* >( "<ROOT.%s object at %p held by %s at %p>" ),
291 clName.c_str(), pyobj->GetObject(), smartPtrName.c_str(), pyobj->fSmartPtr );
292 } else {
293 return PyROOT_PyUnicode_FromFormat( const_cast< char* >( "<ROOT.%s object at %p>" ),
294 clName.c_str(), pyobj->GetObject() );
295 }
296 }
297
298
299//= PyROOT type number stubs to allow dynamic overrides ======================
300#define PYROOT_STUB( name, op, pystring ) \
301 PyObject* op_##name##_stub( PyObject* left, PyObject* right ) \
302 { \
303 if ( ! ObjectProxy_Check( left ) ) { \
304 if ( ObjectProxy_Check( right ) ) { \
305 std::swap( left, right ); \
306 } else { \
307 Py_INCREF( Py_NotImplemented ); \
308 return Py_NotImplemented; \
309 } \
310 } \
311 /* place holder to lazily install __name__ if a global overload is available */ \
312 if ( ! Utility::AddBinaryOperator( \
313 left, right, #op, "__"#name"__", "__r"#name"__" ) ) { \
314 Py_INCREF( Py_NotImplemented ); \
315 return Py_NotImplemented; \
316 } \
317 \
318 /* redo the call, which will now go to the newly installed method */ \
319 return PyObject_CallMethodObjArgs( left, pystring, right, NULL ); \
320 }
321
326
327////////////////////////////////////////////////////////////////////////////////
328
329 PyNumberMethods op_as_number = {
330 (binaryfunc)op_add_stub, // nb_add
331 (binaryfunc)op_sub_stub, // nb_subtract
332 (binaryfunc)op_mul_stub, // nb_multiply
333#if PY_VERSION_HEX < 0x03000000
334 (binaryfunc)op_div_stub, // nb_divide
335#endif
336 0, // nb_remainder
337 0, // nb_divmod
338 0, // nb_power
339 0, // nb_negative
340 0, // nb_positive
341 0, // nb_absolute
342 0, // tp_nonzero (nb_bool in p3)
343 0, // nb_invert
344 0, // nb_lshift
345 0, // nb_rshift
346 0, // nb_and
347 0, // nb_xor
348 0, // nb_or
349#if PY_VERSION_HEX < 0x03000000
350 0, // nb_coerce
351#endif
352 0, // nb_int
353 0, // nb_long (nb_reserved in p3)
354 0, // nb_float
355#if PY_VERSION_HEX < 0x03000000
356 0, // nb_oct
357 0, // nb_hex
358#endif
359 0, // nb_inplace_add
360 0, // nb_inplace_subtract
361 0, // nb_inplace_multiply
362#if PY_VERSION_HEX < 0x03000000
363 0, // nb_inplace_divide
364#endif
365 0, // nb_inplace_remainder
366 0, // nb_inplace_power
367 0, // nb_inplace_lshift
368 0, // nb_inplace_rshift
369 0, // nb_inplace_and
370 0, // nb_inplace_xor
371 0 // nb_inplace_or
372#if PY_VERSION_HEX >= 0x02020000
373 , 0 // nb_floor_divide
374#if PY_VERSION_HEX < 0x03000000
375 , 0 // nb_true_divide
376#else
377 , (binaryfunc)op_div_stub // nb_true_divide
378#endif
379 , 0 // nb_inplace_floor_divide
380 , 0 // nb_inplace_true_divide
381#endif
382#if PY_VERSION_HEX >= 0x02050000
383 , 0 // nb_index
384#endif
385#if PY_VERSION_HEX >= 0x03050000
386 , 0 // nb_matrix_multiply
387 , 0 // nb_inplace_matrix_multiply
388#endif
389 };
390
391} // unnamed namespace
392
393#if !defined(_MSC_VER)
394#pragma GCC diagnostic push
395#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
396#endif
397
398//= PyROOT object proxy type =================================================
399PyTypeObject ObjectProxy_Type = {
401 (char*)"ROOT.ObjectProxy", // tp_name
402 sizeof(ObjectProxy), // tp_basicsize
403 0, // tp_itemsize
404 (destructor)op_dealloc, // tp_dealloc
405 0, // tp_print (python < 3.8)
406 // tp_vectorcall_offset (python >= 3.8)
407 0, // tp_getattr
408 0, // tp_setattr
409 0, // tp_compare
410 (reprfunc)op_repr, // tp_repr
411 &op_as_number, // tp_as_number
412 0, // tp_as_sequence
413 0, // tp_as_mapping
414 PyBaseObject_Type.tp_hash, // tp_hash
415 0, // tp_call
416 0, // tp_str
417 0, // tp_getattro
418 0, // tp_setattro
419 0, // tp_as_buffer
420 Py_TPFLAGS_DEFAULT |
421 Py_TPFLAGS_BASETYPE |
422 Py_TPFLAGS_HAVE_GC |
423 Py_TPFLAGS_CHECKTYPES, // tp_flags
424 (char*)"PyROOT object proxy (internal)", // tp_doc
425 0, // tp_traverse
426 0, // tp_clear
427 (richcmpfunc)op_richcompare, // tp_richcompare
428 0, // tp_weaklistoffset
429 0, // tp_iter
430 0, // tp_iternext
431 op_methods, // tp_methods
432 0, // tp_members
433 0, // tp_getset
434 0, // tp_base
435 0, // tp_dict
436 0, // tp_descr_get
437 0, // tp_descr_set
438 0, // tp_dictoffset
439 0, // tp_init
440 0, // tp_alloc
441 (newfunc)op_new, // tp_new
442 0, // tp_free
443 0, // tp_is_gc
444 0, // tp_bases
445 0, // tp_mro
446 0, // tp_cache
447 0, // tp_subclasses
448 0 // tp_weaklist
449#if PY_VERSION_HEX >= 0x02030000
450 , 0 // tp_del
451#endif
452#if PY_VERSION_HEX >= 0x02060000
453 , 0 // tp_version_tag
454#endif
455#if PY_VERSION_HEX >= 0x03040000
456 , 0 // tp_finalize
457#endif
458#if PY_VERSION_HEX >= 0x03080000
459 , 0 // tp_vectorcall
460#if PY_VERSION_HEX < 0x03090000
461 , 0 // tp_print (python 3.8 only)
462#endif
463#endif
464};
465
466#if !defined(_MSC_VER)
467#pragma GCC diagnostic pop
468#endif
469
470} // namespace PyROOT
#define R__EXTERN
Definition: DllImport.h:27
#define PYROOT_STUB(name, op, pystring)
#define Py_TYPE(ob)
Definition: PyROOT.h:166
#define PyBytes_FromString
Definition: PyROOT.h:71
#define PyROOT_PyUnicode_AsString
Definition: PyROOT.h:78
#define PyBytes_FromStringAndSize
Definition: PyROOT.h:72
#define PyROOT_PyUnicode_GET_SIZE
Definition: PyROOT.h:80
#define PyROOT_PyUnicode_FromFormat
Definition: PyROOT.h:81
#define PyROOT_PyUnicode_Type
Definition: PyROOT.h:88
#define PyVarObject_HEAD_INIT(type, size)
Definition: PyROOT.h:164
bool Bool_t
Definition: RtypesCore.h:59
char name[80]
Definition: TGX11.cxx:109
_object PyObject
Definition: TPyArg.h:20
#define gROOT
Definition: TROOT.h:415
Cppyy::TCppType_t ObjectIsA() const
Definition: ObjectProxy.h:66
PyObject_HEAD void * fObject
Definition: ObjectProxy.h:77
void * GetObject() const
Definition: ObjectProxy.h:47
Cppyy::TCppType_t fSmartPtrType
Definition: ObjectProxy.h:80
The concrete implementation of TBuffer for writing/reading to/from a ROOT file or socket.
Definition: TBufferFile.h:46
@ kWrite
Definition: TBuffer.h:72
Int_t Length() const
Definition: TBuffer.h:99
char * Buffer() const
Definition: TBuffer.h:95
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:2906
@ kInvalidObject
if object ctor succeeded but object should not be used
Definition: TObject.h:68
void CallDestructor(TCppType_t type, TCppObject_t self)
Definition: Cppyy.cxx:513
void Destruct(TCppType_t type, TCppObject_t instance)
Definition: Cppyy.cxx:292
TCppScope_t TCppType_t
Definition: Cppyy.h:16
void Deallocate(TCppType_t type, TCppObject_t instance)
Definition: Cppyy.cxx:281
TCppScope_t GetScope(const std::string &scope_name)
Definition: Cppyy.cxx:197
std::string GetFinalName(TCppType_t type)
Definition: Cppyy.cxx:585
R__EXTERN PyObject * gAdd
Definition: PyStrings.h:39
R__EXTERN PyObject * gMul
Definition: PyStrings.h:41
R__EXTERN PyObject * gDiv
Definition: PyStrings.h:42
R__EXTERN PyObject * gSub
Definition: PyStrings.h:40
R__EXTERN PyObject * gDeref
Definition: PyStrings.h:21
PyObject * BindCppObject(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, Bool_t isRef=kFALSE)
if the object is a null pointer, return a typed one (as needed for overloading)
PyTypeObject PyRootType_Type
Definition: PyRootType.cxx:233
PyTypeObject ObjectProxy_Type
void op_dealloc_nofree(ObjectProxy *)
Destroy the held C++ object, if owned; does not deallocate the proxy.
Definition: ObjectProxy.cxx:46
R__EXTERN PyObject * gRootModule
Definition: ObjectProxy.cxx:39