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 =
71 PyDict_GetItemString(PyModule_GetDict(PyROOT::gRootModule), "_CPPInstance__expand__");
72
73 // TBuffer and its derived classes can't write themselves, but can be created
74 // directly from the buffer, so handle them in a special case
75 static Cppyy::TCppType_t s_bfClass = Cppyy::GetScope("TBufferFile");
76 TBufferFile *buff = 0;
77 Cppyy::TCppType_t selfClass = ((CPPInstance*)self)->ObjectIsA();
78 if (selfClass == s_bfClass) {
80 } else {
81 auto className = Cppyy::GetScopedFinalName(selfClass);
82 if (className.find("__cppyy_internal::Dispatcher") == 0) {
83 PyErr_Format(PyExc_IOError, "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
94 TClass::GetClass(className.c_str())) != 1) {
95 PyErr_Format(PyExc_IOError, "could not stream object of type %s",
96 Cppyy::GetScopedFinalName(selfClass).c_str());
97 return 0;
98 }
99 buff = &s_buff;
100 }
101 // use a string for the serialized result, as a python buffer will not copy
102 // the buffer contents; use a string for the class name, used when casting
103 // on reading back in (see CPPInstanceExpand defined above)
104 PyObject *res2 = PyTuple_New(2);
105 PyTuple_SET_ITEM(res2, 0, PyBytes_FromStringAndSize(buff->Buffer(), buff->Length()));
106 PyTuple_SET_ITEM(res2, 1, PyBytes_FromString(Cppyy::GetScopedFinalName(selfClass).c_str()));
107
108 PyObject *result = PyTuple_New(2);
109 Py_INCREF(s_expand);
110 PyTuple_SET_ITEM(result, 0, s_expand);
111 PyTuple_SET_ITEM(result, 1, res2);
112
113 return result;
114}
115
116////////////////////////////////////////////////////////////////////////////
117/// \brief Set __reduce__ attribute for CPPInstance objects
118/// \param[in] self Always null, since this is a module function.
119/// \param[in] args Pointer to a Python tuple object containing the arguments
120/// received from Python.
121///
122/// The C++ function op_reduce defined above is wrapped in a Python method
123/// so that it can be injected in CPPInstance
125{
126 PyObject *pyclass = PyTuple_GetItem(args, 0);
127
128 const char *attr = "__reduce__";
129
130 PyMethodDef *pdef = new PyMethodDef();
131 pdef->ml_name = attr;
132 pdef->ml_meth = (PyCFunction)op_reduce;
133 pdef->ml_flags = METH_NOARGS;
134 pdef->ml_doc = nullptr;
135
136 PyObject *func = PyCFunction_New(pdef, nullptr);
137 PyObject *method = CustomInstanceMethod_New(func, nullptr, pyclass);
138
139 // here PyObject_GenericSetAttr is used because CPPInstance does not allow
140 // attribute assignment using PyObject_SetAttr
141 // for more info refer to:
142 // https://bitbucket.org/wlav/cppyy/issues/110/user-defined-classes-in-c-dont-seem-to-be
143 PyObject_GenericSetAttr(pyclass, PyUnicode_FromString(attr), method);
144 Py_DECREF(method);
145 Py_DECREF(func);
146
148}
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:118
CPYCPPYY_EXTERN void * Instance_AsVoidPtr(PyObject *pyobject)
Definition API.cxx:103
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