Skip to content

Commit

Permalink
add eval tests
Browse files Browse the repository at this point in the history
  • Loading branch information
nschloe committed Sep 19, 2023
1 parent 02b7600 commit 72f65eb
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 0 deletions.
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ nanobind_add_module(test_bind_map_ext test_stl_bind_map.cpp ${NB_EXTRA_ARGS})
nanobind_add_module(test_bind_vector_ext test_stl_bind_vector.cpp ${NB_EXTRA_ARGS})
nanobind_add_module(test_chrono_ext test_chrono.cpp ${NB_EXTRA_ARGS})
nanobind_add_module(test_enum_ext test_enum.cpp ${NB_EXTRA_ARGS})
nanobind_add_module(test_eval_ext test_eval.cpp ${NB_EXTRA_ARGS})
nanobind_add_module(test_ndarray_ext test_ndarray.cpp ${NB_EXTRA_ARGS})
nanobind_add_module(test_intrusive_ext test_intrusive.cpp object.cpp object.h ${NB_EXTRA_ARGS})
nanobind_add_module(test_exception_ext test_exception.cpp ${NB_EXTRA_ARGS})
Expand Down
99 changes: 99 additions & 0 deletions tests/test_eval.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
tests/test_eval.cpp -- Usage of eval() and eval_file()
Adapted from pybind11's test_eval.cpp with
Copyright (c) 2016 Klemens D. Morgenstern
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/

#include <nanobind/nanobind.h>

// #include "pybind11_tests.h"

// #include <utility>

namespace nb = nanobind;

NB_MODULE(test_eval_ext, m) {
auto global = nb::dict(nb::module_::import_("__main__").attr("__dict__"));

m.def("test_eval_statements", [global]() {
auto local = nb::dict();
local["call_test"] = nb::cpp_function([&]() -> int { return 42; });

// Regular string literal
nb::exec("message = 'Hello World!'\n"
"x = call_test()",
global,
local);

// Multi-line raw string literal
nb::exec(R"(
if x == 42:
print(message)
else:
raise RuntimeError
)",
global,
local);
auto x = nb::cast<int>(local["x"]);
return x == 42;
});

m.def("test_eval", [global]() {
auto local = nb::dict();
local["x"] = nb::int_(42);
auto x = nb::eval("x", global, local);
return nb::cast<int>(x) == 42;
});

m.def("test_eval_single_statement", []() {
auto local = nb::dict();
local["call_test"] = nb::cpp_function([&]() -> int { return 42; });

auto result = nb::eval<nb::eval_single_statement>("x = call_test()", nb::dict(), local);
auto x = nb::cast<int>(local["x"]);
return result.is_none() && x == 42;
});

m.def("test_eval_failure", []() {
try {
nb::eval("nonsense code ...");
} catch (nb::python_error &) {
return true;
}
return false;
});

// test_eval_empty_globals
m.def("eval_empty_globals", [](nb::dict global) {
if (global.is_none()) {
global = nb::dict();
}
auto int_class = nb::eval("isinstance(42, int)", global);
return global;
});

// test_eval_closure
m.def("test_eval_closure", []() {
nb::dict global;
global["closure_value"] = 42;
nb::dict local;
local["closure_value"] = 0;
nb::exec(R"(
local_value = closure_value
def func_global():
return closure_value
def func_local():
return local_value
)",
global,
local);
return std::make_pair(global, local);
});
}
42 changes: 42 additions & 0 deletions tests/test_eval.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import os

import pytest

import env # noqa: F401
import test_eval_ext as m


def test_evals(capture):
with capture:
assert m.test_eval_statements()
assert capture == "Hello World!"

assert m.test_eval()
assert m.test_eval_single_statement()

assert m.test_eval_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


def test_eval_closure():
global_, local = m.test_eval_closure()

assert global_["closure_value"] == 42
assert local["closure_value"] == 0

assert "local_value" not in global_
assert local["local_value"] == 0

assert "func_global" not in global_
assert local["func_global"]() == 42

assert "func_local" not in global_
with pytest.raises(NameError):
local["func_local"]()

0 comments on commit 72f65eb

Please sign in to comment.