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
17
18#include "CPyCppyy/API.h"
19
20#include "PyROOTPythonize.h"
21
22// ROOT
23#include "TClass.h"
24#include "TTree.h"
25#include "TBranch.h"
26#include "TBranchElement.h"
27#include "TBranchObject.h"
28#include "TLeaf.h"
29#include "TLeafElement.h"
30#include "TLeafObject.h"
31#include "TStreamerElement.h"
32#include "TStreamerInfo.h"
33
34#include <algorithm>
35#include <sstream>
36
37namespace {
38
39// Get the TClass of the C++ object proxied by pyobj
41{
43}
44
45} // namespace
46
47using namespace CPyCppyy;
48
49namespace PyROOT{
50void GetBuffer(PyObject *pyobject, void *&buf);
51}
52
53static TBranch *SearchForBranch(TTree *tree, const char *name)
54{
55 TBranch *branch = tree->GetBranch(name);
56 if (!branch) {
57 // for benefit of naming of sub-branches, the actual name may have a trailing '.'
58 branch = tree->GetBranch((std::string(name) + '.').c_str());
59 }
60 return branch;
61}
62
63static TLeaf *SearchForLeaf(TTree *tree, const char *name, TBranch *branch)
64{
65 TLeaf *leaf = tree->GetLeaf(name);
66 if (branch && !leaf) {
67 leaf = branch->GetLeaf(name);
68 if (!leaf) {
69 TObjArray *leaves = branch->GetListOfLeaves();
70 if (leaves->GetSize() && (leaves->First() == leaves->Last())) {
71 // i.e., if unambiguously only this one
72 leaf = (TLeaf *)leaves->At(0);
73 }
74 }
75 }
76 return leaf;
77}
78
79static std::pair<void *, std::string> ResolveBranch(TTree *tree, const char *name, TBranch *branch)
80{
81 // for partial return of a split object
82 if (branch->InheritsFrom(TBranchElement::Class())) {
84 if (be->GetCurrentClass() && (be->GetCurrentClass() != be->GetTargetClass()) && (0 <= be->GetID())) {
85 Long_t offset = ((TStreamerElement *)be->GetInfo()->GetElements()->At(be->GetID()))->GetOffset();
86 return {be->GetObject() + offset, be->GetCurrentClass()->GetName()};
87 }
88 }
89
90 // for return of a full object
91 if (branch->IsA() == TBranchElement::Class() || branch->IsA() == TBranchObject::Class()) {
92 if (branch->GetAddress())
93 return {*(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 (!tree->GetLeaf(name) && !(leaves->GetSize() && (leaves->First() == leaves->Last())))
98 return {nullptr, branch->GetClassName()};
99 }
100
101 return {nullptr, ""};
102}
103
104/**
105 * @brief Extracts static dimensions from the title of a TLeaf object.
106 *
107 * The function assumes that the title of the TLeaf object contains dimensions
108 * in the format `[dim1][dim2]...`.
109 *
110 * @note In the current implementation of TLeaf, there is no way to extract the
111 * dimensions without string parsing.
112 *
113 * @param title title of the TLeaf object from which to extract dimensions.
114 * @return std::vector<dim_t> A vector containing the extracted dimensions.
115 */
116static std::vector<dim_t> getMultiDims(std::string const &title)
117{
118 std::vector<dim_t> dims;
119 std::stringstream ss{title};
120
121 while (ss.good()) {
122 std::string substr;
123 getline(ss, substr, '[');
124 getline(ss, substr, ']');
125 if (!substr.empty()) {
126 dims.push_back(std::stoi(substr));
127 }
128 }
129
130 return dims;
131}
132
134{
135 if (1 < leaf->GetLenStatic() || leaf->GetLeafCount()) {
136 bool isStatic = 1 < leaf->GetLenStatic();
137 // array types
138 std::string typeName = leaf->GetTypeName();
139 std::vector<dim_t> dimsVec{leaf->GetNdata()};
140 std::string title = leaf->GetTitle();
141 // Multidimensional array case
142 if (std::count(title.begin(), title.end(), '[') >= 2) {
143 dimsVec = getMultiDims(title);
144 }
145 CPyCppyy::Dimensions dims{static_cast<dim_t>(dimsVec.size()), dimsVec.data()};
146 Converter *pcnv = CreateConverter(typeName + (isStatic ? "[]" : "*"), dims);
147
148 void *address = 0;
149 if (leaf->GetBranch())
150 address = (void *)leaf->GetBranch()->GetAddress();
151 if (!address)
152 address = (void *)leaf->GetValuePointer();
153
154 PyObject *value = pcnv->FromMemory(&address);
156
157 return value;
158 } else if (leaf->GetValuePointer()) {
159 // value types
160 Converter *pcnv = CreateConverter(leaf->GetTypeName());
161 PyObject *value = 0;
162 if (leaf->IsA() == TLeafElement::Class() || leaf->IsA() == TLeafObject::Class())
163 value = pcnv->FromMemory((void *)*(void **)leaf->GetValuePointer());
164 else
165 value = pcnv->FromMemory((void *)leaf->GetValuePointer());
167
168 return value;
169 }
170
171 return nullptr;
172}
173
174// Allow access to branches/leaves as if they were data members Returns a
175// Python tuple where the first element is either the desired CPyCppyy proxy,
176// or an address that still needs to be wrapped by the caller in a proxy using
177// cppyy.ll.cast. In the latter case, the second tuple element is the target
178// type name. Otherwise, the second element is an empty string.
180{
181 PyObject *self = nullptr;
182 PyObject *pyname = nullptr;
183
184 PyArg_ParseTuple(args, "OU:GetBranchAttr", &self, &pyname);
185
186 const char *name_possibly_alias = PyUnicode_AsUTF8AndSize(pyname, nullptr);
188 return nullptr;
189
190 // get hold of actual tree
191 auto tree = (TTree *)GetTClass(self)->DynamicCast(TTree::Class(), CPyCppyy::Instance_AsVoidPtr(self));
192
193 if (!tree) {
194 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
195 return 0;
196 }
197
198 // deal with possible aliasing
199 const char *name = tree->GetAlias(name_possibly_alias);
200 if (!name)
202
203 // search for branch first (typical for objects)
205
206 if (branch) {
207 // found a branched object, wrap its address for the object it represents
209 if (!finalTypeName.empty()) {
213 return outTuple;
214 }
215 }
216
217 // if not, try leaf
218 if (TLeaf *leaf = SearchForLeaf(tree, name, branch)) {
219 // found a leaf, extract value and wrap with a Python object according to its type
220 auto wrapper = WrapLeaf(leaf);
221 if (wrapper != nullptr) {
225 return outTuple;
226 }
227 }
228
229 // confused
230 PyErr_Format(PyExc_AttributeError, "\'%s\' object has no attribute \'%s\'", tree->IsA()->GetName(), name);
231 return 0;
232}
233
234////////////////////////////////////////////////////////////////////////////
235/// Try to match the arguments of TTree::Branch to the following overload:
236/// - ( const char*, void*, const char*, Int_t = 32000 )
237/// If the match succeeds, invoke Branch on the C++ tree with the right
238/// arguments.
240{
241 PyObject *treeObj = nullptr;
242 PyObject *name = nullptr, *address = nullptr, *leaflist = nullptr, *bufsize = nullptr;
243
244 if (PyArg_ParseTuple(args, "OO!OO!|O!:Branch", &treeObj, &PyUnicode_Type, &name, &address, &PyUnicode_Type,
246
248 if (!tree) {
249 PyErr_SetString(PyExc_TypeError, "TTree::Branch must be called with a TTree instance as first argument");
250 return nullptr;
251 }
252
253 void *buf = nullptr;
254 if (CPyCppyy::Instance_Check(address))
255 buf = CPyCppyy::Instance_AsVoidPtr(address);
256 else
257 PyROOT::GetBuffer(address, buf);
258
259 if (buf) {
260 TBranch *branch = nullptr;
261 const char *nameString = PyUnicode_AsUTF8AndSize(name, nullptr);
262 if (!nameString) {
263 return nullptr;
264 }
265 const char *leaflistString = PyUnicode_AsUTF8AndSize(leaflist, nullptr);
266 if (!leaflistString) {
267 return nullptr;
268 }
269 if (argc == 5) {
270 branch = tree->Branch(nameString, buf, leaflistString, PyInt_AsLong(bufsize));
271 } else {
272 branch = tree->Branch(nameString, buf, leaflistString);
273 }
274
275 return BindCppObject(branch, Cppyy::GetScope("TBranch"));
276 }
277 }
278 PyErr_Clear();
279
281}
282
283////////////////////////////////////////////////////////////////////////////
284/// Try to match the arguments of TTree::Branch to one of the following
285/// overloads:
286/// - ( const char*, const char*, T**, Int_t = 32000, Int_t = 99 )
287/// - ( const char*, T**, Int_t = 32000, Int_t = 99 )
288/// If the match succeeds, invoke Branch on the C++ tree with the right
289/// arguments.
291{
292 PyObject *treeObj = nullptr;
293 PyObject *name = nullptr, *clName = nullptr, *address = nullptr, *bufsize = nullptr, *splitlevel = nullptr;
294
295 auto bIsMatch = false;
296 if (PyArg_ParseTuple(args, "OO!O!O|O!O!:Branch", &treeObj, &PyUnicode_Type, &name, &PyUnicode_Type, &clName,
297 &address, &PyInt_Type, &bufsize, &PyInt_Type, &splitlevel)) {
298 bIsMatch = true;
299 } else {
300 PyErr_Clear();
301 if (PyArg_ParseTuple(args, "OO!O|O!O!", &treeObj, &PyUnicode_Type, &name, &address, &PyInt_Type, &bufsize,
303 bIsMatch = true;
304 } else {
305 PyErr_Clear();
306 }
307 }
308
309 if (bIsMatch) {
311 if (!tree) {
312 PyErr_SetString(PyExc_TypeError, "TTree::Branch must be called with a TTree instance as first argument");
313 return nullptr;
314 }
315
316 std::string klName;
317 if (clName) {
318 const char *clNameString = PyUnicode_AsUTF8AndSize(clName, nullptr);
319 if (!clNameString) {
320 return nullptr;
321 }
323 }
324 void *buf = nullptr;
325
326 if (CPyCppyy::Instance_Check(address)) {
327 if (((CPPInstance *)address)->fFlags & CPPInstance::kIsReference)
328 buf = (void *)((CPPInstance *)address)->fObject;
329 else
330 buf = (void *)&((CPPInstance *)address)->fObject;
331
332 if (!clName) {
333 klName = GetTClass(address)->GetName();
334 argc += 1;
335 }
336 } else {
337 PyROOT::GetBuffer(address, buf);
338 }
339
340 if (buf && !klName.empty()) {
341 TBranch *branch = nullptr;
342 const char *nameString = nullptr;
343 if (argc == 4 || argc == 5 || argc == 6) {
345 if (!nameString) {
346 return nullptr;
347 }
348 }
349 if (argc == 4) {
350 branch = tree->Branch(nameString, klName.c_str(), buf);
351 } else if (argc == 5) {
352 branch = tree->Branch(nameString, klName.c_str(), buf, PyInt_AsLong(bufsize));
353 } else if (argc == 6) {
354 branch = tree->Branch(nameString, klName.c_str(), buf, PyInt_AsLong(bufsize),
356 }
357
358 return BindCppObject(branch, Cppyy::GetScope("TBranch"));
359 }
360 }
361
363}
364
365////////////////////////////////////////////////////////////////////////////
366/// \brief Add pythonization for TTree::Branch.
367/// \param[in] self Always null, since this is a module function.
368/// \param[in] args Pointer to a Python tuple object containing the arguments
369/// received from Python.
370///
371/// Modify the behaviour of Branch so that proxy references can be passed
372/// as arguments from the Python side, more precisely in cases where the C++
373/// implementation of the method expects the address of a pointer.
374///
375/// For example:
376/// ~~~{.py}
377/// v = ROOT.std.vector('int')()
378/// t.Branch('my_vector_branch', v)
379/// ~~~
380///
381/// The following signatures are treated in this pythonization:
382/// - ( const char*, void*, const char*, Int_t = 32000 )
383/// - ( const char*, const char*, T**, Int_t = 32000, Int_t = 99 )
384/// - ( const char*, T**, Int_t = 32000, Int_t = 99 )
386{
387 int argc = PyTuple_Size(args);
388
389 if (argc >= 3) { // We count the TTree proxy object too
391 if (branch != Py_None)
392 return branch;
393
395 if (branch != Py_None)
396 return branch;
397 }
398
399 // Not the overload we wanted to pythonize, return None
401}
#define Py_RETURN_NONE
Definition CPyCppyy.h:268
#define CPyCppyy_PyText_FromString
Definition CPyCppyy.h:81
uint32_t fFlags
_object PyObject
long Long_t
Signed long integer 4 bytes (long). Size depends on architecture.
Definition RtypesCore.h:68
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
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:157
static TBranch * SearchForBranch(TTree *tree, const char *name)
Definition TTreePyz.cxx:53
PyObject * TryBranchLeafListOverload(int argc, PyObject *args)
Try to match the arguments of TTree::Branch to the following overload:
Definition TTreePyz.cxx:239
PyObject * TryBranchPtrToPtrOverloads(int argc, PyObject *args)
Try to match the arguments of TTree::Branch to one of the following overloads:
Definition TTreePyz.cxx:290
static std::pair< void *, std::string > ResolveBranch(TTree *tree, const char *name, TBranch *branch)
Definition TTreePyz.cxx:79
static TLeaf * SearchForLeaf(TTree *tree, const char *name, TBranch *branch)
Definition TTreePyz.cxx:63
static std::vector< dim_t > getMultiDims(std::string const &title)
Extracts static dimensions from the title of a TLeaf object.
Definition TTreePyz.cxx:116
static PyObject * WrapLeaf(TLeaf *leaf)
Definition TTreePyz.cxx:133
A Branch for the case of an object.
static TClass * Class()
static TClass * Class()
A TTree is a list of TBranches.
Definition TBranch.h:93
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
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:2979
static TClass * Class()
static TClass * Class()
A TLeaf describes individual elements of a TBranch See TBranch structure in TTree.
Definition TLeaf.h:57
An array of TObjects.
Definition TObjArray.h:31
Describe one element (data member) to be Streamed.
A TTree represents a columnar dataset.
Definition TTree.h:89
virtual TBranch * GetBranch(const char *name)
Return pointer to the branch with the given name in this tree or its friends.
Definition TTree.cxx:5430
virtual TLeaf * GetLeaf(const char *branchname, const char *leafname)
Return pointer to the 1st Leaf named name in any Branch of this Tree or any branch in the list of fri...
Definition TTree.cxx:6306
static TClass * Class()
CPYCPPYY_EXTERN bool Instance_Check(PyObject *pyobject)
Definition API.cxx:178
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 std::string Instance_GetScopedFinalName(PyObject *pyobject)
Definition API.cxx:106
CPYCPPYY_EXTERN void * Instance_AsVoidPtr(PyObject *pyobject)
Definition API.cxx:118
CPYCPPYY_EXTERN void DestroyConverter(Converter *p)
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
PyObject * BranchPyz(PyObject *self, PyObject *args)
Add pythonization for TTree::Branch.
Definition TTreePyz.cxx:385
void GetBuffer(PyObject *pyobject, void *&buf)
PyObject * GetBranchAttr(PyObject *self, PyObject *args)
Definition TTreePyz.cxx:179