diff options
Diffstat (limited to '')
-rw-r--r-- | src/boost/libs/python/test/exec.cpp | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/src/boost/libs/python/test/exec.cpp b/src/boost/libs/python/test/exec.cpp new file mode 100644 index 000000000..72ff571bd --- /dev/null +++ b/src/boost/libs/python/test/exec.cpp @@ -0,0 +1,197 @@ +// Copyright Stefan Seefeld 2005. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/python.hpp> + +#include <boost/detail/lightweight_test.hpp> +#include <iostream> + + +namespace python = boost::python; + +// An abstract base class +class Base : public boost::noncopyable +{ +public: + virtual ~Base() {}; + virtual std::string hello() = 0; +}; + +// C++ derived class +class CppDerived : public Base +{ +public: + virtual ~CppDerived() {} + virtual std::string hello() { return "Hello from C++!";} +}; + +// Familiar Boost.Python wrapper class for Base +struct BaseWrap : Base, python::wrapper<Base> +{ + virtual std::string hello() + { +#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300) + // workaround for VC++ 6.x or 7.0, see + // http://boost.org/libs/python/doc/tutorial/doc/html/python/exposing.html#python.class_virtual_functions + return python::call<std::string>(this->get_override("hello").ptr()); +#else + return this->get_override("hello")(); +#endif + } +}; + +// Pack the Base class wrapper into a module +BOOST_PYTHON_MODULE(embedded_hello) +{ + python::class_<BaseWrap, boost::noncopyable> base("Base"); +} + + +void eval_test() +{ + python::object result = python::eval("'abcdefg'.upper()"); + std::string value = python::extract<std::string>(result) BOOST_EXTRACT_WORKAROUND; + BOOST_TEST(value == "ABCDEFG"); +} + +void exec_test() +{ + // Retrieve the main module + python::object main = python::import("__main__"); + + // Retrieve the main module's namespace + python::object global(main.attr("__dict__")); + + // Define the derived class in Python. + python::object result = python::exec( + "from embedded_hello import * \n" + "class PythonDerived(Base): \n" + " def hello(self): \n" + " return 'Hello from Python!' \n", + global, global); + + python::object PythonDerived = global["PythonDerived"]; + + // Creating and using instances of the C++ class is as easy as always. + CppDerived cpp; + BOOST_TEST(cpp.hello() == "Hello from C++!"); + + // But now creating and using instances of the Python class is almost + // as easy! + python::object py_base = PythonDerived(); + Base& py = python::extract<Base&>(py_base) BOOST_EXTRACT_WORKAROUND; + + // Make sure the right 'hello' method is called. + BOOST_TEST(py.hello() == "Hello from Python!"); +} + +void exec_file_test(std::string const &script) +{ + // Run a python script in an empty environment. + python::dict global; + python::object result = python::exec_file(script.c_str(), global, global); + + // Extract an object the script stored in the global dictionary. + BOOST_TEST(python::extract<int>(global["number"]) == 42); +} + +void exec_test_error() +{ + // Execute a statement that raises a python exception. + python::dict global; + python::object result = python::exec("print(unknown) \n", global, global); +} + +void exercise_embedding_html() +{ + using namespace boost::python; + /* code from: libs/python/doc/tutorial/doc/tutorial.qbk + (generates libs/python/doc/tutorial/doc/html/python/embedding.html) + */ + object main_module = import("__main__"); + object main_namespace = main_module.attr("__dict__"); + + object ignored = exec("hello = file('hello.txt', 'w')\n" + "hello.write('Hello world!')\n" + "hello.close()", + main_namespace); +} + +void check_pyerr(bool pyerr_expected=false) +{ + if (PyErr_Occurred()) + { + if (!pyerr_expected) { + BOOST_ERROR("Python Error detected"); + PyErr_Print(); + } + else { + PyErr_Clear(); + } + } + else + { + BOOST_ERROR("A C++ exception was thrown for which " + "there was no exception handler registered."); + } +} + +int main(int argc, char **argv) +{ + BOOST_TEST(argc == 2 || argc == 3); + std::string script = argv[1]; + + // Register the module with the interpreter + if (PyImport_AppendInittab(const_cast<char*>("embedded_hello"), +#if PY_VERSION_HEX >= 0x03000000 + PyInit_embedded_hello +#else + initembedded_hello +#endif + ) == -1) + { + BOOST_ERROR("Failed to add embedded_hello to the interpreter's " + "builtin modules"); + } + + // Initialize the interpreter + Py_Initialize(); + + if (python::handle_exception(eval_test)) { + check_pyerr(); + } + else if(python::handle_exception(exec_test)) { + check_pyerr(); + } + else if (python::handle_exception(boost::bind(exec_file_test, script))) { + check_pyerr(); + } + + if (python::handle_exception(exec_test_error)) + { + check_pyerr(/*pyerr_expected*/ true); + } + else + { + BOOST_ERROR("Python exception expected, but not seen."); + } + + if (argc > 2) { + // The main purpose is to test compilation. Since this test generates + // a file and I (rwgk) am uncertain about the side-effects, run it only + // if explicitly requested. + exercise_embedding_html(); + } + + // Boost.Python doesn't support Py_Finalize yet. + // Py_Finalize(); + return boost::report_errors(); +} + +// Including this file makes sure +// that on Windows, any crashes (e.g. null pointer dereferences) invoke +// the debugger immediately, rather than being translated into structured +// exceptions that can interfere with debugging. +#include "module_tail.cpp" |