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 valuesassert_approx_equal(actual, expected, rtol=1e-5, atol=1e-8)— numeric comparisonassert_fn_equal(sandbox, func_name, args=..., expected=...)— call + compareassert_fn_approx_equal(sandbox, func_name, args=..., expected=...)— call + approx compareassert_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, sympyeliotwrobson/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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d6ab0c1b8baf14e4432953769333d9510d53cbf555bfd7a816739e44f5fec65
|
|
| MD5 |
556c2a3710643fade18c8038c1281bdf
|
|
| BLAKE2b-256 |
3651ff98700146e58452b113926557e4fd767a5aef39f486b5daeff835596146
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
27154a6d39f9cd04ba9658b341a9c103b53129450b320d682ec8b84e45116916
|
|
| MD5 |
7f1aacd3d20ec796f2c0ee1a178b55b0
|
|
| BLAKE2b-256 |
7681347c0f9309b7df4b340e3634d9cfc9874b0631fed596a9db31493cca6054
|