Skip to main content

A pytest plugin for autograding Python code. Designed for use with the PrairieLearn platform.

Project description

pytest-prairielearn-grader

A pytest plugin for autograding Python code in PrairieLearn. Student code runs in isolated subprocess sandboxes with configurable security restrictions, timeouts, and detailed feedback.

Installation

pip install pytest-prairielearn-grader

For notebook grading support:

pip install pytest-prairielearn-grader[notebook]

Quick Start

See quick_start.md for a full tutorial with PrairieLearn integration examples.

Key Features

Sandboxed Execution

Student code runs in a separate subprocess via Unix sockets, providing:

  • Security isolation from the grading harness
  • Import whitelist/blacklist enforcement
  • Builtin function restrictions
  • Timeout enforcement
  • Privilege dropping (Unix)

Student-Friendly Feedback Mode

Control how much information students see when their code fails. Use @pytest.mark.output(level=...) per test, or set output_level globally in your ConfigObject:

from pytest_prairielearn_grader import ConfigObject

autograder_config = ConfigObject(
    output_level="friendly",  # Global default for all tests
)

Output levels:

Level Shows
"none" Exception class name only (e.g., AssertionError)
"message" Exception name + first line of message (default)
"traceback" Full exception with traceback
"friendly" Only the exception message text — no class name, no traceback

Per-test markers override the global setting:

@pytest.mark.output(level="friendly")
@pytest.mark.grading_data(name="Test add", points=5)
def test_add(sandbox: StudentFixture) -> None:
    assert_fn_equal(sandbox, "add", args=(2, 3), expected=5)

Assertion Helpers

Student-friendly assertion functions that produce clean, readable failure messages:

from pytest_prairielearn_grader.assertions import assert_equal, assert_fn_equal

@pytest.mark.output(level="friendly")
@pytest.mark.grading_data(name="Test calculation", points=5)
def test_calc(sandbox: StudentFixture) -> None:
    # Produces: "Checking: add(2, 3)\nExpected output: 5\nYour code output: 4\n..."
    assert_fn_equal(sandbox, "add", args=(2, 3), expected=5)

Available helpers:

  • assert_equal(actual, expected) — compare any values
  • assert_approx_equal(actual, expected, rtol=1e-5, atol=1e-8) — numeric comparison
  • assert_fn_equal(sandbox, func_name, args=..., expected=...) — call + compare
  • assert_fn_approx_equal(sandbox, func_name, args=..., expected=...) — call + approx compare
  • assert_true(condition) / assert_false(condition) — boolean checks

Ungradable Submission Detection

When student code has a SyntaxError, the submission is automatically marked as ungradable ("gradable": false in results). This means the student does not lose a grading attempt and sees a clear error message.

# Opt out if you want syntax errors to score 0 instead:
autograder_config = ConfigObject(
    syntax_errors_ungradable=False,
)

The grader also detects test collection failures (grader-side errors) and marks those as ungradable with a message for course staff.

ConfigObject

Type-safe, immutable configuration for all autograder settings:

from pytest_prairielearn_grader import ConfigObject

autograder_config = ConfigObject(
    sandbox_timeout=2.0,
    import_whitelist=["numpy", "math"],
    builtin_whitelist=["len", "range", "sum"],
    output_level="friendly",
    syntax_errors_ungradable=True,
    starting_vars={"coefficient": 10},
    names_for_user=["coefficient"],
)

See ConfigObject documentation for all options.

Results JSON Format

The autograder produces autograder_results.json with the following structure:

{
    "gradable": true,
    "score": 0.85,
    "tests": [
        {
            "test_id": "test_student.py::test_function[student_code]",
            "name": "Test Function",
            "max_points": 5,
            "points": 5.0,
            "points_frac": 1.0,
            "outcome": "passed",
            "message": ""
        }
    ]
}

When a submission is ungradable:

{
    "gradable": false,
    "format_errors": ["SyntaxError: invalid syntax (line 3)"],
    "message": "Your code could not be parsed. Please fix the syntax errors and resubmit.",
    "tests": []
}

Development

# Install dev dependencies
pip install -e ".[dev]"

# Run tests
uv run pytest tests -p no:prairielearn-grader

# Run specific scenario
uv run pytest tests/test_autograder_scenarios.py -k "test_friendly_feedback" -v

# Format
ruff format src/ tests/

# Lint
ruff check src/ tests/ --fix

# Type check
mypy src/

Docker Images

  • eliotwrobson/grader-python-pytest:latest — full image with numpy, pandas, matplotlib, sympy
  • eliotwrobson/grader-python-pytest:lite — minimal image with core dependencies only

Related Issues

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

pytest_prairielearn_grader-0.3.1.tar.gz (189.9 kB view details)

Uploaded Source

Built Distribution

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

pytest_prairielearn_grader-0.3.1-py3-none-any.whl (41.9 kB view details)

Uploaded Python 3

File details

Details for the file pytest_prairielearn_grader-0.3.1.tar.gz.

File metadata

  • Download URL: pytest_prairielearn_grader-0.3.1.tar.gz
  • Upload date:
  • Size: 189.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pytest_prairielearn_grader-0.3.1.tar.gz
Algorithm Hash digest
SHA256 5d6ab0c1b8baf14e4432953769333d9510d53cbf555bfd7a816739e44f5fec65
MD5 556c2a3710643fade18c8038c1281bdf
BLAKE2b-256 3651ff98700146e58452b113926557e4fd767a5aef39f486b5daeff835596146

See more details on using hashes here.

File details

Details for the file pytest_prairielearn_grader-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: pytest_prairielearn_grader-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 41.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pytest_prairielearn_grader-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 27154a6d39f9cd04ba9658b341a9c103b53129450b320d682ec8b84e45116916
MD5 7f1aacd3d20ec796f2c0ee1a178b55b0
BLAKE2b-256 7681347c0f9309b7df4b340e3634d9cfc9874b0631fed596a9db31493cca6054

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