Logo ROOT  
Reference Guide
TClonesArrayPyz.cxx
Go to the documentation of this file.
1 // Author: Enric Tejedor CERN 02/2019
2 // Original PyROOT code by Wim Lavrijsen, LBL
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2019, 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 "MemoryRegulator.h"
17 #include "Utility.h"
18 #include "PyzCppHelpers.hxx"
19 
20 // ROOT
21 #include "TClass.h"
22 #include "TClonesArray.h"
23 #include "TBufferFile.h"
24 
25 using namespace CPyCppyy;
26 
27 // Clone an object into a position of a TClonesArray
28 static TObject *CloneObjectInPlace(const TObject *obj, TClonesArray *cla, int index)
29 {
30  // Get or create object with default constructor at index
31  char *arrObj = (char *)cla->ConstructedAt(index);
32  if (!arrObj) {
33  PyErr_Format(PyExc_RuntimeError, "Failed to create new object at index %d of TClonesArray", index);
34  return nullptr;
35  }
36 
37  auto baseOffset = obj->IsA()->GetBaseClassOffset(TObject::Class());
38  auto newObj = (TObject *)(arrObj + baseOffset);
39 
40  // Create a buffer where the object will be streamed
41  TBufferFile buffer(TBuffer::kWrite, cla->GetClass()->Size());
42  buffer.MapObject(obj); // register obj in map to handle self reference
43  const_cast<TObject *>(obj)->Streamer(buffer);
44 
45  // Read new object from buffer
46  buffer.SetReadMode();
47  buffer.ResetMap();
48  buffer.SetBufferOffset(0);
49  buffer.MapObject(newObj); // register obj in map to handle self reference
50  newObj->Streamer(buffer);
51  newObj->ResetBit(kIsReferenced);
52  newObj->ResetBit(kCanDelete);
53 
54  return newObj;
55 }
56 
57 // Convert Python index into straight C index
58 static PyObject *PyStyleIndex(PyObject *self, PyObject *index)
59 {
60  Py_ssize_t idx = PyInt_AsSsize_t(index);
61  if (idx == (Py_ssize_t)-1 && PyErr_Occurred())
62  return nullptr;
63 
64  // To know the capacity of a TClonesArray, we need to invoke GetSize
65  PyObject *pysize = CallPyObjMethod(self, "GetSize");
66  if (!pysize) {
67  PyErr_SetString(PyExc_RuntimeError, "unable to get the size of TClonesArray");
68  return nullptr;
69  }
70 
71  Py_ssize_t size = PyInt_AsSsize_t(pysize);
72  Py_DECREF(pysize);
73  if (idx >= size || (idx < 0 && idx < -size)) {
74  PyErr_SetString(PyExc_IndexError, "index out of range");
75  return nullptr;
76  }
77 
78  PyObject *pyindex = nullptr;
79  if (idx >= 0) {
80  Py_INCREF(index);
81  pyindex = index;
82  } else {
83  pyindex = PyLong_FromSsize_t(size + idx);
84  }
85 
86  // We transfer ownership to the caller, who needs to decref
87  return pyindex;
88 }
89 
90 // Customize item setting
92 {
93  CPPInstance *pyobj = nullptr;
94  PyObject *idx = nullptr;
95  if (!PyArg_ParseTuple(args, const_cast<char *>("OO!:__setitem__"), &idx, &CPPInstance_Type, &pyobj))
96  return nullptr;
97 
98  if (!self->GetObject()) {
99  PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
100  return nullptr;
101  }
102 
103  auto pyindex = PyStyleIndex((PyObject *)self, idx);
104  if (!pyindex)
105  return nullptr;
106  auto index = (int)PyLong_AsLong(pyindex);
107  Py_DECREF(pyindex);
108 
109  // Get hold of the actual TClonesArray
110  auto cla = (TClonesArray *)GetTClass(self)->DynamicCast(TClonesArray::Class(), self->GetObject());
111 
112  if (!cla) {
113  PyErr_SetString(PyExc_TypeError, "attempt to call with null object");
114  return nullptr;
115  }
116 
117  if (Cppyy::GetScope(cla->GetClass()->GetName()) != pyobj->ObjectIsA()) {
118  PyErr_Format(PyExc_TypeError, "require object of type %s, but %s given", cla->GetClass()->GetName(),
119  Cppyy::GetFinalName(pyobj->ObjectIsA()).c_str());
120  return nullptr;
121  }
122 
123  // Destroy old object, if applicable
124  if (((const TClonesArray &)*cla)[index]) {
125  cla->RemoveAt(index);
126  }
127 
128  if (pyobj->GetObject()) {
129  auto object = CloneObjectInPlace((TObject *)pyobj->GetObject(), cla, index);
130  if (!object)
131  return nullptr;
132 
133  // Unregister the previous pair (C++ obj, Py obj)
134  auto pyclass = PyObject_GetAttrString((PyObject*)pyobj, "__class__");
135  MemoryRegulator::UnregisterPyObject(pyobj, pyclass);
136  Py_DECREF(pyclass);
137 
138  // Delete the previous C++ object (if Python owns)
139  if (pyobj->fFlags & CPPInstance::kIsOwner)
140  delete static_cast<TObject *>(pyobj->GetObject());
141 
142  // Update the Python proxy with the new C++ object and register the new pair
143  pyobj->Set(object);
144  MemoryRegulator::RegisterPyObject(pyobj, object);
145 
146  // A TClonesArray is always the owner of the objects it contains
147  pyobj->CppOwns();
148  }
149 
151 }
152 
153 ////////////////////////////////////////////////////////////////////////////
154 /// \brief Customize the setting of an item of a TClonesArray.
155 /// \param[in] self Always null, since this is a module function.
156 /// \param[in] args Pointer to a Python tuple object containing the arguments
157 /// received from Python.
158 ///
159 /// Inject a __setitem__ implementation that customizes the setting of an item
160 /// into a TClonesArray.
161 ///
162 /// The __setitem__ pythonization that TClonesArray inherits from TSeqCollection
163 /// does not apply in this case and a redefinition is required. The reason is
164 /// TClonesArray sets objects by constructing them in-place, which is impossible
165 /// to support as the Python object given as value must exist a priori. It can,
166 /// however, be memcpy'd and stolen.
168 {
169  PyObject *pyclass = PyTuple_GetItem(args, 0);
170  Utility::AddToClass(pyclass, "__setitem__", (PyCFunction)SetItem);
172 }
CPyCppyy
Set of helper functions that are invoked from the pythonizors, on the Python side.
Definition: TPyClassGenerator.cxx:31
CPPInstance.h
PyROOT::AddSetItemTCAPyz
PyObject * AddSetItemTCAPyz(PyObject *self, PyObject *args)
Customize the setting of an item of a TClonesArray.
Definition: TClonesArrayPyz.cxx:167
kCanDelete
@ kCanDelete
Definition: TObject.h:339
TClass::Size
Int_t Size() const
Return size of object of this class.
Definition: TClass.cxx:5597
PyObject
_object PyObject
Definition: PyMethodBase.h:42
CallPyObjMethod
PyObject * CallPyObjMethod(PyObject *obj, const char *meth)
Set of helper functions that are invoked from the C++ implementation of pythonizations.
Definition: PyzCppHelpers.cxx:20
TBufferIO::ResetMap
void ResetMap() override
Delete existing fMap and reset map counter.
Definition: TBufferIO.cxx:288
Utility.h
PyROOTPythonize.h
CPyCppyy::CPPInstance
Definition: CPPInstance.h:26
TClass.h
TBufferIO::MapObject
void MapObject(const TObject *obj, UInt_t offset=1) override
Add object to the fMap container.
Definition: TBufferIO.cxx:163
CPyCppyy::CPPInstance::GetObject
void * GetObject()
Definition: CPPInstance.h:93
TBuffer::SetBufferOffset
void SetBufferOffset(Int_t offset=0)
Definition: TBuffer.h:93
MemoryRegulator.h
TClonesArray.h
Cppyy::GetScope
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
Definition: clingwrapper.cxx:497
TBuffer::SetReadMode
void SetReadMode()
Set buffer in read mode.
Definition: TBuffer.cxx:302
PyInt_AsSsize_t
#define PyInt_AsSsize_t
Definition: CPyCppyy.h:229
CloneObjectInPlace
static TObject * CloneObjectInPlace(const TObject *obj, TClonesArray *cla, int index)
Definition: TClonesArrayPyz.cxx:28
GetTClass
TClass * GetTClass(const CPyCppyy::CPPInstance *pyobj)
Definition: PyzCppHelpers.cxx:44
SetItem
PyObject * SetItem(CPPInstance *self, PyObject *args)
Definition: TClonesArrayPyz.cxx:91
kIsReferenced
@ kIsReferenced
Definition: TObject.h:342
CPyCppyy::CPPInstance::kIsOwner
@ kIsOwner
Definition: CPPInstance.h:31
TClonesArray::GetClass
TClass * GetClass() const
Definition: TClonesArray.h:53
CPyCppyy.h
PyStyleIndex
static PyObject * PyStyleIndex(PyObject *self, PyObject *index)
Definition: TClonesArrayPyz.cxx:58
Cppyy::GetFinalName
RPY_EXPORTED std::string GetFinalName(TCppType_t type)
Definition: clingwrapper.cxx:1148
TClass::DynamicCast
void * DynamicCast(const TClass *base, void *obj, Bool_t up=kTRUE)
Cast obj of this class type up to baseclass cl if up is true.
Definition: TClass.cxx:4891
PyzCppHelpers.hxx
CPyCppyy::MemoryRegulator::UnregisterPyObject
static bool UnregisterPyObject(CPPInstance *pyobj, PyObject *pyclass)
Definition: MemoryRegulator.cxx:200
TObject
Mother of all ROOT objects.
Definition: TObject.h:37
TBufferFile.h
CPyCppyy::Utility::AddToClass
bool AddToClass(PyObject *pyclass, const char *label, PyCFunction cfunc, int flags=METH_VARARGS)
Definition: Utility.cxx:169
CPyCppyy::MemoryRegulator::RegisterPyObject
static bool RegisterPyObject(CPPInstance *pyobj, void *cppobj)
Definition: MemoryRegulator.cxx:173
CPyCppyy::CPPInstance_Type
PyTypeObject CPPInstance_Type
Definition: CPPInstance.cxx:745
TClonesArray::ConstructedAt
TObject * ConstructedAt(Int_t idx)
Get an object at index 'idx' that is guaranteed to have been constructed.
Definition: TClonesArray.cxx:366
CPyCppyy::CPPInstance::ObjectIsA
Cppyy::TCppType_t ObjectIsA(bool check_smart=true) const
Definition: CPPInstance.h:106
Class
void Class()
Definition: Class.C:29
TClonesArray
An array of clone (identical) objects.
Definition: TClonesArray.h:29
TBufferFile
The concrete implementation of TBuffer for writing/reading to/from a ROOT file or socket.
Definition: TBufferFile.h:47
TClonesArray::RemoveAt
virtual TObject * RemoveAt(Int_t idx)
Remove object at index idx.
Definition: TClonesArray.cxx:572
TBuffer::kWrite
@ kWrite
Definition: TBuffer.h:73
Py_RETURN_NONE
#define Py_RETURN_NONE
Definition: CPyCppyy.h:281
int