-
- 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