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