Development¶
This guide covers building the package from source, the task runners,
the git hooks, and the conventions that keep the repository clean. The
same checks run locally and in CI, so a green check locally means a
green pipeline.
Set up a working tree¶
Two routes exist. They run the same commands.
With a plain virtualenv:
git clone https://github.com/sanbales/longeron
cd longeron
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]" -e vendor/ipyelk
make check
With pixi, which adds a locked toolchain (node, the
ANTLR tool, a JDK, JupyterLab) on top of the same [project] metadata:
pixi run check
Enable the git hooks once per clone:
make hooks # or: pixi run hooks
The tasks¶
Every task exists in both runners. make <target> uses the venv, and
pixi run <task> uses the locked pixi environment.
Task |
Does |
|---|---|
|
|
|
|
|
the vendored ipyelk test suite ( |
|
the browser-truth tier ( |
|
|
|
|
|
|
|
pytest with |
|
regenerate the ANTLR parsers from |
|
rebuild the prebuilt standard-library JSON ( |
|
run |
|
|
|
re-capture the notebook widget snapshots ( |
|
point |
|
execute every tutorial notebook, then strip outputs. |
|
copy the repo’s labextension builds – the vendored jupyter-elk and the longeron launcher tile ( |
|
JupyterLab in |
Ruff formats and lints the notebooks too (extend-include = ["*.ipynb"]
in pyproject.toml), so lint gates notebook code cells exactly like
.py files.
Browser-truth tests¶
tests/browser/ drives a real JupyterLab in headless Chromium:
elkjs layout, sprotty rendering, widget trait sync, and the served
labextension bundle all run for real. Kernel-side tests cannot see that
class of regression (stale served bundles, unpainted arrowheads,
layout-error starvation), so this tier exists as its own opt-in gate:
pixi run -e browser playwright install chromium # once per machine
pixi run test-browser
The tier is deselected from plain pytest -q (everything there carries
@pytest.mark.browser, and the default addopts excludes that marker),
and its dependencies live in the browser-test extra / the pixi
browser environment – not in dev, so default environments never
grow a browser. This is the one task without a make twin: it needs the
pixi-locked JupyterLab plus a Chromium binary. The task syncs the
vendored labextension first (the stale-bundle footgun below applies
doubly to tests). Assertions are semantic only – settle states, error
counts, DOM presence, kernel round trips; never pixels or timing
margins. The full flake policy, quarantine convention, and
failure-artifact locations are documented in tests/browser/README.md.
Notebook conventions¶
The notebooks in notebooks/ (nine tutorials plus the notation
gallery) follow three rules:
Committed notebooks are output-free. The pre-commit hook strips outputs, execution counts, and volatile metadata from every staged
.ipynb, rewriting only the git index, so your working tree keeps the outputs you are looking at.Executability is tested.
tests/test_notebooks.pyexecutes every notebook, and the documentation build executes them again to render the tutorial pages, so a broken notebook fails both gates.Refresh runs are deterministic.
python scripts/run_notebooks.pyexecutes and strips every notebook in place. A rerun on an unchanged tree produces no diff.
The hook also refuses any staged blob over 5 MB, after stripping. For a
deliberate large asset, commit with --no-verify and say why in the
commit message.
The generated and vendored pieces¶
Some trees are outputs, not sources. Edit the source and regenerate:
Tree |
Source |
Regenerate with |
|---|---|---|
|
|
|
|
|
|
|
upstream ipyelk 2.1.1 + local patches |
edit in place; mark changes |
One labextension footgun to know about: rebuilding the vendored
TypeScript (vendor/ipyelk/js/) writes the bundles into
vendor/ipyelk/src/_d/share/jupyter/labextensions/@jupyrdf/jupyter-elk
(and rebuilding the launcher tile, npm/, writes into
npm/_d/share/jupyter/labextensions/longeron – see npm/README.md),
but JupyterLab serves the copy that pixi install made under
.pixi/envs/*/share/jupyter/labextensions/ – so a rebuilt bundle
silently keeps serving the old code (“the fix didn’t take”). pixi run lab now runs sync-labextension first (it prints a warning whenever a
served copy was stale before syncing); after rebuilding either
extension’s TS, restart lab through pixi run lab (or run make sync-labextension) and
hard-refresh the browser.
CI runs a grammar-regen job that fails when the committed parsers drift
from the .g4 sources, so never hand-edit _gen/.
CI¶
Three workflows run on pixi (prefix-dev/setup-pixi, cached by
pixi.lock):
Workflow |
Runs |
|---|---|
|
the |
|
the documentation build, published to GitHub Pages |
|
on tag push: build, wheel smoke test, and PyPI trusted publishing |
Documentation¶
The site builds with make docs or pixi run docs, which run
sphinx-build -W, so every warning fails the build. Tutorial pages are
the committed notebooks, symlinked into docs/tutorials/ and executed
by myst-nb at build time. If you change a notebook, the next docs build
re-executes it.
Interactive widget outputs (ipyelk diagrams, anywidget viewers) render
on the tutorial pages as committed PNG snapshots from
docs/_static/widget-snapshots/ (swapped in by the
docs/_ext/widget_snapshots.py extension, keyed by its
manifest.json). The docs build itself stays deterministic and
Chromium-free; the snapshots are refreshed manually with
pixi run capture-widgets (browser environment: needs
pixi run -e browser playwright install chromium once per machine,
like test-browser). If you change a widget-bearing notebook cell,
re-run it and commit the refreshed PNGs + manifest – a stale
manifest fails the -W build with a pointer to that command.