diff --git a/docs/advanced/pycpp/utilities.rst b/docs/advanced/pycpp/utilities.rst index ba0dbef88..442d0e9c9 100644 --- a/docs/advanced/pycpp/utilities.rst +++ b/docs/advanced/pycpp/utilities.rst @@ -55,3 +55,18 @@ is always ``none``). // Evaluate the statements in an separate Python file on disk py::eval_file("script.py", scope); + +C++11 raw string literals are also supported and quite handy for this purpose. +The only requirement is that the first statement must be on a new line following +the raw string delimiter ``R"(``, ensuring all lines have common leading indent: + +.. code-block:: cpp + + py::eval(R"( + x = get_answer() + if x == 42: + print('Hello World!') + else: + print('Bye!') + )", scope + ); diff --git a/include/pybind11/eval.h b/include/pybind11/eval.h index 5b2b98272..d4d26c12b 100644 --- a/include/pybind11/eval.h +++ b/include/pybind11/eval.h @@ -11,8 +11,6 @@ #pragma once -#pragma once - #include "pybind11.h" NAMESPACE_BEGIN(pybind11) @@ -56,6 +54,14 @@ object eval(str expr, object global = object(), object local = object()) { return reinterpret_steal(result); } +template +object eval(const char (&s)[N], object global = object(), object local = object()) { + /* Support raw string literals by removing common leading whitespace */ + auto expr = (s[0] == '\n') ? str(module::import("textwrap").attr("dedent")(s)) + : str(s); + return eval(expr, global, local); +} + template object eval_file(str fname, object global = object(), object local = object()) { if (!global) { diff --git a/tests/test_eval.cpp b/tests/test_eval.cpp index ed4c226fe..05cc7f06d 100644 --- a/tests/test_eval.cpp +++ b/tests/test_eval.cpp @@ -20,11 +20,21 @@ test_initializer eval([](py::module &m) { return 42; }); - auto result = py::eval( - "print('Hello World!');\n" - "x = call_test();", + // Regular string literal + py::eval( + "message = 'Hello World!'\n" + "x = call_test()", global, local ); + + // Multi-line raw string literal + auto result = py::eval(R"( + if x == 42: + print(message) + else: + raise RuntimeError + )", global, local + ); auto x = local["x"].cast(); return result == py::none() && x == 42;