# PROJECT DUMP FOR LLM / CODE REVIEW

Generated at: 2026-02-02T13:19:19+00:00
Project root: /home/placerte/projects/mpl-plot-report

Document structure:
1. This header section (what you are reading now).
2. A directory tree of the project (if enabled).
3. The contents of each included file, in deterministic order.

Conventions:
- Each file starts with a header line like:
    ==== FILE: relative/path/to/file.py ==== 
- File contents follow immediately after the header.

LLM instructions (suggested):
- Treat this as a *read-only* snapshot of the project.
- When referencing code, mention the file path and line(s) if possible.
- If you propose changes, explain them in terms of specific files/sections.

Filtering applied:
- Excluded directory names: ['.git', '.hg', '.idea', '.mypy_cache', '.pytest_cache', '.svn', '.venv', '.vscode', '__pycache__', 'build', 'dist', 'node_modules', 'venv']
- Excluded file extensions: ['.7z', '.avi', '.bin', '.dll', '.dylib', '.exe', '.gif', '.gz', '.ico', '.jpeg', '.jpg', '.mkv', '.mov', '.mp3', '.mp4', '.o', '.obj', '.otf', '.pdf', '.png', '.pyc', '.pyo', '.so', '.tar', '.ttf', '.webp', '.woff', '.woff2', '.xz', '.zip']
- Included extensions: ALL non-binary files (minus excluded ext).
- .gitignore: NOT applied

================================================================================

DIRECTORY TREE
--------------------------------------------------------------------------------
mpl-plot-report
├── .git
├── .pytest_cache
├── .ruff_cache
│   ├── 0.12.5
│   │   ├── 12046553944557437416
│   │   ├── 7299540042040150289
│   │   ├── 7824868882482285207
│   │   └── 9231839037935408823
│   ├── .gitignore
│   └── CACHEDIR.TAG
├── .venv
├── docs
│   ├── implementation.md
│   └── specs.md
├── examples
│   └── ruleset_demo.py
├── mpl_plot_report
│   ├── __pycache__
│   ├── __init__.py
│   ├── api.py
│   ├── cli.py
│   ├── diagnostics.py
│   ├── extract.py
│   ├── render_md.py
│   ├── rules.py
│   ├── schema.py
│   └── util.py
├── scripts
│   └── generate_fixtures.py
├── src
├── tests
│   ├── __pycache__
│   ├── fixtures
│   │   ├── expected
│   │   │   ├── happy_path.plot.json
│   │   │   ├── happy_path.plot.md
│   │   │   ├── happy_path.png
│   │   │   ├── nan_ordering.plot.json
│   │   │   ├── nan_ordering.plot.md
│   │   │   ├── nan_ordering.png
│   │   │   ├── wiggles_outliers.plot.json
│   │   │   ├── wiggles_outliers.plot.md
│   │   │   └── wiggles_outliers.png
│   │   └── synthetic
│   │       ├── crowded_and_two_peaks_hit_010.csv
│   │       ├── crowded_but_clean_hit_013.csv
│   │       └── crowded_but_coupled_hit_009.csv
│   ├── conftest.py
│   └── test_report.py
├── .gitignore
├── .python-version
├── agent.md
├── AGENTS.md
├── LICENSE
├── main.py
├── pyproject.toml
├── README.md
└── uv.lock

================================================================================


FILES INCLUDED
--------------------------------------------------------------------------------
.python-version
.ruff_cache/CACHEDIR.TAG
AGENTS.md
LICENSE
README.md
agent.md
docs/implementation.md
docs/specs.md
examples/ruleset_demo.py
main.py
mpl_plot_report/__init__.py
mpl_plot_report/api.py
mpl_plot_report/cli.py
mpl_plot_report/diagnostics.py
mpl_plot_report/extract.py
mpl_plot_report/render_md.py
mpl_plot_report/rules.py
mpl_plot_report/schema.py
mpl_plot_report/util.py
pyproject.toml
scripts/generate_fixtures.py
tests/conftest.py
tests/fixtures/expected/happy_path.plot.json
tests/fixtures/expected/happy_path.plot.md
tests/fixtures/expected/nan_ordering.plot.json
tests/fixtures/expected/nan_ordering.plot.md
tests/fixtures/expected/wiggles_outliers.plot.json
tests/fixtures/expected/wiggles_outliers.plot.md
tests/test_report.py
uv.lock

================================================================================

==== FILE: .python-version ====

3.13


==== FILE: .ruff_cache/CACHEDIR.TAG ====

Signature: 8a477f597d28d172789f06886806bc55

==== FILE: AGENTS.md ====

# Agent Guide for mpl-plot-report

This repo is a small Python package intended to export Matplotlib plot reports
as JSON + Markdown sidecars. The guidance here is for agentic coding tools.

## Quick Start

- Python version: 3.13 (see `.python-version`).
- Dependencies are tracked in `pyproject.toml` and `uv.lock`.
- If you use `uv`, prefer `uv sync` or `uv run ...`.
- If you use `pip`, create a venv and install from `pyproject.toml`.

## Build / Lint / Test

The project is currently very small and uses `pytest` and `black`.

### Install / Sync

- `uv sync`
- `python -m venv .venv && . .venv/bin/activate && pip install -e .[dev]`

### Tests

- Run all tests: `uv run pytest` or `python -m pytest`
- Run a single file: `uv run pytest tests/test_module.py`
- Run a single test: `uv run pytest tests/test_module.py::TestClass::test_name`
- Run by keyword: `uv run pytest -k "keyword"`

### Lint / Format

- Format: `uv run black .` or `python -m black .`
- Check formatting: `uv run black --check .`

### Build / Packaging

This repository is not yet wired with a build tool, but common options are:

- `uv build` (if using uv)
- `python -m build` (requires `build` dependency)

If you need to add a build step, update this file with the exact command.

## Cursor / Copilot Rules

No Cursor rules found in `.cursor/rules/` or `.cursorrules`.
No Copilot instructions found in `.github/copilot-instructions.md`.

## Code Style Guidelines

This repo is early-stage; prefer simple, explicit Python with a focus on
determinism and readability. Use the following conventions unless a file
explicitly establishes something else.

### Formatting

- Use Black defaults (4-space indents, line length per Black).
- Keep imports sorted in logical groups (stdlib, third-party, local).
- Avoid trailing whitespace and excessive blank lines.
- Prefer short, descriptive lines over dense one-liners.

### Imports

- Group imports with a blank line between groups.
- Favor module imports over wildcard imports.
- Keep Matplotlib usage explicit: `import matplotlib.pyplot as plt`.
- Keep NumPy usage explicit: `import numpy as np`.

### Types

- Add type hints for public APIs and data structures.
- Use `typing` module constructs where helpful (e.g., `Sequence`, `Mapping`).
- Prefer `dict[str, Any]` or `Mapping[str, Any]` for free-form context.
- Use `dataclasses` for schema-like objects when appropriate.

### Naming

- Functions and variables: `snake_case`.
- Classes and dataclasses: `PascalCase`.
- Constants: `UPPER_SNAKE_CASE`.
- Keep rule/diagnostic identifiers stable and deterministic.

### Error Handling

- Fail fast on invariant violations when appropriate.
- Use exceptions for programmer errors; return structured warnings for data
  issues that should not crash the exporter.
- Report non-fatal issues in the report `warnings[]` field.
- Prefer explicit error messages over silent fallbacks.

### Determinism and Reproducibility

- Stable ordering: axes by `fig.axes`, series by `ax.lines`.
- Stable IDs: derive from axis index + series index + label.
- Numeric rounding must be controlled by a single `float_precision` parameter.
- Use a deterministic decimation strategy when `n > max_points`.

### JSON + Markdown Outputs

- Always emit `<stem>.plot.json` and `<stem>.plot.md` sidecars.
- Keep the schema version pinned and documented.
- Prefer predictable key ordering when serializing.

### Matplotlib / Headless

- Use the Agg backend for test figures and headless runs.
- PNGs are secondary; JSON is the source of truth for agents.

### Diagnostics and Invariants

- Diagnostics are numeric and data-driven, not visual.
- Invariants encode expectations, not observations.
- Domain-specific rules must remain in consumer projects.
- Report FAIL invariants first; WARN invariants must include rationale.

### Tests and Fixtures

- Tests should be deterministic and fast.
- Prefer small synthetic fixtures for core engine tests.
- Keep fixtures in `tests/fixtures/` (synthetic, expected, curated).
- Avoid brittle golden-value assertions until the schema stabilizes.

## Agent Workflow Requirements (from `agent.md`)

Use this as the default workflow when adding or modifying behavior.

1) Generate 2-3 minimal figures for smoke/regression:
   - Happy path line plot (strictly increasing x, smooth y)
   - Pathology with NaNs/inf and non-monotonic x
   - Pathology with wiggles/jumps/outliers
2) Export sidecars for each figure:
   - PNG
   - `.plot.json`
   - `.plot.md`
3) Inspect JSON before PNGs:
   - FAIL invariants first
   - WARN invariants next
   - Diagnostics for rationale
4) Propose fixes, re-run, compare reports.

CI / regression intent:

- Store `.plot.json` fixtures for the 2-3 minimal figures.
- CI should assert zero FAILs on happy path.
- CI should assert expected FAIL/WARN signatures on pathology cases.
- Use JSON diffs for regression review.

Escalation policy:

- FAIL invariants: stop and propose fix.
- WARN invariants: justify or refine rule.
- Clean report: accept plot without image review.

## Preferred Structure (from docs)

The implementation plan proposes this layout (once implemented):

- `mpl_plot_report/api.py`: `dump_report()` and `build_report()`
- `mpl_plot_report/extract.py`: figure/axes/line extraction
- `mpl_plot_report/diagnostics.py`: numeric metrics
- `mpl_plot_report/rules.py`: `Rule`, `RuleResult`, `RuleSet`
- `mpl_plot_report/schema.py`: dataclasses + schema version
- `mpl_plot_report/render_md.py`: Markdown renderer
- `mpl_plot_report/util.py`: hashing/rounding/decimation

If you add these files, follow the conventions above.

## README Expectations (future)

Once core API and tests are stable, update `README.md` with:

- Installation (uv/pip)
- Minimal usage snippet calling `dump_report`
- Expected output files (`.png`, `.plot.json`, `.plot.md`)
- How to supply a consumer `ruleset`
- How to run tests and where fixtures live

## Notes for Agents

- Favor clear, minimal diffs; avoid large refactors unless required.
- Keep the engine domain-neutral; do not encode project-specific rules.
- Prefer small, composable helpers over monolithic functions.
- When in doubt, document the reasoning in the report output.


==== FILE: LICENSE ====

MIT License

Copyright (c) 2026 placerte

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


==== FILE: README.md ====

# mpl-plot-report

Export Matplotlib figures as deterministic JSON + Markdown sidecars for
LLM-friendly plot debugging. PNGs are emitted as secondary artifacts.

## Installation

Using uv:

```bash
uv sync
```

Using pip:

```bash
python -m venv .venv
. .venv/bin/activate
pip install -e .[dev]
```

## Minimal usage

```python
import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np

from mpl_plot_report import dump_report

x = np.linspace(0, 10, 200)
y = np.sin(x)
fig, ax = plt.subplots()
ax.plot(x, y, label="signal")

dump_report(
    fig,
    out_dir="plots/run_001",
    stem="signal",
    context={"run_id": "run_001", "style_name": "default"},
)
```

## CLI usage

You can point the CLI at a module that exposes a callable returning a
Matplotlib Figure (default callable name: `make_figure`).

```bash
uv run mpl-plot-report --module my_module --callable make_figure --out-dir plots/run_001 --stem signal
```

Or provide a run id and let the CLI create `plots/<run_id>` automatically:

```bash
uv run mpl-plot-report --module my_module --run-id run_001 --stem signal
```

## Output files

For a stem like `signal`, the exporter writes:

- `signal.png`
- `signal.plot.json`
- `signal.plot.md`

The JSON is the source of truth for agents and CI. Markdown is a concise
human-facing summary, and PNGs are optional.

## Rulesets

Domain rules should live in consumer projects and be injected at runtime.
Define a `RuleSet` (or iterable of `Rule`) and pass it to `dump_report`:

```python
from mpl_plot_report import dump_report
from mpl_plot_report.rules import Rule, RuleSet


def always_ok(report):
    return True, "ok", None


ruleset = RuleSet([Rule("demo.always_ok", "warn", always_ok)])
dump_report(fig, out_dir="plots", stem="example", ruleset=ruleset)
```

See a minimal module example at `examples/ruleset_demo.py`.

## Tests

```bash
uv run pytest
```

Single test examples:

```bash
uv run pytest tests/test_report.py
uv run pytest tests/test_report.py::test_dump_report_creates_sidecars
```

## Fixtures

Synthetic fixtures live under `tests/fixtures/expected/`. Regenerate them with:

```bash
uv run python scripts/generate_fixtures.py
```


==== FILE: agent.md ====

## Agent Workflow — Plot Report Sidecars

### Objective
Enable blind-friendly, deterministic critique of plots by agents using structured plot reports instead of rendered images.

---

### Minimal Test Figures (agent-generated)
The agent should generate 2–3 figures as a smoke/regression harness for the plot-report engine:

1) **Happy path line plot**
- x strictly increasing
- smooth y
- should produce zero FAILs and minimal WARNs

2) **Pathology: NaNs / inf / ordering**
- inject NaNs or inf in y
- optionally shuffle a segment of x (non-monotonic)
- should surface non-finite warnings and ordering/dx invariants

3) **Pathology: wiggles / jumps / outliers**
- piecewise y with a step/jump + high-frequency wiggle region
- should trigger outlier metrics and wiggle/curvature diagnostics

These plots must be generated headlessly (Agg) and exported to:
- PNG
- `.plot.json`
- `.plot.md`

---

### Standard Debugging Loop

1. **Run** plotting code to generate figures
2. **Export** sidecar reports:
   - `<stem>.plot.json`
   - `<stem>.plot.md`
3. **Agent inspects JSON**:
   - invariant failures (FAIL first)
   - warnings (NaN, decimation, ordering issues)
   - suspicious diagnostics (wiggles, slope variance, outliers)
4. **Agent proposes changes**:
   - data corrections (ordering, NaN handling)
   - plotting fixes (scale, limits, labels, units)
   - invariant refinements (domain rules, if any)
5. **Re-run and compare** reports

---

### What the Agent Should Rely On

- JSON report as the primary truth
- Invariant results as correctness signals
- Diagnostics to explain *why* something failed

PNG viewing is secondary and optional.

---

### What the Agent Must Avoid

- Judging correctness from PNGs alone
- Guessing intent without reading invariants
- Adding domain rules to the generic engine

---

### CI / Regression Strategy

- Store `.plot.json` fixtures for the 2–3 test figures above
- CI asserts:
  - zero FAIL invariants for the happy-path figure
  - expected FAIL/WARN signatures for pathology figures
- JSON diffs used for regression review

---

### Escalation Policy

- FAIL invariants → stop and propose fix
- WARN invariants → justify or refine rule
- Clean report → accept plot without image review

---

### After Green Tests

Once the package implementation is complete and tests pass:
- Draft a practical `README.md` for the standalone repo covering:
  - installation (uv/pip)
  - minimal Python usage (one small snippet calling `dump_report`)
  - expected output files (`.png`, `.plot.json`, `.plot.md`)
  - how to supply a `ruleset` from a consumer project
  - how to run tests and where fixtures live



==== FILE: docs/implementation.md ====

## Plot Report Engine — Implementation Plan

### Package Boundaries

**Generic (reusable):** `mpl_plot_report`
- Figure / axes / artist extraction
- Numeric diagnostics
- Invariant rule engine
- JSON + Markdown renderers

**Project‑specific (external / plugins):**
- Domain invariants (e.g. frequency bounds, damping bounds, decay expectations)
- Rule sets live outside this package and are injected at runtime

---

### Suggested File Layout

**Generic package:**
```
mpl_plot_report/
  __init__.py
  api.py           # dump_report(), build_report()
  extract.py       # figure / axes / line extraction
  diagnostics.py   # numeric metrics
  rules.py         # Rule, RuleResult, RuleSet
  schema.py        # dataclasses + schema version
  render_md.py     # Markdown rendering
  util.py          # hashing, rounding, decimation, stable IDs
```

**External usage example (not part of this repo):**
```
<consumer_project>/
  plot_rules/
    common.py      # domain bounds
    kinds/
      frf.py
      decay.py
      energy.py
```
```

Notes:
- The **plot report engine** stays fully standalone in `mpl_plot_report`.
- Domain rules are provided by the consumer project as a `ruleset` object or small rules module.
```

---

### Minimal Public API (v1)

```python
dump_report(
    fig,
    out_dir,
    stem,
    *,
    context: dict,
    ruleset=None,
    include_data=True,
    max_points=2000,
    float_precision=6,
) -> dict
```

Optional wrapper:
```python
FigureReport(fig, context=..., ruleset=...).save(out_dir, stem)
```

---

### JSON Schema v1 (Outline)

Top‑level keys:
- `schema_version: "1.0"`
- `context`:
  - run_id, timestamp, git_hash
  - inputs[]
  - parameters{}
  - style { name, rcparams_hash }
- `figure`:
  - dpi, size_inches, backend
- `axes[]`:
  - title, xlabel, ylabel
  - xscale, yscale, xlim, ylim
  - legend[]
  - `series[]`:
    - id (stable)
    - label
    - kind (v1: line2d only)
    - data (optional): x[], y[]
    - stats: n, min, max, endpoints
    - diagnostics{}
- `invariants[]`:
  - rule_id
  - severity (warn / fail)
  - passed
  - message
  - where (axis / series / indices)
- `warnings[]` (export‑time warnings)

---

### Determinism Rules

- Axes order: `fig.axes`
- Series order: `ax.lines`
- Stable IDs based on (axis index, series index, label)
- Numeric rounding controlled by `float_precision`

---

### Data Decimation

- If `n > max_points`:
  - preserve endpoints
  - uniform subsample
  - record original n + decimation method

---

### v1 Scope (Strict)

- Support `Line2D` only
- Numeric diagnostics only (no visuals)
- JSON + MD output

Future versions may add scatter, bars, images, and richer layout analysis.

---

### Test Strategy

**Goal:** keep `mpl_plot_report` domain-neutral while still exercising realistic workloads.

**Tier 1 — Package tests (required, fast, deterministic)**
- Location: `tests/`
- Data: *agent-generated synthetic figures* (no external CSV required)
- Assertions:
  - report generation does not crash (headless/Agg)
  - schema keys exist and are correctly typed
  - exporter warnings/invariants behave as expected on pathology cases
- Artifacts:
  - optionally store small golden JSON in `tests/fixtures/expected/` once the schema stabilizes

**Tier 2 — Consumer integration tests (optional, in consumer repos)**
- Domain projects may keep curated datasets (CSV/WAV-derived) under their own `tests/fixtures/…`
- Those tests should assert **qualitative** properties (fields exist, warnings present) rather than brittle numeric equality.

**Fixtures naming/location convention**
- Prefer: `tests/fixtures/synthetic/` for small, deterministic data used by this package
- Prefer: `tests/fixtures/expected/` for golden `.plot.json` snapshots (introduced after v1 stabilizes)

---

