diff --git a/build/Jamfile b/build/Jamfile index 77c12b23..3d38585a 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -54,6 +54,8 @@ if [ check-python-config ] object_protocol.cpp object_operators.cpp wrapper.cpp + exec.cpp + import.cpp ; dll boost_python diff --git a/doc/v2/exec.html b/doc/v2/exec.html new file mode 100644 index 00000000..d997dbe3 --- /dev/null +++ b/doc/v2/exec.html @@ -0,0 +1,88 @@ + + + + + + + + Boost.Python - <boost/python/exec.hpp> + + + + + + + + + +
+

C++ Boost

+
+

Boost.Python

+ +

Header <boost/python/exec.hpp>

+
+
+ +

Contents

+ +
+
Introduction
+ +
Functions
+ +
+
+
exec
+
exec_file
+
+
+
+
+ +

Introduction

+ +

Exposes a mechanism for embedding the python interpreter into C++ code.

+ +

Functions

+ +

exec

+
+object exec(str code,
+            object globals = object(),
+            object locals = object());
+    
+
+
Effects: + Execute Python source code from code in the context + specified by the dictionaries globals and locals. +
+
Returns: + An instance of object + which holds the result of executing the code. +
+
+ +

exec_file

+
+object exec_file(str filename,
+                 object globals = object(),
+                 object locals = object());
+    
+
+
Effects: + Execute Python source code from the file named by filename + in the context specified by the dictionaries globals and + locals. +
+
Returns: + An instance of object + which holds the result of executing the code. +
+
+ + + + diff --git a/doc/v2/import.html b/doc/v2/import.html new file mode 100644 index 00000000..38a9e30d --- /dev/null +++ b/doc/v2/import.html @@ -0,0 +1,62 @@ + + + + + + + + Boost.Python - <boost/python/import.hpp> + + + + + + + + + +
+

C++ Boost

+
+

Boost.Python

+ +

Header <boost/python/import.hpp>

+
+
+ +

Contents

+ +
+
Introduction
+ +
Functions
+ +
+
+
import
+
+
+
+
+ +

Introduction

+ +

Exposes a mechanism for importing python modules.

+ +

Functions

+ +

import

+
+object import(str name);
+    
+
+
Effects: Imports the module named by name.
+
Returns: An instance of object + which holds a reference to the imported module.
+
+ + + + diff --git a/doc/v2/reference.html b/doc/v2/reference.html index 95097f67..69bb518d 100644 --- a/doc/v2/reference.html +++ b/doc/v2/reference.html @@ -60,6 +60,8 @@
To/From Python Type Conversion
+
Embedding
+
Utility and Infrastructure
Topics
@@ -939,6 +941,39 @@ +

Embedding

+ +
+
exec.hpp
+ +
+
+
Functions
+ +
+
+
exec
+
exec_file
+
+
+
+
+ +
import.hpp
+ +
+
+
Functions
+ +
+
+
import
+
+
+
+
+
+

Utility and Infrastructure

