From c1197f74a040b17c6b7e4458d9019677677f8fe3 Mon Sep 17 00:00:00 2001 From: karamouche Date: Tue, 14 Jul 2026 09:48:43 -0400 Subject: [PATCH 01/10] feat: add MkDocs configuration and documentation structure --- .github/workflows/ci.yml | 11 + .github/workflows/docs.yml | 57 +++ .gitignore | 5 +- AGENTS.md | 2 +- CONTRIBUTING.md | 9 +- README.md | 7 +- docs/concepts.md | 33 ++ .../guide.md} | 4 +- docs/contributing/index.md | 42 ++ docs/getting-started.md | 58 +++ docs/index.md | 38 ++ docs/languages.md | 30 ++ docs/presets.md | 47 ++ docs/{ => reference}/steps.md | 0 docs/usage/cli.md | 40 ++ docs/usage/python.md | 56 +++ mkdocs.yml | 81 +++ pyproject.toml | 5 + scripts/generate_step_docs.py | 8 +- uv.lock | 463 ++++++++++++++++++ 20 files changed, 982 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/docs.yml create mode 100644 docs/concepts.md rename docs/{contributing-guide.md => contributing/guide.md} (98%) create mode 100644 docs/contributing/index.md create mode 100644 docs/getting-started.md create mode 100644 docs/index.md create mode 100644 docs/languages.md create mode 100644 docs/presets.md rename docs/{ => reference}/steps.md (100%) create mode 100644 docs/usage/cli.md create mode 100644 docs/usage/python.md create mode 100644 mkdocs.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9bf271a..cee3e6f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,3 +54,14 @@ jobs: python-version: ${{ matrix.python-version }} - run: uv sync --group dev - run: uv run pytest + + docs: + name: Docs (mkdocs) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: astral-sh/setup-uv@v7 + with: + python-version: "3.13" + - run: uv sync --group docs + - run: uv run mkdocs build --strict diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..6cd080d --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,57 @@ +name: Docs + +on: + push: + branches: [main] + paths: + - "docs/**" + - "mkdocs.yml" + - "pyproject.toml" + - "uv.lock" + - "scripts/generate_step_docs.py" + - "normalization/**" + - ".github/workflows/docs.yml" + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + name: Build MkDocs site + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - uses: astral-sh/setup-uv@v7 + with: + python-version: "3.13" + + - name: Install dependencies + run: uv sync --group docs + + - name: Build site + run: uv run mkdocs build --strict + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: site + + deploy: + name: Deploy to GitHub Pages + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 4d7ac38..8c5b973 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,7 @@ wheels/ .vscode/ .idea/ .cursor/ -.claude/ \ No newline at end of file +.claude/ + +# MkDocs +site/ \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 74de346..ab589f2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -157,4 +157,4 @@ Never modify a published preset YAML. Never let a preset reference a step that h - [ ] Add unit tests in `tests/unit/steps/` - [ ] If it involves placeholder protection, add both protect and restore to `steps/text/placeholders.py` and update `pipeline/base.py`'s `validate()` accordingly; use `ProtectStep`/`RestoreStep` base classes where the contract fits, otherwise use `TextStep` directly and document why in the docstring - [ ] Add the step name to relevant preset YAMLs if needed (new preset version if existing presets are affected) -- [ ] If you added or changed the class docstring, run `python scripts/generate_step_docs.py` to regenerate `docs/steps.md` +- [ ] If you added or changed the class docstring, run `python scripts/generate_step_docs.py` to regenerate `docs/reference/steps.md` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2448a49..c41eefd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,7 +20,7 @@ A new language requires: 4. Decorate with `@register_language` and add one import to `normalization/languages/__init__.py` 5. Add tests under `tests/unit/languages/` and a per-language CSV in `tests/e2e/files/{preset}/` (e.g. `tests/e2e/files/gladia-3/fr.csv`) -See [docs/contributing-guide.md](docs/contributing-guide.md) for the full checklist and design rules. +See [docs/contributing/guide.md](docs/contributing/guide.md) for the full checklist and design rules. ### Add a new normalization step @@ -33,9 +33,9 @@ A new step requires: 3. Add an import to `steps/text/__init__.py` or `steps/word/__init__.py` 4. Add unit tests under `tests/unit/steps/` 5. Add the step name to the relevant preset YAML, or create a new preset version -6. If you added or changed the class docstring, regenerate `docs/steps.md`: `uv run scripts/generate_step_docs.py` +6. If you added or changed the class docstring, regenerate `docs/reference/steps.md`: `uv run scripts/generate_step_docs.py` -See [docs/contributing-guide.md](docs/contributing-guide.md) for base class selection and test conventions. +See [docs/contributing/guide.md](docs/contributing/guide.md) for base class selection and test conventions. ### Report a bug @@ -52,7 +52,7 @@ Open a GitHub issue with the `question` label. We're happy to help. ```bash git clone https://github.com/gladiaio/normalization.git cd normalization -uv sync +uv sync --all-groups uv run pre-commit install --install-hooks # install hooks once ``` @@ -63,6 +63,7 @@ uv run pytest # run tests uv run ruff check . # lint uv run ruff format . # format uv run ty check # type-check +uv run mkdocs serve # preview docs at http://127.0.0.1:8000 ``` --- diff --git a/README.md b/README.md index c8874bf..43af77f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

A lightweight library for normalizing speech transcripts before computing WER.
- Quick Start · Step Reference · Contributing + Quick Start · Documentation · Contributing

