Skip to content
This repository was archived by the owner on Dec 6, 2024. It is now read-only.

Python wrapper #36

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified .bazelrc
100644 → 100755
Empty file.
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
bazel-bin
bazel-testlogs
bazel-out
bazel-code_contests
**/*/build/
**/*.egg-info/
**/*/dist/
.DS_Store
.idea
**/*/__pycache__/*

Empty file modified BUILD
100644 → 100755
Empty file.
Empty file modified CONTRIBUTING.md
100644 → 100755
Empty file.
Empty file modified LICENSE
100644 → 100755
Empty file.
Empty file modified README.md
100644 → 100755
Empty file.
25 changes: 25 additions & 0 deletions WORKSPACE
100644 → 100755
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -132,3 +156,4 @@ sapi_deps()
load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")

protobuf_deps()

Empty file modified contest_problem.proto
100644 → 100755
Empty file.
1 change: 1 addition & 0 deletions execution/BUILD.bazel
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,4 @@ cc_library(
"@com_google_absl//absl/synchronization",
],
)

Empty file modified execution/py_locations.cc
100644 → 100755
Empty file.
Empty file modified execution/py_locations.h
100644 → 100755
Empty file.
15 changes: 15 additions & 0 deletions execution/py_tester_bindings/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -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",

],
)
48 changes: 48 additions & 0 deletions execution/py_tester_bindings/README.md
Original file line number Diff line number Diff line change
@@ -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

=====================================================================

```

2 changes: 2 additions & 0 deletions execution/py_tester_bindings/code_contests_tester/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .py_tester_extention import *

Binary file not shown.
Binary file not shown.
Binary file not shown.
108 changes: 108 additions & 0 deletions execution/py_tester_bindings/py_tester_sandboxer_bindings.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#include <pybind11/pybind11.h>
#include "execution/py_tester_sandboxer.h"
#include <pybind11/stl.h>
#include <asm/unistd.h>
#include <stdio.h>
#include <sys/syscall.h>

#include <filesystem>
#include <fstream>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>

#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_<ProgramStatus>(m, "ProgramStatus")
.value("Unknown", ProgramStatus::kUnknown)
.value("Success", ProgramStatus::kSuccess)
.value("Failed", ProgramStatus::kFailed)
.value("Timeout", ProgramStatus::kTimeout)
.export_values();

py::class_<TestOptions>(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_<ExecutionResult>(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_<MultiTestResult>(m, "MultiTestResult")
.def(py::init<>())
.def_readwrite("compilation_result", &MultiTestResult::compilation_result)
.def_readwrite("test_results", &MultiTestResult::test_results);

// Binding for Py3TesterSandboxer
py::class_<Py3TesterSandboxer>(m, "Py3TesterSandboxer")
.def(py::init<const std::string&, const std::vector<std::string>&>())
.def("test", [](Py3TesterSandboxer& self,
const std::string& code,
const std::vector<std::string>& test_inputs_str,
const TestOptions& test_options,
const std::vector<std::string>& expected_test_outputs_str,
py::function compare_outputs_pyfunc) {

// Convert the test inputs from vector<string> to vector<string_view>
std::vector<absl::string_view> test_inputs(test_inputs_str.begin(), test_inputs_str.end());

// Convert the expected test outputs from vector<string> to vector<string_view>
std::vector<absl::string_view> expected_test_outputs(expected_test_outputs_str.begin(), expected_test_outputs_str.end());

// Convert py::function to std::function
std::function<bool(std::string_view a, std::string_view b)> compare_outputs = [&compare_outputs_pyfunc](std::string_view a, std::string_view b) {
return compare_outputs_pyfunc(a, b).cast<bool>();
};

absl::StatusOr<MultiTestResult> 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.
}

10 changes: 10 additions & 0 deletions execution/py_tester_bindings/setup.py
Original file line number Diff line number Diff line change
@@ -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'],
},
)
48 changes: 48 additions & 0 deletions execution/py_tester_bindings/test_python_binding.py
Original file line number Diff line number Diff line change
@@ -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 <path to Python3.10 bin> <path to Python3.10 lib>")

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])



2 changes: 1 addition & 1 deletion execution/py_tester_sandboxer.cc
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Empty file modified execution/py_tester_sandboxer.h
100644 → 100755
Empty file.
Empty file modified execution/simple_threadpool.h
100644 → 100755
Empty file.
Empty file modified execution/solve_example.cc
100644 → 100755
Empty file.
Empty file modified execution/status_macros.h
100644 → 100755
Empty file.
Empty file modified execution/status_matchers.h
100644 → 100755
Empty file.
Empty file modified execution/temp_path.h
100644 → 100755
Empty file.
8 changes: 4 additions & 4 deletions execution/tester_sandboxer.cc
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Empty file modified execution/tester_sandboxer.h
100644 → 100755
Empty file.
Empty file modified execution/tester_sandboxer_test.cc
100644 → 100755
Empty file.
Empty file modified load_data_test.cc
100644 → 100755
Empty file.
Empty file modified print_names.cc
100644 → 100755
Empty file.
Empty file modified print_names_and_sources.py
100644 → 100755
Empty file.
Empty file modified third_party/BUILD.bazel
100644 → 100755
Empty file.
Empty file modified third_party/crc32.BUILD.bazel
100644 → 100755
Empty file.
Empty file modified third_party/farmhash.BUILD
100644 → 100755
Empty file.
Empty file modified third_party/highwayhash.BUILD.bazel
100644 → 100755
Empty file.
Empty file modified third_party/net_zstd.BUILD.bazel
100644 → 100755
Empty file.
Empty file modified third_party/snappy.BUILD.bazel
100644 → 100755
Empty file.
Empty file modified third_party/zlib.BUILD.bazel
100644 → 100755
Empty file.