marimo-grader-client
Notebook-side client for the dartbrains grader. It gives a marimo notebook three
small widgets - sign in, submit and feedback - backed by the
/api/v1 contract in docs/api.md.
Every HTTP request is made from the student's browser by the widget's JavaScript. The kernel never sees the bearer token; it only reads the notebook file, collects check results and hands the payload to the widget.
pip install marimo-grader-client # core: anywidget + traitlets
pip install "marimo-grader-client[mograder]" # also use MoGrader's check()/sidecar
What an assignment looks like
The instructor (or the grader's publish step) injects the assignment identity
into the notebook's PEP 723 block. Either spelling works; a [tool.grader]
table wins over top-level keys.
import marimo
# /// script
# requires-python = ">=3.11"
# dependencies = ["marimo", "marimo-grader-client"]
# mograder-assignment = "week03-glm"
# mograder-cell-hashes = "8f3a...,1c9e..."
# grader-assignment-version = "1f0e5c2a-...-..."
# grader-offering-id = "9b7d...-..."
# grader-assignment-id = "4a21...-..."
# grader-server = "https://grader.dartbrains.org"
# ///
or
# /// script
# [tool.grader]
# assignment-version = "1f0e5c2a-...-..."
# offering-id = "9b7d...-..."
# assignment-id = "4a21...-..."
# server = "https://grader.dartbrains.org"
# ///
Anything missing from the metadata falls back to the environment variables
GRADER_SERVER, GRADER_ASSIGNMENT_VERSION_ID, GRADER_OFFERING_ID and
GRADER_ASSIGNMENT_ID; explicit Grader(...) arguments override both.
Cells
@app.cell
def _():
from marimo_grader_client import Grader
grader = Grader() # reads the metadata above
grader.signin_button() # "Sign in with Dartmouth" - once per notebook
return (grader,)
@app.cell
def _(grader, beta):
grader.check("q03: GLM betas", [
(beta.shape == (4,), "beta should have four entries"),
(abs(beta[0] - 1.2) < 0.1, "the intercept looks off", 2), # optional weight
])
@app.cell
def _(grader):
grader.submit_button("q03") # "Submit q03"
@app.cell
def _(grader):
grader.feedback("q03") # latest attempt: score, max points, feedback
check() delegates to mograder.runtime.check when MoGrader is installed
(so its callouts, marks badges and JSONL sidecar keep working) and otherwise
renders an equivalent callout on its own. Either way the outcome is recorded on
the Grader instance and included in the next submission as check_results.
Make the submit cell depend on the variables your checks use (as above) so it re-runs after edits; the widget also re-reads the notebook file from disk the moment the button is clicked.
What the widgets do
- Sign in opens a tab to the grader's device verification page, shows the
ABCD-1234code and the URL for manual use, pollsPOST /api/v1/auth/device/tokenuntil approval and stores the token in the browser'slocalStorageundergrader:<server>:token. The token also lands in the widget'stoken/netidtraits. - Submit posts
{assignment_version_id, question_id, notebook, check_results, outputs, client}toPOST /api/v1/submissionswith the bearer token, shows the attempt number and time, then pollsGET /api/v1/submissions/{id}every 3 s (up to 2 min) and showsscore.total / max_pointsplus feedback once graded. A401shows an inline sign-in button. - Feedback reads
GET /api/v1/offerings/{offering_id}/me/submissions?assignment_id=...and renders the latest attempt for the question.
Only the browser can reach the grader, so nothing works in a kernel without a
readable notebook file (for example WASM): read_notebook_source() returns
None and the submit button explains that the notebook cannot be sent.
Python API
from marimo_grader_client import Grader, GraderWidget, check, __version__
from marimo_grader_client.notebook import (
read_notebook_source, read_assignment_metadata,
collect_check_results, build_payload,
)
Grader(server=None, assignment_version_id=None, offering_id=None, assignment_id=None, *, source=None, notebook_path=None).signin_button(),.submit_button(question_id, outputs=None),.feedback(question_id)->GraderWidget.check(label, conditions, **kw)-> callout HTML;.check_results()-> recorded records.build_payload(outputs=None)-> the submission body (minus the ids the widget adds).metadata-> parsed PEP 723 metadata
GraderWidgettraits:server,mode(signin|submit|feedback),assignment_version_id,question_id,offering_id,assignment_id,token,netid,payload,result,status(idle|pending|approved|submitting|done|error),message.
Development
uv sync --all-extras
uv run ruff check src tests
uv run pytest
Metadata
Release files for marimo-grader-client 0.2.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| marimo_grader_client-0.2.4.tar.gz | 195.5 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| marimo_grader_client-0.2.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 238.7 kB
Release files / marimo_grader_client-0.2.4.tar.gz
| Download URL | marimo_grader_client-0.2.4.tar.gz |
|---|---|
| Size | 195.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6e6f1dd1930d673d08be30c59f11d8bdacf5e2ec30e19091425af42741f77208
|
|
BLAKE2b-256 checksum How to use checksums |
074bb563c91796247e84fd71e5615b02cbfb3e22030eb2285e0d05b9abaf60dc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / marimo_grader_client-0.2.4-py3-none-any.whl
| Download URL | marimo_grader_client-0.2.4-py3-none-any.whl |
|---|---|
| Size | 43.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c7f4831e4c40b3861863444e7646b15a614ae05008871b0d0a6e42be982f3c0a
|
|
BLAKE2b-256 checksum How to use checksums |
4f704e03b05a1d58c492dcd624813983cccdcf170cf17a35d495b715706fb0bb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for marimo-grader-client 0.2.4
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for marimo_grader_client-0.2.4.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA 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.