I have problem wrapping an Enum for Python using Boost-Python.
Initially I intended to do something like the following in the try-catch (I've inserted my whole code below) statement:
main_namespace["Motion"] = enum_<TestClass::Motion>("Motion")
.value("walk", TestClass::walk)
.value("bike", TestClass::bike)
;
Everything was fine and compilation was done. At run time I got this error (which makes no sense to me):
AttributeError: 'NoneType' object has no attribute 'Motion'
Afterwards I decided to write a Python module using BOOST_PYTHON_MODULE in my code. After initializing Python interpreter I wanted to use this module right away but didn't know how(?). The following is my whole code:
#include <boost/python.hpp>
#include <iostream>
using namespace std;
using namespace boost::python;
BOOST_PYTHON_MODULE(test)
{
enum_<TestClass::Motion>("Motion")
.value("walk", TestClass::walk)
.value("bike", TestClass::bike)
;
}
int main()
{
Py_Initialize();
try
{
object pyMainModule = import("__main__");
object main_namespace = pyMainModule.attr("__dict__");
//What previously I intended to do
//main_namespace["Motion"] = enum_<TestClass::Motion>("Motion")
// .value("walk", TestClass::walk)
// .value("bike", TestClass::bike)
//;
//I want to use my enum here
//I need something like line below which makes me able to use the enum!
exec("print 'hello world'", main_namespace, main_namespace);
}
catch(error_already_set const&)
{
PyErr_Print();
}
Py_Finalize();
return 0;
}
Anything useful to know about wrapping and using Enums in Python will be appreciated! Thanks in advance
The AttributeError
is the result of trying to create a Python extension type without first setting scope. The boost::python::enum_
constructor states:
Constructs an
enum_
object holding a Python extension type derived fromint
which is namedname
. Thename
d attribute of the current scope is bound to the new extension type.
When embedding Python, to use a custom Python module, it is often easiest to use PyImport_AppendInittab
, then import the module by name.
PyImport_AppendInittab("example", &initexample);
...
boost::python::object example = boost::python::import("example");
Here is a complete example showing two enumerations being exposed through Boost.Python. One is included in a separate module (example
) that is imported by main
, and the other is exposed directly in main
.
#include <iostream>
#include <boost/python.hpp>
/// @brief Mockup class with a nested enum.
struct TestClass
{
/// @brief Mocked enum.
enum Motion
{
walk,
bike
};
// @brief Mocked enum.
enum Color
{
red,
blue
};
};
/// @brief Python example module.
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::enum_<TestClass::Motion>("Motion")
.value("walk", TestClass::walk)
.value("bike", TestClass::bike)
;
}
int main()
{
PyImport_AppendInittab("example", &initexample); // Add example to built-in.
Py_Initialize(); // Start interpreter.
// Create the __main__ module.
namespace python = boost::python;
try
{
python::object main = python::import("__main__");
python::object main_namespace = main.attr("__dict__");
python::scope scope(main); // Force main scope
// Expose TestClass::Color as Color
python::enum_<TestClass::Color>("Color")
.value("red", TestClass::red)
.value("blue", TestClass::blue)
;
// Print values of Color enumeration.
python::exec(
"print Color.values",
main_namespace, main_namespace);
// Get a handle to the Color enumeration.
python::object color = main_namespace["Color"];
python::object blue = color.attr("blue");
if (TestClass::blue == python::extract<TestClass::Color>(blue))
std::cout << "blue enum values matched." << std::endl;
// Import example module into main namespace.
main_namespace["example"] = python::import("example");
// Print the values of the Motion enumeration.
python::exec(
"print example.Motion.values",
main_namespace, main_namespace);
// Check if the Python enums match the C++ enum values.
if (TestClass::bike == python::extract<TestClass::Motion>(
main_namespace["example"].attr("Motion").attr("bike")))
std::cout << "bike enum values matched." << std::endl;
}
catch (const python::error_already_set&)
{
PyErr_Print();
}
}
Output:
{0: __main__.Color.red, 1: __main__.Color.blue}
blue enum values matched.
{0: example.Motion.walk, 1: example.Motion.bike}
bike enum values matched.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With