Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TTreePyz.cxx
Go to the documentation of this file.
1// Author: Enric Tejedor CERN 06/2018
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 "../../cppyy/CPyCppyy/src/CPyCppyy.h"
14#include "../../cppyy/CPyCppyy/src/CPPInstance.h"
15#include "../../cppyy/CPyCppyy/src/ProxyWrappers.h"
16#include "../../cppyy/CPyCppyy/src/Utility.h"
17
18// Th API changed a bit with the new CPyCppyy, which we can detect by checking
19// if CPYCPPYY_VERSION_HEX is defined.
20#ifdef CPYCPPYY_VERSION_HEX
21#include "../../cppyy/CPyCppyy/src/Dimensions.h"
22#endif
23
24#include "CPyCppyy/API.h"
25
26#include "PyROOTPythonize.h"
27
28// ROOT
29#include "TClass.h"
30#include "TTree.h"
31#include "TBranch.h"
32#include "TBranchElement.h"
33#include "TBranchObject.h"
34#include "TLeaf.h"
35#include "TLeafElement.h"
36#include "TLeafObject.h"
37#include "TStreamerElement.h"
38#include "TStreamerInfo.h"
39
40namespace {
41
42// Get the TClass of the C++ object proxied by pyobj
43TClass *GetTClass(const PyObject *pyobj)
44{
45 return TClass::GetClass(Cppyy::GetScopedFinalName(((CPyCppyy::CPPInstance*)pyobj)->ObjectIsA()).c_str());
46}
47
48} // namespace
49
50using namespace CPyCppyy;
51
52static TBranch *SearchForBranch(TTree *tree, const char *name)
53{
54 TBranch *branch = tree->GetBranch(name);
55 if (!branch) {
56 // for benefit of naming of sub-branches, the actual name may have a trailing '.'
57 branch = tree->GetBranch((std::string(name) + '.').c_str());
58 }
59 return branch;
60}
61
62static TLeaf *SearchForLeaf(TTree *tree, const char *name, TBranch *branch)
63{
64 TLeaf *leaf = tree->GetLeaf(name);
65 if (branch && !leaf) {
66 leaf = branch->GetLeaf(name);
67 if (!leaf) {
68 TObjArray *leaves = branch->GetListOfLeaves();
69 if (leaves->GetSize() && (leaves->First() == leaves->Last())) {
70 // i.e., if unambiguously only this one
71 leaf = (TLeaf *)leaves->At(0);
72 }
73 }
74 }
75 return leaf;
76}
77
78static PyObject *BindBranchToProxy(TTree *tree, const char *name, TBranch *branch)
79{
80 // for partial return of a split object
81 if (branch->InheritsFrom(TBranchElement::Class())) {
82 TBranchElement *be = (TBranchElement *)branch;
83 if (be->GetCurrentClass() && (be->GetCurrentClass() != be->GetTargetClass()) && (0 <= be->GetID())) {
84 Long_t offset = ((TStreamerElement *)be->GetInfo()->GetElements()->At(be->GetID()))->GetOffset();
86 }
87 }
88
89 // for return of a full object
90 if (branch->IsA() == TBranchElement::Class() || branch->IsA() == TBranchObject::Class()) {
91 TClass *klass = TClass::GetClass(branch->GetClassName());
92 if (klass && branch->GetAddress())
93 return CPyCppyy::Instance_FromVoidPtr(*(void **)branch->GetAddress(), branch->GetClassName());
94
95 // try leaf, otherwise indicate failure by returning a typed null-object
96 TObjArray *leaves = branch->GetListOfLeaves();
97 if (klass && !tree->GetLeaf(name) && !(leaves->GetSize() && (leaves->First() == leaves->Last())))
98 return CPyCppyy::Instance_FromVoidPtr(nullptr, branch->GetClassName());
99 }
100
101 return nullptr;
102}
103
104static PyObject *WrapLeaf(TLeaf *leaf)
105{
106 if (1 < leaf->GetLenStatic() || leaf->GetLeafCount()) {
107 bool isStatic = 1 < leaf->GetLenStatic();
108 // array types
109 std::string typeName = leaf->GetTypeName();
110#ifdef CPYCPPYY_VERSION_HEX
111 dim_t dimsArr[]{ leaf->GetNdata() };
112 CPyCppyy::Dimensions dims{1, dimsArr};
113#else
114 dim_t dims[]{ 1, leaf->GetNdata() }; // first entry is the number of dims
115#endif
116 Converter *pcnv = CreateConverter(typeName + (isStatic ? "[]" : "*"), dims);
117
118 void *address = 0;
119 if (leaf->GetBranch())
120 address = (void *)leaf->GetBranch()->GetAddress();
121 if (!address)
122 address = (void *)leaf->GetValuePointer();
123
124 PyObject *value = pcnv->FromMemory(&address);
126
127 return value;
128 } else if (leaf->GetValuePointer()) {
129 // value types
130 Converter *pcnv = CreateConverter(leaf->GetTypeName());
131 PyObject *value = 0;
132 if (leaf->IsA() == TLeafElement::Class() || leaf->IsA() == TLeafObject::Class())
133 value = pcnv->FromMemory((void *)*(void **)leaf->GetValuePointer());
134 else
135 value = pcnv->FromMemory((void *)leaf->GetValuePointer());
137
138 return value;
139 }
140
141 return nullptr;
142}
143
144// Allow access to branches/leaves as if they were data members
146{
147 const char *name_possibly_alias = PyUnicode_AsUTF8(pyname);
148 if (!name_possibly_alias)
149 return 0;
150
151 // get hold of actual tree
152 auto tree = (TTree *)GetTClass(self)->DynamicCast(TTree::Class(), CPyCppyy::Instance_AsVoidPtr(self));
153
154 if (!tree) {
155 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
156 return 0;
157 }
158
159 // deal with possible aliasing
160 const char *name = tree->GetAlias(name_possibly_alias);
161 if (!name)
162 name = name_possibly_alias;
163
164 // search for branch first (typical for objects)
165 TBranch *branch = SearchForBranch(tree, name);
166
167 if (branch) {
168 // found a branched object, wrap its address for the object it represents
169 auto proxy = BindBranchToProxy(tree, name, branch);
170 if (proxy != nullptr)
171 return proxy;
172 }
173
174 // if not, try leaf
175 TLeaf *leaf = SearchForLeaf(tree, name, branch);
176
177 if (leaf) {
178 // found a leaf, extract value and wrap with a Python object according to its type
179 auto wrapper = WrapLeaf(leaf);
180 if (wrapper != nullptr)
181 return wrapper;
182 }
183
184 // confused
185 PyErr_Format(PyExc_AttributeError, "\'%s\' object has no attribute \'%s\'", tree->IsA()->GetName(), name);
186 return 0;
187}
188
189////////////////////////////////////////////////////////////////////////////
190/// \brief Allow branches to be accessed as attributes of a tree.
191/// \param[in] self Always null, since this is a module function.
192/// \param[in] args Pointer to a Python tuple object containing the arguments
193/// received from Python.
194///
195/// Allow access to branches/leaves as if they were Python data attributes of the tree
196/// (e.g. mytree.branch)
198{
199 PyObject *pyclass = PyTuple_GetItem(args, 0);
200 Utility::AddToClass(pyclass, "__getattr__", (PyCFunction)GetAttr, METH_O);
202}
203
204////////////////////////////////////////////////////////////////////////////
205/// \brief Add pythonization for TTree::SetBranchAddress.
206/// \param[in] self Always null, since this is a module function.
207/// \param[in] args Pointer to a Python tuple object containing the arguments
208/// received from Python.
209///
210/// Modify the behaviour of SetBranchAddress so that proxy references can be passed
211/// as arguments from the Python side, more precisely in cases where the C++
212/// implementation of the method expects the address of a pointer.
213///
214/// For example:
215/// ~~~{.py}
216/// v = ROOT.std.vector('int')()
217/// t.SetBranchAddress("my_vector_branch", v)
218/// ~~~
220{
221 PyObject *treeObj = nullptr, *name = nullptr, *address = nullptr;
222
223 int argc = PyTuple_GET_SIZE(args);
224
225// Look for the (const char*, void*) overload
226 auto argParseStr = "OUO:SetBranchAddress";
227 if (argc == 3 && PyArg_ParseTuple(args, argParseStr, &treeObj, &name, &address)) {
228
229 auto tree = (TTree *)GetTClass(treeObj)->DynamicCast(TTree::Class(), CPyCppyy::Instance_AsVoidPtr(treeObj));
230
231 if (!tree) {
232 PyErr_SetString(PyExc_TypeError,
233 "TTree::SetBranchAddress must be called with a TTree instance as first argument");
234 return nullptr;
235 }
236
237 auto branchName = PyUnicode_AsUTF8(name);
238 auto branch = tree->GetBranch(branchName);
239 if (!branch) {
240 PyErr_SetString(PyExc_TypeError, "TTree::SetBranchAddress must be called with a valid branch name");
241 return nullptr;
242 }
243
244 bool isLeafList = branch->IsA() == TBranch::Class();
245
246 void *buf = 0;
247 if (CPyCppyy::Instance_Check(address)) {
248 ((CPPInstance *)address)->GetDatamemberCache(); // force creation of cache
249
250 if (((CPPInstance *)address)->fFlags & CPPInstance::kIsReference || isLeafList)
251 buf = CPyCppyy::Instance_AsVoidPtr(address);
252 else
253 buf = (void *)&(((CPPInstance *)address)->GetObjectRaw());
254 } else
255 Utility::GetBuffer(address, '*', 1, buf, false);
256
257 if (buf != nullptr) {
258 auto res = tree->SetBranchAddress(PyUnicode_AsUTF8(name), buf);
259 return PyInt_FromLong(res);
260 }
261 }
262
263 // Not the overload we wanted to pythonize, return None
265}
266
267////////////////////////////////////////////////////////////////////////////
268/// Try to match the arguments of TTree::Branch to the following overload:
269/// - ( const char*, void*, const char*, Int_t = 32000 )
270/// If the match succeeds, invoke Branch on the C++ tree with the right
271/// arguments.
273{
274 PyObject *treeObj = nullptr;
275 PyObject *name = nullptr, *address = nullptr, *leaflist = nullptr, *bufsize = nullptr;
276
277 if (PyArg_ParseTuple(args, "OO!OO!|O!:Branch",
278 &treeObj,
279 &PyUnicode_Type, &name,
280 &address,
281 &PyUnicode_Type, &leaflist,
282 &PyInt_Type, &bufsize)) {
283
284 auto tree = (TTree *)GetTClass(treeObj)->DynamicCast(TTree::Class(), CPyCppyy::Instance_AsVoidPtr(treeObj));
285 if (!tree) {
286 PyErr_SetString(PyExc_TypeError, "TTree::Branch must be called with a TTree instance as first argument");
287 return nullptr;
288 }
289
290 void *buf = nullptr;
291 if (CPPInstance_Check(address))
292 buf = CPyCppyy::Instance_AsVoidPtr(address);
293 else
294 Utility::GetBuffer(address, '*', 1, buf, false);
295
296 if (buf) {
297 TBranch *branch = nullptr;
298 if (argc == 5) {
299 branch = tree->Branch(PyUnicode_AsUTF8(name), buf, PyUnicode_AsUTF8(leaflist),
300 PyInt_AS_LONG(bufsize));
301 } else {
302 branch = tree->Branch(PyUnicode_AsUTF8(name), buf, PyUnicode_AsUTF8(leaflist));
303 }
304
305 return BindCppObject(branch, Cppyy::GetScope("TBranch"));
306 }
307 }
308 PyErr_Clear();
309
311}
312
313////////////////////////////////////////////////////////////////////////////
314/// Try to match the arguments of TTree::Branch to one of the following
315/// overloads:
316/// - ( const char*, const char*, T**, Int_t = 32000, Int_t = 99 )
317/// - ( const char*, T**, Int_t = 32000, Int_t = 99 )
318/// If the match succeeds, invoke Branch on the C++ tree with the right
319/// arguments.
321{
322 PyObject *treeObj = nullptr;
323 PyObject *name = nullptr, *clName = nullptr, *address = nullptr, *bufsize = nullptr, *splitlevel = nullptr;
324
325 auto bIsMatch = false;
326 if (PyArg_ParseTuple(args, "OO!O!O|O!O!:Branch",
327 &treeObj,
328 &PyUnicode_Type, &name,
329 &PyUnicode_Type, &clName,
330 &address,
331 &PyInt_Type, &bufsize,
332 &PyInt_Type, &splitlevel)) {
333 bIsMatch = true;
334 } else {
335 PyErr_Clear();
336 if (PyArg_ParseTuple(args, "OO!O|O!O!",
337 &treeObj,
338 &PyUnicode_Type, &name,
339 &address,
340 &PyInt_Type, &bufsize,
341 &PyInt_Type, &splitlevel)) {
342 bIsMatch = true;
343 } else {
344 PyErr_Clear();
345 }
346 }
347
348 if (bIsMatch) {
349 auto tree = (TTree *)GetTClass(treeObj)->DynamicCast(TTree::Class(), CPyCppyy::Instance_AsVoidPtr(treeObj));
350 if (!tree) {
351 PyErr_SetString(PyExc_TypeError, "TTree::Branch must be called with a TTree instance as first argument");
352 return nullptr;
353 }
354
355 std::string klName = clName ? PyUnicode_AsUTF8(clName) : "";
356 void *buf = nullptr;
357
358 if (CPPInstance_Check(address)) {
359 if (((CPPInstance *)address)->fFlags & CPPInstance::kIsReference)
360 buf = (void *)((CPPInstance *)address)->fObject;
361 else
362 buf = (void *)&((CPPInstance *)address)->fObject;
363
364 if (!clName) {
365 klName = GetTClass(address)->GetName();
366 argc += 1;
367 }
368 } else {
369 Utility::GetBuffer(address, '*', 1, buf, false);
370 }
371
372 if (buf && !klName.empty()) {
373 TBranch *branch = nullptr;
374 if (argc == 4) {
375 branch = tree->Branch(PyUnicode_AsUTF8(name), klName.c_str(), buf);
376 } else if (argc == 5) {
377 branch = tree->Branch(PyUnicode_AsUTF8(name), klName.c_str(), buf, PyInt_AS_LONG(bufsize));
378 } else if (argc == 6) {
379 branch = tree->Branch(PyUnicode_AsUTF8(name), klName.c_str(), buf, PyInt_AS_LONG(bufsize),
380 PyInt_AS_LONG(splitlevel));
381 }
382
383 return BindCppObject(branch, Cppyy::GetScope("TBranch"));
384 }
385 }
386
388}
389
390////////////////////////////////////////////////////////////////////////////
391/// \brief Add pythonization for TTree::Branch.
392/// \param[in] self Always null, since this is a module function.
393/// \param[in] args Pointer to a Python tuple object containing the arguments
394/// received from Python.
395///
396/// Modify the behaviour of Branch so that proxy references can be passed
397/// as arguments from the Python side, more precisely in cases where the C++
398/// implementation of the method expects the address of a pointer.
399///
400/// For example:
401/// ~~~{.py}
402/// v = ROOT.std.vector('int')()
403/// t.Branch('my_vector_branch', v)
404/// ~~~
405///
406/// The following signatures are treated in this pythonization:
407/// - ( const char*, void*, const char*, Int_t = 32000 )
408/// - ( const char*, const char*, T**, Int_t = 32000, Int_t = 99 )
409/// - ( const char*, T**, Int_t = 32000, Int_t = 99 )
411{
412 int argc = PyTuple_GET_SIZE(args);
413
414 if (argc >= 3) { // We count the TTree proxy object too
415 auto branch = TryBranchLeafListOverload(argc, args);
416 if (branch != Py_None)
417 return branch;
418
419 branch = TryBranchPtrToPtrOverloads(argc, args);
420 if (branch != Py_None)
421 return branch;
422 }
423
424 // Not the overload we wanted to pythonize, return None
426}
#define Py_RETURN_NONE
Definition CPyCppyy.h:268
_object PyObject
std::ios_base::fmtflags fFlags
long Long_t
Definition RtypesCore.h:54
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 Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
char name[80]
Definition TGX11.cxx:110
static TBranch * SearchForBranch(TTree *tree, const char *name)
Definition TTreePyz.cxx:52
static PyObject * BindBranchToProxy(TTree *tree, const char *name, TBranch *branch)
Definition TTreePyz.cxx:78
PyObject * TryBranchLeafListOverload(int argc, PyObject *args)
Try to match the arguments of TTree::Branch to the following overload:
Definition TTreePyz.cxx:272
PyObject * TryBranchPtrToPtrOverloads(int argc, PyObject *args)
Try to match the arguments of TTree::Branch to one of the following overloads:
Definition TTreePyz.cxx:320
static TLeaf * SearchForLeaf(TTree *tree, const char *name, TBranch *branch)
Definition TTreePyz.cxx:62
PyObject * GetAttr(PyObject *self, PyObject *pyname)
Definition TTreePyz.cxx:145
static PyObject * WrapLeaf(TLeaf *leaf)
Definition TTreePyz.cxx:104
virtual PyObject * FromMemory(void *address)
A Branch for the case of an object.
static TClass * Class()
Int_t GetID() const
TStreamerInfo * GetInfo() const
Get streamer info for the branch class.
TClass * GetCurrentClass()
Return a pointer to the current type of the data member corresponding to branch element.
virtual TClass * GetTargetClass()
char * GetObject() const
Return a pointer to our object.
static TClass * Class()
A TTree is a list of TBranches.
Definition TBranch.h:93
virtual TLeaf * GetLeaf(const char *name) const
Return pointer to the 1st Leaf named name in thisBranch.
Definition TBranch.cxx:2055
virtual const char * GetClassName() const
Return the name of the user class whose content is stored in this branch, if any.
Definition TBranch.cxx:1324
virtual char * GetAddress() const
Definition TBranch.h:212
static TClass * Class()
TClass * IsA() const override
Definition TBranch.h:295
TObjArray * GetListOfLeaves()
Definition TBranch.h:247
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:81
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
virtual Int_t GetSize() const
Return the capacity of the collection, i.e.
static TClass * Class()
static TClass * Class()
A TLeaf describes individual elements of a TBranch See TBranch structure in TTree.
Definition TLeaf.h:57
virtual void * GetValuePointer() const
Definition TLeaf.h:138
virtual const char * GetTypeName() const
Definition TLeaf.h:139
virtual TLeaf * GetLeafCount() const
If this leaf stores a variable-sized array or a multi-dimensional array whose last dimension has vari...
Definition TLeaf.h:121
TClass * IsA() const override
Definition TLeaf.h:168
virtual Int_t GetNdata() const
Definition TLeaf.h:136
TBranch * GetBranch() const
Definition TLeaf.h:116
virtual Int_t GetLenStatic() const
Return the fixed length of this leaf.
Definition TLeaf.h:132
const char * GetName() const override
Returns name of object.
Definition TNamed.h:47
An array of TObjects.
Definition TObjArray.h:31
TObject * Last() const override
Return the object in the last filled slot. Returns 0 if no entries.
TObject * At(Int_t idx) const override
Definition TObjArray.h:164
TObject * First() const override
Return the object in the first slot.
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:525
TObjArray * GetElements() const override
A TTree represents a columnar dataset.
Definition TTree.h:79
static TClass * Class()
Py_ssize_t GetBuffer(PyObject *pyobject, char tc, int size, void *&buf, bool check=true)
Definition Utility.cxx:808
bool AddToClass(PyObject *pyclass, const char *label, PyCFunction cfunc, int flags=METH_VARARGS)
Definition Utility.cxx:182
CPYCPPYY_EXTERN bool Instance_Check(PyObject *pyobject)
Definition API.cxx:163
CPYCPPYY_EXTERN PyObject * Instance_FromVoidPtr(void *addr, const std::string &classname, bool python_owns=false)
Definition API.cxx:118
bool CPPInstance_Check(T *object)
CPYCPPYY_EXTERN Converter * CreateConverter(const std::string &name, cdims_t=0)
PyObject * BindCppObject(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
CPYCPPYY_EXTERN void * Instance_AsVoidPtr(PyObject *pyobject)
Definition API.cxx:103
CPYCPPYY_EXTERN void DestroyConverter(Converter *p)
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
PyObject * SetBranchAddressPyz(PyObject *self, PyObject *args)
Add pythonization for TTree::SetBranchAddress.
Definition TTreePyz.cxx:219
PyObject * BranchPyz(PyObject *self, PyObject *args)
Add pythonization for TTree::Branch.
Definition TTreePyz.cxx:410
PyObject * AddBranchAttrSyntax(PyObject *self, PyObject *args)
Allow branches to be accessed as attributes of a tree.
Definition TTreePyz.cxx:197