diff --git a/docs/advanced.rst b/docs/advanced.rst index a13bc2b6f..2a0e26870 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -282,7 +282,7 @@ helper class that is defined as follows: The macro :func:`PYBIND11_OVERLOAD_PURE` should be used for pure virtual functions, and :func:`PYBIND11_OVERLOAD` should be used for functions which have -a default implementation. +a default implementation. There are also two alternate macros :func:`PYBIND11_OVERLOAD_PURE_NAME` and :func:`PYBIND11_OVERLOAD_NAME` which take a string-valued name argument @@ -1612,32 +1612,38 @@ work, it is important that all lines are indented consistently, i.e.: .. [#f4] http://www.sphinx-doc.org .. [#f5] http://github.com/pybind/python_example -Calling Python from C++ -======================= +Evaluating Python expressions from strings and files +==================================================== -Pybind11 also allows to call python code from C++. Note that this code assumes, that the intepreter is already initialized. +pybind11 provides the :func:`eval` and :func:`eval_file` functions to evaluate +Python expressions and statements. The following example illustrates how they +can be used. + +Both functions accept a template parameter that describes how the argument +should be interpreted. Possible choices include ``eval_expr`` (isolated +expression), ``eval_single_statement`` (a single statement, return value is +always ``none``), and ``eval_statements`` (sequence of statements, return value +is always ``none``). .. code-block:: cpp - // get the main module, so we can access and declare stuff - py::module main_module = py::module::import("__main__"); - - //get the main namespace, so I can declare variables - py::object main_namespace = main_module.attr("__dict__"); + // At beginning of file + #include - //now execute code - py::exec( - "print('Hello World1!')\n" - "print('Other Data');", - main_namespace); + ... - //execute a single statement - py::exec_statement("x=42", main_namespace); + // Evaluate in scope of main module + py::object scope = py::module::import("__main__").attr("__dict__"); + + // Evaluate an isolated expression + int result = py::eval("my_variable + 10", scope).cast(); + + // Evaluate a sequence of statements + py::eval( + "print('Hello')\n" + "print('world!');", + scope); + + // Evaluate the statements in an separate Python file on disk + py::eval_file("script.py", scope); - //ok, now I want to get the result of a statement, we'll use x in this example - py::object res = py::eval("x"); - std:cout << "Yielded: " << res.cast() << std::endl; - - //or we can execute a file within the same content - py::exec_file("my_script.py", main_namespace); - diff --git a/docs/reference.rst b/docs/reference.rst index e3fe01849..7edc43a2d 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -244,26 +244,4 @@ Passing extra arguments to the def function .. function:: name::name(const char *value) Used to specify the function name - -Calling Python from C++ -======================= - -.. function:: eval(str string, object global = object(), object local = object()) - - Evaluate a statement, i.e. one that does not yield None. - The return value the result of the expression. It throws pybind11::error_already_set if the commands are invalid. - -.. function:: exec(str string, object global = object(), object local = object()) - - Execute a set of statements. The return value the result of the code. It throws pybind11::error_already_set if the commands are invalid. - -.. function:: exec_statement(str string, object global = object(), object local = object()) - - Execute a single statement. The return value the result of the code. It throws pybind11::error_already_set if the commands are invalid. - -.. function:: exec_file(str filename, object global = object(), object local = object()) - - Execute a file. The function exec_file will throw std::invalid_argument if the file cannot be opened. - The return value the result of the code. It throws pybind11::error_already_set if the commands are invalid and - std::invalid_argument if the file cannot be opened. diff --git a/example/example18.cpp b/example/example18.cpp index f85675d65..8fdab8157 100644 --- a/example/example18.cpp +++ b/example/example18.cpp @@ -1,5 +1,5 @@ /* - example/example18.cpp -- Usage of exec, eval etc. + example/example18.cpp -- Usage of eval() and eval_file() Copyright (c) 2016 Klemens D. Morgenstern @@ -8,29 +8,30 @@ */ -#include +#include #include "example.h" void example18() { py::module main_module = py::module::import("__main__"); py::object main_namespace = main_module.attr("__dict__"); - bool executed = false; + bool ok = false; - main_module.def("call_test", [&]()-> int {executed = true; return 42;}); + main_module.def("call_test", [&]() -> int { + ok = true; + return 42; + }); - cout << "exec test" << endl; + cout << "eval_statements test" << endl; - py::exec( + auto result = py::eval( "print('Hello World!');\n" - "x = call_test();", - main_namespace); + "x = call_test();", main_namespace); - if (executed) - cout << "exec passed" << endl; - else { - cout << "exec failed" << endl; - } + if (ok && result == py::none()) + cout << "eval_statements passed" << endl; + else + cout << "eval_statements failed" << endl; cout << "eval test" << endl; @@ -38,83 +39,64 @@ void example18() { if (val.cast() == 42) cout << "eval passed" << endl; - else { + else cout << "eval failed" << endl; - } + ok = false; + cout << "eval_single_statement test" << endl; - executed = false; - cout << "exec_statement test" << endl; + py::eval( + "y = call_test();", main_namespace); - py::exec_statement("y = call_test();", main_namespace); + if (ok) + cout << "eval_single_statement passed" << endl; + else + cout << "eval_single_statement failed" << endl; - - if (executed) - cout << "exec_statement passed" << endl; - else { - cout << "exec_statement failed" << endl; - } - - cout << "exec_file test" << endl; + cout << "eval_file test" << endl; int val_out; main_module.def("call_test2", [&](int value) {val_out = value;}); - - py::exec_file("example18_call.py", main_namespace); - - if (val_out == 42) - cout << "exec_file passed" << endl; - else { - cout << "exec_file failed" << endl; - } - - executed = false; - cout << "exec failure test" << endl; try { - py::exec("non-sense code ..."); - } - catch (py::error_already_set & err) { - executed = true; - } - if (executed) - cout << "exec failure test passed" << endl; - else { - cout << "exec failure test failed" << endl; + result = py::eval_file("example18_call.py", main_namespace); + } catch (...) { + result = py::eval_file("example/example18_call.py", main_namespace); } + if (val_out == 42 && result == py::none()) + cout << "eval_file passed" << endl; + else + cout << "eval_file failed" << endl; - executed = false; - cout << "exec_file failure test" << endl; - try { - py::exec_file("none-existing file"); - } - catch (std::invalid_argument & err) { - executed = true; - } - if (executed) - cout << "exec_file failure test passed" << endl; - else { - cout << "exec_file failure test failed" << endl; - } - - executed = false; + ok = false; cout << "eval failure test" << endl; try { - py::eval("print('dummy')"); + py::eval("nonsense code ..."); + } catch (py::error_already_set &) { + PyErr_Clear(); + ok = true; } - catch (py::error_already_set & err) { - executed = true; - } - if (executed) + + if (ok) cout << "eval failure test passed" << endl; - else { + else cout << "eval failure test failed" << endl; + + ok = false; + cout << "eval_file failure test" << endl; + try { + py::eval_file("nonexisting file"); + } catch (std::exception &) { + ok = true; } + + if (ok) + cout << "eval_file failure test passed" << endl; + else + cout << "eval_file failure test failed" << endl; } void init_ex18(py::module & m) { - m.def("example18", &example18); + m.def("example18", &example18); } - - diff --git a/example/example18.ref b/example/example18.ref index 81f10491c..db350bde8 100644 --- a/example/example18.ref +++ b/example/example18.ref @@ -1,15 +1,13 @@ -exec test -Hello World! -exec passed +eval_statements test +eval_statements passed eval test eval passed -exec_statement test -exec_statement passed -exec_file test -exec_file passed -exec failure test -exec failure test passed -exec_file failure test -exec_file failure test passed +eval_single_statement test +eval_single_statement passed +eval_file test +eval_file passed eval failure test eval failure test passed +eval_file failure test +eval_file failure test passed +Hello World! diff --git a/include/pybind11/eval.h b/include/pybind11/eval.h new file mode 100644 index 000000000..5dbffc4d2 --- /dev/null +++ b/include/pybind11/eval.h @@ -0,0 +1,88 @@ +/* + pybind11/exec.h: Support for evaluating Python expressions and statements + from strings and files + + Copyright (c) 2016 Klemens Morgenstern and + Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#pragma once + +#include "pytypes.h" + +NAMESPACE_BEGIN(pybind11) + +enum eval_mode { + /// Evaluate a string containing an isolated expression + eval_expr, + + /// Evaluate a string containing a single statement. Returns \c none + eval_single_statement, + + /// Evaluate a string containing a sequence of statement. Returns \c none + eval_statements +}; + +template +object eval(const std::string& str, object global = object(), object local = object()) { + if (!global) { + global = object(PyEval_GetGlobals(), true); + if (!global) + global = dict(); + } + if (!local) + local = global; + + int start; + switch (mode) { + case eval_expr: start = Py_eval_input; break; + case eval_single_statement: start = Py_single_input; break; + case eval_statements: start = Py_file_input; break; + default: pybind11_fail("invalid evaluation mode"); + } + + object result(PyRun_String(str.c_str(), start, global.ptr(), local.ptr()), false); + + if (!result) + throw error_already_set(); + return result; +} + +template +object eval_file(const std::string& fname, object global = object(), object local = object()) { + if (!global) { + global = object(PyEval_GetGlobals(), true); + if (!global) + global = dict(); + } + if (!local) + local = global; + + int start; + switch (mode) { + case eval_expr: start = Py_eval_input; break; + case eval_single_statement: start = Py_single_input; break; + case eval_statements: start = Py_file_input; break; + default: pybind11_fail("invalid evaluation mode"); + } + + FILE *f = fopen(fname.c_str(), "r"); + if (!f) + pybind11_fail("File \"" + fname + "\" could not be opened!"); + + object result(PyRun_FileEx(f, fname.c_str(), Py_file_input, global.ptr(), + local.ptr(), 1), + false); + + if (!result) + throw error_already_set(); + + return result; +} + +NAMESPACE_END(pybind11) diff --git a/include/pybind11/exec.h b/include/pybind11/exec.h deleted file mode 100644 index 3b9ddecdc..000000000 --- a/include/pybind11/exec.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - pybind11/exec.h: Functions to execute python from C++. Based on code from boost.python. - - Copyright (c) 2005 Stefan Seefeld - - This code is based on the boost.python implementation, so a different license applies to this file. - - Boost Software License - Version 1.0 - August 17th, 2003 - - Permission is hereby granted, free of charge, to any person or organization - obtaining a copy of the software and accompanying documentation covered by - this license (the "Software") to use, reproduce, display, distribute, - execute, and transmit the Software, and to prepare derivative works of the - Software, and to permit third-parties to whom the Software is furnished to - do so, all subject to the following: - - The copyright notices in the Software and this entire statement, including - the above license grant, this restriction and the following disclaimer, - must be included in all copies of the Software, in whole or in part, and - all derivative works of the Software, unless such copies or derivative - works are solely in the form of machine-executable object code generated by - a source language processor. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. - -*/ - -#pragma once - -#include "pytypes.h" - -NAMESPACE_BEGIN(pybind11) - - - -inline object eval (const std::string& st, object global = object(), object local = object()) { - if (!global) { - if (PyObject *g = PyEval_GetGlobals()) - global = object(g, true); - else - global = dict(); - } - if (!local) - local = global; - - PyObject *res = PyRun_String(st.c_str() , Py_eval_input, global.ptr(), local.ptr()); - - if (res == nullptr) - throw error_already_set(); - - return {res, false}; -} - -inline object exec (const std::string& st, object global = object(), object local = object()) { - if (!global) { - if (PyObject *g = PyEval_GetGlobals()) - global = object(g, true); - else - global = dict(); - } - if (!local) - local = global; - - PyObject *res = PyRun_String(st.c_str() , Py_file_input, global.ptr(), local.ptr()); - - if (res == nullptr) - throw error_already_set(); - - return {res, false}; -} - -inline object exec_statement (const std::string& st, object global = object(), object local = object()) { - if (!global) { - if (PyObject *g = PyEval_GetGlobals()) - global = object(g, true); - else - global = dict(); - } - if (!local) - local = global; - - PyObject *res = PyRun_String(st.c_str() , Py_single_input, global.ptr(), local.ptr()); - if (res == nullptr) - throw error_already_set(); - - return {res, false}; -} - -inline object exec_file(const std::string& filename, object global = object(), object local = object()) { - // Set suitable default values for global and local dicts. - if (!global) { - if (PyObject *g = PyEval_GetGlobals()) - global = object(g, true); - else - global = dict(); - } - if (!local) local = global; - - std::string f = filename; //need to copy for the signature of PyFile_FromString - - - // Let python open the file to avoid potential binary incompatibilities. -#if PY_VERSION_HEX >= 0x03040000 - const static int close_it = 1; - FILE *fs = _Py_fopen(f.c_str(), "r"); -#elif PY_VERSION_HEX >= 0x03000000 - const static int close_it = 1; - PyObject *fo = Py_BuildValue("s", f.c_str()); - FILE *fs = _Py_fopen(fo, "r"); - Py_DECREF(fo); -#else - const static int close_it = 0; - PyObject *pyfile = PyFile_FromString(&f.front(), const_cast("r")); - if (!pyfile) - throw std::invalid_argument(std::string(f) + " : no such file"); - object file(pyfile, false); - FILE *fs = PyFile_AsFile(file.ptr()); -#endif - if (fs == nullptr) - throw std::invalid_argument(std::string(f) + " : could not be opened"); - - PyObject* res = PyRun_FileEx(fs, - f.c_str(), - Py_file_input, - global.ptr(), local.ptr(), - close_it); - - if (res == nullptr) - throw error_already_set(); - - return {res, false}; - -} - -inline object exec (str string, object global = object(), object local = object()) { - return exec(static_cast(string), global, local); -} - -inline object eval (str string, object global = object(), object local = object()) { - return eval(static_cast(string), global, local); -} - -inline object exec_file(str filename, object global = object(), object local = object()) { - return exec_file(static_cast(filename), global, local); -} -inline object exec_statement (str string, object global = object(), object local = object()) { - return exec_statement(static_cast(string), global, local); -} - -NAMESPACE_END(pybind11)