ATLAS Offline Software
PyAthenaUtils.cxx
Go to the documentation of this file.
1 
3 /*
4  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
5 */
6 
7 // PyAthenaUtils.cxx
8 // Implementation file for various PyAthena helpers
9 // Author: S.Binet<binet@cern.ch>
10 // Modified: Wim Lavrijsen <WLavrijsen@lbl.gov>
12 
13 // Python includes
14 #include "Python.h"
15 
16 // AthenaPython includes
19 #include "RootUtils/PyGetString.h"
20 
21 // Framework includes
22 #include "GaudiKernel/IInterface.h"
23 #include "GaudiKernel/INamedInterface.h"
24 #include "GaudiKernel/StatusCode.h"
25 
26 // PyROOT includes
27 #include "TPython.h"
28 #include "CPyCppyy/PyException.h"
29 
30 // Reflex includes
31 #include "DataModelRoot/RootType.h"
32 
33 // ROOT includes
34 #include "TClassRef.h"
35 
36 // STL includes
37 #include <string>
38 #include <iostream>
39 #include <sstream>
40 
41 namespace {
42 
43  struct MyObjProxy {
44  PyObject_HEAD
45  void* m_object;
46  TClassRef m_class;
47  };
48 
50 fetchInterfaceId( PyObject* klass,
51  unsigned long& id,
52  unsigned long& major,
53  unsigned long& minor )
54 {
55  Py_INCREF( klass );
56 
58  PyObject* idObj = PyObject_CallMethod( klass,
59  const_cast<char*>("interfaceID"),
60  const_cast<char*>("") );
61  Py_DECREF (klass);
62  if ( 0 == idObj ) {
63  PyErr_Clear();
64  return StatusCode::FAILURE;
65  }
66 
67  PyObject* pyId = PyObject_CallMethod( idObj,
68  const_cast<char*>("id"),
69  const_cast<char*>("") );
70  if ( 0 == pyId ) {
71  PyErr_Clear();
72  Py_DECREF (idObj);
73  return StatusCode::FAILURE;
74  }
75  id = PyLong_AsUnsignedLong(pyId);
76  Py_DECREF(pyId);
77 
78  PyObject* pyMajor = PyObject_CallMethod( idObj,
79  const_cast<char*>("majorVersion"),
80  const_cast<char*>("") );
81  if ( 0 == pyMajor ) {
82  PyErr_Clear();
83  Py_DECREF (idObj);
84  return StatusCode::FAILURE;
85  }
86  major = PyLong_AsUnsignedLong(pyMajor);
87  Py_DECREF(pyMajor);
88 
89  PyObject* pyMinor = PyObject_CallMethod( idObj,
90  const_cast<char*>("minorVersion"),
91  const_cast<char*>("") );
92  if ( 0 == pyMinor ) {
93  PyErr_Clear();
94  Py_DECREF (idObj);
95  return StatusCode::FAILURE;
96  }
97  minor = PyLong_AsUnsignedLong(pyMinor);
98  Py_DECREF(pyMajor);
99 
100  Py_DECREF (idObj);
101  return StatusCode::SUCCESS;
102 }
103 } //> anonymous namespace
104 
105 std::string
107 {
108  // PyObject_Repr returns a new ref.
110  PyObject* py_repr = PyObject_Repr( o );
111 
112  auto [cpp_repr, flag] = RootUtils::PyGetString (py_repr);
113  Py_DECREF( py_repr );
114  if (!flag) {
116  }
117  return cpp_repr;
118 }
119 
120 std::string
122 {
123  // PyObject_Str returns a new ref.
125  PyObject* py_str = PyObject_Str( o );
126  auto [cpp_str, flag] = RootUtils::PyGetString (py_str);
127  Py_DECREF( py_str );
128  if ( !flag ) {
130  }
131  return cpp_str;
132 }
133 
134 [[noreturn]]
136 {
137  if (display) {
139  // fetch error
140  PyObject* pytype = 0, *pyvalue = 0, *pytrace = 0;
141  PyErr_Fetch (&pytype, &pyvalue, &pytrace);
142  Py_XINCREF (pytype);
143  Py_XINCREF (pyvalue);
144  Py_XINCREF (pytrace);
145  // restore...
146  PyErr_Restore (pytype, pyvalue, pytrace);
147  // and print
148  PyErr_Print();
149  {
150  // With py3, need to explicitly flush the python stderr
151  // for the error to be visible.
152  PyObject* f = PySys_GetObject (const_cast<char*>("stderr"));
153  PyObject* fstr = PyUnicode_FromString ("flush");
154  PyObject* x = PyObject_CallMethodObjArgs (f, fstr, NULL);
155  Py_XDECREF (x);
156  Py_XDECREF (fstr);
157  Py_XDECREF (f);
158  }
159  }
160  throw CPyCppyy::PyException();
161 }
162 
163 StatusCode
164 PyAthena::callPyMethod ATLAS_NOT_THREAD_SAFE ( PyObject* self,
165  const char* methodName,
166  PyObject* arg /*= nullptr*/)
167 {
168  // that's a bit ugly...
169  char* method = const_cast<char*>(methodName);
170 
171  // check arguments
172  if ( 0 == self || 0 == method ) { return StatusCode::FAILURE; }
173 
174  // call Python
176  PyObject* r;
177  if (arg)
178  r = PyObject_CallMethod( self, method, const_cast<char*>("O"), arg );
179  else
180  r = PyObject_CallMethod( self, method, const_cast<char*>("") );
181 
182  if ( 0 == r ) {
184  }
185 
186  if ( PyLong_Check( r ) ) {
187  StatusCode sc(PyLong_AS_LONG( r ));
188  Py_DECREF( r );
189  return sc;
190  }
191 
192  // look for the method getCode with the signature:
193  // ' int getCode() '
194  PyObject* c = 0;
195  if ( PyObject_HasAttrString (r, (char*)"getCode") ) {
196  c = PyObject_CallMethod( r,
197  const_cast<char*>("getCode"),
198  const_cast<char*>("") );
199  Py_DECREF (r);
200  }
201 
202  else {
203  std::ostringstream msg;
204  msg << "unexpected returned type from (python) function '"
205  << method << "()' [got "
206  << PyAthena::repr ((PyObject*)r->ob_type)
207  << "]";
208  PyErr_SetString (PyExc_TypeError, msg.str().c_str());
209  Py_XDECREF (c);
211  }
212 
213  if ( !c ) {
214  // error on python side...
216  }
217 
218  if ( PyLong_Check (c) ) {
219  StatusCode sc(PyLong_AsLong (c));
220  Py_DECREF (c);
221  return sc;
222  }
223 
224  else {
225  std::ostringstream msg;
226  msg << "unexpected returned type from (python) function '"
227  << method << "().getCode()' [got "
228  << PyAthena::repr ((PyObject*)c->ob_type)
229  << "]";
230  PyErr_SetString (PyExc_TypeError, msg.str().c_str());
231  Py_DECREF (c);
233  }
234  return StatusCode::FAILURE;
235 }
236 
237 StatusCode PyAthena::queryInterface ATLAS_NOT_THREAD_SAFE
238  ( PyObject* self,
239  const InterfaceID& riid,
240  void** ppvInterface )
241 {
242  StatusCode sc = StatusCode::FAILURE;
243 
244  {
245  std::cout
246  << "==============[ " << PyAthena::repr(self) << " ]=============="
247  << std::endl;
248  }
249 
251  PyObject* type = PyObject_GetAttrString( self,
252  const_cast<char*>("__class__") );
253  if ( !type ) {
254  Py_XDECREF(type);
255  return sc;
256  }
257 
258  PyObject* bases;
259  bases = PyObject_CallMethod( type,
260  const_cast<char*>("mro"),
261  const_cast<char*>("") );
262  if ( !bases || !PySequence_Check( bases ) ) {
263  Py_XDECREF(type);
264  Py_XDECREF(bases);
265  return sc;
266  }
267 
268  const int nBases = PySequence_Size( bases );
269  if ( -1 == nBases ) {
270  Py_XDECREF(type);
271  Py_XDECREF(bases);
272  return sc;
273  }
274 
275  for ( int i = 0; i < nBases; ++i ) {
276  PyObject* base = PySequence_GetItem( bases, i );
277  if ( !base ) {
278  Py_XDECREF( base );
279  continue;
280  }
281  unsigned long id = 0;
282  unsigned long major = 0;
283  unsigned long minor = 0;
284  if ( !fetchInterfaceId( base, id, major, minor ).isSuccess() ) {
285  Py_DECREF( base );
286  continue;
287  }
288 
289  InterfaceID pyID( id, major, minor );
290  if ( !pyID.versionMatch( riid ) ) {
291  Py_DECREF( base );
292  continue;
293  }
294 
295  PyObject* pyname = 0;
296  pyname = PyObject_GetAttrString( base,
297  const_cast<char*>("__name__") );
298  auto [cppBaseName, flag] = RootUtils::PyGetString (pyname);
299  Py_DECREF(pyname);
300  if ( !flag ) {
301  Py_DECREF ( base );
302  continue;
303  }
304 
305  const std::string cppName = reinterpret_cast<MyObjProxy*>(self)->m_class->GetName();
306 
307  std::cout << "::: would like to do: *ppvInterface = static_cast<"
308  << cppBaseName << "*>( "
309  << cppName << "|m_self );"
310  << std::endl;
311 
312  const RootType fromType( cppName );
313  const RootType toType( cppBaseName );
314  void* objProxy = TPython::CPPInstance_AsVoidPtr(self);
315  *ppvInterface = objProxy;
316  if (fromType.Class() && toType.Class())
317  *ppvInterface = fromType.Class()->DynamicCast (toType.Class(), objProxy);
318  std::cout << "::: [" << cppName << "]: "
319  << ( (bool)fromType ? " OK" : "ERR" )
320  << " " << objProxy
321  << "\n"
322  << "::: [" << cppBaseName << "]: "
323  << ( (bool)toType ? " OK" : "ERR" )
324  << " " << *ppvInterface
325  << "\n";
326  std::cout << "::: *ppvInterface: " << *ppvInterface << std::endl;
327  if ( *ppvInterface ) {
328  PyObject* c = PyObject_CallMethod( self,
329  const_cast<char*>("addRef"),
330  const_cast<char*>("") );
331  if ( c && PyLong_Check(c) ) {
332  sc = StatusCode::SUCCESS;
333  }
334  Py_DECREF(c);
335  }
336  Py_DECREF( base );
337  if ( sc.isSuccess() ) {
338  break;
339  }
340  }
341 
342  Py_DECREF(type);
343  Py_DECREF(bases);
344  return sc;
345 }
346 
348 void PyAthena::pyAudit ATLAS_NOT_THREAD_SAFE
349  ( PyObject* self,
350  const char* method,
351  const char* evt,
352  const char* component )
353 {
355  PyObject* call = PyObject_CallMethod(self,
356  (char*)method,
357  (char*)"ss", evt, component);
358  if ( !call ) {
359  Py_XDECREF(call);
361  }
362  Py_DECREF(call);
363  return;
364 }
365 
367 void PyAthena::pyAudit ATLAS_NOT_THREAD_SAFE
368  ( PyObject* self,
369  const char* method,
370  const char* evt,
371  const char* component,
372  const StatusCode& sc )
373 {
375  PyObject* pySc = TPython::CPPInstance_FromVoidPtr((void*)&sc,
376  "StatusCode");
377  if ( !pySc ) {
378  throw CPyCppyy::PyException();
379  }
380 
381  PyObject* call = PyObject_CallMethod(self,
382  (char*)method,
383  (char*)"ssO", evt, component, pySc);
384  Py_DECREF(pySc);
385 
386  if ( !call ) {
387  Py_XDECREF(call);
389  }
390  Py_DECREF(call);
391  return;
392 }
393 
RunTileTBRec.method
method
Definition: RunTileTBRec.py:73
RootUtils::PyException
CPyCppyy::PyException PyException
Definition: Utility.h:24
base
std::string base
Definition: hcg.cxx:78
beamspotman.r
def r
Definition: beamspotman.py:674
PyAthena::throw_py_exception
void throw_py_exception(bool display=true)
helper function to capture the boilerplate code for user friendly stack trace display
Definition: PyAthenaUtils.cxx:135
LArG4FSStartPointFilter.evt
evt
Definition: LArG4FSStartPointFilter.py:42
x
#define x
python.CaloAddPedShiftConfig.type
type
Definition: CaloAddPedShiftConfig.py:42
AthenaPoolTestRead.sc
sc
Definition: AthenaPoolTestRead.py:27
klass
This class describe the base functionalities of a HypoTool used by the ComboAlg.
RootUtils::PyGILStateEnsure
Definition: PyAthenaGILStateEnsure.h:20
PyAthena::str
std::string str(PyObject *o)
returns the string representation of a python object equivalent of calling str(o) in python
Definition: PyAthenaUtils.cxx:121
python.trfUtils.call
def call(args, bufsize=0, executable=None, stdin=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, message="", logger=msg, loglevel=None, timeout=None, retry=2, timefactor=1.5, sleeptime=10)
Definition: trfUtils.py:155
lumiFormat.i
int i
Definition: lumiFormat.py:85
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
master.flag
bool flag
Definition: master.py:29
PyAthena::repr
std::string repr(PyObject *o)
returns the string representation of a python object equivalent of calling repr(o) in python
Definition: PyAthenaUtils.cxx:106
hist_file_dump.f
f
Definition: hist_file_dump.py:140
ATLAS_NOT_THREAD_SAFE
StatusCode PyAthena::callPyMethod ATLAS_NOT_THREAD_SAFE(PyObject *self, const char *methodName, PyObject *arg)
Definition: PyAthenaUtils.cxx:164
pyroot.display
display
Definition: pyroot.py:42
PyAthenaUtils.h
create_dcsc_inputs_sqlite.arg
list arg
Definition: create_dcsc_inputs_sqlite.py:48
PyGetString.h
Convert python string -> C++ string for py2 and py3.
RootUtils::PyGetString
std::pair< std::string, bool > PyGetString(PyObject *s)
Convert python string -> C++ string for py2 and py3.
Definition: PyGetString.h:40
RootType.h
PyAthenaGILStateEnsure.h
xAOD::bool
setBGCode setTAP setLVL2ErrorBits bool
Definition: TrigDecision_v1.cxx:60
PyObject
_object PyObject
Definition: IPyComponent.h:26
python.compressB64.c
def c
Definition: compressB64.py:93
python.AutoConfigFlags.msg
msg
Definition: AutoConfigFlags.py:7
TScopeAdapter::Class
TClass * Class() const
Definition: RootType.h:183
TScopeAdapter
Definition: RootType.h:119