ATLAS Offline Software
Loading...
Searching...
No Matches
SgPyDataModel.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
3*/
4
5
6#include "SgPyDataModel.h"
7#include "GaudiKernel/ServiceHandle.h"
8
9// Called from python, so only excuted single-threaded (GIL).
12
13const CLID PyCLID = 72785480;
14
15namespace {
16
17
18TClass* objectIsA (PyObject* obj)
19{
20 TClass* cls = nullptr;
21 PyObject* attr = PyObject_GetAttrString ((PyObject*)Py_TYPE(obj), "__cpp_name__");
22 if (attr) {
23 const char* s = PyUnicode_AsUTF8AndSize (attr, nullptr);
24 if (s) {
25 if (*s == '<') ++s;
26 if (strncmp (s, "ROOT.", 5) == 0)
27 s += 5;
28 if (strncmp (s, "cppyy.gbl.", 10) == 0)
29 s += 10;
30 cls = TClass::GetClass (s);
31 }
32 Py_XDECREF (attr);
33 }
34 PyErr_Clear();
35 return cls;
36}
37
38
39}
40
41namespace SG {
42
44 CLID clid ) :
46 m_pyObj( pyObj ),
47 m_clid ( clid ),
48 m_bib ( SG::BaseInfoBase::find(clid) )
49 {
50 // prevent Python from sweeping the rug under our feet
51 Py_INCREF( pyObj );
52 }
53
55 IRegisterTransient* /*itr*/,
56 bool /*isConst*/ )
57 {
59 // if requested type is same than myself ==> no conversion needed
60 if ( clid == m_clid ) {
61 return clid == PyCLID
62 ? m_pyObj
64 }
65 void* address = (m_clid == PyCLID)
66 ? (void*)m_pyObj
68
69 // try SG-based conversion functions
70 {
71 void* o = m_bib ? m_bib->cast(address, clid) : 0;
72 if ( o ) { return o; }
73 }
74
75 // try PyRoot based ones
76 PyObject* pytp = PyProxyMgr::instance().pytp(clid);
77 if ( !pytp ) {
78 PyErr_Format( PyExc_TypeError, "actual type of CLID %lu unknown",
79 (long unsigned int)clid );
80 return 0;
81 }
82
83 // this will be a conversion for a class instance only (see below:
84 // verified that only a CPPInstance is expected), so bind with cast
85 std::string pytpstr = RootUtils::PyGetString(pytp).first;
86 TClass* cls = TClass::GetClass (pytpstr.c_str());
87 if (!cls) {
88 PyErr_Format( PyExc_TypeError, "Can't find TClass for `%s'",
89 pytpstr.c_str() );
90 return 0;
91 }
92 TClass* act_class = cls->GetActualClass (address);
93 PyObject* value = TPython::CPPInstance_FromVoidPtr (address, act_class->GetName());
94
95 if ( value && TPython::CPPInstance_Check(value) ) {
96 return CPPInstance_ASVOIDPTR(value);
97 }
98 Py_XDECREF(value);
99 throw CPyCppyy::PyException();
100 return 0;
101 }
102
103 void* PyDataBucket::cast( const std::type_info& tinfo,
104 IRegisterTransient* /*itr*/,
105 bool /*isConst*/)
106 {
108 // if regular PyObject, meaningless
109 if ( m_clid == PyCLID ) {
110 return 0;
111 }
112
113 // if requested type is same than myself ==> no conversion needed
114 TClass* tcls = objectIsA (m_pyObj);
115 if ( tcls && (tinfo == *(tcls->GetTypeInfo())) ) {
117 }
118 void* address = CPPInstance_ASVOIDPTR(m_pyObj);
119
120 // try SG-based conversion functions
121 {
122 void* o = m_bib ? m_bib->cast(address, tinfo) : 0;
123 if ( o ) { return o; }
124 }
125
126 // this will be a conversion for a class instance only (see below:
127 // verified that only a CPPInstance is expected), so bind with cast
128 TClass* clsnew = TClass::GetClass (tinfo);
129 if (!clsnew) {
130 PyErr_SetString
131 ( PyExc_RuntimeError,
132 "SG::PyDataBucket::cast() can't find TClass" );
133 return 0;
134 }
135 TClass* act_class = clsnew->GetActualClass (address);
136 PyObject* value = TPython::CPPInstance_FromVoidPtr (address, act_class->GetName());
137 PyErr_Clear();
138
139 if ( value && TPython::CPPInstance_Check(value) ) {
140 return CPPInstance_ASVOIDPTR(value);
141 }
142 Py_XDECREF(value);
143 //throw PyROOT::TPyException();
144 return 0;
145 }
146
148 {
150 if (!m_pyObj) return;
151 if (!PyObject_HasAttrString (m_pyObj, "lock"))
152 return;
153 PyObject* lock = PyObject_GetAttrString (m_pyObj, "lock");
154 if (!lock) return;
155 if (PyCallable_Check (lock)) {
156 PyObject* ret = PyObject_CallObject (lock, NULL);
157 Py_DECREF (ret);
158 }
159 Py_DECREF (lock);
160 }
161
163 // PyProxyMgr
164
166 {
168 m_clids = PyDict_New();
169 m_clidSvc = 0;
170 {
171 ServiceHandle<IClassIDSvc> svc("ClassIDSvc", "SgPyDataModel");
172 if ( !svc.retrieve().isSuccess()) {
173 throw std::runtime_error
174 ("SG::PyProxyMgr: Could not retrieve ClassIDSvc");
175 }
176 m_clidSvc = svc.operator->();
177 }
178 m_dictSvc = 0;
179 {
180 ServiceHandle<IDictLoaderSvc> svc("AthDictLoaderSvc", "SgPyDataModel");
181 if ( !svc.retrieve().isSuccess()) {
182 throw std::runtime_error
183 ("SG::PyProxyMgr: Could not retrieve AthDictLoaderSvc");
184 }
185 m_dictSvc = svc.operator->();
186 }
187 }
188
190 {
191 // Don't do this if don't have a valid thread state.
192 // (With py3, the interpreter gets shut down before global dtors run...)
193 if (_PyThreadState_UncheckedGet())
194 {
195 Py_DECREF(m_aliases);
196 Py_DECREF(m_clids);
197 }
198 // delete the proxy dicts...
199 for ( PyProxyMap_t::iterator
200 i = m_proxyMap.begin(),
201 iEnd = m_proxyMap.end();
202 i != iEnd;
203 ++i ) {
204 delete i->second; i->second = 0;
205 }
206 }
207
208} //< end namespace SG
#define CPPInstance_ASVOIDPTR(o)
uint32_t CLID
The Class ID type.
_object PyObject
const CLID PyCLID
Define macros for attributes used to control the static checker.
#define ATLAS_NO_CHECK_FILE_THREAD_SAFETY
The non-template portion of the BaseInfo implementation.
Interface for registering a transient object in t2p map.
PyDataBucket(PyObject *obj, CLID clid)
Constructor.
PyObject * m_pyObj
Pointer to the held pyroot object (or 'regular' PyObject)
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 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...
std::string find(const std::string &s)
return a remapped string
Definition hcg.cxx:138
std::pair< std::string, bool > PyGetString(PyObject *s)
Convert python string -> C++ string for py2 and py3.
Definition PyGetString.h:40
Forward declaration.
PyObject * importDictAliases()
import the dictionary of aliases from a well known location
static PyProxyMgr & instance()
PyObject * m_aliases
a dictionary of "typedef'ed typename" -> "typename"
PyProxyMap_t m_proxyMap
IDictLoaderSvc * m_dictSvc
IClassIDSvc * m_clidSvc
PyObject * pytp(PyObject *clid)
returns a borrowed reference
PyObject * m_clids
a dictionary of 'typename' -> CLID (and reverse CLID->'typename')