Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
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/API.h"
14
15#include "../../cppyy/CPyCppyy/src/CPyCppyy.h"
16#include "../../cppyy/CPyCppyy/src/CPPInstance.h"
17#include "../../cppyy/CPyCppyy/src/CustomPyTypes.h"
18
19#include "PyROOTPythonize.h"
20#include "TBufferFile.h"
21
22using namespace CPyCppyy;
23
24namespace PyROOT {
25extern PyObject *gRootModule;
26}
27
28////////////////////////////////////////////////////////////////////////////
29/// \brief Deserialize pickled objects
30/// \param[in] self Always null, since this is a module function.
31/// \param[in] args Pointer to a Python tuple object containing the arguments
32/// received from Python.
33///
34/// Helper function that deserializes pickled objects. It needs to be
35/// included in the extension module API because otherwise it is not
36/// callable from Python. This is important because it will be Python
37/// itself calling it when trying to expand a serialized object.
39{
40 PyObject *pybuf = 0, *pyname = 0;
41 if (!PyArg_ParseTuple(args, "O!O!:__expand__", &PyBytes_Type, &pybuf, &PyBytes_Type, &pyname))
42 return 0;
43 const char *clname = PyBytes_AS_STRING(pyname);
44 // TBuffer and its derived classes can't write themselves, but can be created
45 // directly from the buffer, so handle them in a special case
46 void *newObj = 0;
47 if (strcmp(clname, "TBufferFile") == 0) {
50 newObj = buf;
51 } else {
52 // use the PyString macro's to by-pass error checking; do not adopt the buffer,
53 // as the local TBufferFile can go out of scope (there is no copying)
55 newObj = buf.ReadObjectAny(0);
56 }
57 PyObject *result = CPyCppyy::Instance_FromVoidPtr(newObj, clname, /*python_owns=*/true);
58 return result;
59}
60
61/// PyROOT object proxy pickle support
62/// Turn the object proxy instance into a character stream and return for
63/// pickle, together with the callable object that can restore the stream
64/// into the object proxy instance.
66{
67 // keep a borrowed reference around to the callable function for expanding;
68 // because it is borrowed, it means that there can be no pickling during the
69 // shutdown of the libPyROOT module
70 static PyObject *s_expand = PyDict_GetItemString(PyModule_GetDict(PyROOT::gRootModule), "_CPPInstance__expand__");
71
72 // TBuffer and its derived classes can't write themselves, but can be created
73 // directly from the buffer, so handle them in a special case
74 static Cppyy::TCppType_t s_bfClass = Cppyy::GetScope("TBufferFile");
75 TBufferFile *buff = 0;
76 Cppyy::TCppType_t selfClass = ((CPPInstance *)self)->ObjectIsA();
77 if (selfClass == s_bfClass) {
79 } else {
80 auto className = Cppyy::GetScopedFinalName(selfClass);
81 if (className.find("__cppyy_internal::Dispatcher") == 0) {
82 PyErr_Format(PyExc_IOError,
83 "generic streaming of Python objects whose class derives from a C++ class is not supported. "
84 "Please refer to the Python pickle documentation for instructions on how to define "
85 "a custom __reduce__ method for the derived Python class");
86 return 0;
87 }
88 // no cast is needed, but WriteObject taking a TClass argument is protected,
89 // so use WriteObjectAny()
90 static TBufferFile s_buff(TBuffer::kWrite);
91 s_buff.Reset();
92 // to delete
93 if (s_buff.WriteObjectAny(CPyCppyy::Instance_AsVoidPtr(self), TClass::GetClass(className.c_str())) != 1) {
94 PyErr_Format(PyExc_IOError, "could not stream object of type %s",
95 Cppyy::GetScopedFinalName(selfClass).c_str());
96 return 0;
97 }
98 buff = &s_buff;
99 }
100 // use a string for the serialized result, as a python buffer will not copy
101 // the buffer contents; use a string for the class name, used when casting
102 // on reading back in (see CPPInstanceExpand defined above)
103 PyObject *res2 = PyTuple_New(2);
104 PyTuple_SET_ITEM(res2, 0, PyBytes_FromStringAndSize(buff->Buffer(), buff->Length()));
105 PyTuple_SET_ITEM(res2, 1, PyBytes_FromString(Cppyy::GetScopedFinalName(selfClass).c_str()));
106
107 PyObject *result = PyTuple_New(2);
108 Py_INCREF(s_expand);
109 PyTuple_SET_ITEM(result, 0, s_expand);
110 PyTuple_SET_ITEM(result, 1, res2);
111
112 return result;
113}
114
115////////////////////////////////////////////////////////////////////////////
116/// \brief Set __reduce__ attribute for CPPInstance objects
117/// \param[in] self Always null, since this is a module function.
118/// \param[in] args Pointer to a Python tuple object containing the arguments
119/// received from Python.
120///
121/// The C++ function op_reduce defined above is wrapped in a Python method
122/// so that it can be injected in CPPInstance
124{
125 PyObject *pyclass = PyTuple_GetItem(args, 0);
126
127 const char *attr = "__reduce__";
128
129 PyMethodDef *pdef = new PyMethodDef();
130 pdef->ml_name = attr;
131 pdef->ml_meth = (PyCFunction)op_reduce;
132 pdef->ml_flags = METH_NOARGS;
133 pdef->ml_doc = nullptr;
134
135 PyObject *func = PyCFunction_New(pdef, nullptr);
136 PyObject *method = CustomInstanceMethod_New(func, nullptr, pyclass);
137
138 // here PyObject_GenericSetAttr is used because CPPInstance does not allow
139 // attribute assignment using PyObject_SetAttr
140 // for more info refer to:
141 // https://bitbucket.org/wlav/cppyy/issues/110/user-defined-classes-in-c-dont-seem-to-be
142 PyObject_GenericSetAttr(pyclass, PyUnicode_FromString(attr), method);
143 Py_DECREF(method);
144 Py_DECREF(func);
145
147}
PyObject * op_reduce(PyObject *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:63
#define PyBytes_FromString
Definition CPyCppyy.h:69
#define PyBytes_FromStringAndSize
Definition CPyCppyy.h:70
#define Py_RETURN_NONE
Definition CPyCppyy.h:268
#define PyBytes_Type
Definition CPyCppyy.h:72
#define PyBytes_GET_SIZE
Definition CPyCppyy.h:66
_object PyObject
constexpr 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
The concrete implementation of TBuffer for writing/reading to/from a ROOT file or socket.
Definition TBufferFile.h:47
void WriteFastArray(const Bool_t *b, Long64_t n) override
Write array of n bools into the I/O buffer.
void * ReadObjectAny(const TClass *cast) override
Read object from I/O buffer.
void Reset() override
Reset buffer object. Resets map and buffer offset.
Int_t WriteObjectAny(const void *obj, const TClass *ptrClass, Bool_t cacheReuse=kTRUE) override
Write object to I/O buffer.
@ 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
PyObject * CustomInstanceMethod_New(PyObject *func, PyObject *self, PyObject *pyclass)
CPYCPPYY_EXTERN PyObject * Instance_FromVoidPtr(void *addr, const std::string &classname, bool python_owns=false)
Definition API.cxx:121
CPYCPPYY_EXTERN void * Instance_AsVoidPtr(PyObject *pyobject)
Definition API.cxx:106
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