ATLAS Offline Software
Loading...
Searching...
No Matches
PyROOTPickle.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
11
12// Called from python, so only excuted single-threaded (GIL).
15
16
19#include "Utility.h"
20#include "Python.h"
21#include "TClass.h"
22#include "TBufferFile.h"
23#include "TPython.h"
24
25
26//- data _______________________________________________________________________
27static PyObject* gExpand = 0;
28
29
30namespace 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
112void PyROOTPickle::Initialize( PyObject* libpyroot_pymodule, PyObject* cppinstance_pytype )
113{
114 Py_INCREF( libpyroot_pymodule );
115 PyTypeObject* pytype = reinterpret_cast<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
_object PyObject
Convert python string -> C++ string for py2 and py3.
static PyObject * gExpand
Port pickling functionality while awaiting newer release.
Utility code originally from pyroot.
Define macros for attributes used to control the static checker.
#define ATLAS_NO_CHECK_FILE_THREAD_SAFETY
static void Initialize(PyObject *libpyroot_pymodule, PyObject *objectproxy_pytype)
Install the pickling of ObjectProxy's functionality.
PyObject * CPPInstanceReduce(PyObject *self, PyObject *)
PyROOT object proxy pickle support.
PyObject * CPPInstanceExpand(PyObject *, PyObject *args)
Helper for (un)pickling of CPPInstance's.
std::pair< std::string, bool > PyGetString(PyObject *s)
Convert python string -> C++ string for py2 and py3.
Definition PyGetString.h:40