Re: [C++-sig] Creating a python class from c++
James Healey wrote:
Can any body tell me how you create a python class from within c++?
so in my .py file I have
class MyClass: def f(self): return 'hello world'
And from my c++ code i want to be able to do....
x = new MyClass x.f()
namespace bpl = boost::python; bpl::dict global; bpl::exec_file("your_file.py", global, global); // load the definition bpl::object my_class = global["MyClass"]; // extract the new type bpl::object my_instance = my_class(); // instantiate it bpl::object retn = my_instance.attr("f")(); // call it std::string value = bpl::extract<std::string>(retn); // extract return value
HTH, Stefan
I dont seem to have the exec_file command in my current boost build, I'm using 1.33.1 which was downloaded via the boost website. Send instant messages to your online friends http://uk.messenger.yahoo.com
James Healey wrote:
James Healey wrote:
Can any body tell me how you create a python class from within c++?
so in my .py file I have
class MyClass: def f(self): return 'hello world'
And from my c++ code i want to be able to do....
x = new MyClass x.f() namespace bpl = boost::python; bpl::dict global; bpl::exec_file("your_file.py", global, global); // load the definition bpl::object my_class = global["MyClass"]; // extract the new type bpl::object my_instance = my_class(); // instantiate it bpl::object retn = my_instance.attr("f")(); // call it std::string value = bpl::extract<std::string>(retn); // extract return value
HTH, Stefan
I dont seem to have the exec_file command in my current boost build, I'm using 1.33.1 which was downloaded via the boost website.
True, it has been added to boost.python sometime 2005, but unfortunately didn't make it into the 1.33 release. If you can't wait a current snapshot, you can take a verbatim copy of the function: // Execute python source code from file filename. // global and local are the global and local scopes respectively, // used during execution. object BOOST_PYTHON_DECL exec_file(str filename, object global, object local) { // should be 'char const *' but older python versions don't use 'const' yet. char *f = python::extract<char *>(filename); // Let python open the file to avoid potential binary incompatibilities. PyObject *pyfile = PyFile_FromString(f, "r"); if (!pyfile) throw std::invalid_argument(std::string(f) + " : no such file"); python::handle<> file(pyfile); PyObject* result = PyRun_File(PyFile_AsFile(file.get()), f, Py_file_input, global.ptr(), local.ptr()); if (!result) throw_error_already_set(); return object(detail::new_reference(result)); } HTH, Stefan -- ...ich hab' noch einen Koffer in Berlin...
True, it has been added to boost.python sometime 2005, but unfortunately didn't make it into the 1.33 release.
If you can't wait a current snapshot, you can take a verbatim copy of the function:
// Execute python source code from file filename. // global and local are the global and local scopes respectively, // used during execution. object BOOST_PYTHON_DECL exec_file(str filename, object global, object local) { // should be 'char const *' but older python versions don't use 'const' yet. char *f = python::extract<char *>(filename); // Let python open the file to avoid potential binary incompatibilities. PyObject *pyfile = PyFile_FromString(f, "r"); if (!pyfile) throw std::invalid_argument(std::string(f) + " : no such file"); python::handle<> file(pyfile); PyObject* result = PyRun_File(PyFile_AsFile(file.get()), f, Py_file_input, global.ptr(), local.ptr()); if (!result) throw_error_already_set(); return object(detail::new_reference(result)); }
HTH, Stefan
When I run the PyRun_File command result comes back as NULL. The only thing im doing before calling exec_file is Py_Initialize(); Regards. Jimmy. Send instant messages to your online friends http://uk.messenger.yahoo.com
James Healey wrote:
When I run the PyRun_File command result comes back as NULL. The only thing im doing before calling exec_file is Py_Initialize();
Check the exception python has set. It should tell you why the execution failed. (Does the file actually exist ? Can it be executed by python without error ?) boost.python should actually translate that exception into a C++ exception, which you can catch inside your C++ code... HTH, Stefan -- ...ich hab' noch einen Koffer in Berlin...
PyFile_AsFile returns a ptr to a file object so that's working as it should. And how do i check the exception? Do i need to do any thing with the dict that gets passed into exec_file? Regards. Jimmy. --- Stefan Seefeld <seefeld@sympatico.ca> wrote:
James Healey wrote:
When I run the PyRun_File command result comes back as NULL. The only thing im doing before calling exec_file is Py_Initialize();
Check the exception python has set. It should tell you why the execution failed. (Does the file actually exist ? Can it be executed by python without error ?)
boost.python should actually translate that exception into a C++ exception, which you can catch inside your C++ code...
HTH, Stefan
--
...ich hab' noch einen Koffer in Berlin... _______________________________________________ C++-sig mailing list C++-sig@python.org http://mail.python.org/mailman/listinfo/c++-sig
Send instant messages to your online friends http://uk.messenger.yahoo.com
James Healey wrote:
PyFile_AsFile returns a ptr to a file object so that's working as it should.
And how do i check the exception?
Do i need to do any thing with the dict that gets passed into exec_file?
No. If the PyFile_AsFile call returns NULL you know something is wrong, and an exception is set (i.e. PyErr_Occured() returns a non-null pointer to the exception object. However, all that should be dealt with by boost.python already. Don't you see a C++ exception fly by ? FWIW, you may want to look at the exec.cpp unit test in a boost CVS snapshot. It should test all of the above. HTH, Stefan -- ...ich hab' noch einen Koffer in Berlin...
Stefan Seefeld wrote:
James Healey wrote:
PyFile_AsFile returns a ptr to a file object so that's working as it should.
And how do i check the exception?
Do i need to do any thing with the dict that gets passed into exec_file?
No. If the PyFile_AsFile call returns NULL you know something is wrong, and an exception is set (i.e.
I meant PyRun_File here. Sorry for the confusion. Regards, Stefan -- ...ich hab' noch einen Koffer in Berlin...
It seem to have some thing to do with what's in the .py file im loading, if i have it empty or do some thing simple like var = 1, then it loads fine. I did have: import MyTest MyTest() and call a test function, my c++ code looks like: BOOST_PYTHON_MODULE( MyTest ) { boost::python::def( "TestFunc", &TestFunc ); } And just before I call the exec_file function i have this... Py_Initialize(); initMyTest(); --- Stefan Seefeld <seefeld@sympatico.ca> wrote:
PyFile_AsFile returns a ptr to a file object so
James Healey wrote: that's
working as it should.
And how do i check the exception?
Do i need to do any thing with the dict that gets passed into exec_file?
No. If the PyFile_AsFile call returns NULL you know something is wrong, and an exception is set (i.e. PyErr_Occured() returns a non-null pointer to the exception object.
However, all that should be dealt with by boost.python already. Don't you see a C++ exception fly by ?
FWIW, you may want to look at the exec.cpp unit test in a boost CVS snapshot. It should test all of the above.
HTH, Stefan
--
...ich hab' noch einen Koffer in Berlin... _______________________________________________ C++-sig mailing list C++-sig@python.org http://mail.python.org/mailman/listinfo/c++-sig
Send instant messages to your online friends http://uk.messenger.yahoo.com
Ok got the exception catch working and printing out the error and stack trace. ImportError: __import__ not found Is the error that I am getting. --- Stefan Seefeld <seefeld@sympatico.ca> wrote:
James Healey wrote:
I did have:
import MyTest
MyTest()
This doesn't look correct. You import a module, which can't have a call operator defined. Only objects can.
Regards, Stefan
--
...ich hab' noch einen Koffer in Berlin... _______________________________________________ C++-sig mailing list C++-sig@python.org http://mail.python.org/mailman/listinfo/c++-sig
___________________________________________________________ The all-new Yahoo! Mail goes wherever you go - free your email address from your Internet provider. http://uk.docs.yahoo.com/nowyoucan.html
James Healey wrote:
Ok got the exception catch working and printing out the error and stack trace.
ImportError: __import__ not found
Is the error that I am getting.
OK, I think I now understand what's going on: The code I proposed looked like this: namespace bpl = boost::python; bpl::dict global; bpl::exec_file("your_file.py", global, global); Note that 'global' is completely empty, as thus is the environment seen by the script. It appears we need to import the '__main__' module and take its dictionary instead, as that provides some objects we are using: bpl::object main = bpl::import("__main__"); bpl::dict global = main.attr("__dict__"); bpl::exec_file("your_file.py", global, global); That should work. Regards, Stefan PS: And in case you are going to ask where the 'import' function is coming from: I added it at the same time as the exec / exec_file functions, so boost 1.33 doesn't have it yet. Here is a copy: namespace boost { namespace python { object BOOST_PYTHON_DECL import(str name) { // should be 'char const *' but older python versions don't use 'const' yet. char *n = extract<char *>(name); handle<> module(borrowed(PyImport_AddModule(n))); return object(module); } } } -- ...ich hab' noch einen Koffer in Berlin...
I did ask a few posts ago if any thing should be done with the global variable, but you gave no answer. I also had these 2 lines before: // Retrieve the main module //boost::python::object main = boost::python::import("__main__"); // Retrieve the main module's namespace //boost::python::object global(main.attr("__dict__")); But it didnt compile because I never had the import function. Also this line: bpl::dict global = main.attr("__dict__"); doesnt compile and switched it with: boost::python::object global(main.attr("__dict__")); it gave the error: error C2440: 'initializing' : cannot convert from 'boost::python::api::object_attribute' to 'boost::python::dict' Constructor for class 'boost::python::dict' is declared 'explicit' Every thing works as asked in my original question. Thanks for all your help. ___________________________________________________________ Yahoo! Messenger - with free PC-PC calling and photo sharing. http://uk.messenger.yahoo.com
James Healey wrote:
Also this line: bpl::dict global = main.attr("__dict__");
doesnt compile and switched it with: boost::python::object global(main.attr("__dict__"));
it gave the error: error C2440: 'initializing' : cannot convert from 'boost::python::api::object_attribute' to 'boost::python::dict' Constructor for class 'boost::python::dict' is declared 'explicit'
Every thing works as asked in my original question.
Good ! Regards, Stefan -- ...ich hab' noch einen Koffer in Berlin...
If I have this: class Test { public: int iNumber; int getNumber(void) { return iNumber; } }; BOOST_PYTHON_MODULE( extended_test ) { boost::python::class_<Test>("Test") .def( "getNumber", &Test::getNumber ) ; } boost::python::object my_class = global["MyTest"]; How would I go about creating an instance of class Test and passing it to my python class instance? boost::python::object my_instance = my_class(test boost::python::object retn = my_instance.attr("TestFunc")(); Regards. Jimmy. ___________________________________________________________ The all-new Yahoo! Mail goes wherever you go - free your email address from your Internet provider. http://uk.docs.yahoo.com/nowyoucan.html
James Healey wrote:
If I have this:
class Test { public: int iNumber;
int getNumber(void) { return iNumber; } };
BOOST_PYTHON_MODULE( extended_test ) { boost::python::class_<Test>("Test") .def( "getNumber", &Test::getNumber ) ; }
boost::python::object my_class = global["MyTest"];
How would I go about creating an instance of class Test and passing it to my python class instance?
boost::python::object my_instance = my_class(test boost::python::object retn = my_instance.attr("TestFunc")();
I'm not sure what you are trying to accomplish. Are you asking how to detach the lifetime of the C++ Test instance from the lifetime of the Python Test instance ? In that case it seems most suitable to export a C++ function returning your (C++) Test instance, letting boost.python wrap the existing instance, instead of instantiating the wrapper with a copy of it. You may want to read the docs on call- / return-value policies, too. HTH, Stefan -- ...ich hab' noch einen Koffer in Berlin...
Well basically I want to be able to pass a c++ class to a pyhon class and have the python class call the c++ class methods. --- Stefan Seefeld <seefeld@sympatico.ca> wrote:
James Healey wrote:
If I have this:
class Test { public: int iNumber;
int getNumber(void) { return iNumber; } };
BOOST_PYTHON_MODULE( extended_test ) { boost::python::class_<Test>("Test") .def( "getNumber", &Test::getNumber ) ; }
boost::python::object my_class = global["MyTest"];
How would I go about creating an instance of class Test and passing it to my python class instance?
boost::python::object my_instance = my_class(test boost::python::object retn = my_instance.attr("TestFunc")();
I'm not sure what you are trying to accomplish. Are you asking how to detach the lifetime of the C++ Test instance from the lifetime of the Python Test instance ?
In that case it seems most suitable to export a C++ function returning your (C++) Test instance, letting boost.python wrap the existing instance, instead of instantiating the wrapper with a copy of it. You may want to read the docs on call- / return-value policies, too.
HTH, Stefan
--
...ich hab' noch einen Koffer in Berlin... _______________________________________________ C++-sig mailing list C++-sig@python.org http://mail.python.org/mailman/listinfo/c++-sig
Send instant messages to your online friends http://uk.messenger.yahoo.com
James Healey wrote:
Well basically I want to be able to pass a c++ class to a pyhon class and have the python class call the c++ class methods.
I take it you mean 'instance' whenever you say 'class', right ? Does what I suggest not work ? Regards, Stefan -- ...ich hab' noch einen Koffer in Berlin...
Im trying to call a python function which is not in a class: exec_file("Data\\Script\\Editor.py", global, global); boost::python::object func = global["OnClick"]; func(); But it's falling over when I call the object, global returns a valid object and if i look at func in the debugger it says it's a function. It falls over when calling: // This conversion *must not* be done in the same expression as // the call, because, in the special case where the result is a // reference a Python object which was created by converting a C++ // argument for passing to PyEval_CallFunction, its reference // count will be 2 until the end of the full expression containing // the conversion, and that interferes with dangling // pointer/reference detection. converter::return_from_python<R> converter; The ref count of func is 2 is that helps. ___________________________________________________________ Try the all-new Yahoo! Mail. "The New Version is radically easier to use" The Wall Street Journal http://uk.docs.yahoo.com/nowyoucan.html
James Healey wrote:
Im trying to call a python function which is not in a class:
exec_file("Data\\Script\\Editor.py", global, global);
boost::python::object func = global["OnClick"]; func();
But it's falling over when I call the object, global returns a valid object and if i look at func in the debugger it says it's a function.
It falls over when calling:
...what ?
// This conversion *must not* be done in the same expression as // the call, because, in the special case where the result is a // reference a Python object which was created by converting a C++ // argument for passing to PyEval_CallFunction, its reference // count will be 2 until the end of the full expression containing // the conversion, and that interferes with dangling // pointer/reference detection. converter::return_from_python<R> converter;
The ref count of func is 2 is that helps.
Sorry, I don't see any call here. Further, if you really expect anybody to help you please provide a minimal self-contained test case that allows others to reproduce the problem. Not to mention that you didn't tell us what platform you are working on, what toolchain, etc. Regards, Stefan -- ...ich hab' noch einen Koffer in Berlin...
Stefan Seefeld <seefeld@sympatico.ca> writes:
James Healey wrote:
PyFile_AsFile returns a ptr to a file object so that's working as it should.
And how do i check the exception?
Do i need to do any thing with the dict that gets passed into exec_file?
No. If the PyFile_AsFile call returns NULL
Careful with that one. If Python wasn't compiled with the exact same compiler (and release of that compiler!) and standard lib that you're using to build your extension, you will most likely be trashing memory because the FILE struct layouts and semantics will be different. -- Dave Abrahams Boost Consulting www.boost-consulting.com
David Abrahams wrote:
Stefan Seefeld <seefeld@sympatico.ca> writes:
James Healey wrote:
PyFile_AsFile returns a ptr to a file object so that's working as it should.
And how do i check the exception?
Do i need to do any thing with the dict that gets passed into exec_file? No. If the PyFile_AsFile call returns NULL
Careful with that one. If Python wasn't compiled with the exact same compiler (and release of that compiler!) and standard lib that you're using to build your extension, you will most likely be trashing memory because the FILE struct layouts and semantics will be different.
Yup. This was a typo, I meant to refer to PyRun_File in that mail. (See my followup mail.) Thanks, Stefan -- ...ich hab' noch einen Koffer in Berlin...
Ok lets take boost away from what im trying to do, and try to achieve the same thing with just python. Here's my .py file: class MyTest: def TestFunc(): print "Hello From MyTest.TestFunc" And here's my code to handle all this so far: Py_Initialize(); PyObject *pMainModule = PyImport_AddModule( "__main__" ); PyObject *pDict = PyModule_GetDict( pMainModule ); std::string *pythonScript = readPythonScript( "Game.py" ); if( pythonScript != NULL ) { PyRun_String( pythonScript->c_str(), Py_file_input, pDict, pDict ); delete pythonScript; } PyObject* test = PyDict_GetItemString( pDict, "MyTest"); And this is as far as I have got. Any help you can give would be great. Send instant messages to your online friends http://uk.messenger.yahoo.com
And it looks like this is the exception: exception: boost::python::error_already_set --- Stefan Seefeld <seefeld@sympatico.ca> wrote:
James Healey wrote:
When I run the PyRun_File command result comes back as NULL. The only thing im doing before calling exec_file is Py_Initialize();
Check the exception python has set. It should tell you why the execution failed. (Does the file actually exist ? Can it be executed by python without error ?)
boost.python should actually translate that exception into a C++ exception, which you can catch inside your C++ code...
HTH, Stefan
--
...ich hab' noch einen Koffer in Berlin... _______________________________________________ C++-sig mailing list C++-sig@python.org http://mail.python.org/mailman/listinfo/c++-sig
Send instant messages to your online friends http://uk.messenger.yahoo.com
James Healey wrote:
And it looks like this is the exception:
exception: boost::python::error_already_set
http://boost.org/libs/python/doc/tutorial/doc/html/python/embedding.html#pyt... talks a bit about catching exceptions thrown by the python runtime. Look for the section on 'Exception handling'. (The python-exception handling, such as extracting the stack trace, could probably be wrapped into a C++ API, but AFAIK it currently isn't.) HTH, Stefan -- ...ich hab' noch einen Koffer in Berlin...
Ok got the exception catch working and printing out the error and stack trace. ImportError: __import__ not found Is the error that I am getting. --- Stefan Seefeld <seefeld@sympatico.ca> wrote:
James Healey wrote:
And it looks like this is the exception:
exception: boost::python::error_already_set
http://boost.org/libs/python/doc/tutorial/doc/html/python/embedding.html#pyt...
talks a bit about catching exceptions thrown by the python runtime. Look for the section on 'Exception handling'.
(The python-exception handling, such as extracting the stack trace, could probably be wrapped into a C++ API, but AFAIK it currently isn't.)
HTH, Stefan
--
...ich hab' noch einen Koffer in Berlin... _______________________________________________ C++-sig mailing list C++-sig@python.org http://mail.python.org/mailman/listinfo/c++-sig
Send instant messages to your online friends http://uk.messenger.yahoo.com
James Healey wrote:
Ok got the exception catch working and printing out the error and stack trace.
ImportError: __import__ not found
Is the error that I am getting.
Hmm, that's strange. It looks as if the python runtime is missing some essential piece of initialization. Unfortunately I have no idea what that could be. Sorry. Stefan -- ...ich hab' noch einen Koffer in Berlin...
Ok lets take boost away from what im trying to do, and try to achieve the same thing with just python. Here's my .py file: class MyTest: def TestFunc(): print "Hello From MyTest.TestFunc" And here's my code to handle all this so far: Py_Initialize(); PyObject *pMainModule = PyImport_AddModule( "__main__" ); PyObject *pDict = PyModule_GetDict( pMainModule ); std::string *pythonScript = readPythonScript( "Game.py" ); if( pythonScript != NULL ) { PyRun_String( pythonScript->c_str(), Py_file_input, pDict, pDict ); delete pythonScript; } PyObject* test = PyDict_GetItemString( pDict, "MyTest"); And this is as far as I have got. Any help you can give would be great. ___________________________________________________________ All new Yahoo! Mail "The new Interface is stunning in its simplicity and ease of use." - PC Magazine http://uk.docs.yahoo.com/nowyoucan.html
participants (3)
-
David Abrahams -
James Healey -
Stefan Seefeld