Logo ROOT  
Reference Guide
CPPInstancePyz.cxx
Go to the documentation of this file.
1// Author: Massimiliano Galli CERN 07/2019
2// Original PyROOT code by Wim Lavrijsen, LBL
3
4/*************************************************************************
5 * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12// Bindings
13#include "CPyCppyy.h"
14#include "PyROOTPythonize.h"
15#include "CPPInstance.h"
16#include "ProxyWrappers.h"
17#include "Converters.h"
18#include "Utility.h"
19#include "PyzCppHelpers.hxx"
20#include "TBufferFile.h"
21#include "CustomPyTypes.h"
22
23using namespace CPyCppyy;
24
25namespace PyROOT {
26extern PyObject *gRootModule;
27}
28
29////////////////////////////////////////////////////////////////////////////
30/// \brief Deserialize pickled objects
31/// \param[in] self Always null, since this is a module function.
32/// \param[in] args Pointer to a Python tuple object containing the arguments
33/// received from Python.
34///
35/// Helper function that deserializes pickled objects. It needs to be
36/// included in the extension module API because otherwise it is not
37/// callable from Python. This is important because it will be Python
38/// itself calling it when trying to expand a serialized object.
40{
41 PyObject *pybuf = 0, *pyname = 0;
42 if (!PyArg_ParseTuple(args, const_cast<char *>("O!O!:__expand__"), &PyBytes_Type, &pybuf, &PyBytes_Type, &pyname))
43 return 0;
44 const char *clname = PyBytes_AS_STRING(pyname);
45 // TBuffer and its derived classes can't write themselves, but can be created
46 // directly from the buffer, so handle them in a special case
47 void *newObj = 0;
48 if (strcmp(clname, "TBufferFile") == 0) {
51 newObj = buf;
52 } else {
53 // use the PyString macro's to by-pass error checking; do not adopt the buffer,
54 // as the local TBufferFile can go out of scope (there is no copying)
56 newObj = buf.ReadObjectAny(0);
57 }
58 PyObject *result = BindCppObject(newObj, Cppyy::GetScope(clname));
59 if (result) {
60 // this object is to be owned by the Python interpreter, assuming that the call
61 // originated from there
62 ((CPPInstance *)result)->PythonOwns();
63 }
64 return result;
65}
66
67/// PyROOT object proxy pickle support
68/// Turn the object proxy instance into a character stream and return for
69/// pickle, together with the callable object that can restore the stream
70/// into the object proxy instance.
72{
73 // keep a borrowed reference around to the callable function for expanding;
74 // because it is borrowed, it means that there can be no pickling during the
75 // shutdown of the libPyROOT module
76 static PyObject *s_expand =
77 PyDict_GetItemString(PyModule_GetDict(PyROOT::gRootModule), const_cast<char *>("_CPPInstance__expand__"));
78
79 // TBuffer and its derived classes can't write themselves, but can be created
80 // directly from the buffer, so handle them in a special case
81 static Cppyy::TCppType_t s_bfClass = Cppyy::GetScope("TBufferFile");
82 TBufferFile *buff = 0;
83 if (s_bfClass == self->ObjectIsA()) {
84 buff = (TBufferFile *)self->GetObject();
85 } else {
86 // no cast is needed, but WriteObject taking a TClass argument is protected,
87 // so use WriteObjectAny()
88 static TBufferFile s_buff(TBuffer::kWrite);
89 s_buff.Reset();
90 // to delete
91 if (s_buff.WriteObjectAny(self->GetObject(),
93 PyErr_Format(PyExc_IOError, "could not stream object of type %s",
94 Cppyy::GetScopedFinalName(self->ObjectIsA()).c_str());
95 return 0;
96 }
97 buff = &s_buff;
98 }
99 // use a string for the serialized result, as a python buffer will not copy
100 // the buffer contents; use a string for the class name, used when casting
101 // on reading back in (see CPPInstanceExpand defined above)
102 PyObject *res2 = PyTuple_New(2);
103 PyTuple_SET_ITEM(res2, 0, PyBytes_FromStringAndSize(buff->Buffer(), buff->Length()));
104 PyTuple_SET_ITEM(res2, 1, PyBytes_FromString(Cppyy::GetScopedFinalName(self->ObjectIsA()).c_str()));
105
106 PyObject *result = PyTuple_New(2);
107 Py_INCREF(s_expand);
108 PyTuple_SET_ITEM(result, 0, s_expand);
109 PyTuple_SET_ITEM(result, 1, res2);
110
111 return result;
112}
113
114////////////////////////////////////////////////////////////////////////////
115/// \brief Set __reduce__ attribute for CPPInstance objects
116/// \param[in] self Always null, since this is a module function.
117/// \param[in] args Pointer to a Python tuple object containing the arguments
118/// received from Python.
119///
120/// The C++ function op_reduce defined above is wrapped in a Python method
121/// so that it can be injected in CPPInstance
123{
124 PyObject *pyclass = PyTuple_GetItem(args, 0);
125
126 const char *attr = "__reduce__";
127
128 PyMethodDef *pdef = new PyMethodDef();
129 pdef->ml_name = attr;
130 pdef->ml_meth = (PyCFunction)op_reduce;
131 pdef->ml_flags = METH_NOARGS;
132 pdef->ml_doc = nullptr;
133
134 PyObject *func = PyCFunction_New(pdef, nullptr);
135 PyObject *method = CustomInstanceMethod_New(func, nullptr, pyclass);
136
137 // here PyObject_GenericSetAttr is used because CPPInstance does not allow
138 // attribute assignment using PyObject_SetAttr
139 // for more info refer to:
140 // https://bitbucket.org/wlav/cppyy/issues/110/user-defined-classes-in-c-dont-seem-to-be
141 PyObject_GenericSetAttr(pyclass, CPyCppyy_PyText_FromString(attr), method);
142 Py_DECREF(method);
143 Py_DECREF(func);
144
146}
PyObject * op_reduce(CPPInstance *self, PyObject *)
PyROOT object proxy pickle support Turn the object proxy instance into a character stream and return ...
#define PyBytes_AS_STRING
Definition: CPyCppyy.h:85
#define PyBytes_FromString
Definition: CPyCppyy.h:90
#define PyBytes_FromStringAndSize
Definition: CPyCppyy.h:91
#define Py_RETURN_NONE
Definition: CPyCppyy.h:281
#define PyBytes_Type
Definition: CPyCppyy.h:93
#define PyBytes_GET_SIZE
Definition: CPyCppyy.h:87
#define CPyCppyy_PyText_FromString
Definition: CPyCppyy.h:102
_object PyObject
Definition: PyMethodBase.h:41
const Bool_t kFALSE
Definition: RtypesCore.h:90
#define pyname
Definition: TMCParticle.cxx:19
Cppyy::TCppType_t ObjectIsA(bool check_smart=true) const
Definition: CPPInstance.h:106
The concrete implementation of TBuffer for writing/reading to/from a ROOT file or socket.
Definition: TBufferFile.h:46
void * ReadObjectAny(const TClass *cast) override
Read object from I/O buffer.
void WriteFastArray(const Bool_t *b, Int_t n) override
Write array of n bools into the I/O buffer.
void Reset() override
Reset buffer object. Resets map and buffer offset.
Definition: TBufferIO.cxx:305
Int_t WriteObjectAny(const void *obj, const TClass *ptrClass, Bool_t cacheReuse=kTRUE) override
Write object to I/O buffer.
Definition: TBufferIO.cxx:492
@ kWrite
Definition: TBuffer.h:72
@ kRead
Definition: TBuffer.h:72
Int_t Length() const
Definition: TBuffer.h:99
char * Buffer() const
Definition: TBuffer.h:95
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition: TClass.cxx:2948
PyObject * CustomInstanceMethod_New(PyObject *func, PyObject *self, PyObject *pyclass)
PyObject * BindCppObject(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
TCppScope_t TCppType_t
Definition: cpp_cppyy.h:19
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
PyObject * AddCPPInstancePickling(PyObject *self, PyObject *args)
Set reduce attribute for CPPInstance objects.
PyObject * CPPInstanceExpand(PyObject *self, PyObject *args)
Deserialize pickled objects.
PyObject * gRootModule