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 auto className = Cppyy::GetScopedFinalName(self->ObjectIsA());
87 if (className.find("__cppyy_internal::Dispatcher") == 0) {
88 PyErr_Format(PyExc_IOError, "generic streaming of Python objects whose class derives from a C++ class is not supported. "
89 "Please refer to the Python pickle documentation for instructions on how to define "
90 "a custom __reduce__ method for the derived Python class");
91 return 0;
92 }
93 // no cast is needed, but WriteObject taking a TClass argument is protected,
94 // so use WriteObjectAny()
95 static TBufferFile s_buff(TBuffer::kWrite);
96 s_buff.Reset();
97 // to delete
98 if (s_buff.WriteObjectAny(self->GetObject(),
99 TClass::GetClass(className.c_str())) != 1) {
100 PyErr_Format(PyExc_IOError, "could not stream object of type %s",
101 Cppyy::GetScopedFinalName(self->ObjectIsA()).c_str());
102 return 0;
103 }
104 buff = &s_buff;
105 }
106 // use a string for the serialized result, as a python buffer will not copy
107 // the buffer contents; use a string for the class name, used when casting
108 // on reading back in (see CPPInstanceExpand defined above)
109 PyObject *res2 = PyTuple_New(2);
110 PyTuple_SET_ITEM(res2, 0, PyBytes_FromStringAndSize(buff->Buffer(), buff->Length()));
111 PyTuple_SET_ITEM(res2, 1, PyBytes_FromString(Cppyy::GetScopedFinalName(self->ObjectIsA()).c_str()));
112
113 PyObject *result = PyTuple_New(2);
114 Py_INCREF(s_expand);
115 PyTuple_SET_ITEM(result, 0, s_expand);
116 PyTuple_SET_ITEM(result, 1, res2);
117
118 return result;
119}
120
121////////////////////////////////////////////////////////////////////////////
122/// \brief Set __reduce__ attribute for CPPInstance objects
123/// \param[in] self Always null, since this is a module function.
124/// \param[in] args Pointer to a Python tuple object containing the arguments
125/// received from Python.
126///
127/// The C++ function op_reduce defined above is wrapped in a Python method
128/// so that it can be injected in CPPInstance
130{
131 PyObject *pyclass = PyTuple_GetItem(args, 0);
132
133 const char *attr = "__reduce__";
134
135 PyMethodDef *pdef = new PyMethodDef();
136 pdef->ml_name = attr;
137 pdef->ml_meth = (PyCFunction)op_reduce;
138 pdef->ml_flags = METH_NOARGS;
139 pdef->ml_doc = nullptr;
140
141 PyObject *func = PyCFunction_New(pdef, nullptr);
142 PyObject *method = CustomInstanceMethod_New(func, nullptr, pyclass);
143
144 // here PyObject_GenericSetAttr is used because CPPInstance does not allow
145 // attribute assignment using PyObject_SetAttr
146 // for more info refer to:
147 // https://bitbucket.org/wlav/cppyy/issues/110/user-defined-classes-in-c-dont-seem-to-be
148 PyObject_GenericSetAttr(pyclass, CPyCppyy_PyText_FromString(attr), method);
149 Py_DECREF(method);
150 Py_DECREF(func);
151
153}
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:289
#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:43
const Bool_t kFALSE
Definition: RtypesCore.h:101
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t attr
#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:47
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:73
@ kRead
Definition: TBuffer.h:73
Int_t Length() const
Definition: TBuffer.h:100
char * Buffer() const
Definition: TBuffer.h:96
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:2968
Set of helper functions that are invoked from the pythonizors, on the Python side.
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