ATLAS Offline Software
PyAthenaUtils.cxx
Go to the documentation of this file.
1 
3 /*
4  Copyright (C) 2002-2024 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 #if PY_VERSION_HEX < 0x03000000
154  PyObject* fstr = PyString_FromString ("flush");
155 #else
156  PyObject* fstr = PyUnicode_FromString ("flush");
157 #endif
158  PyObject* x = PyObject_CallMethodObjArgs (f, fstr, NULL);
159  Py_XDECREF (x);
160  Py_XDECREF (fstr);
161  Py_XDECREF (f);
162  }
163  }
164  throw CPyCppyy::PyException();
165 }
166 
167 StatusCode
168 PyAthena::callPyMethod ATLAS_NOT_THREAD_SAFE ( PyObject* self,
169  const char* methodName,
170  PyObject* arg /*= nullptr*/)
171 {
172  // that's a bit ugly...
173  char* method = const_cast<char*>(methodName);
174 
175  // check arguments
176  if ( 0 == self || 0 == method ) { return StatusCode::FAILURE; }
177 
178  // call Python
180  PyObject* r;
181  if (arg)
182  r = PyObject_CallMethod( self, method, const_cast<char*>("O"), arg );
183  else
184  r = PyObject_CallMethod( self, method, const_cast<char*>("") );
185 
186  if ( 0 == r ) {
188  }
189 
190 #if PY_VERSION_HEX < 0x03000000
191  if ( PyInt_Check( r ) || PyLong_Check(r) ) {
192  StatusCode sc(PyInt_AS_LONG( r ));
193 #else
194  if ( PyLong_Check( r ) ) {
195  StatusCode sc(PyLong_AS_LONG( r ));
196 #endif
197  Py_DECREF( r );
198  return sc;
199  }
200 
201  // look for the method getCode with the signature:
202  // ' int getCode() '
203  PyObject* c = 0;
204  if ( PyObject_HasAttrString (r, (char*)"getCode") ) {
205  c = PyObject_CallMethod( r,
206  const_cast<char*>("getCode"),
207  const_cast<char*>("") );
208  Py_DECREF (r);
209  }
210 
211  else {
212  std::ostringstream msg;
213  msg << "unexpected returned type from (python) function '"
214  << method << "()' [got "
215  << PyAthena::repr ((PyObject*)r->ob_type)
216  << "]";
217  PyErr_SetString (PyExc_TypeError, msg.str().c_str());
218  Py_XDECREF (c);
220  }
221 
222  if ( !c ) {
223  // error on python side...
225  }
226 
227  if ( PyLong_Check (c) ) {
228  StatusCode sc(PyLong_AsLong (c));
229  Py_DECREF (c);
230  return sc;
231  }
232 
233  else {
234  std::ostringstream msg;
235  msg << "unexpected returned type from (python) function '"
236  << method << "().getCode()' [got "
237  << PyAthena::repr ((PyObject*)c->ob_type)
238  << "]";
239  PyErr_SetString (PyExc_TypeError, msg.str().c_str());
240  Py_DECREF (c);
242  }
243  return StatusCode::FAILURE;
244 }
245 
246 StatusCode PyAthena::queryInterface ATLAS_NOT_THREAD_SAFE
247  ( PyObject* self,
248  const InterfaceID& riid,
249  void** ppvInterface )
250 {
251  StatusCode sc = StatusCode::FAILURE;
252 
253  {
254  std::cout
255  << "==============[ " << PyAthena::repr(self) << " ]=============="
256  << std::endl;
257  }
258 
260  PyObject* type = PyObject_GetAttrString( self,
261  const_cast<char*>("__class__") );
262  if ( !type ) {
263  Py_XDECREF(type);
264  return sc;
265  }
266 
267  PyObject* bases;
268  bases = PyObject_CallMethod( type,
269  const_cast<char*>("mro"),
270  const_cast<char*>("") );
271  if ( !bases || !PySequence_Check( bases ) ) {
272  Py_XDECREF(type);
273  Py_XDECREF(bases);
274  return sc;
275  }
276 
277  const int nBases = PySequence_Size( bases );
278  if ( -1 == nBases ) {
279  Py_XDECREF(type);
280  Py_XDECREF(bases);
281  return sc;
282  }
283 
284  for ( int i = 0; i < nBases; ++i ) {
285  PyObject* base = PySequence_GetItem( bases, i );
286  if ( !base ) {
287  Py_XDECREF( base );
288  continue;
289  }
290  unsigned long id = 0;
291  unsigned long major = 0;
292  unsigned long minor = 0;
293  if ( !fetchInterfaceId( base, id, major, minor ).isSuccess() ) {
294  Py_DECREF( base );
295  continue;
296  }
297 
298  InterfaceID pyID( id, major, minor );
299  if ( !pyID.versionMatch( riid ) ) {
300  Py_DECREF( base );
301  continue;
302  }
303 
304  PyObject* pyname = 0;
305  pyname = PyObject_GetAttrString( base,
306  const_cast<char*>("__name__") );
307  auto [cppBaseName, flag] = RootUtils::PyGetString (pyname);
308  Py_DECREF(pyname);
309  if ( !flag ) {
310  Py_DECREF ( base );
311  continue;
312  }
313 
314  const std::string cppName = ((MyObjProxy*)self)->m_class->GetName();
315 
316  std::cout << "::: would like to do: *ppvInterface = static_cast<"
317  << cppBaseName << "*>( "
318  << cppName << "|m_self );"
319  << std::endl;
320 
321  const RootType fromType( cppName );
322  const RootType toType( cppBaseName );
323  void* objProxy = TPython::CPPInstance_AsVoidPtr(self);
324  *ppvInterface = objProxy;
325  if (fromType.Class() && toType.Class())
326  *ppvInterface = fromType.Class()->DynamicCast (toType.Class(), objProxy);
327  std::cout << "::: [" << cppName << "]: "
328  << ( (bool)fromType ? " OK" : "ERR" )
329  << " " << objProxy
330  << "\n"
331  << "::: [" << cppBaseName << "]: "
332  << ( (bool)toType ? " OK" : "ERR" )
333  << " " << *ppvInterface
334  << "\n";
335  std::cout << "::: *ppvInterface: " << *ppvInterface << std::endl;
336  if ( *ppvInterface ) {
337  PyObject* c = PyObject_CallMethod( self,
338  const_cast<char*>("addRef"),
339  const_cast<char*>("") );
340  if ( c && PyLong_Check(c) ) {
341  sc = StatusCode::SUCCESS;
342  }
343  Py_DECREF(c);
344  }
345  Py_DECREF( base );
346  if ( sc.isSuccess() ) {
347  break;
348  }
349  }
350 
351  Py_DECREF(type);
352  Py_DECREF(bases);
353  return sc;
354 }
355 
357 void PyAthena::pyAudit ATLAS_NOT_THREAD_SAFE
358  ( PyObject* self,
359  const char* method,
360  const char* evt,
361  const char* component )
362 {
364  PyObject* call = PyObject_CallMethod(self,
365  (char*)method,
366  (char*)"ss", evt, component);
367  if ( !call ) {
368  Py_XDECREF(call);
370  }
371  Py_DECREF(call);
372  return;
373 }
374 
376 void PyAthena::pyAudit ATLAS_NOT_THREAD_SAFE
377  ( PyObject* self,
378  const char* method,
379  const char* evt,
380  const char* component,
381  const StatusCode& sc )
382 {
384  PyObject* pySc = TPython::CPPInstance_FromVoidPtr((void*)&sc,
385  "StatusCode");
386  if ( !pySc ) {
387  throw CPyCppyy::PyException();
388  }
389 
390  PyObject* call = PyObject_CallMethod(self,
391  (char*)method,
392  (char*)"ssO", evt, component, pySc);
393  Py_DECREF(pySc);
394 
395  if ( !call ) {
396  Py_XDECREF(call);
398  }
399  Py_DECREF(call);
400  return;
401 }
402 
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:676
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:141
ATLAS_NOT_THREAD_SAFE
StatusCode PyAthena::callPyMethod ATLAS_NOT_THREAD_SAFE(PyObject *self, const char *methodName, PyObject *arg)
Definition: PyAthenaUtils.cxx:168
pyroot.display
display
Definition: pyroot.py:44
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