The Gaudi Framework  master (181af51f)
Loading...
Searching...
No Matches
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\***********************************************************************************/
17
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
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 )
33static PyObject* gExpand = 0;
34#endif
35
36namespace 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
Port pickling functionality while awaiting newer release.
static void Initialize(PyObject *libpyroot_pymodule, PyObject *objectproxy_pytype)
Install the pickling of ObjectProxy's functionality.
A class to serialize/deserialize TES objects to and from a TBufferFile Author: P.