diff --git a/bindings/pyroot_experimental/PyROOT/CMakeLists.txt b/bindings/pyroot_experimental/PyROOT/CMakeLists.txt index 77944f6a9d7f611a16ad298c0b96b64a743e06ec..c20f4a2016f322254c81dcacb8005fc4f44143e5 100644 --- a/bindings/pyroot_experimental/PyROOT/CMakeLists.txt +++ b/bindings/pyroot_experimental/PyROOT/CMakeLists.txt @@ -11,6 +11,7 @@ set(py_sources ROOT/pythonization/_rvec.py ROOT/pythonization/_stl_vector.py ROOT/pythonization/_tarray.py + ROOT/pythonization/_tclonesarray.py ROOT/pythonization/_tcollection.py ROOT/pythonization/_tdirectory.py ROOT/pythonization/_tdirectoryfile.py @@ -26,6 +27,7 @@ set(sources src/TDirectoryPyz.cxx src/TFilePyz.cxx src/TTreePyz.cxx + src/TClonesArrayPyz.cxx src/GenericPyz.cxx src/RVecPyz.cxx src/PyzPythonHelpers.cxx diff --git a/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_tclonesarray.py b/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_tclonesarray.py new file mode 100644 index 0000000000000000000000000000000000000000..bfb159512bc83599b6ac4893749a839abd412a2d --- /dev/null +++ b/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_tclonesarray.py @@ -0,0 +1,25 @@ +# Author: Enric Tejedor CERN 02/2019 + +################################################################################ +# Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. # +# All rights reserved. # +# # +# For the licensing terms see $ROOTSYS/LICENSE. # +# For the list of contributors see $ROOTSYS/README/CREDITS. # +################################################################################ + +from ROOT import pythonization +from libROOTPython import AddSetItemTCAPyz + + +@pythonization() +def pythonize_tclonesarray(klass, name): + # Parameters: + # klass: class to be pythonized + # name: string containing the name of the class + + if name == 'TClonesArray': + # Add item setter method + AddSetItemTCAPyz(klass) + + return True diff --git a/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx b/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx index 7a32b0e0687454a57769a184edc1619681078223..141722ac7593adbb50f07a492a9416350cd78ded 100644 --- a/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx +++ b/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx @@ -49,6 +49,8 @@ static PyMethodDef gPyROOTMethods[] = {{(char *)"AddDirectoryWritePyz", (PyCFunc (char *)"Fully enable the use of TTree::SetBranchAddress from Python"}, {(char *)"BranchPyz", (PyCFunction)PyROOT::BranchPyz, METH_VARARGS, (char *)"Fully enable the use of TTree::Branch from Python"}, + {(char *)"AddSetItemTCAPyz", (PyCFunction)PyROOT::AddSetItemTCAPyz, METH_VARARGS, + (char *)"Customize the setting of an item of a TClonesArray"}, {(char *)"AddPrettyPrintingPyz", (PyCFunction)PyROOT::AddPrettyPrintingPyz, METH_VARARGS, (char *)"Add pretty printing pythonization"}, {(char *)"GetEndianess", (PyCFunction)PyROOT::GetEndianess, METH_NOARGS, diff --git a/bindings/pyroot_experimental/PyROOT/src/PyROOTPythonize.h b/bindings/pyroot_experimental/PyROOT/src/PyROOTPythonize.h index a1173c9dc6996377a9a2f0535257528ab1573993..ec7761a51ade461bf7825cde004eb2eea5a68e4d 100644 --- a/bindings/pyroot_experimental/PyROOT/src/PyROOTPythonize.h +++ b/bindings/pyroot_experimental/PyROOT/src/PyROOTPythonize.h @@ -21,6 +21,7 @@ PyObject *AddDirectoryWritePyz(PyObject *self, PyObject *args); PyObject *AddDirectoryAttrSyntaxPyz(PyObject *self, PyObject *args); PyObject *AddBranchAttrSyntax(PyObject *self, PyObject *args); PyObject *AddFileOpenPyz(PyObject *self, PyObject *args); +PyObject *AddSetItemTCAPyz(PyObject *self, PyObject *args); PyObject *SetBranchAddressPyz(PyObject *self, PyObject *args); PyObject *BranchPyz(PyObject *self, PyObject *args); PyObject *GetEndianess(PyObject *self); diff --git a/bindings/pyroot_experimental/PyROOT/src/PyzCppHelpers.cxx b/bindings/pyroot_experimental/PyROOT/src/PyzCppHelpers.cxx index ba2497004f2b929cee651e14bff90423e32c5e29..be613845027775ba5cfbac201fb466490733b5b4 100644 --- a/bindings/pyroot_experimental/PyROOT/src/PyzCppHelpers.cxx +++ b/bindings/pyroot_experimental/PyROOT/src/PyzCppHelpers.cxx @@ -21,11 +21,11 @@ pythonizations. #include "CPPInstance.h" #include "TClass.h" -PyObject *CallPyObjMethod(PyObject *obj, const char *meth, PyObject *arg1) +PyObject *CallPyObjMethod(PyObject *obj, const char *meth) { - // Helper; call method with signature: obj->meth(arg1). + // Helper; call method with signature: obj->meth() Py_INCREF(obj); - PyObject *result = PyObject_CallMethod(obj, const_cast<char *>(meth), const_cast<char *>("O"), arg1); + PyObject* result = PyObject_CallMethod(obj, const_cast<char*>(meth), const_cast<char*>("")); Py_DECREF(obj); return result; } diff --git a/bindings/pyroot_experimental/PyROOT/src/PyzCppHelpers.hxx b/bindings/pyroot_experimental/PyROOT/src/PyzCppHelpers.hxx index b8ef65fe553d113be4f3736338168a92409a5ab4..2e14d1804cf54970877629d1fb24de2f726f7f79 100644 --- a/bindings/pyroot_experimental/PyROOT/src/PyzCppHelpers.hxx +++ b/bindings/pyroot_experimental/PyROOT/src/PyzCppHelpers.hxx @@ -16,5 +16,5 @@ namespace CPyCppyy { class CPPInstance; } -PyObject *CallPyObjMethod(PyObject *obj, const char *meth, PyObject *arg1); +PyObject *CallPyObjMethod(PyObject *obj, const char *meth); TClass *GetTClass(const CPyCppyy::CPPInstance *pyobj); diff --git a/bindings/pyroot_experimental/PyROOT/src/TClonesArrayPyz.cxx b/bindings/pyroot_experimental/PyROOT/src/TClonesArrayPyz.cxx new file mode 100644 index 0000000000000000000000000000000000000000..35ef6f1d07f35929ac1a35958f9d9d3e39419805 --- /dev/null +++ b/bindings/pyroot_experimental/PyROOT/src/TClonesArrayPyz.cxx @@ -0,0 +1,125 @@ +// Author: Enric Tejedor CERN 02/2019 +// Original PyROOT code by Wim Lavrijsen, LBL + +/************************************************************************* + * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +// Bindings +#include "CPyCppyy.h" +#include "PyROOTPythonize.h" +#include "CPPInstance.h" +#include "MemoryRegulator.h" +#include "Utility.h" +#include "PyzCppHelpers.hxx" + +// ROOT +#include "TClass.h" +#include "TClonesArray.h" + +using namespace CPyCppyy; + +// Helper: converts Python index into straight C index. +static PyObject *PyStyleIndex(PyObject *self, PyObject *index) +{ + Py_ssize_t idx = PyInt_AsSsize_t(index); + if (idx == (Py_ssize_t)-1 && PyErr_Occurred()) + return nullptr; + + // To know the capacity of a TClonesArray, we need to invoke GetSize + PyObject *pysize = CallPyObjMethod(self, "GetSize"); + if (!pysize) { + PyErr_Clear(); + return nullptr; + } + + Py_ssize_t size = PyInt_AsSsize_t(pysize); + Py_DECREF(pysize); + if (idx >= size || (idx < 0 && idx < -size)) { + PyErr_SetString(PyExc_IndexError, "index out of range"); + return nullptr; + } + + PyObject *pyindex = nullptr; + if (idx >= 0) { + Py_INCREF(index); + pyindex = index; + } else { + pyindex = PyLong_FromSsize_t(size + idx); + } + + return pyindex; +} + +// Customize item setting +PyObject *SetItem(CPPInstance *self, PyObject *args) +{ + CPPInstance *pyobj = nullptr; + PyObject *idx = nullptr; + if (!PyArg_ParseTuple(args, const_cast<char *>("OO!:__setitem__"), &idx, &CPPInstance_Type, &pyobj)) + return nullptr; + + if (!self->GetObject()) { + PyErr_SetString(PyExc_TypeError, "unsubscriptable object"); + return nullptr; + } + + PyObject *pyindex = PyStyleIndex((PyObject *)self, idx); + if (!pyindex) + return nullptr; + int index = (int)PyLong_AsLong(pyindex); + Py_DECREF(pyindex); + + // Get hold of the actual TClonesArray + auto cla = (TClonesArray *)GetTClass(self)->DynamicCast(TClonesArray::Class(), self->GetObject()); + + if (!cla) { + PyErr_SetString(PyExc_TypeError, "attempt to call with null object"); + return nullptr; + } + + if (Cppyy::GetScope(cla->GetClass()->GetName()) != pyobj->ObjectIsA()) { + PyErr_Format(PyExc_TypeError, "require object of type %s, but %s given", cla->GetClass()->GetName(), + Cppyy::GetFinalName(pyobj->ObjectIsA()).c_str()); + } + + // Destroy old object, if applicable + if (((const TClonesArray &)*cla)[index]) { + cla->RemoveAt(index); + } + + if (pyobj->GetObject()) { + // Accessing an entry will result in new, uninitialized memory (if properly used) + TObject *object = (*cla)[index]; + pyobj->CppOwns(); + MemoryRegulator::RegisterPyObject(pyobj, object); + memcpy((void *)object, pyobj->GetObject(), cla->GetClass()->Size()); + } + + Py_RETURN_NONE; +} + +//////////////////////////////////////////////////////////////////////////// +/// \brief Customize the setting of an item of a TClonesArray. +/// \param[in] self Always null, since this is a module function. +/// \param[in] args Pointer to a Python tuple object containing the arguments +/// received from Python. +/// +/// Inject a __setitem__ implementation that customizes the setting of an item +/// into a TClonesArray. +/// +/// The __setitem__ pythonization that TClonesArray inherits from TSeqCollection +/// does not apply in this case and a redefinition is required. The reason is +/// TClonesArray sets objects by constructing them in-place, which is impossible +/// to support as the Python object given as value must exist a priori. It can, +/// however, be memcpy'd and stolen. +PyObject *PyROOT::AddSetItemTCAPyz(PyObject * /* self */, PyObject *args) +{ + PyObject *pyclass = PyTuple_GetItem(args, 0); + Utility::AddToClass(pyclass, "__setitem__", (PyCFunction)SetItem); + Py_RETURN_NONE; +}