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 
23 using namespace CPyCppyy;
24 
25 namespace PyROOT {
26 extern 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(),
92  TClass::GetClass(Cppyy::GetScopedFinalName(self->ObjectIsA()).c_str())) != 1) {
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 }
CPyCppyy
Set of helper functions that are invoked from the pythonizors, on the Python side.
Definition: TPyClassGenerator.cxx:31
CPPInstance.h
TBuffer::kRead
@ kRead
Definition: TBuffer.h:73
PyObject
_object PyObject
Definition: PyMethodBase.h:42
PyROOT::AddCPPInstancePickling
PyObject * AddCPPInstancePickling(PyObject *self, PyObject *args)
Set reduce attribute for CPPInstance objects.
Definition: CPPInstancePyz.cxx:122
Utility.h
pyname
#define pyname
Definition: TMCParticle.cxx:19
TBufferIO::WriteObjectAny
Int_t WriteObjectAny(const void *obj, const TClass *ptrClass, Bool_t cacheReuse=kTRUE) override
Write object to I/O buffer.
Definition: TBufferIO.cxx:492
PyROOTPythonize.h
CPyCppyy::CPPInstance
Definition: CPPInstance.h:26
TBufferFile::WriteFastArray
void WriteFastArray(const Bool_t *b, Int_t n) override
Write array of n bools into the I/O buffer.
Definition: TBufferFile.cxx:1940
CPyCppyy::CustomInstanceMethod_New
PyObject * CustomInstanceMethod_New(PyObject *func, PyObject *self, PyObject *pyclass)
Definition: CustomPyTypes.cxx:103
TBuffer::Buffer
char * Buffer() const
Definition: TBuffer.h:96
TBufferIO::Reset
void Reset() override
Reset buffer object. Resets map and buffer offset.
Definition: TBufferIO.cxx:305
PyBytes_GET_SIZE
#define PyBytes_GET_SIZE
Definition: CPyCppyy.h:87
CPyCppyy_PyText_FromString
#define CPyCppyy_PyText_FromString
Definition: CPyCppyy.h:102
PyROOT::CPPInstanceExpand
PyObject * CPPInstanceExpand(PyObject *self, PyObject *args)
Deserialize pickled objects.
Definition: CPPInstancePyz.cxx:39
PyBytes_FromString
#define PyBytes_FromString
Definition: CPyCppyy.h:90
TBuffer::Length
Int_t Length() const
Definition: TBuffer.h:100
Cppyy::GetScope
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
Definition: clingwrapper.cxx:497
CPyCppyy::BindCppObject
PyObject * BindCppObject(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
Definition: ProxyWrappers.cxx:873
PyBytes_AS_STRING
#define PyBytes_AS_STRING
Definition: CPyCppyy.h:85
ProxyWrappers.h
PyBytes_Type
#define PyBytes_Type
Definition: CPyCppyy.h:93
kFALSE
const Bool_t kFALSE
Definition: RtypesCore.h:92
TBufferFile::ReadObjectAny
void * ReadObjectAny(const TClass *cast) override
Read object from I/O buffer.
Definition: TBufferFile.cxx:2348
CPyCppyy.h
TClass::GetClass
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:2938
Cppyy::GetScopedFinalName
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)
Definition: clingwrapper.cxx:1161
PyROOT
Definition: CPPInstancePyz.cxx:25
PyBytes_FromStringAndSize
#define PyBytes_FromStringAndSize
Definition: CPyCppyy.h:91
Cppyy::TCppType_t
TCppScope_t TCppType_t
Definition: cpp_cppyy.h:19
PyzCppHelpers.hxx
TBufferFile.h
PyROOT::gRootModule
PyObject * gRootModule
Definition: PyROOTModule.cxx:39
CustomPyTypes.h
TBufferFile
The concrete implementation of TBuffer for writing/reading to/from a ROOT file or socket.
Definition: TBufferFile.h:47
Converters.h
TBuffer::kWrite
@ kWrite
Definition: TBuffer.h:73
Py_RETURN_NONE
#define Py_RETURN_NONE
Definition: CPyCppyy.h:281
op_reduce
PyObject * op_reduce(CPPInstance *self, PyObject *)
PyROOT object proxy pickle support Turn the object proxy instance into a character stream and return ...
Definition: CPPInstancePyz.cxx:71