ATLAS Offline Software
PyROOTPickle.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
3 */
4 
12 // Called from python, so only excuted single-threaded (GIL).
15 
16 
17 #include "RootUtils/PyROOTPickle.h"
18 #include "RootUtils/PyGetString.h"
19 #include "Utility.h"
20 #include "Python.h"
21 #include "TClass.h"
22 #include "TBufferFile.h"
23 #include "TPython.h"
24 
25 
26 //- data _______________________________________________________________________
27 static PyObject* gExpand = 0;
28 
29 
30 namespace RootUtils {
31 
37 {
38  // Turn the object proxy instance into a character stream and return for
39  // pickle, together with the callable object that can restore the stream
40  // into the object proxy instance.
41 
42  void* vself = TPython::CPPInstance_AsVoidPtr( self ); // checks type
43  if ( ! vself ) {
44  PyErr_SetString( PyExc_TypeError,
45  "__reduce__ requires an object proxy instance as first argument" );
46  return 0;
47  }
48 
49  PyObject* nattr = PyObject_GetAttrString( (PyObject*)self->ob_type, (char*)"__name__" );
50  PyObject* pyname = PyObject_Str( nattr );
51  Py_DECREF( nattr );
52 
53  std::string klassname = PyGetString( pyname ).first;
54  TClass* klass = TClass::GetClass( klassname.c_str() );
55 
56  // no cast is needed, but WriteObject taking a TClass argument is protected,
57  // so use WriteObjectAny()
58  TBufferFile buf( TBuffer::kWrite );
59  if ( buf.WriteObjectAny( vself, klass ) != 1 ) {
60  PyErr_Format( PyExc_IOError,
61  "could not stream object of type %s", klassname.c_str() );
62  Py_DECREF( pyname );
63  return 0;
64  }
65 
66  // use a string for the serialized result, as a python buffer will not copy
67  // the buffer contents; use a string for the class name, used when casting
68  // on reading back in
69  PyObject* res2 = PyTuple_New( 2 );
70  PyTuple_SET_ITEM( res2, 0, PyUnicode_FromStringAndSize( buf.Buffer(), buf.Length() ) );
71  PyTuple_SET_ITEM( res2, 1, pyname );
72 
73  PyObject* result = PyTuple_New( 2 );
74  Py_INCREF( gExpand );
75  PyTuple_SET_ITEM( result, 0, gExpand );
76  PyTuple_SET_ITEM( result, 1, res2 );
77 
78  return result;
79 }
80 
81 
87 {
88  // This method is a helper for (un)pickling of CPPInstance instances.
89  PyObject* pybuf = 0;
90  const char* clname = 0;
91  if ( ! PyArg_ParseTuple( args, const_cast< char* >( "O!s:__expand__" ),
92  &PyUnicode_Type, &pybuf, &clname ) )
93  return 0;
94 
95  // use the PyString macros to by-pass error checking; do not adopt the buffer,
96  // as the local TBufferFile can go out of scope (there is no copying)
97  TBufferFile buf( TBuffer::kRead,
98  PyUnicode_GET_LENGTH( pybuf ),
99  (char*)PyGetString( pybuf ).first.c_str(),
100  kFALSE );
101 
102  void* result = buf.ReadObjectAny( 0 );
103  return TPython::CPPInstance_FromVoidPtr( result, clname );
104 }
105 
106 
112 void PyROOTPickle::Initialize( PyObject* libpyroot_pymodule, PyObject* cppinstance_pytype )
113 {
114  Py_INCREF( libpyroot_pymodule );
115  PyTypeObject* pytype = (PyTypeObject*)cppinstance_pytype;
116 
117  // Don't change this name to CPPInstance since it's saved in pickles.
118  static PyMethodDef s_pdefExp = { (char*)"_ObjectProxy__expand__",
119  (PyCFunction)CPPInstanceExpand, METH_VARARGS, (char*)"internal function" };
120 
121  PyObject* pymname = PyUnicode_FromString( PyModule_GetName( libpyroot_pymodule ) );
122  gExpand = PyCFunction_NewEx( &s_pdefExp, NULL, pymname );
123  Py_DECREF( pymname );
124  Bool_t isOk = PyObject_SetAttrString( libpyroot_pymodule, s_pdefExp.ml_name, gExpand ) == 0;
125  Py_DECREF( gExpand ); // is moderately risky, but Weakref not allowed (?)
126 
127  if ( ! isOk ) {
128  Py_DECREF( libpyroot_pymodule );
129  PyErr_SetString( PyExc_TypeError, "could not add expand function to libPyROOT" );
130  return;
131  }
132 
133  static PyMethodDef s_pdefRed = { (char*)"__reduce__",
134  (PyCFunction)CPPInstanceReduce, METH_NOARGS, (char*)"internal function" };
135 
136  PyObject* descr = PyDescr_NewMethod( pytype, &s_pdefRed );
137  isOk = PyDict_SetItemString( pytype->tp_dict, s_pdefRed.ml_name, descr) == 0;
138  Py_DECREF( descr );
139  if ( ! isOk ) {
140  Py_DECREF( libpyroot_pymodule );
141  PyErr_SetString( PyExc_TypeError, "could not add __reduce__ function to CPPInstance" );
142  return;
143  }
144 
145  Py_DECREF( libpyroot_pymodule );
146 }
147 
148 
149 } // namespace RootUtils
RootUtils
Definition: ILogger.h:20
get_generator_info.result
result
Definition: get_generator_info.py:21
Utility.h
Utility code originally from pyroot.
RootUtils::CPPInstanceReduce
PyObject * CPPInstanceReduce(PyObject *self, PyObject *)
PyROOT object proxy pickle support.
Definition: PyROOTPickle.cxx:36
klass
This class describe the base functionalities of a HypoTool used by the ComboAlg.
python.TransformConfig.descr
descr
print "%s.properties()" % self.__name__
Definition: TransformConfig.py:360
RootUtils::PyROOTPickle::Initialize
static void Initialize(PyObject *libpyroot_pymodule, PyObject *objectproxy_pytype)
Install the pickling of ObjectProxy's functionality.
Definition: PyROOTPickle.cxx:112
RootUtils::CPPInstanceExpand
PyObject * CPPInstanceExpand(PyObject *, PyObject *args)
Helper for (un)pickling of CPPInstance's.
Definition: PyROOTPickle.cxx:86
ATLAS_NO_CHECK_FILE_THREAD_SAFETY
ATLAS_NO_CHECK_FILE_THREAD_SAFETY
Definition: PyROOTPickle.cxx:14
PyROOTPickle.h
Port pickling functionality while awaiting newer release.
PyGetString.h
Convert python string -> C++ string for py2 and py3.
RootUtils::PyGetString
std::pair< std::string, bool > PyGetString(PyObject *s)
Convert python string -> C++ string for py2 and py3.
Definition: PyGetString.h:40
DeMoScan.first
bool first
Definition: DeMoScan.py:534
checker_macros.h
Define macros for attributes used to control the static checker.
PyObject
_object PyObject
Definition: IPyComponent.h:26
python.CaloScaleNoiseConfig.args
args
Definition: CaloScaleNoiseConfig.py:80