ATLAS Offline Software
Loading...
Searching...
No Matches
PyComponentMgr.cxx
Go to the documentation of this file.
1
2
3/*
4 Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
5*/
6
7// PyComponentMgr.cxx
8// Implementation file for class PyComponentMgr
9// Author: S.Binet<binet@cern.ch>
10// Modified: Wim Lavrijsen <WLavrijsen@lbl.gov>
12
13// Python includes
14#include "Python.h"
15
17CLASS_DEF (PyObject, 72785480, 1)
18
19// AthenaPython includes
20#include "PyComponentMgr.h"
22
23// STL includes
24
25// FrameWork includes
26#include "Gaudi/Property.h"
28
29// PyROOT includes
30#include "CPyCppyy/PyException.h"
31#include "TPython.h"
32#include "TROOT.h"
33#include "TClassGenerator.h"
34
35using namespace PyAthena;
36
37namespace {
38
39// Remove the TPyClassGenerator from the list of class generators.
40// TPyClassGenerator makes classes defined in Python accessible to ROOT.
41// We don't rely on this at all in Athena. Further, in MT jobs,
42// we can get deadlocks. TClass::GetClass holds the ROOT internal
43// mutexes, and if it ends up calling TPyClassGenerator, then the
44// generator will end up acquiring the Python GIL.
45// On the other hand, Python-based algorithms will hold the GIL.
46// If they then use PyROOT (or do anything which could trigger I/O),
47// then they can try to acquire the ROOT internal locks.
48// Since we have the same locks being acquired in different orders
49// in different threads, we can deadlock.
50//
51// Ideally, TClass::GetClass should probably drop the ROOT locks
52// before calling a class generator. But since we don't actually
53// use this functionality on Athena, just suppress it.
54void removePyGen ()
55{
56 TCollection* gens = gROOT->GetListOfClassGenerators();
57 TIter next(gens);
58 TClassGenerator *gen;
59 while( (gen = (TClassGenerator*) next()) ) {
60 if (System::typeinfoName (typeid (*gen)) == "TPyClassGenerator") {
61 gens->Remove (gen);
62 break;
63 }
64 }
65}
66} // anonymous namespace
67
68
69
70// Constructors
72PyComponentMgr::PyComponentMgr( const std::string& name,
73 ISvcLocator* pSvcLocator ) :
74 base_class ( name, pSvcLocator ),
75 m_dict ( nullptr ),
77{
78 //
79 // Property declaration
80 //
81 //declareProperty( "Property", m_nProperty, "descr" );
82
83}
84
85// Destructor
88{
89 ATH_MSG_DEBUG("Calling destructor");
90
91 // we own the repository of instances' description
92 if ( m_dict ) {
94 Py_DECREF( m_dict );
95 m_dict = nullptr;
96 }
97
98 // as well as the one of corresponding instances
99 if ( m_components.size() ) {
101 for ( PyComponents_t::iterator
102 i = m_components.begin(),
103 iEnd = m_components.end();
104 i != iEnd;
105 ++i ) {
106 ATH_MSG_VERBOSE("__del__(" << i->first << ")...");
107 Py_XDECREF( i->second );
108 }
109 }
110
111}
112
113// Athena Algorithm's Hooks
115StatusCode
117{
118 ATH_MSG_INFO("Initializing " << name() << "...");
119
120 const std::string pyModuleName = "AthenaPython.Configurables";
121
122 // import the module holding the dictionary of component instances
124 ATH_MSG_DEBUG("Importing module [" << pyModuleName << "]...");
125 PyObject* module = PyImport_ImportModule( const_cast<char*>(pyModuleName.c_str()) );
126 if ( !module || !PyModule_Check( module ) ) {
127 ATH_MSG_ERROR("Could not import [" << pyModuleName << "] !!");
128 Py_XDECREF (module);
129 throw CPyCppyy::PyException();
130 }
131
132 const std::string pyClassName = "PyComponents";
133 PyObject* pyClass = 0;
134 pyClass = PyDict_GetItemString( PyModule_GetDict( module ),
135 const_cast<char*>( pyClassName.c_str() ) );
136
137 // borrowed ref. so ->increment
138 Py_XINCREF( pyClass );
139
140 if ( !pyClass ) {
141 ATH_MSG_ERROR("Could not retrieve class [" << pyClassName
142 << "] from module [" << pyModuleName << "] !!");
143 Py_XDECREF(pyClass);
144 Py_DECREF (module);
145 return StatusCode::FAILURE;
146 }
147
148 m_dict = PyObject_GetAttrString( pyClass,
149 const_cast<char*>( "instances" ) );
150 if ( !m_dict || !PyDict_Check( m_dict ) ) {
151 ATH_MSG_ERROR("Could not retrieve attribute [instances] from class ["
152 << pyClassName << "] !!");
153 Py_DECREF (pyClass);
154 Py_DECREF (module);
155 return StatusCode::FAILURE;
156 }
157 Py_DECREF(pyClass);
158
159 // install the fancy attribute getter for PyComponents to automatically
160 // retrieve an initialized component.
161 // see bug #46668
162 {
163 PyObject* fancy_attr = 0;
164 fancy_attr = PyDict_GetItemString(PyModule_GetDict(module),
165 (char*)"_install_fancy_attrs");
166 // borrowed ref => increment
167 Py_XINCREF(fancy_attr);
168 if (!fancy_attr) {
169 PyErr_Clear();
171 ("could not retrieve function [_install_fancy_attrs] from module ["
172 << pyModuleName << "]");
173 } else {
174 PyObject* ret = PyObject_CallFunction(fancy_attr, NULL);
175 Py_XDECREF(ret);
176 }
177 Py_XDECREF(fancy_attr);
178 }
179 Py_DECREF (module);
180
181 // make sure the PyROOT fixes are installed
182 const std::string pyRootFixes = "RootUtils.PyROOTFixes";
183 ATH_MSG_DEBUG("Importing module [" << pyRootFixes << "]...");
184 PyObject *rootFixes = 0;
185 rootFixes = PyImport_ImportModule(const_cast<char*>(pyRootFixes.c_str()));
186 if ( !rootFixes || !PyModule_Check( rootFixes ) ) {
187 PyErr_Print();
188 PyErr_Clear();
189 ATH_MSG_WARNING("Could not import [" << pyRootFixes << "] !!"
190 << endmsg
191 << "Some problem may appear with some C++->python binded classes (YMMV)");
192 }
193 Py_XDECREF(rootFixes);
194 return StatusCode::SUCCESS;
195}
196
197StatusCode
199{
200 ATH_MSG_INFO("Finalizing " << name() << "...");
201 return StatusCode::SUCCESS;
202}
203
204
207{
208 const std::string& name = cppComp->name();
209
210 // Check if we already have instantiated that component
212 PyComponents_t::iterator comp = m_components.find( name );
213 if ( comp != m_components.end() && comp->second ) {
214 Py_INCREF (comp->second);
215 return comp->second;
216 }
217 m_components[name] = static_cast<PyObject*>( NULL );
218
219 // Check we have a valid dict.
220 if ( !m_dict || !PyDict_Check(m_dict) ) {
221 ATH_MSG_ERROR("Invalid repository of Python components !!");
222 Py_INCREF( Py_None );
223 return Py_None;
224 }
225
226 PyObject* o = PyDict_GetItemString( m_dict,
227 const_cast<char*>(name.c_str()) );
228 // borrowed ref -> incr
229 Py_XINCREF( o );
230
231 // Check we have a valid dict.
232 if ( !o ) {
233 ATH_MSG_ERROR("No such python component [" << name
234 << "] or invalid item !!");
235 Py_XDECREF( o );
236 throw CPyCppyy::PyException();
237 }
238 m_components[name] = o;
239
243// // remove the object from the python dict
244// if ( PyDict_DelItemString( m_dict, const_cast<char*>(name.c_str()) ) ) {
245// ATH_MSG_ERROR("Could not remove [" << name << "] from PyComponents.instances !!");
246// throw PyROOT::TPyException();
247// }
248
249 // now we tell the PyObject which C++ object it is the cousin of.
250 if ( !cppComp->setPyAttr(o) ) {
251 ATH_MSG_WARNING("Could not connect the C++ object ["
252 << cppComp->typeName() << "/" << cppComp->name()
253 << "] with its python cousin !");
254 }
255
256 removePyGen();
257
258 Py_INCREF(o);
259 return o;
260}
#define endmsg
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
macros to associate a CLID to a type
#define CLASS_DEF(NAME, CID, VERSION)
associate a clid and a version to a type eg
_object PyObject
virtual const char * typeName() const =0
return the std::type_info name of the underlying py-component This is used by concrete implementation...
virtual bool setPyAttr(PyObject *pyobj)=0
attach the C++ component to its python cousin
PyComponents_t m_components
A fast look-up hash-map for python components { 'name' : PyObject* } PyObject* is NULL if not yet ins...
virtual StatusCode finalize() override
virtual StatusCode initialize() override
Gaudi Service Implementation.
virtual PyObject * pyObject(IPyComponent *component) override
Retrieve a python object from the python world.
virtual ~PyComponentMgr()
Destructor:
PyObject * m_dict
The dictionary of python components' description It should be of the form: { 'name' : { 'package' : "...