1 // Copyright Stefan Seefeld 2005.
2 // Distributed under the Boost Software License, Version 1.0. (See
3 // accompanying file LICENSE_1_0.txt or copy at
4 // http://www.boost.org/LICENSE_1_0.txt)
5
6 #include <boost/python.hpp>
7
8 #include <boost/detail/lightweight_test.hpp>
9 #include <iostream>
10
11 namespace python = boost::python;
12
13 // An abstract base class
14 class Base : public boost::noncopyable
15 {
16 public:
~Base()17 virtual ~Base() {};
18 virtual std::string hello() = 0;
19 };
20
21 // C++ derived class
22 class CppDerived : public Base
23 {
24 public:
~CppDerived()25 virtual ~CppDerived() {}
hello()26 virtual std::string hello() { return "Hello from C++!";}
27 };
28
29 // Familiar Boost.Python wrapper class for Base
30 struct BaseWrap : Base, python::wrapper<Base>
31 {
helloBaseWrap32 virtual std::string hello()
33 {
34 #if BOOST_WORKAROUND(BOOST_MSVC, <= 1300)
35 // workaround for VC++ 6.x or 7.0, see
36 // http://boost.org/libs/python/doc/tutorial/doc/html/python/exposing.html#python.class_virtual_functions
37 return python::call<std::string>(this->get_override("hello").ptr());
38 #else
39 return this->get_override("hello")();
40 #endif
41 }
42 };
43
44 // Pack the Base class wrapper into a module
BOOST_PYTHON_MODULE(embedded_hello)45 BOOST_PYTHON_MODULE(embedded_hello)
46 {
47 python::class_<BaseWrap, boost::noncopyable> base("Base");
48 }
49
50
exec_test()51 void exec_test()
52 {
53 std::cout << "registering extension module embedded_hello..." << std::endl;
54
55 // Register the module with the interpreter
56 if (PyImport_AppendInittab("embedded_hello", initembedded_hello) == -1)
57 throw std::runtime_error("Failed to add embedded_hello to the interpreter's "
58 "builtin modules");
59
60 std::cout << "defining Python class derived from Base..." << std::endl;
61
62 // Retrieve the main module
63 python::object main = python::import("__main__");
64
65 // Retrieve the main module's namespace
66 python::object global(main.attr("__dict__"));
67
68 // Define the derived class in Python.
69 python::object result = python::exec(
70 "from embedded_hello import * \n"
71 "class PythonDerived(Base): \n"
72 " def hello(self): \n"
73 " return 'Hello from Python!' \n",
74 global, global);
75
76 python::object PythonDerived = global["PythonDerived"];
77
78 // Creating and using instances of the C++ class is as easy as always.
79 CppDerived cpp;
80 BOOST_TEST(cpp.hello() == "Hello from C++!");
81
82 std::cout << "testing derived class from C++..." << std::endl;
83
84 // But now creating and using instances of the Python class is almost
85 // as easy!
86 python::object py_base = PythonDerived();
87 Base& py = python::extract<Base&>(py_base) BOOST_EXTRACT_WORKAROUND;
88
89 // Make sure the right 'hello' method is called.
90 BOOST_TEST(py.hello() == "Hello from Python!");
91
92 std::cout << "success!" << std::endl;
93 }
94
exec_file_test(std::string const & script)95 void exec_file_test(std::string const &script)
96 {
97 std::cout << "running file " << script << "..." << std::endl;
98
99 // Run a python script in an empty environment.
100 python::dict global;
101 python::object result = python::exec_file(script.c_str(), global, global);
102
103 // Extract an object the script stored in the global dictionary.
104 BOOST_TEST(python::extract<int>(global["number"]) == 42);
105
106 std::cout << "success!" << std::endl;
107 }
108
exec_test_error()109 void exec_test_error()
110 {
111 std::cout << "intentionally causing a python exception..." << std::endl;
112
113 // Execute a statement that raises a python exception.
114 python::dict global;
115 python::object result = python::exec("print unknown \n", global, global);
116
117 std::cout << "Oops! This statement should be skipped due to an exception" << std::endl;
118 }
119
main(int argc,char ** argv)120 int main(int argc, char **argv)
121 {
122 BOOST_TEST(argc == 2);
123 std::string script = argv[1];
124 // Initialize the interpreter
125 Py_Initialize();
126
127 bool error_expected = false;
128
129 if (
130 python::handle_exception(exec_test)
131 || python::handle_exception(boost::bind(exec_file_test, script))
132 || (
133 (error_expected = true)
134 && python::handle_exception(exec_test_error)
135 )
136
137 )
138 {
139 if (PyErr_Occurred())
140 {
141 if (!error_expected)
142 BOOST_ERROR("Python Error detected");
143 PyErr_Print();
144 }
145 else
146 {
147 BOOST_ERROR("A C++ exception was thrown for which "
148 "there was no exception translator registered.");
149 }
150 }
151
152 // Boost.Python doesn't support Py_Finalize yet, so don't call it!
153 return boost::report_errors();
154 }
155