diff --git a/include/boost/python.hpp b/include/boost/python.hpp index 5205d40d..7a1a9084 100644 --- a/include/boost/python.hpp +++ b/include/boost/python.hpp @@ -25,11 +25,13 @@ # include # include # include +# include # include # include # include # include # include +# include # include # include # include diff --git a/include/boost/python/exec.hpp b/include/boost/python/exec.hpp new file mode 100644 index 00000000..fa4e324a --- /dev/null +++ b/include/boost/python/exec.hpp @@ -0,0 +1,31 @@ +// 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) +#ifndef EXEC_SS20050616_HPP +# define EXEC_SS20050616_HPP + +# include +# include + +namespace boost +{ +namespace python +{ + +// Execute python source code from str. +// global and local are the global and local scopes respectively, +// used during execution. +object +exec(str string, object global = object(), object local = object()); + +// Execute python source code from file filename. +// global and local are the global and local scopes respectively, +// used during execution. +object +exec_file(str filename, object global = object(), object local = object()); + +} +} + +#endif diff --git a/include/boost/python/extract.hpp b/include/boost/python/extract.hpp index f2269b1d..4e876f50 100644 --- a/include/boost/python/extract.hpp +++ b/include/boost/python/extract.hpp @@ -22,6 +22,13 @@ # include # include +#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300) || BOOST_WORKAROUND(BOOST_INTEL_WIN, <= 900) +// workaround for VC++ 6.x or 7.0 +# define BOOST_EXTRACT_WORKAROUND () +#else +# define BOOST_EXTRACT_WORKAROUND +#endif + namespace boost { namespace python { namespace api diff --git a/include/boost/python/import.hpp b/include/boost/python/import.hpp new file mode 100644 index 00000000..85d1afc0 --- /dev/null +++ b/include/boost/python/import.hpp @@ -0,0 +1,22 @@ +// 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) +#ifndef IMPORT_SS20050624_HPP +# define IMPORT_SS20050624_HPP + +# include +# include + +namespace boost +{ +namespace python +{ + +// Import the named module and return a reference to it. +object import(str name); + +} +} + +#endif diff --git a/src/exec.cpp b/src/exec.cpp new file mode 100644 index 00000000..0981f8b1 --- /dev/null +++ b/src/exec.cpp @@ -0,0 +1,45 @@ +// 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 +#include +#include +#include + +namespace boost +{ +namespace python +{ + +object exec(str string, object global, object local) +{ + // should be 'char const *' but older python versions don't use 'const' yet. + char *s = python::extract(string); + PyObject* result = PyRun_String(s, Py_file_input, global.ptr(), local.ptr()); + if (!result) throw_error_already_set(); + return object(detail::new_reference(result)); +} + +// Execute python source code from file filename. +// global and local are the global and local scopes respectively, +// used during execution. +object exec_file(str filename, object global, object local) +{ + // should be 'char const *' but older python versions don't use 'const' yet. + char *f = python::extract(filename); + // Let python open the file to avoid potential binary incompatibilities. + PyObject *pyfile = PyFile_FromString(f, "r"); + if (!pyfile) throw std::invalid_argument(std::string(f) + " : no such file"); + python::handle<> file(pyfile); + PyObject* result = PyRun_File(PyFile_AsFile(file.get()), + f, + Py_file_input, + global.ptr(), local.ptr()); + if (!result) throw_error_already_set(); + return object(detail::new_reference(result)); +} + +} // namespace boost::python +} // namespace boost diff --git a/src/import.cpp b/src/import.cpp new file mode 100644 index 00000000..b0df9b23 --- /dev/null +++ b/src/import.cpp @@ -0,0 +1,25 @@ +// 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 +#include +#include +#include + +namespace boost +{ +namespace python +{ + +object import(str name) +{ + // should be 'char const *' but older python versions don't use 'const' yet. + char *n = python::extract(name); + python::handle<> module(python::borrowed(PyImport_AddModule(n))); + return python::object(module); +} + +} // namespace boost::python +} // namespace boost diff --git a/test/Jamfile b/test/Jamfile index e993bf51..36bd0fa3 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -74,10 +74,10 @@ rule bpl-test ( name ? : files * : requirements * ) test-suite python : - [ - run ../test/embedding.cpp ../build/boost_python + [ + run ../test/exec.cpp ../build/boost_python : # program args - : # input files + : ../test/exec.py : # requirements $(PYTHON_PROPERTIES) BOOST_PYTHON_STATIC_LIB diff --git a/test/embedding.cpp b/test/embedding.cpp deleted file mode 100644 index acd1f180..00000000 --- a/test/embedding.cpp +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright David Abrahams 2002. -// 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) - -// embedded_hello -- A simple Boost.Python embedding example -- by -// Dirk Gerrits - -#include -#include -#include -#include -#include - -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() {} - - std::string hello() - { - return "Hello from C++!"; - } -}; - -// Familiar Boost.Python wrapper class for Base -struct BaseWrap : public Base -{ - BaseWrap(PyObject* self_) - : self(self_) {} - - std::string hello() { return python::call_method(self, "hello"); } - - PyObject* self; -}; - -// Pack the Base class wrapper into a module -BOOST_PYTHON_MODULE(embedded_hello) -{ - python::class_("Base") - ; - -} - - -void test() -{ -//- INITIALIZATION -----------------------------------------------------------// - - // Register the module with the interpreter - if (PyImport_AppendInittab("embedded_hello", initembedded_hello) == -1) - throw std::runtime_error("Failed to add embedded_hello to the interpreter's " - "builtin modules"); - // Retrieve the main module - python::object main_module(( - python::handle<>(python::borrowed(PyImport_AddModule("__main__"))))); - - // Retrieve the main module's namespace - python::object main_namespace((main_module.attr("__dict__"))); - - // Define the derived class in Python. - // (You'll normally want to put this in a .py file.) - python::handle<> result( - PyRun_String( - "from embedded_hello import * \n" - "class PythonDerived(Base): \n" - " def hello(self): \n" - " return 'Hello from Python!' \n", - Py_file_input, main_namespace.ptr(), main_namespace.ptr()) - ); - // Result is not needed - result.reset(); - - // Extract the raw Python object representing the just defined derived class - python::handle<> class_ptr( - PyRun_String("PythonDerived\n", Py_eval_input, - main_namespace.ptr(), main_namespace.ptr()) ); - - // Wrap the raw Python object in a Boost.Python object - python::object PythonDerived(class_ptr); - -//- MAIN PROGRAM -------------------------------------------------------------// - - // Creating and using instances of the C++ class is as easy as always. - CppDerived cpp; - std::cout << cpp.hello() << std::endl; - - // But now creating and using instances of the Python class is almost - // as easy! - python::object py_base = PythonDerived(); - Base& py = python::extract(py_base) -#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300) || BOOST_WORKAROUND(BOOST_INTEL_WIN, <= 900) - () -#endif - ; - std::cout << py.hello() << std::endl; -} - -void -test_tutorial() -{ - using namespace boost::python; - - object main_module(( - handle<>(borrowed(PyImport_AddModule("__main__"))))); - - object main_namespace = main_module.attr("__dict__"); - - handle<> ignored((PyRun_String( - - "hello = file('hello.txt', 'w')\n" - "hello.write('Hello world!')\n" - "hello.close()" - - , Py_file_input - , main_namespace.ptr() - , main_namespace.ptr()) - )); -} - -void -test_tutorial2() -{ - using namespace boost::python; - - object main_module(( - handle<>(borrowed(PyImport_AddModule("__main__"))))); - - object main_namespace = main_module.attr("__dict__"); - - handle<> ignored((PyRun_String( - - "result = 5 ** 2" - - , Py_file_input - , main_namespace.ptr() - , main_namespace.ptr()) - )); - - int five_squared = extract(main_namespace["result"]); - assert(five_squared == 25); - - object result((handle<>( - PyRun_String("5 ** 2" - , Py_eval_input - , main_namespace.ptr() - , main_namespace.ptr())) - )); - - int five_squared2 = extract(result); - assert(five_squared2 == 25); -} - -int main() -{ - // Initialize the interpreter - Py_Initialize(); - - if (python::handle_exception(test)) - { - if (PyErr_Occurred()) - PyErr_Print(); - return 1; - } - - if (python::handle_exception(test_tutorial)) - { - if (PyErr_Occurred()) - PyErr_Print(); - return 1; - } - - if (python::handle_exception(test_tutorial2)) - { - if (PyErr_Occurred()) - PyErr_Print(); - return 1; - } - - // Boost.Python doesn't support Py_Finalize yet. - // Py_Finalize(); - return 0; -} -#include "module_tail.cpp" diff --git a/test/exec.cpp b/test/exec.cpp new file mode 100644 index 00000000..c0ce7fb6 --- /dev/null +++ b/test/exec.cpp @@ -0,0 +1,138 @@ +// 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 +#include + +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 +{ + 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(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_ base("Base"); +} + + +void exec_test() +{ + // Register the module with the interpreter + if (PyImport_AppendInittab("embedded_hello", initembedded_hello) == -1) + throw std::runtime_error("Failed to add embedded_hello to the interpreter's " + "builtin modules"); + // 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; + std::cout << cpp.hello() << std::endl; + + // But now creating and using instances of the Python class is almost + // as easy! + python::object py_base = PythonDerived(); + Base& py = python::extract(py_base) BOOST_EXTRACT_WORKAROUND; + std::cout << py.hello() << std::endl; +} + +void exec_file_test() +{ + python::object main = python::import("__main__"); + python::dict global(main.attr("__dict__")); + global.clear(); + python::object result = python::exec_file("exec.py", global, global); + std::string global_as_string = python::extract(python::str(global)) + BOOST_EXTRACT_WORKAROUND; + std::cout << global_as_string << std::endl; +} + +void exec_test_error() +{ + python::object main = python::import("__main__"); + python::dict global(main.attr("__dict__")); + python::object result = python::exec("print unknown \n", global, global); +} + +int main() +{ + bool success = true; + // Initialize the interpreter + Py_Initialize(); + + if (python::handle_exception(exec_test) || + python::handle_exception(exec_file_test)) + { + if (PyErr_Occurred()) PyErr_Print(); + else + { + std::cerr << "A C++ exception was thrown for which " + << "there was no exception handler registered." << std::endl; + } + success = false; + } + if (python::handle_exception(exec_test_error)) + { + if (PyErr_Occurred()) PyErr_Print(); + else + { + std::cerr << "A C++ exception was thrown for which " + << "there was no exception handler registered." << std::endl; + success = false; + } + } + else success = false; + + // Boost.Python doesn't support Py_Finalize yet. + // Py_Finalize(); + return success ? 0 : 1; +} + +// 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" diff --git a/test/exec.py b/test/exec.py new file mode 100644 index 00000000..5c1ad592 --- /dev/null +++ b/test/exec.py @@ -0,0 +1,2 @@ +print 'Hello World !' +number = 42