ATLAS Offline Software
PyComponentMgr.cxx
Go to the documentation of this file.
1 
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 
16 #include "AthenaKernel/CLASS_DEF.h"
17 CLASS_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 
35 using namespace PyAthena;
36 
37 namespace {
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.
54 void 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
72 PyComponentMgr::PyComponentMgr( const std::string& name,
73  ISvcLocator* pSvcLocator ) :
74  base_class ( name, pSvcLocator ),
75  m_dict ( nullptr ),
76  m_components( )
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() ) {
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
115 StatusCode
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 
197 StatusCode
199 {
200  ATH_MSG_INFO("Finalizing " << name() << "...");
201  return StatusCode::SUCCESS;
202 }
203 
204 
205 PyObject*
207 {
208  const std::string& name = cppComp->name();
209 
210  // Check if we already have instantiated that component
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 }
xAOD::iterator
JetConstituentVector::iterator iterator
Definition: JetConstituentVector.cxx:68
RootUtils::PyException
CPyCppyy::PyException PyException
Definition: Utility.h:24
plotting.yearwise_luminosity_vs_mu.comp
comp
Definition: yearwise_luminosity_vs_mu.py:24
PyAthena::PyComponentMgr::pyObject
virtual PyObject * pyObject(IPyComponent *component) override
Retrieve a python object from the python world.
Definition: PyComponentMgr.cxx:206
ATH_MSG_INFO
#define ATH_MSG_INFO(x)
Definition: AthMsgStreamMacros.h:31
PyAthena::PyComponentMgr::m_components
PyComponents_t m_components
A fast look-up hash-map for python components { 'name' : PyObject* } PyObject* is NULL if not yet ins...
Definition: PyComponentMgr.h:90
PyAthena::PyComponentMgr::finalize
virtual StatusCode finalize() override
Definition: PyComponentMgr.cxx:198
ATH_MSG_VERBOSE
#define ATH_MSG_VERBOSE(x)
Definition: AthMsgStreamMacros.h:28
master.gen
gen
Definition: master.py:32
RootUtils::PyGILStateEnsure
Definition: PyAthenaGILStateEnsure.h:20
PyAthena::PyComponentMgr::m_dict
PyObject * m_dict
The dictionary of python components' description It should be of the form: { 'name' : { 'package' : "...
Definition: PyComponentMgr.h:82
python.PyAthena.module
module
Definition: PyAthena.py:134
PyAthena::PyComponentMgr::PyComponentMgr
PyComponentMgr()=delete
IPyComponent::typeName
virtual const char * typeName() const =0
return the std::type_info name of the underlying py-component This is used by concrete implementation...
ATH_MSG_ERROR
#define ATH_MSG_ERROR(x)
Definition: AthMsgStreamMacros.h:33
PyAthena::PyComponentMgr::initialize
virtual StatusCode initialize() override
Gaudi Service Implementation.
Definition: PyComponentMgr.cxx:116
fillPileUpNoiseLumi.next
next
Definition: fillPileUpNoiseLumi.py:52
lumiFormat.i
int i
Definition: lumiFormat.py:92
ret
T ret(T t)
Definition: rootspy.cxx:260
endmsg
#define endmsg
Definition: AnalysisConfig_Ntuple.cxx:63
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
ATH_MSG_DEBUG
#define ATH_MSG_DEBUG(x)
Definition: AthMsgStreamMacros.h:29
IPyComponent::setPyAttr
virtual bool setPyAttr(PyObject *pyobj)=0
attach the C++ component to its python cousin
IPyComponent
Definition: IPyComponent.h:31
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:195
IPyComponent.h
PyAthena
Definition: IPyComponent.h:28
ATH_MSG_WARNING
#define ATH_MSG_WARNING(x)
Definition: AthMsgStreamMacros.h:32
CLASS_DEF
#define CLASS_DEF(NAME, CID, VERSION)
associate a clid and a version to a type eg
Definition: Control/AthenaKernel/AthenaKernel/CLASS_DEF.h:64
PyAthenaGILStateEnsure.h
PyAthena::PyComponentMgr::~PyComponentMgr
virtual ~PyComponentMgr()
Destructor:
Definition: PyComponentMgr.cxx:87
PyObject
_object PyObject
Definition: IPyComponent.h:26
CLASS_DEF.h
macros to associate a CLID to a type