Open TheGreatMonkey opened 6 years ago
After some experimentation I discovered that the problem is pybind cannot see the import form inside the scope of the function.
import foo
def bar():
foo.func()
will always cause an error_already_set exception.
However;
def bar():
import foo
foo.func()
will function properly.
I can't reproduce this:
>>> import f
>>> def bar():
... f.func()
...
>>> bar()
hello
>>> class c(object):
... def func2(self):
... f.func()
...
>>> c().func2()
hello
(This is a module consisting of a simple: m.def("func", []() { py::print("hello"); });
).
I suspect the error is coming from something else being done inside the foo.func
call.
Thanks for the response. I see that I accidently left out some key details, my apologies.
I'm building a c++ application which uses two way communication with python. The application is built using the Urho3D game engine.
I have MyAppPython.h which looks like this
#pragma once
#include <Urho3D/Input/InputEvents.h>
#ifdef _DEBUG
#undef _DEBUG
#include <python.h>
#define _DEBUG
#else
#include <python.h>
#endif
#include <embed.h>
#include <fstream>
#include <iostream>
namespace py = pybind11;
using namespace py::literals;
std::string test_py =
(
R"(
class PyTest(object) :
def __init__(self, message = "Test Object initialized") :
import MyApp
self.message = message
self.iter = 0
message = self.message
MyApp.DebugInfo(message)
def start(self) :
import MyApp
self.message = "Starting Python Object"
self.iter = self.iter + 1
message = self.message
MyApp.DebugInfo(message)
def update(self) :
import MyApp
self.message = "Python Object Update Cycle:"
self.iter = self.iter + 1
iterstr = str(self.iter)
message = self.message + iterstr
MyApp.DebugInfo(message)
Object = PyTest()
)"
);
std::string Import_File(std::string filename_)
{
std::ifstream ifs_(filename_);
std::string content_((std::istreambuf_iterator<char>(ifs_)),
(std::istreambuf_iterator<char>()));
return content_;
}
void DebugInfo(std::string string_)
{
String LogMessage_(string_.c_str());
URHO3D_LOGINFO(LogMessage_);
}
PYBIND11_EMBEDDED_MODULE(MyApp, m) {
// `m` is a `py::module` which is used to bind functions and classes
m.def("DebugInfo", &DebugInfo);
}
and a from my main.cpp it's called using
py::scoped_interpreter guard{};
auto locals = py::dict();
py::exec(test_py, py::globals(), locals);
thing_ = locals["Object "].cast<py::object>();
thing_.attr("start")();
The above code is with the work around with the import call inside the scope of each method. If the import call is at the global level it will throw the error_already_set error against cast.h line 1926, or if it's in the init method; against eval.h line 49
If the problem isn't readily apparent from my code, I can port it to an clean Urho3D repo for testing and sharing.
@TheGreatMonkey I seem to running into the same thing -- did you ever figure this out?
Well, I figured this out. Basically, you don't actually want a blank py::dict
for locals
; it will cause all sorts of problems. If you look embedding sample code from the docs or the tests, the locals
value always copies the global scope.
See:
Your options are to copy the global scope, or, in this case, simply don't pass in a locals
py::scoped_interpreter guard{};
auto globals = py::globals();
py::exec(test_py, globals);
thing_ = globals["Object"].cast<py::object>();
thing_.attr("start")();
It looks like that in the case of top-level code (not inside any module), the globals
variable holds the values at this scope.
I've written a c++ binding that allows me to post a simple message such that:
when called in python will post "message".
However, if I call the same embedded function from within a class method such as:
I will get an exception error_already_set. This occurs if I call the function from python or from c++. I'm not sure if this is a pybind11 bug, or if I just missed something.