### Release Checklist (v1)

- [ ] `pytest` passes locally and in CI (headless)
- [ ] Export produces: `.png`, `.plot.json`, `.plot.md`
- [ ] Schema version pinned and documented
- [ ] Add a helpful `README.md` (usage, installation, minimal examples)
  - Document Python API (`dump_report`) and expected output files
  - Show the 2–3 synthetic test plots as examples
  - Mention how consumer projects inject domain rules via `ruleset`

---

### Testing Strategy (Locked)

**Test types:**

1) **Engine sanity tests (required)**
- Use existing curated CSV fixtures from the consumer project during early development
- Assert:
  - report generation does not crash
  - JSON schema keys exist
  - diagnostics fields are populated
  - expected qualitative signals appear (wiggles, NaNs, ordering issues)
- Avoid golden-value assertions at this stage

2) **Synthetic fixtures (future hardening)**
- Introduce 2–3 tiny synthetic datasets only once diagnostics stabilize
- Use them to generate golden `.plot.json` outputs
- Enable strict CI regression checks at that point

**Fixtures location convention:**
```
tests/
  fixtures/
    curated/      # realistic datasets (integration stress tests)
    synthetic/    # small, deterministic datasets (later)
```

---

### Agent Handoff Requirement

Once:
- core API is stable
- tests pass reliably
- diagnostics are minimally validated

An agent should generate a **README.md** that includes:
- package purpose and philosophy
- installation instructions
- minimal usage examples
- explanation of sidecar artifacts
- guidance for injecting domain-specific rules

The README must target both human users and agent-based workflows.



==== FILE: docs/specs.md ====

## Plot Report Sidecar Export

> Packaging: This spec is for a standalone package repo (working name: `mpl_plot_report`). Domain rules belong to consumer projects and are injected at runtime.

### Problem
Agents cannot reliably critique plot correctness from rendered images (PNGs). Images are not stable, machine-testable artifacts.

### Solution
For every Matplotlib figure, export a *sidecar plot report* (JSON + Markdown) that captures:
- figure / axes / series metadata
- numeric diagnostics computed from series data
- invariant checks (rules) with pass / fail + locations

This enables agentic, blind-friendly debugging loops and CI regression checks.

---

### Required Artifacts (per plot)

```
plots/<run_id>/
  <stem>.png
  <stem>.plot.json
  <stem>.plot.md
```

---

### Inputs
- Matplotlib `Figure`
- Optional `PlotPayload` used to construct the figure
- `context` metadata:
  - run_id, timestamp
  - git hash / version
  - input dataset identities (CSV, WAV, etc.)
  - parameters used to generate the plot
  - style identity

---

### Outputs
- `<stem>.plot.json` — machine-readable, schema-versioned report
- `<stem>.plot.md` — human-readable summary highlighting warnings and failures

---

### Diagnostics (Generic, Reusable)
Diagnostics operate on numeric data, not visuals:

- x ordering (strictly increasing / expected ordering)
- NaN / inf detection and locations
- dx sanity: min, median, non-positive dx count
- first-difference outliers on dy (max |dy|, robust z-score)
- slope statistics on dy/dx: mean, median, std, quantiles
- wiggles: sign changes in slope after light smoothing / downsampling
- curvature proxy: second difference stats (std, max abs, sign changes)

---

### Invariant Checks
- Invariants encode *expectations*, not observations
- Each invariant returns:
  - pass / fail
  - severity (warn / fail)
  - reason
  - affected axis / series / index ranges

**Separation of concerns:**
- Generic engine supports invariant evaluation
- Domain-specific invariants live in consumer projects

---

### Styling Identity
Reports must record:
- style name (if using `.mplstyle`)
- rcParams fingerprint / hash
- backend (optional)

---

### Non-Goals
- No OCR or image analysis
- No monkey-patching Matplotlib
- No pyplot wrapper as the main API surface

---

### Portability & Determinism
- Pure Python + matplotlib + numpy
- Headless compatible (Agg backend)
- Stable ordering of axes and series
- Controlled float precision and optional data decimation
- Schema version pinned (v1.x)



==== FILE: examples/ruleset_demo.py ====

from __future__ import annotations

from mpl_plot_report.rules import Rule, RuleSet


def always_ok(report):
    return True, "ok", None


RULESET = RuleSet([Rule("demo.always_ok", "warn", always_ok)])


==== FILE: main.py ====

def main():
    print("Hello from mpl-plot-report!")


if __name__ == "__main__":
    main()


==== FILE: mpl_plot_report/__init__.py ====

"""Public API for mpl_plot_report."""

from mpl_plot_report.api import build_report, dump_report

__all__ = ["build_report", "dump_report"]


==== FILE: mpl_plot_report/api.py ====

from __future__ import annotations

import json
from pathlib import Path
from typing import Any, Mapping

import matplotlib

from mpl_plot_report.extract import extract_figure
from mpl_plot_report.render_md import render_report
from mpl_plot_report.rules import RuleResult, coerce_ruleset
from mpl_plot_report.schema import SCHEMA_VERSION
from mpl_plot_report.util import hash_rcparams, round_floats


def build_report(
    fig,
    *,
    context: Mapping[str, Any] | None = None,
    ruleset=None,
    include_data: bool = True,
    max_points: int = 2000,
    float_precision: int = 6,
) -> dict[str, Any]:
    context_payload = dict(context or {})
    rcparams_hash = hash_rcparams(matplotlib.rcParams)
    context_payload.setdefault(
        "style",
        {
            "name": context_payload.get("style_name"),
            "rcparams_hash": rcparams_hash,
        },
    )

    axes_payload, warnings = extract_figure(
        fig,
        include_data=include_data,
        max_points=max_points,
    )

    report: dict[str, Any] = {
        "schema_version": SCHEMA_VERSION,
        "context": context_payload,
        "figure": {
            "dpi": float(fig.dpi),
            "size_inches": [float(v) for v in fig.get_size_inches()],
            "backend": matplotlib.get_backend(),
        },
        "axes": axes_payload,
        "invariants": [],
        "warnings": warnings,
    }

    rules = coerce_ruleset(ruleset)
    if rules:
        invariants: list[RuleResult] = rules.evaluate(report)
        report["invariants"] = [inv.__dict__ for inv in invariants]

    return round_floats(report, float_precision)


def dump_report(
    fig,
    out_dir: str | Path,
    stem: str,
    *,
    context: Mapping[str, Any] | None = None,
    ruleset=None,
    include_data: bool = True,
    max_points: int = 2000,
    float_precision: int = 6,
) -> dict[str, Any]:
    report = build_report(
        fig,
        context=context,
        ruleset=ruleset,
        include_data=include_data,
        max_points=max_points,
        float_precision=float_precision,
    )

    out_path = Path(out_dir)
    out_path.mkdir(parents=True, exist_ok=True)

    png_path = out_path / f"{stem}.png"
    json_path = out_path / f"{stem}.plot.json"
    md_path = out_path / f"{stem}.plot.md"

    fig.savefig(png_path)

    json_path.write_text(json.dumps(report, indent=2, sort_keys=True), encoding="utf-8")
    md_path.write_text(render_report(report), encoding="utf-8")

    return report


==== FILE: mpl_plot_report/cli.py ====

from __future__ import annotations

import argparse
import json
import sys
from importlib import import_module
from typing import Any, Callable

import matplotlib

from mpl_plot_report.api import dump_report


def _load_callable(module_path: str, callable_name: str) -> Callable[..., Any]:
    module = import_module(module_path)
    try:
        target = getattr(module, callable_name)
    except AttributeError as exc:
        raise SystemExit(
            f"Callable '{callable_name}' not found in module '{module_path}'."
        ) from exc
    if not callable(target):
        raise SystemExit(f"{module_path}.{callable_name} is not callable.")
    return target


def _parse_args(argv: list[str]) -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Export Matplotlib plot reports.")
    parser.add_argument(
        "--module",
        required=True,
        help="Module path containing a callable that returns a Figure.",
    )
    parser.add_argument(
        "--callable",
        default="make_figure",
        help="Callable name that returns a Matplotlib Figure.",
    )
    parser.add_argument("--out-dir", help="Output directory.")
    parser.add_argument("--stem", required=True, help="Output file stem.")
    parser.add_argument(
        "--run-id",
        help="Run identifier; uses plots/<run_id> when --out-dir is omitted.",
    )
    parser.add_argument(
        "--context",
        default="{}",
        help="JSON string for context metadata.",
    )
    parser.add_argument(
        "--max-points",
        type=int,
        default=2000,
        help="Maximum number of points per series.",
    )
    parser.add_argument(
        "--float-precision",
        type=int,
        default=6,
        help="Float rounding precision for report output.",
    )
    parser.add_argument(
        "--no-data",
        action="store_true",
        help="Exclude raw series data from JSON output.",
    )
    return parser.parse_args(argv)


def main(argv: list[str] | None = None) -> int:
    args = _parse_args(argv or sys.argv[1:])
    matplotlib.use("Agg")

    if args.out_dir is None and not args.run_id:
        raise SystemExit("Provide --out-dir or --run-id.")
    out_dir = args.out_dir or f"plots/{args.run_id}"

    try:
        context = json.loads(args.context)
    except json.JSONDecodeError as exc:
        raise SystemExit("--context must be valid JSON.") from exc

    figure_factory = _load_callable(args.module, args.callable)
    fig = figure_factory()

    dump_report(
        fig,
        out_dir,
        args.stem,
        context=context,
        include_data=not args.no_data,
        max_points=args.max_points,
        float_precision=args.float_precision,
    )
    return 0


if __name__ == "__main__":
    raise SystemExit(main())


==== FILE: mpl_plot_report/diagnostics.py ====

from __future__ import annotations

from typing import Any

import numpy as np


def _safe_stat(values: np.ndarray, func) -> float | None:
    if values.size == 0:
        return None
    return float(func(values))


def _robust_z_stats(values: np.ndarray) -> tuple[float | None, int]:
    if values.size == 0:
        return None, 0
    median = float(np.median(values))
    mad = float(np.median(np.abs(values - median)))
    if mad == 0.0:
        return 0.0, 0
    z = 0.6745 * (values - median) / mad
    max_abs = float(np.max(np.abs(z)))
    outliers = int(np.sum(np.abs(z) > 3.5))
    return max_abs, outliers


def compute_diagnostics(x: np.ndarray, y: np.ndarray) -> dict[str, Any]:
    diagnostics: dict[str, Any] = {}

    n = int(len(x))
    finite_mask = np.isfinite(x) & np.isfinite(y)
    diagnostics["n"] = n
    diagnostics["non_finite_count"] = int(n - np.count_nonzero(finite_mask))

    x_finite = x[np.isfinite(x)]
    dx = np.diff(x_finite) if x_finite.size > 1 else np.array([])
    diagnostics["dx_min"] = _safe_stat(dx, np.min)
    diagnostics["dx_median"] = _safe_stat(dx, np.median)
    diagnostics["dx_non_positive_count"] = int(np.sum(dx <= 0)) if dx.size else 0

    y_finite = y[finite_mask]
    x_for_slope = x[finite_mask]
    dy = np.diff(y_finite) if y_finite.size > 1 else np.array([])
    diagnostics["dy_max_abs"] = _safe_stat(np.abs(dy), np.max)
    robust_z, outlier_count = _robust_z_stats(dy)
    diagnostics["dy_robust_z_max"] = robust_z
    diagnostics["dy_outlier_count"] = outlier_count

    slope = np.array([])
    if x_for_slope.size > 1:
        dx_slope = np.diff(x_for_slope)
        dy_slope = np.diff(y_finite)
        valid = dx_slope != 0
        if np.any(valid):
            slope = dy_slope[valid] / dx_slope[valid]

    diagnostics["slope_mean"] = _safe_stat(slope, np.mean)
    diagnostics["slope_median"] = _safe_stat(slope, np.median)
    diagnostics["slope_std"] = _safe_stat(slope, np.std)
    diagnostics["slope_p10"] = _safe_stat(slope, lambda v: np.quantile(v, 0.1))
    diagnostics["slope_p90"] = _safe_stat(slope, lambda v: np.quantile(v, 0.9))

    slope_signs = np.sign(slope)
    slope_signs = slope_signs[slope_signs != 0]
    diagnostics["wiggle_sign_changes"] = (
        int(np.sum(slope_signs[1:] != slope_signs[:-1])) if slope_signs.size > 1 else 0
    )

    curvature = np.diff(y_finite, n=2) if y_finite.size > 2 else np.array([])
    diagnostics["curvature_std"] = _safe_stat(curvature, np.std)
    diagnostics["curvature_max_abs"] = _safe_stat(np.abs(curvature), np.max)
    curvature_signs = np.sign(curvature)
    curvature_signs = curvature_signs[curvature_signs != 0]
    diagnostics["curvature_sign_changes"] = (
        int(np.sum(curvature_signs[1:] != curvature_signs[:-1]))
        if curvature_signs.size > 1
        else 0
    )

    return diagnostics


==== FILE: mpl_plot_report/extract.py ====

from __future__ import annotations

from typing import Any

import numpy as np
from matplotlib.lines import Line2D

from mpl_plot_report.diagnostics import compute_diagnostics
from mpl_plot_report.util import decimate_series, make_stable_id


def _safe_min(values: np.ndarray) -> float | None:
    if values.size == 0:
        return None
    finite = values[np.isfinite(values)]
    if finite.size == 0:
        return None
    return float(np.min(finite))


def _safe_max(values: np.ndarray) -> float | None:
    if values.size == 0:
        return None
    finite = values[np.isfinite(values)]
    if finite.size == 0:
        return None
    return float(np.max(finite))


def _series_stats(x: np.ndarray, y: np.ndarray) -> dict[str, Any]:
    stats: dict[str, Any] = {
        "n": int(len(x)),
        "x_min": _safe_min(x),
        "x_max": _safe_max(x),
        "y_min": _safe_min(y),
        "y_max": _safe_max(y),
        "x_endpoints": [float(x[0]), float(x[-1])] if len(x) else None,
        "y_endpoints": [float(y[0]), float(y[-1])] if len(y) else None,
    }
    return stats


def extract_figure(
    fig,
    *,
    include_data: bool,
    max_points: int,
) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
    axes_payload: list[dict[str, Any]] = []
    warnings: list[dict[str, Any]] = []

    for axis_index, ax in enumerate(fig.axes):
        legend_labels = []
        if ax.get_legend() is not None:
            _, labels = ax.get_legend_handles_labels()
            legend_labels = [label for label in labels if label]

        axis_payload: dict[str, Any] = {
            "index": axis_index,
            "title": ax.get_title(),
            "xlabel": ax.get_xlabel(),
            "ylabel": ax.get_ylabel(),
            "xscale": ax.get_xscale(),
            "yscale": ax.get_yscale(),
            "xlim": list(ax.get_xlim()),
            "ylim": list(ax.get_ylim()),
            "legend": legend_labels,
            "series": [],
        }

        for series_index, line in enumerate(ax.lines):
            if not isinstance(line, Line2D):
                continue
            x = np.asarray(line.get_xdata(), dtype=float)
            y = np.asarray(line.get_ydata(), dtype=float)

            if x.size != y.size:
                min_len = min(x.size, y.size)
                warnings.append(
                    {
                        "code": "length_mismatch",
                        "message": "Line2D x/y lengths do not match; truncating.",
                        "where": {
                            "axis_index": axis_index,
                            "series_index": series_index,
                        },
                    }
                )
                x = x[:min_len]
                y = y[:min_len]

            label = line.get_label() or ""
            if not label or label.startswith("_"):
                label = f"series_{series_index}"

            series_id = make_stable_id(axis_index, series_index, label)
            diagnostics = compute_diagnostics(x, y)
            stats = _series_stats(x, y)

            if diagnostics.get("non_finite_count", 0) > 0:
                warnings.append(
                    {
                        "code": "non_finite",
                        "message": "Series contains non-finite values.",
                        "where": {
                            "axis_index": axis_index,
                            "series_index": series_index,
                        },
                    }
                )

            data_payload = None
            if include_data:
                x_use, y_use, decimation = decimate_series(x, y, max_points)
                data_payload = {
                    "x": x_use.tolist(),
                    "y": y_use.tolist(),
                    "decimation": decimation,
                }

            axis_payload["series"].append(
                {
                    "id": series_id,
                    "label": label,
                    "kind": "line2d",
                    "data": data_payload,
                    "stats": stats,
                    "diagnostics": diagnostics,
                }
            )

        axes_payload.append(axis_payload)

    return axes_payload, warnings


==== FILE: mpl_plot_report/render_md.py ====

from __future__ import annotations

from typing import Any


def render_report(report: dict[str, Any]) -> str:
    lines: list[str] = []
    lines.append("# Plot Report")
    lines.append("")
    lines.append(f"- Schema: {report.get('schema_version')}")
    lines.append(f"- Axes: {len(report.get('axes', []))}")
    lines.append(f"- Invariants: {len(report.get('invariants', []))}")
    lines.append(f"- Warnings: {len(report.get('warnings', []))}")

    warnings = report.get("warnings", [])
    if warnings:
        lines.append("")
        lines.append("## Warnings")
        for warning in warnings:
            code = warning.get("code", "warning")
            message = warning.get("message", "")
            where = warning.get("where")
            where_text = f" ({where})" if where else ""
            lines.append(f"- {code}: {message}{where_text}")

    invariants = report.get("invariants", [])
    if invariants:
        fails = [inv for inv in invariants if not inv.get("passed")]
        passes = [inv for inv in invariants if inv.get("passed")]

        if fails:
            lines.append("")
            lines.append("## Invariant Failures")
            for inv in fails:
                lines.append(
                    f"- {inv.get('rule_id')}: {inv.get('message')} ({inv.get('severity')})"
                )

        if passes:
            lines.append("")
            lines.append("## Invariant Passes")
            for inv in passes:
                lines.append(
                    f"- {inv.get('rule_id')}: {inv.get('message')} ({inv.get('severity')})"
                )

    axes = report.get("axes", [])
    if axes:
        lines.append("")
        lines.append("## Axes Summary")
        for axis in axes:
            title = axis.get("title") or "(untitled)"
            series_count = len(axis.get("series", []))
            lines.append(f"- Axis {axis.get('index')}: {title} ({series_count} series)")

    lines.append("")
    return "\n".join(lines)


==== FILE: mpl_plot_report/rules.py ====

from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Callable, Iterable, Sequence


@dataclass(frozen=True)
class RuleResult:
    rule_id: str
    severity: str
    passed: bool
    message: str
    where: dict[str, Any] | None = None


@dataclass(frozen=True)
class Rule:
    rule_id: str
    severity: str
    evaluator: Callable[[dict[str, Any]], tuple[bool, str, dict[str, Any] | None]]

    def evaluate(self, report: dict[str, Any]) -> RuleResult:
        passed, message, where = self.evaluator(report)
        return RuleResult(
            rule_id=self.rule_id,
            severity=self.severity,
            passed=passed,
            message=message,
            where=where,
        )


@dataclass
class RuleSet:
    rules: Sequence[Rule]

    def evaluate(self, report: dict[str, Any]) -> list[RuleResult]:
        return [rule.evaluate(report) for rule in self.rules]


