ATLAS Offline Software
SgPyDataModel.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 
3 /*
4  Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
5 */
6 
7 #ifndef STOREGATEBINDINGS_SGPYDATAMODEL_H
8 #define STOREGATEBINDINGS_SGPYDATAMODEL_H 1
9 
10 #include "Python.h"
11 
12 #include "StoreGateBindingsDict.h"
13 #include "SgPyMsg.h"
14 
15 // Framework includes
16 #include "GaudiKernel/Bootstrap.h"
17 #include "GaudiKernel/ClassID.h"
18 #include "GaudiKernel/DataObject.h"
19 #include "GaudiKernel/IClassIDSvc.h"
22 #include "AthenaKernel/BaseInfo.h"
23 
25 
26 extern const CLID PyCLID;
27 
28 
29 // ROOT includes
30 #include "TClass.h"
31 #include "TClassEdit.h"
32 #include "TClassRef.h"
33 #include "TROOT.h"
34 #include "TMethod.h"
35 #include "TMethodCall.h"
36 
37 // PyROOT includes
38 #include "AthenaPyRoot.h"
39 #include "CPyCppyy/PyException.h"
40 
41 #include <unordered_map>
42 
43 // Called from python, so only excuted single-threaded (GIL).
46 
47 // fwd declares
48 namespace SG { struct PyProxyMgr; }
49 namespace SG { struct PyProxyDict; }
50 
51 namespace {
54  std::string::size_type
55  cxx_replace(std::string& s,
56  const std::string& from,
57  const std::string& to)
58  {
59  std::string::size_type cnt(std::string::npos);
60 
61  if(from != to && !from.empty()) {
62  std::string::size_type pos1(0);
63  std::string::size_type pos2(0);
64  const std::string::size_type from_len(from.size());
65  const std::string::size_type to_len(to.size());
66  cnt = 0;
67 
68  while((pos1 = s.find(from, pos2)) != std::string::npos) {
69  s.replace(pos1, from_len, to);
70  pos2 = pos1 + to_len;
71  ++cnt;
72  }
73  }
74  return cnt;
75  }
76 }
77 
78 namespace SG {
79 
93  {
94  public:
101  CLID clid );
102 
103 
107  virtual ~PyDataBucket() override {
109  Py_DECREF( m_pyObj );
110  }
111 
115  virtual void* object() override
116  {
117  return m_clid != PyCLID
119  : m_pyObj;
120  }
121 
122 
123  using DataBucketBase::cast;
124 
125 
134  virtual void* cast (CLID clid, IRegisterTransient* itr = 0,
135  bool isConst = true) override;
136 
137 
146  virtual void* cast (const std::type_info& tinfo,
147  IRegisterTransient* itr = 0,
148  bool isConst = true) override;
149 
153  virtual const CLID& clID() const override { return m_clid; }
154 
158  virtual const std::type_info& tinfo() const override
159  {
160  return m_bib->typeinfo();
161  }
162 
166  virtual void relinquish() override
167  {
168  Py_DECREF( m_pyObj );
169  }
170 
174  virtual void lock() override;
175 
176  private:
179 
182 
186  };
187 
191  struct PyProxyMgr
192  {
194  IClassIDSvc* m_clidSvc;
197 
200 
201  typedef std::unordered_map<StoreGateSvc*,SG::PyProxyDict*> PyProxyMap_t;
203 
205  typedef std::unordered_map<CLID, std::string> ClidMap_t;
207 
208  static
210  { static PyProxyMgr mgr; return mgr; }
211 
212  PyProxyMgr();
213  ~PyProxyMgr();
214 
216 
218  inline
220  {
221  PyObject* clid = PyDict_GetItem(m_clids, tp);
222  if ( NULL == clid ) {
223  const CLID id = this->clid(tp);
224  if ( id == CLID_NULL ) {
225  return NULL;
226  }
227  clid = PyLong_FromLong(id);
228  PyDict_SetItem(m_clids, tp, clid);
229  // add reverse look-up entry too
230  PyDict_SetItem(m_clids, clid, tp);
231  //Py_INCREF(clid);
232  }
233  return clid;
234  }
235 
237  inline
239  {
240  PyObject* tp = PyDict_GetItem(m_clids, clid);
241  if ( NULL == tp ) {
242  std::string cpp_tp;
243  if (!m_clidSvc->getTypeNameOfID(PyLong_AsUnsignedLong(clid),
244  cpp_tp).isSuccess()) {
245  return NULL;
246  }
247  PyObject* alias = PyDict_GetItemString(m_aliases, cpp_tp.c_str());
248  if ( alias ) {
249  tp = alias;
250  } else {
251  tp = PyUnicode_FromString(cpp_tp.c_str());
252  }
253  PyDict_SetItem(m_clids, clid, tp);
254  // reverse look-up
255  PyDict_SetItem(m_clids, tp, clid);
256  if (!alias) {
257  Py_DECREF(tp);
258  }
259  }
260  return tp;
261  }
262 
264  inline
266  {
267  PyObject* pyclid = PyLong_FromLong(clid);
268  PyObject* o = this->pytp(pyclid);
269  Py_DECREF(pyclid);
270  return o;
271  }
272 
273  inline
275  {
276  CLID id = CLID_NULL;
277  // FIXME: get rid of this massaging when/if ROOT finally
278  // standardize on keeping the std:: namespace...
279  std::string tpstr = RootUtils::PyGetString(tp).first;
280  std::string tn;
281  {
282  // Protect against data race inside TClassEdit.
283  // https://github.com/root-project/root/issues/10353
284  // Should be fixed in root 6.26.02.
285  R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
286  tn = TClassEdit::ShortType(tpstr.c_str(),
287  TClassEdit::kDropAllDefault);
288  }
289  m_clidSvc->getIDOfTypeName(tn, id).ignore();
290  if ( id == CLID_NULL ) {
291  // try an alias...
292  PyObject* alias = PyDict_GetItemString(m_aliases, tn.c_str());
293  if ( alias ){
294  std::string aliasstr = RootUtils::PyGetString(alias).first;
295  m_clidSvc->getIDOfTypeName(aliasstr, id).ignore();
296  }
297  }
298  if (id == CLID_NULL) {
299  // then try a type-id name...
300  return this->clid_from_tid (tp);
301  }
302  return id;
303  }
304 
305  inline
307  {
308  CLID id = CLID_NULL;
309  // FIXME: get rid of this massaging when/if ROOT finally
310  // standardize on keeping the std:: namespace...
311  std::string tpstr = RootUtils::PyGetString(tp).first;
312  std::string tn;
313  {
314  // Protect against data race inside TClassEdit.
315  // https://github.com/root-project/root/issues/10353
316  // Should be fixed in root 6.26.02.
317  R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
318  tn = TClassEdit::ShortType(tpstr.c_str(),
319  TClassEdit::kDropAllDefault);
320  }
321  m_clidSvc->getIDOfTypeInfoName(tn, id).ignore();
322  if ( id == CLID_NULL ) {
323  // try an alias...
324  PyObject* alias = PyDict_GetItemString(m_aliases, tn.c_str());
325  if ( alias ){
326  std::string aliasstr = RootUtils::PyGetString(alias).first;
327  m_clidSvc->getIDOfTypeInfoName(aliasstr,
328  id).ignore();
329  }
330  }
331  return id;
332  }
333 
338  inline
339  const char* load_type(CLID id)
340  {
341  typedef ClidMap_t::iterator Itr_t;
342  Itr_t i = m_clidMap.find(id);
343  if (i != m_clidMap.end()) {
344  return i->second.c_str();
345  }
346  // Reflex now returns std::basic_string<char> instead of std::string
347  std::string tname = m_dictSvc->load_type(id).Name(Reflex::SCOPED);
348  // handle '> >'. order MATTERS!
349  ::cxx_replace(tname, "basic_string<char> >", "string>");
350  ::cxx_replace(tname, "basic_string<char>", "string");
351  m_clidMap[id] = tname;
352 
353  // Try to make sure the BIB is initialized.
354  std::string bibname = "SG::BaseInfo<" + tname + ">";
355  TClass* bibcl = gROOT->GetClass (bibname.c_str());
356  if (bibcl) {
357  TMethod* m = bibcl->GetMethodAny ("baseinfo");
358  if (m) {
359  TMethodCall call (m);
360  call.Execute();
361  }
362  }
363 
364  return m_clidMap[id].c_str();
365  }
366 
367  private:
370  {
371  const std::string moduleName = "AthenaPython.Bindings";
372  PyObject* module = PyImport_ImportModule
373  ( const_cast<char*>(moduleName.c_str()) );
374  if ( !module || !PyModule_Check(module) ) {
375  std::cerr << "SG::PyProxyDict WARNING: could not import module '"
376  << moduleName << "' !\n";
377  Py_XDECREF(module);
378  m_aliases = PyDict_New();
379  return m_aliases;
380  }
381 
382  m_aliases = PyDict_GetItemString(PyModule_GetDict(module),
383  (char*)"_clid_typename_aliases");
384  // borrowed ref. so we increment it
385  Py_XINCREF(m_aliases);
386  // don't need this one anymore
387  Py_DECREF(module);
388 
389  if ( !m_aliases ) {
390  std::cerr << "SG::PyProxyDict WARNING: could not retrieve the "
391  << "dictionary of aliases from '"
392  << moduleName << "' !\n";
393  Py_XDECREF(m_aliases);
394  m_aliases = PyDict_New();
395  }
396  return m_aliases;
397  }
398 
399  }; //> class PyProxyMgr
400 
405  struct PyProxyDict
406  {
408 
410  m_sgSvc( sgSvc )
411  {}
412 
414  {}
415 
416  inline
417  PyObject* proxy( PyObject* pyclid, PyObject* pykey )
418  {
419  _SGPY_MSG("PyProxyDict::proxy(...)...");
420 
421  PyObject* pyproxy = NULL;
422 
423  unsigned int id_tmp = 0;
424  if (!PyArg_Parse( pyclid, "I", &id_tmp )) {
425  return nullptr;
426  }
427 
428  CLID id = id_tmp;
429  std::string skey;
430  if (pykey != Py_None) {
431  skey = RootUtils::PyGetString (pykey).first;
432  }
433  SG::DataProxy* proxy = skey.empty()
434  ? m_sgSvc->proxy(id)
435  : m_sgSvc->proxy(id, skey);
436  _SGPY_MSG("PyProxyDict::proxy("
437  << (proxy ? proxy->clID() : id) << ", "
438  << (proxy ? proxy->name() : skey) << ")...");
439  pyproxy = TPython::CPPInstance_FromVoidPtr((void*)proxy,
440  "SG::DataProxy");
441 
442  _SGPY_MSG("PyProxyDict::proxy(...)... [done]");
443  return pyproxy;
444  }
445 
446  inline
447  PyObject* newPyDataObject( const char* klass, void* addr=0 )
448  {
449  PyObject* obj = NULL;
450  if ( !(obj = TPython::CPPInstance_FromVoidPtr((void*)addr, klass)) ) {
451  throw CPyCppyy::PyException();
452  }
453  return obj;
454  }
455  }; //> struct PyProxyDict
456 
457 
458 
459  // -------- inlines ----------------------
460  inline
461  PyProxyDict*
463  {
464  SG::PyProxyDict* p = 0;
466  if ( i == m_proxyMap.end() ) {
467  m_proxyMap[sg] = p = new SG::PyProxyDict(sg);
468  } else {
469  p = i->second;
470  }
471  return p;
472  }
473 
474 
475 } //> end namespace SG
476 
477 #endif // !STOREGATEBINDINGS_SGPYDATAMODEL_H
478 
xAOD::iterator
JetConstituentVector::iterator iterator
Definition: JetConstituentVector.cxx:68
RootUtils::PyException
CPyCppyy::PyException PyException
Definition: Utility.h:24
SG::IRegisterTransient
Interface for registering a transient object in t2p map.
Definition: IRegisterTransient.h:28
SG::PyDataBucket::tinfo
virtual const std::type_info & tinfo() const override
Return the type_info for the stored object.
Definition: SgPyDataModel.h:158
SG::PyDataBucket::lock
virtual void lock() override
If the held object derives from ILockable, call lock() on it.
Definition: SgPyDataModel.cxx:147
SG::PyDataBucket::relinquish
virtual void relinquish() override
Give up ownership of the DataBucket contents.
Definition: SgPyDataModel.h:166
SG::PyProxyDict::newPyDataObject
PyObject * newPyDataObject(const char *klass, void *addr=0)
Definition: SgPyDataModel.h:447
python.SystemOfUnits.s
int s
Definition: SystemOfUnits.py:131
SG::PyDataBucket::m_bib
const SG::BaseInfoBase * m_bib
pointer to the SG::BaseInfoBase structure holding the converter functions for objects held by StoreGa...
Definition: SgPyDataModel.h:185
python.SystemOfUnits.m
int m
Definition: SystemOfUnits.py:91
python.PerfMonSerializer.p
def p
Definition: PerfMonSerializer.py:743
SG::PyProxyDict::m_sgSvc
StoreGateSvc * m_sgSvc
Definition: SgPyDataModel.h:407
SG
Forward declaration.
Definition: CaloCellPacker_400_500.h:32
SG::PyDataBucket::m_clid
CLID m_clid
The class ID of the wrapped object.
Definition: SgPyDataModel.h:181
IDictLoaderSvc
Definition: IDictLoaderSvc.h:37
DataBucketBase
A non-templated base class for DataBucket, allows to access the transient object address as a void*.
Definition: DataBucketBase.h:24
DataBucketBase.h
StdJOSetup.sgSvc
sgSvc
Definition: StdJOSetup.py:37
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
SG::PyProxyMgr::m_dictSvc
IDictLoaderSvc * m_dictSvc
Definition: SgPyDataModel.h:193
ParticleTest.tp
tp
Definition: ParticleTest.py:25
SG::PyDataBucket::object
virtual void * object() override
Return the held object.
Definition: SgPyDataModel.h:115
SG::PyProxyMgr::m_aliases
PyObject * m_aliases
a dictionary of "typedef'ed typename" -> "typename"
Definition: SgPyDataModel.h:196
SG::PyProxyMgr::m_clidMap
ClidMap_t m_clidMap
Definition: SgPyDataModel.h:206
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::PyDataBucket::m_pyObj
PyObject * m_pyObj
Pointer to the held pyroot object (or 'regular' PyObject)
Definition: SgPyDataModel.h:178
SG::PyProxyDict::proxy
PyObject * proxy(PyObject *pyclid, PyObject *pykey)
Definition: SgPyDataModel.h:417
ATLAS_NO_CHECK_FILE_THREAD_SAFETY
ATLAS_NO_CHECK_FILE_THREAD_SAFETY
Definition: SgPyDataModel.h:45
SG::PyProxyMgr::pyproxy
SG::PyProxyDict * pyproxy(StoreGateSvc *sg)
Definition: SgPyDataModel.h:462
klass
This class describe the base functionalities of a HypoTool used by the ComboAlg.
SG::PyProxyMgr::clid
CLID clid(PyObject *tp)
Definition: SgPyDataModel.h:274
RootUtils::PyGILStateEnsure
Definition: PyAthenaGILStateEnsure.h:20
CPPInstance_ASVOIDPTR
#define CPPInstance_ASVOIDPTR(o)
Definition: AthenaPyRoot.h:20
BchCleanup.mgr
mgr
Definition: BchCleanup.py:294
python.PyAthena.module
module
Definition: PyAthena.py:134
SG::PyProxyMgr::PyProxyMap_t
std::unordered_map< StoreGateSvc *, SG::PyProxyDict * > PyProxyMap_t
Definition: SgPyDataModel.h:201
StoreGateSvc
The Athena Transient Store API.
Definition: StoreGateSvc.h:128
IDictLoaderSvc::load_type
virtual const RootType load_type(const std::string &type_name, bool recursive=false)=0
retrieve a RootType by name (auto)loading the dictionary by any necessary means.
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
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
lumiFormat.i
int i
Definition: lumiFormat.py:92
BaseInfo.h
Provide an interface for finding inheritance information at run time.
Reflex::SCOPED
@ SCOPED
Definition: RootType.h:27
SG::PyProxyMgr::pytp
PyObject * pytp(CLID clid)
returns a borrowed reference
Definition: SgPyDataModel.h:265
COOLRates.alias
alias
Definition: COOLRates.py:1172
SG::PyProxyDict::~PyProxyDict
~PyProxyDict()
Definition: SgPyDataModel.h:413
CLID
uint32_t CLID
The Class ID type.
Definition: Event/xAOD/xAODCore/xAODCore/ClassID_traits.h:47
SG::PyProxyMgr::importDictAliases
PyObject * importDictAliases()
import the dictionary of aliases from a well known location
Definition: SgPyDataModel.h:369
TScopeAdapter::Name
std::string Name(unsigned int mod=Reflex::SCOPED) const
Definition: RootType.cxx:607
SG::PyProxyMgr::m_proxyMap
PyProxyMap_t m_proxyMap
Definition: SgPyDataModel.h:202
SG::PyProxyMgr::clid_from_tid
CLID clid_from_tid(PyObject *tp)
Definition: SgPyDataModel.h:306
SG::PyProxyMgr::m_clidSvc
IClassIDSvc * m_clidSvc
Definition: SgPyDataModel.h:194
SG::PyProxyMgr::m_clids
PyObject * m_clids
a dictionary of 'typename' -> CLID (and reverse CLID->'typename')
Definition: SgPyDataModel.h:199
SG::PyProxyMgr::PyProxyMgr
PyProxyMgr()
Definition: SgPyDataModel.cxx:165
StoreGateSvc::proxy
virtual SG::DataProxy * proxy(const void *const pTransient) const override final
get proxy for a given data object address in memory
id
SG::auxid_t id
Definition: Control/AthContainers/Root/debug.cxx:191
_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
SG::PyProxyDict::PyProxyDict
PyProxyDict(StoreGateSvc *sgSvc)
Definition: SgPyDataModel.h:409
CxxUtils::to
CONT to(RANGE &&r)
Definition: ranges.h:32
SG::PyDataBucket::~PyDataBucket
virtual ~PyDataBucket() override
Destructor.
Definition: SgPyDataModel.h:107
RootUtils::PyGetString
std::pair< std::string, bool > PyGetString(PyObject *s)
Convert python string -> C++ string for py2 and py3.
Definition: PyGetString.h:40
SG::PyDataBucket
Concrete DataBucket that holds the object via a void* and uses the Root dictionary to do conversions.
Definition: SgPyDataModel.h:93
IDictLoaderSvc.h
SG::PyProxyMgr::pytp
PyObject * pytp(PyObject *clid)
returns a borrowed reference
Definition: SgPyDataModel.h:238
trigbs_pickEvents.cnt
cnt
Definition: trigbs_pickEvents.py:71
SG::BaseInfoBase
The non-template portion of the BaseInfo implementation.
Definition: Control/AthenaKernel/AthenaKernel/BaseInfo.h:451
PyAthenaGILStateEnsure.h
PyCLID
const CLID PyCLID
Definition: SgPyDataModel.cxx:13
SG::PyProxyMgr::instance
static PyProxyMgr & instance()
Definition: SgPyDataModel.h:209
SG::BaseInfoBase::typeinfo
const std::type_info & typeinfo() const
Return the std::type_info for this class.
Definition: BaseInfo.cxx:151
checker_macros.h
Define macros for attributes used to control the static checker.
StoreGateBindingsDict.h
python.PyAthena.obj
obj
Definition: PyAthena.py:135
SG::DataProxy
Definition: DataProxy.h:44
AthenaPyRoot.h
SG::PyProxyMgr::~PyProxyMgr
~PyProxyMgr()
Definition: SgPyDataModel.cxx:189
PyObject
_object PyObject
Definition: IPyComponent.h:26
SG::PyProxyMgr::ClidMap_t
std::unordered_map< CLID, std::string > ClidMap_t
a dictionary of CLID -> reflex typename
Definition: SgPyDataModel.h:205
SG::PyDataBucket::PyDataBucket
PyDataBucket(PyObject *obj, CLID clid)
Constructor.
Definition: SgPyDataModel.cxx:43
SG::PyProxyMgr
A helper class to manage accesses to PyProxies.
Definition: SgPyDataModel.h:192