Skip to main content

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-1234 code and the URL for manual use, polls POST /api/v1/auth/device/token until approval and stores the token in the browser's localStorage under grader:<server>:token. The token also lands in the widget's token / netid traits.
  • Submit posts {assignment_version_id, question_id, notebook, check_results, outputs, client} to POST /api/v1/submissions with the bearer token, shows the attempt number and time, then polls GET /api/v1/submissions/{id} every 3 s (up to 2 min) and shows score.total / max_points plus feedback once graded. A 401 shows 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
  • GraderWidget traits: 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)

Source distribution for marimo-grader-client 0.2.4
File Size Uploaded Provenance
marimo_grader_client-0.2.4.tar.gz 195.5 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for marimo-grader-client 0.2.4
File Interpreter ABI Platform
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

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