• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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