def coerce_ruleset(ruleset: RuleSet | Iterable[Rule] | None) -> RuleSet | None:
    if ruleset is None:
        return None
    if isinstance(ruleset, RuleSet):
        return ruleset
    return RuleSet(list(ruleset))


==== FILE: mpl_plot_report/schema.py ====

from __future__ import annotations

SCHEMA_VERSION = "1.0"


==== FILE: mpl_plot_report/util.py ====

from __future__ import annotations

import hashlib
import json
from typing import Any, Mapping

import numpy as np


def make_stable_id(axis_index: int, series_index: int, label: str) -> str:
    safe = "".join(ch if ch.isalnum() or ch in "-_" else "_" for ch in label.strip())
    safe = safe or "series"
    return f"ax{axis_index}_s{series_index}_{safe}"


def hash_rcparams(rcparams: Mapping[str, Any]) -> str:
    payload = json.dumps(rcparams, sort_keys=True, default=repr)
    return hashlib.sha256(payload.encode("utf-8")).hexdigest()


def round_floats(value: Any, precision: int) -> Any:
    if isinstance(value, float):
        return round(value, precision)
    if isinstance(value, list):
        return [round_floats(item, precision) for item in value]
    if isinstance(value, tuple):
        return [round_floats(item, precision) for item in value]
    if isinstance(value, dict):
        return {key: round_floats(item, precision) for key, item in value.items()}
    return value


def decimate_series(
    x: np.ndarray, y: np.ndarray, max_points: int
) -> tuple[np.ndarray, np.ndarray, dict[str, Any]]:
    n = len(x)
    if max_points <= 0:
        max_points = 1
    if n <= max_points:
        return (
            x,
            y,
            {
                "decimated": False,
                "method": None,
                "n_original": n,
                "max_points": max_points,
            },
        )

    target = max(max_points, 2)
    idx = np.linspace(0, n - 1, target, dtype=int)
    idx = np.unique(idx)
    return (
        x[idx],
        y[idx],
        {
            "decimated": True,
            "method": "uniform",
            "n_original": n,
            "max_points": max_points,
        },
    )


==== FILE: pyproject.toml ====

[project]
name = "mpl-plot-report"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
    "matplotlib>=3.8.0",
    "numpy>=2.0.0",
]

[project.scripts]
mpl-plot-report = "mpl_plot_report.cli:main"

[dependency-groups]
dev = [
    "black>=26.1.0",
    "produm>=0.1.2",
    "pytest>=9.0.2",
]


==== FILE: scripts/generate_fixtures.py ====

from __future__ import annotations

from pathlib import Path
import sys

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
import numpy as np

ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
    sys.path.insert(0, str(ROOT))

from mpl_plot_report.api import dump_report


def make_happy_path() -> Figure:
    x = np.linspace(0, 10, 200)
    y = np.sin(x) * 0.5 + 0.5
    fig, ax = plt.subplots()
    ax.plot(x, y, label="happy")
    ax.set_title("Happy Path")
    return fig


def make_nan_ordering() -> Figure:
    x = np.array([0, 1, 2, 1.5, 3, 4, 5], dtype=float)
    y = np.array([0, 1, np.nan, 2, np.inf, 3, 4], dtype=float)
    fig, ax = plt.subplots()
    ax.plot(x, y, label="nan_ordering")
    ax.set_title("NaN + Ordering")
    return fig


def make_wiggles_outliers() -> Figure:
    x = np.linspace(0, 6, 400)
    y = np.sin(12 * x) * 0.2
    y[200:] += 1.0
    y[300] += 3.0
    fig, ax = plt.subplots()
    ax.plot(x, y, label="wiggles")
    ax.set_title("Wiggles + Outlier")
    return fig


def main() -> None:
    out_dir = Path("tests/fixtures/expected")
    out_dir.mkdir(parents=True, exist_ok=True)

    figures = {
        "happy_path": make_happy_path,
        "nan_ordering": make_nan_ordering,
        "wiggles_outliers": make_wiggles_outliers,
    }

    for stem, factory in figures.items():
        fig = factory()
        dump_report(
            fig,
            out_dir,
            stem,
            context={"run_id": "fixtures", "style_name": "default"},
            include_data=True,
            max_points=2000,
        )


if __name__ == "__main__":
    main()


==== FILE: tests/conftest.py ====

from __future__ import annotations

import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
    sys.path.insert(0, str(ROOT))


==== FILE: tests/fixtures/expected/happy_path.plot.json ====

{
  "axes": [
    {
      "index": 0,
      "legend": [],
      "series": [
        {
          "data": {
            "decimation": {
              "decimated": false,
              "max_points": 2000,
              "method": null,
              "n_original": 200
            },
            "x": [
              0.0,
              0.050251,
              0.100503,
              0.150754,
              0.201005,
              0.251256,
              0.301508,
              0.351759,
              0.40201,
              0.452261,
              0.502513,
              0.552764,
              0.603015,
              0.653266,
              0.703518,
              0.753769,
              0.80402,
              0.854271,
              0.904523,
              0.954774,
              1.005025,
              1.055276,
              1.105528,
              1.155779,
              1.20603,
              1.256281,
              1.306533,
              1.356784,
              1.407035,
              1.457286,
              1.507538,
              1.557789,
              1.60804,
              1.658291,
              1.708543,
              1.758794,
              1.809045,
              1.859296,
              1.909548,
              1.959799,
              2.01005,
              2.060302,
              2.110553,
              2.160804,
              2.211055,
              2.261307,
              2.311558,
              2.361809,
              2.41206,
              2.462312,
              2.512563,
              2.562814,
              2.613065,
              2.663317,
              2.713568,
              2.763819,
              2.81407,
              2.864322,
              2.914573,
              2.964824,
              3.015075,
              3.065327,
              3.115578,
              3.165829,
              3.21608,
              3.266332,
              3.316583,
              3.366834,
              3.417085,
              3.467337,
              3.517588,
              3.567839,
              3.61809,
              3.668342,
              3.718593,
              3.768844,
              3.819095,
              3.869347,
              3.919598,
              3.969849,
              4.020101,
              4.070352,
              4.120603,
              4.170854,
              4.221106,
              4.271357,
              4.321608,
              4.371859,
              4.422111,
              4.472362,
              4.522613,
              4.572864,
              4.623116,
              4.673367,
              4.723618,
              4.773869,
              4.824121,
              4.874372,
              4.924623,
              4.974874,
              5.025126,
              5.075377,
              5.125628,
              5.175879,
              5.226131,
              5.276382,
              5.326633,
              5.376884,
              5.427136,
              5.477387,
              5.527638,
              5.577889,
              5.628141,
              5.678392,
              5.728643,
              5.778894,
              5.829146,
              5.879397,
              5.929648,
              5.979899,
              6.030151,
              6.080402,
              6.130653,
              6.180905,
              6.231156,
              6.281407,
              6.331658,
              6.38191,
              6.432161,
              6.482412,
              6.532663,
              6.582915,
              6.633166,
              6.683417,
              6.733668,
              6.78392,
              6.834171,
              6.884422,
              6.934673,
              6.984925,
              7.035176,
              7.085427,
              7.135678,
              7.18593,
              7.236181,
              7.286432,
              7.336683,
              7.386935,
              7.437186,
              7.487437,
              7.537688,
              7.58794,
              7.638191,
              7.688442,
              7.738693,
              7.788945,
              7.839196,
              7.889447,
              7.939698,
              7.98995,
              8.040201,
              8.090452,
              8.140704,
              8.190955,
              8.241206,
              8.291457,
              8.341709,
              8.39196,
              8.442211,
              8.492462,
              8.542714,
              8.592965,
              8.643216,
              8.693467,
              8.743719,
              8.79397,
              8.844221,
              8.894472,
              8.944724,
              8.994975,
              9.045226,
              9.095477,
              9.145729,
              9.19598,
              9.246231,
              9.296482,
              9.346734,
              9.396985,
              9.447236,
              9.497487,
              9.547739,
              9.59799,
              9.648241,
              9.698492,
              9.748744,
              9.798995,
              9.849246,
              9.899497,
              9.949749,
              10.0
            ],
            "y": [
              0.5,
              0.525115,
              0.550167,
              0.575092,
              0.599827,
              0.62431,
              0.64848,
              0.672275,
              0.695634,
              0.7185,
              0.740815,
              0.762521,
              0.783564,
              0.803892,
              0.823452,
              0.842196,
              0.860076,
              0.877046,
              0.893065,
              0.908092,
              0.922088,
              0.935018,
              0.946851,
              0.957555,
              0.967104,
              0.975473,
              0.982643,
              0.988593,
              0.993311,
              0.996782,
              0.999,
              0.999958,
              0.999653,
              0.998087,
              0.995264,
              0.99119,
              0.985876,
              0.979336,
              0.971585,
              0.962644,
              0.952535,
              0.941283,
              0.928917,
              0.915468,
              0.900971,
              0.885461,
              0.868977,
              0.851563,
              0.833261,
              0.814117,
              0.79418,
              0.773501,
              0.752131,
              0.730125,
              0.707537,
              0.684426,
              0.660849,
              0.636866,
              0.612537,
              0.587925,
              0.56309,
              0.538096,
              0.513006,
              0.487883,
              0.462791,
              0.437792,
              0.412951,
              0.388329,
              0.363989,
              0.339993,
              0.316401,
              0.293272,
              0.270665,
              0.248637,
              0.227244,
              0.206539,
              0.186575,
              0.167403,
              0.14907,
              0.131623,
              0.115106,
              0.099561,
              0.085027,
              0.071541,
              0.059136,
              0.047844,
              0.037694,
              0.028711,
              0.020918,
              0.014334,
              0.008977,
              0.004859,
              0.001991,
              0.000381,
              3.2e-05,
              0.000945,
              0.003118,
              0.006545,
              0.011219,
              0.017126,
              0.024252,
              0.03258,
              0.042088,
              0.052751,
              0.064544,
              0.077436,
              0.091395,
              0.106386,
              0.12237,
              0.139308,
              0.157156,
              0.17587,
              0.195403,
              0.215704,
              0.236723,
              0.258407,
              0.2807,
              0.303548,
              0.326891,
              0.350671,
              0.374828,
              0.399302,
              0.424029,
              0.448949,
              0.473997,
              0.499111,
              0.524227,
              0.549282,
              0.574213,
              0.598956,
              0.623449,
              0.647631,
              0.67144,
              0.694816,
              0.7177,
              0.740035,
              0.761764,
              0.782831,
              0.803185,
              0.822774,
              0.841547,
              0.859458,
              0.876462,
              0.892515,
              0.907577,
              0.92161,
              0.934579,
              0.946451,
              0.957195,
              0.966786,
              0.975197,
              0.98241,
              0.988404,
              0.993165,
              0.996681,
              0.998943,
              0.999945,
              0.999686,
              0.998164,
              0.995385,
              0.991356,
              0.986085,
              0.979588,
              0.97188,
              0.96298,
              0.952912,
              0.9417,
              0.929373,
              0.915962,
              0.901501,
              0.886026,
              0.869577,
              0.852194,
              0.833923,
              0.814808,
              0.794899,
              0.774245,
              0.752898,
              0.730914,
              0.708346,
              0.685252,
              0.661691,
              0.637721,
              0.613404,
              0.5888,
              0.563972,
              0.538983,
              0.513895,
              0.488772,
              0.463677,
              0.438674,
              0.413826,
              0.389196,
              0.364845,
              0.340836,
              0.317228,
              0.294082,
              0.271455,
              0.249406,
              0.227989
            ]
          },
          "diagnostics": {
            "curvature_max_abs": 0.001262,
            "curvature_sign_changes": 3,
            "curvature_std": 0.000841,
            "dx_median": 0.050251,
            "dx_min": 0.050251,
            "dx_non_positive_count": 0,
            "dy_max_abs": 0.025123,
            "dy_outlier_count": 0,
            "dy_robust_z_max": 1.013353,
            "n": 200,
            "non_finite_count": 0,
            "slope_mean": -0.027201,
            "slope_median": -0.055302,
            "slope_p10": -0.484183,
            "slope_p90": 0.473285,
            "slope_std": 0.360473,
            "wiggle_sign_changes": 3
          },
          "id": "ax0_s0_happy",
          "kind": "line2d",
          "label": "happy",
          "stats": {
            "n": 200,
            "x_endpoints": [
              0.0,
              10.0
            ],
            "x_max": 10.0,
            "x_min": 0.0,
            "y_endpoints": [
              0.5,
              0.227989
            ],
            "y_max": 0.999958,
            "y_min": 3.2e-05
          }
        }
      ],
      "title": "Happy Path",
      "xlabel": "",
      "xlim": [
        -0.5,
        10.5
      ],
      "xscale": "linear",
      "ylabel": "",
      "ylim": [
        -0.049965,
        1.049954
      ],
      "yscale": "linear"
    }
  ],
  "context": {
    "run_id": "fixtures",
    "style": {
      "name": "default",
      "rcparams_hash": "66132a6156c9be7c48dfc67d2c148a68a2c1e5d4887205007c121326c2ecd51e"
    },
    "style_name": "default"
  },
  "figure": {
    "backend": "Agg",
    "dpi": 100.0,
    "size_inches": [
      6.4,
      4.8
    ]
  },
  "invariants": [],
  "schema_version": "1.0",
  "warnings": []
}

==== FILE: tests/fixtures/expected/happy_path.plot.md ====

# Plot Report

- Schema: 1.0
- Axes: 1
- Invariants: 0
- Warnings: 0

## Axes Summary
- Axis 0: Happy Path (1 series)


==== FILE: tests/fixtures/expected/nan_ordering.plot.json ====

{
  "axes": [
    {
      "index": 0,
      "legend": [],
      "series": [
        {
          "data": {
            "decimation": {
              "decimated": false,
              "max_points": 2000,
              "method": null,
              "n_original": 7
            },
            "x": [
              0.0,
              1.0,
              2.0,
              1.5,
              3.0,
              4.0,
              5.0
            ],
            "y": [
              0.0,
              1.0,
              NaN,
              2.0,
              Infinity,
              3.0,
              4.0
            ]
          },
          "diagnostics": {
            "curvature_max_abs": 0.0,
            "curvature_sign_changes": 0,
            "curvature_std": 0.0,
            "dx_median": 1.0,
            "dx_min": -0.5,
            "dx_non_positive_count": 1,
            "dy_max_abs": 1.0,
            "dy_outlier_count": 0,
            "dy_robust_z_max": 0.0,
            "n": 7,
            "non_finite_count": 2,
            "slope_mean": 1.1,
            "slope_median": 1.0,
            "slope_p10": 0.58,
            "slope_p90": 1.7,
            "slope_std": 0.574456,
            "wiggle_sign_changes": 0
          },
          "id": "ax0_s0_nan_ordering",
          "kind": "line2d",
          "label": "nan_ordering",
          "stats": {
            "n": 7,
            "x_endpoints": [
              0.0,
              5.0
            ],
            "x_max": 5.0,
            "x_min": 0.0,
            "y_endpoints": [
              0.0,
              4.0
            ],
            "y_max": 4.0,
            "y_min": 0.0
          }
        }
      ],
      "title": "NaN + Ordering",
      "xlabel": "",
      "xlim": [
        -0.25,
        5.25
      ],
      "xscale": "linear",
      "ylabel": "",
      "ylim": [
        -0.2,
        4.2
      ],
      "yscale": "linear"
    }
  ],
  "context": {
    "run_id": "fixtures",
    "style": {
      "name": "default",
      "rcparams_hash": "66132a6156c9be7c48dfc67d2c148a68a2c1e5d4887205007c121326c2ecd51e"
    },
    "style_name": "default"
  },
  "figure": {
    "backend": "Agg",
    "dpi": 100.0,
    "size_inches": [
      6.4,
      4.8
    ]
  },
  "invariants": [],
  "schema_version": "1.0",
  "warnings": [
    {
      "code": "non_finite",
      "message": "Series contains non-finite values.",
      "where": {
        "axis_index": 0,
        "series_index": 0
      }
    }
  ]
}

==== FILE: tests/fixtures/expected/nan_ordering.plot.md ====

# Plot Report

- Schema: 1.0
- Axes: 1
- Invariants: 0
- Warnings: 1

## Warnings
- non_finite: Series contains non-finite values. ({'axis_index': 0, 'series_index': 0})

## Axes Summary
- Axis 0: NaN + Ordering (1 series)


==== FILE: tests/fixtures/expected/wiggles_outliers.plot.json ====

