ATLAS Offline Software
Loading...
Searching...
No Matches
PyAthenaUtils.cxx
Go to the documentation of this file.
1
2
3/*
4 Copyright (C) 2002-2026 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#ifdef ATLAS_HAVE_CPYCPPYY
29# include "CPyCppyy/PyException.h"
30#endif // ATLAS_HAVE_CPYCPPYY
31
32// Reflex includes
34
35// ROOT includes
36#include "TClassRef.h"
37
38// STL includes
39#include <string>
40#include <iostream>
41#include <sstream>
42#include <stdexcept>
43
44namespace {
45
46 struct MyObjProxy {
47 PyObject_HEAD
48 void* m_object;
49 TClassRef m_class;
50 };
51
53fetchInterfaceId( PyObject* klass,
54 unsigned long& id,
55 unsigned long& major,
56 unsigned long& minor )
57{
58 Py_INCREF( klass );
59
61 PyObject* idObj = PyObject_CallMethod( klass,
62 const_cast<char*>("interfaceID"),
63 const_cast<char*>("") );
64 Py_DECREF (klass);
65 if ( 0 == idObj ) {
66 PyErr_Clear();
67 return StatusCode::FAILURE;
68 }
69
70 PyObject* pyId = PyObject_CallMethod( idObj,
71 const_cast<char*>("id"),
72 const_cast<char*>("") );
73 if ( 0 == pyId ) {
74 PyErr_Clear();
75 Py_DECREF (idObj);
76 return StatusCode::FAILURE;
77 }
78 id = PyLong_AsUnsignedLong(pyId);
79 Py_DECREF(pyId);
80
81 PyObject* pyMajor = PyObject_CallMethod( idObj,
82 const_cast<char*>("majorVersion"),
83 const_cast<char*>("") );
84 if ( 0 == pyMajor ) {
85 PyErr_Clear();
86 Py_DECREF (idObj);
87 return StatusCode::FAILURE;
88 }
89 major = PyLong_AsUnsignedLong(pyMajor);
90 Py_DECREF(pyMajor);
91
92 PyObject* pyMinor = PyObject_CallMethod( idObj,
93 const_cast<char*>("minorVersion"),
94 const_cast<char*>("") );
95 if ( 0 == pyMinor ) {
96 PyErr_Clear();
97 Py_DECREF (idObj);
98 return StatusCode::FAILURE;
99 }
100 minor = PyLong_AsUnsignedLong(pyMinor);
101 Py_DECREF(pyMajor);
102
103 Py_DECREF (idObj);
104 return StatusCode::SUCCESS;
105}
106} //> anonymous namespace
107
108std::string
110{
111 // PyObject_Repr returns a new ref.
113 PyObject* py_repr = PyObject_Repr( o );
114
115 auto [cpp_repr, flag] = RootUtils::PyGetString (py_repr);
116 Py_DECREF( py_repr );
117 if (!flag) {
119 }
120 return cpp_repr;
121}
122
123std::string
125{
126 // PyObject_Str returns a new ref.
128 PyObject* py_str = PyObject_Str( o );
129 auto [cpp_str, flag] = RootUtils::PyGetString (py_str);
130 Py_DECREF( py_str );
131 if ( !flag ) {
133 }
134 return cpp_str;
135}
136
137[[noreturn]]
139{
140 if (display) {
142 // fetch error
143 PyObject* pytype = 0, *pyvalue = 0, *pytrace = 0;
144 PyErr_Fetch (&pytype, &pyvalue, &pytrace);
145 Py_XINCREF (pytype);
146 Py_XINCREF (pyvalue);
147 Py_XINCREF (pytrace);
148 // restore...
149 PyErr_Restore (pytype, pyvalue, pytrace);
150 // and print
151 PyErr_Print();
152 {
153 // With py3, need to explicitly flush the python stderr
154 // for the error to be visible.
155 PyObject* f = PySys_GetObject (const_cast<char*>("stderr"));
156 PyObject* fstr = PyUnicode_FromString ("flush");
157 PyObject* x = PyObject_CallMethodObjArgs (f, fstr, NULL);
158 Py_XDECREF (x);
159 Py_XDECREF (fstr);
160 Py_XDECREF (f);
161 }
162 }
163#ifdef ATLAS_HAVE_CPYCPPYY
164 throw CPyCppyy::PyException();
165#else
166 throw std::runtime_error ("Python exception");
167#endif // ATLAS_HAVE_CPYCPPYY
168}
169
170StatusCode
171PyAthena::callPyMethod ATLAS_NOT_THREAD_SAFE ( PyObject* self,
172 const char* methodName,
173 PyObject* arg /*= nullptr*/)
174{
175 // that's a bit ugly...
176 char* method = const_cast<char*>(methodName);
177
178 // check arguments
179 if ( 0 == self || 0 == method ) { return StatusCode::FAILURE; }
180
181 // call Python
183 PyObject* r;
184 if (arg)
185 r = PyObject_CallMethod( self, method, const_cast<char*>("O"), arg );
186 else
187 r = PyObject_CallMethod( self, method, const_cast<char*>("") );
188
189 if ( 0 == r ) {
190 throw_py_exception();
191 }
192
193 if ( PyLong_Check( r ) ) {
194 StatusCode sc(PyLong_AS_LONG( r ));
195 Py_DECREF( r );
196 return sc;
197 }
198
199 // look for the method getCode with the signature:
200 // ' int getCode() '
201 PyObject* c = 0;
202 if ( PyObject_HasAttrString (r, (char*)"getCode") ) {
203 c = PyObject_CallMethod( r,
204 const_cast<char*>("getCode"),
205 const_cast<char*>("") );
206 Py_DECREF (r);
207 }
208
209 else {
210 std::ostringstream msg;
211 msg << "unexpected returned type from (python) function '"
212 << method << "()' [got "
213 << PyAthena::repr ((PyObject*)r->ob_type)
214 << "]";
215 PyErr_SetString (PyExc_TypeError, msg.str().c_str());
216 Py_XDECREF (c);
217 throw_py_exception();
218 }
219
220 if ( !c ) {
221 // error on python side...
222 throw_py_exception();
223 }
224
225 if ( PyLong_Check (c) ) {
226 StatusCode sc(PyLong_AsLong (c));
227 Py_DECREF (c);
228 return sc;
229 }
230
231 else {
232 std::ostringstream msg;
233 msg << "unexpected returned type from (python) function '"
234 << method << "().getCode()' [got "
235 << PyAthena::repr ((PyObject*)c->ob_type)
236 << "]";
237 PyErr_SetString (PyExc_TypeError, msg.str().c_str());
238 Py_DECREF (c);
239 throw_py_exception();
240 }
241 return StatusCode::FAILURE;
242}
243
244StatusCode PyAthena::queryInterface ATLAS_NOT_THREAD_SAFE
245 ( PyObject* self,
246 const InterfaceID& riid,
247 void** ppvInterface )
248{
249 StatusCode sc = StatusCode::FAILURE;
250
251 {
252 std::cout
253 << "==============[ " << PyAthena::repr(self) << " ]=============="
254 << std::endl;
255 }
256
258 PyObject* type = PyObject_GetAttrString( self,
259 const_cast<char*>("__class__") );
260 if ( !type ) {
261 Py_XDECREF(type);
262 return sc;
263 }
264
265 PyObject* bases;
266 bases = PyObject_CallMethod( type,
267 const_cast<char*>("mro"),
268 const_cast<char*>("") );
269 if ( !bases || !PySequence_Check( bases ) ) {
270 Py_XDECREF(type);
271 Py_XDECREF(bases);
272 return sc;
273 }
274
275 const int nBases = PySequence_Size( bases );
276 if ( -1 == nBases ) {
277 Py_XDECREF(type);
278 Py_XDECREF(bases);
279 return sc;
280 }
281
282 for ( int i = 0; i < nBases; ++i ) {
283 PyObject* base = PySequence_GetItem( bases, i );
284 if ( !base ) {
285 Py_XDECREF( base );
286 continue;
287 }
288 unsigned long id = 0;
289 unsigned long major = 0;
290 unsigned long minor = 0;
291 if ( !fetchInterfaceId( base, id, major, minor ).isSuccess() ) {
292 Py_DECREF( base );
293 continue;
294 }
295
296 InterfaceID pyID( id, major, minor );
297 if ( !pyID.versionMatch( riid ) ) {
298 Py_DECREF( base );
299 continue;
300 }
301
302 PyObject* pyname = 0;
303 pyname = PyObject_GetAttrString( base,
304 const_cast<char*>("__name__") );
305 auto [cppBaseName, flag] = RootUtils::PyGetString (pyname);
306 Py_DECREF(pyname);
307 if ( !flag ) {
308 Py_DECREF ( base );
309 continue;
310 }
311
312 const std::string cppName = reinterpret_cast<MyObjProxy*>(self)->m_class->GetName();
313
314 std::cout << "::: would like to do: *ppvInterface = static_cast<"
315 << cppBaseName << "*>( "
316 << cppName << "|m_self );"
317 << std::endl;
318
319 const RootType fromType( cppName );
320 const RootType toType( cppBaseName );
321 void* objProxy = TPython::CPPInstance_AsVoidPtr(self);
322 *ppvInterface = objProxy;
323 if (fromType.Class() && toType.Class())
324 *ppvInterface = fromType.Class()->DynamicCast (toType.Class(), objProxy);
325 std::cout << "::: [" << cppName << "]: "
326 << ( (bool)fromType ? " OK" : "ERR" )
327 << " " << objProxy
328 << "\n"
329 << "::: [" << cppBaseName << "]: "
330 << ( (bool)toType ? " OK" : "ERR" )
331 << " " << *ppvInterface
332 << "\n";
333 std::cout << "::: *ppvInterface: " << *ppvInterface << std::endl;
334 if ( *ppvInterface ) {
335 PyObject* c = PyObject_CallMethod( self,
336 const_cast<char*>("addRef"),
337 const_cast<char*>("") );
338 if ( c && PyLong_Check(c) ) {
339 sc = StatusCode::SUCCESS;
340 }
341 Py_DECREF(c);
342 }
343 Py_DECREF( base );
344 if ( sc.isSuccess() ) {
345 break;
346 }
347 }
348
349 Py_DECREF(type);
350 Py_DECREF(bases);
351 return sc;
352}
353
355void PyAthena::pyAudit ATLAS_NOT_THREAD_SAFE
356 ( PyObject* self,
357 const char* method,
358 const char* evt,
359 const char* component )
360{
362 PyObject* call = PyObject_CallMethod(self,
363 (char*)method,
364 (char*)"ss", evt, component);
365 if ( !call ) {
366 Py_XDECREF(call);
367 throw_py_exception();
368 }
369 Py_DECREF(call);
370 return;
371}
372
374void PyAthena::pyAudit ATLAS_NOT_THREAD_SAFE
375 ( PyObject* self,
376 const char* method,
377 const char* evt,
378 const char* component,
379 const StatusCode& sc )
380{
382 PyObject* pySc = TPython::CPPInstance_FromVoidPtr((void*)&sc,
383 "StatusCode");
384 if ( !pySc ) {
385#ifdef ATLAS_HAVE_CPYCPPYY
386 throw CPyCppyy::PyException();
387#else
388 throw std::runtime_error ("Python exception");
389#endif // ATLAS_HAVE_CPYCPPYY
390 }
391
392 PyObject* call = PyObject_CallMethod(self,
393 (char*)method,
394 (char*)"ssO", evt, component, pySc);
395 Py_DECREF(pySc);
396
397 if ( !call ) {
398 Py_XDECREF(call);
399 throw_py_exception();
400 }
401 Py_DECREF(call);
402 return;
403}
_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:83
::StatusCode StatusCode
StatusCode definition for legacy code.
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