Skip to main content

Generic code evaluation in isolated Docker containers

Project description

docker-evaluator

A code evaluation backend for competitive programming judges. Runs untrusted submissions in isolated Docker containers, enforces time and memory limits, and checks output against expected results — similar to how Codeforces/CodeChef judge submissions.

Supports Python 2/3, C, and C++.

Requirements

  • Docker (running and accessible to the current user)
  • Python 3.9+

Installation

pip install docker-evaluator

Usage

from docker_evaluator import DockerEvaluator

evaluator = DockerEvaluator()

result = evaluator.evaluate(
    code='n = int(input()); print(n * 2)',
    input="21",
    expected_output="42",
    language="py3",
    time_limit=1,
    memory_limit=256 * 1024,  # 256 MB in KB
)

print(result)
# {'correct': True, 'details': 'OK (8ms)'}

evaluator.close()

Parameters

Parameter Type Default Description
code str Submission source code
input str Problem input (stdin or file content)
expected_output str Correct output to compare against
language str "py3", "py2", "c", or "cpp"
time_limit int Time limit in seconds
input_type str "stdin" "stdin" or "file"
file_io_name str "" File name when using file I/O (e.g. "input.txt")
memory_limit int 1024 Memory limit in KB (minimum enforced: 256 MB)

Return value

{"correct": bool, "details": str}

details is one of:

  • OK (Xms) — accepted
  • Wrong Answer
  • Time Limit Exceeded
  • Memory Limit Exceeded
  • Runtime Error (exit code N)
  • Compilation Error

Supported languages

Key Language
py3 Python 3
py2 Python 2
c C
cpp C++

Docker images are built automatically on first use and cached for subsequent runs.

File I/O

For problems that read/write files instead of stdin/stdout:

result = evaluator.evaluate(
    code=open("solution.cpp").read(),
    input="5\n1 2 3 4 5",
    expected_output="15",
    language="cpp",
    time_limit=2,
    input_type="file",
    file_io_name="input.txt",
)

Configuration

Create a .env file in your working directory:

Variable Default Description
KEEP_EVAL_CONTAINERS 0 Set to 1 to keep containers after a run (useful for debugging)
ENVIRONMENT If set, also loads .env.<ENVIRONMENT>

How It Works

  1. The submission is written to a temporary directory
  2. A language-specific Docker image is built (once) and cached for subsequent runs
  3. A container is started with the submission mounted read-only
  4. Resource limits (CPU, memory, PID count) are applied before execution
  5. The code is compiled (if needed) and executed inside the container with no network access
  6. Output is captured and compared against expected output
  7. The container is removed after execution

Security Model

  • Code runs inside isolated Docker containers with no network access
  • No access to the host filesystem outside the mounted read-only workspace
  • Containers are ephemeral and removed after execution
  • Resource limits prevent abuse (memory exhaustion, fork bombs, infinite loops)
  • PID limit (64) restricts process spawning inside the container

Note: This is not a fully hardened sandbox and should not be exposed directly to the internet without additional isolation (e.g. VM-level sandboxing). Docker isolation has known escape vectors in misconfigured environments.

Design Decisions

Why Docker? Provides portable, language-agnostic process isolation without requiring a VM. Adding a new language is as simple as writing a Dockerfile and entrypoint script.

Why cache compilation? Compiled languages (C, C++) are recompiled only when the source changes. Repeated evaluations of the same submission skip compilation entirely, reducing latency significantly in judge workloads.

Why a synchronous API? Keeps integration simple. The library can be wrapped in an async worker or job queue by the caller without imposing an execution model.

Limitations

  • Not a fully secure sandbox — Docker isolation has known escape risks in misconfigured environments
  • No distributed execution (single-machine only)
  • No built-in concurrency control or request queuing
  • Memory limit enforcement has a minimum floor of 256 MB (Docker constraint)

Compilation cache

C and C++ submissions are cached by source hash — repeated evaluations of the same code skip recompilation. To clear the cache:

from docker_evaluator import clear_cache

clear_cache()

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

docker_evaluator-0.1.2.tar.gz (14.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

docker_evaluator-0.1.2-py3-none-any.whl (14.6 kB view details)

Uploaded Python 3

File details

Details for the file docker_evaluator-0.1.2.tar.gz.

File metadata

  • Download URL: docker_evaluator-0.1.2.tar.gz
  • Upload date:
  • Size: 14.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for docker_evaluator-0.1.2.tar.gz
Algorithm Hash digest
SHA256 bf2c0846309422bd0084f22e7e0328890d42ade8c62485b1d0c48ab185da2e01
MD5 6b80c4a7e1fadaa5357a1e443d6b0971
BLAKE2b-256 7ba8c983aac9a1c9de35f09bce1c1962bb08ccdaec3226349e47076f14dee7e7

See more details on using hashes here.

File details

Details for the file docker_evaluator-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for docker_evaluator-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 6f9ac65c214d849cf592628df82bf677db5d5747e2916e78b7bbbbea2f21aeeb
MD5 5e299ad6b5881fcde2999f9699d97c92
BLAKE2b-256 d16bc61b6c4e6daa7a3ee62d000f948cf62a8d2ff80e3a5f17b1a57946189dc5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page