Skip to content
Snippets Groups Projects
  • Michael Kuron's avatar
    Fix building on some compilers · f26d67e2
    Michael Kuron authored and Christian Godenschwager's avatar Christian Godenschwager committed
    - Need to find Boost library path before we search for the Python libraries, even if we don't need any other libraries
    - std::experimental::filesystem::current_path is broken in libstdc++ 5.4 and lower
    f26d67e2
Forked from waLBerla / waLBerla
2009 commits behind the upstream repository.
PythonCallback.cpp 6.71 KiB
//======================================================================================================================
//
//  This file is part of waLBerla. waLBerla is free software: you can
//  redistribute it and/or modify it under the terms of the GNU General Public
//  License as published by the Free Software Foundation, either version 3 of
//  the License, or (at your option) any later version.
//
//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
//  for more details.
//
//  You should have received a copy of the GNU General Public License along
//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
//
//! \file PythonCallback.cpp
//! \ingroup python_coupling
//! \author Martin Bauer <martin.bauer@fau.de>
//
//======================================================================================================================

#include "PythonCallback.h"
#include "PythonWrapper.h"
#include "DictWrapper.h"

#ifdef WALBERLA_BUILD_WITH_PYTHON

#include "Manager.h"
#include "core/Abort.h"
#include "core/logging/Logging.h"
#include "helper/ExceptionHandling.h"
#include "core/Filesystem.h"

#if defined(__GLIBCXX__) && __GLIBCXX__ <= 20160609
#define CURRENT_PATH_WORKAROUND
#include <unistd.h>
#include <errno.h>
#endif

namespace walberla {
namespace python_coupling {

   static boost::python::object importModuleOrFileInternal( const std::string & fileOrModuleName, const std::vector< std::string > & argv )
   {
      auto manager = python_coupling::Manager::instance();
      manager->triggerInitialization();

      namespace bp = boost::python;

      std::string moduleName = fileOrModuleName;

      std::stringstream code;
      code << "import sys" << "\n" ;
      code << "sys.argv = [ ";
      for( auto argStrIt = argv.begin(); argStrIt != argv.end(); ++argStrIt )
         code << "'" << *argStrIt  << "',";
      code << "] \n";

      filesystem::path path ( fileOrModuleName );
#ifdef CURRENT_PATH_WORKAROUND
      // workaround for double free in filesystem::current_path in libstdc++ 5.4 and lower
      size_t cwd_size = 16;
      char * cwd_buf = (char*) std::malloc(cwd_size * sizeof(char));

      while( getcwd( cwd_buf, cwd_size ) == NULL )
      {
         if (errno == ERANGE)
         {
            cwd_size *= 2;
            cwd_buf = (char*) std::realloc( cwd_buf, cwd_size * sizeof(char) );
         }
         else
         {
            python_coupling::terminateOnPythonException( std::string("Could not determine working directory") );
         }
      }

      std::string cwd(cwd_buf);
      std::free(cwd_buf);
#else
      filesystem::path cwd = filesystem::current_path();
#endif
      path = filesystem::absolute( path, cwd );
      if ( path.extension() == ".py" )
      {
         moduleName = path.stem().string();


         if ( ! path.parent_path().empty() )  {
            std::string p = filesystem::canonical(path.parent_path(), cwd).string();
            code << "sys.path.append( r'" << p << "')" << "\n";
         }
      }
      bp::exec( code.str().c_str(), bp::import("__main__").attr("__dict__") );

      try {
         return bp::import( moduleName.c_str() );
      }
      catch ( bp::error_already_set & ) {
         python_coupling::terminateOnPythonException( std::string("Python Error while loading ") + fileOrModuleName );
         return boost::python::object();
      }
   }

   void importModuleOrFile( const std::string & fileOrModuleName, const std::vector< std::string > & argv )
   {
      importModuleOrFileInternal( fileOrModuleName, argv );
   }

   // initializes invalid/empty callback
   PythonCallback::PythonCallback()
      : exposedVars_( new DictWrapper() ), callbackDict_( new DictWrapper() )
   {
      Manager::instance()->triggerInitialization();
      callbackDict_->dict() = boost::python::dict();
   }


   PythonCallback::PythonCallback( const std::string & fileOrModuleName, const std::string & functionName, const std::vector<std::string> & argv )
      : functionName_( functionName ), exposedVars_( new DictWrapper() ), callbackDict_( new DictWrapper() )
   {
      Manager::instance()->triggerInitialization();

      using namespace boost::python;

      // Add empty callbacks module
      importModuleOrFileInternal( fileOrModuleName, argv );
      object callbackModule = import( "walberla_cpp.callbacks");

      callbackDict_->dict() = extract<dict>( callbackModule.attr( "__dict__" ) );
   }


   PythonCallback::PythonCallback( const std::string & functionName )
      : functionName_( functionName ), exposedVars_( new DictWrapper() ), callbackDict_( new DictWrapper() )
   {
      Manager::instance()->triggerInitialization();

      using namespace boost::python;
      // Add empty callbacks module
      object callbackModule = import( "walberla_cpp.callbacks");

      callbackDict_->dict() = extract<dict>( callbackModule.attr( "__dict__" ) );
   }

   bool PythonCallback::isCallable() const
   {
      return callbackDict_->dict().has_key( functionName_ );
   }

   void PythonCallback::operator() ()
   {
      if ( ! isCallable() )
         WALBERLA_ABORT_NO_DEBUG_INFO( "Could not call python function '" << functionName_ << "'. " <<
                                        "Did you forget to set the callback function?" );

      namespace bp = boost::python;

      try
      {
         if ( exposedVars_->dict().has_key("returnValue"))
            bp::api::delitem( exposedVars_->dict(), "returnValue" );

         bp::object function = callbackDict_->dict()[ functionName_ ];

         bp::object returnVal;
         returnVal = function( *bp::tuple(), **(exposedVars_->dict() ) );

         exposedVars_->dict()["returnValue"] = returnVal;
      }
      catch ( bp::error_already_set & ) {
         python_coupling::terminateOnPythonException( std::string("Error while running Python function ") + functionName_ );
      }
   }


} // namespace python_coupling
} // namespace walberla

#else

namespace walberla {
namespace python_coupling {

   void importModuleOrFile( const std::string &, const std::vector< std::string > & ) { }

   PythonCallback::PythonCallback( const std::string & functionName )
   : functionName_( functionName ), exposedVars_( new DictWrapper() ), callbackDict_( new DictWrapper() ) {}


   PythonCallback::PythonCallback( const std::string & functionName, const std::string & , const std::vector<std::string> &  )
   : functionName_( functionName ), exposedVars_( new DictWrapper() ), callbackDict_( new DictWrapper() ) {}

   bool PythonCallback::isCallable() const { return false; }
   void PythonCallback::operator() ()      {  }


} // namespace python_coupling
} // namespace walberla



#endif