quenta
Analysis, figures, and reusable utilities for the towers evidence-accumulation task.
Layering
Imports should point down this list, never up:
notebooks/ # calls + prose, no logic
plots/ # data -> Figure
analysis/ # data -> numbers
io/ # disk <-> data
settings.py # machine config, paths
utils/ # standalone
Install
git clone <url> && cd quenta
pip install -e ".[dev]" # or: uv pip install -e ".[dev]"
If some other project depends on quenta (e.g. the repo for a paper), install
it there pinned to a specific version, never to the latest commit:
pip install quenta==0.1.0 # from PyPI
pip install "quenta @ git+ssh://git@github.com/ThomasMorvan/quenta@v0.1.0" # from git
Pinning matters because anything that produced a figure in a paper must stay reproducible: a version is a permanent pointer to one commit, so the exact code can be recovered later even after defaults change.
Releasing a new version
Three steps, in this order. Do all three, or the version number stops meaning one specific state of the code.
1. Bump the version. Edit version in pyproject.toml, then commit:
git commit -am "release v0.1.1"
2. Tag it and push. This is what a git+ install resolves:
git tag v0.1.1
git push && git push --tags
3. Upload to PyPI. This is what pip install quenta resolves:
rm -rf dist/ # or old builds get uploaded too
python -m build # writes dist/quenta-0.1.1.tar.gz + .whl
twine upload dist/*
Neither a pushed tag nor an uploaded PyPI version can be changed afterwards —
that is the point of them, since other people's installs depend on them
meaning the same thing forever. If a release is wrong, don't fix it in place:
bump to 0.1.2 and release again.
One-time PyPI setup
Needed once per machine, before step 3 ever works:
pip install build twine
Create an API token at https://pypi.org/manage/account/token/ (scope it to
this project once the first upload exists), and save it in ~/.pypirc:
[pypi]
username = __token__
password = pypi-AgEIcHlwaS5vcmc...
Then chmod 600 ~/.pypirc, since that file is a credential.
To rehearse without burning a real version number, upload to
https://test.pypi.org instead: twine upload -r testpypi dist/* (separate
account, separate token).
Updating an install
git pull # this clone; the editable install picks it up
pip install -e ".[dev]" # only if pyproject.toml dependencies changed
In a consumer project, bump the pinned version and reinstall. For a git+
pin, add --force-reinstall: pip compares version strings, and two different
commits can carry the same one.
Settings
Data should live outside the repo. Paths resolve in this order:
| priority | source | scope |
|---|---|---|
| 1 | an explicit data_root= argument |
for tests where you need to change root |
| 2 | settings.local.toml |
this machine, gitignored |
| 3 | settings.toml |
committed defaults |
| 4 | built-in DEFAULTS |
so an installed package still works |
cp settings.local.toml.example settings.local.toml
Never commit a personal path: a tracked machine-specific file means a merge conflict every time you change device.
Run the verification pipeline
python -m quenta.verify --data-root /path/to/data
# or open notebooks/demo.ipynb
Generates a synthetic per-session dataset, makes a fit and summary
at group level, and writes overview.png/.pdf to the figures directory.
Notebook kernel (VS Code)
The system Python has no pip and no ipykernel, so install:
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]" jupyter ipykernel
(or just run scripts/setup-venv.sh, which does the same thing and prints the next step below)
Then in notebooks/demo.ipynb: pick kernel (top-right of the
notebook toolbar) --> Select Another Kernel --> Python Environments... -->
.venv (Python 3.x). If not listed, use Enter interpreter
path... and paste the full path to .venv/bin/python.
Only need to do that once per clone.
Commit notebooks with output, without the metadata noise
Inspired by https://gist.github.com/33eyes/431e3d432f73371509d176d0dfb95b6e Cell outputs (figures, printed numbers) are worth committing as a record of what the notebook produced. Kernelspec, language_info, and execution counts are not: they're machine-specific and churn the diff every run, even when nothing meaningful changed. A git filter strips just that, on the way in.
- Add the filter to git config by running the following command in bash inside the repo:
git config filter.strip-notebook-metadata.clean 'python3 scripts/nb_strip_metadata.py'
(or just run scripts/setup-git.sh, which does the same thing)
2. notebooks/.gitattributes already routes *.ipynb through it:
*.ipynb filter=strip-notebook-metadata
After that, commit to git as usual. Outputs stay in the committed notebook; kernelspec/language_info/execution counts don't. Your local file on disk is untouched either way.
To bypass the filter for one commit (e.g. you want the raw file, metadata and
all), use git -c filter.strip-notebook-metadata.clean= add <path> instead of
the usual git add.
Tests
From an activated environment:
pytest # everything, quick params, good for a pre-commit hook
pytest --slow # same tests, full params for statistical confidence
pytest --cov # which lines the tests actually run; fails under 90%
Coverage is opt-in rather than always-on, so a plain pytest stays fast. CI
runs it on the --slow pass. The floor exists to catch a module that stopped
being tested at all, not to chase 100%.
if __name__ == "__main__": blocks are excluded outright (see
[tool.coverage.report] in pyproject.toml).
From outside environment, prefix with the interpreter: .venv/bin/python -m pytest.
Linting is ruff (pycodestyle + pyflakes + import order, 79 cols; configured
in pyproject.toml), and CI runs it before the tests:
ruff check . # report
ruff check . --fix # fix what's mechanically fixable
Tests that pick a value from the slow fixture run every time, just with
reduced parameters (e.g. fewer iterations) by default and full ones under
--slow, instead of being skipped outright.
The name
The two behavioral apparatuses are named Cirith Ungol and Orthanc, for together they make the Two Towers. The instrument of reckoning is Amon Hen, the Hill of the Eye, from which all things may be seen. And Quenta is the word in the High Speech signifying a tale or an account, as in Quenta Silmarillion, wherein the lore of many ages was gathered from scattered memories into a single history. Thus is this work likewise named; for out of much that was watched and set down is wrought one account, and out of that account are the figures drawn, and from those figures the tale is sung.
Release files for quenta 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| quenta-0.1.0.tar.gz | 27.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| quenta-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.5 kB
Release files / quenta-0.1.0.tar.gz
| Download URL | quenta-0.1.0.tar.gz |
|---|---|
| Size | 27.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c76b43aee99cef2dbed2ad4ad251b2f74e49c41efd5de8e37be18373f6447fd1
|
|
BLAKE2b-256 checksum How to use checksums |
b86cf3179ed121c66823734ad769e589cfd30274b0a0dce56de8835afcd9180c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / quenta-0.1.0-py3-none-any.whl
| Download URL | quenta-0.1.0-py3-none-any.whl |
|---|---|
| Size | 20.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d009bcb9ea27bc9d91bcac2ab5441c493ff10ecc013e8358e85f13d6c9c30002
|
|
BLAKE2b-256 checksum How to use checksums |
310f430ad88bbe9831f89f23b000b24cf54d1064aa90302e7dfbc19db5fb4c04
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|