Skip to content

Commit

Permalink
Add __builtins__ to globals argument of py::exec and py::eval if …
Browse files Browse the repository at this point in the history
…not present (pybind#2616)

* Add __builtins__ to globals argument of `py::exec` and `py::eval` if not present

* Refactor into inline ensure_builtins_in_globals function
  • Loading branch information
YannickJadoul authored Oct 27, 2020
1 parent ace4deb commit 3a37d33
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 0 deletions.
20 changes: 20 additions & 0 deletions include/pybind11/eval.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@
#include "pybind11.h"

PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)

inline void ensure_builtins_in_globals(object &global) {
#if PY_VERSION_HEX < 0x03080000
// Running exec and eval on Python 2 and 3 adds `builtins` module under
// `__builtins__` key to globals if not yet present.
// Python 3.8 made PyRun_String behave similarly. Let's also do that for
// older versions, for consistency.
if (!global.contains("__builtins__"))
global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE);
#else
(void) global;
#endif
}

PYBIND11_NAMESPACE_END(detail)

enum eval_mode {
/// Evaluate a string containing an isolated expression
Expand All @@ -31,6 +47,8 @@ object eval(str expr, object global = globals(), object local = object()) {
if (!local)
local = global;

detail::ensure_builtins_in_globals(global);

/* PyRun_String does not accept a PyObject / encoding specifier,
this seems to be the only alternative */
std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr;
Expand Down Expand Up @@ -85,6 +103,8 @@ object eval_file(str fname, object global = globals(), object local = object())
if (!local)
local = global;

detail::ensure_builtins_in_globals(global);

int start;
switch (mode) {
case eval_expr: start = Py_eval_input; break;
Expand Down
8 changes: 8 additions & 0 deletions tests/test_eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,12 @@ TEST_SUBMODULE(eval_, m) {
}
return false;
});

// test_eval_empty_globals
m.def("eval_empty_globals", [](py::object global) {
if (global.is_none())
global = py::dict();
auto int_class = py::eval("isinstance(42, int)", global);
return global;
});
}
8 changes: 8 additions & 0 deletions tests/test_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,11 @@ def test_eval_file():
assert m.test_eval_file(filename)

assert m.test_eval_file_failure()


def test_eval_empty_globals():
assert "__builtins__" in m.eval_empty_globals(None)

g = {}
assert "__builtins__" in m.eval_empty_globals(g)
assert "__builtins__" in g

0 comments on commit 3a37d33

Please sign in to comment.