ATLAS Offline Software
Loading...
Searching...
No Matches
PyAthenaUtils.cxx
Go to the documentation of this file.
1
2
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
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
32
33// ROOT includes
34#include "TClassRef.h"
35
36// STL includes
37#include <string>
38#include <iostream>
39#include <sstream>
40
41namespace {
42
43 struct MyObjProxy {
44 PyObject_HEAD
45 void* m_object;
46 TClassRef m_class;
47 };
48
49StatusCode
50fetchInterfaceId( 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
105std::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
120std::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
163StatusCode
164PyAthena::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 ) {
183 throw_py_exception();
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);
210 throw_py_exception();
211 }
212
213 if ( !c ) {
214 // error on python side...
215 throw_py_exception();
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);
232 throw_py_exception();
233 }
234 return StatusCode::FAILURE;
235}
236
237StatusCode 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
348void 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);
360 throw_py_exception();
361 }
362 Py_DECREF(call);
363 return;
364}
365
367void 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);
388 throw_py_exception();
389 }
390 Py_DECREF(call);
391 return;
392}
393
_object PyObject
static Double_t sc
Convert python string -> C++ string for py2 and py3.
TTypeAdapter RootType
Definition RootType.h:211
#define x
#define ATLAS_NOT_THREAD_SAFE
getNoisyStrip() Find noisy strips from hitmaps and write out into xml/db formats
TClass * Class() const
Definition RootType.h:183
int r
Definition globals.cxx:22
std::string base
Definition hcg.cxx:81
std::string repr(PyObject *o)
returns the string representation of a python object equivalent of calling repr(o) in python
void throw_py_exception(bool display=true)
helper function to capture the boilerplate code for user friendly stack trace display
std::string str(PyObject *o)
returns the string representation of a python object equivalent of calling str(o) in python
std::pair< std::string, bool > PyGetString(PyObject *s)
Convert python string -> C++ string for py2 and py3.
Definition PyGetString.h:40
MsgStream & msg
Definition testRead.cxx:32