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