diff --git a/.bazelrc b/.bazelrc old mode 100644 new mode 100755 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65c2ad7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +bazel-bin +bazel-testlogs +bazel-out +bazel-code_contests +**/*/build/ +**/*.egg-info/ +**/*/dist/ +.DS_Store +.idea +**/*/__pycache__/* + diff --git a/BUILD b/BUILD old mode 100644 new mode 100755 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md old mode 100644 new mode 100755 diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/WORKSPACE b/WORKSPACE old mode 100644 new mode 100755 index 90a401b..8361125 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,6 +1,30 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") +http_archive( + name = "pybind11_bazel", + strip_prefix = "pybind11_bazel-2.11.1", + urls = ["https://github.com/pybind/pybind11_bazel/archive/v2.11.1.zip"], + #strip_prefix = "pybind11_bazel-34206c29f891dbd5f6f5face7b91664c2ff7185c", + #urls = ["https://github.com/pybind/pybind11_bazel/archive/34206c29f891dbd5f6f5face7b91664c2ff7185c.zip"], + #sha256 = "8d0b776ea5b67891f8585989d54aa34869fc12f14bf33f1dc7459458dd222e95", +) + +http_archive( + name = "pybind11", + build_file = "@pybind11_bazel//:pybind11.BUILD", + strip_prefix = "pybind11-2.11.1", + urls = ["https://github.com/pybind/pybind11/archive/v2.11.1.tar.gz"], + #strip_prefix = "pybind11-a54eab92d265337996b8e4b4149d9176c2d428a6", + #urls = ["https://github.com/pybind/pybind11/archive/a54eab92d265337996b8e4b4149d9176c2d428a6.tar.gz"], + #sha256 = "c9375b7453bef1ba0106849c83881e6b6882d892c9fae5b2572a2192100ffb8a", +) + +load("@pybind11_bazel//:python_configure.bzl", "python_configure") +python_configure(name = "local_config_python") +#python_configure(name = "local_config_python",python_interpreter_target = "@python_interpreter//:/usr/bin/python3.11") + + git_repository( name = "com_github_grpc_grpc", remote = "https://github.com/grpc/grpc.git", @@ -132,3 +156,4 @@ sapi_deps() load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") protobuf_deps() + diff --git a/contest_problem.proto b/contest_problem.proto old mode 100644 new mode 100755 diff --git a/execution/BUILD.bazel b/execution/BUILD.bazel old mode 100644 new mode 100755 index 5a238c8..4ad2b63 --- a/execution/BUILD.bazel +++ b/execution/BUILD.bazel @@ -140,3 +140,4 @@ cc_library( "@com_google_absl//absl/synchronization", ], ) + diff --git a/execution/py_locations.cc b/execution/py_locations.cc old mode 100644 new mode 100755 diff --git a/execution/py_locations.h b/execution/py_locations.h old mode 100644 new mode 100755 diff --git a/execution/py_tester_bindings/BUILD.bazel b/execution/py_tester_bindings/BUILD.bazel new file mode 100755 index 0000000..49188c7 --- /dev/null +++ b/execution/py_tester_bindings/BUILD.bazel @@ -0,0 +1,15 @@ +# Functionality for sandboxed execution, i.e. running code on an input and +# collecting its output. + +licenses(["notice"]) + +load("@pybind11_bazel//:build_defs.bzl", "pybind_extension") + +pybind_extension( + name = "py_tester_extention", + srcs = ["py_tester_sandboxer_bindings.cc"], + deps = [ + "//execution:py_tester_sandboxer", + + ], +) diff --git a/execution/py_tester_bindings/README.md b/execution/py_tester_bindings/README.md new file mode 100644 index 0000000..e4bf442 --- /dev/null +++ b/execution/py_tester_bindings/README.md @@ -0,0 +1,48 @@ +# Python binding for running code_contest test harness + +* Builds a Python extention for the C++ code +* The build needs Python 3.9 + + +## Execution + +``` +chmod+x ./make_python_bindings.sh + +./make_python_bindings.sh + +``` + +This generates the binary artifacts (.so), wraps them in a Python package and install them with pip + +## Test + +run `python3.9 test_python_binding.py` + + +**Note** + +Running the test requires Python 3.9. + +However, the test itself requires Python3.10, see [bug report](https://github.com/google-deepmind/code_contests/issues/31) + +The code itself has +as a +the output should be something like : + +``` +python3.9 test_python_binding.py +[mounts.cc : 415] RAW: Failed to resolve library: libpython3.10.so.1.0 +[global_forkclient.cc : 122] RAW: Starting global forkserver +[mounts.cc : 415] RAW: Failed to resolve library: libpython3.10.so.1.0 +compilation results:ProgramStatus.Success +OK - Exit code: 0 + +test-0 :: status=ProgramStatus.Success, pased=True +===================================================================== +hello + +===================================================================== + +``` + diff --git a/execution/py_tester_bindings/code_contests_tester/__init__.py b/execution/py_tester_bindings/code_contests_tester/__init__.py new file mode 100755 index 0000000..91eda99 --- /dev/null +++ b/execution/py_tester_bindings/code_contests_tester/__init__.py @@ -0,0 +1,2 @@ +from .py_tester_extention import * + diff --git a/execution/py_tester_bindings/code_contests_tester/__pycache__/__init__.cpython-310.pyc b/execution/py_tester_bindings/code_contests_tester/__pycache__/__init__.cpython-310.pyc new file mode 100755 index 0000000..b73926f Binary files /dev/null and b/execution/py_tester_bindings/code_contests_tester/__pycache__/__init__.cpython-310.pyc differ diff --git a/execution/py_tester_bindings/code_contests_tester/__pycache__/__init__.cpython-39.pyc b/execution/py_tester_bindings/code_contests_tester/__pycache__/__init__.cpython-39.pyc new file mode 100755 index 0000000..a814114 Binary files /dev/null and b/execution/py_tester_bindings/code_contests_tester/__pycache__/__init__.cpython-39.pyc differ diff --git a/execution/py_tester_bindings/code_contests_tester/py_tester_extention.so b/execution/py_tester_bindings/code_contests_tester/py_tester_extention.so new file mode 100755 index 0000000..81624ee Binary files /dev/null and b/execution/py_tester_bindings/code_contests_tester/py_tester_extention.so differ diff --git a/execution/py_tester_bindings/py_tester_sandboxer_bindings.cc b/execution/py_tester_bindings/py_tester_sandboxer_bindings.cc new file mode 100755 index 0000000..05f41ea --- /dev/null +++ b/execution/py_tester_bindings/py_tester_sandboxer_bindings.cc @@ -0,0 +1,108 @@ +#include +#include "execution/py_tester_sandboxer.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "absl/strings/string_view.h" +#include "absl/time/time.h" +#include "absl/types/span.h" +#include "execution/status_macros.h" +#include "execution/temp_path.h" +#include "execution/tester_sandboxer.h" +#include "farmhash.h" +#include "sandboxed_api/sandbox2/policy.h" +#include "sandboxed_api/sandbox2/policybuilder.h" +#include "sandboxed_api/sandbox2/result.h" +#include "sandboxed_api/sandbox2/sandbox2.h" + +namespace py = pybind11; +using namespace deepmind::code_contests; + +PYBIND11_MODULE(py_tester_extention, m) { + m.doc() = "Python bindings for py_tester_sandboxer"; + py::register_exception_translator([](std::exception_ptr p) { + try { + if (p) std::rethrow_exception(p); + } catch (const absl::Status& s) { + if (!s.ok()) { + throw std::runtime_error(s.ToString()); + } + } + }); + + py::enum_(m, "ProgramStatus") + .value("Unknown", ProgramStatus::kUnknown) + .value("Success", ProgramStatus::kSuccess) + .value("Failed", ProgramStatus::kFailed) + .value("Timeout", ProgramStatus::kTimeout) + .export_values(); + + py::class_(m, "TestOptions") + .def(py::init<>()) + .def_readwrite("num_threads", &TestOptions::num_threads) + .def_readwrite("stop_on_first_failure", &TestOptions::stop_on_first_failure) + // ... other fields ... + ; + + + py::class_(m, "ExecutionResult") + .def(py::init<>()) + .def_readwrite("program_status", &ExecutionResult::program_status) + .def_readwrite("program_hash", &ExecutionResult::program_hash) + .def_readwrite("stdout", &ExecutionResult::stdout) + .def_readwrite("stderr", &ExecutionResult::stderr) + .def_readwrite("execution_duration", &ExecutionResult::execution_duration) + .def_readwrite("sandbox_result", &ExecutionResult::sandbox_result) + .def_readwrite("passed", &ExecutionResult::passed) + .def("sandbox_result_status", &ExecutionResult::SandboxResultStatus); + + py::class_(m, "MultiTestResult") + .def(py::init<>()) + .def_readwrite("compilation_result", &MultiTestResult::compilation_result) + .def_readwrite("test_results", &MultiTestResult::test_results); + + // Binding for Py3TesterSandboxer + py::class_(m, "Py3TesterSandboxer") + .def(py::init&>()) + .def("test", [](Py3TesterSandboxer& self, + const std::string& code, + const std::vector& test_inputs_str, + const TestOptions& test_options, + const std::vector& expected_test_outputs_str, + py::function compare_outputs_pyfunc) { + + // Convert the test inputs from vector to vector + std::vector test_inputs(test_inputs_str.begin(), test_inputs_str.end()); + + // Convert the expected test outputs from vector to vector + std::vector expected_test_outputs(expected_test_outputs_str.begin(), expected_test_outputs_str.end()); + + // Convert py::function to std::function + std::function compare_outputs = [&compare_outputs_pyfunc](std::string_view a, std::string_view b) { + return compare_outputs_pyfunc(a, b).cast(); + }; + + absl::StatusOr result = self.Test(code, test_inputs, test_options, expected_test_outputs, compare_outputs); + if (!result.ok()) { + throw std::runtime_error(result.status().ToString()); + } + + return result.value(); + }); + // If there are public methods in the `TesterSandboxer` or `PyTesterSandboxer` + // classes that you want to expose, you can continue the bindings in a similar manner. +} + diff --git a/execution/py_tester_bindings/setup.py b/execution/py_tester_bindings/setup.py new file mode 100755 index 0000000..3421f9a --- /dev/null +++ b/execution/py_tester_bindings/setup.py @@ -0,0 +1,10 @@ +from setuptools import setup, find_packages + +setup( + name="code_contests_tester", + version="0.1", + packages=find_packages(), + package_data={ + 'code_contests_tester': ['py_tester_extention.so'], + }, +) diff --git a/execution/py_tester_bindings/test_python_binding.py b/execution/py_tester_bindings/test_python_binding.py new file mode 100755 index 0000000..6f40d70 --- /dev/null +++ b/execution/py_tester_bindings/test_python_binding.py @@ -0,0 +1,48 @@ +import sys +from code_contests_tester import ProgramStatus, Py3TesterSandboxer, TestOptions + +program = """ +x = input() +print(x) + +""" + + +def test_binding(python_310_bin_path, python_310_lib_path): + tester = Py3TesterSandboxer(python_310_bin_path, python_310_lib_path.split(",")) + options = TestOptions() + options.num_threads = 4 + options.stop_on_first_failure = True + + + def compare_func(a,b): + return a==b + + result = tester.test(program, ["hello"], options, ["hello\n"], compare_func) + print(f"compilation results:{result.compilation_result.program_status}") + print(result.compilation_result.sandbox_result) + print(result.compilation_result.stderr) + + for i, test_res in enumerate(result.test_results): + print(f"test-{i} :: status={test_res.program_status}, pased={test_res.passed}") + print("=====================================================================") + print(test_res.stdout) + print("=====================================================================") + +if __name__ == '__main__': + + print("Usage: python3.9 test_python_binding.py ") + + if not len(sys.argv) ==3: + print("verify you've read the usage") + exit(1) + + print(sys.argv[1]) + + print("---") + print(sys.argv[2]) + + test_binding(sys.argv[1], sys.argv[2]) + + + diff --git a/execution/py_tester_sandboxer.cc b/execution/py_tester_sandboxer.cc old mode 100644 new mode 100755 index 3fd895a..61d06dd --- a/execution/py_tester_sandboxer.cc +++ b/execution/py_tester_sandboxer.cc @@ -178,7 +178,7 @@ PyTesterSandboxer::CreatePolicy(absl::string_view binary_path, #endif builder.AllowSyscall(__NR_renameat); builder.AllowSyscall(__NR_renameat2); - + builder.AllowSyscall(__NR_arch_prctl); // Python fails if /dev/urandom is mounted as read-only, despite opening it as // O_RDONLY. For now, just mount it as read-write. builder.AddFile("/dev/urandom", /*is_ro=*/false); diff --git a/execution/py_tester_sandboxer.h b/execution/py_tester_sandboxer.h old mode 100644 new mode 100755 diff --git a/execution/simple_threadpool.h b/execution/simple_threadpool.h old mode 100644 new mode 100755 diff --git a/execution/solve_example.cc b/execution/solve_example.cc old mode 100644 new mode 100755 diff --git a/execution/status_macros.h b/execution/status_macros.h old mode 100644 new mode 100755 diff --git a/execution/status_matchers.h b/execution/status_matchers.h old mode 100644 new mode 100755 diff --git a/execution/temp_path.h b/execution/temp_path.h old mode 100644 new mode 100755 diff --git a/execution/tester_sandboxer.cc b/execution/tester_sandboxer.cc old mode 100644 new mode 100755 index 8b00b6d..8a1d47a --- a/execution/tester_sandboxer.cc +++ b/execution/tester_sandboxer.cc @@ -632,10 +632,10 @@ sandbox2::PolicyBuilder CreateBasePolicy(absl::string_view binary, // Disables stack traces on violations, crashes, timeouts and signals. // Done to reduce the amount of unnecessary clutter in the output logs. - builder.CollectStacktracesOnViolation(false); - builder.CollectStacktracesOnSignal(false); - builder.CollectStacktracesOnTimeout(false); - builder.CollectStacktracesOnKill(false); + builder.CollectStacktracesOnViolation(true); + builder.CollectStacktracesOnSignal(true); + builder.CollectStacktracesOnTimeout(true); + builder.CollectStacktracesOnKill(true); return builder; } diff --git a/execution/tester_sandboxer.h b/execution/tester_sandboxer.h old mode 100644 new mode 100755 diff --git a/execution/tester_sandboxer_test.cc b/execution/tester_sandboxer_test.cc old mode 100644 new mode 100755 diff --git a/load_data_test.cc b/load_data_test.cc old mode 100644 new mode 100755 diff --git a/print_names.cc b/print_names.cc old mode 100644 new mode 100755 diff --git a/print_names_and_sources.py b/print_names_and_sources.py old mode 100644 new mode 100755 diff --git a/third_party/BUILD.bazel b/third_party/BUILD.bazel old mode 100644 new mode 100755 diff --git a/third_party/crc32.BUILD.bazel b/third_party/crc32.BUILD.bazel old mode 100644 new mode 100755 diff --git a/third_party/farmhash.BUILD b/third_party/farmhash.BUILD old mode 100644 new mode 100755 diff --git a/third_party/highwayhash.BUILD.bazel b/third_party/highwayhash.BUILD.bazel old mode 100644 new mode 100755 diff --git a/third_party/net_zstd.BUILD.bazel b/third_party/net_zstd.BUILD.bazel old mode 100644 new mode 100755 diff --git a/third_party/snappy.BUILD.bazel b/third_party/snappy.BUILD.bazel old mode 100644 new mode 100755 diff --git a/third_party/zlib.BUILD.bazel b/third_party/zlib.BUILD.bazel old mode 100644 new mode 100755