{
  "axes": [
    {
      "index": 0,
      "legend": [],
      "series": [
        {
          "data": {
            "decimation": {
              "decimated": false,
              "max_points": 2000,
              "method": null,
              "n_original": 400
            },
            "x": [
              0.0,
              0.015038,
              0.030075,
              0.045113,
              0.06015,
              0.075188,
              0.090226,
              0.105263,
              0.120301,
              0.135338,
              0.150376,
              0.165414,
              0.180451,
              0.195489,
              0.210526,
              0.225564,
              0.240602,
              0.255639,
              0.270677,
              0.285714,
              0.300752,
              0.315789,
              0.330827,
              0.345865,
              0.360902,
              0.37594,
              0.390977,
              0.406015,
              0.421053,
              0.43609,
              0.451128,
              0.466165,
              0.481203,
              0.496241,
              0.511278,
              0.526316,
              0.541353,
              0.556391,
              0.571429,
              0.586466,
              0.601504,
              0.616541,
              0.631579,
              0.646617,
              0.661654,
              0.676692,
              0.691729,
              0.706767,
              0.721805,
              0.736842,
              0.75188,
              0.766917,
              0.781955,
              0.796992,
              0.81203,
              0.827068,
              0.842105,
              0.857143,
              0.87218,
              0.887218,
              0.902256,
              0.917293,
              0.932331,
              0.947368,
              0.962406,
              0.977444,
              0.992481,
              1.007519,
              1.022556,
              1.037594,
              1.052632,
              1.067669,
              1.082707,
              1.097744,
              1.112782,
              1.12782,
              1.142857,
              1.157895,
              1.172932,
              1.18797,
              1.203008,
              1.218045,
              1.233083,
              1.24812,
              1.263158,
              1.278195,
              1.293233,
              1.308271,
              1.323308,
              1.338346,
              1.353383,
              1.368421,
              1.383459,
              1.398496,
              1.413534,
              1.428571,
              1.443609,
              1.458647,
              1.473684,
              1.488722,
              1.503759,
              1.518797,
              1.533835,
              1.548872,
              1.56391,
              1.578947,
              1.593985,
              1.609023,
              1.62406,
              1.639098,
              1.654135,
              1.669173,
              1.684211,
              1.699248,
              1.714286,
              1.729323,
              1.744361,
              1.759398,
              1.774436,
              1.789474,
              1.804511,
              1.819549,
              1.834586,
              1.849624,
              1.864662,
              1.879699,
              1.894737,
              1.909774,
              1.924812,
              1.93985,
              1.954887,
              1.969925,
              1.984962,
              2.0,
              2.015038,
              2.030075,
              2.045113,
              2.06015,
              2.075188,
              2.090226,
              2.105263,
              2.120301,
              2.135338,
              2.150376,
              2.165414,
              2.180451,
              2.195489,
              2.210526,
              2.225564,
              2.240602,
              2.255639,
              2.270677,
              2.285714,
              2.300752,
              2.315789,
              2.330827,
              2.345865,
              2.360902,
              2.37594,
              2.390977,
              2.406015,
              2.421053,
              2.43609,
              2.451128,
              2.466165,
              2.481203,
              2.496241,
              2.511278,
              2.526316,
              2.541353,
              2.556391,
              2.571429,
              2.586466,
              2.601504,
              2.616541,
              2.631579,
              2.646617,
              2.661654,
              2.676692,
              2.691729,
              2.706767,
              2.721805,
              2.736842,
              2.75188,
              2.766917,
              2.781955,
              2.796992,
              2.81203,
              2.827068,
              2.842105,
              2.857143,
              2.87218,
              2.887218,
              2.902256,
              2.917293,
              2.932331,
              2.947368,
              2.962406,
              2.977444,
              2.992481,
              3.007519,
              3.022556,
              3.037594,
              3.052632,
              3.067669,
              3.082707,
              3.097744,
              3.112782,
              3.12782,
              3.142857,
              3.157895,
              3.172932,
              3.18797,
              3.203008,
              3.218045,
              3.233083,
              3.24812,
              3.263158,
              3.278195,
              3.293233,
              3.308271,
              3.323308,
              3.338346,
              3.353383,
              3.368421,
              3.383459,
              3.398496,
              3.413534,
              3.428571,
              3.443609,
              3.458647,
              3.473684,
              3.488722,
              3.503759,
              3.518797,
              3.533835,
              3.548872,
              3.56391,
              3.578947,
              3.593985,
              3.609023,
              3.62406,
              3.639098,
              3.654135,
              3.669173,
              3.684211,
              3.699248,
              3.714286,
              3.729323,
              3.744361,
              3.759398,
              3.774436,
              3.789474,
              3.804511,
              3.819549,
              3.834586,
              3.849624,
              3.864662,
              3.879699,
              3.894737,
              3.909774,
              3.924812,
              3.93985,
              3.954887,
              3.969925,
              3.984962,
              4.0,
              4.015038,
              4.030075,
              4.045113,
              4.06015,
              4.075188,
              4.090226,
              4.105263,
              4.120301,
              4.135338,
              4.150376,
              4.165414,
              4.180451,
              4.195489,
              4.210526,
              4.225564,
              4.240602,
              4.255639,
              4.270677,
              4.285714,
              4.300752,
              4.315789,
              4.330827,
              4.345865,
              4.360902,
              4.37594,
              4.390977,
              4.406015,
              4.421053,
              4.43609,
              4.451128,
              4.466165,
              4.481203,
              4.496241,
              4.511278,
              4.526316,
              4.541353,
              4.556391,
              4.571429,
              4.586466,
              4.601504,
              4.616541,
              4.631579,
              4.646617,
              4.661654,
              4.676692,
              4.691729,
              4.706767,
              4.721805,
              4.736842,
              4.75188,
              4.766917,
              4.781955,
              4.796992,
              4.81203,
              4.827068,
              4.842105,
              4.857143,
              4.87218,
              4.887218,
              4.902256,
              4.917293,
              4.932331,
              4.947368,
              4.962406,
              4.977444,
              4.992481,
              5.007519,
              5.022556,
              5.037594,
              5.052632,
              5.067669,
              5.082707,
              5.097744,
              5.112782,
              5.12782,
              5.142857,
              5.157895,
              5.172932,
              5.18797,
              5.203008,
              5.218045,
              5.233083,
              5.24812,
              5.263158,
              5.278195,
              5.293233,
              5.308271,
              5.323308,
              5.338346,
              5.353383,
              5.368421,
              5.383459,
              5.398496,
              5.413534,
              5.428571,
              5.443609,
              5.458647,
              5.473684,
              5.488722,
              5.503759,
              5.518797,
              5.533835,
              5.548872,
              5.56391,
              5.578947,
              5.593985,
              5.609023,
              5.62406,
              5.639098,
              5.654135,
              5.669173,
              5.684211,
              5.699248,
              5.714286,
              5.729323,
              5.744361,
              5.759398,
              5.774436,
              5.789474,
              5.804511,
              5.819549,
              5.834586,
              5.849624,
              5.864662,
              5.879699,
              5.894737,
              5.909774,
              5.924812,
              5.93985,
              5.954887,
              5.969925,
              5.984962,
              6.0
            ],
            "y": [
              0.0,
              0.035895,
              0.070624,
              0.103059,
              0.132148,
              0.156945,
              0.176646,
              0.19061,
              0.198385,
              0.199716,
              0.194563,
              0.18309,
              0.165673,
              0.142875,
              0.115437,
              0.08425,
              0.050328,
              0.014771,
              -0.021265,
              -0.056611,
              -0.090119,
              -0.1207,
              -0.147361,
              -0.169237,
              -0.185617,
              -0.195969,
              -0.199957,
              -0.197452,
              -0.188535,
              -0.173495,
              -0.152821,
              -0.127184,
              -0.097417,
              -0.064486,
              -0.029462,
              0.00652,
              0.042289,
              0.076686,
              0.108592,
              0.136972,
              0.160903,
              0.17961,
              0.192483,
              0.199106,
              0.199263,
              0.192949,
              0.180369,
              0.161932,
              0.138236,
              0.110052,
              0.078293,
              0.043991,
              0.008262,
              -0.027737,
              -0.062834,
              -0.095891,
              -0.125834,
              -0.151691,
              -0.172621,
              -0.187946,
              -0.197167,
              -0.199986,
              -0.19631,
              -0.186259,
              -0.170159,
              -0.148534,
              -0.122085,
              -0.091671,
              -0.058281,
              -0.022998,
              0.013032,
              0.048639,
              0.082666,
              0.114009,
              0.14165,
              0.16469,
              0.182382,
              0.194151,
              0.199616,
              0.198598,
              0.191131,
              0.177457,
              0.15802,
              0.133451,
              0.104549,
              0.072252,
              0.037608,
              0.001743,
              -0.034179,
              -0.06899,
              -0.101562,
              -0.130835,
              -0.155859,
              -0.175822,
              -0.190075,
              -0.198156,
              -0.199802,
              -0.194959,
              -0.183785,
              -0.166643,
              -0.144089,
              -0.116856,
              -0.085828,
              -0.052013,
              -0.016509,
              0.019531,
              0.054937,
              0.088559,
              0.119305,
              0.146177,
              0.168302,
              0.184961,
              0.195613,
              0.199914,
              0.197722,
              0.189109,
              0.174355,
              0.153939,
              0.128524,
              0.098936,
              0.066134,
              0.031185,
              -0.004777,
              -0.040584,
              -0.075073,
              -0.107124,
              -0.135696,
              -0.159862,
              -0.178836,
              -0.192003,
              -0.198934,
              -0.199405,
              -0.193401,
              -0.181116,
              -0.162949,
              -0.139491,
              -0.111503,
              -0.079894,
              -0.04569,
              -0.010003,
              0.026009,
              0.061177,
              0.094358,
              0.124474,
              0.150549,
              0.171734,
              0.187343,
              0.196867,
              0.199999,
              0.196636,
              0.186887,
              0.171069,
              0.149695,
              0.123461,
              0.093217,
              0.059946,
              0.024728,
              -0.011293,
              -0.046947,
              -0.081076,
              -0.112573,
              -0.140414,
              -0.163695,
              -0.18166,
              -0.193726,
              -0.1995,
              -0.198797,
              -0.191637,
              -0.178254,
              -0.159082,
              -0.134745,
              -0.106031,
              -0.073874,
              -0.039319,
              -0.003486,
              0.03246,
              0.067352,
              0.100056,
              0.129511,
              0.154761,
              0.174985,
              0.189526,
              0.197912,
              0.199872,
              0.19534,
              0.184465,
              0.1676,
              0.145292,
              0.118266,
              0.087399,
              0.053694,
              0.018245,
              -0.017796,
              -0.053259,
              -0.086993,
              -0.117902,
              -0.144982,
              -0.167354,
              -0.184291,
              -0.195243,
              0.800145,
              0.802023,
              0.810331,
              0.824797,
              0.844954,
              0.870145,
              0.899553,
              0.932224,
              0.967095,
              1.003035,
              1.038876,
              1.073455,
              1.105648,
              1.134411,
              1.158808,
              1.178049,
              1.191507,
              1.198747,
              1.199532,
              1.193837,
              1.181848,
              1.163954,
              1.140735,
              1.112945,
              1.081489,
              1.047385,
              1.011743,
              0.97572,
              0.940485,
              0.907183,
              0.876895,
              0.850604,
              0.829166,
              0.813275,
              0.803447,
              0.800003,
              0.803053,
              0.8125,
              0.828035,
              0.849154,
              0.875173,
              0.905244,
              0.938394,
              0.973543,
              1.009552,
              1.045251,
              1.07948,
              1.111128,
              1.139167,
              1.162687,
              1.180924,
              1.193285,
              1.19937,
              1.19898,
              1.192128,
              1.179038,
              1.160133,
              1.136027,
              1.107505,
              1.075491,
              1.041026,
              1.005228,
              0.969261,
              0.934292,
              0.901457,
              0.871822,
              0.846349,
              0.825866,
              0.811038,
              0.802346,
              0.800074,
              0.804293,
              0.814868,
              0.831455,
              0.853515,
              0.880333,
              0.911036,
              0.944629,
              0.98002,
              1.016059,
              1.051577,
              1.08542,
              1.116489,
              1.143776,
              1.166393,
              1.183606,
              1.194858,
              1.199781,
              1.198217,
              1.190215,
              1.176037,
              1.156141,
              1.131176,
              1.10195,
              1.069414,
              1.034623,
              0.998708,
              0.962835,
              0.928169,
              0.895836,
              3.866885,
              0.842257,
              0.822752,
              0.809003,
              0.801456,
              0.800357,
              0.805741,
              0.817433,
              0.835054,
              0.858032,
              0.88562,
              0.916923,
              0.950923,
              0.986517,
              1.022549,
              1.057849,
              1.09127,
              1.121727,
              1.148231,
              1.169922,
              1.186094,
              1.196223,
              1.19998,
              1.197242,
              1.1881,
              1.172849,
              1.151984,
              1.126184,
              1.096287,
              1.063262,
              1.028184,
              0.992189,
              0.956449,
              0.922123,
              0.890326,
              0.86209,
              0.838333,
              0.819826,
              0.80717,
              0.800776,
              0.800852,
              0.807395,
              0.820192,
              0.838829,
              0.8627,
              0.891029,
              0.922898,
              0.95727,
              0.993029,
              1.029015,
              1.064059,
              1.097023,
              1.126835,
              1.152529,
              1.17327,
              1.188384,
              1.19738,
              1.199966,
              1.196059,
              1.185784,
              1.169477,
              1.147666,
              1.121059,
              1.090521,
              1.057044,
              1.021714,
              0.985679,
              0.950109,
              0.916159,
              0.884932,
              0.857442,
              0.834581,
              0.817092,
              0.805542,
              0.800308,
              0.801559,
              0.809254,
              0.823143,
              0.842775,
              0.867514,
              0.896554,
              0.928954,
              0.963661,
              0.999549,
              1.035451,
              1.070201,
              1.102672,
              1.131809,
              1.156665,
              1.176434,
              1.190473,
              1.198327,
              1.19974,
              1.194667,
              1.183272,
              1.165925,
              1.14319,
              1.115805,
              1.08466,
              1.050765
            ]
          },
          "diagnostics": {
            "curvature_max_abs": 5.995677,
            "curvature_sign_changes": 26,
            "curvature_std": 0.375129,
            "dx_median": 0.015038,
            "dx_min": 0.015038,
            "dx_non_positive_count": 0,
            "dy_max_abs": 3.024628,
            "dy_outlier_count": 3,
            "dy_robust_z_max": 79.514536,
            "n": 400,
            "non_finite_count": 0,
            "slope_mean": 0.175127,
            "slope_median": 0.052216,
            "slope_p10": -2.271896,
            "slope_p90": 2.289481,
            "slope_std": 14.595387,
            "wiggle_sign_changes": 25
          },
          "id": "ax0_s0_wiggles",
          "kind": "line2d",
          "label": "wiggles",
          "stats": {
            "n": 400,
            "x_endpoints": [
              0.0,
              6.0
            ],
            "x_max": 6.0,
            "x_min": 0.0,
            "y_endpoints": [
              0.0,
              1.050765
            ],
            "y_max": 3.866885,
            "y_min": -0.199986
          }
        }
      ],
      "title": "Wiggles + Outlier",
      "xlabel": "",
      "xlim": [
        -0.3,
        6.3
      ],
      "xscale": "linear",
      "ylabel": "",
      "ylim": [
        -0.403329,
        4.070229
      ],
      "yscale": "linear"
    }
  ],
  "context": {
    "run_id": "fixtures",
    "style": {
      "name": "default",
      "rcparams_hash": "66132a6156c9be7c48dfc67d2c148a68a2c1e5d4887205007c121326c2ecd51e"
    },
    "style_name": "default"
  },
  "figure": {
    "backend": "Agg",
    "dpi": 100.0,
    "size_inches": [
      6.4,
      4.8
    ]
  },
  "invariants": [],
  "schema_version": "1.0",
  "warnings": []
}

==== FILE: tests/fixtures/expected/wiggles_outliers.plot.md ====

# Plot Report

- Schema: 1.0
- Axes: 1
- Invariants: 0
- Warnings: 0

## Axes Summary
- Axis 0: Wiggles + Outlier (1 series)


==== FILE: tests/test_report.py ====

import numpy as np
import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt

from mpl_plot_report.api import build_report, dump_report


def test_dump_report_creates_sidecars(tmp_path):
    x = np.linspace(0, 1, 50)
    y = x**2
    fig, ax = plt.subplots()
    ax.plot(x, y, label="quad")

    report = dump_report(
        fig,
        tmp_path,
        "happy",
        context={"run_id": "test"},
        include_data=True,
    )

    assert (tmp_path / "happy.png").exists()
    assert (tmp_path / "happy.plot.json").exists()
    assert (tmp_path / "happy.plot.md").exists()
    assert report["schema_version"] == "1.0"
    assert len(report["axes"]) == 1
    assert len(report["axes"][0]["series"]) == 1


def test_pathology_diagnostics_and_warnings():
    x = np.array([0, 1, 2, 1, 3, 4], dtype=float)
    y = np.array([0, 1, np.nan, 2, np.inf, 3], dtype=float)
    fig, ax = plt.subplots()
    ax.plot(x, y, label="pathology")

    report = build_report(fig, include_data=False)
    diagnostics = report["axes"][0]["series"][0]["diagnostics"]

    assert diagnostics["non_finite_count"] > 0
    assert diagnostics["dx_non_positive_count"] > 0
    assert any(warning["code"] == "non_finite" for warning in report["warnings"])


def test_decimation_metadata():
    x = np.arange(0, 5000, dtype=float)
    y = np.sin(x)
    fig, ax = plt.subplots()
    ax.plot(x, y, label="long")

    report = build_report(fig, include_data=True, max_points=200)
    data = report["axes"][0]["series"][0]["data"]

    assert len(data["x"]) <= 200
    assert data["decimation"]["decimated"] is True
    assert data["decimation"]["n_original"] == 5000


==== FILE: uv.lock ====

version = 1
revision = 3
requires-python = ">=3.13"

[[package]]
name = "black"
version = "26.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
    { name = "click" },
    { name = "mypy-extensions" },
    { name = "packaging" },
    { name = "pathspec" },
    { name = "platformdirs" },
    { name = "pytokens" },
]
sdist = { url = "https://files.pythonhosted.org/packages/13/88/560b11e521c522440af991d46848a2bde64b5f7202ec14e1f46f9509d328/black-26.1.0.tar.gz", hash = "sha256:d294ac3340eef9c9eb5d29288e96dc719ff269a88e27b396340459dd85da4c58", size = 658785, upload-time = "2026-01-18T04:50:11.993Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/79/04/fa2f4784f7237279332aa735cdfd5ae2e7730db0072fb2041dadda9ae551/black-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ba1d768fbfb6930fc93b0ecc32a43d8861ded16f47a40f14afa9bb04ab93d304", size = 1877781, upload-time = "2026-01-18T04:59:39.054Z" },
    { url = "https://files.pythonhosted.org/packages/cf/ad/5a131b01acc0e5336740a039628c0ab69d60cf09a2c87a4ec49f5826acda/black-26.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b807c240b64609cb0e80d2200a35b23c7df82259f80bef1b2c96eb422b4aac9", size = 1699670, upload-time = "2026-01-18T04:59:41.005Z" },
    { url = "https://files.pythonhosted.org/packages/da/7c/b05f22964316a52ab6b4265bcd52c0ad2c30d7ca6bd3d0637e438fc32d6e/black-26.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1de0f7d01cc894066a1153b738145b194414cc6eeaad8ef4397ac9abacf40f6b", size = 1775212, upload-time = "2026-01-18T04:59:42.545Z" },
    { url = "https://files.pythonhosted.org/packages/a6/a3/e8d1526bea0446e040193185353920a9506eab60a7d8beb062029129c7d2/black-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:91a68ae46bf07868963671e4d05611b179c2313301bd756a89ad4e3b3db2325b", size = 1409953, upload-time = "2026-01-18T04:59:44.357Z" },
    { url = "https://files.pythonhosted.org/packages/c7/5a/d62ebf4d8f5e3a1daa54adaab94c107b57be1b1a2f115a0249b41931e188/black-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:be5e2fe860b9bd9edbf676d5b60a9282994c03fbbd40fe8f5e75d194f96064ca", size = 1217707, upload-time = "2026-01-18T04:59:45.719Z" },
    { url = "https://files.pythonhosted.org/packages/6a/83/be35a175aacfce4b05584ac415fd317dd6c24e93a0af2dcedce0f686f5d8/black-26.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9dc8c71656a79ca49b8d3e2ce8103210c9481c57798b48deeb3a8bb02db5f115", size = 1871864, upload-time = "2026-01-18T04:59:47.586Z" },
    { url = "https://files.pythonhosted.org/packages/a5/f5/d33696c099450b1274d925a42b7a030cd3ea1f56d72e5ca8bbed5f52759c/black-26.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b22b3810451abe359a964cc88121d57f7bce482b53a066de0f1584988ca36e79", size = 1701009, upload-time = "2026-01-18T04:59:49.443Z" },
    { url = "https://files.pythonhosted.org/packages/1b/87/670dd888c537acb53a863bc15abbd85b22b429237d9de1b77c0ed6b79c42/black-26.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53c62883b3f999f14e5d30b5a79bd437236658ad45b2f853906c7cbe79de00af", size = 1767806, upload-time = "2026-01-18T04:59:50.769Z" },
    { url = "https://files.pythonhosted.org/packages/fe/9c/cd3deb79bfec5bcf30f9d2100ffeec63eecce826eb63e3961708b9431ff1/black-26.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:f016baaadc423dc960cdddf9acae679e71ee02c4c341f78f3179d7e4819c095f", size = 1433217, upload-time = "2026-01-18T04:59:52.218Z" },
    { url = "https://files.pythonhosted.org/packages/4e/29/f3be41a1cf502a283506f40f5d27203249d181f7a1a2abce1c6ce188035a/black-26.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:66912475200b67ef5a0ab665011964bf924745103f51977a78b4fb92a9fc1bf0", size = 1245773, upload-time = "2026-01-18T04:59:54.457Z" },
    { url = "https://files.pythonhosted.org/packages/e4/3d/51bdb3ecbfadfaf825ec0c75e1de6077422b4afa2091c6c9ba34fbfc0c2d/black-26.1.0-py3-none-any.whl", hash = "sha256:1054e8e47ebd686e078c0bb0eaf31e6ce69c966058d122f2c0c950311f9f3ede", size = 204010, upload-time = "2026-01-18T04:50:09.978Z" },
]

