Skip to main content

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

Metadata

Release files for pytest-prairielearn-grader 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-prairielearn-grader 0.3.1
File Size Uploaded Provenance
pytest_prairielearn_grader-0.3.1.tar.gz 189.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-prairielearn-grader 0.3.1
File Interpreter ABI Platform
pytest_prairielearn_grader-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 231.8 kB

Release files / pytest_prairielearn_grader-0.3.1.tar.gz

Download URL pytest_prairielearn_grader-0.3.1.tar.gz
Size 189.9 kB
Tags Source
SHA-256 checksum
How to use checksums
5d6ab0c1b8baf14e4432953769333d9510d53cbf555bfd7a816739e44f5fec65
BLAKE2b-256 checksum
How to use checksums
3651ff98700146e58452b113926557e4fd767a5aef39f486b5daeff835596146
Upload date
Uploaded using Trusted Publishing?
What is 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}

Release files / pytest_prairielearn_grader-0.3.1-py3-none-any.whl

Download URL pytest_prairielearn_grader-0.3.1-py3-none-any.whl
Size 41.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
27154a6d39f9cd04ba9658b341a9c103b53129450b320d682ec8b84e45116916
BLAKE2b-256 checksum
How to use checksums
7681347c0f9309b7df4b340e3634d9cfc9874b0631fed596a9db31493cca6054
Upload date
Uploaded using Trusted Publishing?
What is 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}

Security for pytest-prairielearn-grader 0.3.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page