From 21c514b7d04d2a06e02790d6c952ae0976837ef5 Mon Sep 17 00:00:00 2001 From: Shane Steinert-Threlkeld Date: Thu, 5 Mar 2026 14:29:01 -0800 Subject: [PATCH 1/6] Prepare ULTK for release: fix packaging, source bugs, and tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Packaging - Bump version from invalid `0.0.1c` to PEP 440-compliant `0.1.0` - Add `tqdm` to runtime dependencies (imported by `effcomm/optimization.py` and `language/sampling.py` but was missing from `pyproject.toml`) - Move dev-only tools (`mypy`, `pytest`, `scipy-stubs`) out of `dependencies` and into a `[dependency-groups] dev` section so they are not installed by end-users of the library - Delete `setup.py` legacy shim, which conflicted with the `uv_build` backend ## Source-code bug fixes - `language/sampling.py` `all_meanings()`: fix `Meaning` construction to use the tuple-based API (boolean values indexed parallel to `universe.referents`) instead of passing a raw referent subset-tuple, which broke after the `FrozenDict`→tuple refactor - `language/language.py` `Expression` default: replace `Meaning(FrozenDict(), ...)` with `Meaning(tuple(), ...)` and remove the now-unused `FrozenDict` import - `language/grammar/grammar.py`: - `complement()`: replace the non-existent `.referents` attribute with `tuple(not val for val in self.meaning.mapping)` - `draw_referent()`: replace `.referents` attribute access with correct `zip(universe.referents, mapping)` iteration ## Test fixes - `test_language.py`: replace all `FrozenDict({ref: bool})` `Meaning` constructors with `tuple(bool for ref in ...)`, fix `isAnimal` helper to iterate via `zip(universe.referents, mapping)` instead of dict `.items()`, rename duplicate `test_exp_subset` method to `test_exp_can_express_positive` so both positive and negative can_express tests actually run, remove unused `FrozenDict` import - `test_grammar.py`: fix `goal_meaning` to use the tuple-based `Meaning` API ## CI workflows - Replace `actions/setup-python` + `pip` with `astral-sh/setup-uv@v5` + `uv` in all three workflows (`test.yml`, `docs.yml`, `pypi-publish.yml`) - `test.yml`: `uv sync --group dev` + `uv run pytest src/tests/` - `docs.yml`: `uv sync` + `uv run pdoc ...`; upgrade to Python 3.13 - `pypi-publish.yml`: replace `python -m build` with `uv build` ## Documentation - `README.md`: update install instructions to recommend `uv sync`, note Python 3.13 requirement, update testing section to use `uv run pytest` - `CLAUDE.md`: update commands to use `uv sync --group dev` and `uv run pytest src/tests/` Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/docs.yml | 12 ++---- .github/workflows/pypi-publish.yml | 11 ++--- .github/workflows/test.yml | 15 +++---- CLAUDE.md | 64 ++++++++++++++++++++++++++++ README.md | 22 ++++++++-- pyproject.toml | 9 ++-- setup.py | 4 -- src/tests/test_grammar.py | 2 +- src/tests/test_language.py | 25 +++++------ src/ultk/language/grammar/grammar.py | 7 +-- src/ultk/language/language.py | 3 +- src/ultk/language/sampling.py | 3 +- uv.lock | 20 ++++++--- 13 files changed, 132 insertions(+), 65 deletions(-) create mode 100644 CLAUDE.md delete mode 100644 setup.py diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c51430d5..16fcf5ef 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -19,16 +19,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.12' + - uses: astral-sh/setup-uv@v5 - # ADJUST THIS: install all dependencies (including pdoc) - - run: pip install -e . - - run: pip install pdoc - # ADJUST THIS: build your documentation into docs/. - # We use a custom build script for pdoc itself, ideally you just run `pdoc -o docs/ ...` here. - - run: pdoc src/ultk -d google --math -o ./docs + - run: uv sync + - run: uv run pdoc src/ultk -d google --math -o ./docs - uses: actions/upload-pages-artifact@v3 with: diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index b8fc563b..4424989b 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -13,15 +13,10 @@ jobs: id-token: write # IMPORTANT: this permission is mandatory for trusted publishing steps: # retrieve your distributions here - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - name: Install package - run: pip install --upgrade build - pip install -e . + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v5 - name: Build dist - run: python -m build + run: uv build - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0eb9eba2..f40ff1d2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,14 +6,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - name: Install package - run: pip install -e . + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v5 + - name: Install dependencies + run: uv sync --group dev - name: Test with pytest - run: | - pip install pytest - pytest \ No newline at end of file + run: uv run pytest src/tests/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..c973cad7 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,64 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +ULTK (Unnatural Language ToolKit) is a Python library for computational semantic typology research — specifically for "efficient communication" analyses that explain natural language structure in terms of competing pressures: minimizing cognitive complexity vs. maximizing communicative accuracy. + +## Commands + +```bash +# Install all dependencies (including dev group for tests) +uv sync --group dev + +# Run all tests +uv run pytest src/tests/ + +# Run a single test file +uv run pytest src/tests/test_language.py + +# Run a single test by name +uv run pytest src/tests/test_language.py::TestLanguage::test_name + +# Format code (Black is enforced via CI on PRs) +black src/ +``` + +Tests are discovered automatically by pytest from `src/tests/`. The CI workflow runs `uv run pytest src/tests/` from the repo root. + +## Architecture + +### Two Main Modules + +**`ultk.language`** — Core data structures for semantic representations: +- `semantics.py`: `Referent` (immutable semantic object), `Universe` (collection of Referents with a prior distribution), `Meaning` (mapping from Universe to arbitrary type T — e.g., booleans for truth values) +- `language.py`: `Expression` (form + meaning pair), `Language` (frozenset of Expressions sharing a Universe). Helper `aggregate_expression_complexity()` bridges language and effcomm. +- `sampling.py`: Generators for all meanings, expressions, and languages from a universe — used to enumerate the full hypothesis space. +- `grammar/`: A probabilistic context-free grammar (PCFG) framework for building expressions as programs in a Language of Thought. `grammar.py` defines `Rule` and `Grammar`/`GrammaticalExpression`; `likelihood.py` provides scoring functions; `inference.py` handles MDL/Bayesian inference. + +**`ultk.effcomm`** — Efficient communication analysis tools: +- `agent.py`: RSA (Rational Speech Act) agents — `LiteralSpeaker`, `LiteralListener`, `PragmaticSpeaker`, `PragmaticListener` — represented as weight matrices. +- `informativity.py`: `informativity()` and `communicative_success()` — compute how well a language supports communication (vectorized as `diag(prior) @ S @ R ⊙ U`). +- `tradeoff.py`: Pareto front computation (`pareto_optimal_languages`, `non_dominated_2d`, `dominates`) for simplicity/informativeness trade-off analysis. +- `optimization.py`: `EvolutionaryOptimizer` — iterative algorithm to approximate the Pareto frontier via mutations (`AddExpression`, `RemoveExpression`). +- `sampling.py`: `get_hypothetical_variants()` — generates null-hypothesis languages by permuting speaker weight matrices. +- `analysis.py`: Aggregation utilities for building results DataFrames. + +**`ultk.util`**: +- `frozendict.py`: `FrozenDict` — an immutable dict used extensively as keys in frozen dataclasses. +- `io.py`: I/O helpers. + +### Key Design Patterns + +- Core objects (`Universe`, `Meaning`, `Expression`) are **frozen/immutable** (`@dataclass(frozen=True)` or manual `_frozen` flag), enabling hashing and use as dict keys. +- `Meaning` stores its mapping as a `tuple[T, ...]` indexed parallel to `Universe.referents`, with `_ref_to_idx` for O(1) lookup. Access via `meaning[referent]`. +- `Language` stores expressions as a `frozenset` — order-independent, hashable. +- Grammar rules are defined via Python type annotations; `Rule.from_callable()` introspects function signatures to build rules automatically. + +### Examples + +`src/examples/` contains complete worked analyses: +- `indefinites/` — efficient communication analysis of indefinite pronouns +- `modals/` — semantic universals for modals +- `learn_quant/` — quantifier learning diff --git a/README.md b/README.md index 326d8606..46ee05d3 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,21 @@ Read the [documentation](https://clmbr.shane.st/ultk). ## Installing ULTK -First, set up a virtual environment (e.g. via [miniconda](https://docs.conda.io/en/latest/miniconda.html), `conda create -n ultk python=3.11`, and `conda activate ultk`). +ULTK requires Python 3.13+. We recommend using [uv](https://docs.astral.sh/uv/) to manage dependencies. 1. Download or clone this repository and navigate to the root folder. -2. Install ULTK (We recommend doing this inside a virtual environment) +2. Install ULTK and all dependencies: - `pip install -e .` + ``` + uv sync + ``` + + Alternatively, if you prefer pip inside an activated virtual environment: + + ``` + pip install -e . + ``` ## Getting started @@ -33,7 +41,13 @@ The source code is available on github [here](https://github.com/CLMBRs/ultk). ## Testing -Unit tests are written in [pytest](https://docs.pytest.org/en/7.3.x/) and executed via running `pytest` in the `src/tests` folder. +Unit tests are written in [pytest](https://docs.pytest.org/en/7.3.x/) and executed via: + +``` +uv run pytest src/tests/ +``` + +Or, if inside an activated virtual environment: `pytest src/tests/`. ## References diff --git a/pyproject.toml b/pyproject.toml index 93b49731..0033cd37 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ultk" -version = "0.0.1c" +version = "0.1.0" authors = [ { name="Chris Haberland", email="haberc@uw.edu"}, { name="Nathaniel Imel", email="nimel@uci.edu"}, @@ -16,18 +16,19 @@ classifiers = [ ] license = {file = "LICENSE.txt"} dependencies = [ - "mypy", "numpy", "nltk", "pyyaml", "pandas", "plotnine", "pathos", - "pytest", "scipy>=1.7.3", - "scipy-stubs[scipy]>=1.16.3.3", + "tqdm", ] +[dependency-groups] +dev = ["mypy", "pytest", "scipy-stubs[scipy]>=1.16.3.3"] + [project.urls] "Homepage" = "https://clmbr.shane.st/ultk" "Bug Tracker" = "https://github.com/CLMBRs/ultk/issues" diff --git a/setup.py b/setup.py deleted file mode 100644 index 1abbd068..00000000 --- a/setup.py +++ /dev/null @@ -1,4 +0,0 @@ -import setuptools - -if __name__ == "__main__": - setuptools.setup() diff --git a/src/tests/test_grammar.py b/src/tests/test_grammar.py index ef9906ed..18664cb6 100644 --- a/src/tests/test_grammar.py +++ b/src/tests/test_grammar.py @@ -25,7 +25,7 @@ def test_meaning(self): parsed_expression = TestGrammar.grammar.parse(TestGrammar.geq2_expr_str) expr_meaning = parsed_expression.evaluate(TestGrammar.universe) goal_meaning = Meaning( - {referent: referent.num > 2 for referent in TestGrammar.referents}, + tuple(referent.num > 2 for referent in TestGrammar.referents), TestGrammar.universe, ) assert expr_meaning == goal_meaning diff --git a/src/tests/test_language.py b/src/tests/test_language.py index c4010877..9943e850 100644 --- a/src/tests/test_language.py +++ b/src/tests/test_language.py @@ -4,7 +4,6 @@ from ultk.language.language import Expression, Language from ultk.language.semantics import Referent, Universe, Meaning -from ultk.util.frozendict import FrozenDict class TestLanguage: @@ -27,35 +26,35 @@ class TestLanguage: dog = Expression( form="dog", meaning=Meaning( - mapping=FrozenDict({ref: ref.name == "dog" for ref in uni_refs}), + mapping=tuple(ref.name == "dog" for ref in uni_refs), universe=uni, ), ) cat = Expression( form="cat", meaning=Meaning( - mapping=FrozenDict({ref: ref.name == "cat" for ref in uni_refs}), + mapping=tuple(ref.name == "cat" for ref in uni_refs), universe=uni, ), ) tree = Expression( form="tree", meaning=Meaning( - mapping=FrozenDict({ref: ref.name == "tree" for ref in uni_refs}), + mapping=tuple(ref.name == "tree" for ref in uni_refs), universe=uni, ), ) shroom = Expression( form="shroom", meaning=Meaning( - mapping=FrozenDict({ref: ref.name == "shroom" for ref in uni_refs}), + mapping=tuple(ref.name == "shroom" for ref in uni_refs), universe=uni, ), ) bird = Expression( form="bird", meaning=Meaning( - mapping=FrozenDict({ref: ref.name == "bird" for ref in uni_refs}), + mapping=tuple(ref.name == "bird" for ref in uni_refs), universe=uni, ), ) @@ -65,7 +64,7 @@ class TestLanguage: lang_subset_expr = Language(expressions=tuple([dog, cat, tree])) lang_of_different_order = Language(expressions=tuple([dog, cat, shroom, tree])) - def test_exp_subset(self): + def test_exp_can_express_positive(self): assert TestLanguage.dog.can_express(Referent("dog", {"phylum": "animal"})) def test_exp_subset(self): @@ -83,11 +82,9 @@ def test_language_universe_check(self): Expression( form="dog", meaning=Meaning( - mapping=FrozenDict( - { - ref: ref.name == "dog" - for ref in TestLanguage.uni.referents - } + mapping=tuple( + ref.name == "dog" + for ref in TestLanguage.uni.referents ), universe=TestLanguage.uni2, ), @@ -98,8 +95,8 @@ def test_language_universe_check(self): def test_language_degree(self): def isAnimal(exp: Expression) -> bool: print("checking phylum of " + str(exp)) - for k, v in exp.meaning.mapping.items(): - if v and k.phylum != "animal": + for ref, v in zip(exp.meaning.universe.referents, exp.meaning.mapping): + if v and ref.phylum != "animal": return False return True diff --git a/src/ultk/language/grammar/grammar.py b/src/ultk/language/grammar/grammar.py index 7be0ab86..004fda79 100644 --- a/src/ultk/language/grammar/grammar.py +++ b/src/ultk/language/grammar/grammar.py @@ -168,15 +168,16 @@ def complement(self) -> Meaning: the expression evaluates to False.""" return Meaning( - tuple(set(self.meaning.universe.referents) - set(self.meaning.referents)), + tuple(not val for val in self.meaning.mapping), self.meaning.universe, ) def draw_referent(self, complement=False): """Get a random referent from the meaning's referents.""" + universe_refs = self.meaning.universe.referents if complement: - return random.choice(list(self.complement().referents)) - return random.choice(list(self.meaning.referents)) + return random.choice([r for r, v in zip(universe_refs, self.meaning.mapping) if not v]) + return random.choice([r for r, v in zip(universe_refs, self.meaning.mapping) if v]) def to_dict(self) -> dict: the_dict = super().to_dict() diff --git a/src/ultk/language/language.py b/src/ultk/language/language.py index a89b6004..e20c7234 100644 --- a/src/ultk/language/language.py +++ b/src/ultk/language/language.py @@ -16,7 +16,6 @@ from dataclasses import dataclass from typing import Callable, Generic, Iterable, TypeVar from ultk.language.semantics import Meaning, Referent, Universe -from ultk.util.frozendict import FrozenDict # TODO: require Python 3.12 and use type parameter syntax instead? https://docs.python.org/3/reference/compound_stmts.html#type-params T = TypeVar("T") @@ -30,7 +29,7 @@ class Expression(Generic[T]): # useful for hashing in certain cases # (e.g. a GrammaticalExpression which has not yet been evaluate()'d and so does not yet have a Meaning) form: str = "" - meaning: Meaning[T] = Meaning(FrozenDict(), Universe(tuple(), tuple())) + meaning: Meaning[T] = Meaning(tuple(), Universe(tuple(), tuple())) def can_express(self, referent: Referent) -> bool: """Return True if the expression can express the input single meaning point and false otherwise.""" diff --git a/src/ultk/language/sampling.py b/src/ultk/language/sampling.py index 23dcb167..b6297db0 100644 --- a/src/ultk/language/sampling.py +++ b/src/ultk/language/sampling.py @@ -32,7 +32,8 @@ def all_meanings(universe: Universe) -> Generator[Meaning, None, None]: """Generate all Meanings (sets of Referents) from a given Universe.""" referents = universe.referents for refset in powerset(referents): - yield Meaning(refset, universe) + refset_set = set(refset) + yield Meaning(tuple(ref in refset_set for ref in referents), universe) def all_expressions(meanings: Iterable[Meaning]) -> Generator[Expression, None, None]: diff --git a/uv.lock b/uv.lock index 64b3f092..1cc56a1f 100644 --- a/uv.lock +++ b/uv.lock @@ -934,31 +934,41 @@ wheels = [ [[package]] name = "ultk" -version = "0.0.1rc0" +version = "0.1.0" source = { editable = "." } dependencies = [ - { name = "mypy" }, { name = "nltk" }, { name = "numpy" }, { name = "pandas" }, { name = "pathos" }, { name = "plotnine" }, - { name = "pytest" }, { name = "pyyaml" }, { name = "scipy" }, + { name = "tqdm" }, +] + +[package.dev-dependencies] +dev = [ + { name = "mypy" }, + { name = "pytest" }, { name = "scipy-stubs", extra = ["scipy"] }, ] [package.metadata] requires-dist = [ - { name = "mypy" }, { name = "nltk" }, { name = "numpy" }, { name = "pandas" }, { name = "pathos" }, { name = "plotnine" }, - { name = "pytest" }, { name = "pyyaml" }, { name = "scipy", specifier = ">=1.7.3" }, + { name = "tqdm" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "mypy" }, + { name = "pytest" }, { name = "scipy-stubs", extras = ["scipy"], specifier = ">=1.16.3.3" }, ] From 1c56bcf613892b77395a8d978418d740e2b75dd1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 5 Mar 2026 22:33:33 +0000 Subject: [PATCH 2/6] Automated black formatting --- src/tests/test_language.py | 3 +-- src/ultk/language/grammar/grammar.py | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/tests/test_language.py b/src/tests/test_language.py index 9943e850..0186d844 100644 --- a/src/tests/test_language.py +++ b/src/tests/test_language.py @@ -83,8 +83,7 @@ def test_language_universe_check(self): form="dog", meaning=Meaning( mapping=tuple( - ref.name == "dog" - for ref in TestLanguage.uni.referents + ref.name == "dog" for ref in TestLanguage.uni.referents ), universe=TestLanguage.uni2, ), diff --git a/src/ultk/language/grammar/grammar.py b/src/ultk/language/grammar/grammar.py index 004fda79..6e84599b 100644 --- a/src/ultk/language/grammar/grammar.py +++ b/src/ultk/language/grammar/grammar.py @@ -100,7 +100,12 @@ def _and(p1: bool, p2: bool) -> bool: rhs: tuple[Any, ...] | None = tuple(arg.annotation for arg in args.values()) # if one type annotation is class, a type of Referent, treat this as a terminal, no children = None RHS # TODO: make this more general? - if rhs and len(rhs) == 1 and inspect.isclass(rhs[0]) and issubclass(rhs[0], Referent): + if ( + rhs + and len(rhs) == 1 + and inspect.isclass(rhs[0]) + and issubclass(rhs[0], Referent) + ): rhs = None return cls( name=rule_name, @@ -176,8 +181,12 @@ def draw_referent(self, complement=False): """Get a random referent from the meaning's referents.""" universe_refs = self.meaning.universe.referents if complement: - return random.choice([r for r, v in zip(universe_refs, self.meaning.mapping) if not v]) - return random.choice([r for r, v in zip(universe_refs, self.meaning.mapping) if v]) + return random.choice( + [r for r, v in zip(universe_refs, self.meaning.mapping) if not v] + ) + return random.choice( + [r for r, v in zip(universe_refs, self.meaning.mapping) if v] + ) def to_dict(self) -> dict: the_dict = super().to_dict() From cc40dc00331a16a41aeed718220b95b92006e90d Mon Sep 17 00:00:00 2001 From: Shane Steinert-Threlkeld Date: Thu, 5 Mar 2026 14:41:56 -0800 Subject: [PATCH 3/6] python version in pypi-publish Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/pypi-publish.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 4424989b..30cce4c6 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -15,6 +15,8 @@ jobs: # retrieve your distributions here - uses: actions/checkout@v4 - uses: astral-sh/setup-uv@v5 + with: + python-version: "3.13" - name: Build dist run: uv build - name: Publish package distributions to PyPI From b32f63c6b8fa4ec82a8521620a3bfd462d4c64ed Mon Sep 17 00:00:00 2001 From: Shane Steinert-Threlkeld Date: Thu, 5 Mar 2026 14:50:07 -0800 Subject: [PATCH 4/6] add pdoc to dev group, update docs workflow --- .github/workflows/docs.yml | 2 +- README.md | 1 + pyproject.toml | 2 +- uv.lock | 90 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 16fcf5ef..b6107dfa 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/checkout@v4 - uses: astral-sh/setup-uv@v5 - - run: uv sync + - run: uv sync --group dev - run: uv run pdoc src/ultk -d google --math -o ./docs - uses: actions/upload-pages-artifact@v3 diff --git a/README.md b/README.md index 46ee05d3..326e834b 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ The source code is available on github [here](https://github.com/CLMBRs/ultk). Unit tests are written in [pytest](https://docs.pytest.org/en/7.3.x/) and executed via: ``` +uv sync --group dev uv run pytest src/tests/ ``` diff --git a/pyproject.toml b/pyproject.toml index 0033cd37..bdcab423 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ dependencies = [ ] [dependency-groups] -dev = ["mypy", "pytest", "scipy-stubs[scipy]>=1.16.3.3"] +dev = ["mypy", "pytest", "scipy-stubs[scipy]>=1.16.3.3", "pdoc"] [project.urls] "Homepage" = "https://clmbr.shane.st/ultk" diff --git a/uv.lock b/uv.lock index 1cc56a1f..49d01317 100644 --- a/uv.lock +++ b/uv.lock @@ -138,6 +138,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + [[package]] name = "joblib" version = "1.5.3" @@ -247,6 +259,67 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/36/e9/a0aa60f5322814dd084a89614e9e31139702e342f8459ad8af1984a18168/librt-0.7.4-cp314-cp314t-win_arm64.whl", hash = "sha256:76b2ba71265c0102d11458879b4d53ccd0b32b0164d14deb8d2b598a018e502f", size = 39724, upload-time = "2025-12-15T16:52:29.836Z" }, ] +[[package]] +name = "markdown2" +version = "2.5.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/ae/07d4a5fcaa5509221287d289323d75ac8eda5a5a4ac9de2accf7bbcc2b88/markdown2-2.5.5.tar.gz", hash = "sha256:001547e68f6e7fcf0f1cb83f7e82f48aa7d48b2c6a321f0cd20a853a8a2d1664", size = 157249, upload-time = "2026-03-02T20:46:53.411Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/af/4b3891eb0a49d6cfd5cbf3e9bf514c943afc2b0f13e2c57cc57cd88ecc21/markdown2-2.5.5-py3-none-any.whl", hash = "sha256:be798587e09d1f52d2e4d96a649c4b82a778c75f9929aad52a2c95747fa26941", size = 56250, upload-time = "2026-03-02T20:46:52.032Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + [[package]] name = "matplotlib" version = "3.10.8" @@ -541,6 +614,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/70/ba4b949bdc0490ab78d545459acd7702b211dfccf7eb89bbc1060f52818d/patsy-1.0.2-py2.py3-none-any.whl", hash = "sha256:37bfddbc58fcf0362febb5f54f10743f8b21dd2aa73dec7e7ef59d1b02ae668a", size = 233301, upload-time = "2025-10-20T16:17:36.563Z" }, ] +[[package]] +name = "pdoc" +version = "16.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "markdown2" }, + { name = "markupsafe" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/fe/ab3f34a5fb08c6b698439a2c2643caf8fef0d61a86dd3fdcd5501c670ab8/pdoc-16.0.0.tar.gz", hash = "sha256:fdadc40cc717ec53919e3cd720390d4e3bcd40405cb51c4918c119447f913514", size = 111890, upload-time = "2025-10-27T16:02:16.345Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/a1/56a17b7f9e18c2bb8df73f3833345d97083b344708b97bab148fdd7e0b82/pdoc-16.0.0-py3-none-any.whl", hash = "sha256:070b51de2743b9b1a4e0ab193a06c9e6c12cf4151cf9137656eebb16e8556628", size = 100014, upload-time = "2025-10-27T16:02:15.007Z" }, +] + [[package]] name = "pillow" version = "12.0.0" @@ -950,6 +1038,7 @@ dependencies = [ [package.dev-dependencies] dev = [ { name = "mypy" }, + { name = "pdoc" }, { name = "pytest" }, { name = "scipy-stubs", extra = ["scipy"] }, ] @@ -969,6 +1058,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ { name = "mypy" }, + { name = "pdoc" }, { name = "pytest" }, { name = "scipy-stubs", extras = ["scipy"], specifier = ">=1.16.3.3" }, ] From 8fde82abaa46c37e43138d7abc7b4fb50d788e09 Mon Sep 17 00:00:00 2001 From: Shane Steinert-Threlkeld Date: Thu, 5 Mar 2026 14:51:55 -0800 Subject: [PATCH 5/6] Update .github/workflows/test.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f40ff1d2..f66f3144 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,6 +8,8 @@ jobs: steps: - uses: actions/checkout@v4 - uses: astral-sh/setup-uv@v5 + with: + python-version: "3.13" - name: Install dependencies run: uv sync --group dev - name: Test with pytest From d5598b352076fdf504d0a498318ce5b6efe77127 Mon Sep 17 00:00:00 2001 From: Shane Steinert-Threlkeld Date: Thu, 5 Mar 2026 15:15:59 -0800 Subject: [PATCH 6/6] init.py for pytest --- src/tests/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/tests/__init__.py diff --git a/src/tests/__init__.py b/src/tests/__init__.py new file mode 100644 index 00000000..e69de29b