@@ -106,7 +106,7 @@ Every pipeline runs exactly **three stages**, always in this order: This ordering is a hard constraint. Some steps depend on earlier steps having run (e.g. a placeholder protecting a decimal point in Stage 1 must be restored in Stage 3, so that `remove_symbols` doesn't destroy it in between). -Pipelines are defined declaratively in **YAML presets**. Each preset lists the steps that run in each stage and the order they run in. See the full [step reference](docs/steps.md) for every available step. +Pipelines are defined declaratively in **YAML presets**. Each preset lists the steps that run in each stage and the order they run in. See the full [step reference](https://gladiaio.github.io/normalization/reference/steps/) for every available step. ## Supported languages @@ -120,6 +120,7 @@ Pipelines are defined declaratively in **YAML presets**. Each preset lists the s | `fr` | French | | `it` | Italian | | `nl` | Dutch | +| `no` | Norwegian | | `sv` | Swedish | Unsupported language codes fall back to a safe default that applies language-independent normalization only. @@ -140,7 +141,6 @@ stages: - casefold_text - remove_symbols - remove_diacritics - - normalize_whitespace word: - apply_word_replacements @@ -148,7 +148,6 @@ stages: text_post: - restore_email_at_symbol_with_word - restore_email_dot_symbol_with_word - - normalize_whitespace ``` _Load from your custom configuration:_ diff --git a/docs/concepts.md b/docs/concepts.md new file mode 100644 index 0000000..107f676 --- /dev/null +++ b/docs/concepts.md @@ -0,0 +1,33 @@ +# How it works + +Every pipeline runs **three stages**, always in this order: + +```text +text ──► [1] text_pre ──► split ──► [2] word ──► join ──► [3] text_post ──► text +``` + +| Stage | Operates on | Typical work | +| --- | --- | --- | +| **1. Text pre** | Full string | Protect symbols, expand contractions/numbers, convert currency/degrees, casefold, remove punctuation | +| **2. Word** | One token | Replacements (`vs` → `versus`), skip emails | +| **3. Text post** | Full string | Restore placeholders, format times, collapse digits, whitespace | + +This order is a hard constraint. Placeholder steps illustrate why: a decimal point is **protected** in stage 1 so `remove_symbols` cannot erase it, then **restored** in stage 3. + +## Steps + +A step is a small, stateless transform with a unique `name`. Steps register themselves via `@register_step`; presets reference those names. + +See the full [step reference](reference/steps.md). + +## Presets + +A preset is a YAML file that lists which steps run in each stage and in what order. Published presets are **immutable** — new behavior means a new preset file (e.g. `gladia-4`). + +Details and custom YAML: [Presets](presets.md). + +## Languages + +Language-specific data and behavior live under `normalization/languages/`. Unknown codes fall back to a language-agnostic default. + +Details: [Languages](languages.md). diff --git a/docs/contributing-guide.md b/docs/contributing/guide.md similarity index 98% rename from docs/contributing-guide.md rename to docs/contributing/guide.md index e57407f..596d705 100644 --- a/docs/contributing-guide.md +++ b/docs/contributing/guide.md @@ -10,7 +10,7 @@ Every pipeline runs exactly **three stages**, always in this order: 2. **Word processing** — per-token transforms after splitting on spaces (replacements, filler removal, …) 3. **Text post-processing** — full-text cleanup after rejoining words (placeholder restoration, digit collapsing, …) -This ordering is a hard constraint — some steps depend on earlier steps having run. See the [README](../README.md) for more detail. +This ordering is a hard constraint — some steps depend on earlier steps having run. See [How it works](../concepts.md) for more detail. --- @@ -49,7 +49,7 @@ This is the central design rule. Ask: "does the _logic_ change by language, or j - [ ] Add unit tests in `tests/unit/steps/` - [ ] If it involves placeholder protection, add both protect and restore to `steps/text/placeholders.py` and update `pipeline/base.py`'s `validate()` accordingly - [ ] Add the step name to relevant preset YAMLs if needed (new preset version if existing presets are affected) -- [ ] If you added or changed the class docstring, run `uv run scripts/generate_step_docs.py` to regenerate `docs/steps.md` +- [ ] If you added or changed the class docstring, run `uv run scripts/generate_step_docs.py` to regenerate `docs/reference/steps.md` ### Choosing a base class diff --git a/docs/contributing/index.md b/docs/contributing/index.md new file mode 100644 index 0000000..16d3695 --- /dev/null +++ b/docs/contributing/index.md @@ -0,0 +1,42 @@ +# Contributing + +Bug reports, new steps, and new language support are welcome. + +## Setup + +```bash +git clone https://github.com/gladiaio/normalization.git +cd normalization +uv sync --all-groups +uv run pre-commit install --install-hooks +``` + +## Checks + +```bash +uv run pytest +uv run ruff check . +uv run ruff format . +uv run ty check +``` + +## Docs site (this site) + +```bash +uv sync --group docs +uv run mkdocs serve # http://127.0.0.1:8000 +uv run mkdocs build +``` + +## How to help + +| Contribution | Start here | +| --- | --- | +| New language | [Checklist](guide.md#adding-a-new-language-checklist) | +| New step | [Checklist](guide.md#adding-a-new-step-checklist) | +| Bug report | GitHub issue with reproduce steps + expected vs actual | +| Question | GitHub issue with the `question` label | + +Full design rules, base-class choice, and test conventions: [Contributor guide](guide.md). + +Also see [`CONTRIBUTING.md`](https://github.com/gladiaio/normalization/blob/main/CONTRIBUTING.md) in the repo root for PR workflow and commit style. diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..28261fc --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,58 @@ +# Getting started + +## Install + +=== "pip" + + ```bash + pip install gladia-normalization + ``` + +=== "uv" + + ```bash + uv add gladia-normalization + ``` + +=== "From source" + + ```bash + git clone https://github.com/gladiaio/normalization.git + cd normalization + uv sync + ``` + +Requires **Python 3.10+**. + +## Normalize text + +```python +from normalization import load_pipeline + +pipeline = load_pipeline("gladia-3", language="en") +print(pipeline.normalize("It's $50 at 3:00PM")) +# it is 50 dollars at 3 pm +``` + +- First argument: built-in preset name (`"gladia-3"`) or path to a YAML file +- Second argument: language code (`"en"`, `"fr"`, …) — see [Languages](languages.md) + +## CLI + +```bash +gladia-normalization "A tea cost £2 at 3:30PM" --language en +``` + +Or without installing permanently: + +```bash +uvx gladia-normalization "A tea cost £2 at 3:30PM" --language en +``` + +More options in the [CLI reference](usage/cli.md). + +## Next steps + +- [Python API](usage/python.md) — `describe()`, custom presets +- [How it works](concepts.md) — three-stage pipeline +- [Step reference](reference/steps.md) — every available step diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..30f6351 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,38 @@ +# gladia-normalization + +Normalize speech-to-text transcripts before computing Word Error Rate (WER), so formatting differences stop looking like recognition errors. + +| Ground truth | STT output | Without normalization | +| --- | --- | --- | +| It's $50 | it is fifty dollars | treated as errors | +| 3:00 PM | 3 pm | treated as errors | +| Mr. Smith | mister smith | treated as errors | + +```text +Input: "It's $50.9 at 3:00PM — y'know, roughly." +Output: "it is 50 point 9 dollars at 3 pm you know roughly" +``` + +## Quick example + +```python +from normalization import load_pipeline + +pipeline = load_pipeline("gladia-3", language="en") +pipeline.normalize("It's $50 at 3:00PM") +# => "it is 50 dollars at 3 pm" +``` + +```bash +pip install gladia-normalization +gladia-normalization "It's $50 at 3:00PM" --language en +``` + +## What you get + +- **Deterministic presets** — same YAML preset → same output every time +- **Language-aware** — [supported languages](languages.md) with a safe fallback for others +- **Composable steps** — build custom pipelines without forking the core +- **Inspectable** — `pipeline.describe()` shows exactly what will run + +Continue with [Getting started](getting-started.md) or skim [How it works](concepts.md). diff --git a/docs/languages.md b/docs/languages.md new file mode 100644 index 0000000..eff3331 --- /dev/null +++ b/docs/languages.md @@ -0,0 +1,30 @@ +# Languages + +Pass a language code to `load_pipeline` or `--language`. Unknown codes fall back to a language-agnostic default (independent transforms only). + +| Code | Language | +| --- | --- | +| `da` | Danish | +| `de` | German | +| `en` | English | +| `es` | Spanish | +| `fi` | Finnish | +| `fr` | French | +| `it` | Italian | +| `nl` | Dutch | +| `no` | Norwegian | +| `sv` | Swedish | + +## What “language-aware” means + +Each language folder under `normalization/languages/` provides: + +- **Config data** — currency words, fillers, digit words, time maps, etc. (`LanguageConfig`) +- **Behavior overrides** — only when the algorithm itself differs (e.g. number expansion) +- **Word replacements** — a plain dict in `replacements.py` + +Steps stay language-agnostic: they read `operators.config.*` or call operator methods. + +## Adding a language + +See the [contributor guide](contributing/guide.md#adding-a-new-language-checklist). diff --git a/docs/presets.md b/docs/presets.md new file mode 100644 index 0000000..8fea3b5 --- /dev/null +++ b/docs/presets.md @@ -0,0 +1,47 @@ +# Presets + +A preset declares which steps run in each stage and in what order. + +## Built-in + +| Name | Package path | +| --- | --- | +| `gladia-3` | `normalization/presets/gladia-3.yaml` | + +```python +load_pipeline("gladia-3", language="en") +``` + +## Custom preset + +```yaml +name: my-preset-v1 + +stages: + text_pre: + - protect_email_symbols + - expand_contractions + - casefold_text + - remove_symbols + - remove_diacritics + + word: + - apply_word_replacements + + text_post: + - restore_email_at_symbol_with_word + - restore_email_dot_symbol_with_word +``` + +```python +from normalization import load_pipeline + +pipeline = load_pipeline("path/to/my-preset.yaml", language="en") +``` + +## Rules + +- Step names must match a registered step's `name` attribute — see [Step reference](reference/steps.md) +- List order = execution order +- Every `protect_*` in `text_pre` needs a matching `restore_*` in `text_post` (validated at load) +- **Published presets are immutable** — change behavior by shipping a new versioned file, never by editing an existing one diff --git a/docs/steps.md b/docs/reference/steps.md similarity index 100% rename from docs/steps.md rename to docs/reference/steps.md diff --git a/docs/usage/cli.md b/docs/usage/cli.md new file mode 100644 index 0000000..8a12ee4 --- /dev/null +++ b/docs/usage/cli.md @@ -0,0 +1,40 @@ +# CLI + +Installed as `gladia-normalization` (entry point from the package). + +## Examples + +```bash +# Single string +gladia-normalization "A tea cost £2 at 3:30PM" --language en + +# File +gladia-normalization --file transcript.txt --language en + +# Stdin +echo "A tea cost £2 at 3:30PM" | gladia-normalization --language fr + +# Custom preset +gladia-normalization "..." --preset path/to/my-preset.yaml --language en + +# Inspect pipeline (JSON) +gladia-normalization --describe --language en +``` + +Without a permanent install: + +```bash +uvx gladia-normalization "A tea cost £2 at 3:30PM" --language en +``` + +## Options + +| Flag | Default | Description | +| --- | --- | --- | +| `text` | — | Positional text to normalize (or use stdin / `--file`) | +| `-l`, `--language` | `en` | Language code | +| `-p`, `--preset` | `gladia-3` | Built-in preset name or YAML path | +| `-f`, `--file` | — | Read input from a file (mutually exclusive with positional text) | +| `--describe` | — | Print pipeline description as JSON and exit | + +Provide either positional text or `--file`, not both. diff --git a/docs/usage/python.md b/docs/usage/python.md new file mode 100644 index 0000000..961fd21 --- /dev/null +++ b/docs/usage/python.md @@ -0,0 +1,56 @@ +# Python API + +Public entry points: + +```python +from normalization import load_pipeline, NormalizationPipeline +``` + +## `load_pipeline` + +```python +load_pipeline(preset: str | Path, language: str) -> NormalizationPipeline +``` + +| Argument | Meaning | +| --- | --- | +| `preset` | Built-in name (`"gladia-3"`) or path to a YAML file | +| `language` | Language code (`"en"`, `"fr"`, …). Unknown codes use the safe default | + +Raises `FileNotFoundError` if the preset cannot be resolved. Validates protect/restore pairing at load time. + +## `NormalizationPipeline.normalize` + +```python +pipeline.normalize(text: str) -> str +``` + +Runs all three stages and returns the normalized string. + +```python +pipeline = load_pipeline("gladia-3", language="en") +pipeline.normalize("Mr. Smith paid $1,000") +# => "mister smith paid 1000 dollars" +``` + +## Inspect a pipeline + +```python +pipeline.describe() +# { +# "name": "gladia-3", +# "language": "en", +# "text_pre_steps": [...], +# "word_steps": [...], +# "text_post_steps": [...], +# } +``` + +## Custom preset file + +```python +pipeline = load_pipeline("path/to/my-preset.yaml", language="fr") +result = pipeline.normalize("Bonjour — ça coûte 50€") +``` + +Preset format: [Presets](../presets.md). diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..7a483e0 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,81 @@ +site_name: gladia-normalization +site_description: Normalize speech-to-text transcripts for fair WER comparison +site_url: https://gladiaio.github.io/normalization/ +repo_url: https://github.com/gladiaio/normalization +repo_name: gladiaio/normalization +edit_uri: edit/main/docs/ +docs_dir: docs +site_dir: site + +theme: + name: material + palette: + - media: "(prefers-color-scheme: light)" + scheme: default + primary: blue grey + accent: teal + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: blue grey + accent: teal + toggle: + icon: material/brightness-4 + name: Switch to light mode + features: + - content.code.copy + - content.tabs.link + - navigation.footer + - navigation.instant + - navigation.sections + - navigation.top + - search.highlight + - search.suggest + - toc.follow + icon: + repo: fontawesome/brands/github + +plugins: + - search + +validation: + omitted_files: warn + absolute_links: info + unrecognized_links: warn + anchors: warn + +markdown_extensions: + - admonition + - attr_list + - pymdownx.details + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.superfences + - pymdownx.tabbed: + alternate_style: true + - tables + - toc: + permalink: true + +nav: + - Home: index.md + - Getting started: getting-started.md + - How it works: concepts.md + - Usage: + - Python API: usage/python.md + - CLI: usage/cli.md + - Presets: presets.md + - Languages: languages.md + - Step reference: reference/steps.md + - Contributing: + - Overview: contributing/index.md + - Contributor guide: contributing/guide.md + +extra: + social: + - icon: fontawesome/brands/github + link: https://github.com/gladiaio/normalization + - icon: fontawesome/solid/globe + link: https://gladia.io diff --git a/pyproject.toml b/pyproject.toml index 1e9a8e9..61a1043 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,11 +38,16 @@ gladia-normalization = "normalization.cli:main" [project.urls] Homepage = "https://github.com/gladiaio/normalization" +Documentation = "https://gladiaio.github.io/normalization/" Repository = "https://github.com/gladiaio/normalization" "Bug Tracker" = "https://github.com/gladiaio/normalization/issues" [dependency-groups] dev = ["pre-commit>=4.5.1", "ty>=0.0.20", "pytest>=9.0.3", "pygments>=2.20.0", "ruff>=0.15.2"] +docs = [ + "mkdocs>=1.6.1", + "mkdocs-material>=9.7.6", +] [build-system] requires = ["hatchling"] diff --git a/scripts/generate_step_docs.py b/scripts/generate_step_docs.py index b644f1c..aea7bb2 100644 --- a/scripts/generate_step_docs.py +++ b/scripts/generate_step_docs.py @@ -5,7 +5,7 @@ Usage: python scripts/generate_step_docs.py [output_path] -Output defaults to docs/steps.md. +Output defaults to docs/reference/steps.md. """ import inspect @@ -74,5 +74,9 @@ def generate(output_path: Path) -> None: if __name__ == "__main__": - out = Path(sys.argv[1]) if len(sys.argv) > 1 else ROOT / "docs" / "steps.md" + out = ( + Path(sys.argv[1]) + if len(sys.argv) > 1 + else ROOT / "docs" / "reference" / "steps.md" + ) generate(out) diff --git a/uv.lock b/uv.lock index bc77bd9..07d63ce 100644 --- a/uv.lock +++ b/uv.lock @@ -11,6 +11,37 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c2/76/783b75a21ce3563b8709050de030ae253853b147bd52e141edc1025aa268/anyascii-0.3.3-py3-none-any.whl", hash = "sha256:f5ab5e53c8781a36b5a40e1296a0eeda2f48c649ef10c3921c1381b1d00dee7a", size = 345090, upload-time = "2025-06-29T03:33:28.356Z" }, ] +[[package]] +name = "babel" +version = "2.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, +] + +[[package]] +name = "backrefs" +version = "7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/a7dd63622beef68cc0d3c3c36d472e143dd95443d5ebf14cd1a5b4dfbf11/backrefs-7.0.tar.gz", hash = "sha256:4989bb9e1e99eb23647c7160ed51fb21d0b41b5d200f2d3017da41e023097e82", size = 7012453, upload-time = "2026-04-28T16:28:04.215Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/39/39a31d7eae729ea14ed10c3ccef79371197177b9355a86cb3525709e8502/backrefs-7.0-py310-none-any.whl", hash = "sha256:b57cd227ea556b0aed3dc9b8da4628db4eabc0402c6d7fcfc69283a93955f7e9", size = 380824, upload-time = "2026-04-28T16:27:55.647Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b5/9302644225ba7dfa934a2ff2b9c7bb85701313a90dddb3dfaf693fa5bae2/backrefs-7.0-py311-none-any.whl", hash = "sha256:a0fa7360c63509e9e077e174ef4e6d3c21c8db94189b9d957289ae6d794b9475", size = 392626, upload-time = "2026-04-28T16:27:57.42Z" }, + { url = "https://files.pythonhosted.org/packages/36/da/87912ddec6e06feffbaa3d7aa18fc6352bee2e8f1fee185d7d1690f8f4e8/backrefs-7.0-py312-none-any.whl", hash = "sha256:ca42ce6a49ace3d75684dfa9937f3373902a63284ecb385ce36d15e5dcb41c12", size = 398537, upload-time = "2026-04-28T16:27:58.913Z" }, + { url = "https://files.pythonhosted.org/packages/00/bb/90ba423612b6aa0adccc6b1874bcd4a9b44b660c0c16f346611e00f64ac3/backrefs-7.0-py313-none-any.whl", hash = "sha256:f2c52955d631b9e1ac4cd56209f0a3a946d592b98e7790e77699339ae01c102a", size = 400491, upload-time = "2026-04-28T16:28:00.928Z" }, + { url = "https://files.pythonhosted.org/packages/3e/5c/fb93d3092640a24dfb7bd7727a24016d7c01774ca013e60efd3f683c8002/backrefs-7.0-py314-none-any.whl", hash = "sha256:a6448b28180e3ca01134c9cf09dcebafad8531072e09903c5451748a05f24bc9", size = 412349, upload-time = "2026-04-28T16:28:02.412Z" }, +] + +[[package]] +name = "certifi" +version = "2026.6.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" }, +] + [[package]] name = "cfgv" version = "3.5.0" @@ -20,6 +51,105 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, ] +[[package]] +name = "charset-normalizer" +version = "3.4.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/2a/23f34ec9d04624958e137efdc394888716353190e75f25dd22c7a2c7a8aa/charset_normalizer-3.4.9.tar.gz", hash = "sha256:673611bbd43f0810bec0b0f028ddeaaa501190339cac411f347ac76917c3ae7b", size = 152439, upload-time = "2026-07-07T14:34:58.454Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/81/8e983840c6e5b93b33c2ba81aa3d52c2e42f0e9a690ce7607a2e61da4a5c/charset_normalizer-3.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd6280cf040f233bd7d3407b743b4b4c74f70e8e1c4199cb112a62c941c0772a", size = 322240, upload-time = "2026-07-07T14:32:36.236Z" }, + { url = "https://files.pythonhosted.org/packages/de/d1/b4319dc3229d8272fba305e206fc0a148e2de8d4087917ce62ae6382f359/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa99adc8f081b475a12843953db36831eaf83ec33eb46a90629ca6a5de45a616", size = 216475, upload-time = "2026-07-07T14:32:38.142Z" }, + { url = "https://files.pythonhosted.org/packages/80/33/6c99c1b3e6b8bf730e1bc809b9a2608f224145069114c479a2e9e1494346/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c1225416b463483160e4af85d5fc3a9690ccb53fd4b1865a6437825f5ede3209", size = 238670, upload-time = "2026-07-07T14:32:39.658Z" }, + { url = "https://files.pythonhosted.org/packages/7f/f4/ffbb83546e1f198ecc70ecd372b65cf2b50f9068b380abd67640f17a8e18/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:16d10d789dd9bcca1173c95af82c58433122564b7bc39385124be735a35cbe99", size = 233476, upload-time = "2026-07-07T14:32:41.155Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5f/b98b8da398637b551e427e7be922bdec19177dc54d6811dcdaa503f23aac/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bb41182d93ea91f60b4bc8fbf4c820c69ef8a12ab2d917f3f1834f1acad07e8", size = 223817, upload-time = "2026-07-07T14:32:42.592Z" }, + { url = "https://files.pythonhosted.org/packages/36/31/a276bb2e66243072a3fd06fdcab9cbb61a305b02143d70d2bda21d888fa8/charset_normalizer-3.4.9-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:bcf74c1df76758a395bf0af608c04c82257523f55c9868b334f06270d0f2112b", size = 207974, upload-time = "2026-07-07T14:32:44.258Z" }, + { url = "https://files.pythonhosted.org/packages/5e/be/7ee4453d7e88dfbc4104ccd34900b9f2c7c17dac22881865fe0e82424a25/charset_normalizer-3.4.9-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b5314963fce9b0b12743891de876e724997864ee22aa496f903f426c7e2fa5b2", size = 221655, upload-time = "2026-07-07T14:32:45.64Z" }, + { url = "https://files.pythonhosted.org/packages/1d/85/181c652953eb5276d198f375b1dd641047392050098100a3a02d6534f657/charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e9701d0049d92c16703a42771b98d560b95248949f23f8cf7b4eddd201814fb9", size = 219229, upload-time = "2026-07-07T14:32:47.376Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e7/aaf6da33fc9f4691cda8f7efbc9f69179d3d39ec8a4799baf273ee1d8db0/charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:65a7ff3f705e57d392f7261b6d0550fe137c3019477431f1c355e0db0a7d3e15", size = 209704, upload-time = "2026-07-07T14:32:48.855Z" }, + { url = "https://files.pythonhosted.org/packages/63/01/f2fb3bd3a73be48b173ee0c6aa8d2497af97d5663a8c4c4b491de4c62f7a/charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79580094b00d1789d1f93ea55bc43cb2f611910c72235b7657f3482ddcc1b22d", size = 226243, upload-time = "2026-07-07T14:32:50.239Z" }, + { url = "https://files.pythonhosted.org/packages/c4/02/c57a22739fe05246b0b5783b3bfb6afaac4eebb46f3ececdfb2f048f780e/charset_normalizer-3.4.9-cp310-cp310-win32.whl", hash = "sha256:432786d3561e69aeeae6c7e8648964ce0ad05736120135601f87ac26b9c83381", size = 150935, upload-time = "2026-07-07T14:32:51.676Z" }, + { url = "https://files.pythonhosted.org/packages/37/8d/ca39a7559a4797505530d084fd3a49a2c959efbbbff146302fb7be4e3b35/charset_normalizer-3.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:8c041122946b7ba21bb32c45b1aa57b1be35527690aeb3c5c234521085632eee", size = 162314, upload-time = "2026-07-07T14:32:53.193Z" }, + { url = "https://files.pythonhosted.org/packages/01/da/a44bd7a13d426e69e4894557106cd58669097bfad4a8681123b618fbfc5d/charset_normalizer-3.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:375b83ed0aecfce76c16d198fbc21f3b11b337d68662bea0a995046682a11419", size = 153075, upload-time = "2026-07-07T14:32:54.554Z" }, + { url = "https://files.pythonhosted.org/packages/0b/e3/85ec501f206fb049259288c1f3506e53876937fb00edb47009348e66756b/charset_normalizer-3.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0e94703ec9684807f20cfb5eed95c70f67f2a8f21ad620146d7b5a13677b93e5", size = 317075, upload-time = "2026-07-07T14:32:56.021Z" }, + { url = "https://files.pythonhosted.org/packages/c3/69/2a5385192e67175f7d8bd5ce4f57c24bc956439adeae5c13a99aa28a53d1/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a441ea71902098ffe78c5abe6c494f44160b4af614ed16c3d9a3b1d17fd8ee2", size = 213837, upload-time = "2026-07-07T14:32:57.78Z" }, + { url = "https://files.pythonhosted.org/packages/b3/46/03ddc7da576d814fe0a36dd1f0fd3258e95404b4b2e3c026b7923d7e133f/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:304b13570067b2547562e308af560b3963857b1fa90bd6afd978130130fe2d6a", size = 235503, upload-time = "2026-07-07T14:32:59.205Z" }, + { url = "https://files.pythonhosted.org/packages/4e/6e/de0229a7ef40f6f9d28a837eebf4ec47bdca5dab4e900c84f22919af636a/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4773092f8019072343a7447203308b176e10199920eb02d6195e81bbb3274c29", size = 229944, upload-time = "2026-07-07T14:33:00.803Z" }, + { url = "https://files.pythonhosted.org/packages/a5/34/49b9060e8418b14fb5cba9cf6bfb383111e2538a03a1fb18e66a95aeb3d5/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:04ce310cb89c15df659582aee80a0603788732a5e017d5bd5c81158106ce249c", size = 221276, upload-time = "2026-07-07T14:33:02.199Z" }, + { url = "https://files.pythonhosted.org/packages/44/95/80282cce0fae9c3061203d723ee87da996aed79679e65d8935050ee7ca1f/charset_normalizer-3.4.9-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:c0323c9daef75ef2e5083624b4585018a0c9d5e3b40f607eed81a311270b934b", size = 205260, upload-time = "2026-07-07T14:33:03.698Z" }, + { url = "https://files.pythonhosted.org/packages/0c/74/2f62c8821b969ea3bd67cc2e6976834f48ca5d12664d2559ebcd9bcfbed7/charset_normalizer-3.4.9-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:871ff67ea1aad4dfd91736464934d56b32dac49f9fbe16cddba36198a7b3a0db", size = 217786, upload-time = "2026-07-07T14:33:05.12Z" }, + { url = "https://files.pythonhosted.org/packages/d9/8d/feabb82cb49fcad14515b1d7d1ca4787b0da7fc723a212bf89bc9e0fac52/charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:67830fc78e67501f47bb950471b2dcb9b35b140084429318e862895a8e89c993", size = 216798, upload-time = "2026-07-07T14:33:06.629Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ff/c946d63bc3786d5b84d960b0f7ab7e25b828486a946b5aa997625bcaf6a6/charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:3d92613ec25e43b05f042302531ec0f00b8445190e43325880cbd6ab7c2581da", size = 206429, upload-time = "2026-07-07T14:33:08.006Z" }, + { url = "https://files.pythonhosted.org/packages/af/ba/5e5007c370702f85d2ef75791fac7943ed41e080364a673b20142e430e3e/charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:280081916dc341820640489a66e4696049401ef1cf6dd672f672e70ad915aca3", size = 223066, upload-time = "2026-07-07T14:33:09.783Z" }, + { url = "https://files.pythonhosted.org/packages/83/d5/9096aa3cf532dfad237861544eb47a0f20d5adbf1039760fed8eaae935d9/charset_normalizer-3.4.9-cp311-cp311-win32.whl", hash = "sha256:ac351b3b8014eead140e77e9717e2992c6bbe30b63bc3422422eb84865412e3d", size = 150456, upload-time = "2026-07-07T14:33:11.217Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a1/e29995109e455dc8eff8d0fac6ae509be39561318a7cfeac5d33ad029213/charset_normalizer-3.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:6366a16e1a25018694d6a5d784d09b046edc9eac40ea2b54065c3052672516a1", size = 161410, upload-time = "2026-07-07T14:33:12.743Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8d/1569f4d0032d6ba2a4fe4591c35bf87868c600c41a71eb5c2e1ffa8464c2/charset_normalizer-3.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:1d22856ffbe153a602df38e4a5464f0b748a54002e0d69ac6d2ad0a197cc99ec", size = 152649, upload-time = "2026-07-07T14:33:14.173Z" }, + { url = "https://files.pythonhosted.org/packages/70/4a/ecbd131485c07fcdfad54e28946d513e3da22ef3b4bd854dcafae54ec739/charset_normalizer-3.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:45b0cc4e3556cd875e09102988d1ab8356c998b596c9fced84547c8138b487a0", size = 319300, upload-time = "2026-07-07T14:33:15.666Z" }, + { url = "https://files.pythonhosted.org/packages/ec/96/5d9364e3342d69f3a045e1777bc47c85c383e6e9466d561b33fdb419d1f9/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b2aff1c7b3884512b9512c3eaadd9bab39fb45042ffaaa1dd08ff2b9f8109d9", size = 215802, upload-time = "2026-07-07T14:33:17.031Z" }, + { url = "https://files.pythonhosted.org/packages/4b/4c/5361f9aa7f2cb58d94f2ab831b3d493f69efb1d239654b4744e3c09527cb/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9104ed0bd76a429d46f9ec0dbc9b08ad1d2dcdf2b00a5a0daa1c145329b35b44", size = 237171, upload-time = "2026-07-07T14:33:18.576Z" }, + { url = "https://files.pythonhosted.org/packages/50/78/ce342ca4ff30b2eb49fe6d9578df85974f90c67d294113e94efdd9664cbd/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b86a2b16095d250c6f58b3d9b2eee6f4147754344f3dab0922f7c9bf7d226c9", size = 233075, upload-time = "2026-07-07T14:33:20.084Z" }, + { url = "https://files.pythonhosted.org/packages/01/c4/4fa4c8b3097a11f3c5f09a35b72ed6855fb1d332469504962ab7bafcc702/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e226f6218febc71f6c1fc2fafb91c226f75bdc1d8fb12d66823716e891608fd", size = 224256, upload-time = "2026-07-07T14:33:21.747Z" }, + { url = "https://files.pythonhosted.org/packages/87/3a/ad914516df7e358a81aae018caa5e0470ba827fa6d763b1d2e87d920a5f6/charset_normalizer-3.4.9-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:90c44bc373b7687f6948b693cceaea1348ae0975d7474746559494468e3c1d84", size = 208784, upload-time = "2026-07-07T14:33:23.313Z" }, + { url = "https://files.pythonhosted.org/packages/d7/74/3c12f9755717dfe5c5c87da63f35d765fa0c00382ec26bf23f7fae34f2ba/charset_normalizer-3.4.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cdef90ae47919cae358d8ab15797a800ed41da7aba5d72419fb510729e2ed4b", size = 219928, upload-time = "2026-07-07T14:33:24.814Z" }, + { url = "https://files.pythonhosted.org/packages/33/9a/895095b83e7907abd6d3d99aad3a38ad0d9686cc186cb0c94c24320fe63e/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:60f44ade2cf573dad7a277e6f8ca9a51a21dda572b13bd7d8539bb3cd5dbedde", size = 218489, upload-time = "2026-07-07T14:33:26.42Z" }, + { url = "https://files.pythonhosted.org/packages/a1/34/ef5c05f412f42520d7709b7d3784d19640839eb7366ded1755511585429f/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a1786910334ed46ab1dd73222f2cd1e05c2c3bb39f6dddb4f8b36fc382058a39", size = 210267, upload-time = "2026-07-07T14:33:27.952Z" }, + { url = "https://files.pythonhosted.org/packages/83/dc/9b29fa4412b318bf3bfea985c35d67eb55e04b59a7c3f2237168b0e0be6f/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03d07803992c6c7bbc976327f34b18b6160327fc81cb82c9d504720ac0be3b62", size = 226030, upload-time = "2026-07-07T14:33:29.397Z" }, + { url = "https://files.pythonhosted.org/packages/0e/42/6dbc00b8cd16011691203e33570fa42ed5746599a2e878112d16eab403a3/charset_normalizer-3.4.9-cp312-cp312-win32.whl", hash = "sha256:78841cccf1af7b40f6f716338d50c0902dbe88d9f800b3c973b7a9a0a693a642", size = 151185, upload-time = "2026-07-07T14:33:30.781Z" }, + { url = "https://files.pythonhosted.org/packages/80/cc/f920afd1a23c58ccd53c1d36085a71893a4737ff5e66e0371efab6809850/charset_normalizer-3.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:4b3dac63058cc36820b0dd072f89898604e2d39686fe05321729d00d8ac185a0", size = 162557, upload-time = "2026-07-07T14:33:32.176Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e6/0386d43a261ff4e4b30c5857af7df877254b46bec7b9d1b74b6bf969a90b/charset_normalizer-3.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:78fa18e436a1a0e58dbd7e02fc4473f3f32cceb12df9dfca542d075961c307d2", size = 152665, upload-time = "2026-07-07T14:33:33.711Z" }, + { url = "https://files.pythonhosted.org/packages/b2/06/97ec2aeae780b31d742b6352218b43841a6871e2564578ca522dce4a45c3/charset_normalizer-3.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:440eede837960000d74978f0eba527be106b5b9aee0daf779d395276ed0b0614", size = 317688, upload-time = "2026-07-07T14:33:35.408Z" }, + { url = "https://files.pythonhosted.org/packages/d0/39/8ff066c672434225f8d25f8b739f992af250944392173dcc88362681c9bf/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21e764fd1e70b6a3e205a0e46f3051701f98a8cb3fad66eeb80e48bb502f8698", size = 214982, upload-time = "2026-07-07T14:33:36.996Z" }, + { url = "https://files.pythonhosted.org/packages/92/8f/3a47a3667c83c2df9483d91644c6c107de3bf8874aa1793da9d3012eb986/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e4fd89cc178bced6ad29cb3e6dd4aa63fa5017c3524dbd0b25998fb64a87cc8b", size = 236460, upload-time = "2026-07-07T14:33:38.536Z" }, + { url = "https://files.pythonhosted.org/packages/f1/60/b22cdbee7e4013dab8b0d7647fc6181120fbbbc8f7025c226d15bd5a47fc/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bd47ba7fc3ca94896759ea0109775132d3e7ab921fbf54038e1bab2e46c313c9", size = 232003, upload-time = "2026-07-07T14:33:40.059Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f8/72eb13dcabe7257035cea8aefd922caad2f110d252bf9f67c4c2ca763aee/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:84fd18bcc17526fc2b3c1af7d2b9217d32c9c04448c16ec693b9b4f1985c3d33", size = 223149, upload-time = "2026-07-07T14:33:41.631Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3e/faee8f9de92b14ee1198e9163252bb15efee7301b31256a3b6d9ebfdd0dd/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:5b10cd92fc5c498b35a8635df6d5a100207f88b63a4dc1de7ef9a548e1e2cd63", size = 207901, upload-time = "2026-07-07T14:33:43.209Z" }, + { url = "https://files.pythonhosted.org/packages/3a/25/45f30093ae27dd7b92a793b61882a38685f993700113ca36e0c9c14965e1/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4fbdde9dd4a9ce5fd52c2b3a347bb50cc89483ef783f1cb00d408c13f7a96c0", size = 219176, upload-time = "2026-07-07T14:33:44.725Z" }, + { url = "https://files.pythonhosted.org/packages/48/18/c8f397329c35e32f6a837e488986f4ae03bd2abebc453b48714991630c2f/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:416c229f77e5ea25b3dfd4b582f8d73d7e43c22320302b9ab128a2d3a0b38efe", size = 217356, upload-time = "2026-07-07T14:33:46.192Z" }, + { url = "https://files.pythonhosted.org/packages/86/7e/5ce0bba863470fd1902d5e5843968951bddf38abe4742fc97116ef4598b3/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:75286256590a6320cf106a0d28970d3560aad9ee09aa7b34fb40524792436d35", size = 209614, upload-time = "2026-07-07T14:33:47.705Z" }, + { url = "https://files.pythonhosted.org/packages/6c/ef/2473d3c4d869155be4af1191111d59c4d5c4e0173026f7e85b176e23bf65/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69b157c5d3292bcd443faca052f3096f637f1e074b98212a933c074ae23dc3b8", size = 224991, upload-time = "2026-07-07T14:33:49.238Z" }, + { url = "https://files.pythonhosted.org/packages/d0/a3/53ddae3db108a088156aa8ddfafd411ebbc1340f48c5573f697b27f69a39/charset_normalizer-3.4.9-cp313-cp313-win32.whl", hash = "sha256:51307f5c71007673a2bf8232ad973483d281e74cb99c8c5a990af1eefa6277d9", size = 150622, upload-time = "2026-07-07T14:33:50.711Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ef/6953a77c7cf2c2ff9998e6f575ab3e380119f100223381565a4f94c1f836/charset_normalizer-3.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:fe2c7201c642b7c308f1675355ad7ff7b66acfe3541625efe5a3ad38f29d6115", size = 161947, upload-time = "2026-07-07T14:33:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/6e/fb/d560d1d1555debbfe7849d9cac6145c1b537709d79576bf22557ed803b82/charset_normalizer-3.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:611057cc5d5c0afc743ba8be6bd828c17e0aaa8643f9d0a9b9bb7dea80eb8012", size = 152594, upload-time = "2026-07-07T14:33:53.486Z" }, + { url = "https://files.pythonhosted.org/packages/7e/8d/496817fa0944239ecae662dd57ea765cfeaec6a735f9f025d4b7b72e7143/charset_normalizer-3.4.9-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:0327fcd59a935777d83410750c50600ee9571af2846f71ce40f25b13da1ef380", size = 317253, upload-time = "2026-07-07T14:33:54.994Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f9/ef4a69ea338ad3c0deceea0f5f7d2380ae8b52132b06d652cb0d2cd86706/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a79d9f4d8001473a30c163556b3c3bfebec837495a412dde78b51672f6134f9", size = 215898, upload-time = "2026-07-07T14:33:56.334Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e7/5ddfd76fc061eb52de219658a4aa431cbacadf0a0219c8854f00da50d289/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33bdcc2a32c0a0e861f60841a512c8acc658c87c2ac59d89e3a46dacf7d866e4", size = 236718, upload-time = "2026-07-07T14:33:57.9Z" }, + { url = "https://files.pythonhosted.org/packages/49/ba/768fa3f36048d81c477a0ce61f813bc1454d80917ccfe550abd9f44f5e24/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f840ed6d8ecba8255df8c42b87fadeda98ddfc6eeec05e2dc66e26d46dd6f58a", size = 232519, upload-time = "2026-07-07T14:33:59.811Z" }, + { url = "https://files.pythonhosted.org/packages/f4/c4/b3e049d2aa3766180c78507110543d9d50894cc97f57de543f1be521dcdc/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c25fe15c70c59eb7c5ce8c06a1f3fa1da0ecc5ea1e7a5922c40fd2fa9b0d5046", size = 223143, upload-time = "2026-07-07T14:34:01.517Z" }, + { url = "https://files.pythonhosted.org/packages/19/79/55c32d06d76ae4feafe053f061f3e3ab70bcf19f4007797ce8c3efda7830/charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:f7fb7d750cfa0a070d2c24e831fd3481019a60dd317ea2b39acbcebc08b6ed81", size = 206742, upload-time = "2026-07-07T14:34:03.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/e0/47c079dd82d217c807479cd59ffd30af56307ea31c108b75758970459ad3/charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1c96a7a18b9690a4d46df09e3e3382406ae3213727cd1019ebade1c4a81917", size = 219191, upload-time = "2026-07-07T14:34:04.657Z" }, + { url = "https://files.pythonhosted.org/packages/42/ab/b9bc2e77d6b44a7e46ef62ec5cac1c9a6ba7b9135a5d560f002696ec9995/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a4cfde78a9f2880208d16a93b795726a3017d5977e08d1e162a7a31322479c41", size = 218328, upload-time = "2026-07-07T14:34:06.115Z" }, + { url = "https://files.pythonhosted.org/packages/f1/78/c9c71d599f5aa2d42bcdd35cbbd46d7f535351a57e40ff7d8e5a7e219401/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4d6fcde76f94f5cb9e43e9e9a61f16dacefd228cbbf6f1a09bd9b219a92f1a1", size = 207406, upload-time = "2026-07-07T14:34:07.554Z" }, + { url = "https://files.pythonhosted.org/packages/f6/39/c914445c321a845097ce4f6ac7de9a18228a77b766272125a1ce00d851eb/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:898f0e9068ca27d37f8e83a5b962821df851532e6c4a7d615c1c033f9da6eedf", size = 225157, upload-time = "2026-07-07T14:34:09.061Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f2/c0d4b8508565a36bc5c624e88ed297f5b0b1095011034d7f5b83a69908b5/charset_normalizer-3.4.9-cp314-cp314-win32.whl", hash = "sha256:c1c948747b03be832dceed96ca815cef7360de9aa19d37c730f8e3f6101aca48", size = 151095, upload-time = "2026-07-07T14:34:10.901Z" }, + { url = "https://files.pythonhosted.org/packages/49/fd/a1d26144398c67486422a72bf5812cda22cb4ccfcd95a290fb41ceb4b8e2/charset_normalizer-3.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:16b65ea0f2465b6fb52aa22de5eca612aa964ddfec00a912e26f4656cbef890b", size = 162796, upload-time = "2026-07-07T14:34:12.47Z" }, + { url = "https://files.pythonhosted.org/packages/20/95/d75e82f8ce9fd323ebf059c16c9aadefb22a1ecde13b7840b35835e4886c/charset_normalizer-3.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:40a126142a56b2dfc0aacbad1de8310cbf60da7656db0e6b16eebd48e3e93519", size = 153334, upload-time = "2026-07-07T14:34:14.044Z" }, + { url = "https://files.pythonhosted.org/packages/00/5e/17398df3a139985ba9d11ed072531986f408c8fca952835ef1ab1820c02b/charset_normalizer-3.4.9-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:609b3ba8fcc0fb5ab7af00719d0fb6ad0cb518e48e7712d12fd68f1327951198", size = 338848, upload-time = "2026-07-07T14:34:15.688Z" }, + { url = "https://files.pythonhosted.org/packages/cd/91/7253a32e86b7e1d1239b1b36ba6dd0f021a21107ab33054b53119cc083b9/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51447e9aa2684679af07ca5021c3db526e0284347ebf4ffcec1154c3350cfe32", size = 223022, upload-time = "2026-07-07T14:34:17.248Z" }, + { url = "https://files.pythonhosted.org/packages/cb/32/2e64bd2be10e89c61e57ebe6a93fd98ae88eb7ebe414b5121f22c96c69eb/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cc1b0fff8ead343dae06305f954eb8468ba0ec1a97881f42489d198e4ce3c632", size = 241590, upload-time = "2026-07-07T14:34:18.813Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ef/d96ec496cfea0c21db43b0ad03891308b02388d054cc902cf0e5a1ad6a88/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa36ec09ef71d158186bc79e359ff5fdd6e7996fe8ab638f00d6b93139ba4fcf", size = 239584, upload-time = "2026-07-07T14:34:20.52Z" }, + { url = "https://files.pythonhosted.org/packages/d4/ce/9af95f7876194bd7a14e3dfe4a4de2e0bff02666a3910d72beafd06cc297/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df115d4d83168fdf2cae48ef1ff6d1cb4c466364e30861b37121de0f3bf1b990", size = 230224, upload-time = "2026-07-07T14:34:22.189Z" }, + { url = "https://files.pythonhosted.org/packages/52/94/af74dde74a3996bd959c350709bfe50e297823d70a8c1cbd54b838880863/charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f86c6358749bd4fda175388691e3ba8c46e24c5347d0afd20f9b7edfc9faf07d", size = 212667, upload-time = "2026-07-07T14:34:23.857Z" }, + { url = "https://files.pythonhosted.org/packages/ee/f0/f1c4fe746c395922961b5916ed1d7d6e7d4c84851d19ed43cc89980ec953/charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:32286a2c8d167e897177b673176c1e3e00d4057caf5d2b64eef9a3666b03018e", size = 227179, upload-time = "2026-07-07T14:34:25.586Z" }, + { url = "https://files.pythonhosted.org/packages/e4/56/6c745619ac397e8871e2bcd3cea1eec86b877488f33888b3aef5c3ed506e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:83aed2c10721ddd90f68140685391b50811a880af20654c59af6b6c66c40513c", size = 225372, upload-time = "2026-07-07T14:34:27.212Z" }, + { url = "https://files.pythonhosted.org/packages/78/ad/98aae8630ac71f16711968e38a5acfecce41b778bf2f0312851020f565a8/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cd6c3d4b783c556fa00bf540854e42f135e2f256abd29669fcd0da0f2dec79c2", size = 215222, upload-time = "2026-07-07T14:34:28.774Z" }, + { url = "https://files.pythonhosted.org/packages/f7/40/9593d54209765207a7f11073c06494c1721e4ca4a0a426c597679bf7f91e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ee2f2a527e3c1a6e6411eb4209642e138b544a2d72fe5d0d76daf77b24063534", size = 231958, upload-time = "2026-07-07T14:34:30.345Z" }, + { url = "https://files.pythonhosted.org/packages/b1/27/693ee5e8a18191eb38647360c51cd505013e2bd3b366aa43fd5344c21e3c/charset_normalizer-3.4.9-cp314-cp314t-win32.whl", hash = "sha256:0d861473f743244d349b50f850d10eb87aeb22bbdcc8e64f79273c94af5a8226", size = 155580, upload-time = "2026-07-07T14:34:31.884Z" }, + { url = "https://files.pythonhosted.org/packages/80/3f/bd97d3d9c613013d07cb7733d299385b41df37f0471310f5a73dc359f0b8/charset_normalizer-3.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:9b8e0f3107e2200b76f6054de99016eac3ee6762713587b36baaa7e4bd2ae177", size = 167620, upload-time = "2026-07-07T14:34:33.438Z" }, + { url = "https://files.pythonhosted.org/packages/3d/c6/eee9dca4439b1061f76373f06ea855678cc4a64c1c3c90b50e479edbb8eb/charset_normalizer-3.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:19ac87f93086ce37b86e098888555c4b4bc48102279bae3350098c0ed664b501", size = 158037, upload-time = "2026-07-07T14:34:35.018Z" }, + { url = "https://files.pythonhosted.org/packages/98/2b/f97f1c193fb855c345d678f5077d6926034db0722df74c8f057020e05a25/charset_normalizer-3.4.9-py3-none-any.whl", hash = "sha256:68e5f26a1ad57ded6d1cfb85331d1c1a195314756471d97758c48498bb4dcdf5", size = 64538, upload-time = "2026-07-07T14:34:56.993Z" }, +] + +[[package]] +name = "click" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, +] + [[package]] name = "colorama" version = "0.4.6" @@ -70,6 +200,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9c/0f/5d0c71a1aefeb08efff26272149e07ab922b64f46c63363756224bd6872e/filelock-3.24.3-py3-none-any.whl", hash = "sha256:426e9a4660391f7f8a810d71b0555bce9008b0a1cc342ab1f6947d37639e002d", size = 24331, upload-time = "2026-02-19T00:48:18.465Z" }, ] +[[package]] +name = "ghp-import" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, +] + [[package]] name = "gladia-normalization" version = "0.1.0" @@ -88,6 +230,10 @@ dev = [ { name = "ruff" }, { name = "ty" }, ] +docs = [ + { name = "mkdocs" }, + { name = "mkdocs-material" }, +] [package.metadata] requires-dist = [ @@ -104,6 +250,10 @@ dev = [ { name = "ruff", specifier = ">=0.15.2" }, { name = "ty", specifier = ">=0.0.20" }, ] +docs = [ + { name = "mkdocs", specifier = ">=1.6.1" }, + { name = "mkdocs-material", specifier = ">=9.7.6" }, +] [[package]] name = "identify" @@ -114,6 +264,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b8/58/40fbbcefeda82364720eba5cf2270f98496bdfa19ea75b4cccae79c698e6/identify-2.6.16-py2.py3-none-any.whl", hash = "sha256:391ee4d77741d994189522896270b787aed8670389bfd60f326d677d64a6dfb0", size = 99202, upload-time = "2026-01-12T18:58:56.627Z" }, ] +[[package]] +name = "idna" +version = "3.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, +] + [[package]] name = "iniconfig" version = "2.3.0" @@ -123,6 +282,190 @@ 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 = "markdown" +version = "3.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, +] + +[[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/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { 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 = "mergedeep" +version = "1.3.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "ghp-import" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mergedeep" }, + { name = "mkdocs-get-deps" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "pyyaml" }, + { name = "pyyaml-env-tag" }, + { name = "watchdog" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" }, +] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mergedeep" }, + { name = "platformdirs" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/25/b3cccb187655b9393572bde9b09261d267c3bf2f2cdabe347673be5976a6/mkdocs_get_deps-0.2.2.tar.gz", hash = "sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1", size = 11047, upload-time = "2026-03-10T02:46:33.632Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl", hash = "sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650", size = 9555, upload-time = "2026-03-10T02:46:32.256Z" }, +] + +[[package]] +name = "mkdocs-material" +version = "9.7.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel" }, + { name = "backrefs" }, + { name = "colorama" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "mkdocs" }, + { name = "mkdocs-material-extensions" }, + { name = "paginate" }, + { name = "pygments" }, + { name = "pymdown-extensions" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/45/29/6d2bcf41ae40802c4beda2432396fff97b8456fb496371d1bc7aad6512ec/mkdocs_material-9.7.6.tar.gz", hash = "sha256:00bdde50574f776d328b1862fe65daeaf581ec309bd150f7bff345a098c64a69", size = 4097959, upload-time = "2026-03-19T15:41:58.161Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl", hash = "sha256:71b84353921b8ea1ba84fe11c50912cc512da8fe0881038fcc9a0761c0e635ba", size = 9305470, upload-time = "2026-03-19T15:41:55.217Z" }, +] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" }, +] + [[package]] name = "nodeenv" version = "1.10.0" @@ -141,6 +484,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, ] +[[package]] +name = "paginate" +version = "0.5.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" }, +] + +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + [[package]] name = "platformdirs" version = "4.9.2" @@ -217,6 +578,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] +[[package]] +name = "pymdown-extensions" +version = "11.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/a9/5f0c535ba3b08fe09270c16808e053a968868242ecbd5676d4e3a488bf28/pymdown_extensions-11.0.1.tar.gz", hash = "sha256:dd2905ae6fc5b75582fafb139a1266ffc754705efa902aa50067fa7ff4f94ec0", size = 857113, upload-time = "2026-07-02T17:59:22.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/54/da572c98c0b77626a91b5d3b89f0231d8bff5125c225420908632f8b342d/pymdown_extensions-11.0.1-py3-none-any.whl", hash = "sha256:db3943a62bab7e03af1364f0c4083e64b91fb097675a4b6cceccfbe9a77e5eb2", size = 269455, upload-time = "2026-07-02T17:59:21.271Z" }, +] + [[package]] name = "pytest" version = "9.0.3" @@ -235,6 +609,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, ] +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + [[package]] name = "pyyaml" version = "6.0.3" @@ -299,6 +685,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] +[[package]] +name = "pyyaml-env-tag" +version = "1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + [[package]] name = "ruff" version = "0.15.2" @@ -324,6 +737,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6d/78/097c0798b1dab9f8affe73da9642bb4500e098cb27fd8dc9724816ac747b/ruff-0.15.2-py3-none-win_arm64.whl", hash = "sha256:cabddc5822acdc8f7b5527b36ceac55cc51eec7b1946e60181de8fe83ca8876e", size = 10941649, upload-time = "2026-02-19T22:32:18.108Z" }, ] +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + [[package]] name = "text2num" version = "3.0.1" @@ -518,6 +940,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + [[package]] name = "virtualenv" version = "20.39.0" @@ -532,3 +963,35 @@ sdist = { url = "https://files.pythonhosted.org/packages/ed/54/809199edc537dbace wheels = [ { url = "https://files.pythonhosted.org/packages/f7/b4/8268da45f26f4fe84f6eae80a6ca1485ffb490a926afecff75fc48f61979/virtualenv-20.39.0-py3-none-any.whl", hash = "sha256:44888bba3775990a152ea1f73f8e5f566d49f11bbd1de61d426fd7732770043e", size = 5839121, upload-time = "2026-02-23T18:09:11.173Z" }, ] + +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/56/90994d789c61df619bfc5ce2ecdabd5eeff564e1eb47512bd01b5e019569/watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26", size = 96390, upload-time = "2024-11-01T14:06:24.793Z" }, + { url = "https://files.pythonhosted.org/packages/55/46/9a67ee697342ddf3c6daa97e3a587a56d6c4052f881ed926a849fcf7371c/watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112", size = 88389, upload-time = "2024-11-01T14:06:27.112Z" }, + { url = "https://files.pythonhosted.org/packages/44/65/91b0985747c52064d8701e1075eb96f8c40a79df889e59a399453adfb882/watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3", size = 89020, upload-time = "2024-11-01T14:06:29.876Z" }, + { url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393, upload-time = "2024-11-01T14:06:31.756Z" }, + { url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392, upload-time = "2024-11-01T14:06:32.99Z" }, + { url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019, upload-time = "2024-11-01T14:06:34.963Z" }, + { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, + { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, + { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, + { url = "https://files.pythonhosted.org/packages/30/ad/d17b5d42e28a8b91f8ed01cb949da092827afb9995d4559fd448d0472763/watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881", size = 87902, upload-time = "2024-11-01T14:06:53.119Z" }, + { url = "https://files.pythonhosted.org/packages/5c/ca/c3649991d140ff6ab67bfc85ab42b165ead119c9e12211e08089d763ece5/watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11", size = 88380, upload-time = "2024-11-01T14:06:55.19Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, +] From 0960808c81315375bda42e683af51777ef66e11d Mon Sep 17 00:00:00 2001 From: karamouche Date: Tue, 14 Jul 2026 10:09:23 -0400 Subject: [PATCH 02/10] refactor: update MkDocs configuration and enhance documentation layout --- .github/workflows/docs.yml | 5 +- docs/assets/favicon.svg | 4 + docs/assets/fonts/GeistMono-Regular.woff2 | Bin 0 -> 30756 bytes docs/assets/logo.svg | 4 + docs/assets/symbol.svg | 3 + docs/index.md | 15 +- docs/stylesheets/gladia.css | 494 ++++++++++++++++++++++ mkdocs.yml | 33 +- overrides/main.html | 1 + overrides/partials/logo.html | 5 + 10 files changed, 547 insertions(+), 17 deletions(-) create mode 100644 docs/assets/favicon.svg create mode 100644 docs/assets/fonts/GeistMono-Regular.woff2 create mode 100644 docs/assets/logo.svg create mode 100644 docs/assets/symbol.svg create mode 100644 docs/stylesheets/gladia.css create mode 100644 overrides/main.html create mode 100644 overrides/partials/logo.html diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 6cd080d..847ac68 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -5,6 +5,7 @@ on: branches: [main] paths: - "docs/**" + - "overrides/**" - "mkdocs.yml" - "pyproject.toml" - "uv.lock" @@ -40,7 +41,7 @@ jobs: run: uv run mkdocs build --strict - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v5 with: path: site @@ -54,4 +55,4 @@ jobs: steps: - name: Deploy id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 diff --git a/docs/assets/favicon.svg b/docs/assets/favicon.svg new file mode 100644 index 0000000..e62800d --- /dev/null +++ b/docs/assets/favicon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/docs/assets/fonts/GeistMono-Regular.woff2 b/docs/assets/fonts/GeistMono-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..16e3724d2ae9ccf84032580277dad337c0739461 GIT binary patch literal 30756 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 literal 0 HcmV?d00001 diff --git a/docs/assets/logo.svg b/docs/assets/logo.svg new file mode 100644 index 0000000..47d58f9 --- /dev/null +++ b/docs/assets/logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/docs/assets/symbol.svg b/docs/assets/symbol.svg new file mode 100644 index 0000000..4bf1e7d --- /dev/null +++ b/docs/assets/symbol.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/index.md b/docs/index.md index 30f6351..ddda0a3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,12 @@ -# gladia-normalization +

Open source · STT · WER

-Normalize speech-to-text transcripts before computing Word Error Rate (WER), so formatting differences stop looking like recognition errors. +# Normalization + +

+Normalize speech-to-text transcripts before computing Word Error Rate, so formatting differences stop looking like recognition errors. +

+ +
| Ground truth | STT output | Without normalization | | --- | --- | --- | @@ -13,6 +19,11 @@ Input: "It's $50.9 at 3:00PM — y'know, roughly." Output: "it is 50 point 9 dollars at 3 pm you know roughly" ``` +[Get started](getting-started.md){ .md-button .md-button--primary } +[How it works](concepts.md){ .md-button } + +
+ ## Quick example ```python diff --git a/docs/stylesheets/gladia.css b/docs/stylesheets/gladia.css new file mode 100644 index 0000000..858f668 --- /dev/null +++ b/docs/stylesheets/gladia.css @@ -0,0 +1,494 @@ +/* Gladia design tokens — mapped onto MkDocs Material + * Source: gladia-design/DESIGN.md (dark-first, white contrast, purple spike) + * Suisse Intl is licensed — use the system stack that matches its metrics. + * Geist Mono (SIL OFL) is self-hosted for code / mono labels. + */ + +@font-face { + font-family: "Geist Mono"; + src: url("../assets/fonts/GeistMono-Regular.woff2") format("woff2"); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +:root { + /* Primitives */ + --gladia-neutral-0: #ffffff; + --gladia-neutral-100: #f5f5f5; + --gladia-neutral-200: #e5e5e5; + --gladia-neutral-300: #d4d4d4; + --gladia-neutral-400: #a3a3a3; + --gladia-neutral-500: #727272; + --gladia-neutral-600: #515151; + --gladia-neutral-700: #252525; + --gladia-neutral-800: #1c1c1e; + --gladia-neutral-900: #0c0c0c; + --gladia-neutral-1000: #000000; + --gladia-purple-400: #947afc; + --gladia-blue-400: #1a9eff; + + --gladia-font-sans: "Suisse Intl", -apple-system, BlinkMacSystemFont, "Segoe UI", + Helvetica, Arial, sans-serif; + --gladia-font-mono: "Geist Mono", "JetBrains Mono", ui-monospace, monospace; + + --gladia-radius-1: 8px; + --gladia-radius-2: 16px; + --gladia-radius-5: 40px; + --gladia-space-4: 16px; + --gladia-space-6: 24px; + --gladia-ease-out: cubic-bezier(0, 0, 0.2, 1); + --gladia-duration: 160ms; +} + +/* ── Dark (default / Canvas) ─────────────────────────────────────────────── */ + +[data-md-color-scheme="slate"] { + --md-default-fg-color: var(--gladia-neutral-0); + --md-default-fg-color--light: var(--gladia-neutral-400); + --md-default-fg-color--lighter: var(--gladia-neutral-500); + --md-default-fg-color--lightest: var(--gladia-neutral-600); + --md-default-bg-color: var(--gladia-neutral-1000); + --md-default-bg-color--light: var(--gladia-neutral-900); + --md-default-bg-color--lighter: var(--gladia-neutral-800); + --md-default-bg-color--lightest: var(--gladia-neutral-700); + + --md-primary-fg-color: var(--gladia-neutral-1000); + --md-primary-fg-color--light: var(--gladia-neutral-900); + --md-primary-fg-color--dark: var(--gladia-neutral-1000); + --md-primary-bg-color: var(--gladia-neutral-0); + --md-primary-bg-color--light: var(--gladia-neutral-300); + + --md-accent-fg-color: var(--gladia-purple-400); + --md-accent-fg-color--transparent: rgba(148, 122, 252, 0.1); + --md-accent-bg-color: var(--gladia-neutral-0); + --md-accent-bg-color--light: var(--gladia-neutral-300); + + --md-code-fg-color: var(--gladia-neutral-200); + --md-code-bg-color: var(--gladia-neutral-900); + --md-code-hl-color: rgba(148, 122, 252, 0.18); + + --md-typeset-a-color: var(--gladia-neutral-300); + --md-typeset-table-color: rgba(255, 255, 255, 0.14); + --md-typeset-table-color--light: rgba(255, 255, 255, 0.08); + + --md-footer-bg-color: var(--gladia-neutral-1000); + --md-footer-bg-color--dark: var(--gladia-neutral-1000); + --md-footer-fg-color: var(--gladia-neutral-400); + --md-footer-fg-color--light: var(--gladia-neutral-0); + --md-footer-fg-color--lighter: var(--gladia-neutral-500); + + --md-admonition-bg-color: var(--gladia-neutral-900); + --md-admonition-fg-color: var(--gladia-neutral-0); +} + +/* ── Light (opt-in editorial band) ───────────────────────────────────────── */ + +[data-md-color-scheme="default"] { + --md-default-fg-color: var(--gladia-neutral-1000); + --md-default-fg-color--light: var(--gladia-neutral-600); + --md-default-fg-color--lighter: var(--gladia-neutral-500); + --md-default-fg-color--lightest: var(--gladia-neutral-300); + --md-default-bg-color: var(--gladia-neutral-0); + --md-default-bg-color--light: var(--gladia-neutral-50, #fafafa); + --md-default-bg-color--lighter: var(--gladia-neutral-100); + --md-default-bg-color--lightest: var(--gladia-neutral-200); + + --md-primary-fg-color: var(--gladia-neutral-0); + --md-primary-fg-color--light: var(--gladia-neutral-100); + --md-primary-fg-color--dark: var(--gladia-neutral-0); + --md-primary-bg-color: var(--gladia-neutral-1000); + --md-primary-bg-color--light: var(--gladia-neutral-700); + + --md-accent-fg-color: var(--gladia-purple-400); + --md-accent-fg-color--transparent: rgba(148, 122, 252, 0.12); + --md-accent-bg-color: var(--gladia-neutral-1000); + --md-accent-bg-color--light: var(--gladia-neutral-700); + + --md-code-fg-color: var(--gladia-neutral-800); + --md-code-bg-color: var(--gladia-neutral-100); + --md-code-hl-color: rgba(148, 122, 252, 0.14); + + --md-typeset-a-color: var(--gladia-neutral-600); + --md-typeset-table-color: rgba(0, 0, 0, 0.12); + --md-typeset-table-color--light: rgba(0, 0, 0, 0.06); + + --md-footer-bg-color: var(--gladia-neutral-1000); + --md-footer-bg-color--dark: var(--gladia-neutral-1000); + --md-footer-fg-color: var(--gladia-neutral-400); + --md-footer-fg-color--light: var(--gladia-neutral-0); + --md-footer-fg-color--lighter: var(--gladia-neutral-500); +} + +/* ── Typography ──────────────────────────────────────────────────────────── */ + +:root { + --md-text-font: var(--gladia-font-sans); + --md-code-font: var(--gladia-font-mono); +} + +body { + font-weight: 400; + letter-spacing: 0; + -webkit-font-smoothing: antialiased; +} + +.md-typeset { + font-weight: 400; + line-height: 1.5; +} + +.md-typeset h1, +.md-typeset h2, +.md-typeset h3, +.md-typeset h4 { + font-weight: 400; + letter-spacing: -0.02em; + color: var(--md-default-fg-color); +} + +.md-typeset h1 { + font-size: 2rem; + letter-spacing: -0.04em; + margin-bottom: 0.6em; +} + +.md-typeset h2 { + font-size: 1.4rem; + margin-top: 1.8em; +} + +.md-typeset strong { + font-weight: 400; + color: var(--md-default-fg-color); +} + +/* Eyebrow / section label — Geist Mono, the only brand mono usage outside code */ +.md-typeset .gladia-eyebrow { + display: inline-flex; + align-items: center; + gap: 0.5rem; + font-family: var(--gladia-font-mono); + font-size: 0.75rem; + font-weight: 400; + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--md-default-fg-color--light); + margin: 0 0 1rem; +} + +.md-typeset .gladia-eyebrow::before { + content: ""; + width: 6px; + height: 6px; + border-radius: 9999px; + background: var(--gladia-purple-400); + flex-shrink: 0; +} + +/* ── Header / nav — glass on black ───────────────────────────────────────── */ + +.md-header { + background-color: rgba(12, 12, 12, 0.72); + backdrop-filter: blur(40px); + -webkit-backdrop-filter: blur(40px); + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + box-shadow: none; + color: var(--gladia-neutral-0); +} + +[data-md-color-scheme="default"] .md-header { + background-color: rgba(255, 255, 255, 0.82); + border-bottom: 1px solid rgba(0, 0, 0, 0.08); + color: var(--gladia-neutral-1000); +} + +/* Product title beside the Gladia wordmark — Material defaults this to 700 */ +.md-header__title { + font-family: var(--gladia-font-sans); + font-size: 0.95rem; + font-weight: 400; + letter-spacing: -0.02em; +} + +.md-header__topic, +.md-header__topic:first-child { + font-family: var(--gladia-font-sans); + font-weight: 400; + letter-spacing: -0.02em; +} + +.md-header__button.md-logo { + padding: 0.3rem; + margin: 0.1rem 0.35rem 0.1rem 0; + color: inherit; +} + +.md-header__button.md-logo .md-logo__svg, +.md-header__button.md-logo svg { + display: block; + height: 1.35rem; + width: auto; +} + +/* Wordmark uses currentColor — white on dark header, black on light */ +[data-md-color-scheme="slate"] .md-header, +[data-md-color-scheme="slate"] .md-header__button { + color: var(--gladia-neutral-0); +} + +[data-md-color-scheme="default"] .md-header, +[data-md-color-scheme="default"] .md-header__button { + color: var(--gladia-neutral-1000); +} + +.md-tabs { + background-color: transparent; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); +} + +[data-md-color-scheme="default"] .md-tabs { + border-bottom: 1px solid rgba(0, 0, 0, 0.08); +} + +.md-nav__title, +.md-nav__link--active, +.md-nav__item .md-nav__link--active, +.md-nav__link:focus, +.md-nav__link:hover { + color: var(--md-default-fg-color); +} + +.md-nav__link--active { + font-weight: 400; +} + +.md-nav__link--active:not(.md-nav__link--passed), +.md-nav__item .md-nav__link--active { + color: var(--gladia-purple-400); +} + +/* Sidebar titles: same sans as site title, all caps */ +.md-nav__title, +.md-nav__item--section > .md-nav__link { + font-family: var(--gladia-font-sans); + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + color: var(--md-default-fg-color); +} + +/* ── Links — white/grey contrast; purple only as hover spike ─────────────── */ + +.md-typeset a { + color: var(--md-typeset-a-color); + text-decoration: underline; + text-decoration-color: rgba(148, 122, 252, 0); + text-underline-offset: 0.18em; + transition: + color var(--gladia-duration) var(--gladia-ease-out), + text-decoration-color var(--gladia-duration) var(--gladia-ease-out); +} + +.md-typeset a:hover, +.md-typeset a:focus { + color: var(--gladia-purple-400); + text-decoration-color: var(--gladia-purple-400); +} + +/* ── Code ────────────────────────────────────────────────────────────────── */ + +.md-typeset code, +.md-typeset kbd, +.md-typeset pre { + font-family: var(--gladia-font-mono); + font-weight: 400; + border-radius: var(--gladia-radius-1); +} + +.md-typeset pre > code { + border-radius: var(--gladia-radius-2); + border: 1px solid rgba(255, 255, 255, 0.14); + padding: var(--gladia-space-4); +} + +[data-md-color-scheme="default"] .md-typeset pre > code { + border-color: rgba(0, 0, 0, 0.1); +} + +.md-typeset .tabbed-set > .tabbed-content { + border-radius: 0 0 var(--gladia-radius-2) var(--gladia-radius-2); +} + +.md-typeset .tabbed-labels > label { + font-weight: 400; + border-radius: var(--gladia-radius-5); +} + +/* ── Tables / cards ──────────────────────────────────────────────────────── */ + +.md-typeset table:not([class]) { + border: 1px solid rgba(255, 255, 255, 0.14); + border-radius: var(--gladia-radius-2); + overflow: hidden; + box-shadow: none; +} + +[data-md-color-scheme="default"] .md-typeset table:not([class]) { + border-color: rgba(0, 0, 0, 0.1); +} + +.md-typeset table:not([class]) th { + background-color: var(--gladia-neutral-900); + font-weight: 400; + color: var(--md-default-fg-color--light); + font-family: var(--gladia-font-mono); + font-size: 0.75rem; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +[data-md-color-scheme="default"] .md-typeset table:not([class]) th { + background-color: var(--gladia-neutral-100); +} + +.md-typeset table:not([class]) td, +.md-typeset table:not([class]) th { + border-color: rgba(255, 255, 255, 0.08); + padding: 0.7em 1em; +} + +[data-md-color-scheme="default"] .md-typeset table:not([class]) td, +[data-md-color-scheme="default"] .md-typeset table:not([class]) th { + border-color: rgba(0, 0, 0, 0.08); +} + +/* ── Buttons / search ────────────────────────────────────────────────────── */ + +.md-search__form { + background-color: rgba(255, 255, 255, 0.08); + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: var(--gladia-radius-5); + box-shadow: none; +} + +[data-md-color-scheme="default"] .md-search__form { + background-color: var(--gladia-neutral-100); + border-color: rgba(0, 0, 0, 0.08); +} + +.md-search__input { + font-weight: 400; +} + +.md-typeset .md-button { + border-radius: var(--gladia-radius-5); + font-weight: 400; + letter-spacing: 0; + padding: 0.5em 1.25em; + transition: transform var(--gladia-duration) var(--gladia-ease-out); + border: 1px solid rgba(255, 255, 255, 0.12); +} + +.md-typeset .md-button--primary { + background-color: var(--gladia-neutral-0); + color: var(--gladia-neutral-1000); + border: none; +} + +[data-md-color-scheme="default"] .md-typeset .md-button--primary { + background-color: var(--gladia-neutral-1000); + color: var(--gladia-neutral-0); +} + +.md-typeset .md-button:hover, +.md-typeset .md-button:focus { + transform: scale(1.02); + background-color: var(--gladia-neutral-0); + color: var(--gladia-neutral-1000); + border-color: transparent; +} + +[data-md-color-scheme="default"] .md-typeset .md-button:hover, +[data-md-color-scheme="default"] .md-typeset .md-button:focus { + background-color: var(--gladia-neutral-1000); + color: var(--gladia-neutral-0); +} + +/* Never purple-fill a button (G-11) */ +.md-typeset .md-button--primary:hover, +.md-typeset .md-button--primary:focus { + background-color: var(--gladia-neutral-200); + color: var(--gladia-neutral-1000); +} + +[data-md-color-scheme="default"] .md-typeset .md-button--primary:hover, +[data-md-color-scheme="default"] .md-typeset .md-button--primary:focus { + background-color: var(--gladia-neutral-700); + color: var(--gladia-neutral-0); +} + +/* ── Sidebar / content chrome ────────────────────────────────────────────── */ + +.md-sidebar__scrollwrap { + scrollbar-color: rgba(255, 255, 255, 0.14) transparent; +} + +.md-typeset hr { + border-color: rgba(255, 255, 255, 0.12); +} + +[data-md-color-scheme="default"] .md-typeset hr { + border-color: rgba(0, 0, 0, 0.1); +} + +.md-footer { + border-top: 1px solid rgba(255, 255, 255, 0.08); +} + +.md-annotation__index { + background-color: var(--gladia-purple-400); +} + +/* Focus ring — blue, not purple */ +:focus-visible { + outline: 2px solid var(--gladia-blue-400); + outline-offset: 2px; +} + +/* Home hero breathing room */ +.md-content article > .gladia-eyebrow:first-child { + margin-top: 0.25rem; +} + +.md-typeset .gladia-lead { + font-size: 1.125rem; + line-height: 1.5; + color: var(--md-default-fg-color--light); + max-width: 40rem; + margin: 0 0 1.75rem; +} + +/* Home hero — center table + CTAs */ +.md-typeset .gladia-hero-center { + text-align: center; + margin: 0 0 2rem; +} + +.md-typeset .gladia-hero-center .md-typeset__table { + display: inline-block; + max-width: 100%; + margin: 0 auto 1.25rem; + text-align: left; +} + +.md-typeset .gladia-hero-center > pre { + text-align: left; + margin-left: auto; + margin-right: auto; + max-width: 42rem; +} + +.md-typeset .gladia-hero-center .md-button { + margin: 0.35rem 0.35rem 0; +} diff --git a/mkdocs.yml b/mkdocs.yml index 7a483e0..2a74210 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,4 +1,4 @@ -site_name: gladia-normalization +site_name: Normalization site_description: Normalize speech-to-text transcripts for fair WER comparison site_url: https://gladiaio.github.io/normalization/ repo_url: https://github.com/gladiaio/normalization @@ -6,24 +6,27 @@ repo_name: gladiaio/normalization edit_uri: edit/main/docs/ docs_dir: docs site_dir: site +copyright: Copyright © Gladia theme: name: material + custom_dir: overrides + favicon: assets/favicon.svg + font: false palette: - - media: "(prefers-color-scheme: light)" - scheme: default - primary: blue grey - accent: teal + # Dark-first (Gladia canvas) + - scheme: slate + primary: custom + accent: custom toggle: - icon: material/brightness-7 - name: Switch to dark mode - - media: "(prefers-color-scheme: dark)" - scheme: slate - primary: blue grey - accent: teal - toggle: - icon: material/brightness-4 + icon: material/white-balance-sunny name: Switch to light mode + - scheme: default + primary: custom + accent: custom + toggle: + icon: material/weather-night + name: Switch to dark mode features: - content.code.copy - content.tabs.link @@ -37,6 +40,9 @@ theme: icon: repo: fontawesome/brands/github +extra_css: + - stylesheets/gladia.css + plugins: - search @@ -49,6 +55,7 @@ validation: markdown_extensions: - admonition - attr_list + - md_in_html - pymdownx.details - pymdownx.highlight: anchor_linenums: true diff --git a/overrides/main.html b/overrides/main.html new file mode 100644 index 0000000..94d9808 --- /dev/null +++ b/overrides/main.html @@ -0,0 +1 @@ +{% extends "base.html" %} diff --git a/overrides/partials/logo.html b/overrides/partials/logo.html new file mode 100644 index 0000000..0a363a5 --- /dev/null +++ b/overrides/partials/logo.html @@ -0,0 +1,5 @@ +{# Inlined wordmark so fill="currentColor" follows header text color. #} + + + + From 88030bf7f4acca413f0eceff48fd4d69c45426de Mon Sep 17 00:00:00 2001 From: karamouche Date: Tue, 14 Jul 2026 10:34:02 -0400 Subject: [PATCH 03/10] chore: adjust GitHub Actions permissions for deployment --- .github/workflows/docs.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 847ac68..eb4e81c 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,8 +16,6 @@ on: permissions: contents: read - pages: write - id-token: write concurrency: group: pages @@ -49,6 +47,9 @@ jobs: name: Deploy to GitHub Pages needs: build runs-on: ubuntu-latest + permissions: + pages: write + id-token: write environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} From 763462300eedb7b085a89bf3996bce111a3101e9 Mon Sep 17 00:00:00 2001 From: karamouche Date: Tue, 14 Jul 2026 11:19:17 -0400 Subject: [PATCH 04/10] fix(doc): fixed secondary button style --- docs/stylesheets/gladia.css | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/stylesheets/gladia.css b/docs/stylesheets/gladia.css index 858f668..45f9760 100644 --- a/docs/stylesheets/gladia.css +++ b/docs/stylesheets/gladia.css @@ -387,9 +387,18 @@ body { letter-spacing: 0; padding: 0.5em 1.25em; transition: transform var(--gladia-duration) var(--gladia-ease-out); + /* Material defaults color to --md-primary-fg-color (black in dark / white in + light), which makes outline buttons invisible against the page bg. */ + color: var(--gladia-neutral-0); + background-color: transparent; border: 1px solid rgba(255, 255, 255, 0.12); } +[data-md-color-scheme="default"] .md-typeset .md-button { + color: var(--gladia-neutral-1000); + border-color: rgba(0, 0, 0, 0.12); +} + .md-typeset .md-button--primary { background-color: var(--gladia-neutral-0); color: var(--gladia-neutral-1000); From 595acae0a18f5b2bb6fb76afe811372ccf7a3bfd Mon Sep 17 00:00:00 2001 From: karamouche Date: Tue, 14 Jul 2026 11:22:07 -0400 Subject: [PATCH 05/10] docs: update pipeline stage representation for clarity --- docs/concepts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/concepts.md b/docs/concepts.md index 107f676..c75ceba 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -3,7 +3,7 @@ Every pipeline runs **three stages**, always in this order: ```text -text ──► [1] text_pre ──► split ──► [2] word ──► join ──► [3] text_post ──► text +text > [1] text_pre > split > [2] word > join > [3] text_post > text ``` | Stage | Operates on | Typical work | From 6ff6d60c1d62090c7f3c68e1a9eb38536a46d00c Mon Sep 17 00:00:00 2001 From: karamouche Date: Tue, 14 Jul 2026 11:25:30 -0400 Subject: [PATCH 06/10] docs: reorganize navigation structure in MkDocs configuration for improved clarity --- mkdocs.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 2a74210..6721312 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -73,9 +73,10 @@ nav: - Usage: - Python API: usage/python.md - CLI: usage/cli.md - - Presets: presets.md - - Languages: languages.md - - Step reference: reference/steps.md + - Reference: + - Presets: presets.md + - Languages: languages.md + - Steps: reference/steps.md - Contributing: - Overview: contributing/index.md - Contributor guide: contributing/guide.md From 924a6c73bb92446a4aa75eeb7ca512e9afa27629 Mon Sep 17 00:00:00 2001 From: karamouche Date: Tue, 14 Jul 2026 12:00:09 -0400 Subject: [PATCH 07/10] docs: enhance contributing guide formatting --- docs/contributing/guide.md | 95 ++++++++++++++++++------------------- docs/stylesheets/gladia.css | 2 +- 2 files changed, 48 insertions(+), 49 deletions(-) diff --git a/docs/contributing/guide.md b/docs/contributing/guide.md index 596d705..d08e8d7 100644 --- a/docs/contributing/guide.md +++ b/docs/contributing/guide.md @@ -6,56 +6,56 @@ Detailed reference for contributors. Read this before adding a step or language. Every pipeline runs exactly **three stages**, always in this order: -1. **Text pre-processing** — full-text transforms before word splitting (placeholder protection, symbol conversion, contraction expansion, …) -2. **Word processing** — per-token transforms after splitting on spaces (replacements, filler removal, …) -3. **Text post-processing** — full-text cleanup after rejoining words (placeholder restoration, digit collapsing, …) +1. **Text pre-processing** : full-text transforms before word splitting (placeholder protection, symbol conversion, contraction expansion, …) +2. **Word processing** : per-token transforms after splitting on spaces (replacements, filler removal, …) +3. **Text post-processing** : full-text cleanup after rejoining words (placeholder restoration, digit collapsing, …) -This ordering is a hard constraint — some steps depend on earlier steps having run. See [How it works](../concepts.md) for more detail. +This ordering is a hard constraint - some steps depend on earlier steps having run. See [How it works](../concepts.md) for more detail. --- -## Adding a new language — checklist +## Adding a new language -- [ ] Create `languages/{lang}/` with `operators.py`, `replacements.py`, `__init__.py` -- [ ] Put all word-level substitutions in `replacements.py`; do not add inline entries in `operators.py` -- [ ] Instantiate a `LanguageConfig` in `operators.py`, filling in all required fields and any optional dict fields your language needs (`time_words`, `sentence_replacements`, etc.) -- [ ] Subclass `LanguageOperators`, overriding only methods where the _algorithm_ differs (not just the data) -- [ ] If the language has digit words, populate `digit_words` in `LanguageConfig` -- [ ] If the language uses spoken time patterns, populate `time_words` with all needed word→digit mappings; if it also uses compound minute expressions (e.g. "twenty-one"), override `get_compound_minutes()` — do **not** put this in config -- [ ] If number expansion is needed and the algorithm is complex, implement it in a `number_normalizer.py` file and override `expand_written_numbers`; otherwise do not create the file -- [ ] Decorate the class with `@register_language` -- [ ] Add one import to `languages/__init__.py` -- [ ] Add tests in `tests/unit/languages/` -- [ ] Add a CSV file `tests/e2e/files/{preset}/{language_code}.csv` for each relevant preset (e.g. `tests/e2e/files/gladia-3/fr.csv`) +- Create `languages/{lang}/` with `operators.py`, `replacements.py`, `__init__.py` +- Put all word-level substitutions in `replacements.py`; do not add inline entries in `operators.py` +- Instantiate a `LanguageConfig` in `operators.py`, filling in all required fields and any optional dict fields your language needs (`time_words`, `sentence_replacements`, etc.) +- Subclass `LanguageOperators`, overriding only methods where the _algorithm_ differs (not just the data) +- If the language has digit words, populate `digit_words` in `LanguageConfig` +- If the language uses spoken time patterns, populate `time_words` with all needed word-to-digit mappings; if it also uses compound minute expressions (e.g. "twenty-one"), override `get_compound_minutes()` - do **not** put this in config +- If number expansion is needed and the algorithm is complex, implement it in a `number_normalizer.py` file and override `expand_written_numbers`; otherwise do not create the file +- Decorate the class with `@register_language` +- Add one import to `languages/__init__.py` +- Add tests in `tests/unit/languages/` +- Add a CSV file `tests/e2e/files/{preset}/{language_code}.csv` for each relevant preset (e.g. `tests/e2e/files/gladia-3/fr.csv`) ### Language data vs. language behavior This is the central design rule. Ask: "does the _logic_ change by language, or just the _values_?" -**`LanguageConfig` (data)** — everything that can be expressed as a value: strings, lists, dicts. Separator characters, currency words, filler words, digit words, number words, time words, sentence replacements. Optional fields default to `None`; steps that read them skip gracefully when `None`. +**`LanguageConfig` (data)** : everything that can be expressed as a value: strings, lists, dicts. Separator characters, currency words, filler words, digit words, number words, time words, sentence replacements. Optional fields default to `None`; steps that read them skip gracefully when `None`. -**`LanguageOperators` (behavior)** — only methods where the _algorithm itself_ varies by language. Examples: `expand_contractions`, `expand_written_numbers`, `normalize_numeric_time_formats`, `get_compound_minutes`. If the algorithm is generic and only the _data_ differs, put the data in `LanguageConfig` and the algorithm in the step — not in the operator. +**`LanguageOperators` (behavior)** : only methods where the _algorithm itself_ varies by language. Examples: `expand_contractions`, `expand_written_numbers`, `normalize_numeric_time_formats`, `get_compound_minutes`. If the algorithm is generic and only the _data_ differs, put the data in `LanguageConfig` and the algorithm in the step - not in the operator. --- -## Adding a new step — checklist +## Adding a new step -- [ ] Add the class to the appropriate file in `steps/text/` or `steps/word/` -- [ ] Set a unique `name` class attribute -- [ ] Decorate with `@register_step` -- [ ] Add one import to `steps/text/__init__.py` or `steps/word/__init__.py` -- [ ] Place the algorithm in `__call__`; read language data from `operators.config.*`; call operator methods only for genuinely behavioral differences -- [ ] If the step reads an optional `LanguageConfig` field, guard with `if operators.config.field is None: return text` -- [ ] Add unit tests in `tests/unit/steps/` -- [ ] If it involves placeholder protection, add both protect and restore to `steps/text/placeholders.py` and update `pipeline/base.py`'s `validate()` accordingly -- [ ] Add the step name to relevant preset YAMLs if needed (new preset version if existing presets are affected) -- [ ] If you added or changed the class docstring, run `uv run scripts/generate_step_docs.py` to regenerate `docs/reference/steps.md` +- Add the class to the appropriate file in `steps/text/` or `steps/word/` +- Set a unique `name` class attribute +- Decorate with `@register_step` +- Add one import to `steps/text/__init__.py` or `steps/word/__init__.py` +- Place the algorithm in `__call__`; read language data from `operators.config.*`; call operator methods only for genuinely behavioral differences +- If the step reads an optional `LanguageConfig` field, guard with `if operators.config.field is None: return text` +- Add unit tests in `tests/unit/steps/` +- If it involves placeholder protection, add both protect and restore to `steps/text/placeholders.py` and update `pipeline/base.py`'s `validate()` accordingly +- Add the step name to relevant preset YAMLs if needed (new preset version if existing presets are affected) +- If you added or changed the class docstring, run `uv run scripts/generate_step_docs.py` to regenerate `docs/reference/steps.md` ### Choosing a base class Pick the narrowest one that fits your step. -**`WordStep`** — use when your transformation operates on a single token in isolation, with no knowledge of neighboring words. This is the only base class for Stage 2 steps. +**`WordStep`** : use when your transformation operates on a single token in isolation, with no knowledge of neighboring words. This is the only base class for Stage 2 steps. ```python @register_step @@ -66,7 +66,7 @@ class MyWordStep(WordStep): ... ``` -**`TextStep`** — the general-purpose base for Stage 1 and Stage 3. Use it when your transformation needs to see the full string, or when none of the more specific bases below fit. +**`TextStep`** : the general-purpose base for Stage 1 and Stage 3. Use it when your transformation needs to see the full string, or when none of the more specific bases below fit. ```python @register_step @@ -77,7 +77,7 @@ class MyTextStep(TextStep): ... ``` -**`ProtectStep`** — a specialization of `TextStep` for replacing a character with a placeholder token. Implement `_pattern`, which returns a compiled regex with **exactly two capture groups** (what comes before and after the character being replaced). The `__call__` is fixed: it applies the pattern as `\1{placeholder}\2`. +**`ProtectStep`** : a specialization of `TextStep` for replacing a character with a placeholder token. Implement `_pattern`, which returns a compiled regex with **exactly two capture groups** (what comes before and after the character being replaced). The `__call__` is fixed: it applies the pattern as `\1{placeholder}\2`. ```python @register_step @@ -89,11 +89,11 @@ class MyProtectStep(ProtectStep): return re.compile(r"(\d+)X(\d+)") # two capture groups required ``` -Use `ProtectStep` when: one regex pattern maps to exactly one placeholder substitution. +> Use `ProtectStep` when: one regex pattern maps to exactly one placeholder substitution. -Use `TextStep` directly instead when: a single pass must protect two different symbols, the replacement needs to absorb surrounding whitespace with `\s*`, or the replacement is a per-match function rather than a fixed template. +> Use `TextStep` directly instead when: a single pass must protect two different symbols, the replacement needs to absorb surrounding whitespace with `\s*`, or the replacement is a per-match function rather than a fixed template. -**`RestoreStep`** — a specialization of `TextStep` for restoring a placeholder back to a string. Implement `_replacement`, which returns the string to substitute in. The `__call__` does a plain `str.replace` of the placeholder (and its case-folded form). +**`RestoreStep`** : a specialization of `TextStep` for restoring a placeholder back to a string. Implement `_replacement`, which returns the string to substitute in. The `__call__` does a plain `str.replace` of the placeholder (and its case-folded form). ```python @register_step @@ -105,9 +105,9 @@ class MyRestoreStep(RestoreStep): return operators.config.some_word or " " ``` -Use `RestoreStep` when: restoration is a straight token swap with no surrounding whitespace to absorb and no additional logic needed. +> Use `RestoreStep` when: restoration is a straight token swap with no surrounding whitespace to absorb and no additional logic needed. -Use `TextStep` directly instead when: the placeholder was inserted with spaces around it (requiring `re.sub` with `\s*` to avoid double spaces), the marker should be deleted entirely rather than replaced, or post-replacement cleanup is needed. +> Use `TextStep` directly instead when: the placeholder was inserted with spaces around it (requiring `re.sub` with `\s*` to avoid double spaces), the marker should be deleted entirely rather than replaced, or post-replacement cleanup is needed. --- @@ -119,9 +119,9 @@ Unit tests live under `tests/unit/steps/text/` or `tests/unit/steps/word/`, mirr The `tests/unit/steps/text/conftest.py` provides two fixtures and a helper: -- `operators` — a bare `LanguageOperators()` instance (language-agnostic) -- `english_operators` — an `EnglishOperators()` instance -- `assert_text_step_registered(step_cls)` — verifies the step is in the registry under its name +- `operators` : a bare `LanguageOperators()` instance (language-agnostic) +- `english_operators` : an `EnglishOperators()` instance +- `assert_text_step_registered(step_cls)` : verifies the step is in the registry under its name Every test file for a step should at minimum: @@ -161,7 +161,7 @@ def test_my_step_with_english(english_operators): E2E tests validate the full pipeline (preset + language) against CSV fixtures. The test runner lives in `tests/e2e/normalization_test.py` and CSV files are organized under `tests/e2e/files/`. -**Directory structure** — one folder per preset, one CSV per language: +**Directory structure** : one folder per preset, one CSV per language: ``` tests/e2e/files/ @@ -178,19 +178,18 @@ tests/e2e/files/ sv.csv ``` -**CSV format** — two columns (`input,expected`), no quoting needed unless the value contains a comma: +**CSV format** : two columns (`input,expected`), no quoting needed unless the value contains a comma: ``` input,expected "$1,000,000",1000000 dollars hello world,hello world ``` +> The language is derived from the filename (e.g. `fr.csv` → language code `fr`). Use `default.csv` for the language-agnostic fallback. -The language is derived from the filename (e.g. `fr.csv` → language code `fr`). Use `default.csv` for the language-agnostic fallback. +**Adding test cases for an existing preset** : drop rows into the appropriate `{language_code}.csv` file, or create a new CSV if the language isn't covered yet. Tests are discovered automatically. -**Adding test cases for an existing preset** — drop rows into the appropriate `{language_code}.csv` file, or create a new CSV if the language isn't covered yet. Tests are discovered automatically. - -**Registering a new preset** — add a block to `normalization_test.py` following the existing pattern: +**Registering a new preset** : add a block to `normalization_test.py` following the existing pattern: ```python _MY_PRESET_DIR = _FILES_DIR / "my-preset" @@ -203,7 +202,7 @@ for _language in sorted(_MY_PRESET_BY_LANGUAGE): ) ``` -Pipelines are cached per language to avoid reloading for each parametrized case. +> Pipelines are cached per language to avoid reloading for each parametrized case. --- @@ -211,6 +210,6 @@ Pipelines are cached per language to avoid reloading for each parametrized case. - **Data vs. behavior**: if only the _values_ change by language, put them in `LanguageConfig`. If the _algorithm_ changes, override a method in `LanguageOperators`. - **Steps are language-agnostic**: a step must not contain any language-specific logic or string literals. Read from `operators.config.*` or call `operators.method()`. -- **Presets are immutable**: never modify a published preset YAML — new behavior means a new preset file. +- **Presets are immutable**: never modify a published preset YAML - new behavior means a new preset file. - **Placeholder pairs**: every `protect_*` step in Stage 1 must have a matching `restore_*` in Stage 3. The pipeline validates this at load time. - **Language folders are self-contained**: everything specific to a language lives inside its folder. Helpers used only by one language (e.g. `number_normalizer.py`) go in that language's folder, not in `steps/`. \ No newline at end of file diff --git a/docs/stylesheets/gladia.css b/docs/stylesheets/gladia.css index 45f9760..a3e3d91 100644 --- a/docs/stylesheets/gladia.css +++ b/docs/stylesheets/gladia.css @@ -159,7 +159,7 @@ body { } .md-typeset strong { - font-weight: 400; + font-weight: 600; color: var(--md-default-fg-color); } From 3573b4a660d3083dee760eb5531a27b675b5c26a Mon Sep 17 00:00:00 2001 From: karamouche Date: Tue, 14 Jul 2026 12:09:35 -0400 Subject: [PATCH 08/10] docs: update installation instructions and enhance table formatting for clarity --- docs/getting-started.md | 9 +++++---- docs/index.md | 16 ++++++---------- docs/languages.md | 4 +++- docs/stylesheets/gladia.css | 16 ++++++++++++++-- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 28261fc..a96a983 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -2,16 +2,17 @@ ## Install -=== "pip" + +=== "uv" ```bash - pip install gladia-normalization + uv add gladia-normalization ``` -=== "uv" +=== "pip" ```bash - uv add gladia-normalization + pip install gladia-normalization ``` === "From source" diff --git a/docs/index.md b/docs/index.md index ddda0a3..8a48936 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,22 +8,18 @@ Normalize speech-to-text transcripts before computing Word Error Rate, so format
-| Ground truth | STT output | Without normalization | -| --- | --- | --- | -| It's $50 | it is fifty dollars | treated as errors | -| 3:00 PM | 3 pm | treated as errors | -| Mr. Smith | mister smith | treated as errors | - -```text -Input: "It's $50.9 at 3:00PM — y'know, roughly." -Output: "it is 50 point 9 dollars at 3 pm you know roughly" -``` +| Ground truth | STT output | WER | +| ------------ | ------------------- | ---- | +| It's $50 | it is fifty dollars | 100% | +| 3:00 PM | 3 pm | 100% | +| Mr. Smith | mister smith | 100% | [Get started](getting-started.md){ .md-button .md-button--primary } [How it works](concepts.md){ .md-button }
+ ## Quick example ```python diff --git a/docs/languages.md b/docs/languages.md index eff3331..dd23461 100644 --- a/docs/languages.md +++ b/docs/languages.md @@ -2,6 +2,7 @@ Pass a language code to `load_pipeline` or `--language`. Unknown codes fall back to a language-agnostic default (independent transforms only). +
| Code | Language | | --- | --- | | `da` | Danish | @@ -14,8 +15,9 @@ Pass a language code to `load_pipeline` or `--language`. Unknown codes fall back | `nl` | Dutch | | `no` | Norwegian | | `sv` | Swedish | +
-## What “language-aware” means +## What "language-aware" means Each language folder under `normalization/languages/` provides: diff --git a/docs/stylesheets/gladia.css b/docs/stylesheets/gladia.css index a3e3d91..092c235 100644 --- a/docs/stylesheets/gladia.css +++ b/docs/stylesheets/gladia.css @@ -354,13 +354,25 @@ body { .md-typeset table:not([class]) td, .md-typeset table:not([class]) th { - border-color: rgba(255, 255, 255, 0.08); + border: none; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + border-right: 1px solid rgba(255, 255, 255, 0.08); padding: 0.7em 1em; } +.md-typeset table:not([class]) td:last-child, +.md-typeset table:not([class]) th:last-child { + border-right: none; +} + +.md-typeset table:not([class]) tr:last-child td { + border-bottom: none; +} + [data-md-color-scheme="default"] .md-typeset table:not([class]) td, [data-md-color-scheme="default"] .md-typeset table:not([class]) th { - border-color: rgba(0, 0, 0, 0.08); + border-bottom-color: rgba(0, 0, 0, 0.08); + border-right-color: rgba(0, 0, 0, 0.08); } /* ── Buttons / search ────────────────────────────────────────────────────── */ From 63dc270eb3a44e1b29a14c13a0f96220b1bf7d65 Mon Sep 17 00:00:00 2001 From: karamouche Date: Tue, 14 Jul 2026 12:12:14 -0400 Subject: [PATCH 09/10] docs: refine contributing guide index --- docs/contributing/index.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/docs/contributing/index.md b/docs/contributing/index.md index 16d3695..f4a50c8 100644 --- a/docs/contributing/index.md +++ b/docs/contributing/index.md @@ -1,6 +1,6 @@ # Contributing -Bug reports, new steps, and new language support are welcome. +Bug reports, new steps, and new languages are welcome. Setup and where to start are below. Check [Contributor guide](guide.md) for design rules. ## Setup @@ -30,13 +30,11 @@ uv run mkdocs build ## How to help -| Contribution | Start here | -| --- | --- | -| New language | [Checklist](guide.md#adding-a-new-language-checklist) | -| New step | [Checklist](guide.md#adding-a-new-step-checklist) | -| Bug report | GitHub issue with reproduce steps + expected vs actual | -| Question | GitHub issue with the `question` label | +| Contribution | Start here | +| ------------ | ------------------------------------------------------ | +| New language | [Checklist](guide.md#adding-a-new-language-checklist) | +| New step | [Checklist](guide.md#adding-a-new-step-checklist) | +| Bug report | GitHub issue with reproduce steps + expected vs actual | +| Question | GitHub issue with the `question` label | -Full design rules, base-class choice, and test conventions: [Contributor guide](guide.md). - -Also see [`CONTRIBUTING.md`](https://github.com/gladiaio/normalization/blob/main/CONTRIBUTING.md) in the repo root for PR workflow and commit style. +Also see [`CONTRIBUTING.md`](https://github.com/gladiaio/normalization/blob/main/CONTRIBUTING.md) for PR workflow and commit style. From 3467213afd51ce8b4d3ae25d98c1077f82cbd7a9 Mon Sep 17 00:00:00 2001 From: karamouche Date: Tue, 14 Jul 2026 12:16:54 -0400 Subject: [PATCH 10/10] docs: fixed anchor links with title rewriting --- docs/contributing/index.md | 4 ++-- docs/languages.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/contributing/index.md b/docs/contributing/index.md index f4a50c8..d099f73 100644 --- a/docs/contributing/index.md +++ b/docs/contributing/index.md @@ -32,8 +32,8 @@ uv run mkdocs build | Contribution | Start here | | ------------ | ------------------------------------------------------ | -| New language | [Checklist](guide.md#adding-a-new-language-checklist) | -| New step | [Checklist](guide.md#adding-a-new-step-checklist) | +| New language | [Checklist](guide.md#adding-a-new-language) | +| New step | [Checklist](guide.md#adding-a-new-step) | | Bug report | GitHub issue with reproduce steps + expected vs actual | | Question | GitHub issue with the `question` label | diff --git a/docs/languages.md b/docs/languages.md index dd23461..d3c8fd8 100644 --- a/docs/languages.md +++ b/docs/languages.md @@ -29,4 +29,4 @@ Steps stay language-agnostic: they read `operators.config.*` or call operator me ## Adding a language -See the [contributor guide](contributing/guide.md#adding-a-new-language-checklist). +See the [contributor guide](contributing/guide.md#adding-a-new-language).