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