ATLAS Offline Software
Loading...
Searching...
No Matches
StoreGatePyExt.cxx
Go to the documentation of this file.
1
2
3/*
4 Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
5*/
6
8
9#include "Python.h"
10
12
13// Framework includes
14#include "GaudiKernel/Bootstrap.h"
15#include "GaudiKernel/DataObject.h"
16#include "GaudiKernel/IClassIDSvc.h"
20
21// SGTools includes
23
24// ROOT includes
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
44namespace {
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
60PyObject* 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
100PyObject*
102 PyObject* tp, PyObject* pykey )
103{
105 return retrieveObjectFromStore(store,tp,pykey);
106}
107
108PyObject*
110 PyObject* tp, PyObject* pykey )
111{
112 void* res = 0;
113 PyObject* objProxy = NULL;
114
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
345PyObject*
347 PyObject* tp, PyObject* pykey)
348{
350 return py_sg_contains(store,tp,pykey);
351}
352
353PyObject*
355 PyObject* tp, PyObject* pykey)
356{
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
413PyObject*
415 PyObject* pykey)
416{
418 return py_sg_getitem(store,pykey);
419}
420
421PyObject*
423 PyObject* pykey)
424{
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
457void
462
463PyObject*
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
475PyObject*
477 PyObject* obj,
478 PyObject* pykey,
479 bool allowMods /*= true*/,
480 bool resetOnly /*= true*/,
481 bool noHist /*= false*/)
482{
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//
#define CPPInstance_ASVOIDPTR(o)
Provide an interface for finding inheritance information at run time.
a traits class that associates a CLID to a type T It also detects whether T inherits from Gaudi DataO...
uint32_t CLID
The Class ID type.
_object PyObject
std::pair< std::vector< unsigned int >, bool > res
static Double_t sc
TTypeAdapter RootType
Definition RootType.h:211
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
A non-templated base class for DataBucket, allows to access the transient object address as a void*.
virtual void * object()=0
T * cast(SG::IRegisterTransient *irt=0, bool isConst=true)
Return the contents of the DataBucket, converted to type T.
Concrete DataBucket that holds the object via a void* and uses the Root dictionary to do conversions.
virtual void * object() override
Return the held object.
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.
The Athena Transient Store API.
void clearProxyPayload(SG::DataProxy *)
use to reset a proxy (clearing the data object it contains) Unlike DataProxy::reset this method corre...
static TScopeAdapter ByNameNoQuiet(const std::string &name, Bool_t load=kTRUE)
Definition RootType.cxx:586
TClass * Class() const
Definition RootType.h:183
PyObject * retrieveObjectFromStore(PyObject *storeGateSvc, PyObject *tp, PyObject *key)
PyObject * py_sg_getitem(PyObject *storeGateSvc, PyObject *key)
PyObject * py_sg_contains(PyObject *storeGateSvc, PyObject *tp, PyObject *key)
PyObject * recordObjectToStore(StoreGateSvc *, PyObject *, PyObject *, bool, bool, bool)
record object to storegate
void py_sg_clearProxyPayload(StoreGateSvc *self, SG::DataProxy *)
std::pair< std::string, bool > PyGetString(PyObject *s)
Convert python string -> C++ string for py2 and py3.
Definition PyGetString.h:40
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)
A helper class to manage accesses to PyProxies.
static PyProxyMgr & instance()
CLID clid(PyObject *tp)
const char * load_type(CLID id)
ensure everything has been loaded for the clid id (classid, reflex dict, baseinfobase,...
SG::PyProxyDict * pyproxy(StoreGateSvc *sg)
PyObject * pyclid(PyObject *tp)
returns a borrowed reference
IClassIDSvc * m_clidSvc
PyObject * pytp(PyObject *clid)
returns a borrowed reference