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
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
42{
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())) {
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 if (branch->GetAddress())
90 return {*(void **)branch->GetAddress(), branch->GetClassName()};
91
92 // try leaf, otherwise indicate failure by returning a typed null-object
93 TObjArray *leaves = branch->GetListOfLeaves();
94 if (!tree->GetLeaf(name) && !(leaves->GetSize() && (leaves->First() == leaves->Last())))
95 return {nullptr, branch->GetClassName()};
96 }
97
98 return {nullptr, ""};
99}
100
101/**
102 * @brief Extracts static dimensions from the title of a TLeaf object.
103 *
104 * The function assumes that the title of the TLeaf object contains dimensions
105 * in the format `[dim1][dim2]...`.
106 *
107 * @note In the current implementation of TLeaf, there is no way to extract the
108 * dimensions without string parsing.
109 *
110 * @param leaf Pointer to the TLeaf object from which to extract dimensions.
111 * @return std::vector<dim_t> A vector containing the extracted dimensions.
112 */
113static std::vector<dim_t> getMultiDims(std::string const &title)
114{
115 std::vector<dim_t> dims;
116 std::stringstream ss{title};
117
118 while (ss.good()) {
119 std::string substr;
120 getline(ss, substr, '[');
121 getline(ss, substr, ']');
122 if (!substr.empty()) {
123 dims.push_back(std::stoi(substr));
124 }
125 }
126
127 return dims;
128}
129
131{
132 if (1 < leaf->GetLenStatic() || leaf->GetLeafCount()) {
133 bool isStatic = 1 < leaf->GetLenStatic();
134 // array types
135 std::string typeName = leaf->GetTypeName();
136 std::vector<dim_t> dimsVec{leaf->GetNdata()};
137 std::string title = leaf->GetTitle();
138 // Multidimensional array case
139 if (std::count(title.begin(), title.end(), '[') >= 2) {
140 dimsVec = getMultiDims(title);
141 }
142 CPyCppyy::Dimensions dims{static_cast<dim_t>(dimsVec.size()), dimsVec.data()};
143 Converter *pcnv = CreateConverter(typeName + (isStatic ? "[]" : "*"), dims);
144
145 void *address = 0;
146 if (leaf->GetBranch())
147 address = (void *)leaf->GetBranch()->GetAddress();
148 if (!address)
149 address = (void *)leaf->GetValuePointer();
150
151 PyObject *value = pcnv->FromMemory(&address);
153
154 return value;
155 } else if (leaf->GetValuePointer()) {
156 // value types
157 Converter *pcnv = CreateConverter(leaf->GetTypeName());
158 PyObject *value = 0;
159 if (leaf->IsA() == TLeafElement::Class() || leaf->IsA() == TLeafObject::Class())
160 value = pcnv->FromMemory((void *)*(void **)leaf->GetValuePointer());
161 else
162 value = pcnv->FromMemory((void *)leaf->GetValuePointer());
164
165 return value;
166 }
167
168 return nullptr;
169}
170
171// Allow access to branches/leaves as if they were data members Returns a
172// Python tuple where the first element is either the desired CPyCppyy proxy,
173// or an address that still needs to be wrapped by the caller in a proxy using
174// cppyy.ll.cast. In the latter case, the second tuple element is the target
175// type name. Otherwise, the second element is an empty string.
177{
178 PyObject *self = nullptr;
179 PyObject *pyname = nullptr;
180
181 PyArg_ParseTuple(args, "OU:GetBranchAttr", &self, &pyname);
182
185 return 0;
186
187 // get hold of actual tree
188 auto tree = (TTree *)GetTClass(self)->DynamicCast(TTree::Class(), CPyCppyy::Instance_AsVoidPtr(self));
189
190 if (!tree) {
191 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
192 return 0;
193 }
194
195 // deal with possible aliasing
196 const char *name = tree->GetAlias(name_possibly_alias);
197 if (!name)
199
200 // search for branch first (typical for objects)
202
203 if (branch) {
204 // found a branched object, wrap its address for the object it represents
206 if (!finalTypeName.empty()) {
210 return outTuple;
211 }
212 }
213
214 // if not, try leaf
215 if (TLeaf *leaf = SearchForLeaf(tree, name, branch)) {
216 // found a leaf, extract value and wrap with a Python object according to its type
217 auto wrapper = WrapLeaf(leaf);
218 if (wrapper != nullptr) {
222 return outTuple;
223 }
224 }
225
226 // confused
227 PyErr_Format(PyExc_AttributeError, "\'%s\' object has no attribute \'%s\'", tree->IsA()->GetName(), name);
228 return 0;
229}
230
231////////////////////////////////////////////////////////////////////////////
232/// Try to match the arguments of TTree::Branch to the following overload:
233/// - ( const char*, void*, const char*, Int_t = 32000 )
234/// If the match succeeds, invoke Branch on the C++ tree with the right
235/// arguments.
237{
238 PyObject *treeObj = nullptr;
239 PyObject *name = nullptr, *address = nullptr, *leaflist = nullptr, *bufsize = nullptr;
240
241 if (PyArg_ParseTuple(args, "OO!OO!|O!:Branch", &treeObj, &PyUnicode_Type, &name, &address, &PyUnicode_Type,
243
245 if (!tree) {
246 PyErr_SetString(PyExc_TypeError, "TTree::Branch must be called with a TTree instance as first argument");
247 return nullptr;
248 }
249
250 void *buf = nullptr;
251 if (CPPInstance_Check(address))
252 buf = CPyCppyy::Instance_AsVoidPtr(address);
253 else
254 Utility::GetBuffer(address, '*', 1, buf, false);
255
256 if (buf) {
257 TBranch *branch = nullptr;
258 if (argc == 5) {
260 } else {
261 branch = tree->Branch(PyUnicode_AsUTF8(name), buf, PyUnicode_AsUTF8(leaflist));
262 }
263
264 return BindCppObject(branch, Cppyy::GetScope("TBranch"));
265 }
266 }
267 PyErr_Clear();
268
270}
271
272////////////////////////////////////////////////////////////////////////////
273/// Try to match the arguments of TTree::Branch to one of the following
274/// overloads:
275/// - ( const char*, const char*, T**, Int_t = 32000, Int_t = 99 )
276/// - ( const char*, T**, Int_t = 32000, Int_t = 99 )
277/// If the match succeeds, invoke Branch on the C++ tree with the right
278/// arguments.
280{
281 PyObject *treeObj = nullptr;
282 PyObject *name = nullptr, *clName = nullptr, *address = nullptr, *bufsize = nullptr, *splitlevel = nullptr;
283
284 auto bIsMatch = false;
285 if (PyArg_ParseTuple(args, "OO!O!O|O!O!:Branch", &treeObj, &PyUnicode_Type, &name, &PyUnicode_Type, &clName,
286 &address, &PyInt_Type, &bufsize, &PyInt_Type, &splitlevel)) {
287 bIsMatch = true;
288 } else {
289 PyErr_Clear();
290 if (PyArg_ParseTuple(args, "OO!O|O!O!", &treeObj, &PyUnicode_Type, &name, &address, &PyInt_Type, &bufsize,
292 bIsMatch = true;
293 } else {
294 PyErr_Clear();
295 }
296 }
297
298 if (bIsMatch) {
300 if (!tree) {
301 PyErr_SetString(PyExc_TypeError, "TTree::Branch must be called with a TTree instance as first argument");
302 return nullptr;
303 }
304
305 std::string klName = clName ? PyUnicode_AsUTF8(clName) : "";
306 void *buf = nullptr;
307
308 if (CPPInstance_Check(address)) {
309 if (((CPPInstance *)address)->fFlags & CPPInstance::kIsReference)
310 buf = (void *)((CPPInstance *)address)->fObject;
311 else
312 buf = (void *)&((CPPInstance *)address)->fObject;
313
314 if (!clName) {
315 klName = GetTClass(address)->GetName();
316 argc += 1;
317 }
318 } else {
319 Utility::GetBuffer(address, '*', 1, buf, false);
320 }
321
322 if (buf && !klName.empty()) {
323 TBranch *branch = nullptr;
324 if (argc == 4) {
325 branch = tree->Branch(PyUnicode_AsUTF8(name), klName.c_str(), buf);
326 } else if (argc == 5) {
327 branch = tree->Branch(PyUnicode_AsUTF8(name), klName.c_str(), buf, PyInt_AS_LONG(bufsize));
328 } else if (argc == 6) {
329 branch = tree->Branch(PyUnicode_AsUTF8(name), klName.c_str(), buf, PyInt_AS_LONG(bufsize),
331 }
332
333 return BindCppObject(branch, Cppyy::GetScope("TBranch"));
334 }
335 }
336
338}
339
340////////////////////////////////////////////////////////////////////////////
341/// \brief Add pythonization for TTree::Branch.
342/// \param[in] self Always null, since this is a module function.
343/// \param[in] args Pointer to a Python tuple object containing the arguments
344/// received from Python.
345///
346/// Modify the behaviour of Branch so that proxy references can be passed
347/// as arguments from the Python side, more precisely in cases where the C++
348/// implementation of the method expects the address of a pointer.
349///
350/// For example:
351/// ~~~{.py}
352/// v = ROOT.std.vector('int')()
353/// t.Branch('my_vector_branch', v)
354/// ~~~
355///
356/// The following signatures are treated in this pythonization:
357/// - ( const char*, void*, const char*, Int_t = 32000 )
358/// - ( const char*, const char*, T**, Int_t = 32000, Int_t = 99 )
359/// - ( const char*, T**, Int_t = 32000, Int_t = 99 )
361{
362 int argc = PyTuple_GET_SIZE(args);
363
364 if (argc >= 3) { // We count the TTree proxy object too
366 if (branch != Py_None)
367 return branch;
368
370 if (branch != Py_None)
371 return branch;
372 }
373
374 // Not the overload we wanted to pythonize, return None
376}
#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
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: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:236
PyObject * TryBranchPtrToPtrOverloads(int argc, PyObject *args)
Try to match the arguments of TTree::Branch to one of the following overloads:
Definition TTreePyz.cxx:279
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:113
static PyObject * WrapLeaf(TLeaf *leaf)
Definition TTreePyz.cxx:130
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:3074
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:84
static TClass * Class()
Py_ssize_t GetBuffer(PyObject *pyobject, char tc, int size, void *&buf, bool check=true)
Definition Utility.cxx:813
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:106
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:360
PyObject * GetBranchAttr(PyObject *self, PyObject *args)
Definition TTreePyz.cxx:176