• 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 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