Skip to content
Snippets Groups Projects
Forked from atlas / athena
84261 commits behind the upstream repository.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
PyComponentMgr.cxx 6.22 KiB
///////////////////////// -*- C++ -*- /////////////////////////////

/*
  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
*/

// PyComponentMgr.cxx 
// Implementation file for class PyComponentMgr
// Author: S.Binet<binet@cern.ch>
// Modified: Wim Lavrijsen <WLavrijsen@lbl.gov>
/////////////////////////////////////////////////////////////////// 

// Python includes
#include "Python.h"

#include "AthenaKernel/CLASS_DEF.h"
CLASS_DEF (PyObject, 72785480, 1)

// AthenaPython includes
#include "PyComponentMgr.h"
#include "RootUtils/PyAthenaGILStateEnsure.h"

// STL includes

// FrameWork includes
#include "Gaudi/Property.h"
#include "AthenaPython/IPyComponent.h"

// PyROOT includes
#include "TPyException.h"
#include "TPython.h"

using namespace PyAthena;

// Constructors
////////////////
PyComponentMgr::PyComponentMgr( const std::string& name, 
				ISvcLocator* pSvcLocator ) : 
  base_class  ( name,     pSvcLocator ),
  m_dict      ( nullptr ),
  m_components(   )
{
  //
  // Property declaration
  // 
  //declareProperty( "Property", m_nProperty, "descr" );

}

// Destructor
///////////////
PyComponentMgr::~PyComponentMgr()
{ 
  ATH_MSG_DEBUG("Calling destructor");

  // we own the repository of instances' description
  if ( m_dict ) {
    RootUtils::PyGILStateEnsure ensure;
    Py_DECREF( m_dict );
    m_dict = nullptr;
  }

  // as well as the one of corresponding instances
  if ( m_components.size() ) {
    RootUtils::PyGILStateEnsure ensure;
    for ( PyComponents_t::iterator
            i    = m_components.begin(),
            iEnd = m_components.end();
          i != iEnd;
          ++i ) {
      ATH_MSG_VERBOSE("__del__(" << i->first << ")...");
      Py_XDECREF( i->second );
    }
  }

}

// Athena Algorithm's Hooks
////////////////////////////
StatusCode 
PyComponentMgr::initialize()
{
  ATH_MSG_INFO("Initializing " << name() << "...");

  const std::string pyModuleName = "AthenaPython.Configurables";

  // import the module holding the dictionary of component instances
  RootUtils::PyGILStateEnsure ensure;
  ATH_MSG_DEBUG("Importing module [" << pyModuleName << "]...");
  PyObject* module = PyImport_ImportModule( const_cast<char*>(pyModuleName.c_str()) );
  if ( !module || !PyModule_Check( module ) ) {
    ATH_MSG_ERROR("Could not import [" << pyModuleName << "] !!");
    Py_XDECREF (module);
    throw PyROOT::TPyException();
  }

  const std::string pyClassName = "PyComponents";
  PyObject* pyClass = 0;
  pyClass = PyDict_GetItemString( PyModule_GetDict( module ),
				  const_cast<char*>( pyClassName.c_str() ) );
  
  // borrowed ref. so ->increment
  Py_XINCREF( pyClass );

  if ( !pyClass ) {
    ATH_MSG_ERROR("Could not retrieve class [" << pyClassName
                  << "] from module [" << pyModuleName << "] !!");
    Py_XDECREF(pyClass);
    Py_DECREF (module);
    return StatusCode::FAILURE;
  }

  m_dict = PyObject_GetAttrString( pyClass,
				   const_cast<char*>( "instances" ) );
  if ( !m_dict || !PyDict_Check( m_dict ) ) {
    ATH_MSG_ERROR("Could not retrieve attribute [instances] from class ["
                  << pyClassName << "] !!");
    Py_DECREF (pyClass);
    Py_DECREF (module);
    return StatusCode::FAILURE;
  }
  Py_DECREF(pyClass);

  // install the fancy attribute getter for PyComponents to automatically
  // retrieve an initialized component.
  // see bug #46668
  {
    PyObject* fancy_attr = 0;
    fancy_attr = PyDict_GetItemString(PyModule_GetDict(module),
				      (char*)"_install_fancy_attrs");
    // borrowed ref => increment
    Py_XINCREF(fancy_attr);
    if (!fancy_attr) {
      PyErr_Clear();
      ATH_MSG_INFO
        ("could not retrieve function [_install_fancy_attrs] from module ["
         << pyModuleName << "]");
    } else {
      PyObject* ret = PyObject_CallFunction(fancy_attr, NULL);
      Py_XDECREF(ret);
    }
    Py_XDECREF(fancy_attr);
  }
  Py_DECREF (module);

  // make sure the PyROOT fixes are installed
  const std::string pyRootFixes = "RootUtils.PyROOTFixes";
  ATH_MSG_DEBUG("Importing module [" << pyRootFixes << "]...");
  PyObject *rootFixes = 0;
  rootFixes = PyImport_ImportModule(const_cast<char*>(pyRootFixes.c_str()));
  if ( !rootFixes || !PyModule_Check( rootFixes ) ) {
    PyErr_Print();
    PyErr_Clear();
    ATH_MSG_WARNING("Could not import [" << pyRootFixes << "] !!"
                    << endmsg
                    << "Some problem may appear with some C++->python binded classes (YMMV)");
  }
  Py_XDECREF(rootFixes);
  return StatusCode::SUCCESS;
}

StatusCode 
PyComponentMgr::finalize()
{
  ATH_MSG_INFO("Finalizing " << name() << "...");
  return StatusCode::SUCCESS;
}


PyObject*
PyComponentMgr::pyObject( IPyComponent* cppComp )
{
  const std::string& name = cppComp->name();

  // Check if we already have instantiated that component
  RootUtils::PyGILStateEnsure ensure;
  PyComponents_t::iterator comp = m_components.find( name );
  if ( comp != m_components.end() && comp->second ) {
    Py_INCREF (comp->second);
    return comp->second;
  }
  m_components[name] = static_cast<PyObject*>( NULL );

  // Check we have a valid dict.
  if ( !m_dict || !PyDict_Check(m_dict) ) {
    ATH_MSG_ERROR("Invalid repository of Python components !!");
    Py_INCREF( Py_None );
    return Py_None;
  }
  
  PyObject* o = PyDict_GetItemString( m_dict, 
				      const_cast<char*>(name.c_str()) );
  // borrowed ref -> incr
  Py_XINCREF( o );

  // Check we have a valid dict.
  if ( !o ) {
    ATH_MSG_ERROR("No such python component [" << name
                  << "] or invalid item !!");
    Py_XDECREF( o );
    throw PyROOT::TPyException();
  }
  m_components[name] = o;

  /// Hum... remove ? or not ?
  /// leaving the objects on the python side may allow easier retrieval from
  /// py-components...
//   // remove the object from the python dict
//   if ( PyDict_DelItemString( m_dict, const_cast<char*>(name.c_str()) ) ) {
//     ATH_MSG_ERROR("Could not remove [" << name << "] from PyComponents.instances !!");
//     throw PyROOT::TPyException();
//   }

  // now we tell the PyObject which C++ object it is the cousin of.
  if ( !cppComp->setPyAttr(o) ) {
    ATH_MSG_WARNING("Could not connect the C++ object ["
                    << cppComp->typeName() << "/" << cppComp->name()
                    << "] with its python cousin !");
  }

  Py_INCREF(o);
  return o;
}