ATLAS Offline Software
StoreGatePyExt.cxx
Go to the documentation of this file.
1 
3 /*
4  Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
5 */
6 
8 
9 #include "Python.h"
10 
11 #include "StoreGateBindingsDict.h"
12 
13 // Framework includes
14 #include "GaudiKernel/Bootstrap.h"
15 #include "GaudiKernel/DataObject.h"
16 #include "GaudiKernel/IClassIDSvc.h"
18 #include "AthenaKernel/BaseInfo.h"
20 
21 // SGTools includes
22 #include "SGTools/BuiltinsClids.h"
23 
24 // ROOT includes
25 #include "DataModelRoot/RootType.h"
26 #include "TClass.h"
27 #include "TClassRef.h"
28 #include "TROOT.h"
29 
30 // PyROOT includes
31 #include "AthenaPyRoot.h"
32 
33 #include "SgPyDataModel.h"
34 #include "SgPyMsg.h"
35 
36 #include <string>
37 #include <vector>
38 #include <algorithm> // for stable_partition
39 
40 // Called from python, so only excuted single-threaded (GIL).
43 
44 namespace {
45  static const CLID bool_clid = ClassID_traits<bool>::ID();
46  static const CLID char_clid = ClassID_traits<char>::ID();
47  static const CLID int_clid = ClassID_traits<int>::ID();
48  static const CLID uint_clid = ClassID_traits<unsigned int>::ID();
49  static const CLID long_clid = ClassID_traits<long>::ID();
50  static const CLID ulong_clid= ClassID_traits<unsigned long>::ID();
51  static const CLID longlong_clid= ClassID_traits<long long>::ID();
52  static const CLID float_clid= ClassID_traits<float>::ID();
53  static const CLID double_clid=ClassID_traits<double>::ID();
54 
55 
60 PyObject* pynameFromType (PyObject* tp)
61 {
62  PyObject* pyname = nullptr;
63 
64  if ( ! PyType_Check( tp ) ) {
65  if ( ! PyUnicode_Check( tp ) )
66  {
67  PyErr_SetString( PyExc_TypeError,
68  "contains() argument 1 must be type or class name" );
69  return nullptr;
70  }
71  else {
72  Py_INCREF( tp );
73  pyname = tp;
74  }
75  }
76  else {
77  pyname = PyObject_GetAttrString( tp, (char*)"__cpp_name__" );
78  if (!pyname) {
79  pyname = PyObject_GetAttrString( tp, (char*)"__name__" );
80  }
81  if ( pyname && ! PyUnicode_Check( pyname ) )
82  {
83  PyObject* pystr = PyObject_Str( pyname );
84  if ( pystr ) {
85  Py_DECREF( pyname );
86  pyname = pystr;
87  }
88  }
89 
90  if ( PyErr_Occurred() )
91  return nullptr;
92  }
93 
94  return pyname;
95 }
96 
97 
98 }
99 
100 PyObject*
102  PyObject* tp, PyObject* pykey )
103 {
105  return retrieveObjectFromStore(store,tp,pykey);
106 }
107 
108 PyObject*
110  PyObject* tp, PyObject* pykey )
111 {
112  void* res = 0;
113  PyObject* objProxy = NULL;
114 
115  static SG::PyProxyMgr& s_mgr = SG::PyProxyMgr::instance();
116 
117 
118  // unlikely to happen, but checking is cheap
119  if ( ! store ) {
120  PyErr_SetString( PyExc_RuntimeError,
121  "no store available, is Athena initialized?" );
122  return 0;
123  }
124 
125  // expect a type or type name and an optional string key
126  PyObject* pyname = pynameFromType( tp );
127  if (!pyname) {
128  return pyname;
129  }
130 
131  auto [namestr, nameflag] = RootUtils::PyGetString( pyname );
132  auto [keystr, keyflag] = RootUtils::PyGetString( pykey );
133  if ( !keyflag ) {
134  if (pykey == Py_None) {
135  keystr = "<None>";
136  }
137  else {
138  PyErr_SetString( PyExc_TypeError,
139  "retrieve() argument 2 must be string key" );
140  Py_XDECREF (pyname);
141  return 0;
142  }
143  }
144 
145  SG::PyProxyDict* proxyDict = s_mgr.pyproxy(store);
146  // retrieve CLID corresponding to the request
147  PyObject* pyclid = s_mgr.pyclid(pyname);
148  if ( ! pyclid ) {
149  PyErr_Format( PyExc_NameError,
150  "ID of \"%s\" is unknown", namestr.c_str() );
151  Py_XDECREF (pyname);
152  return 0;
153  }
154 
155  Py_XDECREF (pyname);
156  pyname = 0;
157 
158  _SGPY_MSG("retrieving py-proxy...");
159  PyObject* pyproxy = proxyDict->proxy(pyclid, pykey);
160  if ( ! pyproxy ) {
161  PyErr_Format( PyExc_LookupError,
162  "no py-proxies for (clid=%lu, type=%s, key=%s)",
163  PyLong_AsUnsignedLong(pyclid),
164  namestr.c_str(),
165  (pykey == Py_None) ? "<None>" : keystr.c_str()
166  );
167  return 0;
168  }
169 
170  _SGPY_MSG("retrieved py-proxy [clid=" << PyLong_AsUnsignedLong(pyclid)
171  << ", type=" << namestr.c_str()
172  << ", key=" << (pykey == Py_None) ? "<None>" : keystr.c_str()
173  << "]");
174 
176 
177  if ( ! proxy ) {
178  PyErr_Format( PyExc_LookupError,
179  "no proxies for (clid=%lu, type=%s, key=%s)",
180  PyLong_AsUnsignedLong(pyclid),
181  namestr.c_str(),
182  (pykey == Py_None) ? "<None>" : keystr.c_str()
183  );
184  return 0;
185  }
186 
187  _SGPY_MSG("retrieved cpp-proxy [clid=" << proxy->clID()
188  << ", key=" << proxy->name() << "]");
189 
190  // cast proxy to pointer type if needed (setting on return type is evil hack)
191  DataObject* dobj = proxy->accessData();
192  if ( ! dobj ) {
193  PyErr_Format( PyExc_LookupError,
194  "no such object \"%s\"", namestr.c_str() );
195  return 0;
196  }
197 
198  _SGPY_MSG("retrieved dobj [clID=" << dobj->clID()
199  << ", classID=" << dobj->classID()
200  << ", name=" << dobj->name() << "]");
201 
202  DataBucketBase* dbb = dynamic_cast< DataBucketBase* >( dobj );
203  if ( ! dbb ) {
204  PyErr_SetString
205  ( PyExc_TypeError,
206  "unexpected kind of DataObject: can not verify final type" );
207  return 0;
208  }
209 
210  _SGPY_MSG("retrieved dbb [clID=" << dbb->clID()
211  << ", classID=" << dbb->classID()
212  << ", name=" << dbb->name() << "]");
213 
214  CLID id = proxy->clID();
215  CLID realID = dbb->clID();
216 
217  _SGPY_MSG("pyid= " << PyLong_AsUnsignedLong(pyclid)
218  << ", id=" << id
219  << ", realID=" << realID);
220 
221  // special case of a regular PyObject
222  if ( PyCLID == realID ) {
223  objProxy = (PyObject*)dbb->object();
224 
225  } else if (realID == bool_clid) {
226  res = dbb->cast( bool_clid );
227  bool *v = reinterpret_cast<bool*>(res);
228  objProxy = PyBool_FromLong(*v);
229  return objProxy;
230 
231  } else if (realID == char_clid) {
232  res = dbb->cast( char_clid );
233  char *v = reinterpret_cast<char*>(res);
234  objProxy = PyUnicode_FromStringAndSize(v, 1);
235  return objProxy;
236 
237  } else if (realID == int_clid) {
238  res = dbb->cast( int_clid );
239  int *v = reinterpret_cast<int*>(res);
240  objProxy = PyLong_FromLong(*v);
241  return objProxy;
242 
243  } else if (realID == uint_clid) {
244  res = dbb->cast( uint_clid );
245  unsigned int *v = reinterpret_cast<unsigned int*>(res);
246  objProxy = PyLong_FromLong(*v);
247  return objProxy;
248 
249  } else if (realID == long_clid) {
250  res = dbb->cast( long_clid );
251  long *v =reinterpret_cast<long*>(res);
252  objProxy = PyLong_FromLong(*v);
253  return objProxy;
254 
255  } else if (realID == ulong_clid) {
256  res = dbb->cast( ulong_clid );
257  unsigned long *v =reinterpret_cast<unsigned long*>(res);
258  objProxy = PyLong_FromUnsignedLong(*v);
259  return objProxy;
260 
261  } else if (realID == longlong_clid) {
262  res = dbb->cast( longlong_clid );
263  long long *v =reinterpret_cast<long long*>(res);
264  objProxy = PyLong_FromUnsignedLong(*v);
265  return objProxy;
266 
267  } else if (realID == float_clid) {
268  res = dbb->cast( float_clid );
269  float *v =reinterpret_cast<float*>(res);
270  objProxy = PyFloat_FromDouble(*v);
271  return objProxy;
272 
273  } else if (realID == double_clid) {
274  res = dbb->cast( double_clid );
275  double *v =reinterpret_cast<double*>(res);
276  objProxy = PyFloat_FromDouble(*v);
277  return objProxy;
278 
279  } else if ( id == realID ) {
280  res = dbb->object();
281 
282  _SGPY_MSG("pyid= " << PyLong_AsUnsignedLong(pyclid)
283  << ", id=" << id
284  << ", realID=" << realID << " => res = [" << res << "]");
285 
286  if ( ! res ) {
287  PyErr_Format( PyExc_RuntimeError, "found an invalid object" );
288  return 0;
289  }
290 
291  const char* klass_name = s_mgr.load_type(id);
292  _SGPY_MSG("retrieving objProxy(" << klass_name
293  << ", " << res << ")...");
294  objProxy = proxyDict->newPyDataObject(klass_name, res);
295  _SGPY_MSG("retrieving objProxy(" << klass_name
296  << ", " << res << ")... [done]");
297 
298  } else {
299  // either use CLID BaseInfo<> or Reflex, try both as appropriate
300  res = dbb->cast( id );
301 
302  // ensure everything is loaded for that clid
303  const char* real_typename = s_mgr.load_type(realID);
304 
305  _SGPY_MSG("dbb::cast(" << id << ") = " << res);
306 
307  if ( res ) {
308 
309  objProxy= proxyDict->newPyDataObject(real_typename, res);
310  } else {
311  // -> try Reflex...
312  IClassIDSvc* clidSvc = s_mgr.m_clidSvc;
313  std::string realName = "";
314  if ( !clidSvc->getTypeNameOfID(realID, realName).isSuccess() ) {
315  PyErr_Format( PyExc_TypeError, "actual type of CLID %lu unknown",
316  (long unsigned int)realID );
317  return 0;
318  }
319 
320  const RootType& fromType = RootType::ByNameNoQuiet(realName);
321 
322  if ( (bool)fromType ) {
323  const RootType& toType = RootType::ByNameNoQuiet( namestr );
324  res = dbb->object();
325  if (fromType.Class() && toType.Class())
326  res = fromType.Class()->DynamicCast (toType.Class(), res);
327 
328  if ( res ) {
329  objProxy = proxyDict->newPyDataObject(realName.c_str(), res);
330  }
331  }
332  }
333 
334  if ( ! res ) {
335  PyErr_SetString( PyExc_TypeError, "cast to requested type failed" );
336  return 0;
337  }
338 
339  }
340 
341  Py_INCREF(objProxy);
342  return objProxy;
343 }
344 
345 PyObject*
347  PyObject* tp, PyObject* pykey)
348 {
350  return py_sg_contains(store,tp,pykey);
351 }
352 
353 PyObject*
355  PyObject* tp, PyObject* pykey)
356 {
357  static SG::PyProxyMgr& s_mgr = SG::PyProxyMgr::instance();
358 
359 
360 
361  // unlikely to happen, but checking is cheap
362  if ( ! store ) {
363  PyErr_SetString( PyExc_RuntimeError,
364  "no store available, is Athena initialized?" );
365  return 0;
366  }
367 
368  // expect a type or type name and an optional string key
369  PyObject* pyname = pynameFromType( tp );
370  if (!pyname) {
371  return pyname;
372  }
373 
374  auto [namestr, nameflag] = RootUtils::PyGetString (pyname);
375  auto [keystr, keyflag] = RootUtils::PyGetString (pykey);
376 
377  if ( !keyflag ) {
378  PyErr_SetString( PyExc_TypeError,
379  "contains() argument 2 must be string key" );
380  Py_XDECREF (pyname);
381  return 0;
382  }
383 
384  // retrieve CLID corresponding to the request
385  const CLID clid = s_mgr.clid (pyname);
386  if ( clid == CLID_NULL ) {
387  PyErr_Format( PyExc_NameError,
388  "ID of \"%s\" is unknown", namestr.c_str() );
389  return 0;
390  }
391 
392  Py_XDECREF (pyname);
393  pyname = 0;
394 
395  const bool sg_contains =
396  store->contains (clid, keystr) ;
397 
398  _SGPY_MSG("sg::contains(" << clid
399  << "/" << namestr.c_str() << ", "
400  << keystr.c_str()
401  << ") = ["
402  << (sg_contains ? std::string("true") : std::string("false"))
403  << "]");
404 
405  PyObject* o = sg_contains
406  ? Py_True
407  : Py_False;
408  Py_INCREF (o);
409  return o;
410 }
411 
412 
413 PyObject*
415  PyObject* pykey)
416 {
418  return py_sg_getitem(store,pykey);
419 }
420 
421 PyObject*
423  PyObject* pykey)
424 {
425  static SG::PyProxyMgr& s_mgr = SG::PyProxyMgr::instance();
426 
427 
428 
429  // unlikely to happen, but checking is cheap
430  if ( ! store ) {
431  PyErr_SetString( PyExc_RuntimeError,
432  "no store available, is Athena initialized?" );
433  return 0;
434  }
435 
436  auto [keystr, keyflag] = RootUtils::PyGetString (pykey);
437  if ( ! keyflag ) {
438  PyErr_SetString( PyExc_TypeError,
439  "__getitem__() argument 1 must be string key" );
440  return 0;
441  }
442 
443  // Retrieve the main @c CLID of the object recorded in @c StoreGate
444  const CLID clid = store->clid (keystr);
445  if ( CLID_NULL == clid ) {
446  PyErr_Format (PyExc_LookupError,
447  "no clid for key=%s",
448  keystr.c_str());
449  return 0;
450  }
451 
453  s_mgr.pytp(clid),
454  pykey);
455 }
456 
457 void
459 {
460  return self->clearProxyPayload(dp);
461 }
462 
463 PyObject*
465  PyObject* obj,
466  PyObject* pykey,
467  bool allowMods /*= true*/,
468  bool resetOnly /*= true*/,
469  bool noHist /*= false*/)
470 {
472  return recordObjectToStore(store,obj,pykey,allowMods,resetOnly,noHist);
473 }
474 
475 PyObject*
477  PyObject* obj,
478  PyObject* pykey,
479  bool allowMods /*= true*/,
480  bool resetOnly /*= true*/,
481  bool noHist /*= false*/)
482 {
483  static SG::PyProxyMgr& s_mgr = SG::PyProxyMgr::instance();
484 
485 
486 
487  // unlikely to happen, but checking is cheap
488  if ( ! store ) {
489  PyErr_SetString( PyExc_RuntimeError,
490  "no store available, is Athena initialized?" );
491  return 0;
492  }
493 
494  // expect a type or type name
495  PyObject* tp = PyObject_GetAttrString( obj, (char*)"__class__" );
496  if ( ! PyType_Check( tp ) ) {
497  PyErr_SetString( PyExc_RuntimeError,
498  "could not retrieve type of object" );
499  return 0;
500  }
501 
502  PyObject* pyname = 0;
503  // check if this is a PyRoot object or a 'regular' PyObject
504  const bool isPlainPyObj = !TPython::CPPInstance_Check (obj);
505  if ( isPlainPyObj ) {
506  pyname = PyUnicode_FromString ((char*)"PyObject");
507  } else {
508  pyname = pynameFromType( tp );
509  }
510 
511  if ( PyErr_Occurred() )
512  return 0;
513 
514  auto [keystr, keyflag] = RootUtils::PyGetString (pykey);
515  auto [namestr, nameflag] = RootUtils::PyGetString (pyname);
516 
517  if ( ! keyflag ) {
518  PyErr_SetString( PyExc_TypeError,
519  "record() argument 2 must be string key" );
520  Py_XDECREF (pyname);
521  return 0;
522  }
523 
524  // retrieve CLID corresponding to the request
525  const CLID id = s_mgr.clid(pyname);
526  if ( CLID_NULL == id ) {
527  PyErr_Format( PyExc_NameError,
528  "ID of \"%s\" is unknown", namestr.c_str() );
529  return 0;
530  }
531 
532  Py_XDECREF (pyname);
533  pyname = 0;
534 
535  // make sure everything has been loaded for that clid...
536  s_mgr.load_type(id);
537 
538  _SGPY_MSG("ID=" << id
539  << ", tp=" << namestr.c_str()
540  << ", key=" << keystr.c_str());
541 
542  SG::PyDataBucket* dbb = new SG::PyDataBucket (obj, id);
543 
544  _SGPY_MSG("created a pdb @" << dbb << ", clID=" << dbb->clID()
545  << ", pdb-obj @" << dbb->object()
546  << ", obj @" << (void*)obj
547  << ", cc: " << CPPInstance_ASVOIDPTR(obj)
548  << ", isplain-pyobj: [" << (int)isPlainPyObj << "]"
549  << ", pyrobj @" << CPPInstance_ASVOIDPTR(obj));
550  _SGPY_MSG("pdb-cast(" << dbb->clID() << "): " << dbb->cast(dbb->clID()));
551 
552  int sc = store->typeless_record( dbb, keystr,
553  isPlainPyObj
554  ? (void*)obj
556  allowMods,
557  resetOnly,
558  noHist ).isSuccess()
559  ? 1
560  : 0;
561  return Py_BuildValue( const_cast<char*>("i"), sc );
562 }
563 
564 //
SGTest::store
TestStore store
Definition: TestStore.cxx:23
TileDCSDataPlotter.dp
dp
Definition: TileDCSDataPlotter.py:840
SG::PyProxyDict::newPyDataObject
PyObject * newPyDataObject(const char *klass, void *addr=0)
Definition: SgPyDataModel.h:447
TScopeAdapter::ByNameNoQuiet
static TScopeAdapter ByNameNoQuiet(const std::string &name, Bool_t load=kTRUE)
Definition: RootType.cxx:581
StateLessPT_NewConfig.proxy
proxy
Definition: StateLessPT_NewConfig.py:392
DataBucketBase
A non-templated base class for DataBucket, allows to access the transient object address as a void*.
Definition: DataBucketBase.h:24
DataBucketBase.h
StoreGatePyExt.h
SG::PyProxyMgr::load_type
const char * load_type(CLID id)
ensure everything has been loaded for the clid id (classid, reflex dict, baseinfobase,...
Definition: SgPyDataModel.h:339
DataBucketBase::object
virtual void * object()=0
ParticleTest.tp
tp
Definition: ParticleTest.py:25
SG::PyDataBucket::object
virtual void * object() override
Return the held object.
Definition: SgPyDataModel.h:115
BuiltinsClids.h
SgPyMsg.h
SG::PyProxyMgr::pyclid
PyObject * pyclid(PyObject *tp)
returns a borrowed reference
Definition: SgPyDataModel.h:219
SG::PyProxyDict
a python front-end to the IProxyDict interface PyProxyDict encapsulates getting python objects from t...
Definition: SgPyDataModel.h:406
DataBucketBase::cast
T * cast(SG::IRegisterTransient *irt=0, bool isConst=true)
Return the contents of the DataBucket, converted to type T.
SG::PyProxyDict::proxy
PyObject * proxy(PyObject *pyclid, PyObject *pykey)
Definition: SgPyDataModel.h:417
AthenaPoolTestRead.sc
sc
Definition: AthenaPoolTestRead.py:27
SgPyDataModel.h
SG::PyProxyMgr::pyproxy
SG::PyProxyDict * pyproxy(StoreGateSvc *sg)
Definition: SgPyDataModel.h:462
SG::PyProxyMgr::clid
CLID clid(PyObject *tp)
Definition: SgPyDataModel.h:274
AthenaInternal::py_sg_contains
PyObject * py_sg_contains(PyObject *storeGateSvc, PyObject *tp, PyObject *key)
Definition: StoreGatePyExt.cxx:346
CPPInstance_ASVOIDPTR
#define CPPInstance_ASVOIDPTR(o)
Definition: AthenaPyRoot.h:20
ClassID_traits::ID
static const CLID & ID()
the CLID of T
Definition: Control/AthenaKernel/AthenaKernel/ClassID_traits.h:50
StoreGateSvc
The Athena Transient Store API.
Definition: StoreGateSvc.h:125
SG::PyDataBucket::cast
virtual void * cast(CLID clid, IRegisterTransient *itr=0, bool isConst=true) override
Return the contents of the DataBucket, converted to type given by clid.
Definition: SgPyDataModel.cxx:54
BaseInfo.h
Provide an interface for finding inheritance information at run time.
ClassID_traits.h
a traits class that associates a CLID to a type T It also detects whether T inherits from Gaudi DataO...
PyCLID
const CLID PyCLID
Definition: SgPyDataModel.cxx:13
res
std::pair< std::vector< unsigned int >, bool > res
Definition: JetGroupProductTest.cxx:14
AthenaInternal::py_sg_getitem
PyObject * py_sg_getitem(PyObject *storeGateSvc, PyObject *key)
Definition: StoreGatePyExt.cxx:414
CLID
uint32_t CLID
The Class ID type.
Definition: Event/xAOD/xAODCore/xAODCore/ClassID_traits.h:47
SG::PyProxyMgr::m_clidSvc
IClassIDSvc * m_clidSvc
Definition: SgPyDataModel.h:194
_SGPY_MSG
#define _SGPY_MSG(x)
Definition: SgPyMsg.h:22
SG::PyDataBucket::clID
virtual const CLID & clID() const override
Retrieve reference to class definition structure.
Definition: SgPyDataModel.h:153
AthenaInternal::py_sg_clearProxyPayload
void py_sg_clearProxyPayload(StoreGateSvc *self, SG::DataProxy *)
Definition: StoreGatePyExt.cxx:458
RootUtils::PyGetString
std::pair< std::string, bool > PyGetString(PyObject *s)
Convert python string -> C++ string for py2 and py3.
Definition: PyGetString.h:40
python.PyAthena.v
v
Definition: PyAthena.py:154
SG::PyDataBucket
Concrete DataBucket that holds the object via a void* and uses the Root dictionary to do conversions.
Definition: SgPyDataModel.h:93
SG::PyProxyMgr::pytp
PyObject * pytp(PyObject *clid)
returns a borrowed reference
Definition: SgPyDataModel.h:238
RootType.h
SG::PyProxyMgr::instance
static PyProxyMgr & instance()
Definition: SgPyDataModel.h:209
ATLAS_NO_CHECK_FILE_THREAD_SAFETY
ATLAS_NO_CHECK_FILE_THREAD_SAFETY
Definition: StoreGatePyExt.cxx:42
checker_macros.h
Define macros for attributes used to control the static checker.
StoreGateBindingsDict.h
python.PyAthena.obj
obj
Definition: PyAthena.py:132
SG::DataProxy
Definition: DataProxy.h:45
AthenaPyRoot.h
PyObject
_object PyObject
Definition: IPyComponent.h:26
AthenaInternal::retrieveObjectFromStore
PyObject * retrieveObjectFromStore(PyObject *storeGateSvc, PyObject *tp, PyObject *key)
Definition: StoreGatePyExt.cxx:101
TScopeAdapter::Class
TClass * Class() const
Definition: RootType.h:183
AthenaInternal::recordObjectToStore
PyObject * recordObjectToStore(StoreGateSvc *, PyObject *, PyObject *, bool, bool, bool)
record object to storegate
Definition: StoreGatePyExt.cxx:476
SG::PyProxyMgr
A helper class to manage accesses to PyProxies.
Definition: SgPyDataModel.h:192
TScopeAdapter
Definition: RootType.h:119