ATLAS Offline Software
PyROOTTTreePatch.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
3 */
11 // Called from python, so only excuted single-threaded (GIL).
14 
17 #include "Utility.h"
18 
19 #include "Python.h"
20 #include "TTree.h"
21 #include "TBranch.h"
22 #include "TPython.h"
23 #include "TNotifyLink.h"
24 
25 
26 //***************************************************************************
27 // Most of the PyROOT headers are not exported.
28 // So we need to copy pieces of some PyROOT declarations here.
29 //
30 
31 namespace PyROOT {
32 
33 class CPPInstance {
34 public:
35  enum EFlags { kIsReference = 0x0008 };
36 
37  PyObject_HEAD
38  void* fObject;
39  int fFlags;
40 };
41 
42 } // namespace PyROOT
43 
44 using namespace PyROOT;
45 
46 
47 //=========================================================================
48 
49 
50 
51 namespace RootUtils {
52 
53 
54 // Interned string constants that we use for dictionary lookups.
55 static PyObject* notifier_str = 0;
56 static PyObject* pynotify_str = 0;
57 
58 
68  : public TNotifyLinkBase
69 {
70 public:
78  TreeNotifier (TTree* tree, PyObject* treeobj_ref);
79 
81  virtual ~TreeNotifier();
82 
84  virtual Bool_t Notify() override;
85 
87  virtual void Clear (Option_t* /*option*/ = "") override;
88 
89 
90 private:
94 
96  TObject* m_chain;
97 
99  TTree* m_tree;
100 };
101 
102 
110 TreeNotifier::TreeNotifier (TTree* tree,
111  PyObject* treeobj_ref)
112  : m_treeobj_ref (treeobj_ref),
113  m_chain (tree->GetNotify()),
114  m_tree (tree)
115 {
116  // We acquire ownership of treeobj_ref.
117 }
118 
119 
124 {
126  if (m_tree) {
127  // Clear the reference from the tree to this object.
128  m_tree->SetNotify (m_chain);
129  }
130  Py_XDECREF (m_treeobj_ref);
131 }
132 
133 
138 {
140 
141  // Intern __pynotify__ if needed.
142  if (pynotify_str == 0)
143  pynotify_str = PyUnicode_InternFromString("__pynotify__");
144 
145  // Look for a notification object.
146  PyObject* treeobj = PyWeakref_GetObject (m_treeobj_ref);
147  if (treeobj) {
148  PyObject** dictptr = _PyObject_GetDictPtr (treeobj);
149  if (dictptr && *dictptr) {
150  PyObject* notobj = PyObject_GetItem (*dictptr, pynotify_str);
151  if (notobj) {
152  // Got it --- call @c Notify.
153  PyObject* ret =
154  PyObject_CallMethod (notobj, const_cast<char*> ("Notify"), NULL);
155  if (!ret) return 0;
156  Py_DECREF (ret);
157  }
158  else
159  PyErr_Clear();
160  }
161  }
162 
163  if (m_chain) m_chain->Notify();
164  return true;
165 }
166 
167 
169 void TreeNotifier::Clear (Option_t* /*option = ""*/)
170 {
171  m_tree = nullptr;
172 }
173 
174 
180 {
181  // Decode the objects --- the tree and the notifier object.
182  PyObject* self = 0;
183  PyObject* obj = 0;
184  if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:SetNotify" ),
185  &self, &obj ) )
186  return 0;
187 
188  // Intern strings if needed.
189  if (pynotify_str == 0) {
190  pynotify_str = PyUnicode_InternFromString("__pynotify__");
191  }
192  if (notifier_str == 0) {
193  notifier_str = PyUnicode_InternFromString("__notifier__");
194  }
195 
196  // Set up notifier.
197  if (!PyObject_HasAttr (self, notifier_str)) {
198  // get hold of actual tree
199  void* vp = TPython::CPPInstance_AsVoidPtr (self);
200  TTree* tree =
201  (TTree*)objectIsA(self)->DynamicCast ( TTree::Class(), vp );
202 
203  PyObject* treeobj_ref = PyWeakref_NewRef (self, 0);
204  if (!treeobj_ref)
205  return 0;
206  TObject* notifier = new TreeNotifier (tree, treeobj_ref);
207  PyObject* notobj = TPython::CPPInstance_FromVoidPtr (notifier, "TObject");
208  setOwnership (notobj, true);
209  int stat = PyObject_SetAttr (self, notifier_str, notobj);
210  Py_XDECREF (notobj);
211  if (stat < 0)
212  return 0;
213  tree->SetNotify (notifier);
214  }
215 
216  // Install the object.
217  int stat = PyObject_SetAttr (self, pynotify_str, obj);
218  if (stat < 0)
219  return 0;
220 
221  Py_INCREF (Py_None);
222  return Py_None;
223 }
224 
225 
231 {
232  // Decode the objects --- the tree.
233  PyObject* self = 0;
234  if ( ! PyArg_ParseTuple( args, const_cast< char* >( "O:SetNotify" ),
235  &self ) )
236  return 0;
237 
238  // Intern string if needed.
239  if (pynotify_str == 0)
240  pynotify_str = PyUnicode_InternFromString("__pynotify__");
241 
242  // Retrieve the notifier.
243  PyObject* ret = PyObject_GetAttr (self, pynotify_str);
244  if (!ret) {
245  PyErr_Clear();
246  Py_INCREF (Py_None);
247  ret = Py_None;
248  }
249  return ret;
250 }
251 
252 
258 {
259  // Decode arguments --- the branch and the buffer.
260  PyObject* self = 0;
261  PyObject* address = 0;
262  if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:SetBranchAddress" ),
263  &self, &address ) )
264  return 0;
265 
266  // The branch as a Root object.
267  TBranch* branch = 0;
268  if (TPython::CPPInstance_Check (self)) {
269  branch = (TBranch*)objectIsA(self)->DynamicCast
270  ( TBranch::Class(),
271  TPython::CPPInstance_AsVoidPtr(self) );
272  }
273 
274  if ( ! branch ) {
275  PyErr_SetString( PyExc_TypeError,
276  "TBranch::SetAddress must be called with a "
277  "TBranch instance as first argument" );
278  return 0;
279  }
280 
281  // Convert the buffer argument to an address.
282  void* buf = 0;
283  if ( TPython::CPPInstance_Check( address ) ) {
284  if ( ((CPPInstance*)address)->fFlags & CPPInstance::kIsReference )
285  buf = (void*)((CPPInstance*)address)->fObject;
286  else
287  buf = (void*)&((CPPInstance*)address)->fObject;
288  } else
289  RootUtils::GetBuffer( address, '*', 1, buf, kFALSE );
290 
291  // Make the call and return.
292  if ( buf != 0 )
293  branch->SetAddress( buf );
294 
295  Py_INCREF( Py_None );
296  return Py_None;
297 }
298 
299 
307 void installMethod (PyObject* pyclass,
308  PyMethodDef& pdef,
309  const char* name,
310  PyCFunction cfunc)
311 {
312  pdef.ml_name = const_cast<char*> (name);
313  pdef.ml_meth = cfunc;
314  pdef.ml_flags = METH_VARARGS;
315  pdef.ml_doc = 0;
316 
317  PyObject* func = PyCFunction_New (&pdef, 0);
318  PyObject* method = PyInstanceMethod_New (func);
319  Bool_t isOK = PyObject_SetAttrString (pyclass, pdef.ml_name, method) == 0;
320 
321  if (PyErr_Occurred())
322  fprintf (stderr, "Py error");
323  else if (!isOK) {
324  fprintf (stderr, "Could not add method %s\n", name);
325  }
326 }
327 
328 
336  PyObject* /*chain_pyclass*/,
337  PyObject* branch_pyclass)
338 {
339 # define INSTALL_METHOD(pyclass, name, func) do { \
340  static PyMethodDef pdef; \
341  installMethod (pyclass, pdef, name, (PyCFunction)func); \
342  } while(0)
343 
344  INSTALL_METHOD (tree_pyclass, "SetNotify", treeSetNotify);
345  INSTALL_METHOD (tree_pyclass, "GetNotify", treeGetNotify);
346  INSTALL_METHOD (branch_pyclass, "SetAddress", branchSetAddress);
347 #undef INSTALL_METHOD
348 }
349 
350 
351 } // namespace RootUtils
RunTileTBRec.method
method
Definition: RunTileTBRec.py:73
RootUtils
Definition: ILogger.h:20
PyROOT::CPPInstance::fObject
PyObject_HEAD void * fObject
Definition: PyROOTTTreePatch.cxx:38
Utility.h
Utility code originally from pyroot.
tree
TChain * tree
Definition: tile_monitor.h:30
get_generator_info.stderr
stderr
Definition: get_generator_info.py:40
RootUtils::TreeNotifier::Clear
virtual void Clear(Option_t *="") override
Called when the tree we're attached to is deleted.
Definition: PyROOTTTreePatch.cxx:169
RootUtils::TreeNotifier::Notify
virtual Bool_t Notify() override
Root notification hook.
Definition: PyROOTTTreePatch.cxx:137
RootUtils::TreeNotifier::m_chain
TObject * m_chain
Notification tree.
Definition: PyROOTTTreePatch.cxx:96
PyROOT::CPPInstance::fFlags
int fFlags
Definition: PyROOTTTreePatch.cxx:39
RootUtils::treeGetNotify
PyObject * treeGetNotify(PyObject *, PyObject *args)
Implementation for pythonization of TTree::SetNotify.
Definition: PyROOTTTreePatch.cxx:230
RootUtils::PyGILStateEnsure
Definition: PyAthenaGILStateEnsure.h:20
RootUtils::objectIsA
TClass * objectIsA(PyObject *obj)
Definition: Utility.cxx:103
RootUtils::branchSetAddress
PyObject * branchSetAddress(PyObject *, PyObject *args)
Pythonization of TBranch::SetAddress.
Definition: PyROOTTTreePatch.cxx:257
RootUtils::treeSetNotify
PyObject * treeSetNotify(PyObject *, PyObject *args)
Implementation for pythonization of TTree::SetNotify.
Definition: PyROOTTTreePatch.cxx:179
ATLAS_NO_CHECK_FILE_THREAD_SAFETY
ATLAS_NO_CHECK_FILE_THREAD_SAFETY
Definition: PyROOTTTreePatch.cxx:13
beamspotman.stat
stat
Definition: beamspotman.py:266
RootUtils::installMethod
void installMethod(PyObject *pyclass, PyMethodDef &pdef, const char *name, PyCFunction cfunc)
Helper to install a method in a Python class.
Definition: PyROOTTTreePatch.cxx:307
RootUtils::PyROOTTTreePatch::Initialize
static void Initialize(PyObject *tree_pyclass, PyObject *chain_pyclass, PyObject *branch_pyclass)
Install the PyROOT patches.
Definition: PyROOTTTreePatch.cxx:335
PyROOT
Definition: PyROOTTTreePatch.cxx:31
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:228
PyROOTTTreePatch.h
Additions to root's pythonized TTree.
RTTAlgmain.address
address
Definition: RTTAlgmain.py:55
RootUtils::TreeNotifier::~TreeNotifier
virtual ~TreeNotifier()
Destructor.
Definition: PyROOTTTreePatch.cxx:123
RootUtils::TreeNotifier::m_treeobj_ref
PyObject * m_treeobj_ref
A weak reference to the Python object for the tree.
Definition: PyROOTTTreePatch.cxx:93
RootUtils::TreeNotifier
Tree notification handler.
Definition: PyROOTTTreePatch.cxx:69
RTTAlgmain.branch
branch
Definition: RTTAlgmain.py:61
PyROOT::CPPInstance::EFlags
EFlags
Definition: PyROOTTTreePatch.cxx:35
PyROOT::CPPInstance
Definition: PyROOTTTreePatch.cxx:33
RootUtils::setOwnership
bool setOwnership(PyObject *obj, bool flag)
Definition: Utility.cxx:141
PyAthenaGILStateEnsure.h
RootUtils::TreeNotifier::m_tree
TTree * m_tree
The tree to which we're attached.
Definition: PyROOTTTreePatch.cxx:99
RootUtils::GetBuffer
int GetBuffer(PyObject *pyobject, char tc, int size, void *&buf, Bool_t check)
Definition: Utility.cxx:26
checker_macros.h
Define macros for attributes used to control the static checker.
python.PyAthena.obj
obj
Definition: PyAthena.py:132
PyObject
_object PyObject
Definition: IPyComponent.h:26
python.CaloScaleNoiseConfig.args
args
Definition: CaloScaleNoiseConfig.py:80
PyROOT::CPPInstance::kIsReference
@ kIsReference
Definition: PyROOTTTreePatch.cxx:35
INSTALL_METHOD
#define INSTALL_METHOD(pyclass, name, func)