ATLAS Offline Software
Loading...
Searching...
No Matches
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
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#include "CPyCppyy/PyException.h"
40
41#include <unordered_map>
42
43// Called from python, so only excuted single-threaded (GIL).
46
47// fwd declares
48namespace SG { struct PyProxyMgr; }
49namespace SG { struct PyProxyDict; }
50
51namespace {
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
78namespace 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
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
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
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;
465 PyProxyMap_t::iterator i = m_proxyMap.find(sg);
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
#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.
CONT to(RANGE &&r)
Definition ranges.h:39
@ 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')