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

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.3
File Size Uploaded Provenance
marimo_grader_client-0.2.3.tar.gz 146.5 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for marimo-grader-client 0.2.3
File Interpreter ABI Platform
marimo_grader_client-0.2.3-py3-none-any.whl Python 3 none any Details

Total release size: 188.7 kB

Release files / marimo_grader_client-0.2.3.tar.gz

Download URL marimo_grader_client-0.2.3.tar.gz
Size 146.5 kB
Tags Source
SHA-256 checksum
How to use checksums
c437209a7317b40db48e125f3f81eccc6447bacc8d91544557d85e8f096fc6c4
BLAKE2b-256 checksum
How to use checksums
63025ba60a3233670985ce165bb533236f0a94e2f38c5e65e08c95ba32a9c598
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 Sep 23, 2026.

Transparency log

Release files / marimo_grader_client-0.2.3-py3-none-any.whl

Download URL marimo_grader_client-0.2.3-py3-none-any.whl
Size 42.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0fdc927b3d2502f7d594c4e754e692f3fe3584a2c54af699e33b1bcb17f740e6
BLAKE2b-256 checksum
How to use checksums
46850ed3ba8d69a4bf7bda03703ba6dd5647233f452116f0bb62499982581789
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 Sep 23, 2026.

Transparency log

Security for marimo-grader-client 0.2.3

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.3.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 Sep 23, 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