Add __builtins__ to globals argument of py::exec and py::eval if not present (#2616)

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

* Refactor into inline ensure_builtins_in_globals function
This commit is contained in:
Yannick Jadoul 2020-10-27 19:58:27 +01:00 committed by GitHub
parent ace4deb4f0
commit 3a37d33830
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 36 additions and 0 deletions

View File

@ -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
@ -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;
@ -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;

View File

@ -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;
});
}

View File

@ -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