Logo ROOT   master
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 _______________________________________________________________________
38 namespace 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 {
54  Cppyy::Deallocate( pyobj->fSmartPtrType, pyobj->fSmartPtr );
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 
71 namespace PyROOT {
72 namespace {
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 
322 PYROOT_STUB( add, +, PyStrings::gAdd )
323 PYROOT_STUB( sub, -, PyStrings::gSub )
324 PYROOT_STUB( mul, *, PyStrings::gMul )
325 PYROOT_STUB( div, /, PyStrings::gDiv )
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 
394 //= PyROOT object proxy type =================================================
395 PyTypeObject ObjectProxy_Type = {
397  (char*)"ROOT.ObjectProxy", // tp_name
398  sizeof(ObjectProxy), // tp_basicsize
399  0, // tp_itemsize
400  (destructor)op_dealloc, // tp_dealloc
401  0, // tp_print (python < 3.8)
402  // tp_vectorcall_offset (python >= 3.8)
403  0, // tp_getattr
404  0, // tp_setattr
405  0, // tp_compare
406  (reprfunc)op_repr, // tp_repr
407  &op_as_number, // tp_as_number
408  0, // tp_as_sequence
409  0, // tp_as_mapping
410  PyBaseObject_Type.tp_hash, // tp_hash
411  0, // tp_call
412  0, // tp_str
413  0, // tp_getattro
414  0, // tp_setattro
415  0, // tp_as_buffer
416  Py_TPFLAGS_DEFAULT |
417  Py_TPFLAGS_BASETYPE |
418  Py_TPFLAGS_HAVE_GC |
419  Py_TPFLAGS_CHECKTYPES, // tp_flags
420  (char*)"PyROOT object proxy (internal)", // tp_doc
421  0, // tp_traverse
422  0, // tp_clear
423  (richcmpfunc)op_richcompare, // tp_richcompare
424  0, // tp_weaklistoffset
425  0, // tp_iter
426  0, // tp_iternext
427  op_methods, // tp_methods
428  0, // tp_members
429  0, // tp_getset
430  0, // tp_base
431  0, // tp_dict
432  0, // tp_descr_get
433  0, // tp_descr_set
434  0, // tp_dictoffset
435  0, // tp_init
436  0, // tp_alloc
437  (newfunc)op_new, // tp_new
438  0, // tp_free
439  0, // tp_is_gc
440  0, // tp_bases
441  0, // tp_mro
442  0, // tp_cache
443  0, // tp_subclasses
444  0 // tp_weaklist
445 #if PY_VERSION_HEX >= 0x02030000
446  , 0 // tp_del
447 #endif
448 #if PY_VERSION_HEX >= 0x02060000
449  , 0 // tp_version_tag
450 #endif
451 #if PY_VERSION_HEX >= 0x03040000
452  , 0 // tp_finalize
453 #endif
454 #if PY_VERSION_HEX >= 0x03080000
455  , 0 // tp_vectorcall
456 #if PY_VERSION_HEX < 0x03090000
457  , 0 // tp_print (python 3.8 only)
458 #endif
459 #endif
460 };
461 
462 } // namespace PyROOT
#define PyBytes_FromString
Definition: PyROOT.h:71
TCppScope_t TCppType_t
Definition: Cppyy.h:16
The concrete implementation of TBuffer for writing/reading to/from a ROOT file or socket...
Definition: TBufferFile.h:46
#define PyROOT_PyUnicode_GET_SIZE
Definition: PyROOT.h:80
Cppyy::TCppType_t fSmartPtrType
Definition: ObjectProxy.h:80
R__EXTERN PyObject * gDiv
Definition: PyStrings.h:42
#define gROOT
Definition: TROOT.h:405
std::string GetFinalName(TCppType_t type)
Definition: Cppyy.cxx:585
bool Bool_t
Definition: RtypesCore.h:61
R__EXTERN PyObject * gDeref
Definition: PyStrings.h:21
#define PyVarObject_HEAD_INIT(type, size)
Definition: PyROOT.h:164
Int_t Length() const
Definition: TBuffer.h:99
R__EXTERN PyObject * gAdd
Definition: PyStrings.h:39
#define PYROOT_STUB(name, op, pystring)
#define PyROOT_PyUnicode_FromFormat
Definition: PyROOT.h:81
R__EXTERN PyObject * gRootModule
Definition: ObjectProxy.cxx:39
char * Buffer() const
Definition: TBuffer.h:95
#define PyROOT_PyUnicode_Type
Definition: PyROOT.h:88
#define PyROOT_PyUnicode_AsString
Definition: PyROOT.h:78
void CallDestructor(TCppType_t type, TCppObject_t self)
Definition: Cppyy.cxx:513
PyTypeObject PyRootType_Type
Definition: PyRootType.cxx:229
PyTypeObject ObjectProxy_Type
if object ctor succeeded but object should not be used
Definition: TObject.h:68
TCppScope_t GetScope(const std::string &scope_name)
Definition: Cppyy.cxx:197
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:2923
void Deallocate(TCppType_t type, TCppObject_t instance)
Definition: Cppyy.cxx:281
R__EXTERN PyObject * gSub
Definition: PyStrings.h:40
PyObject_HEAD void * fObject
Definition: ObjectProxy.h:77
#define R__EXTERN
Definition: DllImport.h:27
#define Py_TYPE(ob)
Definition: PyROOT.h:166
void * GetObject() const
Definition: ObjectProxy.h:47
void op_dealloc_nofree(ObjectProxy *)
Destroy the held C++ object, if owned; does not deallocate the proxy.
Definition: ObjectProxy.cxx:46
void Destruct(TCppType_t type, TCppObject_t instance)
Definition: Cppyy.cxx:292
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)
char name[80]
Definition: TGX11.cxx:109
_object PyObject
Definition: TPyArg.h:20
R__EXTERN PyObject * gMul
Definition: PyStrings.h:41
Cppyy::TCppType_t ObjectIsA() const
Definition: ObjectProxy.h:66
#define PyBytes_FromStringAndSize
Definition: PyROOT.h:72