ATLAS Offline Software
Loading...
Searching...
No Matches
SgPyDataModel.h
Go to the documentation of this file.
1// -*- C++ -*-
2
3/*
4 Copyright (C) 2002-2026 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
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"
23
25
26extern 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#ifdef ATLAS_HAVE_CPYCPPYY
40# include "CPyCppyy/PyException.h"
41#endif // ATLAS_HAVE_CPYCPPYY
42
43#include <unordered_map>
44#include <stdexcept>
45
46// Called from python, so only excuted single-threaded (GIL).
49
50// fwd declares
51namespace SG { struct PyProxyMgr; }
52namespace SG { struct PyProxyDict; }
53
54namespace {
57 std::string::size_type
58 cxx_replace(std::string& s,
59 const std::string& from,
60 const std::string& to)
61 {
62 std::string::size_type cnt(std::string::npos);
63
64 if(from != to && !from.empty()) {
65 std::string::size_type pos1(0);
66 std::string::size_type pos2(0);
67 const std::string::size_type from_len(from.size());
68 const std::string::size_type to_len(to.size());
69 cnt = 0;
70
71 while((pos1 = s.find(from, pos2)) != std::string::npos) {
72 s.replace(pos1, from_len, to);
73 pos2 = pos1 + to_len;
74 ++cnt;
75 }
76 }
77 return cnt;
78 }
79}
80
81namespace SG {
82
96 {
97 public:
104 CLID clid );
105
106
110 virtual ~PyDataBucket() override {
112 Py_DECREF( m_pyObj );
113 }
114
118 virtual void* object() override
119 {
120 return m_clid != PyCLID
122 : m_pyObj;
123 }
124
125
127
128
137 virtual void* cast (CLID clid, IRegisterTransient* itr = 0,
138 bool isConst = true) override;
139
140
149 virtual void* cast (const std::type_info& tinfo,
150 IRegisterTransient* itr = 0,
151 bool isConst = true) override;
152
156 virtual const CLID& clID() const override { return m_clid; }
157
161 virtual const std::type_info& tinfo() const override
162 {
163 return m_bib->typeinfo();
164 }
165
169 virtual void relinquish() override
170 {
171 Py_DECREF( m_pyObj );
172 }
173
177 virtual void lock() override;
178
179 private:
182
185
189 };
190
195 {
197 IClassIDSvc* m_clidSvc;
200
203
204 typedef std::unordered_map<StoreGateSvc*,SG::PyProxyDict*> PyProxyMap_t;
206
208 typedef std::unordered_map<CLID, std::string> ClidMap_t;
210
211 static
213 { static PyProxyMgr mgr; return mgr; }
214
215 PyProxyMgr();
216 ~PyProxyMgr();
217
219
221 inline
223 {
224 PyObject* clid = PyDict_GetItem(m_clids, tp);
225 if ( NULL == clid ) {
226 const CLID id = this->clid(tp);
227 if ( id == CLID_NULL ) {
228 return NULL;
229 }
230 clid = PyLong_FromLong(id);
231 PyDict_SetItem(m_clids, tp, clid);
232 // add reverse look-up entry too
233 PyDict_SetItem(m_clids, clid, tp);
234 //Py_INCREF(clid);
235 }
236 return clid;
237 }
238
240 inline
242 {
243 PyObject* tp = PyDict_GetItem(m_clids, clid);
244 if ( NULL == tp ) {
245 std::string cpp_tp;
246 if (!m_clidSvc->getTypeNameOfID(PyLong_AsUnsignedLong(clid),
247 cpp_tp).isSuccess()) {
248 return NULL;
249 }
250 PyObject* alias = PyDict_GetItemString(m_aliases, cpp_tp.c_str());
251 if ( alias ) {
252 tp = alias;
253 } else {
254 tp = PyUnicode_FromString(cpp_tp.c_str());
255 }
256 PyDict_SetItem(m_clids, clid, tp);
257 // reverse look-up
258 PyDict_SetItem(m_clids, tp, clid);
259 if (!alias) {
260 Py_DECREF(tp);
261 }
262 }
263 return tp;
264 }
265
267 inline
269 {
270 PyObject* pyclid = PyLong_FromLong(clid);
271 PyObject* o = this->pytp(pyclid);
272 Py_DECREF(pyclid);
273 return o;
274 }
275
276 inline
278 {
279 CLID id = CLID_NULL;
280 // FIXME: get rid of this massaging when/if ROOT finally
281 // standardize on keeping the std:: namespace...
282 std::string tpstr = RootUtils::PyGetString(tp).first;
283 std::string tn;
284 {
285 // Protect against data race inside TClassEdit.
286 // https://github.com/root-project/root/issues/10353
287 // Should be fixed in root 6.26.02.
288 R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
289 tn = TClassEdit::ShortType(tpstr.c_str(),
290 TClassEdit::kDropAllDefault);
291 }
292 m_clidSvc->getIDOfTypeName(tn, id).ignore();
293 if ( id == CLID_NULL ) {
294 // try an alias...
295 PyObject* alias = PyDict_GetItemString(m_aliases, tn.c_str());
296 if ( alias ){
297 std::string aliasstr = RootUtils::PyGetString(alias).first;
298 m_clidSvc->getIDOfTypeName(aliasstr, id).ignore();
299 }
300 }
301 if (id == CLID_NULL) {
302 // then try a type-id name...
303 return this->clid_from_tid (tp);
304 }
305 return id;
306 }
307
308 inline
310 {
311 CLID id = CLID_NULL;
312 // FIXME: get rid of this massaging when/if ROOT finally
313 // standardize on keeping the std:: namespace...
314 std::string tpstr = RootUtils::PyGetString(tp).first;
315 std::string tn;
316 {
317 // Protect against data race inside TClassEdit.
318 // https://github.com/root-project/root/issues/10353
319 // Should be fixed in root 6.26.02.
320 R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
321 tn = TClassEdit::ShortType(tpstr.c_str(),
322 TClassEdit::kDropAllDefault);
323 }
324 m_clidSvc->getIDOfTypeInfoName(tn, id).ignore();
325 if ( id == CLID_NULL ) {
326 // try an alias...
327 PyObject* alias = PyDict_GetItemString(m_aliases, tn.c_str());
328 if ( alias ){
329 std::string aliasstr = RootUtils::PyGetString(alias).first;
330 m_clidSvc->getIDOfTypeInfoName(aliasstr,
331 id).ignore();
332 }
333 }
334 return id;
335 }
336
341 inline
342 const char* load_type(CLID id)
343 {
344 typedef ClidMap_t::iterator Itr_t;
345 Itr_t i = m_clidMap.find(id);
346 if (i != m_clidMap.end()) {
347 return i->second.c_str();
348 }
349 // Reflex now returns std::basic_string<char> instead of std::string
350 std::string tname = m_dictSvc->load_type(id).Name(Reflex::SCOPED);
351 // handle '> >'. order MATTERS!
352 ::cxx_replace(tname, "basic_string<char> >", "string>");
353 ::cxx_replace(tname, "basic_string<char>", "string");
354 m_clidMap[id] = tname;
355
356 // Try to make sure the BIB is initialized.
357 std::string bibname = "SG::BaseInfo<" + tname + ">";
358 TClass* bibcl = gROOT->GetClass (bibname.c_str());
359 if (bibcl) {
360 TMethod* m = bibcl->GetMethodAny ("baseinfo");
361 if (m) {
362 TMethodCall call (m);
363 call.Execute();
364 }
365 }
366
367 return m_clidMap[id].c_str();
368 }
369
370 private:
373 {
374 const std::string moduleName = "AthenaPython.Bindings";
375 PyObject* module = PyImport_ImportModule
376 ( const_cast<char*>(moduleName.c_str()) );
377 if ( !module || !PyModule_Check(module) ) {
378 std::cerr << "SG::PyProxyDict WARNING: could not import module '"
379 << moduleName << "' !\n";
380 Py_XDECREF(module);
381 m_aliases = PyDict_New();
382 return m_aliases;
383 }
384
385 m_aliases = PyDict_GetItemString(PyModule_GetDict(module),
386 (char*)"_clid_typename_aliases");
387 // borrowed ref. so we increment it
388 Py_XINCREF(m_aliases);
389 // don't need this one anymore
390 Py_DECREF(module);
391
392 if ( !m_aliases ) {
393 std::cerr << "SG::PyProxyDict WARNING: could not retrieve the "
394 << "dictionary of aliases from '"
395 << moduleName << "' !\n";
396 Py_XDECREF(m_aliases);
397 m_aliases = PyDict_New();
398 }
399 return m_aliases;
400 }
401
402 }; //> class PyProxyMgr
403
409 {
411
413 m_sgSvc( sgSvc )
414 {}
415
417 {}
418
419 inline
420 PyObject* proxy( PyObject* pyclid, PyObject* pykey )
421 {
422 _SGPY_MSG("PyProxyDict::proxy(...)...");
423
424 PyObject* pyproxy = NULL;
425
426 unsigned int id_tmp = 0;
427 if (!PyArg_Parse( pyclid, "I", &id_tmp )) {
428 return nullptr;
429 }
430
431 CLID id = id_tmp;
432 std::string skey;
433 if (pykey != Py_None) {
434 skey = RootUtils::PyGetString (pykey).first;
435 }
436 SG::DataProxy* proxy = skey.empty()
437 ? m_sgSvc->proxy(id)
438 : m_sgSvc->proxy(id, skey);
439 _SGPY_MSG("PyProxyDict::proxy("
440 << (proxy ? proxy->clID() : id) << ", "
441 << (proxy ? proxy->name() : skey) << ")...");
442 pyproxy = TPython::CPPInstance_FromVoidPtr((void*)proxy,
443 "SG::DataProxy");
444
445 _SGPY_MSG("PyProxyDict::proxy(...)... [done]");
446 return pyproxy;
447 }
448
449 inline
450 PyObject* newPyDataObject( const char* klass, void* addr=0 )
451 {
452 PyObject* obj = NULL;
453 if ( !(obj = TPython::CPPInstance_FromVoidPtr((void*)addr, klass)) ) {
454#ifdef ATLAS_HAVE_CPYCPPYY
455 throw CPyCppyy::PyException();
456#else
457 throw std::runtime_error ("Python exception");
458#endif // ATLAS_HAVE_CPYCPPYY
459 }
460 return obj;
461 }
462 }; //> struct PyProxyDict
463
464
465
466 // -------- inlines ----------------------
467 inline
468 PyProxyDict*
470 {
471 SG::PyProxyDict* p = 0;
472 PyProxyMap_t::iterator i = m_proxyMap.find(sg);
473 if ( i == m_proxyMap.end() ) {
474 m_proxyMap[sg] = p = new SG::PyProxyDict(sg);
475 } else {
476 p = i->second;
477 }
478 return p;
479 }
480
481
482} //> end namespace SG
483
484#endif // !STOREGATEBINDINGS_SGPYDATAMODEL_H
#define CPPInstance_ASVOIDPTR(o)
Provide an interface for finding inheritance information at run time.
uint32_t CLID
The Class ID type.
_object PyObject
const CLID PyCLID
#define _SGPY_MSG(x)
Definition SgPyMsg.h:22
Define macros for attributes used to control the static checker.
#define ATLAS_NO_CHECK_FILE_THREAD_SAFETY
T * cast(SG::IRegisterTransient *irt=0, bool isConst=true)
Return the contents of the DataBucket, converted to type T.
a simple interface to interact with the Reflex dictionaries and abstract/workaround a few ROOT bugs.
The non-template portion of the BaseInfo implementation.
Interface for registering a transient object in t2p map.
virtual ~PyDataBucket() override
Destructor.
PyDataBucket(PyObject *obj, CLID clid)
Constructor.
virtual void * object() override
Return the held object.
PyObject * m_pyObj
Pointer to the held pyroot object (or 'regular' PyObject).
virtual const CLID & clID() const override
Retrieve reference to class definition structure.
virtual void * cast(CLID clid, IRegisterTransient *itr=0, bool isConst=true) override
Return the contents of the DataBucket, converted to type given by clid.
virtual void lock() override
If the held object derives from ILockable, call lock() on it.
CLID m_clid
The class ID of the wrapped object.
virtual void relinquish() override
Give up ownership of the DataBucket contents.
virtual const std::type_info & tinfo() const override
Return the type_info for the stored object.
const SG::BaseInfoBase * m_bib
pointer to the SG::BaseInfoBase structure holding the converter functions for objects held by StoreGa...
The Athena Transient Store API.
@ SCOPED
Definition RootType.h:27
std::pair< std::string, bool > PyGetString(PyObject *s)
Convert python string -> C++ string for py2 and py3.
Definition PyGetString.h:40
Forward declaration.
a python front-end to the IProxyDict interface PyProxyDict encapsulates getting python objects from t...
PyObject * proxy(PyObject *pyclid, PyObject *pykey)
PyObject * newPyDataObject(const char *klass, void *addr=0)
PyProxyDict(StoreGateSvc *sgSvc)
StoreGateSvc * m_sgSvc
CLID clid_from_tid(PyObject *tp)
PyObject * importDictAliases()
import the dictionary of aliases from a well known location
static PyProxyMgr & instance()
PyObject * pytp(CLID clid)
returns a borrowed reference
std::unordered_map< CLID, std::string > ClidMap_t
a dictionary of CLID -> reflex typename
PyObject * m_aliases
a dictionary of "typedef'ed typename" -> "typename"
PyProxyMap_t m_proxyMap
CLID clid(PyObject *tp)
ClidMap_t m_clidMap
const char * load_type(CLID id)
ensure everything has been loaded for the clid id (classid, reflex dict, baseinfobase,...
SG::PyProxyDict * pyproxy(StoreGateSvc *sg)
IDictLoaderSvc * m_dictSvc
std::unordered_map< StoreGateSvc *, SG::PyProxyDict * > PyProxyMap_t
PyObject * pyclid(PyObject *tp)
returns a borrowed reference
IClassIDSvc * m_clidSvc
PyObject * pytp(PyObject *clid)
returns a borrowed reference
PyObject * m_clids
a dictionary of 'typename' -> CLID (and reverse CLID->'typename')