[[package]]
name = "click"
version = "8.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
    { name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" },
]

[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]

[[package]]
name = "contourpy"
version = "1.3.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
    { name = "numpy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" },
    { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" },
    { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" },
    { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" },
    { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" },
    { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" },
    { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" },
    { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" },
    { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" },
    { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" },
    { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" },
    { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" },
    { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" },
    { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" },
    { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" },
    { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" },
    { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" },
    { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" },
    { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" },
    { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" },
    { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" },
    { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" },
    { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" },
    { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" },
    { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" },
    { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" },
    { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" },
    { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" },
    { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" },
    { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" },
    { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" },
    { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" },
    { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" },
    { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" },
    { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" },
    { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" },
    { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" },
    { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" },
    { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" },
    { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" },
    { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" },
    { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" },
    { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" },
    { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" },
]

[[package]]
name = "cycler"
version = "0.12.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" },
]

[[package]]
name = "fonttools"
version = "4.61.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ec/ca/cf17b88a8df95691275a3d77dc0a5ad9907f328ae53acbe6795da1b2f5ed/fonttools-4.61.1.tar.gz", hash = "sha256:6675329885c44657f826ef01d9e4fb33b9158e9d93c537d84ad8399539bc6f69", size = 3565756, upload-time = "2025-12-12T17:31:24.246Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/4b/cf/00ba28b0990982530addb8dc3e9e6f2fa9cb5c20df2abdda7baa755e8fe1/fonttools-4.61.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c56c488ab471628ff3bfa80964372fc13504ece601e0d97a78ee74126b2045c", size = 2846454, upload-time = "2025-12-12T17:30:24.938Z" },
    { url = "https://files.pythonhosted.org/packages/5a/ca/468c9a8446a2103ae645d14fee3f610567b7042aba85031c1c65e3ef7471/fonttools-4.61.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dc492779501fa723b04d0ab1f5be046797fee17d27700476edc7ee9ae535a61e", size = 2398191, upload-time = "2025-12-12T17:30:27.343Z" },
    { url = "https://files.pythonhosted.org/packages/a3/4b/d67eedaed19def5967fade3297fed8161b25ba94699efc124b14fb68cdbc/fonttools-4.61.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:64102ca87e84261419c3747a0d20f396eb024bdbeb04c2bfb37e2891f5fadcb5", size = 4928410, upload-time = "2025-12-12T17:30:29.771Z" },
    { url = "https://files.pythonhosted.org/packages/b0/8d/6fb3494dfe61a46258cd93d979cf4725ded4eb46c2a4ca35e4490d84daea/fonttools-4.61.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c1b526c8d3f615a7b1867f38a9410849c8f4aef078535742198e942fba0e9bd", size = 4984460, upload-time = "2025-12-12T17:30:32.073Z" },
    { url = "https://files.pythonhosted.org/packages/f7/f1/a47f1d30b3dc00d75e7af762652d4cbc3dff5c2697a0dbd5203c81afd9c3/fonttools-4.61.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:41ed4b5ec103bd306bb68f81dc166e77409e5209443e5773cb4ed837bcc9b0d3", size = 4925800, upload-time = "2025-12-12T17:30:34.339Z" },
    { url = "https://files.pythonhosted.org/packages/a7/01/e6ae64a0981076e8a66906fab01539799546181e32a37a0257b77e4aa88b/fonttools-4.61.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b501c862d4901792adaec7c25b1ecc749e2662543f68bb194c42ba18d6eec98d", size = 5067859, upload-time = "2025-12-12T17:30:36.593Z" },
    { url = "https://files.pythonhosted.org/packages/73/aa/28e40b8d6809a9b5075350a86779163f074d2b617c15d22343fce81918db/fonttools-4.61.1-cp313-cp313-win32.whl", hash = "sha256:4d7092bb38c53bbc78e9255a59158b150bcdc115a1e3b3ce0b5f267dc35dd63c", size = 2267821, upload-time = "2025-12-12T17:30:38.478Z" },
    { url = "https://files.pythonhosted.org/packages/1a/59/453c06d1d83dc0951b69ef692d6b9f1846680342927df54e9a1ca91c6f90/fonttools-4.61.1-cp313-cp313-win_amd64.whl", hash = "sha256:21e7c8d76f62ab13c9472ccf74515ca5b9a761d1bde3265152a6dc58700d895b", size = 2318169, upload-time = "2025-12-12T17:30:40.951Z" },
    { url = "https://files.pythonhosted.org/packages/32/8f/4e7bf82c0cbb738d3c2206c920ca34ca74ef9dabde779030145d28665104/fonttools-4.61.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fff4f534200a04b4a36e7ae3cb74493afe807b517a09e99cb4faa89a34ed6ecd", size = 2846094, upload-time = "2025-12-12T17:30:43.511Z" },
    { url = "https://files.pythonhosted.org/packages/71/09/d44e45d0a4f3a651f23a1e9d42de43bc643cce2971b19e784cc67d823676/fonttools-4.61.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d9203500f7c63545b4ce3799319fe4d9feb1a1b89b28d3cb5abd11b9dd64147e", size = 2396589, upload-time = "2025-12-12T17:30:45.681Z" },
    { url = "https://files.pythonhosted.org/packages/89/18/58c64cafcf8eb677a99ef593121f719e6dcbdb7d1c594ae5a10d4997ca8a/fonttools-4.61.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa646ecec9528bef693415c79a86e733c70a4965dd938e9a226b0fc64c9d2e6c", size = 4877892, upload-time = "2025-12-12T17:30:47.709Z" },
    { url = "https://files.pythonhosted.org/packages/8a/ec/9e6b38c7ba1e09eb51db849d5450f4c05b7e78481f662c3b79dbde6f3d04/fonttools-4.61.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f35ad7805edba3aac1a3710d104592df59f4b957e30108ae0ba6c10b11dd75", size = 4972884, upload-time = "2025-12-12T17:30:49.656Z" },
    { url = "https://files.pythonhosted.org/packages/5e/87/b5339da8e0256734ba0dbbf5b6cdebb1dd79b01dc8c270989b7bcd465541/fonttools-4.61.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b931ae8f62db78861b0ff1ac017851764602288575d65b8e8ff1963fed419063", size = 4924405, upload-time = "2025-12-12T17:30:51.735Z" },
    { url = "https://files.pythonhosted.org/packages/0b/47/e3409f1e1e69c073a3a6fd8cb886eb18c0bae0ee13db2c8d5e7f8495e8b7/fonttools-4.61.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b148b56f5de675ee16d45e769e69f87623a4944f7443850bf9a9376e628a89d2", size = 5035553, upload-time = "2025-12-12T17:30:54.823Z" },
    { url = "https://files.pythonhosted.org/packages/bf/b6/1f6600161b1073a984294c6c031e1a56ebf95b6164249eecf30012bb2e38/fonttools-4.61.1-cp314-cp314-win32.whl", hash = "sha256:9b666a475a65f4e839d3d10473fad6d47e0a9db14a2f4a224029c5bfde58ad2c", size = 2271915, upload-time = "2025-12-12T17:30:57.913Z" },
    { url = "https://files.pythonhosted.org/packages/52/7b/91e7b01e37cc8eb0e1f770d08305b3655e4f002fc160fb82b3390eabacf5/fonttools-4.61.1-cp314-cp314-win_amd64.whl", hash = "sha256:4f5686e1fe5fce75d82d93c47a438a25bf0d1319d2843a926f741140b2b16e0c", size = 2323487, upload-time = "2025-12-12T17:30:59.804Z" },
    { url = "https://files.pythonhosted.org/packages/39/5c/908ad78e46c61c3e3ed70c3b58ff82ab48437faf84ec84f109592cabbd9f/fonttools-4.61.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:e76ce097e3c57c4bcb67c5aa24a0ecdbd9f74ea9219997a707a4061fbe2707aa", size = 2929571, upload-time = "2025-12-12T17:31:02.574Z" },
    { url = "https://files.pythonhosted.org/packages/bd/41/975804132c6dea64cdbfbaa59f3518a21c137a10cccf962805b301ac6ab2/fonttools-4.61.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9cfef3ab326780c04d6646f68d4b4742aae222e8b8ea1d627c74e38afcbc9d91", size = 2435317, upload-time = "2025-12-12T17:31:04.974Z" },
    { url = "https://files.pythonhosted.org/packages/b0/5a/aef2a0a8daf1ebaae4cfd83f84186d4a72ee08fd6a8451289fcd03ffa8a4/fonttools-4.61.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a75c301f96db737e1c5ed5fd7d77d9c34466de16095a266509e13da09751bd19", size = 4882124, upload-time = "2025-12-12T17:31:07.456Z" },
    { url = "https://files.pythonhosted.org/packages/80/33/d6db3485b645b81cea538c9d1c9219d5805f0877fda18777add4671c5240/fonttools-4.61.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:91669ccac46bbc1d09e9273546181919064e8df73488ea087dcac3e2968df9ba", size = 5100391, upload-time = "2025-12-12T17:31:09.732Z" },
    { url = "https://files.pythonhosted.org/packages/6c/d6/675ba631454043c75fcf76f0ca5463eac8eb0666ea1d7badae5fea001155/fonttools-4.61.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c33ab3ca9d3ccd581d58e989d67554e42d8d4ded94ab3ade3508455fe70e65f7", size = 4978800, upload-time = "2025-12-12T17:31:11.681Z" },
    { url = "https://files.pythonhosted.org/packages/7f/33/d3ec753d547a8d2bdaedd390d4a814e8d5b45a093d558f025c6b990b554c/fonttools-4.61.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:664c5a68ec406f6b1547946683008576ef8b38275608e1cee6c061828171c118", size = 5006426, upload-time = "2025-12-12T17:31:13.764Z" },
    { url = "https://files.pythonhosted.org/packages/b4/40/cc11f378b561a67bea850ab50063366a0d1dd3f6d0a30ce0f874b0ad5664/fonttools-4.61.1-cp314-cp314t-win32.whl", hash = "sha256:aed04cabe26f30c1647ef0e8fbb207516fd40fe9472e9439695f5c6998e60ac5", size = 2335377, upload-time = "2025-12-12T17:31:16.49Z" },
    { url = "https://files.pythonhosted.org/packages/e4/ff/c9a2b66b39f8628531ea58b320d66d951267c98c6a38684daa8f50fb02f8/fonttools-4.61.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2180f14c141d2f0f3da43f3a81bc8aa4684860f6b0e6f9e165a4831f24e6a23b", size = 2400613, upload-time = "2025-12-12T17:31:18.769Z" },
    { url = "https://files.pythonhosted.org/packages/c7/4e/ce75a57ff3aebf6fc1f4e9d508b8e5810618a33d900ad6c19eb30b290b97/fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371", size = 1148996, upload-time = "2025-12-12T17:31:21.03Z" },
]

[[package]]
name = "iniconfig"
version = "2.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
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 = "kiwisolver"
version = "1.4.9"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564, upload-time = "2025-08-10T21:27:49.279Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/31/c1/c2686cda909742ab66c7388e9a1a8521a59eb89f8bcfbee28fc980d07e24/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5d0432ccf1c7ab14f9949eec60c5d1f924f17c037e9f8b33352fa05799359b8", size = 123681, upload-time = "2025-08-10T21:26:26.725Z" },
    { url = "https://files.pythonhosted.org/packages/ca/f0/f44f50c9f5b1a1860261092e3bc91ecdc9acda848a8b8c6abfda4a24dd5c/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efb3a45b35622bb6c16dbfab491a8f5a391fe0e9d45ef32f4df85658232ca0e2", size = 66464, upload-time = "2025-08-10T21:26:27.733Z" },
    { url = "https://files.pythonhosted.org/packages/2d/7a/9d90a151f558e29c3936b8a47ac770235f436f2120aca41a6d5f3d62ae8d/kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a12cf6398e8a0a001a059747a1cbf24705e18fe413bc22de7b3d15c67cffe3f", size = 64961, upload-time = "2025-08-10T21:26:28.729Z" },
    { url = "https://files.pythonhosted.org/packages/e9/e9/f218a2cb3a9ffbe324ca29a9e399fa2d2866d7f348ec3a88df87fc248fc5/kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098", size = 1474607, upload-time = "2025-08-10T21:26:29.798Z" },
    { url = "https://files.pythonhosted.org/packages/d9/28/aac26d4c882f14de59041636292bc838db8961373825df23b8eeb807e198/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5656aa670507437af0207645273ccdfee4f14bacd7f7c67a4306d0dcaeaf6eed", size = 1276546, upload-time = "2025-08-10T21:26:31.401Z" },
    { url = "https://files.pythonhosted.org/packages/8b/ad/8bfc1c93d4cc565e5069162f610ba2f48ff39b7de4b5b8d93f69f30c4bed/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bfc08add558155345129c7803b3671cf195e6a56e7a12f3dde7c57d9b417f525", size = 1294482, upload-time = "2025-08-10T21:26:32.721Z" },
    { url = "https://files.pythonhosted.org/packages/da/f1/6aca55ff798901d8ce403206d00e033191f63d82dd708a186e0ed2067e9c/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:40092754720b174e6ccf9e845d0d8c7d8e12c3d71e7fc35f55f3813e96376f78", size = 1343720, upload-time = "2025-08-10T21:26:34.032Z" },
    { url = "https://files.pythonhosted.org/packages/d1/91/eed031876c595c81d90d0f6fc681ece250e14bf6998c3d7c419466b523b7/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:497d05f29a1300d14e02e6441cf0f5ee81c1ff5a304b0d9fb77423974684e08b", size = 2224907, upload-time = "2025-08-10T21:26:35.824Z" },
    { url = "https://files.pythonhosted.org/packages/e9/ec/4d1925f2e49617b9cca9c34bfa11adefad49d00db038e692a559454dfb2e/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdd1a81a1860476eb41ac4bc1e07b3f07259e6d55bbf739b79c8aaedcf512799", size = 2321334, upload-time = "2025-08-10T21:26:37.534Z" },
    { url = "https://files.pythonhosted.org/packages/43/cb/450cd4499356f68802750c6ddc18647b8ea01ffa28f50d20598e0befe6e9/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e6b93f13371d341afee3be9f7c5964e3fe61d5fa30f6a30eb49856935dfe4fc3", size = 2488313, upload-time = "2025-08-10T21:26:39.191Z" },
    { url = "https://files.pythonhosted.org/packages/71/67/fc76242bd99f885651128a5d4fa6083e5524694b7c88b489b1b55fdc491d/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d75aa530ccfaa593da12834b86a0724f58bff12706659baa9227c2ccaa06264c", size = 2291970, upload-time = "2025-08-10T21:26:40.828Z" },
    { url = "https://files.pythonhosted.org/packages/75/bd/f1a5d894000941739f2ae1b65a32892349423ad49c2e6d0771d0bad3fae4/kiwisolver-1.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:dd0a578400839256df88c16abddf9ba14813ec5f21362e1fe65022e00c883d4d", size = 73894, upload-time = "2025-08-10T21:26:42.33Z" },
    { url = "https://files.pythonhosted.org/packages/95/38/dce480814d25b99a391abbddadc78f7c117c6da34be68ca8b02d5848b424/kiwisolver-1.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:d4188e73af84ca82468f09cadc5ac4db578109e52acb4518d8154698d3a87ca2", size = 64995, upload-time = "2025-08-10T21:26:43.889Z" },
    { url = "https://files.pythonhosted.org/packages/e2/37/7d218ce5d92dadc5ebdd9070d903e0c7cf7edfe03f179433ac4d13ce659c/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5a0f2724dfd4e3b3ac5a82436a8e6fd16baa7d507117e4279b660fe8ca38a3a1", size = 126510, upload-time = "2025-08-10T21:26:44.915Z" },
    { url = "https://files.pythonhosted.org/packages/23/b0/e85a2b48233daef4b648fb657ebbb6f8367696a2d9548a00b4ee0eb67803/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b11d6a633e4ed84fc0ddafd4ebfd8ea49b3f25082c04ad12b8315c11d504dc1", size = 67903, upload-time = "2025-08-10T21:26:45.934Z" },
    { url = "https://files.pythonhosted.org/packages/44/98/f2425bc0113ad7de24da6bb4dae1343476e95e1d738be7c04d31a5d037fd/kiwisolver-1.4.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61874cdb0a36016354853593cffc38e56fc9ca5aa97d2c05d3dcf6922cd55a11", size = 66402, upload-time = "2025-08-10T21:26:47.101Z" },
    { url = "https://files.pythonhosted.org/packages/98/d8/594657886df9f34c4177cc353cc28ca7e6e5eb562d37ccc233bff43bbe2a/kiwisolver-1.4.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:60c439763a969a6af93b4881db0eed8fadf93ee98e18cbc35bc8da868d0c4f0c", size = 1582135, upload-time = "2025-08-10T21:26:48.665Z" },
    { url = "https://files.pythonhosted.org/packages/5c/c6/38a115b7170f8b306fc929e166340c24958347308ea3012c2b44e7e295db/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92a2f997387a1b79a75e7803aa7ded2cfbe2823852ccf1ba3bcf613b62ae3197", size = 1389409, upload-time = "2025-08-10T21:26:50.335Z" },
    { url = "https://files.pythonhosted.org/packages/bf/3b/e04883dace81f24a568bcee6eb3001da4ba05114afa622ec9b6fafdc1f5e/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31d512c812daea6d8b3be3b2bfcbeb091dbb09177706569bcfc6240dcf8b41c", size = 1401763, upload-time = "2025-08-10T21:26:51.867Z" },
    { url = "https://files.pythonhosted.org/packages/9f/80/20ace48e33408947af49d7d15c341eaee69e4e0304aab4b7660e234d6288/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:52a15b0f35dad39862d376df10c5230155243a2c1a436e39eb55623ccbd68185", size = 1453643, upload-time = "2025-08-10T21:26:53.592Z" },
    { url = "https://files.pythonhosted.org/packages/64/31/6ce4380a4cd1f515bdda976a1e90e547ccd47b67a1546d63884463c92ca9/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a30fd6fdef1430fd9e1ba7b3398b5ee4e2887783917a687d86ba69985fb08748", size = 2330818, upload-time = "2025-08-10T21:26:55.051Z" },
    { url = "https://files.pythonhosted.org/packages/fa/e9/3f3fcba3bcc7432c795b82646306e822f3fd74df0ee81f0fa067a1f95668/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cc9617b46837c6468197b5945e196ee9ca43057bb7d9d1ae688101e4e1dddf64", size = 2419963, upload-time = "2025-08-10T21:26:56.421Z" },
    { url = "https://files.pythonhosted.org/packages/99/43/7320c50e4133575c66e9f7dadead35ab22d7c012a3b09bb35647792b2a6d/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:0ab74e19f6a2b027ea4f845a78827969af45ce790e6cb3e1ebab71bdf9f215ff", size = 2594639, upload-time = "2025-08-10T21:26:57.882Z" },
    { url = "https://files.pythonhosted.org/packages/65/d6/17ae4a270d4a987ef8a385b906d2bdfc9fce502d6dc0d3aea865b47f548c/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dba5ee5d3981160c28d5490f0d1b7ed730c22470ff7f6cc26cfcfaacb9896a07", size = 2391741, upload-time = "2025-08-10T21:26:59.237Z" },
    { url = "https://files.pythonhosted.org/packages/2a/8f/8f6f491d595a9e5912971f3f863d81baddccc8a4d0c3749d6a0dd9ffc9df/kiwisolver-1.4.9-cp313-cp313t-win_arm64.whl", hash = "sha256:0749fd8f4218ad2e851e11cc4dc05c7cbc0cbc4267bdfdb31782e65aace4ee9c", size = 68646, upload-time = "2025-08-10T21:27:00.52Z" },
    { url = "https://files.pythonhosted.org/packages/6b/32/6cc0fbc9c54d06c2969faa9c1d29f5751a2e51809dd55c69055e62d9b426/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9928fe1eb816d11ae170885a74d074f57af3a0d65777ca47e9aeb854a1fba386", size = 123806, upload-time = "2025-08-10T21:27:01.537Z" },
    { url = "https://files.pythonhosted.org/packages/b2/dd/2bfb1d4a4823d92e8cbb420fe024b8d2167f72079b3bb941207c42570bdf/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d0005b053977e7b43388ddec89fa567f43d4f6d5c2c0affe57de5ebf290dc552", size = 66605, upload-time = "2025-08-10T21:27:03.335Z" },
    { url = "https://files.pythonhosted.org/packages/f7/69/00aafdb4e4509c2ca6064646cba9cd4b37933898f426756adb2cb92ebbed/kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2635d352d67458b66fd0667c14cb1d4145e9560d503219034a18a87e971ce4f3", size = 64925, upload-time = "2025-08-10T21:27:04.339Z" },
    { url = "https://files.pythonhosted.org/packages/43/dc/51acc6791aa14e5cb6d8a2e28cefb0dc2886d8862795449d021334c0df20/kiwisolver-1.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:767c23ad1c58c9e827b649a9ab7809fd5fd9db266a9cf02b0e926ddc2c680d58", size = 1472414, upload-time = "2025-08-10T21:27:05.437Z" },
    { url = "https://files.pythonhosted.org/packages/3d/bb/93fa64a81db304ac8a246f834d5094fae4b13baf53c839d6bb6e81177129/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72d0eb9fba308b8311685c2268cf7d0a0639a6cd027d8128659f72bdd8a024b4", size = 1281272, upload-time = "2025-08-10T21:27:07.063Z" },
    { url = "https://files.pythonhosted.org/packages/70/e6/6df102916960fb8d05069d4bd92d6d9a8202d5a3e2444494e7cd50f65b7a/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f68e4f3eeca8fb22cc3d731f9715a13b652795ef657a13df1ad0c7dc0e9731df", size = 1298578, upload-time = "2025-08-10T21:27:08.452Z" },
    { url = "https://files.pythonhosted.org/packages/7c/47/e142aaa612f5343736b087864dbaebc53ea8831453fb47e7521fa8658f30/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d84cd4061ae292d8ac367b2c3fa3aad11cb8625a95d135fe93f286f914f3f5a6", size = 1345607, upload-time = "2025-08-10T21:27:10.125Z" },
    { url = "https://files.pythonhosted.org/packages/54/89/d641a746194a0f4d1a3670fb900d0dbaa786fb98341056814bc3f058fa52/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a60ea74330b91bd22a29638940d115df9dc00af5035a9a2a6ad9399ffb4ceca5", size = 2230150, upload-time = "2025-08-10T21:27:11.484Z" },
    { url = "https://files.pythonhosted.org/packages/aa/6b/5ee1207198febdf16ac11f78c5ae40861b809cbe0e6d2a8d5b0b3044b199/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ce6a3a4e106cf35c2d9c4fa17c05ce0b180db622736845d4315519397a77beaf", size = 2325979, upload-time = "2025-08-10T21:27:12.917Z" },
    { url = "https://files.pythonhosted.org/packages/fc/ff/b269eefd90f4ae14dcc74973d5a0f6d28d3b9bb1afd8c0340513afe6b39a/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:77937e5e2a38a7b48eef0585114fe7930346993a88060d0bf886086d2aa49ef5", size = 2491456, upload-time = "2025-08-10T21:27:14.353Z" },
    { url = "https://files.pythonhosted.org/packages/fc/d4/10303190bd4d30de547534601e259a4fbf014eed94aae3e5521129215086/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:24c175051354f4a28c5d6a31c93906dc653e2bf234e8a4bbfb964892078898ce", size = 2294621, upload-time = "2025-08-10T21:27:15.808Z" },
    { url = "https://files.pythonhosted.org/packages/28/e0/a9a90416fce5c0be25742729c2ea52105d62eda6c4be4d803c2a7be1fa50/kiwisolver-1.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:0763515d4df10edf6d06a3c19734e2566368980d21ebec439f33f9eb936c07b7", size = 75417, upload-time = "2025-08-10T21:27:17.436Z" },
    { url = "https://files.pythonhosted.org/packages/1f/10/6949958215b7a9a264299a7db195564e87900f709db9245e4ebdd3c70779/kiwisolver-1.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:0e4e2bf29574a6a7b7f6cb5fa69293b9f96c928949ac4a53ba3f525dffb87f9c", size = 66582, upload-time = "2025-08-10T21:27:18.436Z" },
    { url = "https://files.pythonhosted.org/packages/ec/79/60e53067903d3bc5469b369fe0dfc6b3482e2133e85dae9daa9527535991/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d976bbb382b202f71c67f77b0ac11244021cfa3f7dfd9e562eefcea2df711548", size = 126514, upload-time = "2025-08-10T21:27:19.465Z" },
    { url = "https://files.pythonhosted.org/packages/25/d1/4843d3e8d46b072c12a38c97c57fab4608d36e13fe47d47ee96b4d61ba6f/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2489e4e5d7ef9a1c300a5e0196e43d9c739f066ef23270607d45aba368b91f2d", size = 67905, upload-time = "2025-08-10T21:27:20.51Z" },
    { url = "https://files.pythonhosted.org/packages/8c/ae/29ffcbd239aea8b93108de1278271ae764dfc0d803a5693914975f200596/kiwisolver-1.4.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e2ea9f7ab7fbf18fffb1b5434ce7c69a07582f7acc7717720f1d69f3e806f90c", size = 66399, upload-time = "2025-08-10T21:27:21.496Z" },
    { url = "https://files.pythonhosted.org/packages/a1/ae/d7ba902aa604152c2ceba5d352d7b62106bedbccc8e95c3934d94472bfa3/kiwisolver-1.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b34e51affded8faee0dfdb705416153819d8ea9250bbbf7ea1b249bdeb5f1122", size = 1582197, upload-time = "2025-08-10T21:27:22.604Z" },
    { url = "https://files.pythonhosted.org/packages/f2/41/27c70d427eddb8bc7e4f16420a20fefc6f480312122a59a959fdfe0445ad/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8aacd3d4b33b772542b2e01beb50187536967b514b00003bdda7589722d2a64", size = 1390125, upload-time = "2025-08-10T21:27:24.036Z" },
    { url = "https://files.pythonhosted.org/packages/41/42/b3799a12bafc76d962ad69083f8b43b12bf4fe78b097b12e105d75c9b8f1/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7cf974dd4e35fa315563ac99d6287a1024e4dc2077b8a7d7cd3d2fb65d283134", size = 1402612, upload-time = "2025-08-10T21:27:25.773Z" },
    { url = "https://files.pythonhosted.org/packages/d2/b5/a210ea073ea1cfaca1bb5c55a62307d8252f531beb364e18aa1e0888b5a0/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85bd218b5ecfbee8c8a82e121802dcb519a86044c9c3b2e4aef02fa05c6da370", size = 1453990, upload-time = "2025-08-10T21:27:27.089Z" },
    { url = "https://files.pythonhosted.org/packages/5f/ce/a829eb8c033e977d7ea03ed32fb3c1781b4fa0433fbadfff29e39c676f32/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0856e241c2d3df4efef7c04a1e46b1936b6120c9bcf36dd216e3acd84bc4fb21", size = 2331601, upload-time = "2025-08-10T21:27:29.343Z" },
    { url = "https://files.pythonhosted.org/packages/e0/4b/b5e97eb142eb9cd0072dacfcdcd31b1c66dc7352b0f7c7255d339c0edf00/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9af39d6551f97d31a4deebeac6f45b156f9755ddc59c07b402c148f5dbb6482a", size = 2422041, upload-time = "2025-08-10T21:27:30.754Z" },
    { url = "https://files.pythonhosted.org/packages/40/be/8eb4cd53e1b85ba4edc3a9321666f12b83113a178845593307a3e7891f44/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:bb4ae2b57fc1d8cbd1cf7b1d9913803681ffa903e7488012be5b76dedf49297f", size = 2594897, upload-time = "2025-08-10T21:27:32.803Z" },
    { url = "https://files.pythonhosted.org/packages/99/dd/841e9a66c4715477ea0abc78da039832fbb09dac5c35c58dc4c41a407b8a/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aedff62918805fb62d43a4aa2ecd4482c380dc76cd31bd7c8878588a61bd0369", size = 2391835, upload-time = "2025-08-10T21:27:34.23Z" },
    { url = "https://files.pythonhosted.org/packages/0c/28/4b2e5c47a0da96896fdfdb006340ade064afa1e63675d01ea5ac222b6d52/kiwisolver-1.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:1fa333e8b2ce4d9660f2cda9c0e1b6bafcfb2457a9d259faa82289e73ec24891", size = 79988, upload-time = "2025-08-10T21:27:35.587Z" },
    { url = "https://files.pythonhosted.org/packages/80/be/3578e8afd18c88cdf9cb4cffde75a96d2be38c5a903f1ed0ceec061bd09e/kiwisolver-1.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:4a48a2ce79d65d363597ef7b567ce3d14d68783d2b2263d98db3d9477805ba32", size = 70260, upload-time = "2025-08-10T21:27:36.606Z" },
]

[[package]]
name = "matplotlib"
version = "3.10.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
    { name = "contourpy" },
    { name = "cycler" },
    { name = "fonttools" },
    { name = "kiwisolver" },
    { name = "numpy" },
    { name = "packaging" },
    { name = "pillow" },
    { name = "pyparsing" },
    { name = "python-dateutil" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269, upload-time = "2025-12-10T22:56:51.155Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/3d/b9/15fd5541ef4f5b9a17eefd379356cf12175fe577424e7b1d80676516031a/matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3f2e409836d7f5ac2f1c013110a4d50b9f7edc26328c108915f9075d7d7a91b6", size = 8261076, upload-time = "2025-12-10T22:55:44.648Z" },
    { url = "https://files.pythonhosted.org/packages/8d/a0/2ba3473c1b66b9c74dc7107c67e9008cb1782edbe896d4c899d39ae9cf78/matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56271f3dac49a88d7fca5060f004d9d22b865f743a12a23b1e937a0be4818ee1", size = 8148794, upload-time = "2025-12-10T22:55:46.252Z" },
    { url = "https://files.pythonhosted.org/packages/75/97/a471f1c3eb1fd6f6c24a31a5858f443891d5127e63a7788678d14e249aea/matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0a7f52498f72f13d4a25ea70f35f4cb60642b466cbb0a9be951b5bc3f45a486", size = 8718474, upload-time = "2025-12-10T22:55:47.864Z" },
    { url = "https://files.pythonhosted.org/packages/01/be/cd478f4b66f48256f42927d0acbcd63a26a893136456cd079c0cc24fbabf/matplotlib-3.10.8-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:646d95230efb9ca614a7a594d4fcacde0ac61d25e37dd51710b36477594963ce", size = 9549637, upload-time = "2025-12-10T22:55:50.048Z" },
    { url = "https://files.pythonhosted.org/packages/5d/7c/8dc289776eae5109e268c4fb92baf870678dc048a25d4ac903683b86d5bf/matplotlib-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f89c151aab2e2e23cb3fe0acad1e8b82841fd265379c4cecd0f3fcb34c15e0f6", size = 9613678, upload-time = "2025-12-10T22:55:52.21Z" },
    { url = "https://files.pythonhosted.org/packages/64/40/37612487cc8a437d4dd261b32ca21fe2d79510fe74af74e1f42becb1bdb8/matplotlib-3.10.8-cp313-cp313-win_amd64.whl", hash = "sha256:e8ea3e2d4066083e264e75c829078f9e149fa119d27e19acd503de65e0b13149", size = 8142686, upload-time = "2025-12-10T22:55:54.253Z" },
    { url = "https://files.pythonhosted.org/packages/66/52/8d8a8730e968185514680c2a6625943f70269509c3dcfc0dcf7d75928cb8/matplotlib-3.10.8-cp313-cp313-win_arm64.whl", hash = "sha256:c108a1d6fa78a50646029cb6d49808ff0fc1330fda87fa6f6250c6b5369b6645", size = 8012917, upload-time = "2025-12-10T22:55:56.268Z" },
    { url = "https://files.pythonhosted.org/packages/b5/27/51fe26e1062f298af5ef66343d8ef460e090a27fea73036c76c35821df04/matplotlib-3.10.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ad3d9833a64cf48cc4300f2b406c3d0f4f4724a91c0bd5640678a6ba7c102077", size = 8305679, upload-time = "2025-12-10T22:55:57.856Z" },
    { url = "https://files.pythonhosted.org/packages/2c/1e/4de865bc591ac8e3062e835f42dd7fe7a93168d519557837f0e37513f629/matplotlib-3.10.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:eb3823f11823deade26ce3b9f40dcb4a213da7a670013929f31d5f5ed1055b22", size = 8198336, upload-time = "2025-12-10T22:55:59.371Z" },
    { url = "https://files.pythonhosted.org/packages/c6/cb/2f7b6e75fb4dce87ef91f60cac4f6e34f4c145ab036a22318ec837971300/matplotlib-3.10.8-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d9050fee89a89ed57b4fb2c1bfac9a3d0c57a0d55aed95949eedbc42070fea39", size = 8731653, upload-time = "2025-12-10T22:56:01.032Z" },
    { url = "https://files.pythonhosted.org/packages/46/b3/bd9c57d6ba670a37ab31fb87ec3e8691b947134b201f881665b28cc039ff/matplotlib-3.10.8-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b44d07310e404ba95f8c25aa5536f154c0a8ec473303535949e52eb71d0a1565", size = 9561356, upload-time = "2025-12-10T22:56:02.95Z" },
    { url = "https://files.pythonhosted.org/packages/c0/3d/8b94a481456dfc9dfe6e39e93b5ab376e50998cddfd23f4ae3b431708f16/matplotlib-3.10.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0a33deb84c15ede243aead39f77e990469fff93ad1521163305095b77b72ce4a", size = 9614000, upload-time = "2025-12-10T22:56:05.411Z" },
    { url = "https://files.pythonhosted.org/packages/bd/cd/bc06149fe5585ba800b189a6a654a75f1f127e8aab02fd2be10df7fa500c/matplotlib-3.10.8-cp313-cp313t-win_amd64.whl", hash = "sha256:3a48a78d2786784cc2413e57397981fb45c79e968d99656706018d6e62e57958", size = 8220043, upload-time = "2025-12-10T22:56:07.551Z" },
    { url = "https://files.pythonhosted.org/packages/e3/de/b22cf255abec916562cc04eef457c13e58a1990048de0c0c3604d082355e/matplotlib-3.10.8-cp313-cp313t-win_arm64.whl", hash = "sha256:15d30132718972c2c074cd14638c7f4592bd98719e2308bccea40e0538bc0cb5", size = 8062075, upload-time = "2025-12-10T22:56:09.178Z" },
    { url = "https://files.pythonhosted.org/packages/3c/43/9c0ff7a2f11615e516c3b058e1e6e8f9614ddeca53faca06da267c48345d/matplotlib-3.10.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b53285e65d4fa4c86399979e956235deb900be5baa7fc1218ea67fbfaeaadd6f", size = 8262481, upload-time = "2025-12-10T22:56:10.885Z" },
    { url = "https://files.pythonhosted.org/packages/6f/ca/e8ae28649fcdf039fda5ef554b40a95f50592a3c47e6f7270c9561c12b07/matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32f8dce744be5569bebe789e46727946041199030db8aeb2954d26013a0eb26b", size = 8151473, upload-time = "2025-12-10T22:56:12.377Z" },
    { url = "https://files.pythonhosted.org/packages/f1/6f/009d129ae70b75e88cbe7e503a12a4c0670e08ed748a902c2568909e9eb5/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf267add95b1c88300d96ca837833d4112756045364f5c734a2276038dae27d", size = 9553896, upload-time = "2025-12-10T22:56:14.432Z" },
    { url = "https://files.pythonhosted.org/packages/f5/26/4221a741eb97967bc1fd5e4c52b9aa5a91b2f4ec05b59f6def4d820f9df9/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2cf5bd12cecf46908f286d7838b2abc6c91cda506c0445b8223a7c19a00df008", size = 9824193, upload-time = "2025-12-10T22:56:16.29Z" },
    { url = "https://files.pythonhosted.org/packages/1f/f3/3abf75f38605772cf48a9daf5821cd4f563472f38b4b828c6fba6fa6d06e/matplotlib-3.10.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:41703cc95688f2516b480f7f339d8851a6035f18e100ee6a32bc0b8536a12a9c", size = 9615444, upload-time = "2025-12-10T22:56:18.155Z" },
    { url = "https://files.pythonhosted.org/packages/93/a5/de89ac80f10b8dc615807ee1133cd99ac74082581196d4d9590bea10690d/matplotlib-3.10.8-cp314-cp314-win_amd64.whl", hash = "sha256:83d282364ea9f3e52363da262ce32a09dfe241e4080dcedda3c0db059d3c1f11", size = 8272719, upload-time = "2025-12-10T22:56:20.366Z" },
    { url = "https://files.pythonhosted.org/packages/69/ce/b006495c19ccc0a137b48083168a37bd056392dee02f87dba0472f2797fe/matplotlib-3.10.8-cp314-cp314-win_arm64.whl", hash = "sha256:2c1998e92cd5999e295a731bcb2911c75f597d937341f3030cc24ef2733d78a8", size = 8144205, upload-time = "2025-12-10T22:56:22.239Z" },
    { url = "https://files.pythonhosted.org/packages/68/d9/b31116a3a855bd313c6fcdb7226926d59b041f26061c6c5b1be66a08c826/matplotlib-3.10.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b5a2b97dbdc7d4f353ebf343744f1d1f1cca8aa8bfddb4262fcf4306c3761d50", size = 8305785, upload-time = "2025-12-10T22:56:24.218Z" },
    { url = "https://files.pythonhosted.org/packages/1e/90/6effe8103f0272685767ba5f094f453784057072f49b393e3ea178fe70a5/matplotlib-3.10.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f5c3e4da343bba819f0234186b9004faba952cc420fbc522dc4e103c1985908", size = 8198361, upload-time = "2025-12-10T22:56:26.787Z" },
    { url = "https://files.pythonhosted.org/packages/d7/65/a73188711bea603615fc0baecca1061429ac16940e2385433cc778a9d8e7/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f62550b9a30afde8c1c3ae450e5eb547d579dd69b25c2fc7a1c67f934c1717a", size = 9561357, upload-time = "2025-12-10T22:56:28.953Z" },
    { url = "https://files.pythonhosted.org/packages/f4/3d/b5c5d5d5be8ce63292567f0e2c43dde9953d3ed86ac2de0a72e93c8f07a1/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:495672de149445ec1b772ff2c9ede9b769e3cb4f0d0aa7fa730d7f59e2d4e1c1", size = 9823610, upload-time = "2025-12-10T22:56:31.455Z" },
    { url = "https://files.pythonhosted.org/packages/4d/4b/e7beb6bbd49f6bae727a12b270a2654d13c397576d25bd6786e47033300f/matplotlib-3.10.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:595ba4d8fe983b88f0eec8c26a241e16d6376fe1979086232f481f8f3f67494c", size = 9614011, upload-time = "2025-12-10T22:56:33.85Z" },
    { url = "https://files.pythonhosted.org/packages/7c/e6/76f2813d31f032e65f6f797e3f2f6e4aab95b65015924b1c51370395c28a/matplotlib-3.10.8-cp314-cp314t-win_amd64.whl", hash = "sha256:25d380fe8b1dc32cf8f0b1b448470a77afb195438bafdf1d858bfb876f3edf7b", size = 8362801, upload-time = "2025-12-10T22:56:36.107Z" },
    { url = "https://files.pythonhosted.org/packages/5d/49/d651878698a0b67f23aa28e17f45a6d6dd3d3f933fa29087fa4ce5947b5a/matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f", size = 8192560, upload-time = "2025-12-10T22:56:38.008Z" },
]

[[package]]
name = "mpl-plot-report"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
    { name = "matplotlib" },
    { name = "numpy" },
]

[package.dev-dependencies]
dev = [
    { name = "black" },
    { name = "produm" },
    { name = "pytest" },
]

[package.metadata]
requires-dist = [
    { name = "matplotlib", specifier = ">=3.8.0" },
    { name = "numpy", specifier = ">=2.0.0" },
]

[package.metadata.requires-dev]
dev = [
    { name = "black", specifier = ">=26.1.0" },
    { name = "produm", specifier = ">=0.1.2" },
    { name = "pytest", specifier = ">=9.0.2" },
]

[[package]]
name = "mypy-extensions"
version = "1.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
]

[[package]]
name = "numpy"
version = "2.4.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/57/fd/0005efbd0af48e55eb3c7208af93f2862d4b1a56cd78e84309a2d959208d/numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae", size = 20723651, upload-time = "2026-01-31T23:13:10.135Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/a1/22/815b9fe25d1d7ae7d492152adbc7226d3eff731dffc38fe970589fcaaa38/numpy-2.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25f2059807faea4b077a2b6837391b5d830864b3543627f381821c646f31a63c", size = 16663696, upload-time = "2026-01-31T23:11:17.516Z" },
    { url = "https://files.pythonhosted.org/packages/09/f0/817d03a03f93ba9c6c8993de509277d84e69f9453601915e4a69554102a1/numpy-2.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bd3a7a9f5847d2fb8c2c6d1c862fa109c31a9abeca1a3c2bd5a64572955b2979", size = 14688322, upload-time = "2026-01-31T23:11:19.883Z" },
    { url = "https://files.pythonhosted.org/packages/da/b4/f805ab79293c728b9a99438775ce51885fd4f31b76178767cfc718701a39/numpy-2.4.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8e4549f8a3c6d13d55041925e912bfd834285ef1dd64d6bc7d542583355e2e98", size = 5198157, upload-time = "2026-01-31T23:11:22.375Z" },
    { url = "https://files.pythonhosted.org/packages/74/09/826e4289844eccdcd64aac27d13b0fd3f32039915dd5b9ba01baae1f436c/numpy-2.4.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:aea4f66ff44dfddf8c2cffd66ba6538c5ec67d389285292fe428cb2c738c8aef", size = 6546330, upload-time = "2026-01-31T23:11:23.958Z" },
    { url = "https://files.pythonhosted.org/packages/19/fb/cbfdbfa3057a10aea5422c558ac57538e6acc87ec1669e666d32ac198da7/numpy-2.4.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3cd545784805de05aafe1dde61752ea49a359ccba9760c1e5d1c88a93bbf2b7", size = 15660968, upload-time = "2026-01-31T23:11:25.713Z" },
    { url = "https://files.pythonhosted.org/packages/04/dc/46066ce18d01645541f0186877377b9371b8fa8017fa8262002b4ef22612/numpy-2.4.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0d9b7c93578baafcbc5f0b83eaf17b79d345c6f36917ba0c67f45226911d499", size = 16607311, upload-time = "2026-01-31T23:11:28.117Z" },
    { url = "https://files.pythonhosted.org/packages/14/d9/4b5adfc39a43fa6bf918c6d544bc60c05236cc2f6339847fc5b35e6cb5b0/numpy-2.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f74f0f7779cc7ae07d1810aab8ac6b1464c3eafb9e283a40da7309d5e6e48fbb", size = 17012850, upload-time = "2026-01-31T23:11:30.888Z" },
    { url = "https://files.pythonhosted.org/packages/b7/20/adb6e6adde6d0130046e6fdfb7675cc62bc2f6b7b02239a09eb58435753d/numpy-2.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7ac672d699bf36275c035e16b65539931347d68b70667d28984c9fb34e07fa7", size = 18334210, upload-time = "2026-01-31T23:11:33.214Z" },
    { url = "https://files.pythonhosted.org/packages/78/0e/0a73b3dff26803a8c02baa76398015ea2a5434d9b8265a7898a6028c1591/numpy-2.4.2-cp313-cp313-win32.whl", hash = "sha256:8e9afaeb0beff068b4d9cd20d322ba0ee1cecfb0b08db145e4ab4dd44a6b5110", size = 5958199, upload-time = "2026-01-31T23:11:35.385Z" },
    { url = "https://files.pythonhosted.org/packages/43/bc/6352f343522fcb2c04dbaf94cb30cca6fd32c1a750c06ad6231b4293708c/numpy-2.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:7df2de1e4fba69a51c06c28f5a3de36731eb9639feb8e1cf7e4a7b0daf4cf622", size = 12310848, upload-time = "2026-01-31T23:11:38.001Z" },
    { url = "https://files.pythonhosted.org/packages/6e/8d/6da186483e308da5da1cc6918ce913dcfe14ffde98e710bfeff2a6158d4e/numpy-2.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:0fece1d1f0a89c16b03442eae5c56dc0be0c7883b5d388e0c03f53019a4bfd71", size = 10221082, upload-time = "2026-01-31T23:11:40.392Z" },
    { url = "https://files.pythonhosted.org/packages/25/a1/9510aa43555b44781968935c7548a8926274f815de42ad3997e9e83680dd/numpy-2.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5633c0da313330fd20c484c78cdd3f9b175b55e1a766c4a174230c6b70ad8262", size = 14815866, upload-time = "2026-01-31T23:11:42.495Z" },
    { url = "https://files.pythonhosted.org/packages/36/30/6bbb5e76631a5ae46e7923dd16ca9d3f1c93cfa8d4ed79a129814a9d8db3/numpy-2.4.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d9f64d786b3b1dd742c946c42d15b07497ed14af1a1f3ce840cce27daa0ce913", size = 5325631, upload-time = "2026-01-31T23:11:44.7Z" },
    { url = "https://files.pythonhosted.org/packages/46/00/3a490938800c1923b567b3a15cd17896e68052e2145d8662aaf3e1ffc58f/numpy-2.4.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:b21041e8cb6a1eb5312dd1d2f80a94d91efffb7a06b70597d44f1bd2dfc315ab", size = 6646254, upload-time = "2026-01-31T23:11:46.341Z" },
    { url = "https://files.pythonhosted.org/packages/d3/e9/fac0890149898a9b609caa5af7455a948b544746e4b8fe7c212c8edd71f8/numpy-2.4.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00ab83c56211a1d7c07c25e3217ea6695e50a3e2f255053686b081dc0b091a82", size = 15720138, upload-time = "2026-01-31T23:11:48.082Z" },
    { url = "https://files.pythonhosted.org/packages/ea/5c/08887c54e68e1e28df53709f1893ce92932cc6f01f7c3d4dc952f61ffd4e/numpy-2.4.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fb882da679409066b4603579619341c6d6898fc83a8995199d5249f986e8e8f", size = 16655398, upload-time = "2026-01-31T23:11:50.293Z" },
    { url = "https://files.pythonhosted.org/packages/4d/89/253db0fa0e66e9129c745e4ef25631dc37d5f1314dad2b53e907b8538e6d/numpy-2.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66cb9422236317f9d44b67b4d18f44efe6e9c7f8794ac0462978513359461554", size = 17079064, upload-time = "2026-01-31T23:11:52.927Z" },
    { url = "https://files.pythonhosted.org/packages/2a/d5/cbade46ce97c59c6c3da525e8d95b7abe8a42974a1dc5c1d489c10433e88/numpy-2.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0f01dcf33e73d80bd8dc0f20a71303abbafa26a19e23f6b68d1aa9990af90257", size = 18379680, upload-time = "2026-01-31T23:11:55.22Z" },
    { url = "https://files.pythonhosted.org/packages/40/62/48f99ae172a4b63d981babe683685030e8a3df4f246c893ea5c6ef99f018/numpy-2.4.2-cp313-cp313t-win32.whl", hash = "sha256:52b913ec40ff7ae845687b0b34d8d93b60cb66dcee06996dd5c99f2fc9328657", size = 6082433, upload-time = "2026-01-31T23:11:58.096Z" },
    { url = "https://files.pythonhosted.org/packages/07/38/e054a61cfe48ad9f1ed0d188e78b7e26859d0b60ef21cd9de4897cdb5326/numpy-2.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:5eea80d908b2c1f91486eb95b3fb6fab187e569ec9752ab7d9333d2e66bf2d6b", size = 12451181, upload-time = "2026-01-31T23:11:59.782Z" },
    { url = "https://files.pythonhosted.org/packages/6e/a4/a05c3a6418575e185dd84d0b9680b6bb2e2dc3e4202f036b7b4e22d6e9dc/numpy-2.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fd49860271d52127d61197bb50b64f58454e9f578cb4b2c001a6de8b1f50b0b1", size = 10290756, upload-time = "2026-01-31T23:12:02.438Z" },
    { url = "https://files.pythonhosted.org/packages/18/88/b7df6050bf18fdcfb7046286c6535cabbdd2064a3440fca3f069d319c16e/numpy-2.4.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:444be170853f1f9d528428eceb55f12918e4fda5d8805480f36a002f1415e09b", size = 16663092, upload-time = "2026-01-31T23:12:04.521Z" },
    { url = "https://files.pythonhosted.org/packages/25/7a/1fee4329abc705a469a4afe6e69b1ef7e915117747886327104a8493a955/numpy-2.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d1240d50adff70c2a88217698ca844723068533f3f5c5fa6ee2e3220e3bdb000", size = 14698770, upload-time = "2026-01-31T23:12:06.96Z" },
    { url = "https://files.pythonhosted.org/packages/fb/0b/f9e49ba6c923678ad5bc38181c08ac5e53b7a5754dbca8e581aa1a56b1ff/numpy-2.4.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:7cdde6de52fb6664b00b056341265441192d1291c130e99183ec0d4b110ff8b1", size = 5208562, upload-time = "2026-01-31T23:12:09.632Z" },
    { url = "https://files.pythonhosted.org/packages/7d/12/d7de8f6f53f9bb76997e5e4c069eda2051e3fe134e9181671c4391677bb2/numpy-2.4.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:cda077c2e5b780200b6b3e09d0b42205a3d1c68f30c6dceb90401c13bff8fe74", size = 6543710, upload-time = "2026-01-31T23:12:11.969Z" },
    { url = "https://files.pythonhosted.org/packages/09/63/c66418c2e0268a31a4cf8a8b512685748200f8e8e8ec6c507ce14e773529/numpy-2.4.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d30291931c915b2ab5717c2974bb95ee891a1cf22ebc16a8006bd59cd210d40a", size = 15677205, upload-time = "2026-01-31T23:12:14.33Z" },
    { url = "https://files.pythonhosted.org/packages/5d/6c/7f237821c9642fb2a04d2f1e88b4295677144ca93285fd76eff3bcba858d/numpy-2.4.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bba37bc29d4d85761deed3954a1bc62be7cf462b9510b51d367b769a8c8df325", size = 16611738, upload-time = "2026-01-31T23:12:16.525Z" },
    { url = "https://files.pythonhosted.org/packages/c2/a7/39c4cdda9f019b609b5c473899d87abff092fc908cfe4d1ecb2fcff453b0/numpy-2.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b2f0073ed0868db1dcd86e052d37279eef185b9c8db5bf61f30f46adac63c909", size = 17028888, upload-time = "2026-01-31T23:12:19.306Z" },
    { url = "https://files.pythonhosted.org/packages/da/b3/e84bb64bdfea967cc10950d71090ec2d84b49bc691df0025dddb7c26e8e3/numpy-2.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7f54844851cdb630ceb623dcec4db3240d1ac13d4990532446761baede94996a", size = 18339556, upload-time = "2026-01-31T23:12:21.816Z" },
    { url = "https://files.pythonhosted.org/packages/88/f5/954a291bc1192a27081706862ac62bb5920fbecfbaa302f64682aa90beed/numpy-2.4.2-cp314-cp314-win32.whl", hash = "sha256:12e26134a0331d8dbd9351620f037ec470b7c75929cb8a1537f6bfe411152a1a", size = 6006899, upload-time = "2026-01-31T23:12:24.14Z" },
    { url = "https://files.pythonhosted.org/packages/05/cb/eff72a91b2efdd1bc98b3b8759f6a1654aa87612fc86e3d87d6fe4f948c4/numpy-2.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:068cdb2d0d644cdb45670810894f6a0600797a69c05f1ac478e8d31670b8ee75", size = 12443072, upload-time = "2026-01-31T23:12:26.33Z" },
    { url = "https://files.pythonhosted.org/packages/37/75/62726948db36a56428fce4ba80a115716dc4fad6a3a4352487f8bb950966/numpy-2.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:6ed0be1ee58eef41231a5c943d7d1375f093142702d5723ca2eb07db9b934b05", size = 10494886, upload-time = "2026-01-31T23:12:28.488Z" },
    { url = "https://files.pythonhosted.org/packages/36/2f/ee93744f1e0661dc267e4b21940870cabfae187c092e1433b77b09b50ac4/numpy-2.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:98f16a80e917003a12c0580f97b5f875853ebc33e2eaa4bccfc8201ac6869308", size = 14818567, upload-time = "2026-01-31T23:12:30.709Z" },
    { url = "https://files.pythonhosted.org/packages/a7/24/6535212add7d76ff938d8bdc654f53f88d35cddedf807a599e180dcb8e66/numpy-2.4.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:20abd069b9cda45874498b245c8015b18ace6de8546bf50dfa8cea1696ed06ef", size = 5328372, upload-time = "2026-01-31T23:12:32.962Z" },
    { url = "https://files.pythonhosted.org/packages/5e/9d/c48f0a035725f925634bf6b8994253b43f2047f6778a54147d7e213bc5a7/numpy-2.4.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:e98c97502435b53741540a5717a6749ac2ada901056c7db951d33e11c885cc7d", size = 6649306, upload-time = "2026-01-31T23:12:34.797Z" },
    { url = "https://files.pythonhosted.org/packages/81/05/7c73a9574cd4a53a25907bad38b59ac83919c0ddc8234ec157f344d57d9a/numpy-2.4.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da6cad4e82cb893db4b69105c604d805e0c3ce11501a55b5e9f9083b47d2ffe8", size = 15722394, upload-time = "2026-01-31T23:12:36.565Z" },
    { url = "https://files.pythonhosted.org/packages/35/fa/4de10089f21fc7d18442c4a767ab156b25c2a6eaf187c0db6d9ecdaeb43f/numpy-2.4.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4424677ce4b47fe73c8b5556d876571f7c6945d264201180db2dc34f676ab5", size = 16653343, upload-time = "2026-01-31T23:12:39.188Z" },
    { url = "https://files.pythonhosted.org/packages/b8/f9/d33e4ffc857f3763a57aa85650f2e82486832d7492280ac21ba9efda80da/numpy-2.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2b8f157c8a6f20eb657e240f8985cc135598b2b46985c5bccbde7616dc9c6b1e", size = 17078045, upload-time = "2026-01-31T23:12:42.041Z" },
    { url = "https://files.pythonhosted.org/packages/c8/b8/54bdb43b6225badbea6389fa038c4ef868c44f5890f95dd530a218706da3/numpy-2.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5daf6f3914a733336dab21a05cdec343144600e964d2fcdabaac0c0269874b2a", size = 18380024, upload-time = "2026-01-31T23:12:44.331Z" },
    { url = "https://files.pythonhosted.org/packages/a5/55/6e1a61ded7af8df04016d81b5b02daa59f2ea9252ee0397cb9f631efe9e5/numpy-2.4.2-cp314-cp314t-win32.whl", hash = "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443", size = 6153937, upload-time = "2026-01-31T23:12:47.229Z" },
    { url = "https://files.pythonhosted.org/packages/45/aa/fa6118d1ed6d776b0983f3ceac9b1a5558e80df9365b1c3aa6d42bf9eee4/numpy-2.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236", size = 12631844, upload-time = "2026-01-31T23:12:48.997Z" },
    { url = "https://files.pythonhosted.org/packages/32/0a/2ec5deea6dcd158f254a7b372fb09cfba5719419c8d66343bab35237b3fb/numpy-2.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181", size = 10565379, upload-time = "2026-01-31T23:12:51.345Z" },
]

[[package]]
name = "packaging"
version = "26.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" }
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 = "pathspec"
version = "1.0.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" },
]

[[package]]
name = "pillow"
version = "12.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d0/02/d52c733a2452ef1ffcc123b68e6606d07276b0e358db70eabad7e40042b7/pillow-12.1.0.tar.gz", hash = "sha256:5c5ae0a06e9ea030ab786b0251b32c7e4ce10e58d983c0d5c56029455180b5b9", size = 46977283, upload-time = "2026-01-02T09:13:29.892Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/dd/c7/2530a4aa28248623e9d7f27316b42e27c32ec410f695929696f2e0e4a778/pillow-12.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7b5dd7cbae20285cdb597b10eb5a2c13aa9de6cde9bb64a3c1317427b1db1ae1", size = 4062543, upload-time = "2026-01-02T09:11:31.566Z" },
    { url = "https://files.pythonhosted.org/packages/8f/1f/40b8eae823dc1519b87d53c30ed9ef085506b05281d313031755c1705f73/pillow-12.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:29a4cef9cb672363926f0470afc516dbf7305a14d8c54f7abbb5c199cd8f8179", size = 4138373, upload-time = "2026-01-02T09:11:33.367Z" },
    { url = "https://files.pythonhosted.org/packages/d4/77/6fa60634cf06e52139fd0e89e5bbf055e8166c691c42fb162818b7fda31d/pillow-12.1.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:681088909d7e8fa9e31b9799aaa59ba5234c58e5e4f1951b4c4d1082a2e980e0", size = 3601241, upload-time = "2026-01-02T09:11:35.011Z" },
    { url = "https://files.pythonhosted.org/packages/4f/bf/28ab865de622e14b747f0cd7877510848252d950e43002e224fb1c9ababf/pillow-12.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:983976c2ab753166dc66d36af6e8ec15bb511e4a25856e2227e5f7e00a160587", size = 5262410, upload-time = "2026-01-02T09:11:36.682Z" },
    { url = "https://files.pythonhosted.org/packages/1c/34/583420a1b55e715937a85bd48c5c0991598247a1fd2eb5423188e765ea02/pillow-12.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:db44d5c160a90df2d24a24760bbd37607d53da0b34fb546c4c232af7192298ac", size = 4657312, upload-time = "2026-01-02T09:11:38.535Z" },
    { url = "https://files.pythonhosted.org/packages/1d/fd/f5a0896839762885b3376ff04878f86ab2b097c2f9a9cdccf4eda8ba8dc0/pillow-12.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6b7a9d1db5dad90e2991645874f708e87d9a3c370c243c2d7684d28f7e133e6b", size = 6232605, upload-time = "2026-01-02T09:11:40.602Z" },
    { url = "https://files.pythonhosted.org/packages/98/aa/938a09d127ac1e70e6ed467bd03834350b33ef646b31edb7452d5de43792/pillow-12.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6258f3260986990ba2fa8a874f8b6e808cf5abb51a94015ca3dc3c68aa4f30ea", size = 8041617, upload-time = "2026-01-02T09:11:42.721Z" },
    { url = "https://files.pythonhosted.org/packages/17/e8/538b24cb426ac0186e03f80f78bc8dc7246c667f58b540bdd57c71c9f79d/pillow-12.1.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e115c15e3bc727b1ca3e641a909f77f8ca72a64fff150f666fcc85e57701c26c", size = 6346509, upload-time = "2026-01-02T09:11:44.955Z" },
    { url = "https://files.pythonhosted.org/packages/01/9a/632e58ec89a32738cabfd9ec418f0e9898a2b4719afc581f07c04a05e3c9/pillow-12.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6741e6f3074a35e47c77b23a4e4f2d90db3ed905cb1c5e6e0d49bff2045632bc", size = 7038117, upload-time = "2026-01-02T09:11:46.736Z" },
    { url = "https://files.pythonhosted.org/packages/c7/a2/d40308cf86eada842ca1f3ffa45d0ca0df7e4ab33c83f81e73f5eaed136d/pillow-12.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:935b9d1aed48fcfb3f838caac506f38e29621b44ccc4f8a64d575cb1b2a88644", size = 6460151, upload-time = "2026-01-02T09:11:48.625Z" },
    { url = "https://files.pythonhosted.org/packages/f1/88/f5b058ad6453a085c5266660a1417bdad590199da1b32fb4efcff9d33b05/pillow-12.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5fee4c04aad8932da9f8f710af2c1a15a83582cfb884152a9caa79d4efcdbf9c", size = 7164534, upload-time = "2026-01-02T09:11:50.445Z" },
    { url = "https://files.pythonhosted.org/packages/19/ce/c17334caea1db789163b5d855a5735e47995b0b5dc8745e9a3605d5f24c0/pillow-12.1.0-cp313-cp313-win32.whl", hash = "sha256:a786bf667724d84aa29b5db1c61b7bfdde380202aaca12c3461afd6b71743171", size = 6332551, upload-time = "2026-01-02T09:11:52.234Z" },
    { url = "https://files.pythonhosted.org/packages/e5/07/74a9d941fa45c90a0d9465098fe1ec85de3e2afbdc15cc4766622d516056/pillow-12.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:461f9dfdafa394c59cd6d818bdfdbab4028b83b02caadaff0ffd433faf4c9a7a", size = 7040087, upload-time = "2026-01-02T09:11:54.822Z" },
    { url = "https://files.pythonhosted.org/packages/88/09/c99950c075a0e9053d8e880595926302575bc742b1b47fe1bbcc8d388d50/pillow-12.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:9212d6b86917a2300669511ed094a9406888362e085f2431a7da985a6b124f45", size = 2452470, upload-time = "2026-01-02T09:11:56.522Z" },
    { url = "https://files.pythonhosted.org/packages/b5/ba/970b7d85ba01f348dee4d65412476321d40ee04dcb51cd3735b9dc94eb58/pillow-12.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:00162e9ca6d22b7c3ee8e61faa3c3253cd19b6a37f126cad04f2f88b306f557d", size = 5264816, upload-time = "2026-01-02T09:11:58.227Z" },
    { url = "https://files.pythonhosted.org/packages/10/60/650f2fb55fdba7a510d836202aa52f0baac633e50ab1cf18415d332188fb/pillow-12.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7d6daa89a00b58c37cb1747ec9fb7ac3bc5ffd5949f5888657dfddde6d1312e0", size = 4660472, upload-time = "2026-01-02T09:12:00.798Z" },
    { url = "https://files.pythonhosted.org/packages/2b/c0/5273a99478956a099d533c4f46cbaa19fd69d606624f4334b85e50987a08/pillow-12.1.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e2479c7f02f9d505682dc47df8c0ea1fc5e264c4d1629a5d63fe3e2334b89554", size = 6268974, upload-time = "2026-01-02T09:12:02.572Z" },
    { url = "https://files.pythonhosted.org/packages/b4/26/0bf714bc2e73d5267887d47931d53c4ceeceea6978148ed2ab2a4e6463c4/pillow-12.1.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f188d580bd870cda1e15183790d1cc2fa78f666e76077d103edf048eed9c356e", size = 8073070, upload-time = "2026-01-02T09:12:04.75Z" },
    { url = "https://files.pythonhosted.org/packages/43/cf/1ea826200de111a9d65724c54f927f3111dc5ae297f294b370a670c17786/pillow-12.1.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0fde7ec5538ab5095cc02df38ee99b0443ff0e1c847a045554cf5f9af1f4aa82", size = 6380176, upload-time = "2026-01-02T09:12:06.626Z" },
    { url = "https://files.pythonhosted.org/packages/03/e0/7938dd2b2013373fd85d96e0f38d62b7a5a262af21ac274250c7ca7847c9/pillow-12.1.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ed07dca4a8464bada6139ab38f5382f83e5f111698caf3191cb8dbf27d908b4", size = 7067061, upload-time = "2026-01-02T09:12:08.624Z" },
    { url = "https://files.pythonhosted.org/packages/86/ad/a2aa97d37272a929a98437a8c0ac37b3cf012f4f8721e1bd5154699b2518/pillow-12.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f45bd71d1fa5e5749587613037b172e0b3b23159d1c00ef2fc920da6f470e6f0", size = 6491824, upload-time = "2026-01-02T09:12:10.488Z" },
    { url = "https://files.pythonhosted.org/packages/a4/44/80e46611b288d51b115826f136fb3465653c28f491068a72d3da49b54cd4/pillow-12.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:277518bf4fe74aa91489e1b20577473b19ee70fb97c374aa50830b279f25841b", size = 7190911, upload-time = "2026-01-02T09:12:12.772Z" },
    { url = "https://files.pythonhosted.org/packages/86/77/eacc62356b4cf81abe99ff9dbc7402750044aed02cfd6a503f7c6fc11f3e/pillow-12.1.0-cp313-cp313t-win32.whl", hash = "sha256:7315f9137087c4e0ee73a761b163fc9aa3b19f5f606a7fc08d83fd3e4379af65", size = 6336445, upload-time = "2026-01-02T09:12:14.775Z" },
    { url = "https://files.pythonhosted.org/packages/e7/3c/57d81d0b74d218706dafccb87a87ea44262c43eef98eb3b164fd000e0491/pillow-12.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:0ddedfaa8b5f0b4ffbc2fa87b556dc59f6bb4ecb14a53b33f9189713ae8053c0", size = 7045354, upload-time = "2026-01-02T09:12:16.599Z" },
    { url = "https://files.pythonhosted.org/packages/ac/82/8b9b97bba2e3576a340f93b044a3a3a09841170ab4c1eb0d5c93469fd32f/pillow-12.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:80941e6d573197a0c28f394753de529bb436b1ca990ed6e765cf42426abc39f8", size = 2454547, upload-time = "2026-01-02T09:12:18.704Z" },
    { url = "https://files.pythonhosted.org/packages/8c/87/bdf971d8bbcf80a348cc3bacfcb239f5882100fe80534b0ce67a784181d8/pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:5cb7bc1966d031aec37ddb9dcf15c2da5b2e9f7cc3ca7c54473a20a927e1eb91", size = 4062533, upload-time = "2026-01-02T09:12:20.791Z" },
    { url = "https://files.pythonhosted.org/packages/ff/4f/5eb37a681c68d605eb7034c004875c81f86ec9ef51f5be4a63eadd58859a/pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:97e9993d5ed946aba26baf9c1e8cf18adbab584b99f452ee72f7ee8acb882796", size = 4138546, upload-time = "2026-01-02T09:12:23.664Z" },
    { url = "https://files.pythonhosted.org/packages/11/6d/19a95acb2edbace40dcd582d077b991646b7083c41b98da4ed7555b59733/pillow-12.1.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:414b9a78e14ffeb98128863314e62c3f24b8a86081066625700b7985b3f529bd", size = 3601163, upload-time = "2026-01-02T09:12:26.338Z" },
    { url = "https://files.pythonhosted.org/packages/fc/36/2b8138e51cb42e4cc39c3297713455548be855a50558c3ac2beebdc251dd/pillow-12.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e6bdb408f7c9dd2a5ff2b14a3b0bb6d4deb29fb9961e6eb3ae2031ae9a5cec13", size = 5266086, upload-time = "2026-01-02T09:12:28.782Z" },
    { url = "https://files.pythonhosted.org/packages/53/4b/649056e4d22e1caa90816bf99cef0884aed607ed38075bd75f091a607a38/pillow-12.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3413c2ae377550f5487991d444428f1a8ae92784aac79caa8b1e3b89b175f77e", size = 4657344, upload-time = "2026-01-02T09:12:31.117Z" },
    { url = "https://files.pythonhosted.org/packages/6c/6b/c5742cea0f1ade0cd61485dc3d81f05261fc2276f537fbdc00802de56779/pillow-12.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e5dcbe95016e88437ecf33544ba5db21ef1b8dd6e1b434a2cb2a3d605299e643", size = 6232114, upload-time = "2026-01-02T09:12:32.936Z" },
    { url = "https://files.pythonhosted.org/packages/bf/8f/9f521268ce22d63991601aafd3d48d5ff7280a246a1ef62d626d67b44064/pillow-12.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d0a7735df32ccbcc98b98a1ac785cc4b19b580be1bdf0aeb5c03223220ea09d5", size = 8042708, upload-time = "2026-01-02T09:12:34.78Z" },
    { url = "https://files.pythonhosted.org/packages/1a/eb/257f38542893f021502a1bbe0c2e883c90b5cff26cc33b1584a841a06d30/pillow-12.1.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c27407a2d1b96774cbc4a7594129cc027339fd800cd081e44497722ea1179de", size = 6347762, upload-time = "2026-01-02T09:12:36.748Z" },
    { url = "https://files.pythonhosted.org/packages/c4/5a/8ba375025701c09b309e8d5163c5a4ce0102fa86bbf8800eb0d7ac87bc51/pillow-12.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15c794d74303828eaa957ff8070846d0efe8c630901a1c753fdc63850e19ecd9", size = 7039265, upload-time = "2026-01-02T09:12:39.082Z" },
    { url = "https://files.pythonhosted.org/packages/cf/dc/cf5e4cdb3db533f539e88a7bbf9f190c64ab8a08a9bc7a4ccf55067872e4/pillow-12.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c990547452ee2800d8506c4150280757f88532f3de2a58e3022e9b179107862a", size = 6462341, upload-time = "2026-01-02T09:12:40.946Z" },
    { url = "https://files.pythonhosted.org/packages/d0/47/0291a25ac9550677e22eda48510cfc4fa4b2ef0396448b7fbdc0a6946309/pillow-12.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b63e13dd27da389ed9475b3d28510f0f954bca0041e8e551b2a4eb1eab56a39a", size = 7165395, upload-time = "2026-01-02T09:12:42.706Z" },
    { url = "https://files.pythonhosted.org/packages/4f/4c/e005a59393ec4d9416be06e6b45820403bb946a778e39ecec62f5b2b991e/pillow-12.1.0-cp314-cp314-win32.whl", hash = "sha256:1a949604f73eb07a8adab38c4fe50791f9919344398bdc8ac6b307f755fc7030", size = 6431413, upload-time = "2026-01-02T09:12:44.944Z" },
    { url = "https://files.pythonhosted.org/packages/1c/af/f23697f587ac5f9095d67e31b81c95c0249cd461a9798a061ed6709b09b5/pillow-12.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:4f9f6a650743f0ddee5593ac9e954ba1bdbc5e150bc066586d4f26127853ab94", size = 7176779, upload-time = "2026-01-02T09:12:46.727Z" },
    { url = "https://files.pythonhosted.org/packages/b3/36/6a51abf8599232f3e9afbd16d52829376a68909fe14efe29084445db4b73/pillow-12.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:808b99604f7873c800c4840f55ff389936ef1948e4e87645eaf3fccbc8477ac4", size = 2543105, upload-time = "2026-01-02T09:12:49.243Z" },
    { url = "https://files.pythonhosted.org/packages/82/54/2e1dd20c8749ff225080d6ba465a0cab4387f5db0d1c5fb1439e2d99923f/pillow-12.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc11908616c8a283cf7d664f77411a5ed2a02009b0097ff8abbba5e79128ccf2", size = 5268571, upload-time = "2026-01-02T09:12:51.11Z" },
    { url = "https://files.pythonhosted.org/packages/57/61/571163a5ef86ec0cf30d265ac2a70ae6fc9e28413d1dc94fa37fae6bda89/pillow-12.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:896866d2d436563fa2a43a9d72f417874f16b5545955c54a64941e87c1376c61", size = 4660426, upload-time = "2026-01-02T09:12:52.865Z" },
    { url = "https://files.pythonhosted.org/packages/5e/e1/53ee5163f794aef1bf84243f755ee6897a92c708505350dd1923f4afec48/pillow-12.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8e178e3e99d3c0ea8fc64b88447f7cac8ccf058af422a6cedc690d0eadd98c51", size = 6269908, upload-time = "2026-01-02T09:12:54.884Z" },
    { url = "https://files.pythonhosted.org/packages/bc/0b/b4b4106ff0ee1afa1dc599fde6ab230417f800279745124f6c50bcffed8e/pillow-12.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:079af2fb0c599c2ec144ba2c02766d1b55498e373b3ac64687e43849fbbef5bc", size = 8074733, upload-time = "2026-01-02T09:12:56.802Z" },
    { url = "https://files.pythonhosted.org/packages/19/9f/80b411cbac4a732439e629a26ad3ef11907a8c7fc5377b7602f04f6fe4e7/pillow-12.1.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdec5e43377761c5dbca620efb69a77f6855c5a379e32ac5b158f54c84212b14", size = 6381431, upload-time = "2026-01-02T09:12:58.823Z" },
    { url = "https://files.pythonhosted.org/packages/8f/b7/d65c45db463b66ecb6abc17c6ba6917a911202a07662247e1355ce1789e7/pillow-12.1.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:565c986f4b45c020f5421a4cea13ef294dde9509a8577f29b2fc5edc7587fff8", size = 7068529, upload-time = "2026-01-02T09:13:00.885Z" },
    { url = "https://files.pythonhosted.org/packages/50/96/dfd4cd726b4a45ae6e3c669fc9e49deb2241312605d33aba50499e9d9bd1/pillow-12.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:43aca0a55ce1eefc0aefa6253661cb54571857b1a7b2964bd8a1e3ef4b729924", size = 6492981, upload-time = "2026-01-02T09:13:03.314Z" },
    { url = "https://files.pythonhosted.org/packages/4d/1c/b5dc52cf713ae46033359c5ca920444f18a6359ce1020dd3e9c553ea5bc6/pillow-12.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0deedf2ea233722476b3a81e8cdfbad786f7adbed5d848469fa59fe52396e4ef", size = 7191878, upload-time = "2026-01-02T09:13:05.276Z" },
    { url = "https://files.pythonhosted.org/packages/53/26/c4188248bd5edaf543864fe4834aebe9c9cb4968b6f573ce014cc42d0720/pillow-12.1.0-cp314-cp314t-win32.whl", hash = "sha256:b17fbdbe01c196e7e159aacb889e091f28e61020a8abeac07b68079b6e626988", size = 6438703, upload-time = "2026-01-02T09:13:07.491Z" },
    { url = "https://files.pythonhosted.org/packages/b8/0e/69ed296de8ea05cb03ee139cee600f424ca166e632567b2d66727f08c7ed/pillow-12.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27b9baecb428899db6c0de572d6d305cfaf38ca1596b5c0542a5182e3e74e8c6", size = 7182927, upload-time = "2026-01-02T09:13:09.841Z" },
    { url = "https://files.pythonhosted.org/packages/fc/f5/68334c015eed9b5cff77814258717dec591ded209ab5b6fb70e2ae873d1d/pillow-12.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f61333d817698bdcdd0f9d7793e365ac3d2a21c1f1eb02b32ad6aefb8d8ea831", size = 2545104, upload-time = "2026-01-02T09:13:12.068Z" },
]

[[package]]
name = "platformdirs"
version = "4.5.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" },
]

[[package]]
name = "pluggy"
version = "1.6.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
]

[[package]]
name = "produm"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
    { name = "pathspec" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/a9/b1bce11f2b9b17a5c01aed0eb5603a72a90545ba07b18fbe9e581282edd9/produm-0.1.2.tar.gz", hash = "sha256:b611d23e3a4e3a29db41a1c8840bf083da37bacf63e5c5a4c22e85dd8006e846", size = 6836, upload-time = "2025-12-10T12:47:21.152Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/e5/9a/56f92a98c56eccd9353a78f87a8261de4c6e2f7891c02ce47451827cd777/produm-0.1.2-py3-none-any.whl", hash = "sha256:ac66e220a4f30d3a358d7150b23e950772d76e626342d384e62760687b9e9bce", size = 9571, upload-time = "2025-12-10T12:47:20.415Z" },
]

[[package]]
name = "pygments"
version = "2.19.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
]

[[package]]
name = "pyparsing"
version = "3.3.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" },
]

[[package]]
name = "pytest"
version = "9.0.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
    { name = "colorama", marker = "sys_platform == 'win32'" },
    { name = "iniconfig" },
    { name = "packaging" },
    { name = "pluggy" },
    { name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
]

[[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 = "pytokens"
version = "0.4.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" },
    { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" },
    { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" },
    { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" },
    { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" },
    { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" },
    { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" },
    { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" },
    { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" },
    { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" },
    { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" },
    { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" },
    { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" },
    { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" },
    { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" },
    { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" },
]

[[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" },
]

