ATLAS Offline Software
PyROOTTTreePatch.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2025 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 #if PY_VERSION_HEX >= 0x030d0000
147  PyObject* treeobj = nullptr;
148  (void)PyWeakref_GetRef (m_treeobj_ref, &treeobj);
149 #else
150  PyObject* treeobj = PyWeakref_GetObject (m_treeobj_ref);
151 #endif
152  if (treeobj) {
153  PyObject** dictptr = _PyObject_GetDictPtr (treeobj);
154  if (dictptr && *dictptr) {
155  PyObject* notobj = PyObject_GetItem (*dictptr, pynotify_str);
156 #if PY_VERSION_HEX >= 0x030d0000
157  Py_DECREF(treeobj);
158 #endif
159  if (notobj) {
160  // Got it --- call @c Notify.
161  PyObject* ret =
162  PyObject_CallMethod (notobj, const_cast<char*> ("Notify"), NULL);
163 #if PY_VERSION_HEX >= 0x030d0000
164  Py_DECREF (notobj);
165 #endif
166  if (!ret) return 0;
167  Py_DECREF (ret);
168  }
169  else
170  PyErr_Clear();
171  }
172  }
173 
174  if (m_chain) m_chain->Notify();
175  return true;
176 }
177 
178 
180 void TreeNotifier::Clear (Option_t* /*option = ""*/)
181 {
182  m_tree = nullptr;
183 }
184 
185 
191 {
192  // Decode the objects --- the tree and the notifier object.
193  PyObject* self = 0;
194  PyObject* obj = 0;
195  if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:SetNotify" ),
196  &self, &obj ) )
197  return 0;
198 
199  // Intern strings if needed.
200  if (pynotify_str == 0) {
201  pynotify_str = PyUnicode_InternFromString("__pynotify__");
202  }
203  if (notifier_str == 0) {
204  notifier_str = PyUnicode_InternFromString("__notifier__");
205  }
206 
207  // Set up notifier.
208  if (!PyObject_HasAttr (self, notifier_str)) {
209  // get hold of actual tree
210  void* vp = TPython::CPPInstance_AsVoidPtr (self);
211  TTree* tree =
212  (TTree*)objectIsA(self)->DynamicCast ( TTree::Class(), vp );
213 
214  PyObject* treeobj_ref = PyWeakref_NewRef (self, 0);
215  if (!treeobj_ref)
216  return 0;
217  TObject* notifier = new TreeNotifier (tree, treeobj_ref);
218  PyObject* notobj = TPython::CPPInstance_FromVoidPtr (notifier, "TObject");
219  setOwnership (notobj, true);
220  int stat = PyObject_SetAttr (self, notifier_str, notobj);
221  Py_XDECREF (notobj);
222  if (stat < 0)
223  return 0;
224  tree->SetNotify (notifier);
225  }
226 
227  // Install the object.
228  int stat = PyObject_SetAttr (self, pynotify_str, obj);
229  if (stat < 0)
230  return 0;
231 
232  Py_INCREF (Py_None);
233  return Py_None;
234 }
235 
236 
242 {
243  // Decode the objects --- the tree.
244  PyObject* self = 0;
245  if ( ! PyArg_ParseTuple( args, const_cast< char* >( "O:SetNotify" ),
246  &self ) )
247  return 0;
248 
249  // Intern string if needed.
250  if (pynotify_str == 0)
251  pynotify_str = PyUnicode_InternFromString("__pynotify__");
252 
253  // Retrieve the notifier.
254  PyObject* ret = PyObject_GetAttr (self, pynotify_str);
255  if (!ret) {
256  PyErr_Clear();
257  Py_INCREF (Py_None);
258  ret = Py_None;
259  }
260  return ret;
261 }
262 
263 
269 {
270  // Decode arguments --- the branch and the buffer.
271  PyObject* self = 0;
272  PyObject* address = 0;
273  if ( ! PyArg_ParseTuple( args, const_cast< char* >( "OO:SetBranchAddress" ),
274  &self, &address ) )
275  return 0;
276 
277  // The branch as a Root object.
278  TBranch* branch = 0;
279  if (TPython::CPPInstance_Check (self)) {
280  branch = (TBranch*)objectIsA(self)->DynamicCast
281  ( TBranch::Class(),
282  TPython::CPPInstance_AsVoidPtr(self) );
283  }
284 
285  if ( ! branch ) {
286  PyErr_SetString( PyExc_TypeError,
287  "TBranch::SetAddress must be called with a "
288  "TBranch instance as first argument" );
289  return 0;
290  }
291 
292  // Convert the buffer argument to an address.
293  void* buf = 0;
294  if ( TPython::CPPInstance_Check( address ) ) {
295  auto* inst = reinterpret_cast<CPPInstance*>(address);
296  if ( inst->fFlags & CPPInstance::kIsReference )
297  buf = inst->fObject;
298  else
299  buf = &inst->fObject;
300  }
301  else
302  RootUtils::GetBuffer( address, '*', 1, buf, kFALSE );
303 
304  // Make the call and return.
305  if ( buf != 0 )
306  branch->SetAddress( buf );
307 
308  Py_INCREF( Py_None );
309  return Py_None;
310 }
311 
312 
320 void installMethod (PyObject* pyclass,
321  PyMethodDef& pdef,
322  const char* name,
323  PyCFunction cfunc)
324 {
325  pdef.ml_name = const_cast<char*> (name);
326  pdef.ml_meth = cfunc;
327  pdef.ml_flags = METH_VARARGS;
328  pdef.ml_doc = 0;
329 
330  PyObject* func = PyCFunction_New (&pdef, 0);
331  PyObject* method = PyInstanceMethod_New (func);
332  Bool_t isOK = PyObject_SetAttrString (pyclass, pdef.ml_name, method) == 0;
333 
334  if (PyErr_Occurred())
335  fprintf (stderr, "Py error");
336  else if (!isOK) {
337  fprintf (stderr, "Could not add method %s\n", name);
338  }
339 }
340 
341 
349  PyObject* /*chain_pyclass*/,
350  PyObject* branch_pyclass)
351 {
352 # define INSTALL_METHOD(pyclass, name, func) do { \
353  static PyMethodDef pdef; \
354  installMethod (pyclass, pdef, name, (PyCFunction)func); \
355  } while(0)
356 
357  INSTALL_METHOD (tree_pyclass, "SetNotify", treeSetNotify);
358  INSTALL_METHOD (tree_pyclass, "GetNotify", treeGetNotify);
359  INSTALL_METHOD (branch_pyclass, "SetAddress", branchSetAddress);
360 #undef INSTALL_METHOD
361 }
362 
363 
364 } // namespace RootUtils
RunTileTBRec.method
method
Definition: RunTileTBRec.py:73
RootUtils
Definition: ILogger.h:20
python.CaloAddPedShiftConfig.args
args
Definition: CaloAddPedShiftConfig.py:47
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:180
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:241
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:268
RootUtils::treeSetNotify
PyObject * treeSetNotify(PyObject *, PyObject *args)
Implementation for pythonization of TTree::SetNotify.
Definition: PyROOTTTreePatch.cxx:190
ATLAS_NO_CHECK_FILE_THREAD_SAFETY
ATLAS_NO_CHECK_FILE_THREAD_SAFETY
Definition: PyROOTTTreePatch.cxx:13
beamspotman.stat
stat
Definition: beamspotman.py:264
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:320
RootUtils::PyROOTTTreePatch::Initialize
static void Initialize(PyObject *tree_pyclass, PyObject *chain_pyclass, PyObject *branch_pyclass)
Install the PyROOT patches.
Definition: PyROOTTTreePatch.cxx:348
PyROOT
Definition: PyROOTTTreePatch.cxx:31
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:240
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
PyROOT::CPPInstance::kIsReference
@ kIsReference
Definition: PyROOTTTreePatch.cxx:35
INSTALL_METHOD
#define INSTALL_METHOD(pyclass, name, func)