diff --git a/include/pybind11/eval.h b/include/pybind11/eval.h index c829b0cb9..165003b85 100644 --- a/include/pybind11/eval.h +++ b/include/pybind11/eval.h @@ -27,12 +27,7 @@ enum eval_mode { }; template -object eval(str expr, object global = object(), object local = object()) { - if (!global) { - global = reinterpret_borrow(PyEval_GetGlobals()); - if (!global) - global = dict(); - } +object eval(str expr, object global = globals(), object local = object()) { if (!local) local = global; @@ -55,29 +50,24 @@ object eval(str expr, object global = object(), object local = object()) { } template -object eval(const char (&s)[N], object global = object(), object local = object()) { +object eval(const char (&s)[N], object global = globals(), 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); } -inline void exec(str expr, object global = object(), object local = object()) { +inline void exec(str expr, object global = globals(), object local = object()) { eval(expr, global, local); } template -void exec(const char (&s)[N], object global = object(), object local = object()) { +void exec(const char (&s)[N], object global = globals(), object local = object()) { eval(s, global, local); } template -object eval_file(str fname, object global = object(), object local = object()) { - if (!global) { - global = reinterpret_borrow(PyEval_GetGlobals()); - if (!global) - global = dict(); - } +object eval_file(str fname, object global = globals(), object local = object()) { if (!local) local = global; diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 45cd8bfb6..786e36f6a 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -799,8 +799,12 @@ public: }; /// \ingroup python_builtins -/// Return a dictionary representing the global symbol table, i.e. ``__main__.__dict__``. -inline dict globals() { return module::import("__main__").attr("__dict__").cast(); } +/// Return a dictionary representing the global variables in the current execution frame, +/// or ``__main__.__dict__`` if there is no frame (usually when the interpreter is embedded). +inline dict globals() { + PyObject *p = PyEval_GetGlobals(); + return reinterpret_borrow(p ? p : module::import("__main__").attr("__dict__").ptr()); +} NAMESPACE_BEGIN(detail) /// Generic support for creating new Python heap types diff --git a/tests/test_embed/test_interpreter.cpp b/tests/test_embed/test_interpreter.cpp index 01fa98346..6501c85d9 100644 --- a/tests/test_embed/test_interpreter.cpp +++ b/tests/test_embed/test_interpreter.cpp @@ -153,3 +153,10 @@ TEST_CASE("Subinterpreter") { REQUIRE(py::hasattr(py::module::import("__main__"), "main_tag")); REQUIRE(py::hasattr(py::module::import("widget_module"), "extension_module_tag")); } + +TEST_CASE("Execution frame") { + // When the interpreter is embedded, there is no execution frame, but `py::exec` + // should still function by using reasonable globals: `__main__.__dict__`. + py::exec("var = dict(number=42)"); + REQUIRE(py::globals()["var"]["number"].cast() == 42); +}