All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
PyROOTPickle.cpp
Go to the documentation of this file.
1 
8 #ifdef __ICC
9 // disable icc remark #2259: non-pointer conversion from "X" to "Y" may lose significant bits
10 // TODO: To be removed, since it comes from ROOT
11 #pragma warning(disable:2259)
12 #endif
13 
14 #include "GaudiMP/PyROOTPickle.h"
15 #include "TClass.h"
16 #include "TClassRef.h"
17 #include "TBufferFile.h"
18 #include "TPython.h"
19 #include "RVersion.h"
20 
21 //- data _______________________________________________________________________
22 #if ROOT_VERSION_CODE < ROOT_VERSION(5,19,0)
23 static PyObject* gExpand = 0;
24 #endif
25 
26 namespace GaudiMP {
27 
28 #if ROOT_VERSION_CODE < ROOT_VERSION(5,19,0)
29 
34 PyObject* ObjectProxyReduce( PyObject* self )
35 {
36  // Turn the object proxy instance into a character stream and return for
37  // pickle, together with the callable object that can restore the stream
38  // into the object proxy instance.
39 
40  void* vself = TPython::ObjectProxy_AsVoidPtr( self ); // checks type
41  if ( ! vself ) {
42  PyErr_SetString( PyExc_TypeError,
43  "__reduce__ requires an object proxy instance as first argument" );
44  return 0;
45  }
46 
47  PyObject* nattr = PyObject_GetAttrString( (PyObject*)self->ob_type, (char*)"__name__" );
48  PyObject* pyname = PyObject_Str( nattr );
49  Py_DECREF( nattr );
50 
51  static TClass* bufferclass = TClass::GetClass("TBufferFile");
52  TClass* klass = TClass::GetClass( PyString_AS_STRING( pyname ) );
53 
54  // no cast is needed, but WriteObject taking a TClass argument is protected,
55  // so use WriteObjectAny()
56  TBufferFile* buf = 0;
57  if ( klass == bufferclass ) {
58  buf = (TBufferFile*)vself;
59  }
60  else {
61  static TBufferFile buffer( TBuffer::kWrite );
62  buffer.Reset();
63  if ( buffer.WriteObjectAny( vself, klass ) != 1 ) {
64  PyErr_Format( PyExc_IOError,
65  "could not stream object of type %s", PyString_AS_STRING( pyname ) );
66  Py_DECREF( pyname );
67  return 0;
68  }
69  buf = &buffer;
70  }
71 
72  // use a string for the serialized result, as a python buffer will not copy
73  // the buffer contents; use a string for the class name, used when casting
74  // on reading back in
75  PyObject* res2 = PyTuple_New( 2 );
76  PyTuple_SET_ITEM( res2, 0, PyString_FromStringAndSize( buf->Buffer(), buf->Length() ) );
77  PyTuple_SET_ITEM( res2, 1, pyname );
78 
79  PyObject* result = PyTuple_New( 2 );
80  Py_INCREF( gExpand );
81  PyTuple_SET_ITEM( result, 0, gExpand );
82  PyTuple_SET_ITEM( result, 1, res2 );
83 
84  return result;
85 }
86 
87 
88 class ObjectProxy {
89  public:
90  enum EFlags { kNone = 0x0, kIsOwner = 0x0001, kIsReference = 0x0002 };
91 
92  public:
93  void HoldOn() { fFlags |= kIsOwner; }
94  void Release() { fFlags &= ~kIsOwner; }
95  public: // public, as the python C-API works with C structs
96  PyObject_HEAD
97  void* fObject;
98  TClassRef fClass;
99  int fFlags;
100  private: // private, as the python C-API will handle creation
101  ObjectProxy() {}
102 };
103 
104 
109 PyObject* ObjectProxyExpand( PyObject*, PyObject* args )
110 {
111  // This method is a helper for (un)pickling of ObjectProxy instances.
112  PyObject* pybuf = 0;
113  const char* clname = 0;
114  if ( ! PyArg_ParseTuple( args, const_cast< char* >( "O!s:__expand__" ),
115  &PyString_Type, &pybuf, &clname ) )
116  return 0;
117 
118  // use the PyString macro's to by-pass error checking; do not adopt the buffer,
119  // as the local TBufferFile can go out of scope (there is no copying)
120  void* result;
121  if( strcmp(clname, "TBufferFile") == 0) {
122  TBufferFile* buf = new TBufferFile( TBuffer::kWrite);
123  buf->WriteFastArray( PyString_AS_STRING(pybuf), PyString_GET_SIZE( pybuf ));
124  result = buf;
125  }
126  else {
127  TBufferFile buf( TBuffer::kRead,
128  PyString_GET_SIZE( pybuf ), PyString_AS_STRING( pybuf ), kFALSE );
129  result = buf.ReadObjectAny( 0 );
130  }
131  PyObject* pobj = TPython::ObjectProxy_FromVoidPtr( result, clname );
132  // set Ownership of the returned object
133  ObjectProxy* obj = (ObjectProxy*)pobj;
134  obj->HoldOn();
135  return pobj;
136 }
137 
138 
144 void PyROOTPickle::Initialize( PyObject* libpyroot_pymodule, PyObject* objectproxy_pytype )
145 {
146  Py_INCREF( libpyroot_pymodule );
147  PyTypeObject* pytype = (PyTypeObject*)objectproxy_pytype;
148 
149  static PyMethodDef s_pdefExp = { (char*)"_ObjectProxy__expand__",
150  (PyCFunction)ObjectProxyExpand, METH_VARARGS, (char*)"internal function" };
151 
152  PyObject* pymname = PyString_FromString( PyModule_GetName( libpyroot_pymodule ) );
153  gExpand = PyCFunction_NewEx( &s_pdefExp, NULL, pymname );
154  Py_DECREF( pymname );
155  Bool_t isOk = PyObject_SetAttrString( libpyroot_pymodule, s_pdefExp.ml_name, gExpand ) == 0;
156  Py_DECREF( gExpand ); // is moderately risky, but Weakref not allowed (?)
157 
158  if ( ! isOk ) {
159  Py_DECREF( libpyroot_pymodule );
160  PyErr_SetString( PyExc_TypeError, "could not add expand function to libPyROOT" );
161  return;
162  }
163 
164  static PyMethodDef s_pdefRed = { (char*)"__reduce__",
165  (PyCFunction)ObjectProxyReduce, METH_NOARGS, (char*)"internal function" };
166 
167  PyObject* descr = PyDescr_NewMethod( pytype, &s_pdefRed );
168  isOk = PyDict_SetItemString( pytype->tp_dict, s_pdefRed.ml_name, descr) == 0;
169  Py_DECREF( descr );
170  if ( ! isOk ) {
171  Py_DECREF( libpyroot_pymodule );
172  PyErr_SetString( PyExc_TypeError, "could not add __reduce__ function to ObjectProxy" );
173  return;
174  }
175 
176  Py_DECREF( libpyroot_pymodule );
177 }
178 
179 #else // ROOT_VERSION_CODE < ROOT_VERSION(5,19,0)
180 
181 void PyROOTPickle::Initialize( PyObject*, PyObject* )
182 {
183  /* dummy. It is not needed for this version of ROOT */
184 }
185 
186 #endif // ROOT_VERSION_CODE < ROOT_VERSION(5,19,0)
187 
188 } // namespace GaudiMP
A class to serialize/deserialize TES objects to and from a TBufferFile Author: P. ...
Definition: PyROOTPickle.h:32
static void Initialize(PyObject *libpyroot_pymodule, PyObject *objectproxy_pytype)
Install the pickling of ObjectProxy&#39;s functionality.
T strcmp(T...args)
Port pickling functionality while awaiting newer release.