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