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
12 namespace python = boost::python;
13
14 // An abstract base class
15 class Base : public boost::noncopyable
16 {
17 public:
~Base()18 virtual ~Base() {};
19 virtual std::string hello() = 0;
20 };
21
22 // C++ derived class
23 class CppDerived : public Base
24 {
25 public:
~CppDerived()26 virtual ~CppDerived() {}
hello()27 virtual std::string hello() { return "Hello from C++!";}
28 };
29
30 // Familiar Boost.Python wrapper class for Base
31 struct BaseWrap : Base, python::wrapper<Base>
32 {
helloBaseWrap33 virtual std::string hello()
34 {
35 #if BOOST_WORKAROUND(BOOST_MSVC, <= 1300)
36 // workaround for VC++ 6.x or 7.0, see
37 // http://boost.org/libs/python/doc/tutorial/doc/html/python/exposing.html#python.class_virtual_functions
38 return python::call<std::string>(this->get_override("hello").ptr());
39 #else
40 return this->get_override("hello")();
41 #endif
42 }
43 };
44
45 // Pack the Base class wrapper into a module
BOOST_PYTHON_MODULE(embedded_hello)46 BOOST_PYTHON_MODULE(embedded_hello)
47 {
48 python::class_<BaseWrap, boost::noncopyable> base("Base");
49 }
50
51
eval_test()52 void eval_test()
53 {
54 python::object result = python::eval("'abcdefg'.upper()");
55 std::string value = python::extract<std::string>(result) BOOST_EXTRACT_WORKAROUND;
56 BOOST_TEST(value == "ABCDEFG");
57 }
58
exec_test()59 void exec_test()
60 {
61 // Retrieve the main module
62 python::object main = python::import("__main__");
63
64 // Retrieve the main module's namespace
65 python::object global(main.attr("__dict__"));
66
67 // Define the derived class in Python.
68 python::object result = python::exec(
69 "from embedded_hello import * \n"
70 "class PythonDerived(Base): \n"
71 " def hello(self): \n"
72 " return 'Hello from Python!' \n",
73 global, global);
74
75 python::object PythonDerived = global["PythonDerived"];
76
77 // Creating and using instances of the C++ class is as easy as always.
78 CppDerived cpp;
79 BOOST_TEST(cpp.hello() == "Hello from C++!");
80
81 // But now creating and using instances of the Python class is almost
82 // as easy!
83 python::object py_base = PythonDerived();
84 Base& py = python::extract<Base&>(py_base) BOOST_EXTRACT_WORKAROUND;
85
86 // Make sure the right 'hello' method is called.
87 BOOST_TEST(py.hello() == "Hello from Python!");
88 }
89
exec_file_test(std::string const & script)90 void exec_file_test(std::string const &script)
91 {
92 // Run a python script in an empty environment.
93 python::dict global;
94 python::object result = python::exec_file(script.c_str(), global, global);
95
96 // Extract an object the script stored in the global dictionary.
97 BOOST_TEST(python::extract<int>(global["number"]) == 42);
98 }
99
exec_test_error()100 void exec_test_error()
101 {
102 // Execute a statement that raises a python exception.
103 python::dict global;
104 python::object result = python::exec("print(unknown) \n", global, global);
105 }
106
exercise_embedding_html()107 void exercise_embedding_html()
108 {
109 using namespace boost::python;
110 /* code from: libs/python/doc/tutorial/doc/tutorial.qbk
111 (generates libs/python/doc/tutorial/doc/html/python/embedding.html)
112 */
113 object main_module = import("__main__");
114 object main_namespace = main_module.attr("__dict__");
115
116 object ignored = exec("hello = file('hello.txt', 'w')\n"
117 "hello.write('Hello world!')\n"
118 "hello.close()",
119 main_namespace);
120 }
121
check_pyerr(bool pyerr_expected=false)122 void check_pyerr(bool pyerr_expected=false)
123 {
124 if (PyErr_Occurred())
125 {
126 if (!pyerr_expected) {
127 BOOST_ERROR("Python Error detected");
128 PyErr_Print();
129 }
130 else {
131 PyErr_Clear();
132 }
133 }
134 else
135 {
136 BOOST_ERROR("A C++ exception was thrown for which "
137 "there was no exception handler registered.");
138 }
139 }
140
main(int argc,char ** argv)141 int main(int argc, char **argv)
142 {
143 BOOST_TEST(argc == 2 || argc == 3);
144 std::string script = argv[1];
145
146 // Register the module with the interpreter
147 if (PyImport_AppendInittab(const_cast<char*>("embedded_hello"),
148 #if PY_VERSION_HEX >= 0x03000000
149 PyInit_embedded_hello
150 #else
151 initembedded_hello
152 #endif
153 ) == -1)
154 {
155 BOOST_ERROR("Failed to add embedded_hello to the interpreter's "
156 "builtin modules");
157 }
158
159 // Initialize the interpreter
160 Py_Initialize();
161
162 if (python::handle_exception(eval_test)) {
163 check_pyerr();
164 }
165 else if(python::handle_exception(exec_test)) {
166 check_pyerr();
167 }
168 else if (python::handle_exception(boost::bind(exec_file_test, script))) {
169 check_pyerr();
170 }
171
172 if (python::handle_exception(exec_test_error))
173 {
174 check_pyerr(/*pyerr_expected*/ true);
175 }
176 else
177 {
178 BOOST_ERROR("Python exception expected, but not seen.");
179 }
180
181 if (argc > 2) {
182 // The main purpose is to test compilation. Since this test generates
183 // a file and I (rwgk) am uncertain about the side-effects, run it only
184 // if explicitly requested.
185 exercise_embedding_html();
186 }
187
188 // Boost.Python doesn't support Py_Finalize yet.
189 // Py_Finalize();
190 return boost::report_errors();
191 }
192
193 // Including this file makes sure
194 // that on Windows, any crashes (e.g. null pointer dereferences) invoke
195 // the debugger immediately, rather than being translated into structured
196 // exceptions that can interfere with debugging.
197 #include "module_tail.cpp"
198