diff --git a/.github/workflows/_package-publish.yml b/.github/workflows/_package-publish.yml index 1b4ddd29e..f1f6a9ac1 100644 --- a/.github/workflows/_package-publish.yml +++ b/.github/workflows/_package-publish.yml @@ -171,17 +171,36 @@ jobs: GIT_CLIFF_CHANGELOG: ${{ steps.git-cliff.outputs.changelog }} run: cat "$GIT_CLIFF_CHANGELOG" - - name: Build distribution into dist/ + - name: Verify lockstep versioning shell: bash - run: make dist + run: | + SDK_VERSION=$(grep '^version = ' packages/aignostics-sdk/pyproject.toml | head -1 | sed 's/version = "\(.*\)"/\1/') + FULL_VERSION=$(grep '^version = ' packages/aignostics/pyproject.toml | head -1 | sed 's/version = "\(.*\)"/\1/') + if [ "$SDK_VERSION" != "$FULL_VERSION" ]; then + echo "❌ Version mismatch: aignostics-sdk=$SDK_VERSION, aignostics=$FULL_VERSION" + exit 1 + fi + echo "✅ Versions match: $SDK_VERSION" - - name: Publish distribution to Python Package Index at pypi.org + - name: Build aignostics-sdk distribution + shell: bash + run: uv build --package aignostics-sdk --out-dir dist/ + + - name: Publish aignostics-sdk to PyPI shell: bash env: UV_PUBLISH_TOKEN: ${{ secrets.UV_PUBLISH_TOKEN }} - run: | - # Use uv's credential storage - uv will read from UV_PUBLISH_TOKEN env var automatically - uv publish + run: uv publish dist/aignostics_sdk-* + + - name: Build aignostics distribution + shell: bash + run: uv build --package aignostics --out-dir dist/ + + - name: Publish aignostics to PyPI + shell: bash + env: + UV_PUBLISH_TOKEN: ${{ secrets.UV_PUBLISH_TOKEN }} + run: uv publish dist/aignostics-* - name: Download test results for ubuntu-latest generated in _test.yml if: | @@ -276,3 +295,25 @@ jobs: # See https://github.com/cli/cli/discussions/10696 gh api repos/aignostics/python-sdk/dispatches \ -f event_type=release_created_programatically + + smoke_test_slim: + runs-on: ubuntu-latest + needs: package_publish + continue-on-error: true + steps: + - name: Install uv + uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 + + - name: Smoke test aignostics-sdk + shell: bash + run: | + VERSION=$(echo "${{ github.ref_name }}" | sed 's/^v//') + uv venv /tmp/slim-venv + uv pip install --python /tmp/slim-venv/bin/python "aignostics-sdk==$VERSION" + /tmp/slim-venv/bin/aignostics-sdk --help + /tmp/slim-venv/bin/python -c "from aignostics_sdk.platform import Client; print('✅ slim import OK')" + if /tmp/slim-venv/bin/python -c "import openslide" 2>/dev/null; then + echo "❌ openslide should not be installed in slim package" + exit 1 + fi + echo "✅ Heavy deps correctly absent" diff --git a/docs/partials/CLI_REFERENCE_header.md b/docs/partials/CLI_REFERENCE_header.md new file mode 100644 index 000000000..4ae10b0a0 --- /dev/null +++ b/docs/partials/CLI_REFERENCE_header.md @@ -0,0 +1,141 @@ +# CLI Reference + +The Aignostics Python SDK provides two CLI entry points: + +- **`aignostics`** — Full SDK (installed with `pip install aignostics`). Includes all commands + for application runs, WSI processing, dataset downloads, cloud storage, and the Launchpad. +- **`aignostics-sdk`** — Slim API client (installed with `pip install aignostics-sdk`). Includes + only user authentication and SDK metadata schema commands. + +When the full `aignostics` package is installed both entry points are available. + +--- + +## `aignostics-sdk` CLI Reference + +Command Line Interface of the slim `aignostics-sdk` distribution. + +**Usage**: + +```console +$ aignostics-sdk [OPTIONS] COMMAND [ARGS]... +``` + +**Options**: + +* `--install-completion`: Install completion for the current shell. +* `--show-completion`: Show completion for the current shell, to copy it or customize the installation. +* `--help`: Show this message and exit. + +**Commands**: + +* `user`: User operations such as login, logout and whoami. +* `sdk`: Platform operations such as dumping the SDK run and item metadata schemas. + +### `aignostics-sdk user` + +User operations such as login, logout and whoami. + +**Usage**: + +```console +$ aignostics-sdk user [OPTIONS] COMMAND [ARGS]... +``` + +**Commands**: + +* `login`: Authenticate with the Aignostics Platform using OAuth 2.0 device flow. +* `logout`: Remove cached authentication token. +* `whoami`: Show the currently authenticated user. + +#### `aignostics-sdk user login` + +Authenticate with the Aignostics Platform using OAuth 2.0 device flow. + +**Usage**: + +```console +$ aignostics-sdk user login [OPTIONS] +``` + +**Options**: + +* `--help`: Show this message and exit. + +#### `aignostics-sdk user logout` + +Remove cached authentication token. + +**Usage**: + +```console +$ aignostics-sdk user logout [OPTIONS] +``` + +**Options**: + +* `--help`: Show this message and exit. + +#### `aignostics-sdk user whoami` + +Show the currently authenticated user. + +**Usage**: + +```console +$ aignostics-sdk user whoami [OPTIONS] +``` + +**Options**: + +* `--mask-secrets / --no-mask-secrets`: Mask secrets in output. [default: mask-secrets] +* `--help`: Show this message and exit. + +### `aignostics-sdk sdk` + +Platform operations such as dumping the SDK run and item metadata schemas. + +**Usage**: + +```console +$ aignostics-sdk sdk [OPTIONS] COMMAND [ARGS]... +``` + +**Commands**: + +* `run-metadata-schema`: Export the JSON Schema for SDK run custom metadata. +* `item-metadata-schema`: Export the JSON Schema for SDK item custom metadata. + +#### `aignostics-sdk sdk run-metadata-schema` + +Export the JSON Schema for SDK run custom metadata. + +**Usage**: + +```console +$ aignostics-sdk sdk run-metadata-schema [OPTIONS] +``` + +**Options**: + +* `--pretty / --no-pretty`: Pretty-print the JSON output. [default: no-pretty] +* `--help`: Show this message and exit. + +#### `aignostics-sdk sdk item-metadata-schema` + +Export the JSON Schema for SDK item custom metadata. + +**Usage**: + +```console +$ aignostics-sdk sdk item-metadata-schema [OPTIONS] +``` + +**Options**: + +* `--pretty / --no-pretty`: Pretty-print the JSON output. [default: no-pretty] +* `--help`: Show this message and exit. + +--- + +## `aignostics` CLI Reference diff --git a/docs/partials/README_footer.md b/docs/partials/README_footer.md index 26a29209b..9a511460e 100644 --- a/docs/partials/README_footer.md +++ b/docs/partials/README_footer.md @@ -1,5 +1,8 @@ ## Further Reading +1. **Upgrading from v1?** Read the + [v2 Migration Guide](https://aignostics.readthedocs.io/en/latest/migration.html) + for updated import paths and install instructions. 1. Inspect our [security policy](https://aignostics.readthedocs.io/en/latest/security.html) with detailed documentation of checks, tools and principles. diff --git a/docs/partials/README_main.md b/docs/partials/README_main.md index 9232a42bb..4bb043a77 100644 --- a/docs/partials/README_main.md +++ b/docs/partials/README_main.md @@ -24,6 +24,62 @@ more about how we achieve ## Installation +> **v2 dual-package distribution**: The SDK is now published as two separate PyPI packages. +> Use `aignostics-sdk` for the slim API client, or `aignostics` for the full SDK with GUI, WSI +> processing, and dataset downloads. See the [v2 Migration Guide](#migrating-from-v1-to-v2) +> if you are upgrading from v1. + +### API client only (slim) + +For users who only need the platform API client with minimal dependencies: + +```bash +pip install aignostics-sdk +``` + +Python imports: + +```python +from aignostics_sdk.platform import Client +``` + +CLI: + +```bash +aignostics-sdk user login +aignostics-sdk user whoami +``` + +### Full SDK + +For users who need WSI processing, dataset downloads, the desktop Launchpad, and all features: + +```bash +pip install aignostics +``` + +Python imports (full SDK modules unchanged from v1): + +```python +from aignostics.application import ... # ML application orchestration +from aignostics.wsi import ... # Whole slide image processing +from aignostics.dataset import ... # IDC dataset downloads +``` + +Platform/utils imports (updated namespace in v2): + +```python +from aignostics_sdk.platform import Client # was: from aignostics.platform import Client +from aignostics_sdk.utils import BaseService # was: from aignostics.utils import BaseService +``` + +CLI (unchanged): + +```bash +aignostics user login +aignostics application list +``` + The **Aignostics Python SDK** can be installed via the [uv package manager](https://docs.astral.sh/uv/). The installation process sets up the SDK along with the necessary dependencies, including the **uv** package manager itself if not already present. Before proceeding, ensure you have an **Aignostics Platform account**. You can get access either through your organization admin (if your organization has an Aignostics account) or directly from Aignostics. Check your email for an invitation before proceeding. @@ -656,3 +712,41 @@ Now that you have an overview of the Aignostics Python SDK and its interfaces, h - **Review detailed documentation**: See the [CLI reference](https://aignostics.readthedocs.io/en/latest/cli_reference.html) and [Python Library reference](https://aignostics.readthedocs.io/en/latest/lib_reference.html) - **Explore QuPath integration**: Use the QuPath extension to visualize and interact with your results - **Get support**: Contact [support@aignostics.com](mailto:support@aignostics.com) or check the [full documentation](https://aignostics.readthedocs.io/en/latest/) +- **Upgrading from v1?** See the [v2 Migration Guide](https://aignostics.readthedocs.io/en/latest/migration.html) for updated import paths and install instructions + +## Migrating from v1 to v2 + +v2 introduces `aignostics-sdk`, a slim distribution containing only the platform API client. +The full `aignostics` package now depends on `aignostics-sdk`. + +### Import path changes + +| v1 | v2 | +|----|----| +| `from aignostics.platform import Client` | `from aignostics_sdk.platform import Client` | +| `from aignostics.utils import BaseService` | `from aignostics_sdk.utils import BaseService` | +| `from aignostics.constants import INTERNAL_ORGS` | `from aignostics_sdk.constants import INTERNAL_ORGS` | + +All other modules (`application`, `wsi`, `dataset`, `bucket`, `qupath`, `notebook`, `gui`, +`system`) remain under the `aignostics.*` namespace and are unchanged. + +### CLI changes + +The `aignostics` CLI is unchanged. A new `aignostics-sdk` CLI is available when only the slim +package is installed: + +```bash +# slim install +aignostics-sdk user login +aignostics-sdk user whoami + +# full install (unchanged) +aignostics user login +aignostics application list +``` + +### If you only use the API client + +Replace `pip install aignostics` with `pip install aignostics-sdk` for a significantly smaller +install footprint. See the [full migration guide](https://aignostics.readthedocs.io/en/latest/migration.html) +for a complete checklist. diff --git a/docs/source/index.rst b/docs/source/index.rst index 9e33aeb53..957ca1693 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -12,6 +12,7 @@ :hidden: main + migration platform_overview cli_reference lib_reference diff --git a/docs/source/migration.md b/docs/source/migration.md new file mode 100644 index 000000000..e1abd97e9 --- /dev/null +++ b/docs/source/migration.md @@ -0,0 +1,141 @@ +# Migrating from v1 to v2 + +v2 of the Aignostics Python SDK introduces `aignostics-sdk`, a slim distribution that contains +only the platform API client and core utilities. The full `aignostics` package now depends on +`aignostics-sdk` and delegates platform/utils functionality to it. + +This is a **breaking change** for anyone importing `aignostics.platform` or `aignostics.utils` +directly. + +## What changed + +The `platform` and `utils` modules (and `constants`) have moved to a separate PyPI package called +`aignostics-sdk`. Their Python namespace has changed from `aignostics.*` to `aignostics_sdk.*`. + +| v1 (Python namespace) | v2 (Python namespace) | +|-----------------------|-----------------------| +| `aignostics.platform` | `aignostics_sdk.platform` | +| `aignostics.utils` | `aignostics_sdk.utils` | +| `aignostics.constants` | `aignostics_sdk.constants` | + +All other modules (`application`, `wsi`, `dataset`, `bucket`, `qupath`, `notebook`, `gui`, +`system`) remain under the `aignostics.*` namespace and are only available in the full package. + +## Import path changes + +Update your Python imports as follows: + +| v1 import | v2 import | +|-----------|-----------| +| `from aignostics.platform import Client` | `from aignostics_sdk.platform import Client` | +| `from aignostics.platform import InputItem, InputArtifact` | `from aignostics_sdk.platform import InputItem, InputArtifact` | +| `from aignostics.utils import BaseService` | `from aignostics_sdk.utils import BaseService` | +| `from aignostics.utils import Health` | `from aignostics_sdk.utils import Health` | +| `from aignostics.utils import locate_implementations` | `from aignostics_sdk.utils import locate_implementations` | +| `from aignostics.constants import INTERNAL_ORGS` | `from aignostics_sdk.constants import INTERNAL_ORGS` | +| `from aignostics import platform` | `from aignostics_sdk import platform` | + +## Installation changes + +### If you only use the API client + +Replace the full package with the slim package for a significantly smaller install footprint: + +```bash +# v1 +pip install aignostics + +# v2 — slim install +pip install aignostics-sdk +``` + +The slim `aignostics-sdk` package includes only `platform`, `utils`, and `constants`. +It has no dependency on OpenSlide, wsidicom, boto3, Google Cloud Storage, or other heavy +libraries required by the WSI and dataset modules. + +### If you use the full SDK + +The install command is **unchanged**: + +```bash +pip install aignostics +``` + +The full package automatically pulls in `aignostics-sdk` as a dependency, so all +`aignostics_sdk.*` imports work after installing `aignostics`. + +## CLI changes + +The `aignostics` CLI command is **unchanged**. A new `aignostics-sdk` CLI entry point is +registered when the slim package is installed: + +```bash +# Slim install — available commands +aignostics-sdk user login +aignostics-sdk user logout +aignostics-sdk user whoami +aignostics-sdk sdk run-metadata-schema +aignostics-sdk sdk item-metadata-schema + +# Full install — unchanged command, full feature set +aignostics user login +aignostics application list +aignostics wsi inspect slide.svs +``` + +## Health checks using the full SDK + +In v1, the health check import was: + +```python +# v1 +from aignostics.system import Service as SystemService + +health = SystemService().health() +``` + +This is **unchanged** in v2 — `system` remains in the `aignostics` namespace: + +```python +# v2 — same as v1 +from aignostics.system import Service as SystemService + +health = SystemService().health() +``` + +## Dependencies no longer transitive in aignostics-sdk + +The following packages were implicit transitive dependencies of `aignostics` v1.x +but are **not** included in `aignostics-sdk` v2.x. If your project used them without +an explicit declaration, add them directly: + +| Package | Previously pulled in by | +|---------|------------------------| +| `ijson` | `aignostics[qupath]` extra | +| `shapely` | `aignostics` (geometry utils) | +| `openslide-bin` / `openslide-python` | `aignostics` (WSI processing) | +| `boto3` | `aignostics` (bucket operations) | +| `google-cloud-storage` | `aignostics` (bucket operations) | + +## `__project_name__` is no longer a valid env-var prefix + +In `aignostics-sdk`, `__project_name__` returns `"aignostics-sdk"` (with a hyphen), +which is **not** valid in environment variable names. Use the exported constant instead: + +```python +# ❌ wrong in aignostics-sdk v2 +prefix = f"{__project_name__.upper()}_" # gives "AIGNOSTICS-SDK_" + +# ✅ correct +from aignostics_sdk.utils import ENV_PREFIX + +prefix = f"{ENV_PREFIX}_" # gives "AIGNOSTICS_" +``` + +## Quick migration checklist + +1. Update `from aignostics.platform import ...` to `from aignostics_sdk.platform import ...` +2. Update `from aignostics.utils import ...` to `from aignostics_sdk.utils import ...` +3. Update `from aignostics.constants import ...` to `from aignostics_sdk.constants import ...` +4. If you only need the API client, switch `pip install aignostics` to `pip install aignostics-sdk` +5. If you use the full SDK, keep `pip install aignostics` — no pip change needed diff --git a/examples/notebook.py b/examples/notebook.py index c33a468f3..de14fecc6 100644 --- a/examples/notebook.py +++ b/examples/notebook.py @@ -228,7 +228,7 @@ def _(client): def _(mo): mo.md( r""" - from aignostics.platform.resources.runs import ApplicationRun + from aignostics_sdk.platform.resources.runs import ApplicationRun application_run = ApplicationRun.for_run_id("") # download download_folder = "/tmp/" diff --git a/noxfile.py b/noxfile.py index dd69ce240..1c67839f2 100644 --- a/noxfile.py +++ b/noxfile.py @@ -125,7 +125,7 @@ def lint(session: nox.Session) -> None: ".", ) session.run("pyright", "--pythonversion", PYTHON_VERSION, "--threads") - session.run("mypy", "src") + session.run("mypy", "packages/aignostics-sdk/src", "packages/aignostics/src") @nox.session(python=[PYTHON_VERSION]) @@ -502,10 +502,17 @@ def _generate_sdk_metadata_schemas(session: nox.Session) -> None: def _generate_cli_reference(session: nox.Session) -> None: """Generate CLI_REFERENCE.md. + The generated file is assembled from two parts: + 1. A static header partial (docs/partials/CLI_REFERENCE_header.md) that documents + the aignostics-sdk slim CLI entry point and provides an introduction. + 2. The auto-generated aignostics CLI reference produced by typer-cli. + Args: session: The nox session instance """ if CLI_MODULE: + # Generate the aignostics CLI reference into a temporary file + tmp_output = Path("CLI_REFERENCE_aignostics.md") session.run( "python", "-W", @@ -520,10 +527,24 @@ def _generate_cli_reference(session: nox.Session) -> None: "--title", "CLI Reference", "--output", - "CLI_REFERENCE.md", + str(tmp_output), external=True, ) + # Prepend the static header (aignostics-sdk CLI docs + intro) to the typer output. + # The header already includes its own "# CLI Reference" heading and the + # "## `aignostics` CLI Reference" sub-heading, so we strip the auto-generated + # top-level heading from the typer output to avoid duplication. + header = Path("docs/partials/CLI_REFERENCE_header.md").read_text(encoding=UTF8) + aignostics_body = tmp_output.read_text(encoding=UTF8) + # Remove the first "# CLI Reference" heading generated by typer (first line) + aignostics_body_lines = aignostics_body.splitlines(keepends=True) + if aignostics_body_lines and aignostics_body_lines[0].startswith("# CLI Reference"): + aignostics_body = "".join(aignostics_body_lines[1:]).lstrip("\n") + Path("CLI_REFERENCE.md").write_text(header + aignostics_body, encoding=UTF8) + tmp_output.unlink(missing_ok=True) + session.log("Generated CLI_REFERENCE.md with aignostics-sdk header and aignostics CLI docs") + def _generate_api_reference(session: nox.Session) -> None: """Generate API_REFERENCE_v1.md and API_REFERENCE_v2.md. @@ -596,6 +617,7 @@ def _generate_pdf_docs(session: nox.Session) -> None: version_match = re.search(r"Version (\d+\.\d+\w*)", str(out)) if not version_match: session.error("Could not determine latexmk version") + assert version_match is not None # noqa: S101 version_str = version_match.group(1) @@ -603,6 +625,7 @@ def _generate_pdf_docs(session: nox.Session) -> None: match = re.match(r"(\d+\.\d+)", version_str) if not match: session.error(f"Could not parse version number from '{version_str}'") + assert match is not None # noqa: S101 base_version = match.group(1) if float(base_version) < LATEXMK_VERSION_MIN: @@ -665,6 +688,7 @@ def docs_pdf(session: nox.Session) -> None: version_match = re.search(r"Version (\d+\.\d+\w*)", str(out)) if not version_match: session.error("Could not determine latexmk version") + assert version_match is not None # noqa: S101 version_str = version_match.group(1) @@ -672,6 +696,7 @@ def docs_pdf(session: nox.Session) -> None: match = re.match(r"(\d+\.\d+)", version_str) if not match: session.error(f"Could not parse version number from '{version_str}'") + assert match is not None # noqa: S101 base_version = match.group(1) if float(base_version) < LATEXMK_VERSION_MIN: @@ -1027,5 +1052,6 @@ def act(session: nox.Session) -> None: @nox.session() def dist(session: nox.Session) -> None: - """Build wheel and put in dist/.""" - session.run("uv", "build", external=True) + """Build wheels for both packages into dist/.""" + session.run("uv", "build", "--package", "aignostics-sdk", "--out-dir", "dist/", external=True) + session.run("uv", "build", "--package", "aignostics", "--out-dir", "dist/", external=True) diff --git a/packages/aignostics-sdk/CLAUDE.md b/packages/aignostics-sdk/CLAUDE.md new file mode 100644 index 000000000..72278085a --- /dev/null +++ b/packages/aignostics-sdk/CLAUDE.md @@ -0,0 +1,82 @@ +# CLAUDE.md - aignostics-sdk (Slim Package) + +This is the **slim distribution** of the Aignostics SDK, published to PyPI as `aignostics-sdk`. +It contains only the platform API client and core utilities — no WSI processing, no GUI, no dataset +downloads, no cloud storage abstraction. + +## What this package contains + +| Module | Purpose | +|--------|---------| +| `aignostics_sdk.platform` | OAuth 2.0 authentication, API client, SDK metadata tracking, run/item resources | +| `aignostics_sdk.utils` | Core infrastructure: DI container, logging, settings, `BaseService`, health checks, user agent | +| `aignostics_sdk.constants` | Shared constants (e.g. `INTERNAL_ORGS`) | + +## Python namespace + +All public APIs are under the `aignostics_sdk.*` namespace: + +```python +from aignostics_sdk.platform import Client +from aignostics_sdk.utils import BaseService, Health +from aignostics_sdk.constants import INTERNAL_ORGS +``` + +> This is a **breaking change** from v1, where these modules lived under `aignostics.platform`, +> `aignostics.utils`, and `aignostics.constants`. +> See the [v2 migration guide](../../docs/source/migration.md) for the full import mapping. + +## CLI entry point + +The slim package registers the `aignostics-sdk` CLI entry point (in addition to the `aignostics` +entry point provided by the full package): + +```bash +# Slim install only +aignostics-sdk user login +aignostics-sdk user logout +aignostics-sdk user whoami +aignostics-sdk sdk run-metadata-schema +aignostics-sdk sdk item-metadata-schema +``` + +The `aignostics` CLI (full package) provides the same commands plus all heavy-module commands +(`application`, `wsi`, `dataset`, `bucket`, etc.). + +## Source history + +The source for this package was migrated from: + +- `src/aignostics/platform/` → `packages/aignostics-sdk/src/aignostics_sdk/platform/` +- `src/aignostics/utils/` → `packages/aignostics-sdk/src/aignostics_sdk/utils/` +- `src/aignostics/constants.py` → `packages/aignostics-sdk/src/aignostics_sdk/constants.py` + +## Dependencies + +Minimal by design — no OpenSlide, wsidicom, boto3, google-cloud-storage, or other heavy libraries. +Runtime dependencies are limited to what the platform API client strictly needs (httpx/requests, +pydantic, tenacity, etc.). + +## Module architecture + +This package follows the same three-layer pattern as the rest of the SDK: + +```text +aignostics_sdk/ +├── platform/ +│ ├── _service.py # Business logic (API client, OAuth, caching, retry) +│ ├── _cli.py # CLI: user login/logout/whoami, sdk metadata-schema +│ ├── _settings.py # Pydantic settings +│ └── CLAUDE.md # Platform module documentation +├── utils/ +│ ├── _service.py # BaseService, Health, DI container +│ ├── _cli.py # CLI: mcp run/list-tools +│ ├── _settings.py # Pydantic settings +│ └── CLAUDE.md # Utils module documentation +└── constants.py # Shared constants +``` + +## Testing + +Tests for this package live alongside the source in the shared `tests/` tree at the repo root. +Run them with the standard test commands documented in the root `CLAUDE.md`. diff --git a/packages/aignostics-sdk/LICENSE b/packages/aignostics-sdk/LICENSE new file mode 100644 index 000000000..75e0868b4 --- /dev/null +++ b/packages/aignostics-sdk/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [2025] [Aignostics GmbH (support@aignostics.com)] + +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. diff --git a/packages/aignostics-sdk/README.md b/packages/aignostics-sdk/README.md new file mode 100644 index 000000000..b728aa1cc --- /dev/null +++ b/packages/aignostics-sdk/README.md @@ -0,0 +1,1015 @@ + +[//]: # (README.md generated from docs/partials/README_*.md) + +# 🔬Aignostics Python SDK + +[![License](https://img.shields.io/github/license/aignostics/python-sdk?logo=opensourceinitiative&logoColor=3DA639&labelColor=414042&color=A41831)](https://github.com/aignostics/python-sdk/blob/main/LICENSE) +[![Python Version](https://img.shields.io/pypi/pyversions/aignostics.svg?logo=python&color=204361&labelColor=1E2933)](https://pypi.org/project/aignostics/) +[![CI/CD](https://github.com/aignostics/python-sdk/actions/workflows/ci-cd.yml/badge.svg?branch=main)](https://github.com/aignostics/python-sdk/actions/workflows/ci-cd.yml) +[![Docs](https://img.shields.io/readthedocs/aignostics)](https://aignostics.readthedocs.io/en/latest/) +[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=aignostics_python-sdk&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=aignostics_python-sdk) +[![Security](https://sonarcloud.io/api/project_badges/measure?project=aignostics_python-sdk&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=aignostics_python-sdk) +[![Maintainability](https://sonarcloud.io/api/project_badges/measure?project=aignostics_python-sdk&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=aignostics_python-sdk) +[![Coverage](https://codecov.io/gh/aignostics/python-sdk/graph/badge.svg?token=SX34YRP30E)](https://codecov.io/gh/aignostics/python-sdk) +[![Uptime](https://uptime.betterstack.com/status-badges/v2/monitor/1wbqa.svg)](https://aignostics.betteruptime.com) + +## Introduction + +The **Aignostics Python SDK** provides multiple ways to interact with the **Aignostics Platform** for running advanced computational pathology applications like [Atlas H&E-TME](https://www.aignostics.com/products/he-tme-profiling-product), which analyzes tumor microenvironments in H&E-stained tissue samples. + +### We take quality and security seriously + +We know you take **quality** and **security** as seriously as we do. That's why +the Aignostics Python SDK is built following best practices and with full +transparency. This includes (1) making the complete +[source code of the SDK +available on GitHub](https://github.com/aignostics/python-sdk/), maintaining a +(2) +[A-grade code quality](https://sonarcloud.io/summary/new_code?id=aignostics_python-sdk) +with [high test coverage](https://app.codecov.io/gh/aignostics/python-sdk) in +all releases, (3) achieving +[A-grade security](https://sonarcloud.io/summary/new_code?id=aignostics_python-sdk) +with +[active scanning of dependencies](https://github.com/aignostics/python-sdk/issues/4), +and (4) providing +[extensive documentation](https://aignostics.readthedocs.io/en/latest/). Read +more about how we achieve +[operational excellence](https://aignostics.readthedocs.io/en/latest/operational_excellence.html) and +[security](https://aignostics.readthedocs.io/en/latest/security.html). + +## Installation + +The **Aignostics Python SDK** can be installed via the [uv package manager](https://docs.astral.sh/uv/). The installation process sets up the SDK along with the necessary dependencies, including the **uv** package manager itself if not already present. + +Before proceeding, ensure you have an **Aignostics Platform account**. You can get access either through your organization admin (if your organization has an Aignostics account) or directly from Aignostics. Check your email for an invitation before proceeding. + +### Requirements + +- **Python 3.11, 3.12, 3.13, or 3.14** +- **macOS 11.0+, Linux, or Windows** +- **Homebrew** (only if you previously installed `uv` via Homebrew) + +### Installation Steps + +The installation will: + +1. Install or update **uv** (Python package installer) +2. Install the **Aignostics Python SDK** (includes Launchpad, CLI, and Python Library) + +Copy and paste the appropriate command below into your terminal (macOS/Linux) or PowerShell (Windows): + +**Linux/macOS:** + +```bash +if ! command -v uv &> /dev/null; then + echo "uv not found, installing..." + curl -LsSf https://astral.sh/uv/install.sh | sh + source $HOME/.local/bin/env +else + UV_VERSION=$(uv --version | cut -d' ' -f2) + if [ "$(printf '%s\n' "0.6.17" "$UV_VERSION" | sort -V | head -n1)" != "0.6.17" ]; then + echo "Updating uv to the latest version..." + UV_PATH=$(which uv) + if [[ "$UV_PATH" == *"brew"* ]]; then + echo "Updating uv using Homebrew..." + brew upgrade uv + else + echo "Updating uv using the installer..." + uv self update + fi + else + echo "uv is up to date" + fi +fi +``` + +**Windows (PowerShell):** + +```powershell +winget install --id=Microsoft.VCRedist.2015+.x64 -e +powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" +``` + +Verify your installation by running: + +```bash +uvx aignostics --help +``` + +You should see the Aignostics CLI help output. + +You can then proceed by choosing your preferred user interface below. + +## Platform Workflow Overview + +The Aignostics Platform delivers enterprise-grade computational pathology through a secure, scalable cloud architecture. Organizations subscribe to the platform, and their users interact through three interfaces - all part of the Python SDK - to leverage advanced AI/ML models running on dedicated NVIDIA® GPU infrastructure. + +**Key architectural components:** + +- **Python SDK**: Provides three user interfaces (Launchpad desktop app, CLI, and Client Library) with unified functionality +- **Enterprise authentication**: Powered by Auth0, supporting Single Sign-On (SSO) and existing identity management systems +- **Organization storage**: Dedicated Google Cloud Storage bucket per organization with automatic 30-day cleanup +- **Aignostics Platform API**: Orchestrates application discovery, run submission, status monitoring, and results delivery +- **NVIDIA® GPU clusters**: Dedicated compute provisioned per application run for maximum security and compliance + +```mermaid +%%{init: {'theme':'dark', 'themeVariables': { 'fontSize':'18px', 'fontFamily':'arial', 'darkMode':'true', 'background':'#1e1e1e', 'primaryColor':'#4a4a4a', 'primaryTextColor':'#ffffff', 'primaryBorderColor':'#ffffff', 'lineColor':'#ffffff', 'secondaryColor':'#3a3a3a', 'tertiaryColor':'#2a2a2a', 'actorBkg':'#4a4a4a', 'actorBorder':'#ffffff', 'actorTextColor':'#ffffff', 'actorLineColor':'#ffffff', 'signalColor':'#ffffff', 'signalTextColor':'#ffffff', 'labelBoxBkgColor':'#3a3a3a', 'labelBoxBorderColor':'#ffffff', 'labelTextColor':'#ffffff', 'noteBkgColor':'#4a4a4a', 'noteTextColor':'#ffffff', 'noteBorderColor':'#ffffff', 'sequenceNumberColor':'#000000'}}}%% +sequenceDiagram + autonumber + actor User as User
(Organization Member) + participant SDK as Python SDK
(Launchpad/CLI/Client Library) + participant Auth0 as Auth0
(Enterprise Identity) + participant Bucket as Organization Bucket
(Google Cloud Storage) + participant API as Aignostics Platform API + participant GPU as NVIDIA® GPU Cluster
(per-run isolation) + + Note over User,GPU: Authentication & Authorization + User->>SDK: Launch interface + SDK->>Auth0: Authenticate user + Auth0-->>SDK: Access token + SDK->>API: Validate token + API-->>SDK: User authorized + + Note over User,GPU: Application Selection + User->>SDK: Browse applications + SDK->>API: List applications & versions + API-->>SDK: Application catalog + SDK-->>User: Display options + + Note over User,GPU: Data Upload + User->>SDK: Select WSIs + metadata + SDK->>Bucket: Upload files + Note over Bucket: 30-day auto-cleanup + Bucket-->>SDK: Upload complete + SDK->>SDK: Generate signed download URLs + + Note over User,GPU: Run Submission + SDK->>API: Submit run (app, metadata, signed URLs) + API-->>SDK: Run ID + queue position + SDK-->>User: Confirm submission + + Note over User,GPU: GPU Processing + API->>GPU: Provision dedicated NVIDIA® cluster + GPU->>Bucket: Download WSIs via signed URLs + GPU->>GPU: Process slides incrementally + GPU->>API: Upload results per slide + Note over GPU: Deprovision after completion + + Note over User,GPU: Status Monitoring & Results + User->>SDK: Check status + SDK->>API: Poll run status + API-->>SDK: Progress (e.g., "3 of 10 complete") + SDK-->>User: Display progress + + User->>SDK: Download results + SDK->>API: Request result URLs + API-->>SDK: Signed download URLs + SDK->>API: Download files (GeoJSON, CSV, TIFF) + SDK-->>User: Results ready for inspection +``` + +**How it works:** + +Organizations subscribe to the Aignostics Platform and receive dedicated infrastructure including a Google Cloud Storage bucket and API access. Users within the organization authenticate through Auth0, which integrates with enterprise identity management systems for seamless Single Sign-On (SSO). + +The Python SDK - available as a desktop application (Launchpad), command-line interface (CLI), or programmable library (Client Library) - handles all complexity of authentication, data upload, run orchestration, and results delivery. Users simply select an application, provide whole slide images with metadata, and submit. + +Behind the scenes, the Aignostics Platform API provisions dedicated NVIDIA® GPU clusters for each application run, ensuring data isolation and compliance with healthcare regulations. Processing occurs incrementally (slide-by-slide), allowing users to monitor progress and download results as they become available rather than waiting for entire cohorts. + +The organization's Google Cloud Storage bucket stores uploaded files with automatic 30-day cleanup, optimizing costs while maintaining data availability throughout processing. All data transfers use time-limited signed URLs, eliminating credential management complexity and security risks. + +**Enterprise benefits:** + +- **Security & compliance**: Per-run GPU isolation, enterprise SSO integration, zero-trust architecture with signed URLs +- **Scalability**: Handles single exploratory slides through thousand-slide clinical studies with identical user experience +- **Cost efficiency**: Pay-per-use GPU provisioning, automatic storage cleanup, no idle infrastructure costs +- **Operational simplicity**: Python SDK abstracts all cloud complexity; IT teams manage access through existing identity systems + +## Choose your interface + +Choose your preferred interface for working with the Aignostics Platform. Each interface is designed for different user roles and use cases: + +### 🖥️ Launchpad (Desktop Application) + +| | | +|---|---| +| **What it is** | Graphical application for analyzing slides and viewing results in QuPath or Python notebooks | +| **Best for** | Pathologists and researchers who want to analyze slides without writing code | +| **Use when** | Running analyses on individual cases or small cohorts (1-20 slides) and exploring results interactively | +| **Get started** | Install and run your first analysis | + +### ⌨️ CLI (Command-Line Interface) + +| | | +|---|---| +| **What it is** | Terminal tool for scripting and automation | +| **Best for** | Bioinformaticians and technical researchers who work with terminal-based workflows | +| **Use when** | Processing large cohorts (10s-100s of slides), automating repetitive analyses, or integrating with computational pipelines | +| **Get started** | Manage datasets and application runs from your terminal | + +### 📚 Python Library + +| | | +|---|---| +| **What it is** | Python library for programmatic access in scripts, notebooks, and applications | +| **Best for** | Data scientists and developers who want to integrate the platform into Python-based workflows | +| **Use when** | Building custom analysis pipeline in Python for repeated usage and processing large datasets (10s-1000s of slides) | +| **Get started** | Run example notebooks or call the Aignostics Platform API from your Python scripts | + +### 🤖 MCP Server (AI Agent Integration) + +| | | +|---|---| +| **What it is** | Model Context Protocol server that exposes SDK functionality to AI agents like Claude | +| **Best for** | Users who want AI assistants to help with platform operations | +| **Use when** | Working with Claude Desktop or other MCP-compatible AI tools to manage datasets, submit runs, or query results | +| **Get started** | Configure Claude Desktop for MCP integration | + +> 💡 Launchpad and CLI handle authentication automatically. Python Library requires manual setup (see [authentication section](#example-notebooks-interact-with-the-aignostics-platform-from-your-python-notebook-environment)). + +## Launchpad: Run your first computational pathology analysis in 10 minutes from your desktop + +The **Aignostics Launchpad** is a graphical desktop application that allows you to run applications on whole slide images (WSIs) from your computer, and inspect results with QuPath and Python Notebooks with one click. It is designed to be user-friendly and intuitive, for use by Research Pathologists and Data Scientists. + +**New to Launchpad?** See Installation section above to get started. + +### Running Your First Analysis + +This tutorial uses [Atlas H&E-TME](https://www.aignostics.com/products/he-tme-profiling-product) with a public lung cancer dataset from the NCI Image Data Commons. + +**Step 1: Start Aignostics Launchpad** + +1. Open a terminal or command prompt +2. Run the command: `uvx aignostics launchpad` +This starts the Launchpad application. + +**Step 2: Download a Sample Dataset** + +1. Click the menu icon (☰) in the top right corner +2. Click "Download Datasets". The system displays the dataset download interface. +3. Click "EXAMPLE DATASET". The system populates the dataset ID field with a TCGA lung adenocarcinoma sample. +4. Click "DATA". The system shows a folder selection dialog. +5. Click "OK" +6. Click "DOWNLOAD". The system downloads the DICOM dataset. A progress indicator shows download status. +7. Click the menu icon and select "Run Applications" + +**Step 3: Select Atlas H&E-TME** + +1. Click "Atlas H&E-TME" in the left sidebar. The system displays the application workflow with six steps. +2. Click the version dropdown to view available versions. The system shows all available versions with release notes accessible via the "RELEASE NOTES" button. +3. Keep the default version (latest) +4. Click "NEXT" + +**Step 4: Select Slides and Provide Metadata** + +1. Click "DATA". The system opens a folder selection dialog showing the Launchpad datasets directory. +2. Navigate to the downloaded dataset folder (e.g., `/datasets/idc/tcga_luad/`) +3. Click "OK". The system displays the selected folder path and scans the folder, showing a table with all compatible slides. Each row shows thumbnail preview, technical metadata (file size, MPP resolution, dimensions), and status indicators. +4. The system automatically extracts technical file metadata. You must provide the required medical metadata by double-clicking the red cells in the "Tissue" column. The system displays a dropdown menu with tissue types. +5. Select the tissue type (e.g., "LUNG") and disease (e.g., "LUNG_CANCER") by double-clicking in the red cells and selecting the value from the dropdown. The system marks these cells green indicating valid metadata. +6. Review the "Staining" column. The system shows "H&E" if this information was extracted from the DICOM file. +7. Click "NEXT" + +**Step 5: Add Notes and Tags (Optional)** + +1. The system displays the notes and tags screen. +2. Enter an optional note in the text field (e.g., "TCGA lung sample analysis") +3. Add optional tags by typing and pressing Enter (e.g., "TCGA", "lung") +4. Click "NEXT" + +**Step 6: Set Schedule (Optional)** + +1. The system displays scheduling options with soft due date and hard deadline pickers. +2. Click "NEXT" to leave the default settings. + +The soft due date indicates when the platform will attempt to complete processing. The hard deadline is when the platform may cancel the run if resources are unavailable. + +**Step 7: Submit Your Run** + +1. The system displays the submission screen showing number of slides to be analyzed, full file paths, and upload and submit button. +2. Review the slide information +3. Click "UPLOAD AND SUBMIT". The system uploads your slides to the Aignostics Platform and submits the analysis run. A progress indicator shows upload status. + +The left sidebar now shows your submitted run with application name and version, submission timestamp, running status icon (🏃), and any tags you added. + +**Step 8: Monitor Your Run** + +Atlas H&E-TME processing time depends on slide size and system load. Depending on the file size and the number of files, processing can take minutes to many hours. + +Click on your run in the sidebar to view run details and metadata, slide thumbnails, and processing status for each slide. The status icon updates as processing progresses. + +### Understanding Your Results + +When processing completes, Atlas H&E-TME provides comprehensive tumor microenvironment analysis results for each processed slide: + +**What You'll Receive:** + +- **Tissue analysis**: Identification of tissue regions (tumor, stroma, necrosis, etc.) with quality assessment in GeoJSON format +- **Cell analysis**: Individual cells detected and classified by type (tumor cells, immune cells, stromal cells, etc.) in GeoJSON format +- **Visual segmentation maps**: Color-coded images showing spatial distribution of tissue and cell types +- **Quantitative measurements**: Cell counts, densities, spatial relationships, and statistical summaries provided in CSV format + +**Downloading Results:** + +When processing completes, the status icon changes to show completion. To download results: + +1. Click the "Download Results" button +2. The system downloads a ZIP file containing all outputs to your computer + +**Inspecting Results in QuPath:** + +QuPath integration provides the most powerful way to visualize and interact with your results: + +1. Click "Open in QuPath" (requires QuPath extension - see Advanced Setup below) +2. The system automatically creates a QuPath project with your slides and annotations loaded +3. In QuPath, you can: + - View tissue and cell annotations overlaid on your slides + - Explore cell classifications and measurements + - Analyze spatial relationships between different cell types + - Export annotations or perform additional analysis + +**Congratulations!** You have successfully downloaded a public dataset, submitted an Atlas H&E-TME analysis run, and learned how to access and inspect your results. + +### System Health Checks + +The Launchpad automatically monitors system health before allowing run submissions. If the system is unhealthy (e.g., network connectivity issues, authentication problems, or platform unavailability), the submission workflow is blocked: + +- A tooltip displays "System is unhealthy, you cannot prepare a run at this time." +- The "Next" button in the application workflow is disabled. +- The health status is shown in the footer bar at the bottom of the Launchpad. + +To resolve health issues: + +1. Check the health status indicator in the footer bar +2. Click "Info and Settings" in the menu to see detailed health information +3. Verify your network connection and authentication status +4. Check the [Aignostics Platform Status](https://status.aignostics.com) page + +### Advanced Setup: Extensions + +> 💡 The Launchpad features a growing ecosystem of extensions that seamlessly integrate with standard digital pathology tools. To use the Launchpad with all available extensions, run `uvx --from "aignostics[qupath,marimo]" aignostics launchpad`. Currently available extensions are: +> +> 1. **QuPath extension**: View your application results in [QuPath](https://qupath.github.io/) with a single click. The Launchpad creates QuPath projects on-the-fly. +> 2. **Marimo extension**: Analyze your application results using [Marimo](https://marimo.io/) notebooks embedded in the Launchpad. You don't have to leave the Launchpad to do real data science. + +## CLI: Manage datasets and application runs from your terminal + +The Python SDK includes the **Aignostics CLI**, a Command-Line Interface (CLI) that allows you to +interact with the Aignostics Platform directly from your terminal or shell script. + +**New to CLI?** See Installation section above to get started. + +**Common workflows:** + +- Download public datasets from NCI Image Data Commons +- Submit batch processing runs for multiple slides +- Monitor run status and download results incrementally +- Automate repetitive tasks with shell scripts + +See as follows for a simple example where we download a sample dataset for the [Atlas +H&E-TME application](https://www.aignostics.com/products/he-tme-profiling-product), submit an application run, and download the results. + +### Example: Running Atlas H&E-TME with CLI + +1. Open a terminal or command prompt +2. Use the following commands to run the Atlas H&E-TME application on a sample dataset: + +```shell +# Download a sample dataset from the NCI Image Data Commons (IDC) portal to your current working directory +# As the dataset id refers to the TCGA LUAD collection, this creates a directory tcga_luad with the DICOM files +uvx aignostics dataset idc download 1.3.6.1.4.1.5962.99.1.1069745200.1645485340.1637452317744.2.0 data/ +# Prepare the metadata for the application run by creating a metadata.csv, extracting +# the required metadata from the DICOM files. We furthermore add the required +# information about the tissue type and disease. +uvx aignostics application run prepare he-tme data/tcga_luad/run.csv data/ +# Edit the metadata.csv to insert the required information about the staining method, tissue type and disease +# Adapt to your favourite editor +nano tcga_luad/metadata.csv +# Upload the metadata.csv and referenced whole slide images to the Aignostics Platform +uvx aignostics application run upload he-tme data/tcga_luad/run.csv +# Submit the application run and print the run id +uvx aignostics application run submit he-tme data/tcga_luad/run.csv +# Check the status of the application run you submitted +uvx aignostics application run list +# Incrementally download results when they become available +# Fill in the id from the output in the previous step +uvx aignostics application run result download APPLICATION_RUN_ID +``` + +For convenience the `application run execute` command combines preparation, upload, submission and download. +The below is equivalent to the above, while adding additionally required metadata using a mapping: + +```shell +uvx aignostics dataset idc download 1.3.6.1.4.1.5962.99.1.1069745200.1645485340.1637452317744.2.0 data/ +uvx aignostics application run execute he-tme data/tcga_luad/run.csv data/ --mapping ".*\.dcm:staining_method=H&E,tissue=LUNG,disease=LUNG_CANCER" +``` + +The CLI provides extensive help: + +```shell +uvx aignostics --help # list all spaces such as application, dataset, bucket and system, +uvx aignostics application --help # list subcommands in the application space +uvx aignostics application run --help # list subcommands in the application run sub-space +uvx aignostics application run list --help # show help for specific command +uvx aignostics application run execute --help # show help for another command +``` + +Check out our +[CLI reference documentation](https://aignostics.readthedocs.io/en/latest/cli_reference.html) +to learn about all commands and options available. + +### System Health Checks + +The CLI automatically checks system health before uploading slides or submitting runs. If the system is unhealthy, the operation is blocked and an error message is displayed: + +``` +Error: Platform is not healthy: . Aborting. +``` + +To override this behavior (not recommended for production use), add the `--force` flag: + +```shell +uvx aignostics application run upload he-tme metadata.csv --force +uvx aignostics application run submit he-tme metadata.csv --force +uvx aignostics application run execute he-tme metadata.csv data/ --force +``` + +To manually check system health before running commands: + +```shell +uvx aignostics system health +``` + +## Python Library: Call the Aignostics Platform API from your Python scripts + +The Python SDK includes the *Aignostics Python Library* for integration with your Python codebase. + +**New to Python Library?** See Installation section above to get started. + + + +### Installation + +Add the Aignostics Python SDK to your Python project: + +**Install with [uv](https://docs.astral.sh/uv/):** + +```shell +uv add aignostics +``` + +**Install with [pip](https://pip.pypa.io/en/stable/):** + +```shell +# Add Python SDK as dependency to your project +pip install aignostics +``` + +#### Usage + +The following snippet shows how to use the Client to submit an application +run: + +```python +from aignostics import platform + +# initialize the client +client = platform.Client() +# submit an application run +application_run = client.runs.submit( + application_id="test-app", + items=[ + platform.InputItem( + external_id="slide-1", + input_artifacts=[ + platform.InputArtifact( + name="whole_slide_image", + download_url="", + metadata={ + "checksum_base64_crc32c": "AAAAAA==", + "resolution_mpp": 0.25, + "width_px": 1000, + "height_px": 1000, + }, + ) + ], + ), + ], +) +# wait for the results and download incrementally as they become available +application_run.download_to_folder("path/to/download/folder") +``` + +Please look at the notebooks in the `example` folder for a more detailed example +and read the +[client reference documentation](https://aignostics.readthedocs.io/en/latest/lib_reference.html) +to learn about all classes and methods. + +### System Health Checks + +The low-level Python SDK does **not** perform automated health checks before operations. If health verification is required for your use case, you should implement checks in your application logic: + +```python +from aignostics import platform +from aignostics.system import Service as SystemService + +# Check system health before submitting runs +health = SystemService().health() +if not health: + raise RuntimeError(f"System is unhealthy: {health.reason}") + +# Proceed with run submission +client = platform.Client() +run = client.runs.submit(...) +``` + +This design gives you full control over health check behavior, allowing you to: + +- Implement custom retry logic for transient failures +- Log health status for monitoring and debugging +- Gracefully handle unhealthy states in your application + +### Example Notebooks: Interact with the Aignostics Platform from your Python Notebook environment + +> [!IMPORTANT] +> Before you get started, you need to set up your authentication credentials if +> you did not yet do so! Please visit +> [your personal dashboard on the Aignostics Platform website](https://platform.aignostics.com/getting-started/quick-start) +> and follow the steps outlined in the `Use in Python Notebooks` section. + +The Python SDK includes ready-to-use Marimo notebooks that demonstrate platform interaction patterns. These notebooks are ideal for: + +- Learning the API through interactive examples +- Prototyping custom analysis workflows +- Integrating with existing data science pipelines + +The example notebooks use our "Test Application" (free for all users). To run them, +please follow the steps outlined in the snippet below to clone this repository and start the +[Marimo](https://marimo.io/) +([examples/notebook.py](https://github.com/aignostics/python-sdk/blob/main/examples/notebook.py)) +notebook: + +```shell +# clone the `python-sdk` repository +git clone https://github.com/aignostics/python-sdk.git +# within the cloned repository, install the SDK and all dependencies +uv sync --all-extras +# show marimo example notebook in the browser +uv run marimo edit examples/notebook.py +``` + +> 💡 You can also run a notebook within the Aignostics Launchpad. To do so, select the +> Run you want to inspect in the left sidebar, and click the button "Open in Python Notebook". + +### Defining the input for an application run + +The following sections provide technical details for advanced use cases. These examples use the "Test Application" - a free application available to all users for testing and development purposes. + +When creating an application run, you need to specify the `application_id` and optionally the +`application_version` (version number) of the application you want to run. If you omit the version, +the latest version will be used automatically. Additionally, you need to define the input items you +want to process in the run. The input items are defined as follows: + +```python +( + platform.InputItem( + external_id="1", + input_artifacts=[ + platform.InputArtifact( + name="whole_slide_image", # defined by the application version's input artifact schema + download_url="", + metadata={ # defined by the application version's input artifact schema + "checksum_base64_crc32c": "N+LWCg==", + "resolution_mpp": 0.46499982, + "width_px": 3728, + "height_px": 3640, + }, + ) + ], + ), +) +``` + +For each item you want to process, you need to provide a unique `reference` +string. This is used to identify the item in the results later on. The +`input_artifacts` field is a list of `InputArtifact` objects, which defines what +data & metadata you need to provide for each item. The required artifacts depend +on the application version you want to run - in the case of test application, +there is only one artifact required, which is the image to process on. The +artifact name is defined as `whole_slide_image` for this application. + +The `download_url` is a signed URL that allows the Aignostics Platform to +download the image data later during processing. + +### Self-signed URLs for large files + +To make the whole slide images you want to process available to the Aignostics Platform, you +need to provide a signed URL that allows the platform to download the data. +Self-signed URLs for files in google storage buckets can be generated using the +`generate_signed_url` +([code](https://github.com/aignostics/python-sdk/blob/407e74f7ae89289b70efd86cbda59ec7414050d5/src/aignostics/client/utils.py#L85)). + +**We expect that you provide the +[required credentials](https://cloud.google.com/docs/authentication/application-default-credentials) +for the Google Storage Bucket** + +## MCP Server: Integrate with AI Agents + +The Python SDK includes an MCP (Model Context Protocol) server that exposes SDK functionality to AI agents like Claude. This enables AI assistants to help you interact with the Aignostics Platform through natural conversation. + +### Quick Start with Claude Desktop + +Add the following to your Claude Desktop configuration file: + +**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` +**Windows**: `%APPDATA%\Claude\claude_desktop_config.json` + +```json +{ + "mcpServers": { + "aignostics": { + "command": "uvx", + "args": ["aignostics", "mcp", "run"] + } + } +} +``` + +Restart Claude Desktop after adding this configuration. + +### CLI Commands + +```bash +# Using uvx (no installation required) +uvx aignostics mcp run +uvx aignostics mcp list-tools +``` + +### Using Plugins + +The MCP server supports plugins that extend its functionality with additional tools. To run the MCP server with a plugin installed: + +```bash +# With a local plugin +uv run --with /path/to/plugin aignostics mcp run + +# With a plugin from a git repository +uvx --with git+ssh://git@github.com/org/plugin aignostics mcp run +``` + +Plugins register themselves via Python entry points and their tools are automatically discovered and namespaced by the MCP server. + +### What AI Agents Can Do + +Once configured, AI agents can help you with platform operations through natural language, with access to tools from the SDK and any installed plugins. + +## Next Steps + +Now that you have an overview of the Aignostics Python SDK and its interfaces, here are some recommended next steps to deepen your understanding and get the most out of the platform: + +- **Understand the platform**: Read the [Aignostics Platform Overview](platform_overview.md) to learn about architecture and core concepts +- **Review detailed documentation**: See the [CLI reference](https://aignostics.readthedocs.io/en/latest/cli_reference.html) and [Python Library reference](https://aignostics.readthedocs.io/en/latest/lib_reference.html) +- **Explore QuPath integration**: Use the QuPath extension to visualize and interact with your results +- **Get support**: Contact [support@aignostics.com](mailto:support@aignostics.com) or check the [full documentation](https://aignostics.readthedocs.io/en/latest/) + + +## Platform + +### Overview + +The **Aignostics Platform** is a comprehensive cloud-based service that allows organizations to leverage advanced computational pathology applications without the need for specialized expertise or complex infrastructure. Via its API it provides a standardized, secure interface for accessing Aignostics' portfolio of advanced computational pathology applications. These applications perform machine learning based tissue and cell analysis on histopathology slides, delivering quantitative measurements, visual representations, and detailed statistical data. + +![Overview](https://raw.githubusercontent.com/aignostics/python-sdk/main/platform.png) + +### Key Features +Aignostics Platform offers key features designed to maximize value for its users: + +1. **Run Aignostics applications:** Run Aignostics advanced computational pathology applications like [Atlas H&E-TME](https://www.aignostics.com/products/he-tme-profiling-product) on your whole slide images (WSIs) and receive results in a easy to inspect formats. +2. **Multiple Access Points:** Interact with the platform via various pathways, from **Aignostics Launchpad** (desktop application for MacOS, Windows and Linux), **Aignostics CLI** (command-line interface for your terminal or shell scripts), **Example Notebooks** (we support Jupyter and Marimo), **Aignostics Client Library** (for integration with your Python codebase), or directly through the **API of the Aignostics Platform** (for integration with any programming language). Contact your business partner at Aignostics if you are interested to discuss a direct integration with your Imaging Management Systems (IMS) and Laboratory Information Management Systems (LIMS). +3. **Secure Data Handling:** Maintain control of your slide data through secure self-signed URLs. Results are automatically deleted after 30 days, and can be deleted earlier by the user. +4. **High-throughput processing with incremental results delivery:** Submit up to 500 whole slide images (WSI) in one batch request. Access results for individual slides as they completed processing, without having to wait for the entire batch to finish. +5. **Standard formats:** Support for commonly used image formats in digital pathology such as pyramidal DICOM, TIFF, and SVS. Results provided in standard formats like QuPath GeoJSON (polygons), TIFF (heatmaps) and CSV (measurements and statistics). + +### Registration and User Access + +To start using the Aignostics Platform and its advanced applications, your organization must be registered by our business support team: + +1. Access to the Aignostics Platform requires a formal business agreement. Once an agreement is in place between your organization and Aignostics, we proceed with your organization's registration. If your organization does not yet have an account, please contact your account manager or email us at [support@aignostics.com](mailto:support@aignostics.com) to express your interest. +2. To register your organization, we require the name and email address of at least one employee, who will be assigned the Administrator role for your organisation. Your organisation's Administrator can invite and manage additional users. + +> [!Important] +> 1. All user accounts must be associated with your organization's official domain. We do not support the registration of private or personal email addresses. +> 2. For security, Two-Factor Authentication (2FA) is mandatory for all user accounts. +> 3. We can integrate with your IDP system (e.g. SAML, OIDC) for user authentication. Please contact us to discuss the integration. +> 4. Registering your organistation typically takes 2 business days depending on the complexity of the signed business agreement and specific requirements. + +### Console + +The web-based [*Aignostics Console*](https://platform.aignostics.com) is a user-friendly interface that allows you to +manage your organization, applications, quotas, and users registered with the Aignostics Platform. + +1. The Console is available to users registered for your organisation to manage their profile and monitor usage of their quota. +2. Administrators of your organization can invite additional users, manage the organisation and user specific quotas and monitor usage. +3. Both roles can trigger application runs. + +### Applications +An application is a fully automated advanced machine learning based workflow composed of one or more specific tasks (e.g. Tissue Quality Control, Tissue Segmentation, Cell Detection, Cell Classification and predictive analysis). Each application is designed for a particular analysis purpose (e.g. Tumor Micro Environment analysis or biomarker scoring). For each application we define input requirements, processing tasks and output formats. + +As contracted in your business agreement with Aignostics your organisation subscribes to one or more applications. The applications are available for your organization in the Aignostics Platform. You can find the list of available applications in the Console of the Aignostics Platform. + +Each application can have multiple versions. Please make sure you read dedicated application documentation to understand its specific constraints regarding acceptable formats, staining method, tissue types and diseases. + +Once registered to the Platform, your organization will automatically gain access to the "Test Application". This application can be used to configure the workflow and to make sure that the integration works correctly. + + +### Application run + +To trigger the application run, users can use the Aignostics Launchpad, Aignostics CLI, Example Notebooks, our Client Library, or directly call the REST API. The platform expects the user payload, containing the metadata and the signed URLs to the whole slide images (WSIs). The detailed requirements of the payload depend on the application and are described in the documentation, and accessible via the Info button in the Launchpad, as well as via the CLI and `/v1/applications` endpoint in the API. + +When the application run is created, it can be in one of the following states: + +1. **received**: the application run received from the client +2. **scheduled**: the application run request is valid and is scheduled for execution +3. **running**: the application run execution started +4. **completed**: the application run execution is done and all outputs are available for download +5. **completed**: the application run execution is done, but some items end up in the failed state +6. **rejected**: the application run request is rejected before it is scheduled +7. **cancelled by the system**: the application run failed during the execution with the number of errors higher than the threshold +9. **cancelled by the user**: the application run is cancelled by the user before it is finished + +The status and operations of an application run are private to the user who triggered the run. + +### Results +When the processing of whole slide image is successfully completed, the resulting outputs become available for download. To assess specifics of application outputs please consult our application specific documentation, which you can find in the **Console**. Please note that you access to documentation is restricted to those applications your organisation subscribed to. + +Application run outputs are automatically deleted 30 days after the application run has completed. However, the owner of the application run (the user who initiated it) can use the API to manually delete outputs earlier, once the run has reached a final state - completed, cancelled by the system or cancelled by the user. The Launchpad and CLI provide enable to delete results with one click resp. command. + +### Quotas +Every organization has a limit on how many WSIs it can process in a calendar month. The following quotas exist: + +1. **Per organization**: as defined in your business agreement with Aignostics +2. **Per user**: defined by your organization Admin + +When the per month quota is reached, an application run request is denied. + +Other limitations may apply to your organization: + +1. Allowed number of users an organization can register +2. Allowed number of images user can submit per application run +3. Allowed number of parallel application runs for the whole organization + +Additionally, we allow organization Admin to define following limitations for its users: + +1. Maximum number of images the user can process per calendar month. +2. Maximum number of parallel application runs for a given user + +Visit the [Console](https://platform.aignostics.com) to check your current quota and usage. The Console provides a clear overview of the number of images processed by your organization and by each user, as well as the remaining quota for the current month. + +### API + +The **Aignostics Platform API** is a RESTful web service that allows you to interact with the platform programmatically. It provides endpoints for submitting whole slide images (WSIs) for analysis, checking the status of application runs, and retrieving results. + +You can interact with the API using the Python client, which is a wrapper around the RESTful API. The Python client simplifies the process of making requests to the API and handling responses. It also provides convenient methods for uploading WSIs, checking application run status, and downloading results. + +For integration with programming languages other than Python, you can use the RESTful API directly. The API is designed to be language-agnostic, meaning you can use any programming language that supports HTTP requests to interact with it. This includes languages like Java, Kotlin, C#, Ruby, and Typescript. + +### Cost + +Every WSI processed by the Platform generates a cost. Usage of the "Test Application" is free of charge for any registered user. The cost for other applications is defined in your business agreement with Aignostics. The cost is calculated based on the number of slides processed. When an application run is cancelled, either by the system or by the user, only processed images incur a cost. + +**[Read the API reference documentation](https://aignostics.readthedocs.io/en/latest/api_reference_v1.html)** or use our **[Interactive API Explorer](https://platform.aignostics.com/explore-api)** to dive into details of all operations and parameters. + + +## Further Reading + +1. Inspect our + [security policy](https://aignostics.readthedocs.io/en/latest/security.html) + with detailed documentation of checks, tools and principles. +1. Inspect how we achieve + [operational excellence](https://aignostics.readthedocs.io/en/latest/operational_excellence.html) + with information on our modern toolchain and software architecture. +2. Check out the + [CLI reference](https://aignostics.readthedocs.io/en/latest/cli_reference.html) + with detailed documentation of all CLI commands and options. +3. Check out the + [library reference](https://aignostics.readthedocs.io/en/latest/lib_reference.html) + with detailed documentation of public classes and functions. +4. Check out the + [API reference](https://aignostics.readthedocs.io/en/latest/api_reference_v1.html) + with detailed documentation of all API operations and parameters. See as well + the OpenAPI Specification in [JSON](https://github.com/aignostics/python-sdk/blob/main/docs/source/_static/openapi_v1.json) and [YAML](https://github.com/aignostics/python-sdk/blob/main/docs/source/_static/openapi_v1.yaml), and the [API Explorer](https://aignostics.readthedocs.io/en/latest/api_explorer_v1.html). +5. Our + [release notes](https://aignostics.readthedocs.io/en/latest/release-notes.html) + provide a complete log of recent improvements and changes. +6. We gratefully acknowledge the numerous + [open source projects](https://aignostics.readthedocs.io/en/latest/attributions.html) + that this project builds upon. Thank you to all these wonderful contributors! + + +## Glossary + +### A + +**Administrator Role** +A user role within an organization that has permissions to invite and manage additional users, define user-specific quotas, and monitor organizational usage. + +**Aignostics CLI** +Command-Line Interface that allows interaction with the Aignostics Platform directly from terminal or shell scripts, enabling dataset management and application runs. + +**Aignostics Client Library** +Python library for seamless integration of the Aignostics Platform with enterprise image management systems and scientific workflows. + +**Aignostics Console** +Web-based user interface for managing organizations, applications, quotas, users, and monitoring platform usage. + +**Aignostics Launchpad** +Graphical desktop application (available for Mac OS X, Windows, and Linux) that allows users to run computational pathology applications on whole slide images and inspect results with QuPath and Python Notebooks. + +**Aignostics Platform** +Comprehensive cloud-based service providing standardized, secure interface for accessing advanced computational pathology applications without requiring specialized expertise or complex infrastructure. + +**Aignostics Platform API** +RESTful web service that allows programmatic interaction with the Aignostics Platform, providing endpoints for submitting WSIs, checking application run status, and retrieving results. + +**Aignostics Python SDK** +Software Development Kit providing multiple pathways to interact with the Aignostics Platform, including the Launchpad, CLI, Client Library, and example notebooks. + +**Application** +Fully automated advanced machine learning workflow composed of specific tasks (e.g., Tissue Quality Control, Tissue Segmentation, Cell Detection, Cell Classification) designed for particular analysis purposes. + +**Application Run** +The execution instance of an application on submitted whole slide images, which can be in various states: received, scheduled, running, completed, rejected, cancelled by system, or cancelled by user. + +**Application Version** +Specific version of an application with defined input requirements, processing tasks, and output formats. Each application can have multiple versions. + +**Atlas H&E-TME** +Advanced computational pathology application for Hematoxylin and Eosin-stained Tumor Microenvironment analysis. + +### B + +**Base MPP (Microns Per Pixel)** +Metadata parameter specifying the resolution of whole slide images, indicating the physical distance represented by each pixel. + +**Business Agreement** +Formal contract between an organization and Aignostics required for platform access, defining quotas, applications, and terms of service. + +### C + +**Checksum CRC32C** +Cyclic Redundancy Check used to verify data integrity of uploaded whole slide images. + +**Client** +The main class in the Aignostics Python SDK used to initialize connections and interact with the platform API. + +**Computational Pathology** +Field combining digital pathology with artificial intelligence and machine learning to analyze histopathology slides quantitatively. + +**Aignostics Console** +Web-based user interface for managing organizations, applications, quotas, users, and monitoring platform usage. + +### D + +**DICOM (Digital Imaging and Communications in Medicine)** +Standard format for medical imaging data, supported by the Aignostics Platform for whole slide images. + +**Download URL** +Signed URL that allows the Aignostics Platform to securely download image data during processing. + +### G + +**GeoJSON** +Standard format used by QuPath for representing polygonal annotations and results. + +**Google Storage Bucket** +Cloud storage service where users can store whole slide images and generate signed URLs for platform access. + +### H + +**H&E (Hematoxylin and Eosin)** +Common histological staining method for tissue visualization, used in Atlas H&E-TME application. + +**Heatmaps** +Visual representations of analysis results provided in TIFF format showing spatial distribution of measurements. + +### I + +**IDC (NCI Image Data Commons)** +Public repository of medical imaging data that can be queried and downloaded through the Aignostics CLI. + +**IMS (Imaging Management Systems)** +Enterprise systems for managing medical imaging data that can be integrated with the Aignostics Platform. + +**Input Artifact** +Data object required for application processing, including the actual data file and associated metadata. + +**Input Item** +Individual unit of processing in an application run, containing one or more input artifacts with a unique reference identifier. + +**Interactive API Explorer** +Tool for exploring and testing API endpoints and parameters interactively. + +### J + +**Jupyter** +Popular notebook environment supported by the Aignostics Platform for interactive analysis and visualization. + +### L + +**LIMS (Laboratory Information Management Systems)** +Laboratory systems that can be integrated with the Aignostics Platform for workflow automation. + +### M + +**Marimo** +Modern notebook environment supported by the Aignostics Platform as an alternative to Jupyter. + +**MCP (Model Context Protocol)** +Protocol that enables AI agents like Claude to interact with external tools and services. The Aignostics SDK includes an MCP server that exposes platform functionality to AI assistants. + +**Metadata** +Descriptive information about whole slide images including dimensions, resolution, tissue type, and disease information required for processing. + +**MPP (Microns Per Pixel)** +See Base MPP. + +### N + +**NCI Image Data Commons (IDC)** +See IDC. + +### O + +**Operational Excellence** +Aignostics' commitment to high-quality software development practices including A-grade code quality, security scanning, and comprehensive documentation. + +### P + +**Pyramidal** +Multi-resolution image format that stores the same image at different zoom levels for efficient viewing and processing. + +**Python SDK** +Software Development Kit providing multiple pathways to interact with the Aignostics Platform through Python programming language. + +### Q + +**QuPath** +Open-source software for bioimage analysis that can be launched directly from the Aignostics Launchpad to view results. + +**Quota** +Limit on the number of whole slide images an organization or user can process per calendar month, as defined in business agreements. + +### R + +**Reference** +Unique identifier string for each input item in an application run, used to match results with original inputs. + +**Results** +Output data from application processing, including measurements, statistics, heatmaps, and annotations, automatically deleted after 30 days. + +**RESTful API** +Architectural style for web services that the Aignostics Platform API follows, enabling language-agnostic integration. + +### S + +**Self-signed URLs** +Secure URLs with embedded authentication that allow the platform to access user data without exposing credentials. + +**SVS** +Aperio ScanScope Virtual Slide format, commonly used for whole slide images and supported by the platform. + +**System Health Check** +Automated verification that the SDK and Aignostics Platform are operational before critical operations. The Launchpad blocks run submission when unhealthy (no override available for regular users). The CLI blocks uploads and submissions by default but allows override with `--force`. The Python Library does not perform automatic health checks, giving developers full control over health verification logic. + +### T + +**Test Application** +Free application automatically available to all registered organizations for workflow configuration and integration testing. + +**TIFF (Tagged Image File Format)** +Standard image format supported for both input whole slide images and output heatmaps. + +**Tissue Segmentation** +Computational process of identifying and delineating different tissue regions within histopathology slides. + +**TME (Tumor Microenvironment)** +The cellular environment surrounding tumor cells, analyzed by the Atlas H&E-TME application. + +**Two-Factor Authentication (2FA)** +Mandatory security requirement for all user accounts on the Aignostics Platform. + +### U + +**UV** +Modern Python package manager used for dependency management and project setup in the SDK documentation. + +**UVX** +Tool for running Python applications directly without explicit installation, used to execute Aignostics CLI commands. + +### W + +**Whole Slide Image (WSI)** +High-resolution digital image of an entire histopathology slide, the primary input format for computational pathology applications. + +**Workflow** +Sequence of automated processing steps within an application that transform input images into analytical results. diff --git a/packages/aignostics-sdk/pyproject.toml b/packages/aignostics-sdk/pyproject.toml new file mode 100644 index 000000000..cedf21898 --- /dev/null +++ b/packages/aignostics-sdk/pyproject.toml @@ -0,0 +1,138 @@ +[project] +name = "aignostics-sdk" +version = "1.4.3" +description = "🔬 Python SDK providing access to the Aignostics Platform. Includes Aignostics Launchpad (Desktop Application), Aignostics CLI (Command-Line Interface), example notebooks, and Aignostics Client Library." +readme = "README.md" +authors = [ + { name = "Helmut Hoffer von Ankershoffen", email = "helmut@aignostics.com" }, + { name = "Andreas Kunft", email = "andreas@aignostics.com" }, +] +license = { file = "LICENSE" } + +keywords = [ + "aignostics", + "atlas", + "whole-slide-imaging", + "machine-learning", + "digital-pathology", + "medical-imaging", + "qupath", + "dicom", + "pydicom", + "openslide", + "image-data-commons", + "act", + "codecov", + "copier", + "cyclonedx", + "devcontainer", + "detect-secrets", + "docker", + "git-cliff", + "jupyter", + "marimo", + "mypy", + "nox", + "nicegui", + "oe-python-template", + "pip-audit", + "pip-licenses", + "pre-commit", + "pydantic", + "pytest", + "python", + "pypi", + "readthedocs", + "ruff", + "sonarqube", + "sonarcloud", + "sphinx", + "typer", + "uv", +] + +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Intended Audience :: End Users/Desktop", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "License :: OSI Approved :: MIT License", + "Operating System :: MacOS :: MacOS X", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Framework :: Pydantic", + "Framework :: Pytest", + "Typing :: Typed", + "Natural Language :: English", +] + +# WARNING: The upper bound of requires-python is *not* respected by uvx +requires-python = ">=3.11, <3.15" + +dependencies = [ + # From Template + "fastapi[all,standard]>=0.123.10", + "nicegui[native]>=3.11.0,<4", # CVE-2026-21871, CVE-2026-21873, CVE-2026-21874 (>=3.5.0); CVE-2026-25516 (>=3.7.0, #418); CVE-2026-27156 (>=3.8.0, #448); CVE-2026-33332 (>=3.9.0, #498); CVE-2026-39844 (>=3.10.0, #531). 3.11.0 fixes async event handler exception leaks and refines ValueChangeEventArguments generics. + "platformdirs>=4.5.1,<5", + "psutil>=7.1.3,<8", + "pydantic-settings>=2.12.0,<3", + "pywin32>=311,<312; sys_platform == 'win32'", + "sentry-sdk>=2.47.0,<3", + "typer>=0.20.0,<1", + # Custom — slim platform + utils deps + "certifi>=2025.11.12", + "crc32c>=2.8,<3", # used in platform/_utils.py for file upload checksums + "fastmcp>=3.2.0,<4", + "httpx>=0.28.1,<1", + "jsonschema[format-nongpl]>=4.25.1,<5", + "jsf>=0.11.2,<1", + "loguru>=0.7.3,<1", + "pyjwt[crypto]>=2.12.0,<3", # CVE-2026-32597 requires >=2.12.0 (Renovate #475) + "requests>=2.33.0,<3", # CVE-2026-25645 requires >= 2.33.0 + "requests-oauthlib>=2.0.0,<3", + "humanize>=4.14.0,<5", # used in application/_utils.py for human-readable sizes + "pyyaml>=6.0.3,<7", # used in system/_cli.py for YAML output + "semver>=3.0.4,<4", + "tenacity>=9.1.2,<10", + "tqdm>=4.67.1,<5", + "truststore>=0.10.4,<1", + "urllib3>=2.6.3,<3", # CVE-2026-21441 requires >= 2.6.3 + # Transitive CVE overrides for slim deps + # WARNING: one cannot negate or downgrade a dependency required here. use override-dependencies for that. + "rfc3987; sys_platform == 'never'", # GPLv3 + "h11>=0.16.0", # CVE-2025-43859 + "starlette>=1.0.1", # CVE-2025-54121, GHSA-7f5h-v6xp-fcq8, PYSEC-2026-161 + "pillow>=12.2.0", # CVE-2025-48379 (>=11.3.0); CVE-2026-25990 (>=12.1.1); CVE-2026-40192 (>=12.2.0) + "pygments>=2.20.0", # CVE-2026-4539 (>=2.20.0); transitive via rich + "cryptography>=46.0.7", # CVE-2026-39892 (>=46.0.7); transitive via pyjwt[crypto] + "pyasn1>=0.6.3", # CVE-2026-30922 (>=0.6.3); transitive via cryptography + "python-multipart>=0.0.26", # CVE-2026-24486 (>=0.0.22), CVE-2026-40347 (>=0.0.26); transitive via fastapi/starlette + "tornado>=6.5.5", # CVE-2025-47287 (>=6.5.0); GHSA-78cv-mqj4-43f7 (>=6.5.5) + "filelock>=3.20.3", # CVE-2025-68146 (>=3.20.1); CVE-2026-22701 (>=3.20.3) +] +[project.scripts] +aignostics-sdk = "aignostics_sdk.cli:cli" + +[project.entry-points."aignostics.cli"] +user = "aignostics_sdk.platform._cli:cli_user" +sdk = "aignostics_sdk.platform._cli:cli_sdk" +application = "aignostics_sdk.application._cli:cli" +system = "aignostics_sdk.system._cli:cli" + +[build-system] +requires = ["hatchling==1.29.0"] +build-backend = "hatchling.build" + +[tool.hatch.build] +include = ["src/*"] + +[tool.hatch.build.targets.wheel] +packages = ["src/aignostics_sdk", "src/aignx"] + +[tool.hatch.metadata] +allow-direct-references = true diff --git a/packages/aignostics-sdk/src/aignostics_sdk b/packages/aignostics-sdk/src/aignostics_sdk new file mode 120000 index 000000000..1c8bab11e --- /dev/null +++ b/packages/aignostics-sdk/src/aignostics_sdk @@ -0,0 +1 @@ +../../../src/aignostics_sdk \ No newline at end of file diff --git a/packages/aignostics-sdk/src/aignx b/packages/aignostics-sdk/src/aignx new file mode 120000 index 000000000..6841fb003 --- /dev/null +++ b/packages/aignostics-sdk/src/aignx @@ -0,0 +1 @@ +../../../codegen/out/aignx \ No newline at end of file diff --git a/packages/aignostics/LICENSE b/packages/aignostics/LICENSE new file mode 100644 index 000000000..75e0868b4 --- /dev/null +++ b/packages/aignostics/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [2025] [Aignostics GmbH (support@aignostics.com)] + +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. diff --git a/packages/aignostics/README.md b/packages/aignostics/README.md new file mode 100644 index 000000000..b728aa1cc --- /dev/null +++ b/packages/aignostics/README.md @@ -0,0 +1,1015 @@ + +[//]: # (README.md generated from docs/partials/README_*.md) + +# 🔬Aignostics Python SDK + +[![License](https://img.shields.io/github/license/aignostics/python-sdk?logo=opensourceinitiative&logoColor=3DA639&labelColor=414042&color=A41831)](https://github.com/aignostics/python-sdk/blob/main/LICENSE) +[![Python Version](https://img.shields.io/pypi/pyversions/aignostics.svg?logo=python&color=204361&labelColor=1E2933)](https://pypi.org/project/aignostics/) +[![CI/CD](https://github.com/aignostics/python-sdk/actions/workflows/ci-cd.yml/badge.svg?branch=main)](https://github.com/aignostics/python-sdk/actions/workflows/ci-cd.yml) +[![Docs](https://img.shields.io/readthedocs/aignostics)](https://aignostics.readthedocs.io/en/latest/) +[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=aignostics_python-sdk&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=aignostics_python-sdk) +[![Security](https://sonarcloud.io/api/project_badges/measure?project=aignostics_python-sdk&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=aignostics_python-sdk) +[![Maintainability](https://sonarcloud.io/api/project_badges/measure?project=aignostics_python-sdk&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=aignostics_python-sdk) +[![Coverage](https://codecov.io/gh/aignostics/python-sdk/graph/badge.svg?token=SX34YRP30E)](https://codecov.io/gh/aignostics/python-sdk) +[![Uptime](https://uptime.betterstack.com/status-badges/v2/monitor/1wbqa.svg)](https://aignostics.betteruptime.com) + +## Introduction + +The **Aignostics Python SDK** provides multiple ways to interact with the **Aignostics Platform** for running advanced computational pathology applications like [Atlas H&E-TME](https://www.aignostics.com/products/he-tme-profiling-product), which analyzes tumor microenvironments in H&E-stained tissue samples. + +### We take quality and security seriously + +We know you take **quality** and **security** as seriously as we do. That's why +the Aignostics Python SDK is built following best practices and with full +transparency. This includes (1) making the complete +[source code of the SDK +available on GitHub](https://github.com/aignostics/python-sdk/), maintaining a +(2) +[A-grade code quality](https://sonarcloud.io/summary/new_code?id=aignostics_python-sdk) +with [high test coverage](https://app.codecov.io/gh/aignostics/python-sdk) in +all releases, (3) achieving +[A-grade security](https://sonarcloud.io/summary/new_code?id=aignostics_python-sdk) +with +[active scanning of dependencies](https://github.com/aignostics/python-sdk/issues/4), +and (4) providing +[extensive documentation](https://aignostics.readthedocs.io/en/latest/). Read +more about how we achieve +[operational excellence](https://aignostics.readthedocs.io/en/latest/operational_excellence.html) and +[security](https://aignostics.readthedocs.io/en/latest/security.html). + +## Installation + +The **Aignostics Python SDK** can be installed via the [uv package manager](https://docs.astral.sh/uv/). The installation process sets up the SDK along with the necessary dependencies, including the **uv** package manager itself if not already present. + +Before proceeding, ensure you have an **Aignostics Platform account**. You can get access either through your organization admin (if your organization has an Aignostics account) or directly from Aignostics. Check your email for an invitation before proceeding. + +### Requirements + +- **Python 3.11, 3.12, 3.13, or 3.14** +- **macOS 11.0+, Linux, or Windows** +- **Homebrew** (only if you previously installed `uv` via Homebrew) + +### Installation Steps + +The installation will: + +1. Install or update **uv** (Python package installer) +2. Install the **Aignostics Python SDK** (includes Launchpad, CLI, and Python Library) + +Copy and paste the appropriate command below into your terminal (macOS/Linux) or PowerShell (Windows): + +**Linux/macOS:** + +```bash +if ! command -v uv &> /dev/null; then + echo "uv not found, installing..." + curl -LsSf https://astral.sh/uv/install.sh | sh + source $HOME/.local/bin/env +else + UV_VERSION=$(uv --version | cut -d' ' -f2) + if [ "$(printf '%s\n' "0.6.17" "$UV_VERSION" | sort -V | head -n1)" != "0.6.17" ]; then + echo "Updating uv to the latest version..." + UV_PATH=$(which uv) + if [[ "$UV_PATH" == *"brew"* ]]; then + echo "Updating uv using Homebrew..." + brew upgrade uv + else + echo "Updating uv using the installer..." + uv self update + fi + else + echo "uv is up to date" + fi +fi +``` + +**Windows (PowerShell):** + +```powershell +winget install --id=Microsoft.VCRedist.2015+.x64 -e +powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" +``` + +Verify your installation by running: + +```bash +uvx aignostics --help +``` + +You should see the Aignostics CLI help output. + +You can then proceed by choosing your preferred user interface below. + +## Platform Workflow Overview + +The Aignostics Platform delivers enterprise-grade computational pathology through a secure, scalable cloud architecture. Organizations subscribe to the platform, and their users interact through three interfaces - all part of the Python SDK - to leverage advanced AI/ML models running on dedicated NVIDIA® GPU infrastructure. + +**Key architectural components:** + +- **Python SDK**: Provides three user interfaces (Launchpad desktop app, CLI, and Client Library) with unified functionality +- **Enterprise authentication**: Powered by Auth0, supporting Single Sign-On (SSO) and existing identity management systems +- **Organization storage**: Dedicated Google Cloud Storage bucket per organization with automatic 30-day cleanup +- **Aignostics Platform API**: Orchestrates application discovery, run submission, status monitoring, and results delivery +- **NVIDIA® GPU clusters**: Dedicated compute provisioned per application run for maximum security and compliance + +```mermaid +%%{init: {'theme':'dark', 'themeVariables': { 'fontSize':'18px', 'fontFamily':'arial', 'darkMode':'true', 'background':'#1e1e1e', 'primaryColor':'#4a4a4a', 'primaryTextColor':'#ffffff', 'primaryBorderColor':'#ffffff', 'lineColor':'#ffffff', 'secondaryColor':'#3a3a3a', 'tertiaryColor':'#2a2a2a', 'actorBkg':'#4a4a4a', 'actorBorder':'#ffffff', 'actorTextColor':'#ffffff', 'actorLineColor':'#ffffff', 'signalColor':'#ffffff', 'signalTextColor':'#ffffff', 'labelBoxBkgColor':'#3a3a3a', 'labelBoxBorderColor':'#ffffff', 'labelTextColor':'#ffffff', 'noteBkgColor':'#4a4a4a', 'noteTextColor':'#ffffff', 'noteBorderColor':'#ffffff', 'sequenceNumberColor':'#000000'}}}%% +sequenceDiagram + autonumber + actor User as User
(Organization Member) + participant SDK as Python SDK
(Launchpad/CLI/Client Library) + participant Auth0 as Auth0
(Enterprise Identity) + participant Bucket as Organization Bucket
(Google Cloud Storage) + participant API as Aignostics Platform API + participant GPU as NVIDIA® GPU Cluster
(per-run isolation) + + Note over User,GPU: Authentication & Authorization + User->>SDK: Launch interface + SDK->>Auth0: Authenticate user + Auth0-->>SDK: Access token + SDK->>API: Validate token + API-->>SDK: User authorized + + Note over User,GPU: Application Selection + User->>SDK: Browse applications + SDK->>API: List applications & versions + API-->>SDK: Application catalog + SDK-->>User: Display options + + Note over User,GPU: Data Upload + User->>SDK: Select WSIs + metadata + SDK->>Bucket: Upload files + Note over Bucket: 30-day auto-cleanup + Bucket-->>SDK: Upload complete + SDK->>SDK: Generate signed download URLs + + Note over User,GPU: Run Submission + SDK->>API: Submit run (app, metadata, signed URLs) + API-->>SDK: Run ID + queue position + SDK-->>User: Confirm submission + + Note over User,GPU: GPU Processing + API->>GPU: Provision dedicated NVIDIA® cluster + GPU->>Bucket: Download WSIs via signed URLs + GPU->>GPU: Process slides incrementally + GPU->>API: Upload results per slide + Note over GPU: Deprovision after completion + + Note over User,GPU: Status Monitoring & Results + User->>SDK: Check status + SDK->>API: Poll run status + API-->>SDK: Progress (e.g., "3 of 10 complete") + SDK-->>User: Display progress + + User->>SDK: Download results + SDK->>API: Request result URLs + API-->>SDK: Signed download URLs + SDK->>API: Download files (GeoJSON, CSV, TIFF) + SDK-->>User: Results ready for inspection +``` + +**How it works:** + +Organizations subscribe to the Aignostics Platform and receive dedicated infrastructure including a Google Cloud Storage bucket and API access. Users within the organization authenticate through Auth0, which integrates with enterprise identity management systems for seamless Single Sign-On (SSO). + +The Python SDK - available as a desktop application (Launchpad), command-line interface (CLI), or programmable library (Client Library) - handles all complexity of authentication, data upload, run orchestration, and results delivery. Users simply select an application, provide whole slide images with metadata, and submit. + +Behind the scenes, the Aignostics Platform API provisions dedicated NVIDIA® GPU clusters for each application run, ensuring data isolation and compliance with healthcare regulations. Processing occurs incrementally (slide-by-slide), allowing users to monitor progress and download results as they become available rather than waiting for entire cohorts. + +The organization's Google Cloud Storage bucket stores uploaded files with automatic 30-day cleanup, optimizing costs while maintaining data availability throughout processing. All data transfers use time-limited signed URLs, eliminating credential management complexity and security risks. + +**Enterprise benefits:** + +- **Security & compliance**: Per-run GPU isolation, enterprise SSO integration, zero-trust architecture with signed URLs +- **Scalability**: Handles single exploratory slides through thousand-slide clinical studies with identical user experience +- **Cost efficiency**: Pay-per-use GPU provisioning, automatic storage cleanup, no idle infrastructure costs +- **Operational simplicity**: Python SDK abstracts all cloud complexity; IT teams manage access through existing identity systems + +## Choose your interface + +Choose your preferred interface for working with the Aignostics Platform. Each interface is designed for different user roles and use cases: + +### 🖥️ Launchpad (Desktop Application) + +| | | +|---|---| +| **What it is** | Graphical application for analyzing slides and viewing results in QuPath or Python notebooks | +| **Best for** | Pathologists and researchers who want to analyze slides without writing code | +| **Use when** | Running analyses on individual cases or small cohorts (1-20 slides) and exploring results interactively | +| **Get started** |
Install and run your first analysis | + +### ⌨️ CLI (Command-Line Interface) + +| | | +|---|---| +| **What it is** | Terminal tool for scripting and automation | +| **Best for** | Bioinformaticians and technical researchers who work with terminal-based workflows | +| **Use when** | Processing large cohorts (10s-100s of slides), automating repetitive analyses, or integrating with computational pipelines | +| **Get started** | Manage datasets and application runs from your terminal | + +### 📚 Python Library + +| | | +|---|---| +| **What it is** | Python library for programmatic access in scripts, notebooks, and applications | +| **Best for** | Data scientists and developers who want to integrate the platform into Python-based workflows | +| **Use when** | Building custom analysis pipeline in Python for repeated usage and processing large datasets (10s-1000s of slides) | +| **Get started** | Run example notebooks or call the Aignostics Platform API from your Python scripts | + +### 🤖 MCP Server (AI Agent Integration) + +| | | +|---|---| +| **What it is** | Model Context Protocol server that exposes SDK functionality to AI agents like Claude | +| **Best for** | Users who want AI assistants to help with platform operations | +| **Use when** | Working with Claude Desktop or other MCP-compatible AI tools to manage datasets, submit runs, or query results | +| **Get started** | Configure Claude Desktop for MCP integration | + +> 💡 Launchpad and CLI handle authentication automatically. Python Library requires manual setup (see [authentication section](#example-notebooks-interact-with-the-aignostics-platform-from-your-python-notebook-environment)). + +## Launchpad: Run your first computational pathology analysis in 10 minutes from your desktop + +The **Aignostics Launchpad** is a graphical desktop application that allows you to run applications on whole slide images (WSIs) from your computer, and inspect results with QuPath and Python Notebooks with one click. It is designed to be user-friendly and intuitive, for use by Research Pathologists and Data Scientists. + +**New to Launchpad?** See Installation section above to get started. + +### Running Your First Analysis + +This tutorial uses [Atlas H&E-TME](https://www.aignostics.com/products/he-tme-profiling-product) with a public lung cancer dataset from the NCI Image Data Commons. + +**Step 1: Start Aignostics Launchpad** + +1. Open a terminal or command prompt +2. Run the command: `uvx aignostics launchpad` +This starts the Launchpad application. + +**Step 2: Download a Sample Dataset** + +1. Click the menu icon (☰) in the top right corner +2. Click "Download Datasets". The system displays the dataset download interface. +3. Click "EXAMPLE DATASET". The system populates the dataset ID field with a TCGA lung adenocarcinoma sample. +4. Click "DATA". The system shows a folder selection dialog. +5. Click "OK" +6. Click "DOWNLOAD". The system downloads the DICOM dataset. A progress indicator shows download status. +7. Click the menu icon and select "Run Applications" + +**Step 3: Select Atlas H&E-TME** + +1. Click "Atlas H&E-TME" in the left sidebar. The system displays the application workflow with six steps. +2. Click the version dropdown to view available versions. The system shows all available versions with release notes accessible via the "RELEASE NOTES" button. +3. Keep the default version (latest) +4. Click "NEXT" + +**Step 4: Select Slides and Provide Metadata** + +1. Click "DATA". The system opens a folder selection dialog showing the Launchpad datasets directory. +2. Navigate to the downloaded dataset folder (e.g., `/datasets/idc/tcga_luad/`) +3. Click "OK". The system displays the selected folder path and scans the folder, showing a table with all compatible slides. Each row shows thumbnail preview, technical metadata (file size, MPP resolution, dimensions), and status indicators. +4. The system automatically extracts technical file metadata. You must provide the required medical metadata by double-clicking the red cells in the "Tissue" column. The system displays a dropdown menu with tissue types. +5. Select the tissue type (e.g., "LUNG") and disease (e.g., "LUNG_CANCER") by double-clicking in the red cells and selecting the value from the dropdown. The system marks these cells green indicating valid metadata. +6. Review the "Staining" column. The system shows "H&E" if this information was extracted from the DICOM file. +7. Click "NEXT" + +**Step 5: Add Notes and Tags (Optional)** + +1. The system displays the notes and tags screen. +2. Enter an optional note in the text field (e.g., "TCGA lung sample analysis") +3. Add optional tags by typing and pressing Enter (e.g., "TCGA", "lung") +4. Click "NEXT" + +**Step 6: Set Schedule (Optional)** + +1. The system displays scheduling options with soft due date and hard deadline pickers. +2. Click "NEXT" to leave the default settings. + +The soft due date indicates when the platform will attempt to complete processing. The hard deadline is when the platform may cancel the run if resources are unavailable. + +**Step 7: Submit Your Run** + +1. The system displays the submission screen showing number of slides to be analyzed, full file paths, and upload and submit button. +2. Review the slide information +3. Click "UPLOAD AND SUBMIT". The system uploads your slides to the Aignostics Platform and submits the analysis run. A progress indicator shows upload status. + +The left sidebar now shows your submitted run with application name and version, submission timestamp, running status icon (🏃), and any tags you added. + +**Step 8: Monitor Your Run** + +Atlas H&E-TME processing time depends on slide size and system load. Depending on the file size and the number of files, processing can take minutes to many hours. + +Click on your run in the sidebar to view run details and metadata, slide thumbnails, and processing status for each slide. The status icon updates as processing progresses. + +### Understanding Your Results + +When processing completes, Atlas H&E-TME provides comprehensive tumor microenvironment analysis results for each processed slide: + +**What You'll Receive:** + +- **Tissue analysis**: Identification of tissue regions (tumor, stroma, necrosis, etc.) with quality assessment in GeoJSON format +- **Cell analysis**: Individual cells detected and classified by type (tumor cells, immune cells, stromal cells, etc.) in GeoJSON format +- **Visual segmentation maps**: Color-coded images showing spatial distribution of tissue and cell types +- **Quantitative measurements**: Cell counts, densities, spatial relationships, and statistical summaries provided in CSV format + +**Downloading Results:** + +When processing completes, the status icon changes to show completion. To download results: + +1. Click the "Download Results" button +2. The system downloads a ZIP file containing all outputs to your computer + +**Inspecting Results in QuPath:** + +QuPath integration provides the most powerful way to visualize and interact with your results: + +1. Click "Open in QuPath" (requires QuPath extension - see Advanced Setup below) +2. The system automatically creates a QuPath project with your slides and annotations loaded +3. In QuPath, you can: + - View tissue and cell annotations overlaid on your slides + - Explore cell classifications and measurements + - Analyze spatial relationships between different cell types + - Export annotations or perform additional analysis + +**Congratulations!** You have successfully downloaded a public dataset, submitted an Atlas H&E-TME analysis run, and learned how to access and inspect your results. + +### System Health Checks + +The Launchpad automatically monitors system health before allowing run submissions. If the system is unhealthy (e.g., network connectivity issues, authentication problems, or platform unavailability), the submission workflow is blocked: + +- A tooltip displays "System is unhealthy, you cannot prepare a run at this time." +- The "Next" button in the application workflow is disabled. +- The health status is shown in the footer bar at the bottom of the Launchpad. + +To resolve health issues: + +1. Check the health status indicator in the footer bar +2. Click "Info and Settings" in the menu to see detailed health information +3. Verify your network connection and authentication status +4. Check the [Aignostics Platform Status](https://status.aignostics.com) page + +### Advanced Setup: Extensions + +> 💡 The Launchpad features a growing ecosystem of extensions that seamlessly integrate with standard digital pathology tools. To use the Launchpad with all available extensions, run `uvx --from "aignostics[qupath,marimo]" aignostics launchpad`. Currently available extensions are: +> +> 1. **QuPath extension**: View your application results in [QuPath](https://qupath.github.io/) with a single click. The Launchpad creates QuPath projects on-the-fly. +> 2. **Marimo extension**: Analyze your application results using [Marimo](https://marimo.io/) notebooks embedded in the Launchpad. You don't have to leave the Launchpad to do real data science. + +## CLI: Manage datasets and application runs from your terminal + +The Python SDK includes the **Aignostics CLI**, a Command-Line Interface (CLI) that allows you to +interact with the Aignostics Platform directly from your terminal or shell script. + +**New to CLI?** See Installation section above to get started. + +**Common workflows:** + +- Download public datasets from NCI Image Data Commons +- Submit batch processing runs for multiple slides +- Monitor run status and download results incrementally +- Automate repetitive tasks with shell scripts + +See as follows for a simple example where we download a sample dataset for the [Atlas +H&E-TME application](https://www.aignostics.com/products/he-tme-profiling-product), submit an application run, and download the results. + +### Example: Running Atlas H&E-TME with CLI + +1. Open a terminal or command prompt +2. Use the following commands to run the Atlas H&E-TME application on a sample dataset: + +```shell +# Download a sample dataset from the NCI Image Data Commons (IDC) portal to your current working directory +# As the dataset id refers to the TCGA LUAD collection, this creates a directory tcga_luad with the DICOM files +uvx aignostics dataset idc download 1.3.6.1.4.1.5962.99.1.1069745200.1645485340.1637452317744.2.0 data/ +# Prepare the metadata for the application run by creating a metadata.csv, extracting +# the required metadata from the DICOM files. We furthermore add the required +# information about the tissue type and disease. +uvx aignostics application run prepare he-tme data/tcga_luad/run.csv data/ +# Edit the metadata.csv to insert the required information about the staining method, tissue type and disease +# Adapt to your favourite editor +nano tcga_luad/metadata.csv +# Upload the metadata.csv and referenced whole slide images to the Aignostics Platform +uvx aignostics application run upload he-tme data/tcga_luad/run.csv +# Submit the application run and print the run id +uvx aignostics application run submit he-tme data/tcga_luad/run.csv +# Check the status of the application run you submitted +uvx aignostics application run list +# Incrementally download results when they become available +# Fill in the id from the output in the previous step +uvx aignostics application run result download APPLICATION_RUN_ID +``` + +For convenience the `application run execute` command combines preparation, upload, submission and download. +The below is equivalent to the above, while adding additionally required metadata using a mapping: + +```shell +uvx aignostics dataset idc download 1.3.6.1.4.1.5962.99.1.1069745200.1645485340.1637452317744.2.0 data/ +uvx aignostics application run execute he-tme data/tcga_luad/run.csv data/ --mapping ".*\.dcm:staining_method=H&E,tissue=LUNG,disease=LUNG_CANCER" +``` + +The CLI provides extensive help: + +```shell +uvx aignostics --help # list all spaces such as application, dataset, bucket and system, +uvx aignostics application --help # list subcommands in the application space +uvx aignostics application run --help # list subcommands in the application run sub-space +uvx aignostics application run list --help # show help for specific command +uvx aignostics application run execute --help # show help for another command +``` + +Check out our +[CLI reference documentation](https://aignostics.readthedocs.io/en/latest/cli_reference.html) +to learn about all commands and options available. + +### System Health Checks + +The CLI automatically checks system health before uploading slides or submitting runs. If the system is unhealthy, the operation is blocked and an error message is displayed: + +``` +Error: Platform is not healthy: . Aborting. +``` + +To override this behavior (not recommended for production use), add the `--force` flag: + +```shell +uvx aignostics application run upload he-tme metadata.csv --force +uvx aignostics application run submit he-tme metadata.csv --force +uvx aignostics application run execute he-tme metadata.csv data/ --force +``` + +To manually check system health before running commands: + +```shell +uvx aignostics system health +``` + +## Python Library: Call the Aignostics Platform API from your Python scripts + +The Python SDK includes the *Aignostics Python Library* for integration with your Python codebase. + +**New to Python Library?** See Installation section above to get started. + + + +### Installation + +Add the Aignostics Python SDK to your Python project: + +**Install with [uv](https://docs.astral.sh/uv/):** + +```shell +uv add aignostics +``` + +**Install with [pip](https://pip.pypa.io/en/stable/):** + +```shell +# Add Python SDK as dependency to your project +pip install aignostics +``` + +#### Usage + +The following snippet shows how to use the Client to submit an application +run: + +```python +from aignostics import platform + +# initialize the client +client = platform.Client() +# submit an application run +application_run = client.runs.submit( + application_id="test-app", + items=[ + platform.InputItem( + external_id="slide-1", + input_artifacts=[ + platform.InputArtifact( + name="whole_slide_image", + download_url="", + metadata={ + "checksum_base64_crc32c": "AAAAAA==", + "resolution_mpp": 0.25, + "width_px": 1000, + "height_px": 1000, + }, + ) + ], + ), + ], +) +# wait for the results and download incrementally as they become available +application_run.download_to_folder("path/to/download/folder") +``` + +Please look at the notebooks in the `example` folder for a more detailed example +and read the +[client reference documentation](https://aignostics.readthedocs.io/en/latest/lib_reference.html) +to learn about all classes and methods. + +### System Health Checks + +The low-level Python SDK does **not** perform automated health checks before operations. If health verification is required for your use case, you should implement checks in your application logic: + +```python +from aignostics import platform +from aignostics.system import Service as SystemService + +# Check system health before submitting runs +health = SystemService().health() +if not health: + raise RuntimeError(f"System is unhealthy: {health.reason}") + +# Proceed with run submission +client = platform.Client() +run = client.runs.submit(...) +``` + +This design gives you full control over health check behavior, allowing you to: + +- Implement custom retry logic for transient failures +- Log health status for monitoring and debugging +- Gracefully handle unhealthy states in your application + +### Example Notebooks: Interact with the Aignostics Platform from your Python Notebook environment + +> [!IMPORTANT] +> Before you get started, you need to set up your authentication credentials if +> you did not yet do so! Please visit +> [your personal dashboard on the Aignostics Platform website](https://platform.aignostics.com/getting-started/quick-start) +> and follow the steps outlined in the `Use in Python Notebooks` section. + +The Python SDK includes ready-to-use Marimo notebooks that demonstrate platform interaction patterns. These notebooks are ideal for: + +- Learning the API through interactive examples +- Prototyping custom analysis workflows +- Integrating with existing data science pipelines + +The example notebooks use our "Test Application" (free for all users). To run them, +please follow the steps outlined in the snippet below to clone this repository and start the +[Marimo](https://marimo.io/) +([examples/notebook.py](https://github.com/aignostics/python-sdk/blob/main/examples/notebook.py)) +notebook: + +```shell +# clone the `python-sdk` repository +git clone https://github.com/aignostics/python-sdk.git +# within the cloned repository, install the SDK and all dependencies +uv sync --all-extras +# show marimo example notebook in the browser +uv run marimo edit examples/notebook.py +``` + +> 💡 You can also run a notebook within the Aignostics Launchpad. To do so, select the +> Run you want to inspect in the left sidebar, and click the button "Open in Python Notebook". + +### Defining the input for an application run + +The following sections provide technical details for advanced use cases. These examples use the "Test Application" - a free application available to all users for testing and development purposes. + +When creating an application run, you need to specify the `application_id` and optionally the +`application_version` (version number) of the application you want to run. If you omit the version, +the latest version will be used automatically. Additionally, you need to define the input items you +want to process in the run. The input items are defined as follows: + +```python +( + platform.InputItem( + external_id="1", + input_artifacts=[ + platform.InputArtifact( + name="whole_slide_image", # defined by the application version's input artifact schema + download_url="", + metadata={ # defined by the application version's input artifact schema + "checksum_base64_crc32c": "N+LWCg==", + "resolution_mpp": 0.46499982, + "width_px": 3728, + "height_px": 3640, + }, + ) + ], + ), +) +``` + +For each item you want to process, you need to provide a unique `reference` +string. This is used to identify the item in the results later on. The +`input_artifacts` field is a list of `InputArtifact` objects, which defines what +data & metadata you need to provide for each item. The required artifacts depend +on the application version you want to run - in the case of test application, +there is only one artifact required, which is the image to process on. The +artifact name is defined as `whole_slide_image` for this application. + +The `download_url` is a signed URL that allows the Aignostics Platform to +download the image data later during processing. + +### Self-signed URLs for large files + +To make the whole slide images you want to process available to the Aignostics Platform, you +need to provide a signed URL that allows the platform to download the data. +Self-signed URLs for files in google storage buckets can be generated using the +`generate_signed_url` +([code](https://github.com/aignostics/python-sdk/blob/407e74f7ae89289b70efd86cbda59ec7414050d5/src/aignostics/client/utils.py#L85)). + +**We expect that you provide the +[required credentials](https://cloud.google.com/docs/authentication/application-default-credentials) +for the Google Storage Bucket** + +## MCP Server: Integrate with AI Agents + +The Python SDK includes an MCP (Model Context Protocol) server that exposes SDK functionality to AI agents like Claude. This enables AI assistants to help you interact with the Aignostics Platform through natural conversation. + +### Quick Start with Claude Desktop + +Add the following to your Claude Desktop configuration file: + +**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` +**Windows**: `%APPDATA%\Claude\claude_desktop_config.json` + +```json +{ + "mcpServers": { + "aignostics": { + "command": "uvx", + "args": ["aignostics", "mcp", "run"] + } + } +} +``` + +Restart Claude Desktop after adding this configuration. + +### CLI Commands + +```bash +# Using uvx (no installation required) +uvx aignostics mcp run +uvx aignostics mcp list-tools +``` + +### Using Plugins + +The MCP server supports plugins that extend its functionality with additional tools. To run the MCP server with a plugin installed: + +```bash +# With a local plugin +uv run --with /path/to/plugin aignostics mcp run + +# With a plugin from a git repository +uvx --with git+ssh://git@github.com/org/plugin aignostics mcp run +``` + +Plugins register themselves via Python entry points and their tools are automatically discovered and namespaced by the MCP server. + +### What AI Agents Can Do + +Once configured, AI agents can help you with platform operations through natural language, with access to tools from the SDK and any installed plugins. + +## Next Steps + +Now that you have an overview of the Aignostics Python SDK and its interfaces, here are some recommended next steps to deepen your understanding and get the most out of the platform: + +- **Understand the platform**: Read the [Aignostics Platform Overview](platform_overview.md) to learn about architecture and core concepts +- **Review detailed documentation**: See the [CLI reference](https://aignostics.readthedocs.io/en/latest/cli_reference.html) and [Python Library reference](https://aignostics.readthedocs.io/en/latest/lib_reference.html) +- **Explore QuPath integration**: Use the QuPath extension to visualize and interact with your results +- **Get support**: Contact [support@aignostics.com](mailto:support@aignostics.com) or check the [full documentation](https://aignostics.readthedocs.io/en/latest/) + + +## Platform + +### Overview + +The **Aignostics Platform** is a comprehensive cloud-based service that allows organizations to leverage advanced computational pathology applications without the need for specialized expertise or complex infrastructure. Via its API it provides a standardized, secure interface for accessing Aignostics' portfolio of advanced computational pathology applications. These applications perform machine learning based tissue and cell analysis on histopathology slides, delivering quantitative measurements, visual representations, and detailed statistical data. + +![Overview](https://raw.githubusercontent.com/aignostics/python-sdk/main/platform.png) + +### Key Features +Aignostics Platform offers key features designed to maximize value for its users: + +1. **Run Aignostics applications:** Run Aignostics advanced computational pathology applications like [Atlas H&E-TME](https://www.aignostics.com/products/he-tme-profiling-product) on your whole slide images (WSIs) and receive results in a easy to inspect formats. +2. **Multiple Access Points:** Interact with the platform via various pathways, from **Aignostics Launchpad** (desktop application for MacOS, Windows and Linux), **Aignostics CLI** (command-line interface for your terminal or shell scripts), **Example Notebooks** (we support Jupyter and Marimo), **Aignostics Client Library** (for integration with your Python codebase), or directly through the **API of the Aignostics Platform** (for integration with any programming language). Contact your business partner at Aignostics if you are interested to discuss a direct integration with your Imaging Management Systems (IMS) and Laboratory Information Management Systems (LIMS). +3. **Secure Data Handling:** Maintain control of your slide data through secure self-signed URLs. Results are automatically deleted after 30 days, and can be deleted earlier by the user. +4. **High-throughput processing with incremental results delivery:** Submit up to 500 whole slide images (WSI) in one batch request. Access results for individual slides as they completed processing, without having to wait for the entire batch to finish. +5. **Standard formats:** Support for commonly used image formats in digital pathology such as pyramidal DICOM, TIFF, and SVS. Results provided in standard formats like QuPath GeoJSON (polygons), TIFF (heatmaps) and CSV (measurements and statistics). + +### Registration and User Access + +To start using the Aignostics Platform and its advanced applications, your organization must be registered by our business support team: + +1. Access to the Aignostics Platform requires a formal business agreement. Once an agreement is in place between your organization and Aignostics, we proceed with your organization's registration. If your organization does not yet have an account, please contact your account manager or email us at [support@aignostics.com](mailto:support@aignostics.com) to express your interest. +2. To register your organization, we require the name and email address of at least one employee, who will be assigned the Administrator role for your organisation. Your organisation's Administrator can invite and manage additional users. + +> [!Important] +> 1. All user accounts must be associated with your organization's official domain. We do not support the registration of private or personal email addresses. +> 2. For security, Two-Factor Authentication (2FA) is mandatory for all user accounts. +> 3. We can integrate with your IDP system (e.g. SAML, OIDC) for user authentication. Please contact us to discuss the integration. +> 4. Registering your organistation typically takes 2 business days depending on the complexity of the signed business agreement and specific requirements. + +### Console + +The web-based [*Aignostics Console*](https://platform.aignostics.com) is a user-friendly interface that allows you to +manage your organization, applications, quotas, and users registered with the Aignostics Platform. + +1. The Console is available to users registered for your organisation to manage their profile and monitor usage of their quota. +2. Administrators of your organization can invite additional users, manage the organisation and user specific quotas and monitor usage. +3. Both roles can trigger application runs. + +### Applications +An application is a fully automated advanced machine learning based workflow composed of one or more specific tasks (e.g. Tissue Quality Control, Tissue Segmentation, Cell Detection, Cell Classification and predictive analysis). Each application is designed for a particular analysis purpose (e.g. Tumor Micro Environment analysis or biomarker scoring). For each application we define input requirements, processing tasks and output formats. + +As contracted in your business agreement with Aignostics your organisation subscribes to one or more applications. The applications are available for your organization in the Aignostics Platform. You can find the list of available applications in the Console of the Aignostics Platform. + +Each application can have multiple versions. Please make sure you read dedicated application documentation to understand its specific constraints regarding acceptable formats, staining method, tissue types and diseases. + +Once registered to the Platform, your organization will automatically gain access to the "Test Application". This application can be used to configure the workflow and to make sure that the integration works correctly. + + +### Application run + +To trigger the application run, users can use the Aignostics Launchpad, Aignostics CLI, Example Notebooks, our Client Library, or directly call the REST API. The platform expects the user payload, containing the metadata and the signed URLs to the whole slide images (WSIs). The detailed requirements of the payload depend on the application and are described in the documentation, and accessible via the Info button in the Launchpad, as well as via the CLI and `/v1/applications` endpoint in the API. + +When the application run is created, it can be in one of the following states: + +1. **received**: the application run received from the client +2. **scheduled**: the application run request is valid and is scheduled for execution +3. **running**: the application run execution started +4. **completed**: the application run execution is done and all outputs are available for download +5. **completed**: the application run execution is done, but some items end up in the failed state +6. **rejected**: the application run request is rejected before it is scheduled +7. **cancelled by the system**: the application run failed during the execution with the number of errors higher than the threshold +9. **cancelled by the user**: the application run is cancelled by the user before it is finished + +The status and operations of an application run are private to the user who triggered the run. + +### Results +When the processing of whole slide image is successfully completed, the resulting outputs become available for download. To assess specifics of application outputs please consult our application specific documentation, which you can find in the **Console**. Please note that you access to documentation is restricted to those applications your organisation subscribed to. + +Application run outputs are automatically deleted 30 days after the application run has completed. However, the owner of the application run (the user who initiated it) can use the API to manually delete outputs earlier, once the run has reached a final state - completed, cancelled by the system or cancelled by the user. The Launchpad and CLI provide enable to delete results with one click resp. command. + +### Quotas +Every organization has a limit on how many WSIs it can process in a calendar month. The following quotas exist: + +1. **Per organization**: as defined in your business agreement with Aignostics +2. **Per user**: defined by your organization Admin + +When the per month quota is reached, an application run request is denied. + +Other limitations may apply to your organization: + +1. Allowed number of users an organization can register +2. Allowed number of images user can submit per application run +3. Allowed number of parallel application runs for the whole organization + +Additionally, we allow organization Admin to define following limitations for its users: + +1. Maximum number of images the user can process per calendar month. +2. Maximum number of parallel application runs for a given user + +Visit the [Console](https://platform.aignostics.com) to check your current quota and usage. The Console provides a clear overview of the number of images processed by your organization and by each user, as well as the remaining quota for the current month. + +### API + +The **Aignostics Platform API** is a RESTful web service that allows you to interact with the platform programmatically. It provides endpoints for submitting whole slide images (WSIs) for analysis, checking the status of application runs, and retrieving results. + +You can interact with the API using the Python client, which is a wrapper around the RESTful API. The Python client simplifies the process of making requests to the API and handling responses. It also provides convenient methods for uploading WSIs, checking application run status, and downloading results. + +For integration with programming languages other than Python, you can use the RESTful API directly. The API is designed to be language-agnostic, meaning you can use any programming language that supports HTTP requests to interact with it. This includes languages like Java, Kotlin, C#, Ruby, and Typescript. + +### Cost + +Every WSI processed by the Platform generates a cost. Usage of the "Test Application" is free of charge for any registered user. The cost for other applications is defined in your business agreement with Aignostics. The cost is calculated based on the number of slides processed. When an application run is cancelled, either by the system or by the user, only processed images incur a cost. + +**[Read the API reference documentation](https://aignostics.readthedocs.io/en/latest/api_reference_v1.html)** or use our **[Interactive API Explorer](https://platform.aignostics.com/explore-api)** to dive into details of all operations and parameters. + + +## Further Reading + +1. Inspect our + [security policy](https://aignostics.readthedocs.io/en/latest/security.html) + with detailed documentation of checks, tools and principles. +1. Inspect how we achieve + [operational excellence](https://aignostics.readthedocs.io/en/latest/operational_excellence.html) + with information on our modern toolchain and software architecture. +2. Check out the + [CLI reference](https://aignostics.readthedocs.io/en/latest/cli_reference.html) + with detailed documentation of all CLI commands and options. +3. Check out the + [library reference](https://aignostics.readthedocs.io/en/latest/lib_reference.html) + with detailed documentation of public classes and functions. +4. Check out the + [API reference](https://aignostics.readthedocs.io/en/latest/api_reference_v1.html) + with detailed documentation of all API operations and parameters. See as well + the OpenAPI Specification in [JSON](https://github.com/aignostics/python-sdk/blob/main/docs/source/_static/openapi_v1.json) and [YAML](https://github.com/aignostics/python-sdk/blob/main/docs/source/_static/openapi_v1.yaml), and the [API Explorer](https://aignostics.readthedocs.io/en/latest/api_explorer_v1.html). +5. Our + [release notes](https://aignostics.readthedocs.io/en/latest/release-notes.html) + provide a complete log of recent improvements and changes. +6. We gratefully acknowledge the numerous + [open source projects](https://aignostics.readthedocs.io/en/latest/attributions.html) + that this project builds upon. Thank you to all these wonderful contributors! + + +## Glossary + +### A + +**Administrator Role** +A user role within an organization that has permissions to invite and manage additional users, define user-specific quotas, and monitor organizational usage. + +**Aignostics CLI** +Command-Line Interface that allows interaction with the Aignostics Platform directly from terminal or shell scripts, enabling dataset management and application runs. + +**Aignostics Client Library** +Python library for seamless integration of the Aignostics Platform with enterprise image management systems and scientific workflows. + +**Aignostics Console** +Web-based user interface for managing organizations, applications, quotas, users, and monitoring platform usage. + +**Aignostics Launchpad** +Graphical desktop application (available for Mac OS X, Windows, and Linux) that allows users to run computational pathology applications on whole slide images and inspect results with QuPath and Python Notebooks. + +**Aignostics Platform** +Comprehensive cloud-based service providing standardized, secure interface for accessing advanced computational pathology applications without requiring specialized expertise or complex infrastructure. + +**Aignostics Platform API** +RESTful web service that allows programmatic interaction with the Aignostics Platform, providing endpoints for submitting WSIs, checking application run status, and retrieving results. + +**Aignostics Python SDK** +Software Development Kit providing multiple pathways to interact with the Aignostics Platform, including the Launchpad, CLI, Client Library, and example notebooks. + +**Application** +Fully automated advanced machine learning workflow composed of specific tasks (e.g., Tissue Quality Control, Tissue Segmentation, Cell Detection, Cell Classification) designed for particular analysis purposes. + +**Application Run** +The execution instance of an application on submitted whole slide images, which can be in various states: received, scheduled, running, completed, rejected, cancelled by system, or cancelled by user. + +**Application Version** +Specific version of an application with defined input requirements, processing tasks, and output formats. Each application can have multiple versions. + +**Atlas H&E-TME** +Advanced computational pathology application for Hematoxylin and Eosin-stained Tumor Microenvironment analysis. + +### B + +**Base MPP (Microns Per Pixel)** +Metadata parameter specifying the resolution of whole slide images, indicating the physical distance represented by each pixel. + +**Business Agreement** +Formal contract between an organization and Aignostics required for platform access, defining quotas, applications, and terms of service. + +### C + +**Checksum CRC32C** +Cyclic Redundancy Check used to verify data integrity of uploaded whole slide images. + +**Client** +The main class in the Aignostics Python SDK used to initialize connections and interact with the platform API. + +**Computational Pathology** +Field combining digital pathology with artificial intelligence and machine learning to analyze histopathology slides quantitatively. + +**Aignostics Console** +Web-based user interface for managing organizations, applications, quotas, users, and monitoring platform usage. + +### D + +**DICOM (Digital Imaging and Communications in Medicine)** +Standard format for medical imaging data, supported by the Aignostics Platform for whole slide images. + +**Download URL** +Signed URL that allows the Aignostics Platform to securely download image data during processing. + +### G + +**GeoJSON** +Standard format used by QuPath for representing polygonal annotations and results. + +**Google Storage Bucket** +Cloud storage service where users can store whole slide images and generate signed URLs for platform access. + +### H + +**H&E (Hematoxylin and Eosin)** +Common histological staining method for tissue visualization, used in Atlas H&E-TME application. + +**Heatmaps** +Visual representations of analysis results provided in TIFF format showing spatial distribution of measurements. + +### I + +**IDC (NCI Image Data Commons)** +Public repository of medical imaging data that can be queried and downloaded through the Aignostics CLI. + +**IMS (Imaging Management Systems)** +Enterprise systems for managing medical imaging data that can be integrated with the Aignostics Platform. + +**Input Artifact** +Data object required for application processing, including the actual data file and associated metadata. + +**Input Item** +Individual unit of processing in an application run, containing one or more input artifacts with a unique reference identifier. + +**Interactive API Explorer** +Tool for exploring and testing API endpoints and parameters interactively. + +### J + +**Jupyter** +Popular notebook environment supported by the Aignostics Platform for interactive analysis and visualization. + +### L + +**LIMS (Laboratory Information Management Systems)** +Laboratory systems that can be integrated with the Aignostics Platform for workflow automation. + +### M + +**Marimo** +Modern notebook environment supported by the Aignostics Platform as an alternative to Jupyter. + +**MCP (Model Context Protocol)** +Protocol that enables AI agents like Claude to interact with external tools and services. The Aignostics SDK includes an MCP server that exposes platform functionality to AI assistants. + +**Metadata** +Descriptive information about whole slide images including dimensions, resolution, tissue type, and disease information required for processing. + +**MPP (Microns Per Pixel)** +See Base MPP. + +### N + +**NCI Image Data Commons (IDC)** +See IDC. + +### O + +**Operational Excellence** +Aignostics' commitment to high-quality software development practices including A-grade code quality, security scanning, and comprehensive documentation. + +### P + +**Pyramidal** +Multi-resolution image format that stores the same image at different zoom levels for efficient viewing and processing. + +**Python SDK** +Software Development Kit providing multiple pathways to interact with the Aignostics Platform through Python programming language. + +### Q + +**QuPath** +Open-source software for bioimage analysis that can be launched directly from the Aignostics Launchpad to view results. + +**Quota** +Limit on the number of whole slide images an organization or user can process per calendar month, as defined in business agreements. + +### R + +**Reference** +Unique identifier string for each input item in an application run, used to match results with original inputs. + +**Results** +Output data from application processing, including measurements, statistics, heatmaps, and annotations, automatically deleted after 30 days. + +**RESTful API** +Architectural style for web services that the Aignostics Platform API follows, enabling language-agnostic integration. + +### S + +**Self-signed URLs** +Secure URLs with embedded authentication that allow the platform to access user data without exposing credentials. + +**SVS** +Aperio ScanScope Virtual Slide format, commonly used for whole slide images and supported by the platform. + +**System Health Check** +Automated verification that the SDK and Aignostics Platform are operational before critical operations. The Launchpad blocks run submission when unhealthy (no override available for regular users). The CLI blocks uploads and submissions by default but allows override with `--force`. The Python Library does not perform automatic health checks, giving developers full control over health verification logic. + +### T + +**Test Application** +Free application automatically available to all registered organizations for workflow configuration and integration testing. + +**TIFF (Tagged Image File Format)** +Standard image format supported for both input whole slide images and output heatmaps. + +**Tissue Segmentation** +Computational process of identifying and delineating different tissue regions within histopathology slides. + +**TME (Tumor Microenvironment)** +The cellular environment surrounding tumor cells, analyzed by the Atlas H&E-TME application. + +**Two-Factor Authentication (2FA)** +Mandatory security requirement for all user accounts on the Aignostics Platform. + +### U + +**UV** +Modern Python package manager used for dependency management and project setup in the SDK documentation. + +**UVX** +Tool for running Python applications directly without explicit installation, used to execute Aignostics CLI commands. + +### W + +**Whole Slide Image (WSI)** +High-resolution digital image of an entire histopathology slide, the primary input format for computational pathology applications. + +**Workflow** +Sequence of automated processing steps within an application that transform input images into analytical results. diff --git a/packages/aignostics/pyproject.toml b/packages/aignostics/pyproject.toml new file mode 100644 index 000000000..d60e29432 --- /dev/null +++ b/packages/aignostics/pyproject.toml @@ -0,0 +1,156 @@ +[project] +name = "aignostics" +version = "1.4.0" +description = "🔬 Python SDK providing access to the Aignostics Platform. Includes Aignostics Launchpad (Desktop Application), Aignostics CLI (Command-Line Interface), example notebooks, and Aignostics Client Library." +readme = "README.md" +authors = [ + { name = "Helmut Hoffer von Ankershoffen", email = "helmut@aignostics.com" }, + { name = "Andreas Kunft", email = "andreas@aignostics.com" }, +] +license = { file = "LICENSE" } + +keywords = [ + "aignostics", + "atlas", + "whole-slide-imaging", + "machine-learning", + "digital-pathology", + "medical-imaging", + "qupath", + "dicom", + "pydicom", + "openslide", + "image-data-commons", + "act", + "codecov", + "copier", + "cyclonedx", + "devcontainer", + "detect-secrets", + "docker", + "git-cliff", + "jupyter", + "marimo", + "mypy", + "nox", + "nicegui", + "oe-python-template", + "pip-audit", + "pip-licenses", + "pre-commit", + "pydantic", + "pytest", + "python", + "pypi", + "readthedocs", + "ruff", + "sonarqube", + "sonarcloud", + "sphinx", + "typer", + "uv", +] + +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Intended Audience :: End Users/Desktop", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "License :: OSI Approved :: MIT License", + "Operating System :: MacOS :: MacOS X", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Framework :: Pydantic", + "Framework :: Pytest", + "Typing :: Typed", + "Natural Language :: English", +] + +# WARNING: The upper bound of requires-python is *not* respected by uvx +requires-python = ">=3.11, <3.15" + +dependencies = [ + "aignostics-sdk==1.4.0", + # Heavy domain deps — WSI, DICOM, cloud storage, data processing + "boto3>=1.42.4,<2", + "crc32c>=2.8,<3", + "defusedxml>=0.7.1", + "dicom-validator>=0.7.3,<1", + "dicomweb-client[gcp]>=0.59.3,<1", + "duckdb>=1.4.2,<=2", + "fastparquet>=2026.3.0,<2026.4.0; python_version < '3.14'", + "google-cloud-storage>=3.6.0,<4", + "highdicom>=0.26.1,<1; python_version < '3.14'", + "html-sanitizer>=2.6.0,<3", + "humanize>=4.14.0,<5", + "idc-index-data==24.0.3", + "ijson>=3.4.0.post0,<4", + "openslide-bin>=4.0.0.10,<5", + "openslide-python>=1.4.3,<2", + "packaging>=26,<27", + "pandas>=2.3.3,<4", + "procrastinate>=3.5.3", + "pyarrow>=23.0.1,<24; python_version >= '3.14'", + "pydicom>=3.0.2", + "python-dateutil>=2.9.0.post0,<3", + "pyyaml>=6.0.3,<7", + "s5cmd>=0.3.3,<1", + "shapely>=2.1.2,<3", + "wsidicom>=0.28.1,<1", + # Transitive CVE overrides for heavy deps + "aiohttp>=3.13.4", + "lxml>=6.1.0", + "lxml-html-clean>=0.4.4", + "marshmallow>=3.26.2", + "protobuf>=6.33.5", +] + +[project.optional-dependencies] +pyinstaller = ["pyinstaller>=6.14.0,<7"] +jupyter = [ + "jupyter>=1.1.1,<2", + # Transitive overrides + # WARNING: one cannot negate or downgrade a dependency required here. use override-dependencies for that. + "jupyter-core>=5.8.1", # CVE-2025-30167 + "jupyterlab>=4.4.9", # CVE-2025-59842 + "nbconvert>=7.17.1", # CVE-2025-53000 (>=7.17.0, Dependabot #424); CVE-2026-39377, CVE-2026-39378 (>=7.17.1, Dependabot #553) +] +marimo = [ + "cloudpathlib>=0.23.0,<1", + "ipython>=9.8.0,<10", + "marimo>=0.23.0,<1", # GHSA-2679-6mx9-h9xc (Renovate #533) + "matplotlib>=3.10.7,<4", + "shapely>=2.1.0,<3", +] +qupath = [] + +[project.scripts] +aignostics = "aignostics.cli:cli" + +[project.urls] +Homepage = "https://aignostics.readthedocs.io/en/latest/" +Documentation = "https://aignostics.readthedocs.io/en/latest/" +Source = "https://github.com/aignostics/python-sdk" +Changelog = "https://github.com/aignostics/python-sdk/releases" +Issues = "https://github.com/aignostics/python-sdk/issues" + +[build-system] +requires = ["hatchling==1.29.0"] +build-backend = "hatchling.build" + +[tool.hatch.build] +include = ["src/*"] + +[tool.hatch.build.targets.wheel] +packages = ["src/aignostics"] + +[tool.hatch.metadata] +allow-direct-references = true + +[tool.uv.sources] +aignostics-sdk = { workspace = true } diff --git a/src/aignostics/CLAUDE.md b/packages/aignostics/src/aignostics/CLAUDE.md similarity index 96% rename from src/aignostics/CLAUDE.md rename to packages/aignostics/src/aignostics/CLAUDE.md index d14706c47..183662b27 100644 --- a/src/aignostics/CLAUDE.md +++ b/packages/aignostics/src/aignostics/CLAUDE.md @@ -1,5 +1,12 @@ # CLAUDE.md - Aignostics SDK Modules Overview +> **v2 Architecture Note**: In v2, this `src/aignostics/` tree contains only the *heavy* modules: +> `application`, `wsi`, `dataset`, `bucket`, `qupath`, `notebook`, `gui`, and `system`. +> The `platform` and `utils` modules (plus `constants`) have moved to the slim package at +> `packages/aignostics-sdk/` under the `aignostics_sdk` namespace. +> The full `aignostics` package depends on `aignostics-sdk` and re-exports nothing from those +> moved modules — callers must update their imports (see `migration.md` in the docs). + This file provides a comprehensive overview of all modules in the Aignostics SDK, their core features, user interfaces, and interactions. ## Module Index diff --git a/src/aignostics/__init__.py b/packages/aignostics/src/aignostics/__init__.py similarity index 96% rename from src/aignostics/__init__.py rename to packages/aignostics/src/aignostics/__init__.py index dcf7afbe5..78761c366 100644 --- a/src/aignostics/__init__.py +++ b/packages/aignostics/src/aignostics/__init__.py @@ -7,6 +7,8 @@ # TODO(Helmut): remove when google_crc32c supports Python 3.14 warnings.filterwarnings("ignore", message="As the c extension couldn't be imported", category=RuntimeWarning) +from aignostics_sdk.utils.boot import boot # noqa: E402 + from .constants import ( # noqa: E402 HETA_APPLICATION_ID, SENTRY_INTEGRATIONS, @@ -14,7 +16,6 @@ WSI_SUPPORTED_FILE_EXTENSIONS, WSI_SUPPORTED_FILE_EXTENSIONS_TEST_APP, ) -from .utils.boot import boot # noqa: E402 # Add scheme to HTTP proxy environment variables if missing for proxy_var in ["HTTP_PROXY", "HTTPS_PROXY"]: diff --git a/packages/aignostics/src/aignostics/application/__init__.py b/packages/aignostics/src/aignostics/application/__init__.py new file mode 100644 index 000000000..beafbc86a --- /dev/null +++ b/packages/aignostics/src/aignostics/application/__init__.py @@ -0,0 +1,21 @@ +"""Application module — re-exports from slim aignostics_sdk.application.""" + +from aignostics_sdk.application import ( + DownloadProgress, + DownloadProgressState, + Service, + Settings, + cli, +) + +__all__ = ["DownloadProgress", "DownloadProgressState", "Service", "Settings", "cli"] + +from importlib.util import find_spec + +# advertise PageBuilder to enable auto-discovery +if find_spec("nicegui"): + from aignostics.application._gui._page_builder import PageBuilder + + __all__ += [ + "PageBuilder", + ] diff --git a/src/aignostics/application/_gui/_frame.py b/packages/aignostics/src/aignostics/application/_gui/_frame.py similarity index 99% rename from src/aignostics/application/_gui/_frame.py rename to packages/aignostics/src/aignostics/application/_gui/_frame.py index d9613421b..b1d860640 100644 --- a/src/aignostics/application/_gui/_frame.py +++ b/packages/aignostics/src/aignostics/application/_gui/_frame.py @@ -2,13 +2,13 @@ from typing import Any import humanize +from aignostics.gui import frame from loguru import logger from nicegui import app, background_tasks, context, ui # noq from nicegui import run as nicegui_run -from aignostics.gui import frame +from aignostics_sdk.application._service import Service # noqa: PLC2701 # noqa: TID252 -from .._service import Service # noqa: TID252 from ._utils import application_id_to_icon, run_status_to_icon_and_color BORDERED_SEPARATOR = "bordered separator" diff --git a/src/aignostics/application/_gui/_page_application_describe.py b/packages/aignostics/src/aignostics/application/_gui/_page_application_describe.py similarity index 99% rename from src/aignostics/application/_gui/_page_application_describe.py rename to packages/aignostics/src/aignostics/application/_gui/_page_application_describe.py index cf86e9dbe..a0d9ce5be 100644 --- a/src/aignostics/application/_gui/_page_application_describe.py +++ b/packages/aignostics/src/aignostics/application/_gui/_page_application_describe.py @@ -7,13 +7,14 @@ from pathlib import Path from typing import TYPE_CHECKING, Any +from aignostics.utils import GUILocalFilePicker from loguru import logger from nicegui import app, binding, ui from nicegui import run as nicegui_run from nicegui.events import ValueChangeEventArguments -from aignostics.constants import WSI_SUPPORTED_FILE_EXTENSIONS -from aignostics.platform import ( +from aignostics_sdk.constants import WSI_SUPPORTED_FILE_EXTENSIONS +from aignostics_sdk.platform import ( DEFAULT_CPU_PROVISIONING_MODE, DEFAULT_FLEX_START_MAX_RUN_DURATION_MINUTES, DEFAULT_GPU_PROVISIONING_MODE, @@ -21,14 +22,15 @@ DEFAULT_MAX_GPUS_PER_SLIDE, DEFAULT_NODE_ACQUISITION_TIMEOUT_MINUTES, ) -from aignostics.system import Service as SystemService -from aignostics.utils import GUILocalFilePicker, get_user_data_directory +from aignostics_sdk.system import Service as SystemService +from aignostics_sdk.utils import get_user_data_directory if TYPE_CHECKING: - from aignostics.platform import UserInfo + from aignostics_sdk.platform import UserInfo + +from aignostics_sdk.application._service import Service # noqa: PLC2701 # noqa: TID252 +from aignostics_sdk.application._utils import get_mime_type_for_artifact # noqa: PLC2701 -from .._service import Service # noqa: TID252 -from .._utils import get_mime_type_for_artifact # noqa: TID252 from ._frame import _frame from ._utils import ( application_id_to_icon, diff --git a/src/aignostics/application/_gui/_page_application_run_describe.py b/packages/aignostics/src/aignostics/application/_gui/_page_application_run_describe.py similarity index 98% rename from src/aignostics/application/_gui/_page_application_run_describe.py rename to packages/aignostics/src/aignostics/application/_gui/_page_application_run_describe.py index 926aa767d..7ef11537d 100644 --- a/src/aignostics/application/_gui/_page_application_run_describe.py +++ b/packages/aignostics/src/aignostics/application/_gui/_page_application_run_describe.py @@ -12,6 +12,8 @@ from urllib.parse import quote import humanize +from aignostics.third_party.showinfm.showinfm import show_in_file_manager +from aignostics.utils import GUILocalFilePicker from loguru import logger from nicegui import ( app, @@ -19,18 +21,20 @@ ) from nicegui import run as nicegui_run -from aignostics.platform import ArtifactOutput, ItemOutput, ItemResult, ItemState, Run, RunState -from aignostics.third_party.showinfm.showinfm import show_in_file_manager -from aignostics.utils import GUILocalFilePicker, get_user_data_directory +from aignostics_sdk.platform import ArtifactOutput, ItemOutput, ItemResult, ItemState, Run, RunState +from aignostics_sdk.utils import get_user_data_directory if TYPE_CHECKING: - from aignx.codegen.models import RunReadResponse - - from aignostics.platform import UserInfo + from aignostics_sdk._codegen.models import RunReadResponse + from aignostics_sdk.platform import UserInfo + +from aignostics_sdk.application._models import DownloadProgressState # noqa: PLC2701 # noqa: TID252 +from aignostics_sdk.application._service import Service # noqa: PLC2701 # noqa: TID252 +from aignostics_sdk.application._utils import ( # noqa: PLC2701 # noqa: TID252 + get_mime_type_for_artifact, + queue_position_string_from_run, +) -from .._models import DownloadProgressState # noqa: TID252 -from .._service import Service # noqa: TID252 -from .._utils import get_mime_type_for_artifact, queue_position_string_from_run # noqa: TID252 from ._frame import _frame from ._utils import ( mime_type_to_icon, diff --git a/src/aignostics/application/_gui/_page_builder.py b/packages/aignostics/src/aignostics/application/_gui/_page_builder.py similarity index 100% rename from src/aignostics/application/_gui/_page_builder.py rename to packages/aignostics/src/aignostics/application/_gui/_page_builder.py diff --git a/src/aignostics/application/_gui/_page_index.py b/packages/aignostics/src/aignostics/application/_gui/_page_index.py similarity index 100% rename from src/aignostics/application/_gui/_page_index.py rename to packages/aignostics/src/aignostics/application/_gui/_page_index.py diff --git a/src/aignostics/application/_gui/_utils.py b/packages/aignostics/src/aignostics/application/_gui/_utils.py similarity index 97% rename from src/aignostics/application/_gui/_utils.py rename to packages/aignostics/src/aignostics/application/_gui/_utils.py index 0ac555b39..391bd06af 100644 --- a/src/aignostics/application/_gui/_utils.py +++ b/packages/aignostics/src/aignostics/application/_gui/_utils.py @@ -1,6 +1,6 @@ """Utility functions for the application GUI.""" -from aignostics.platform import ItemState, ItemTerminationReason, RunState, RunTerminationReason +from aignostics_sdk.platform import ItemState, ItemTerminationReason, RunState, RunTerminationReason NONE = "none" diff --git a/src/aignostics/application/_gui/assets/.gitignore b/packages/aignostics/src/aignostics/application/_gui/assets/.gitignore similarity index 100% rename from src/aignostics/application/_gui/assets/.gitignore rename to packages/aignostics/src/aignostics/application/_gui/assets/.gitignore diff --git a/src/aignostics/application/_gui/assets/canceled.lottie b/packages/aignostics/src/aignostics/application/_gui/assets/canceled.lottie similarity index 100% rename from src/aignostics/application/_gui/assets/canceled.lottie rename to packages/aignostics/src/aignostics/application/_gui/assets/canceled.lottie diff --git a/src/aignostics/application/_gui/assets/done.lottie b/packages/aignostics/src/aignostics/application/_gui/assets/done.lottie similarity index 100% rename from src/aignostics/application/_gui/assets/done.lottie rename to packages/aignostics/src/aignostics/application/_gui/assets/done.lottie diff --git a/src/aignostics/application/_gui/assets/empty.lottie b/packages/aignostics/src/aignostics/application/_gui/assets/empty.lottie similarity index 100% rename from src/aignostics/application/_gui/assets/empty.lottie rename to packages/aignostics/src/aignostics/application/_gui/assets/empty.lottie diff --git a/src/aignostics/application/_gui/assets/error.lottie b/packages/aignostics/src/aignostics/application/_gui/assets/error.lottie similarity index 100% rename from src/aignostics/application/_gui/assets/error.lottie rename to packages/aignostics/src/aignostics/application/_gui/assets/error.lottie diff --git a/src/aignostics/application/_gui/assets/error_alt.lottie b/packages/aignostics/src/aignostics/application/_gui/assets/error_alt.lottie similarity index 100% rename from src/aignostics/application/_gui/assets/error_alt.lottie rename to packages/aignostics/src/aignostics/application/_gui/assets/error_alt.lottie diff --git a/src/aignostics/application/_gui/assets/home-card-1.png b/packages/aignostics/src/aignostics/application/_gui/assets/home-card-1.png similarity index 100% rename from src/aignostics/application/_gui/assets/home-card-1.png rename to packages/aignostics/src/aignostics/application/_gui/assets/home-card-1.png diff --git a/src/aignostics/application/_gui/assets/home-card-2.png b/packages/aignostics/src/aignostics/application/_gui/assets/home-card-2.png similarity index 100% rename from src/aignostics/application/_gui/assets/home-card-2.png rename to packages/aignostics/src/aignostics/application/_gui/assets/home-card-2.png diff --git a/src/aignostics/application/_gui/assets/home-card-3.png b/packages/aignostics/src/aignostics/application/_gui/assets/home-card-3.png similarity index 100% rename from src/aignostics/application/_gui/assets/home-card-3.png rename to packages/aignostics/src/aignostics/application/_gui/assets/home-card-3.png diff --git a/src/aignostics/application/_gui/assets/home-card-4.png b/packages/aignostics/src/aignostics/application/_gui/assets/home-card-4.png similarity index 100% rename from src/aignostics/application/_gui/assets/home-card-4.png rename to packages/aignostics/src/aignostics/application/_gui/assets/home-card-4.png diff --git a/src/aignostics/application/_gui/assets/home-card-5.png b/packages/aignostics/src/aignostics/application/_gui/assets/home-card-5.png similarity index 100% rename from src/aignostics/application/_gui/assets/home-card-5.png rename to packages/aignostics/src/aignostics/application/_gui/assets/home-card-5.png diff --git a/src/aignostics/application/_gui/assets/image-not-found.png b/packages/aignostics/src/aignostics/application/_gui/assets/image-not-found.png similarity index 100% rename from src/aignostics/application/_gui/assets/image-not-found.png rename to packages/aignostics/src/aignostics/application/_gui/assets/image-not-found.png diff --git a/src/aignostics/application/_gui/assets/pending.lottie b/packages/aignostics/src/aignostics/application/_gui/assets/pending.lottie similarity index 100% rename from src/aignostics/application/_gui/assets/pending.lottie rename to packages/aignostics/src/aignostics/application/_gui/assets/pending.lottie diff --git a/src/aignostics/application/_gui/assets/processing.lottie b/packages/aignostics/src/aignostics/application/_gui/assets/processing.lottie similarity index 100% rename from src/aignostics/application/_gui/assets/processing.lottie rename to packages/aignostics/src/aignostics/application/_gui/assets/processing.lottie diff --git a/src/aignostics/application/_gui/assets/ruo.png b/packages/aignostics/src/aignostics/application/_gui/assets/ruo.png similarity index 100% rename from src/aignostics/application/_gui/assets/ruo.png rename to packages/aignostics/src/aignostics/application/_gui/assets/ruo.png diff --git a/src/aignostics/application/_gui/assets/ruo.svg b/packages/aignostics/src/aignostics/application/_gui/assets/ruo.svg similarity index 100% rename from src/aignostics/application/_gui/assets/ruo.svg rename to packages/aignostics/src/aignostics/application/_gui/assets/ruo.svg diff --git a/src/aignostics/bucket/CLAUDE.md b/packages/aignostics/src/aignostics/bucket/CLAUDE.md similarity index 100% rename from src/aignostics/bucket/CLAUDE.md rename to packages/aignostics/src/aignostics/bucket/CLAUDE.md diff --git a/src/aignostics/bucket/__init__.py b/packages/aignostics/src/aignostics/bucket/__init__.py similarity index 100% rename from src/aignostics/bucket/__init__.py rename to packages/aignostics/src/aignostics/bucket/__init__.py diff --git a/src/aignostics/bucket/_cli.py b/packages/aignostics/src/aignostics/bucket/_cli.py similarity index 99% rename from src/aignostics/bucket/_cli.py rename to packages/aignostics/src/aignostics/bucket/_cli.py index 2ba44e000..77646f936 100644 --- a/src/aignostics/bucket/_cli.py +++ b/packages/aignostics/src/aignostics/bucket/_cli.py @@ -12,7 +12,7 @@ import typer from loguru import logger -from aignostics.utils import console, get_user_data_directory +from aignostics_sdk.utils import console, get_user_data_directory from ._service import DownloadProgress, Service diff --git a/src/aignostics/bucket/_gui.py b/packages/aignostics/src/aignostics/bucket/_gui.py similarity index 99% rename from src/aignostics/bucket/_gui.py rename to packages/aignostics/src/aignostics/bucket/_gui.py index 74c46bd54..2860e8911 100644 --- a/src/aignostics/bucket/_gui.py +++ b/packages/aignostics/src/aignostics/bucket/_gui.py @@ -7,9 +7,9 @@ from aignostics.gui import frame from aignostics.third_party.showinfm.showinfm import show_in_file_manager -from aignostics.utils import get_user_data_directory +from aignostics.utils import BasePageBuilder, GUILocalFilePicker +from aignostics_sdk.utils import get_user_data_directory -from ..utils import BasePageBuilder, GUILocalFilePicker # noqa: TID252 from ._service import DownloadProgress, Service MESSAGE_NO_DOWNLOAD_FOLDER_SELECTED = "No download folder selected" diff --git a/src/aignostics/bucket/_service.py b/packages/aignostics/src/aignostics/bucket/_service.py similarity index 99% rename from src/aignostics/bucket/_service.py rename to packages/aignostics/src/aignostics/bucket/_service.py index f8b3b855a..877c4e5b9 100644 --- a/src/aignostics/bucket/_service.py +++ b/packages/aignostics/src/aignostics/bucket/_service.py @@ -15,8 +15,8 @@ if TYPE_CHECKING: from botocore.client import BaseClient -from aignostics.platform import Service as PlatformService -from aignostics.utils import BaseService, Health, get_user_data_directory +from aignostics_sdk.platform import Service as PlatformService +from aignostics_sdk.utils import BaseService, Health, get_user_data_directory from ._settings import Settings diff --git a/src/aignostics/bucket/_settings.py b/packages/aignostics/src/aignostics/bucket/_settings.py similarity index 94% rename from src/aignostics/bucket/_settings.py rename to packages/aignostics/src/aignostics/bucket/_settings.py index 15fdd6aa9..d6ded2754 100644 --- a/src/aignostics/bucket/_settings.py +++ b/packages/aignostics/src/aignostics/bucket/_settings.py @@ -6,7 +6,7 @@ from pydantic import Field from pydantic_settings import SettingsConfigDict -from ..utils import OpaqueSettings, __env_file__, __project_name__ # noqa: TID252 +from aignostics_sdk.utils import ENV_PREFIX, OpaqueSettings, __env_file__ class BucketProtocol(StrEnum): @@ -18,7 +18,7 @@ class Settings(OpaqueSettings): """Settings.""" model_config = SettingsConfigDict( - env_prefix=f"{__project_name__.upper()}_BUCKET_", + env_prefix=f"{ENV_PREFIX}_BUCKET_", extra="ignore", env_file=__env_file__, env_file_encoding="utf-8", diff --git a/src/aignostics/bucket/assets/Google-Cloud-logo.png b/packages/aignostics/src/aignostics/bucket/assets/Google-Cloud-logo.png similarity index 100% rename from src/aignostics/bucket/assets/Google-Cloud-logo.png rename to packages/aignostics/src/aignostics/bucket/assets/Google-Cloud-logo.png diff --git a/src/aignostics/bucket/assets/Google-Cloud-logo.svg b/packages/aignostics/src/aignostics/bucket/assets/Google-Cloud-logo.svg similarity index 100% rename from src/aignostics/bucket/assets/Google-Cloud-logo.svg rename to packages/aignostics/src/aignostics/bucket/assets/Google-Cloud-logo.svg diff --git a/src/aignostics/cli.py b/packages/aignostics/src/aignostics/cli.py similarity index 92% rename from src/aignostics/cli.py rename to packages/aignostics/src/aignostics/cli.py index c5203d260..38537ec9c 100644 --- a/src/aignostics/cli.py +++ b/packages/aignostics/src/aignostics/cli.py @@ -1,6 +1,7 @@ """CLI (Command Line Interface) of Aignostics Python SDK.""" import sys +from importlib.metadata import entry_points as _entry_points from importlib.util import find_spec from pathlib import Path @@ -8,7 +9,7 @@ from loguru import logger from aignostics.constants import NOTEBOOK_DEFAULT, WINDOW_TITLE -from aignostics.utils import ( +from aignostics_sdk.utils import ( __is_running_in_container__, __python_version__, __version__, @@ -20,6 +21,10 @@ help="Command Line Interface (CLI) of Aignostics Python SDK providing access to Aignostics Platform.", ) +# Mount slim commands from aignostics-sdk (and any other registered plugins) via entry points +for _ep in _entry_points(group="aignostics.cli"): + cli.add_typer(_ep.load()) + if find_spec("nicegui") and find_spec("webview") and not __is_running_in_container__: @cli.command() @@ -33,7 +38,7 @@ def launchpad() -> None: if find_spec("marimo"): from typing import Annotated - from aignostics.utils import create_marimo_app + from aignostics_sdk.utils import create_marimo_app @cli.command() def notebook( diff --git a/src/aignostics/constants.py b/packages/aignostics/src/aignostics/constants.py similarity index 97% rename from src/aignostics/constants.py rename to packages/aignostics/src/aignostics/constants.py index 8acd926b0..9cd1c50df 100644 --- a/src/aignostics/constants.py +++ b/packages/aignostics/src/aignostics/constants.py @@ -4,7 +4,7 @@ from pathlib import Path from typing import TYPE_CHECKING -from aignostics.utils import __version__ +from aignostics_sdk.utils import __version__ if TYPE_CHECKING: from sentry_sdk.integrations import Integration diff --git a/src/aignostics/dataset/CLAUDE.md b/packages/aignostics/src/aignostics/dataset/CLAUDE.md similarity index 100% rename from src/aignostics/dataset/CLAUDE.md rename to packages/aignostics/src/aignostics/dataset/CLAUDE.md diff --git a/src/aignostics/dataset/__init__.py b/packages/aignostics/src/aignostics/dataset/__init__.py similarity index 100% rename from src/aignostics/dataset/__init__.py rename to packages/aignostics/src/aignostics/dataset/__init__.py diff --git a/src/aignostics/dataset/_cli.py b/packages/aignostics/src/aignostics/dataset/_cli.py similarity index 99% rename from src/aignostics/dataset/_cli.py rename to packages/aignostics/src/aignostics/dataset/_cli.py index 5e7938828..b7cea2ee1 100644 --- a/src/aignostics/dataset/_cli.py +++ b/packages/aignostics/src/aignostics/dataset/_cli.py @@ -8,7 +8,7 @@ import typer from loguru import logger -from aignostics.utils import console, get_user_data_directory +from aignostics_sdk.utils import console, get_user_data_directory PATH_LENGTH_MAX = 260 TARGET_LAYOUT_DEFAULT = "%collection_id/%PatientID/%StudyInstanceUID/%Modality_%SeriesInstanceUID/" diff --git a/src/aignostics/dataset/_gui.py b/packages/aignostics/src/aignostics/dataset/_gui.py similarity index 99% rename from src/aignostics/dataset/_gui.py rename to packages/aignostics/src/aignostics/dataset/_gui.py index a4bb89c58..7179b2e61 100644 --- a/src/aignostics/dataset/_gui.py +++ b/packages/aignostics/src/aignostics/dataset/_gui.py @@ -5,9 +5,9 @@ from aignostics.gui import frame from aignostics.third_party.showinfm.showinfm import show_in_file_manager -from aignostics.utils import get_user_data_directory +from aignostics.utils import BasePageBuilder, GUILocalFilePicker +from aignostics_sdk.utils import get_user_data_directory -from ..utils import BasePageBuilder, GUILocalFilePicker # noqa: TID252 from ._service import TARGET_LAYOUT_DEFAULT, Service MESSAGE_NO_DOWNLOAD_FOLDER_SELECTED = "No download folder selected" diff --git a/src/aignostics/dataset/_service.py b/packages/aignostics/src/aignostics/dataset/_service.py similarity index 99% rename from src/aignostics/dataset/_service.py rename to packages/aignostics/src/aignostics/dataset/_service.py index 1d1e07da1..cb6e83212 100644 --- a/src/aignostics/dataset/_service.py +++ b/packages/aignostics/src/aignostics/dataset/_service.py @@ -14,8 +14,8 @@ import requests from loguru import logger -from aignostics.platform import generate_signed_url as platform_generate_signed_url -from aignostics.utils import SUBPROCESS_CREATION_FLAGS, BaseService, Health +from aignostics_sdk.platform import generate_signed_url as platform_generate_signed_url +from aignostics_sdk.utils import SUBPROCESS_CREATION_FLAGS, BaseService, Health PATH_LENGTH_MAX = 260 TARGET_LAYOUT_DEFAULT = "%collection_id/%PatientID/%StudyInstanceUID/%Modality_%SeriesInstanceUID/" diff --git a/src/aignostics/dataset/assets/NIH-IDC-logo.svg b/packages/aignostics/src/aignostics/dataset/assets/NIH-IDC-logo.svg similarity index 100% rename from src/aignostics/dataset/assets/NIH-IDC-logo.svg rename to packages/aignostics/src/aignostics/dataset/assets/NIH-IDC-logo.svg diff --git a/src/aignostics/gui/CLAUDE.md b/packages/aignostics/src/aignostics/gui/CLAUDE.md similarity index 100% rename from src/aignostics/gui/CLAUDE.md rename to packages/aignostics/src/aignostics/gui/CLAUDE.md diff --git a/src/aignostics/gui/__init__.py b/packages/aignostics/src/aignostics/gui/__init__.py similarity index 100% rename from src/aignostics/gui/__init__.py rename to packages/aignostics/src/aignostics/gui/__init__.py diff --git a/src/aignostics/gui/_error.py b/packages/aignostics/src/aignostics/gui/_error.py similarity index 96% rename from src/aignostics/gui/_error.py rename to packages/aignostics/src/aignostics/gui/_error.py index 77eda5721..c2f9226d9 100644 --- a/src/aignostics/gui/_error.py +++ b/packages/aignostics/src/aignostics/gui/_error.py @@ -2,7 +2,8 @@ import traceback -from ..utils import BasePageBuilder # noqa: TID252 +from aignostics.utils import BasePageBuilder + from ._frame import frame diff --git a/src/aignostics/gui/_frame.py b/packages/aignostics/src/aignostics/gui/_frame.py similarity index 98% rename from src/aignostics/gui/_frame.py rename to packages/aignostics/src/aignostics/gui/_frame.py index 790bb5a45..1153e7750 100644 --- a/src/aignostics/gui/_frame.py +++ b/packages/aignostics/src/aignostics/gui/_frame.py @@ -15,7 +15,7 @@ from loguru import logger from aignostics.constants import WINDOW_TITLE -from aignostics.utils import __version__, open_user_data_directory +from aignostics_sdk.utils import __version__, open_user_data_directory from ._theme import theme @@ -52,10 +52,10 @@ def frame( # noqa: C901, PLR0915 """ from nicegui import app, background_tasks, context, run, ui # noqa: PLC0415 - from aignostics.platform import Service as PlatformService # noqa: PLC0415 - from aignostics.platform import UserInfo, settings # noqa: PLC0415 from aignostics.system import Service as SystemService # noqa: PLC0415 from aignostics.utils import NavItem, gui_get_nav_groups # noqa: PLC0415 + from aignostics_sdk.platform import Service as PlatformService # noqa: PLC0415 + from aignostics_sdk.platform import UserInfo, settings # noqa: PLC0415 theme() diff --git a/src/aignostics/gui/_theme.py b/packages/aignostics/src/aignostics/gui/_theme.py similarity index 100% rename from src/aignostics/gui/_theme.py rename to packages/aignostics/src/aignostics/gui/_theme.py diff --git a/src/aignostics/gui/assets/cabin-v27-latin-regular.woff2 b/packages/aignostics/src/aignostics/gui/assets/cabin-v27-latin-regular.woff2 similarity index 100% rename from src/aignostics/gui/assets/cabin-v27-latin-regular.woff2 rename to packages/aignostics/src/aignostics/gui/assets/cabin-v27-latin-regular.woff2 diff --git a/src/aignostics/gui/assets/cat.lottie b/packages/aignostics/src/aignostics/gui/assets/cat.lottie similarity index 100% rename from src/aignostics/gui/assets/cat.lottie rename to packages/aignostics/src/aignostics/gui/assets/cat.lottie diff --git a/src/aignostics/gui/assets/logo.png b/packages/aignostics/src/aignostics/gui/assets/logo.png similarity index 100% rename from src/aignostics/gui/assets/logo.png rename to packages/aignostics/src/aignostics/gui/assets/logo.png diff --git a/src/aignostics/notebook/CLAUDE.md b/packages/aignostics/src/aignostics/notebook/CLAUDE.md similarity index 100% rename from src/aignostics/notebook/CLAUDE.md rename to packages/aignostics/src/aignostics/notebook/CLAUDE.md diff --git a/src/aignostics/notebook/__init__.py b/packages/aignostics/src/aignostics/notebook/__init__.py similarity index 100% rename from src/aignostics/notebook/__init__.py rename to packages/aignostics/src/aignostics/notebook/__init__.py diff --git a/src/aignostics/notebook/_gui.py b/packages/aignostics/src/aignostics/notebook/_gui.py similarity index 97% rename from src/aignostics/notebook/_gui.py rename to packages/aignostics/src/aignostics/notebook/_gui.py index 2ff120bce..d8cc8c75e 100644 --- a/src/aignostics/notebook/_gui.py +++ b/packages/aignostics/src/aignostics/notebook/_gui.py @@ -6,7 +6,8 @@ from loguru import logger from aignostics.gui import frame, theme -from aignostics.utils import BasePageBuilder, get_user_data_directory +from aignostics.utils import BasePageBuilder +from aignostics_sdk.utils import get_user_data_directory class PageBuilder(BasePageBuilder): diff --git a/src/aignostics/notebook/_notebook.py b/packages/aignostics/src/aignostics/notebook/_notebook.py similarity index 98% rename from src/aignostics/notebook/_notebook.py rename to packages/aignostics/src/aignostics/notebook/_notebook.py index 0ab8bd6ec..f19a8a77d 100644 --- a/src/aignostics/notebook/_notebook.py +++ b/packages/aignostics/src/aignostics/notebook/_notebook.py @@ -29,7 +29,7 @@ def _(): from dotenv import load_dotenv from aignostics import dataset, WSI_SUPPORTED_FILE_EXTENSIONS from wsidicom import WsiDicom - from aignostics.utils import get_user_data_directory + from aignostics_sdk.utils import get_user_data_directory mo.sidebar([ mo.md("# aignostics"), diff --git a/src/aignostics/notebook/_service.py b/packages/aignostics/src/aignostics/notebook/_service.py similarity index 98% rename from src/aignostics/notebook/_service.py rename to packages/aignostics/src/aignostics/notebook/_service.py index 317f5c2f2..dd70d646e 100644 --- a/src/aignostics/notebook/_service.py +++ b/packages/aignostics/src/aignostics/notebook/_service.py @@ -10,7 +10,7 @@ from loguru import logger from aignostics.constants import NOTEBOOK_DEFAULT -from aignostics.utils import SUBPROCESS_CREATION_FLAGS, BaseService, Health, get_user_data_directory +from aignostics_sdk.utils import SUBPROCESS_CREATION_FLAGS, BaseService, Health, get_user_data_directory MARIMO_SERVER_STARTUP_TIMEOUT = 60 diff --git a/src/aignostics/notebook/assets/python.lottie b/packages/aignostics/src/aignostics/notebook/assets/python.lottie similarity index 100% rename from src/aignostics/notebook/assets/python.lottie rename to packages/aignostics/src/aignostics/notebook/assets/python.lottie diff --git a/src/aignostics/qupath/CLAUDE.md b/packages/aignostics/src/aignostics/qupath/CLAUDE.md similarity index 100% rename from src/aignostics/qupath/CLAUDE.md rename to packages/aignostics/src/aignostics/qupath/CLAUDE.md diff --git a/src/aignostics/qupath/__init__.py b/packages/aignostics/src/aignostics/qupath/__init__.py similarity index 100% rename from src/aignostics/qupath/__init__.py rename to packages/aignostics/src/aignostics/qupath/__init__.py diff --git a/src/aignostics/qupath/_cli.py b/packages/aignostics/src/aignostics/qupath/_cli.py similarity index 99% rename from src/aignostics/qupath/_cli.py rename to packages/aignostics/src/aignostics/qupath/_cli.py index 3f317dca5..5d425ed65 100644 --- a/src/aignostics/qupath/_cli.py +++ b/packages/aignostics/src/aignostics/qupath/_cli.py @@ -9,7 +9,7 @@ from loguru import logger from rich.table import Table -from aignostics.utils import console +from aignostics_sdk.utils import console from ._service import QUPATH_VERSION, Service diff --git a/src/aignostics/qupath/_gui.py b/packages/aignostics/src/aignostics/qupath/_gui.py similarity index 100% rename from src/aignostics/qupath/_gui.py rename to packages/aignostics/src/aignostics/qupath/_gui.py diff --git a/src/aignostics/qupath/_service.py b/packages/aignostics/src/aignostics/qupath/_service.py similarity index 99% rename from src/aignostics/qupath/_service.py rename to packages/aignostics/src/aignostics/qupath/_service.py index d651ad970..ff64df309 100644 --- a/src/aignostics/qupath/_service.py +++ b/packages/aignostics/src/aignostics/qupath/_service.py @@ -26,7 +26,7 @@ from psutil import Process, wait_procs from pydantic import BaseModel, computed_field -from aignostics.utils import ( +from aignostics_sdk.utils import ( SUBPROCESS_CREATION_FLAGS, BaseService, Health, diff --git a/src/aignostics/qupath/_settings.py b/packages/aignostics/src/aignostics/qupath/_settings.py similarity index 66% rename from src/aignostics/qupath/_settings.py rename to packages/aignostics/src/aignostics/qupath/_settings.py index 728c73cc4..071ba939c 100644 --- a/src/aignostics/qupath/_settings.py +++ b/packages/aignostics/src/aignostics/qupath/_settings.py @@ -2,14 +2,14 @@ from pydantic_settings import SettingsConfigDict -from ..utils import OpaqueSettings, __env_file__, __project_name__ # noqa: TID252 +from aignostics_sdk.utils import ENV_PREFIX, OpaqueSettings, __env_file__ class Settings(OpaqueSettings): """Settings.""" model_config = SettingsConfigDict( - env_prefix=f"{__project_name__.upper()}_QUPATH_", + env_prefix=f"{ENV_PREFIX}_QUPATH_", extra="ignore", env_file=__env_file__, env_file_encoding="utf-8", diff --git a/src/aignostics/qupath/assets/download.lottie b/packages/aignostics/src/aignostics/qupath/assets/download.lottie similarity index 100% rename from src/aignostics/qupath/assets/download.lottie rename to packages/aignostics/src/aignostics/qupath/assets/download.lottie diff --git a/src/aignostics/qupath/assets/microscope.lottie b/packages/aignostics/src/aignostics/qupath/assets/microscope.lottie similarity index 100% rename from src/aignostics/qupath/assets/microscope.lottie rename to packages/aignostics/src/aignostics/qupath/assets/microscope.lottie diff --git a/src/aignostics/qupath/assets/update.lottie b/packages/aignostics/src/aignostics/qupath/assets/update.lottie similarity index 100% rename from src/aignostics/qupath/assets/update.lottie rename to packages/aignostics/src/aignostics/qupath/assets/update.lottie diff --git a/src/aignostics/qupath/scripts/add.groovy b/packages/aignostics/src/aignostics/qupath/scripts/add.groovy similarity index 100% rename from src/aignostics/qupath/scripts/add.groovy rename to packages/aignostics/src/aignostics/qupath/scripts/add.groovy diff --git a/src/aignostics/qupath/scripts/annotate.groovy b/packages/aignostics/src/aignostics/qupath/scripts/annotate.groovy similarity index 100% rename from src/aignostics/qupath/scripts/annotate.groovy rename to packages/aignostics/src/aignostics/qupath/scripts/annotate.groovy diff --git a/src/aignostics/qupath/scripts/inspect.groovy b/packages/aignostics/src/aignostics/qupath/scripts/inspect.groovy similarity index 100% rename from src/aignostics/qupath/scripts/inspect.groovy rename to packages/aignostics/src/aignostics/qupath/scripts/inspect.groovy diff --git a/src/aignostics/qupath/scripts/test.groovy b/packages/aignostics/src/aignostics/qupath/scripts/test.groovy similarity index 100% rename from src/aignostics/qupath/scripts/test.groovy rename to packages/aignostics/src/aignostics/qupath/scripts/test.groovy diff --git a/src/aignostics/system/__init__.py b/packages/aignostics/src/aignostics/system/__init__.py similarity index 53% rename from src/aignostics/system/__init__.py rename to packages/aignostics/src/aignostics/system/__init__.py index 1dab2d033..50d18e240 100644 --- a/src/aignostics/system/__init__.py +++ b/packages/aignostics/src/aignostics/system/__init__.py @@ -1,8 +1,10 @@ -"""System module.""" +"""System module — re-exports from slim aignostics_sdk.system.""" -from ._cli import cli -from ._service import Service -from ._settings import Settings +from aignostics_sdk.system import ( + Service, + Settings, + cli, +) __all__ = [ "Service", @@ -10,12 +12,11 @@ "cli", ] - from importlib.util import find_spec # advertise PageBuilder to enable auto-discovery if find_spec("nicegui"): - from ._gui import PageBuilder + from aignostics.system._gui import PageBuilder __all__ += [ "PageBuilder", diff --git a/src/aignostics/system/_gui.py b/packages/aignostics/src/aignostics/system/_gui.py similarity index 97% rename from src/aignostics/system/_gui.py rename to packages/aignostics/src/aignostics/system/_gui.py index 54aad49ca..bf59120d7 100644 --- a/src/aignostics/system/_gui.py +++ b/packages/aignostics/src/aignostics/system/_gui.py @@ -3,10 +3,9 @@ from pathlib import Path from aignostics.gui import frame -from aignostics.utils import BaseService, locate_subclasses - -from ..utils import BasePageBuilder # noqa: TID252 -from ._service import Service +from aignostics.utils import BasePageBuilder, locate_subclasses +from aignostics_sdk.system._service import Service # noqa: PLC2701 +from aignostics_sdk.utils import BaseService class PageBuilder(BasePageBuilder): diff --git a/src/aignostics/third_party/bottle.py b/packages/aignostics/src/aignostics/third_party/bottle.py similarity index 100% rename from src/aignostics/third_party/bottle.py rename to packages/aignostics/src/aignostics/third_party/bottle.py diff --git a/src/aignostics/third_party/idc_index.py b/packages/aignostics/src/aignostics/third_party/idc_index.py similarity index 99% rename from src/aignostics/third_party/idc_index.py rename to packages/aignostics/src/aignostics/third_party/idc_index.py index 8f585cfa3..28ce44a09 100644 --- a/src/aignostics/third_party/idc_index.py +++ b/packages/aignostics/src/aignostics/third_party/idc_index.py @@ -63,7 +63,7 @@ from tqdm import tqdm from loguru import logger -from aignostics.utils import SUBPROCESS_CREATION_FLAGS +from aignostics_sdk.utils import SUBPROCESS_CREATION_FLAGS aws_endpoint_url = "https://s3.amazonaws.com" gcp_endpoint_url = "https://storage.googleapis.com" diff --git a/src/aignostics/third_party/showinfm/__init__.py b/packages/aignostics/src/aignostics/third_party/showinfm/__init__.py similarity index 100% rename from src/aignostics/third_party/showinfm/__init__.py rename to packages/aignostics/src/aignostics/third_party/showinfm/__init__.py diff --git a/src/aignostics/third_party/showinfm/argumentsparse.py b/packages/aignostics/src/aignostics/third_party/showinfm/argumentsparse.py similarity index 100% rename from src/aignostics/third_party/showinfm/argumentsparse.py rename to packages/aignostics/src/aignostics/third_party/showinfm/argumentsparse.py diff --git a/src/aignostics/third_party/showinfm/constants.py b/packages/aignostics/src/aignostics/third_party/showinfm/constants.py similarity index 100% rename from src/aignostics/third_party/showinfm/constants.py rename to packages/aignostics/src/aignostics/third_party/showinfm/constants.py diff --git a/src/aignostics/third_party/showinfm/py.typed b/packages/aignostics/src/aignostics/third_party/showinfm/py.typed similarity index 100% rename from src/aignostics/third_party/showinfm/py.typed rename to packages/aignostics/src/aignostics/third_party/showinfm/py.typed diff --git a/src/aignostics/third_party/showinfm/showinfm.py b/packages/aignostics/src/aignostics/third_party/showinfm/showinfm.py similarity index 100% rename from src/aignostics/third_party/showinfm/showinfm.py rename to packages/aignostics/src/aignostics/third_party/showinfm/showinfm.py diff --git a/src/aignostics/third_party/showinfm/system/__init__.py b/packages/aignostics/src/aignostics/third_party/showinfm/system/__init__.py similarity index 100% rename from src/aignostics/third_party/showinfm/system/__init__.py rename to packages/aignostics/src/aignostics/third_party/showinfm/system/__init__.py diff --git a/src/aignostics/third_party/showinfm/system/linux.py b/packages/aignostics/src/aignostics/third_party/showinfm/system/linux.py similarity index 100% rename from src/aignostics/third_party/showinfm/system/linux.py rename to packages/aignostics/src/aignostics/third_party/showinfm/system/linux.py diff --git a/src/aignostics/third_party/showinfm/system/tools.py b/packages/aignostics/src/aignostics/third_party/showinfm/system/tools.py similarity index 100% rename from src/aignostics/third_party/showinfm/system/tools.py rename to packages/aignostics/src/aignostics/third_party/showinfm/system/tools.py diff --git a/src/aignostics/third_party/showinfm/system/urivalidate.py b/packages/aignostics/src/aignostics/third_party/showinfm/system/urivalidate.py similarity index 100% rename from src/aignostics/third_party/showinfm/system/urivalidate.py rename to packages/aignostics/src/aignostics/third_party/showinfm/system/urivalidate.py diff --git a/src/aignostics/third_party/showinfm/system/windows.py b/packages/aignostics/src/aignostics/third_party/showinfm/system/windows.py similarity index 100% rename from src/aignostics/third_party/showinfm/system/windows.py rename to packages/aignostics/src/aignostics/third_party/showinfm/system/windows.py diff --git a/src/aignostics/utils/__init__.py b/packages/aignostics/src/aignostics/utils/__init__.py similarity index 75% rename from src/aignostics/utils/__init__.py rename to packages/aignostics/src/aignostics/utils/__init__.py index 7781cbf7d..c1b60ae28 100644 --- a/src/aignostics/utils/__init__.py +++ b/packages/aignostics/src/aignostics/utils/__init__.py @@ -1,8 +1,17 @@ -"""Utilities module.""" +"""Full utils — re-exports slim aignostics_sdk.utils and adds heavy-only utilities.""" -from ._cli import prepare_cli -from ._console import console -from ._constants import ( +from __future__ import annotations + +from aignostics_sdk.utils import ( + ENV_PREFIX, + SUBPROCESS_CREATION_FLAGS, + UNHIDE_SENSITIVE_INFO, + BaseService, + Health, + HealthStatus, + LogSettings, + OpaqueSettings, + ProcessInfo, __author_email__, __author_name__, __base__url__, @@ -21,20 +30,25 @@ __repository_url__, __version__, __version_full__, + boot, + console, + get_process_info, + get_user_data_directory, + load_settings, + open_user_data_directory, + prepare_cli, + sanitize_path, + sanitize_path_component, + strip_to_none_before_validator, + user_agent, ) + from ._di import discover_plugin_packages, load_modules, locate_implementations, locate_subclasses -from ._fs import get_user_data_directory, open_user_data_directory, sanitize_path, sanitize_path_component -from ._health import Health, HealthStatus -from ._log import LogSettings from ._mcp import MCP_SERVER_NAME, MCP_TRANSPORT, mcp_create_server, mcp_discover_servers, mcp_list_tools, mcp_run from ._nav import BaseNavBuilder, NavGroup, NavItem, gui_get_nav_groups -from ._process import SUBPROCESS_CREATION_FLAGS, ProcessInfo, get_process_info -from ._service import BaseService -from ._settings import UNHIDE_SENSITIVE_INFO, OpaqueSettings, load_settings, strip_to_none_before_validator -from ._user_agent import user_agent -from .boot import boot __all__ = [ + "ENV_PREFIX", "MCP_SERVER_NAME", "MCP_TRANSPORT", "SUBPROCESS_CREATION_FLAGS", @@ -91,7 +105,7 @@ from importlib.util import find_spec if find_spec("sentry"): - from ._sentry import SentrySettings + from aignostics_sdk.utils import SentrySettings __all__ += ["SentrySettings"] @@ -101,8 +115,6 @@ __all__ += ["BasePageBuilder", "GUILocalFilePicker", "gui_register_pages", "gui_run"] if find_spec("marimo"): - from ._notebook import create_marimo_app + from aignostics_sdk.utils import create_marimo_app - __all__ += [ - "create_marimo_app", - ] + __all__ += ["create_marimo_app"] diff --git a/src/aignostics/utils/_di.py b/packages/aignostics/src/aignostics/utils/_di.py similarity index 81% rename from src/aignostics/utils/_di.py rename to packages/aignostics/src/aignostics/utils/_di.py index 000066dee..7ce790a5d 100644 --- a/src/aignostics/utils/_di.py +++ b/packages/aignostics/src/aignostics/utils/_di.py @@ -8,14 +8,20 @@ from inspect import isclass from typing import Any -from ._constants import __project_name__ - _implementation_cache: dict[Any, list[Any]] = {} _subclass_cache: dict[Any, list[Any]] = {} # Entry point group name for aignostics plugins PLUGIN_ENTRY_POINT_GROUP = "aignostics.plugins" +# Packages to deep-scan for DI discovery. +# "aignostics_sdk" is the slim package name (importable module name). +# Scans the full "aignostics" package if installed (handled gracefully via ImportError). +# Does NOT scan the slim package (aignostics_sdk) itself for typer instances — slim CLI +# commands are wired into the heavy CLI via [project.entry-points."aignostics.cli"]. +# BaseService subclasses in the slim package ARE scanned (needed for health checks etc.) +_SCAN_PACKAGES = ("aignostics_sdk", "aignostics") + @lru_cache(maxsize=1) def discover_plugin_packages() -> tuple[str, ...]: @@ -37,9 +43,17 @@ def discover_plugin_packages() -> tuple[str, ...]: def load_modules() -> None: - package = importlib.import_module(__project_name__) - for _, name, _ in pkgutil.iter_modules(package.__path__): - importlib.import_module(f"{__project_name__}.{name}") + """Load all top-level modules from all scan packages.""" + for pkg_name in _SCAN_PACKAGES: + try: + package = importlib.import_module(pkg_name) + except ImportError: + continue + for _, name, _ in pkgutil.iter_modules(package.__path__): + try: + importlib.import_module(f"{pkg_name}.{name}") + except ImportError: + continue def _scan_packages_deep(package_name: str, predicate: Callable[[object], bool]) -> list[Any]: @@ -132,7 +146,7 @@ def predicate(member: object) -> bool: results = [ *_scan_packages_shallow(discover_plugin_packages(), predicate), - *_scan_packages_deep(__project_name__, predicate), + *(item for pkg in _SCAN_PACKAGES for item in _scan_packages_deep(pkg, predicate)), ] _implementation_cache[_class] = results @@ -160,7 +174,7 @@ def predicate(member: object) -> bool: results = [ *_scan_packages_shallow(discover_plugin_packages(), predicate), - *_scan_packages_deep(__project_name__, predicate), + *(item for pkg in _SCAN_PACKAGES for item in _scan_packages_deep(pkg, predicate)), ] _subclass_cache[_class] = results diff --git a/src/aignostics/utils/_gui.py b/packages/aignostics/src/aignostics/utils/_gui.py similarity index 98% rename from src/aignostics/utils/_gui.py rename to packages/aignostics/src/aignostics/utils/_gui.py index d6eb18f7f..3245a2e6b 100644 --- a/src/aignostics/utils/_gui.py +++ b/packages/aignostics/src/aignostics/utils/_gui.py @@ -3,7 +3,8 @@ from pathlib import Path from types import EllipsisType -from ._constants import __is_running_in_container__, __project_name__ +from aignostics_sdk.utils._constants import __is_running_in_container__, __project_name__ + from ._di import locate_subclasses WINDOW_SIZE = (1280, 768) # Default window size for the GUI diff --git a/src/aignostics/utils/_mcp.py b/packages/aignostics/src/aignostics/utils/_mcp.py similarity index 96% rename from src/aignostics/utils/_mcp.py rename to packages/aignostics/src/aignostics/utils/_mcp.py index 10feeb7ef..3905cc81a 100644 --- a/src/aignostics/utils/_mcp.py +++ b/packages/aignostics/src/aignostics/utils/_mcp.py @@ -13,7 +13,7 @@ uv run aignostics mcp list-tools # Use programmatically - from aignostics.utils import mcp_create_server, mcp_run, mcp_list_tools + from aignostics_sdk.utils import mcp_create_server, mcp_run, mcp_list_tools server = mcp_create_server() mcp_run() @@ -27,7 +27,8 @@ from fastmcp import FastMCP from loguru import logger -from ._constants import __version__ +from aignostics_sdk.utils._constants import __version__ + from ._di import locate_implementations MCP_SERVER_NAME = "Central Aignostics MCP Server" diff --git a/src/aignostics/utils/_nav.py b/packages/aignostics/src/aignostics/utils/_nav.py similarity index 100% rename from src/aignostics/utils/_nav.py rename to packages/aignostics/src/aignostics/utils/_nav.py diff --git a/src/aignostics/wsi/CLAUDE.md b/packages/aignostics/src/aignostics/wsi/CLAUDE.md similarity index 100% rename from src/aignostics/wsi/CLAUDE.md rename to packages/aignostics/src/aignostics/wsi/CLAUDE.md diff --git a/src/aignostics/wsi/__init__.py b/packages/aignostics/src/aignostics/wsi/__init__.py similarity index 100% rename from src/aignostics/wsi/__init__.py rename to packages/aignostics/src/aignostics/wsi/__init__.py diff --git a/src/aignostics/wsi/_cli.py b/packages/aignostics/src/aignostics/wsi/_cli.py similarity index 99% rename from src/aignostics/wsi/_cli.py rename to packages/aignostics/src/aignostics/wsi/_cli.py index f70518468..f18a9b34b 100644 --- a/src/aignostics/wsi/_cli.py +++ b/packages/aignostics/src/aignostics/wsi/_cli.py @@ -7,7 +7,7 @@ import typer from loguru import logger -from aignostics.utils import console +from aignostics_sdk.utils import console from ._service import Service from ._utils import print_slide_info, print_study_info diff --git a/src/aignostics/wsi/_gui.py b/packages/aignostics/src/aignostics/wsi/_gui.py similarity index 100% rename from src/aignostics/wsi/_gui.py rename to packages/aignostics/src/aignostics/wsi/_gui.py diff --git a/src/aignostics/wsi/_openslide_handler.py b/packages/aignostics/src/aignostics/wsi/_openslide_handler.py similarity index 100% rename from src/aignostics/wsi/_openslide_handler.py rename to packages/aignostics/src/aignostics/wsi/_openslide_handler.py diff --git a/src/aignostics/wsi/_pydicom_handler.py b/packages/aignostics/src/aignostics/wsi/_pydicom_handler.py similarity index 99% rename from src/aignostics/wsi/_pydicom_handler.py rename to packages/aignostics/src/aignostics/wsi/_pydicom_handler.py index 2d9238255..cc0a087e2 100644 --- a/src/aignostics/wsi/_pydicom_handler.py +++ b/packages/aignostics/src/aignostics/wsi/_pydicom_handler.py @@ -14,7 +14,7 @@ from pydicom.sr.coding import Code from shapely.geometry import Polygon -from aignostics.utils import console +from aignostics_sdk.utils import console from ._utils import select_dicom_files diff --git a/src/aignostics/wsi/_service.py b/packages/aignostics/src/aignostics/wsi/_service.py similarity index 99% rename from src/aignostics/wsi/_service.py rename to packages/aignostics/src/aignostics/wsi/_service.py index 358cde04b..3a6da1c21 100644 --- a/src/aignostics/wsi/_service.py +++ b/packages/aignostics/src/aignostics/wsi/_service.py @@ -9,7 +9,7 @@ from loguru import logger from aignostics import WSI_SUPPORTED_FILE_EXTENSIONS -from aignostics.utils import BaseService, Health +from aignostics_sdk.utils import BaseService, Health from ._openslide_handler import DEFAULT_MAX_SAFE_DIMENSION from ._utils import select_dicom_files diff --git a/src/aignostics/wsi/_utils.py b/packages/aignostics/src/aignostics/wsi/_utils.py similarity index 99% rename from src/aignostics/wsi/_utils.py rename to packages/aignostics/src/aignostics/wsi/_utils.py index 6a8437975..cf4712eeb 100644 --- a/src/aignostics/wsi/_utils.py +++ b/packages/aignostics/src/aignostics/wsi/_utils.py @@ -8,7 +8,7 @@ import pydicom from loguru import logger -from aignostics.utils import console +from aignostics_sdk.utils import console def print_file_info(file_info: dict[str, Any], indent: int = 0) -> None: # noqa: C901, PLR0912, PLR0915 diff --git a/src/aignostics/wsi/assets/fallback.png b/packages/aignostics/src/aignostics/wsi/assets/fallback.png similarity index 100% rename from src/aignostics/wsi/assets/fallback.png rename to packages/aignostics/src/aignostics/wsi/assets/fallback.png diff --git a/pyproject.toml b/pyproject.toml index 796a1de07..286dfb8d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,169 +1,3 @@ -[project] -name = "aignostics" -version = "1.4.0" -description = "🔬 Python SDK providing access to the Aignostics Platform. Includes Aignostics Launchpad (Desktop Application), Aignostics CLI (Command-Line Interface), example notebooks, and Aignostics Client Library." -readme = "README.md" -authors = [ - { name = "Helmut Hoffer von Ankershoffen", email = "helmut@aignostics.com" }, - { name = "Andreas Kunft", email = "andreas@aignostics.com" }, -] -license = { file = "LICENSE" } - -keywords = [ - "aignostics", - "atlas", - "whole-slide-imaging", - "machine-learning", - "digital-pathology", - "medical-imaging", - "qupath", - "dicom", - "pydicom", - "openslide", - "image-data-commons", - "act", - "codecov", - "copier", - "cyclonedx", - "devcontainer", - "detect-secrets", - "docker", - "git-cliff", - "jupyter", - "marimo", - "mypy", - "nox", - "nicegui", - "oe-python-template", - "pip-audit", - "pip-licenses", - "pre-commit", - "pydantic", - "pytest", - "python", - "pypi", - "readthedocs", - "ruff", - "sonarqube", - "sonarcloud", - "sphinx", - "typer", - "uv", -] - -classifiers = [ - "Development Status :: 3 - Alpha", - "Intended Audience :: Developers", - "Intended Audience :: End Users/Desktop", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: 3.14", - "License :: OSI Approved :: MIT License", - "Operating System :: MacOS :: MacOS X", - "Operating System :: POSIX :: Linux", - "Operating System :: Microsoft :: Windows", - "Framework :: Pydantic", - "Framework :: Pytest", - "Typing :: Typed", - "Natural Language :: English", -] - -# WARNING: The upper bound of requires-python is *not* respected by uvx -requires-python = ">=3.11, <3.15" - -dependencies = [ - # From Template - "fastapi[all,standard]>=0.123.10", - "humanize>=4.14.0,<5", - "nicegui[native]>=3.11.0,<4", # CVE-2026-21871, CVE-2026-21873, CVE-2026-21874 (>=3.5.0); CVE-2026-25516 (>=3.7.0, #418); CVE-2026-27156 (>=3.8.0, #448); CVE-2026-33332 (>=3.9.0, #498); CVE-2026-39844 (>=3.10.0, #531). 3.11.0 fixes async event handler exception leaks and refines ValueChangeEventArguments generics. - "packaging>=26,<27", - "platformdirs>=4.5.1,<5", - "psutil>=7.1.3,<8", - "pydantic-settings>=2.12.0,<3", - "pywin32>=311,<312; sys_platform == 'win32'", - "pyyaml>=6.0.3,<7", - "sentry-sdk>=2.47.0,<3", - "typer>=0.20.0,<1", - # Custom - "boto3>=1.42.4,<2", - "certifi>=2025.11.12", - "defusedxml>=0.7.1", - "dicom-validator>=0.7.3,<1", - "dicomweb-client[gcp]>=0.59.3,<1", - "duckdb>=1.4.2,<=2", - "google-cloud-storage>=3.6.0,<4", - "crc32c>=2.8,<3", # TODO(Helmut): Remove and back to google_crc32c when that supports Python 3.14 - "highdicom>=0.26.1,<1; python_version < '3.14'", # transitive dependency pyjpegls not yet supporting Python 3.14 - "html-sanitizer>=2.6.0,<3", - "httpx>=0.28.1,<1", - "idc-index-data==24.0.3", - "ijson>=3.4.0.post0,<4", - "jsf>=0.11.2,<1", - "jsonschema[format-nongpl]>=4.25.1,<5", - "loguru>=0.7.3,<1", - "openslide-bin>=4.0.0.10,<5", - "openslide-python>=1.4.3,<2", - "pandas>=2.3.3,<4", - "platformdirs>=4.3.2,<5", - "procrastinate>=3.5.3", - "fastparquet>=2026.3.0,<2026.4.0; python_version < '3.14'", - "pyarrow>=23.0.1,<24; python_version >= '3.14'", - "pyjwt[crypto]>=2.12.0,<3", # CVE-2026-32597 requires >=2.12.0 (Renovate #475) - "python-dateutil>=2.9.0.post0,<3", - # "pywebview[qt6]>=5.4,<6; sys_platform == 'linux'", - "requests>=2.33.0,<3", # CVE-2026-25645 requires >= 2.33.0 - "requests-oauthlib>=2.0.0,<3", - "s5cmd>=0.3.3,<1", - "semver>=3.0.4,<4", - "shapely>=2.1.2,<3", - "tenacity>=9.1.2,<10", - "tqdm>=4.67.1,<5", - "truststore>=0.10.4,<1", - "urllib3>=2.6.3,<3", # CVE-2026-21441 requires >= 2.6.3 - "wsidicom>=0.28.1,<1", - "fastmcp>=3.2.0,<4", - # Transitive overrides: lower bounds enforced to shield consumers from known CVEs/GHSAs. - # WARNING: one cannot negate or downgrade a dependency required here. use override-dependencies for that. - "rfc3987; sys_platform == 'never'", # GPLv3 - "h11>=0.16.0", # CVE-2025-43859 - "tornado>=6.5.5", # CVE-2025-47287 (>=6.5.0); GHSA-78cv-mqj4-43f7 (>=6.5.5, Renovate #472) - "urllib3>=2.5.0", # CVE-2025-50181, CVE-2025-50182 - "pillow>=12.2.0", # CVE-2025-48379 (>=11.3.0); CVE-2026-25990 (>=12.1.1, Renovate #428); CVE-2026-40192 (>=12.2.0, Renovate #539) - "aiohttp>=3.13.4", # CVE-2025-53643, CVE-2025-69223..9 (>=3.13.3); CVE-2026-22815 (>=3.13.4, Renovate #527) - "starlette>=1.0.1", # CVE-2025-54121, GHSA-7f5h-v6xp-fcq8, PYSEC-2026-161 - "lxml>=6.1.0", # CVE-2026-41066 (Renovate #556); also required for python 3.14 pre-built wheels - "filelock>=3.20.3", # CVE-2025-68146 (>=3.20.1); CVE-2026-22701 (>=3.20.3, Renovate #387) - "marshmallow>=3.26.2", # CVE-2025-68480 - "pygments>=2.20.0", # CVE-2026-4539 (>=2.20.0); transitive via rich - "cryptography>=46.0.7", # CVE-2026-39892 (>=46.0.7); transitive via pyjwt[crypto] - "pydicom>=3.0.2", # CVE-2026-32711 (>=3.0.2); transitive via dicomweb-client/wsidicom/highdicom - "pyasn1>=0.6.3", # CVE-2026-30922 (>=0.6.3); transitive via cryptography - "lxml-html-clean>=0.4.4", # CVE-2026-28348, CVE-2026-28350 (>=0.4.4); transitive via html-sanitizer - "python-multipart>=0.0.26", # CVE-2026-24486 (>=0.0.22), CVE-2026-40347 (>=0.0.26); transitive via fastapi/starlette - "protobuf>=6.33.5", # CVE-2026-0994 (>=6.33.5); transitive via google-cloud-storage/sentry-sdk -] - -[project.optional-dependencies] -pyinstaller = ["pyinstaller>=6.14.0,<7"] -jupyter = [ - "jupyter>=1.1.1,<2", - # Transitive overrides - # WARNING: one cannot negate or downgrade a dependency required here. use override-dependencies for that. - "jupyter-core>=5.8.1", # CVE-2025-30167 - "jupyterlab>=4.4.9", # CVE-2025-59842 - "nbconvert>=7.17.1", # CVE-2025-53000 (>=7.17.0, Dependabot #424); CVE-2026-39377, CVE-2026-39378 (>=7.17.1, Dependabot #553) -] -marimo = [ - "cloudpathlib>=0.23.0,<1", - "ipython>=9.8.0,<10", - "marimo>=0.23.0,<1", # GHSA-2679-6mx9-h9xc (Renovate #533) - "matplotlib>=3.10.7,<4", - "shapely>=2.1.0,<3", -] -qupath = [] [dependency-groups] dev = [ "autodoc-pydantic>=2.2.0,<3", @@ -231,28 +65,8 @@ override-dependencies = [ # https://github.com/astral-sh/uv/issues/4422 [tool.uv.sources] # No additional sources outside of src/ yet -[project.scripts] -aignostics = "aignostics.cli:cli" - -[project.urls] -Homepage = "https://aignostics.readthedocs.io/en/latest/" -Documentation = "https://aignostics.readthedocs.io/en/latest/" -Source = "https://github.com/aignostics/python-sdk" -Changelog = "https://github.com/aignostics/python-sdk/releases" -Issues = "https://github.com/aignostics/python-sdk/issues" - -[build-system] -requires = ["hatchling==1.29.0"] -build-backend = "hatchling.build" - -[tool.hatch.build] -include = ["src/*", "codegen/out/*", "examples"] - -[tool.hatch.build.targets.wheel] -packages = ["src/aignostics", "codegen/out/aignx", "examples"] - -[tool.hatch.metadata] -allow-direct-references = true +[tool.uv.workspace] +members = ["packages/*"] [tool.ruff] target-version = "py311" @@ -268,6 +82,7 @@ extend-exclude = [ "examples/*.py", "examples/*.ipynb", "codegen", + "**/_codegen/**", ] [tool.ruff.lint] @@ -295,7 +110,9 @@ ignore = [ "T201", # Remove `print` "INP001", # Checks for packages that are missing an __init__.py file. "BLE001", # Accept for now TODO(Helmut): Add proper exception handling later - "ASYNC240" # Aiopath broken in Python 3.11 which we want to support + "ASYNC240", # Aiopath broken in Python 3.11 which we want to support + "PLW0717", # try clause contains too many statements -> new in ruff 0.15.13, pre-existing patterns, address in individual PRs + "RUF075", # fallible context manager -> new in ruff 0.15.13, pre-existing patterns, address in individual PRs ] [tool.ruff.lint.per-file-ignores] @@ -333,7 +150,15 @@ docstring-code-format = true convention = "google" [tool.mypy] # https://mypy.readthedocs.io/en/latest/config_file.html -exclude = ["src/aignostics/third_party", "_notebook.py", "_pydicom_handler.py"] +exclude = [ + "packages/aignostics-sdk/src/aignostics_sdk/third_party", + "packages/aignostics/src/aignostics/third_party", + "packages/aignostics/src/aignostics/notebook/_notebook.py", + "packages/aignostics/src/aignostics/wsi/_pydicom_handler.py", + "packages/aignostics/src/aignostics/wsi/_openslide_handler.py", + "packages/aignostics-sdk/src/aignx", + "codegen", +] junit_xml = "reports/mypy_junit.xml" plugins = "pydantic.mypy" strict = true @@ -346,6 +171,17 @@ show_error_codes = true show_error_context = true warn_unreachable = true +[[tool.mypy.overrides]] +module = "aignx.*" +ignore_errors = true +follow_imports = "skip" + +[[tool.mypy.overrides]] +module = "aignostics_sdk._codegen.*" +ignore_errors = true +follow_imports = "skip" + + [tool.pydantic-mypy] # https://docs.pydantic.dev/latest/integrations/mypy/#configuring-the-plugin init_forbid_extra = true init_typed = true @@ -356,7 +192,7 @@ warn_untyped_fields = true main_file = "tests/main.py" testpaths = ["tests"] python_files = ["*_test.py", "test_*.py"] -addopts = "-p nicegui.testing.plugin -v --strict-markers --log-disable=aignostics --cov=aignostics --cov-report=term-missing --cov-report=xml:reports/coverage.xml --cov-report=html:reports/coverage_html" +addopts = "-p nicegui.testing.plugin -v --strict-markers --log-disable=aignostics --cov=aignostics_sdk --cov=aignostics --cov-report=term-missing --cov-report=xml:reports/coverage.xml --cov-report=html:reports/coverage_html" asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" timeout = 10 # We use a rather short default timeout. Override with @pytest.mark.timeout(timeout=N) @@ -382,6 +218,7 @@ markers = [ "unit: Solitary unit tests - test a layer of a module in isolation with all dependencies mocked, except interaction with shared utils and the systems module. Unit tests must be able to pass offline, i.e. not calls to external services. The timeout should not be bigger than the default 10s, and must be <5 min.", "integration: Sociable integration tests - test interactions across architectural layers (e.g. CLI/GUI→Service, Service→Utils) or between modules (e.g. Application→Platform), using real SDK collaborators, real file I/O, real subprocesses, and real Docker containers. Integration test must be able to pass offline, i.e. mock external services (Aignostics Platform API, Auth0, S3/GCS buckets, IDC). The timeout should not be bigger than the default 10s, and must be <5 min.", "e2e: End-to-end tests - test complete workflows with real external network services (Aignostics Platform API, cloud storage, IDC, etc). If the test timeout is >= 5 min and < 60 min, additionally mark as `long_running`, if >= 60min mark as 'very_long_running'.", + "slim: Tests for the aignostics-sdk slim package distribution.", ] md_report = true md_report_output = "reports/pytest.md" @@ -393,20 +230,27 @@ md_report_exclude_outcomes = ["passed", "skipped"] [tool.coverage.run] sigterm = true relative_files = true -source = ["src"] +source = [ + "packages/aignostics-sdk/src", + "packages/aignostics/src", +] omit = [ - "src/aignostics/aignostics.py", - "src/aignostics/third_party/*", - "src/aignostics/notebook/_notebook.py", - "src/aignostics/wsi/_pydicom_handler.py", - "src/aignostics/wsi/_openslide_handler.py", + "*/aignostics/aignostics.py", + "*/aignostics/third_party/*", + "*/aignostics_sdk/third_party/*", + "*/aignostics/notebook/_notebook.py", + "*/aignostics/wsi/_pydicom_handler.py", + "*/aignostics/wsi/_openslide_handler.py", ] branch = true parallel = true concurrency = ["thread", "multiprocessing"] [tool.coverage.paths] -source = ["src/"] +source = [ + "packages/aignostics-sdk/src", + "packages/aignostics/src", +] [tool.bumpversion] @@ -434,15 +278,26 @@ pre_commit_hooks = [ post_commit_hooks = [] [[tool.bumpversion.files]] -filename = "pyproject.toml" +filename = "packages/aignostics-sdk/pyproject.toml" +search = """ +[project] +name = \"aignostics-sdk\" +version = \"{current_version}\"\"""" +replace = """ +[project] +name = \"aignostics-sdk\" +version = \"{new_version}\"\"""" + +[[tool.bumpversion.files]] +filename = "packages/aignostics/pyproject.toml" search = """ [project] name = \"aignostics\" -version = \"{current_version}\"""" +version = \"{current_version}\"\"""" replace = """ [project] name = \"aignostics\" -version = \"{new_version}\"""" +version = \"{new_version}\"\"""" [[tool.bumpversion.files]] filename = "VERSION" diff --git a/pyrightconfig.json b/pyrightconfig.json index b6a233da8..1497e9964 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -1,5 +1,10 @@ { "typeCheckingMode": "basic", + "include": [ + "packages/aignostics-sdk/src", + "packages/aignostics/src", + "noxfile.py" + ], "exclude": [ "**/.nox/**", "**/.venv/**", @@ -8,6 +13,8 @@ "**/dist_vercel/.vercel/**", "**/dist_native/**", "**/site-packages/**", + "**/aignx/**", + "**/_codegen/**" ], "ignore": [ "**/third_party/**", @@ -17,10 +24,11 @@ "template/**", "tests/**", "codegen/**", - "src/aignostics/wsi/_pydicom_handler.py", - "src/aignostics/notebook/_notebook.py", + "examples/**", + "packages/aignostics/src/aignostics/wsi/_pydicom_handler.py", + "packages/aignostics/src/aignostics/notebook/_notebook.py" ], "extraPaths": [ - "./src/aignostics/utils/_" + "./packages/aignostics/src/aignostics/utils" ] -} \ No newline at end of file +} diff --git a/runner/gui_watch.py b/runner/gui_watch.py index 7652577aa..b13e05f10 100644 --- a/runner/gui_watch.py +++ b/runner/gui_watch.py @@ -1,6 +1,6 @@ """Graphical User Interface (GUI) of Aignostics Python SDK.""" -from aignostics.utils import gui_run +from aignostics_sdk.utils import gui_run # For development run via `uv run watch_gui.py` gui_run(native=False, show=True, watch=True, dark_mode=False) diff --git a/src/aignostics.py b/src/aignostics.py index caf2cd103..19463a3c0 100644 --- a/src/aignostics.py +++ b/src/aignostics.py @@ -24,7 +24,8 @@ os.environ["LOGFIRE_PYDANTIC_RECORD"] = "off" from aignostics.constants import SENTRY_INTEGRATIONS, WINDOW_TITLE # noqa: E402 -from aignostics.utils import boot, gui_run # noqa: E402 + +from aignostics_sdk.utils import boot, gui_run # noqa: E402 boot(SENTRY_INTEGRATIONS) diff --git a/src/aignostics/application/__init__.py b/src/aignostics/application/__init__.py deleted file mode 100644 index 775898801..000000000 --- a/src/aignostics/application/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -"""Application module.""" - -from ._cli import cli -from ._models import DownloadProgress, DownloadProgressState -from ._service import Service -from ._settings import Settings - -__all__ = ["DownloadProgress", "DownloadProgressState", "Service", "Settings", "cli"] - -from importlib.util import find_spec - -# advertise PageBuilder to enable auto-discovery -if find_spec("nicegui"): - from ._gui._page_builder import PageBuilder - - __all__ += [ - "PageBuilder", - ] diff --git a/src/aignostics/platform/__init__.py b/src/aignostics/platform/__init__.py deleted file mode 100644 index fea6f14f4..000000000 --- a/src/aignostics/platform/__init__.py +++ /dev/null @@ -1,194 +0,0 @@ -"""This module provides the low-level client interface for interacting with the API of the Aignostics Platform. - -The primary class in this module is the `Client` class, serving as the entry point -for authenticated API operations. Login and token management are handled -automatically. - -Further operations are encapsulated in the `Service` class, which provides methods -for manual login, logout and getting information about the authenticated user. - -Higher level abstractions are provided in the application module. -""" - -from aignx.codegen.exceptions import ApiException, ForbiddenException, NotFoundException -from aignx.codegen.models import ApplicationReadResponse as Application -from aignx.codegen.models import ApplicationReadShortResponse as ApplicationSummary -from aignx.codegen.models import ( - ArtifactOutput, - ItemOutput, - ItemState, - ItemTerminationReason, - RunItemStatistics, - RunOutput, - RunState, - RunTerminationReason, -) -from aignx.codegen.models import InputArtifact as InputArtifactData -from aignx.codegen.models import InputArtifactCreationRequest as InputArtifact -from aignx.codegen.models import ItemCreationRequest as InputItem -from aignx.codegen.models import ItemResultReadResponse as ItemResult -from aignx.codegen.models import MeReadResponse as Me -from aignx.codegen.models import OrganizationReadResponse as Organization -from aignx.codegen.models import OutputArtifact as OutputArtifactData -from aignx.codegen.models import OutputArtifactResultReadResponse as OutputArtifactElement -from aignx.codegen.models import RunReadResponse as RunData -from aignx.codegen.models import UserReadResponse as User -from aignx.codegen.models import VersionReadResponse as ApplicationVersion - -from ._cli import cli_sdk, cli_user -from ._client import Client -from ._constants import ( - API_ROOT_DEV, - API_ROOT_PRODUCTION, - API_ROOT_STAGING, - API_ROOT_TEST, - AUDIENCE_DEV, - AUDIENCE_PRODUCTION, - AUDIENCE_STAGING, - AUDIENCE_TEST, - AUTHORIZATION_BASE_URL_DEV, - AUTHORIZATION_BASE_URL_PRODUCTION, - AUTHORIZATION_BASE_URL_STAGING, - AUTHORIZATION_BASE_URL_TEST, - CLIENT_ID_INTERACTIVE_DEV, - CLIENT_ID_INTERACTIVE_PRODUCTION, - CLIENT_ID_INTERACTIVE_STAGING, - CLIENT_ID_INTERACTIVE_TEST, - DEFAULT_CPU_PROVISIONING_MODE, - DEFAULT_FLEX_START_MAX_RUN_DURATION_MINUTES, - DEFAULT_GPU_PROVISIONING_MODE, - DEFAULT_GPU_TYPE, - DEFAULT_MAX_GPUS_PER_SLIDE, - DEFAULT_NODE_ACQUISITION_TIMEOUT_MINUTES, - DEVICE_URL_DEV, - DEVICE_URL_PRODUCTION, - DEVICE_URL_STAGING, - DEVICE_URL_TEST, - JWS_JSON_URL_DEV, - JWS_JSON_URL_PRODUCTION, - JWS_JSON_URL_STAGING, - JWS_JSON_URL_TEST, - REDIRECT_URI_DEV, - REDIRECT_URI_PRODUCTION, - REDIRECT_URI_STAGING, - REDIRECT_URI_TEST, - STATUS_PAGE_URL_DEV, - STATUS_PAGE_URL_PRODUCTION, - STATUS_PAGE_URL_STAGING, - STATUS_PAGE_URL_TEST, - TOKEN_URL_DEV, - TOKEN_URL_PRODUCTION, - TOKEN_URL_STAGING, - TOKEN_URL_TEST, -) -from ._messages import AUTHENTICATION_FAILED, NOT_YET_IMPLEMENTED, UNKNOWN_ENDPOINT_URL -from ._sdk_metadata import ( - PipelineConfig, - RunSdkMetadata, - SchedulingMetadata, -) -from ._service import Service, TokenInfo, UserInfo -from ._settings import Settings, settings -from ._utils import ( - calculate_file_crc32c, - download_file, - generate_signed_url, - get_mime_type_for_artifact, - mime_type_to_file_ending, -) -from .resources.applications import ApplicationVersionDocument, Documents -from .resources.runs import LIST_APPLICATION_RUNS_MAX_PAGE_SIZE, LIST_APPLICATION_RUNS_MIN_PAGE_SIZE, Artifact, Run - -__all__ = [ - "API_ROOT_DEV", - "API_ROOT_PRODUCTION", - "API_ROOT_STAGING", - "API_ROOT_TEST", - "AUDIENCE_DEV", - "AUDIENCE_PRODUCTION", - "AUDIENCE_STAGING", - "AUDIENCE_TEST", - "AUTHENTICATION_FAILED", - "AUTHORIZATION_BASE_URL_DEV", - "AUTHORIZATION_BASE_URL_PRODUCTION", - "AUTHORIZATION_BASE_URL_STAGING", - "AUTHORIZATION_BASE_URL_TEST", - "CLIENT_ID_INTERACTIVE_DEV", - "CLIENT_ID_INTERACTIVE_PRODUCTION", - "CLIENT_ID_INTERACTIVE_STAGING", - "CLIENT_ID_INTERACTIVE_TEST", - "DEFAULT_CPU_PROVISIONING_MODE", - "DEFAULT_FLEX_START_MAX_RUN_DURATION_MINUTES", - "DEFAULT_GPU_PROVISIONING_MODE", - "DEFAULT_GPU_TYPE", - "DEFAULT_MAX_GPUS_PER_SLIDE", - "DEFAULT_NODE_ACQUISITION_TIMEOUT_MINUTES", - "DEVICE_URL_DEV", - "DEVICE_URL_PRODUCTION", - "DEVICE_URL_STAGING", - "DEVICE_URL_TEST", - "JWS_JSON_URL_DEV", - "JWS_JSON_URL_PRODUCTION", - "JWS_JSON_URL_STAGING", - "JWS_JSON_URL_TEST", - "LIST_APPLICATION_RUNS_MAX_PAGE_SIZE", - "LIST_APPLICATION_RUNS_MIN_PAGE_SIZE", - "NOT_YET_IMPLEMENTED", - "REDIRECT_URI_DEV", - "REDIRECT_URI_PRODUCTION", - "REDIRECT_URI_STAGING", - "REDIRECT_URI_TEST", - "STATUS_PAGE_URL_DEV", - "STATUS_PAGE_URL_PRODUCTION", - "STATUS_PAGE_URL_STAGING", - "STATUS_PAGE_URL_TEST", - "TOKEN_URL_DEV", - "TOKEN_URL_PRODUCTION", - "TOKEN_URL_STAGING", - "TOKEN_URL_TEST", - "UNKNOWN_ENDPOINT_URL", - "ApiException", - "Application", - "ApplicationSummary", - "ApplicationVersion", - "ApplicationVersionDocument", - "Artifact", - "ArtifactOutput", - "Client", - "Documents", - "ForbiddenException", - "InputArtifact", - "InputArtifactData", - "InputItem", - "ItemOutput", - "ItemResult", - "ItemState", - "ItemTerminationReason", - "Me", - "NotFoundException", - "Organization", - "OutputArtifactData", - "OutputArtifactElement", - "PipelineConfig", - "Run", - "RunData", - "RunItemStatistics", - "RunOutput", - "RunSdkMetadata", - "RunState", - "RunTerminationReason", - "SchedulingMetadata", - "Service", - "Settings", - "TokenInfo", - "User", - "UserInfo", - "calculate_file_crc32c", - "cli_sdk", - "cli_user", - "download_file", - "generate_signed_url", - "get_mime_type_for_artifact", - "mime_type_to_file_ending", - "settings", -] diff --git a/src/aignostics_sdk/__init__.py b/src/aignostics_sdk/__init__.py new file mode 100644 index 000000000..7122b8500 --- /dev/null +++ b/src/aignostics_sdk/__init__.py @@ -0,0 +1 @@ +"""Aignostics SDK — slim platform client distribution.""" diff --git a/src/aignostics_sdk/_codegen/__init__.py b/src/aignostics_sdk/_codegen/__init__.py new file mode 100644 index 000000000..b35488f5c --- /dev/null +++ b/src/aignostics_sdk/_codegen/__init__.py @@ -0,0 +1 @@ +"""Auto-generated API client code (OpenAPI codegen output).""" diff --git a/src/aignostics_sdk/_codegen/api/__init__.py b/src/aignostics_sdk/_codegen/api/__init__.py new file mode 100644 index 000000000..946dc8e6a --- /dev/null +++ b/src/aignostics_sdk/_codegen/api/__init__.py @@ -0,0 +1 @@ +"""Auto-generated API client code — api sub-package.""" diff --git a/src/aignostics_sdk/_codegen/api/public_api.py b/src/aignostics_sdk/_codegen/api/public_api.py new file mode 100644 index 000000000..e91481567 --- /dev/null +++ b/src/aignostics_sdk/_codegen/api/public_api.py @@ -0,0 +1,4834 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + +from typing import Annotated, Any + +from pydantic import Field, StrictFloat, StrictInt, StrictStr, validate_call + +from aignostics_sdk._codegen.api_client import ApiClient, RequestSerialized +from aignostics_sdk._codegen.api_response import ApiResponse +from aignostics_sdk._codegen.models.application_read_response import ApplicationReadResponse +from aignostics_sdk._codegen.models.application_read_short_response import ApplicationReadShortResponse +from aignostics_sdk._codegen.models.custom_metadata_update_request import CustomMetadataUpdateRequest +from aignostics_sdk._codegen.models.custom_metadata_update_response import CustomMetadataUpdateResponse +from aignostics_sdk._codegen.models.item_result_read_response import ItemResultReadResponse +from aignostics_sdk._codegen.models.item_state import ItemState +from aignostics_sdk._codegen.models.item_termination_reason import ItemTerminationReason +from aignostics_sdk._codegen.models.me_read_response import MeReadResponse +from aignostics_sdk._codegen.models.run_creation_request import RunCreationRequest +from aignostics_sdk._codegen.models.run_creation_response import RunCreationResponse +from aignostics_sdk._codegen.models.run_read_response import RunReadResponse +from aignostics_sdk._codegen.models.version_document_response import VersionDocumentResponse +from aignostics_sdk._codegen.models.version_read_response import VersionReadResponse +from aignostics_sdk._codegen.rest import RESTResponseType + + +class PublicApi: + """NOTE: This class is auto generated by OpenAPI Generator + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + def __init__(self, api_client=None) -> None: + if api_client is None: + api_client = ApiClient.get_default() + self.api_client = api_client + + @validate_call + def application_version_details_v1_applications_application_id_versions_version_get( + self, + application_id: StrictStr, + version: Annotated[str, Field(strict=True)], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> VersionReadResponse: + """Application Version Details + + Get the application version details. Allows caller to retrieve information about application version based on provided application version ID. + + :param application_id: (required) + :type application_id: str + :param version: (required) + :type version: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._application_version_details_v1_applications_application_id_versions_version_get_serialize( + application_id=application_id, + version=version, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "VersionReadResponse", + "403": None, + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def application_version_details_v1_applications_application_id_versions_version_get_with_http_info( + self, + application_id: StrictStr, + version: Annotated[str, Field(strict=True)], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[VersionReadResponse]: + """Application Version Details + + Get the application version details. Allows caller to retrieve information about application version based on provided application version ID. + + :param application_id: (required) + :type application_id: str + :param version: (required) + :type version: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._application_version_details_v1_applications_application_id_versions_version_get_serialize( + application_id=application_id, + version=version, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "VersionReadResponse", + "403": None, + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def application_version_details_v1_applications_application_id_versions_version_get_without_preload_content( + self, + application_id: StrictStr, + version: Annotated[str, Field(strict=True)], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Application Version Details + + Get the application version details. Allows caller to retrieve information about application version based on provided application version ID. + + :param application_id: (required) + :type application_id: str + :param version: (required) + :type version: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._application_version_details_v1_applications_application_id_versions_version_get_serialize( + application_id=application_id, + version=version, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "VersionReadResponse", + "403": None, + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _application_version_details_v1_applications_application_id_versions_version_get_serialize( + self, + application_id, + version, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + if application_id is not None: + _path_params["application_id"] = application_id + if version is not None: + _path_params["version"] = version + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="GET", + resource_path="/api/v1/applications/{application_id}/versions/{version}", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def cancel_run_v1_runs_run_id_cancel_post( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /runs/` endpoint")], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> object: + """Cancel Run + + The run can be canceled by the user who created the run. The execution can be canceled any time while the run is not in the terminated state. The pending items of a canceled run will not be processed and will not add to the cost. When the run is canceled, the already completed items remain available for download. + + :param run_id: Run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._cancel_run_v1_runs_run_id_cancel_post_serialize( + run_id=run_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "202": "object", + "404": None, + "403": None, + "409": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def cancel_run_v1_runs_run_id_cancel_post_with_http_info( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /runs/` endpoint")], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[object]: + """Cancel Run + + The run can be canceled by the user who created the run. The execution can be canceled any time while the run is not in the terminated state. The pending items of a canceled run will not be processed and will not add to the cost. When the run is canceled, the already completed items remain available for download. + + :param run_id: Run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._cancel_run_v1_runs_run_id_cancel_post_serialize( + run_id=run_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "202": "object", + "404": None, + "403": None, + "409": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def cancel_run_v1_runs_run_id_cancel_post_without_preload_content( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /runs/` endpoint")], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Cancel Run + + The run can be canceled by the user who created the run. The execution can be canceled any time while the run is not in the terminated state. The pending items of a canceled run will not be processed and will not add to the cost. When the run is canceled, the already completed items remain available for download. + + :param run_id: Run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._cancel_run_v1_runs_run_id_cancel_post_serialize( + run_id=run_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "202": "object", + "404": None, + "403": None, + "409": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _cancel_run_v1_runs_run_id_cancel_post_serialize( + self, + run_id, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + if run_id is not None: + _path_params["run_id"] = run_id + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="POST", + resource_path="/api/v1/runs/{run_id}/cancel", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def create_run_v1_runs_post( + self, + run_creation_request: RunCreationRequest, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RunCreationResponse: + """Initiate Run + + This endpoint initiates a processing run for a selected application and version, and returns a `run_id` for tracking purposes. Slide processing occurs asynchronously, allowing you to retrieve results for individual slides as soon as they complete processing. The system typically processes slides in batches. Below is an example of the required payload for initiating an Atlas H&E TME processing run. ### Payload The payload includes `application_id`, optional `version_number`, and `items` base fields. `application_id` is the unique identifier for the application. `version_number` is the semantic version to use. If not provided, the latest available version will be used. `items` includes the list of the items to process (slides, in case of HETA application). Every item has a set of standard fields defined by the API, plus the custom_metadata, specific to the chosen application. Example payload structure with the comments: ``` { application_id: \"he-tme\", version_number: \"1.0.0-beta\", items: [{ \"external_id\": \"slide_1\", \"custom_metadata\": {\"project\": \"sample-study\"}, \"input_artifacts\": [{ \"name\": \"user_slide\", \"download_url\": \"https://...\", \"metadata\": { \"specimen\": { \"disease\": \"LUNG_CANCER\", \"tissue\": \"LUNG\" }, \"staining_method\": \"H&E\", \"width_px\": 136223, \"height_px\": 87761, \"resolution_mpp\": 0.2628238, \"media-type\":\"image/tiff\", \"checksum_base64_crc32c\": \"64RKKA==\" } }] }] } ``` | Parameter | Description | | :---- | :---- | | `application_id` required | Unique ID for the application | | `version_number` optional | Semantic version of the application. If not provided, the latest available version will be used | | `items` required | List of submitted items i.e. whole slide images (WSIs) with parameters described below. | | `external_id` required | Unique WSI name or ID for easy reference to items, provided by the caller. The `external_id` should be unique across all items of the run. | | `input_artifacts` required | List of provided artifacts for a WSI; at the moment Atlas H&E-TME receives only 1 artifact per slide (the slide itself), but for some other applications this can be a slide and a segmentation map | | `name` required | Type of artifact; Atlas H&E-TME supports only `\"input_slide\"` | | `download_url` required | Signed URL to the input file in the S3 or GCS; Should be valid for at least 6 days | | `specimen: disease` required | Supported cancer types for Atlas H&E-TME (see full list in Atlas H&E-TME manual) | | `specimen: tissue` required | Supported tissue types for Atlas H&E-TME (see full list in Atlas H&E-TME manual) | | `staining_method` required | WSI stain bio-marker; Atlas H&E-TME supports only `\"H&E\"` | | `width_px` required | Integer value. Number of pixels of the WSI in the X dimension. | | `height_px` required | Integer value. Number of pixels of the WSI in the Y dimension. | | `resolution_mpp` required | Resolution of WSI in micrometers per pixel; check allowed range in Atlas H&E-TME manual | | `media-type` required | Supported media formats; available values are: image/tiff (for .tiff or .tif WSI), application/dicom (for DICOM ), application/zip (for zipped DICOM), and application/octet-stream (for .svs WSI) | | `checksum_base64_crc32c` required | Base64-encoded big-endian CRC32C checksum of the WSI image | ### Response The endpoint returns the run UUID. After that, the job is scheduled for the execution in the background. To check the status of the run, call `GET v1/runs/{run_id}` endpoint with the returned run UUID. ### Rejection Apart from the authentication, authorization, and malformed input error, the request can be rejected when specific quota limit is exceeded. More details on quotas is described in the documentation + + :param run_creation_request: (required) + :type run_creation_request: RunCreationRequest + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._create_run_v1_runs_post_serialize( + run_creation_request=run_creation_request, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "201": "RunCreationResponse", + "404": None, + "403": None, + "400": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def create_run_v1_runs_post_with_http_info( + self, + run_creation_request: RunCreationRequest, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[RunCreationResponse]: + """Initiate Run + + This endpoint initiates a processing run for a selected application and version, and returns a `run_id` for tracking purposes. Slide processing occurs asynchronously, allowing you to retrieve results for individual slides as soon as they complete processing. The system typically processes slides in batches. Below is an example of the required payload for initiating an Atlas H&E TME processing run. ### Payload The payload includes `application_id`, optional `version_number`, and `items` base fields. `application_id` is the unique identifier for the application. `version_number` is the semantic version to use. If not provided, the latest available version will be used. `items` includes the list of the items to process (slides, in case of HETA application). Every item has a set of standard fields defined by the API, plus the custom_metadata, specific to the chosen application. Example payload structure with the comments: ``` { application_id: \"he-tme\", version_number: \"1.0.0-beta\", items: [{ \"external_id\": \"slide_1\", \"custom_metadata\": {\"project\": \"sample-study\"}, \"input_artifacts\": [{ \"name\": \"user_slide\", \"download_url\": \"https://...\", \"metadata\": { \"specimen\": { \"disease\": \"LUNG_CANCER\", \"tissue\": \"LUNG\" }, \"staining_method\": \"H&E\", \"width_px\": 136223, \"height_px\": 87761, \"resolution_mpp\": 0.2628238, \"media-type\":\"image/tiff\", \"checksum_base64_crc32c\": \"64RKKA==\" } }] }] } ``` | Parameter | Description | | :---- | :---- | | `application_id` required | Unique ID for the application | | `version_number` optional | Semantic version of the application. If not provided, the latest available version will be used | | `items` required | List of submitted items i.e. whole slide images (WSIs) with parameters described below. | | `external_id` required | Unique WSI name or ID for easy reference to items, provided by the caller. The `external_id` should be unique across all items of the run. | | `input_artifacts` required | List of provided artifacts for a WSI; at the moment Atlas H&E-TME receives only 1 artifact per slide (the slide itself), but for some other applications this can be a slide and a segmentation map | | `name` required | Type of artifact; Atlas H&E-TME supports only `\"input_slide\"` | | `download_url` required | Signed URL to the input file in the S3 or GCS; Should be valid for at least 6 days | | `specimen: disease` required | Supported cancer types for Atlas H&E-TME (see full list in Atlas H&E-TME manual) | | `specimen: tissue` required | Supported tissue types for Atlas H&E-TME (see full list in Atlas H&E-TME manual) | | `staining_method` required | WSI stain bio-marker; Atlas H&E-TME supports only `\"H&E\"` | | `width_px` required | Integer value. Number of pixels of the WSI in the X dimension. | | `height_px` required | Integer value. Number of pixels of the WSI in the Y dimension. | | `resolution_mpp` required | Resolution of WSI in micrometers per pixel; check allowed range in Atlas H&E-TME manual | | `media-type` required | Supported media formats; available values are: image/tiff (for .tiff or .tif WSI), application/dicom (for DICOM ), application/zip (for zipped DICOM), and application/octet-stream (for .svs WSI) | | `checksum_base64_crc32c` required | Base64-encoded big-endian CRC32C checksum of the WSI image | ### Response The endpoint returns the run UUID. After that, the job is scheduled for the execution in the background. To check the status of the run, call `GET v1/runs/{run_id}` endpoint with the returned run UUID. ### Rejection Apart from the authentication, authorization, and malformed input error, the request can be rejected when specific quota limit is exceeded. More details on quotas is described in the documentation + + :param run_creation_request: (required) + :type run_creation_request: RunCreationRequest + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._create_run_v1_runs_post_serialize( + run_creation_request=run_creation_request, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "201": "RunCreationResponse", + "404": None, + "403": None, + "400": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def create_run_v1_runs_post_without_preload_content( + self, + run_creation_request: RunCreationRequest, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Initiate Run + + This endpoint initiates a processing run for a selected application and version, and returns a `run_id` for tracking purposes. Slide processing occurs asynchronously, allowing you to retrieve results for individual slides as soon as they complete processing. The system typically processes slides in batches. Below is an example of the required payload for initiating an Atlas H&E TME processing run. ### Payload The payload includes `application_id`, optional `version_number`, and `items` base fields. `application_id` is the unique identifier for the application. `version_number` is the semantic version to use. If not provided, the latest available version will be used. `items` includes the list of the items to process (slides, in case of HETA application). Every item has a set of standard fields defined by the API, plus the custom_metadata, specific to the chosen application. Example payload structure with the comments: ``` { application_id: \"he-tme\", version_number: \"1.0.0-beta\", items: [{ \"external_id\": \"slide_1\", \"custom_metadata\": {\"project\": \"sample-study\"}, \"input_artifacts\": [{ \"name\": \"user_slide\", \"download_url\": \"https://...\", \"metadata\": { \"specimen\": { \"disease\": \"LUNG_CANCER\", \"tissue\": \"LUNG\" }, \"staining_method\": \"H&E\", \"width_px\": 136223, \"height_px\": 87761, \"resolution_mpp\": 0.2628238, \"media-type\":\"image/tiff\", \"checksum_base64_crc32c\": \"64RKKA==\" } }] }] } ``` | Parameter | Description | | :---- | :---- | | `application_id` required | Unique ID for the application | | `version_number` optional | Semantic version of the application. If not provided, the latest available version will be used | | `items` required | List of submitted items i.e. whole slide images (WSIs) with parameters described below. | | `external_id` required | Unique WSI name or ID for easy reference to items, provided by the caller. The `external_id` should be unique across all items of the run. | | `input_artifacts` required | List of provided artifacts for a WSI; at the moment Atlas H&E-TME receives only 1 artifact per slide (the slide itself), but for some other applications this can be a slide and a segmentation map | | `name` required | Type of artifact; Atlas H&E-TME supports only `\"input_slide\"` | | `download_url` required | Signed URL to the input file in the S3 or GCS; Should be valid for at least 6 days | | `specimen: disease` required | Supported cancer types for Atlas H&E-TME (see full list in Atlas H&E-TME manual) | | `specimen: tissue` required | Supported tissue types for Atlas H&E-TME (see full list in Atlas H&E-TME manual) | | `staining_method` required | WSI stain bio-marker; Atlas H&E-TME supports only `\"H&E\"` | | `width_px` required | Integer value. Number of pixels of the WSI in the X dimension. | | `height_px` required | Integer value. Number of pixels of the WSI in the Y dimension. | | `resolution_mpp` required | Resolution of WSI in micrometers per pixel; check allowed range in Atlas H&E-TME manual | | `media-type` required | Supported media formats; available values are: image/tiff (for .tiff or .tif WSI), application/dicom (for DICOM ), application/zip (for zipped DICOM), and application/octet-stream (for .svs WSI) | | `checksum_base64_crc32c` required | Base64-encoded big-endian CRC32C checksum of the WSI image | ### Response The endpoint returns the run UUID. After that, the job is scheduled for the execution in the background. To check the status of the run, call `GET v1/runs/{run_id}` endpoint with the returned run UUID. ### Rejection Apart from the authentication, authorization, and malformed input error, the request can be rejected when specific quota limit is exceeded. More details on quotas is described in the documentation + + :param run_creation_request: (required) + :type run_creation_request: RunCreationRequest + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._create_run_v1_runs_post_serialize( + run_creation_request=run_creation_request, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "201": "RunCreationResponse", + "404": None, + "403": None, + "400": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _create_run_v1_runs_post_serialize( + self, + run_creation_request, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + if run_creation_request is not None: + _body_params = run_creation_request + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # set the HTTP header `Content-Type` + if _content_type: + _header_params["Content-Type"] = _content_type + else: + _default_content_type = ( + self.api_client.select_header_content_type( + [ + "application/json" + ] + ) + ) + if _default_content_type is not None: + _header_params["Content-Type"] = _default_content_type + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="POST", + resource_path="/api/v1/runs", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def delete_run_items_v1_runs_run_id_artifacts_delete( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /runs/` endpoint")], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> object: + """Delete Run Items + + This endpoint allows the caller to explicitly delete artifacts generated by a run. It can only be invoked when the run has reached a final state, i.e. `PROCESSED`, `CANCELED_SYSTEM`, or `CANCELED_USER`. Note that by default, all artifacts are automatically deleted 30 days after the run finishes, regardless of whether the caller explicitly requests such deletion. + + :param run_id: Run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._delete_run_items_v1_runs_run_id_artifacts_delete_serialize( + run_id=run_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "object", + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def delete_run_items_v1_runs_run_id_artifacts_delete_with_http_info( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /runs/` endpoint")], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[object]: + """Delete Run Items + + This endpoint allows the caller to explicitly delete artifacts generated by a run. It can only be invoked when the run has reached a final state, i.e. `PROCESSED`, `CANCELED_SYSTEM`, or `CANCELED_USER`. Note that by default, all artifacts are automatically deleted 30 days after the run finishes, regardless of whether the caller explicitly requests such deletion. + + :param run_id: Run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._delete_run_items_v1_runs_run_id_artifacts_delete_serialize( + run_id=run_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "object", + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def delete_run_items_v1_runs_run_id_artifacts_delete_without_preload_content( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /runs/` endpoint")], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Delete Run Items + + This endpoint allows the caller to explicitly delete artifacts generated by a run. It can only be invoked when the run has reached a final state, i.e. `PROCESSED`, `CANCELED_SYSTEM`, or `CANCELED_USER`. Note that by default, all artifacts are automatically deleted 30 days after the run finishes, regardless of whether the caller explicitly requests such deletion. + + :param run_id: Run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._delete_run_items_v1_runs_run_id_artifacts_delete_serialize( + run_id=run_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "object", + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _delete_run_items_v1_runs_run_id_artifacts_delete_serialize( + self, + run_id, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + if run_id is not None: + _path_params["run_id"] = run_id + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="DELETE", + resource_path="/api/v1/runs/{run_id}/artifacts", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /runs/` endpoint")], + artifact_id: Annotated[StrictStr, Field(description="The artifact id to download")], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> object: + """Get Artifact Url + + Download the artifact file with the specified artifact_id, belonging to the specified run. The artifact_is is returned by the `GET /v1/runs/{run_id}/items` endpoint as part of the item results, and can also be retrieved via `GET /v1/runs/{run_id}/items/{external_id}`. The endpoint may return a redirect response with a presigned URL to download the artifact file from the storage bucket. The presigned URL is valid for a limited time, so it should be used immediately after receiving the response. + + :param run_id: Run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param artifact_id: The artifact id to download (required) + :type artifact_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get_serialize( + run_id=run_id, + artifact_id=artifact_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "object", + "404": None, + "307": None, + "403": None, + "410": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get_with_http_info( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /runs/` endpoint")], + artifact_id: Annotated[StrictStr, Field(description="The artifact id to download")], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[object]: + """Get Artifact Url + + Download the artifact file with the specified artifact_id, belonging to the specified run. The artifact_is is returned by the `GET /v1/runs/{run_id}/items` endpoint as part of the item results, and can also be retrieved via `GET /v1/runs/{run_id}/items/{external_id}`. The endpoint may return a redirect response with a presigned URL to download the artifact file from the storage bucket. The presigned URL is valid for a limited time, so it should be used immediately after receiving the response. + + :param run_id: Run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param artifact_id: The artifact id to download (required) + :type artifact_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get_serialize( + run_id=run_id, + artifact_id=artifact_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "object", + "404": None, + "307": None, + "403": None, + "410": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get_without_preload_content( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /runs/` endpoint")], + artifact_id: Annotated[StrictStr, Field(description="The artifact id to download")], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Get Artifact Url + + Download the artifact file with the specified artifact_id, belonging to the specified run. The artifact_is is returned by the `GET /v1/runs/{run_id}/items` endpoint as part of the item results, and can also be retrieved via `GET /v1/runs/{run_id}/items/{external_id}`. The endpoint may return a redirect response with a presigned URL to download the artifact file from the storage bucket. The presigned URL is valid for a limited time, so it should be used immediately after receiving the response. + + :param run_id: Run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param artifact_id: The artifact id to download (required) + :type artifact_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get_serialize( + run_id=run_id, + artifact_id=artifact_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "object", + "404": None, + "307": None, + "403": None, + "410": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get_serialize( + self, + run_id, + artifact_id, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + if run_id is not None: + _path_params["run_id"] = run_id + if artifact_id is not None: + _path_params["artifact_id"] = artifact_id + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="GET", + resource_path="/api/v1/runs/{run_id}/artifacts/{artifact_id}/file", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def get_item_by_run_v1_runs_run_id_items_external_id_get( + self, + run_id: Annotated[StrictStr, Field(description="The run id, returned by `POST /runs/` endpoint")], + external_id: Annotated[StrictStr, Field(description="The `external_id` that was defined for the item by the customer that triggered the run.")], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ItemResultReadResponse: + """Get Item By Run + + Retrieve details of a specific item (slide) by its external ID and the run ID. + + :param run_id: The run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param external_id: The `external_id` that was defined for the item by the customer that triggered the run. (required) + :type external_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_item_by_run_v1_runs_run_id_items_external_id_get_serialize( + run_id=run_id, + external_id=external_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "ItemResultReadResponse", + "404": None, + "403": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def get_item_by_run_v1_runs_run_id_items_external_id_get_with_http_info( + self, + run_id: Annotated[StrictStr, Field(description="The run id, returned by `POST /runs/` endpoint")], + external_id: Annotated[StrictStr, Field(description="The `external_id` that was defined for the item by the customer that triggered the run.")], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[ItemResultReadResponse]: + """Get Item By Run + + Retrieve details of a specific item (slide) by its external ID and the run ID. + + :param run_id: The run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param external_id: The `external_id` that was defined for the item by the customer that triggered the run. (required) + :type external_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_item_by_run_v1_runs_run_id_items_external_id_get_serialize( + run_id=run_id, + external_id=external_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "ItemResultReadResponse", + "404": None, + "403": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def get_item_by_run_v1_runs_run_id_items_external_id_get_without_preload_content( + self, + run_id: Annotated[StrictStr, Field(description="The run id, returned by `POST /runs/` endpoint")], + external_id: Annotated[StrictStr, Field(description="The `external_id` that was defined for the item by the customer that triggered the run.")], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Get Item By Run + + Retrieve details of a specific item (slide) by its external ID and the run ID. + + :param run_id: The run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param external_id: The `external_id` that was defined for the item by the customer that triggered the run. (required) + :type external_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_item_by_run_v1_runs_run_id_items_external_id_get_serialize( + run_id=run_id, + external_id=external_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "ItemResultReadResponse", + "404": None, + "403": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _get_item_by_run_v1_runs_run_id_items_external_id_get_serialize( + self, + run_id, + external_id, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + if run_id is not None: + _path_params["run_id"] = run_id + if external_id is not None: + _path_params["external_id"] = external_id + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="GET", + resource_path="/api/v1/runs/{run_id}/items/{external_id}", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def get_me_v1_me_get( + self, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> MeReadResponse: + """Get current user + + Retrieves your identity details, including name, email, and organization. This is useful for verifying that the request is being made under the correct user profile and organization context, as well as confirming that the expected environment variables are correctly set (in case you are using Python SDK) + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_me_v1_me_get_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "MeReadResponse", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def get_me_v1_me_get_with_http_info( + self, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[MeReadResponse]: + """Get current user + + Retrieves your identity details, including name, email, and organization. This is useful for verifying that the request is being made under the correct user profile and organization context, as well as confirming that the expected environment variables are correctly set (in case you are using Python SDK) + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_me_v1_me_get_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "MeReadResponse", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def get_me_v1_me_get_without_preload_content( + self, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Get current user + + Retrieves your identity details, including name, email, and organization. This is useful for verifying that the request is being made under the correct user profile and organization context, as well as confirming that the expected environment variables are correctly set (in case you are using Python SDK) + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_me_v1_me_get_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "MeReadResponse", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _get_me_v1_me_get_serialize( + self, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="GET", + resource_path="/api/v1/me", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def get_run_v1_runs_run_id_get( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /v1/runs/` endpoint")], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RunReadResponse: + """Get run details + + This endpoint allows the caller to retrieve the current status of a run along with other relevant run details. A run becomes available immediately after it is created through the `POST /v1/runs/` endpoint. To download the output results, use `GET /v1/runs/{run_id}/` items to get outputs for all slides. Access to a run is restricted to the user who created it. + + :param run_id: Run id, returned by `POST /v1/runs/` endpoint (required) + :type run_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_run_v1_runs_run_id_get_serialize( + run_id=run_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "RunReadResponse", + "404": None, + "403": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def get_run_v1_runs_run_id_get_with_http_info( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /v1/runs/` endpoint")], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[RunReadResponse]: + """Get run details + + This endpoint allows the caller to retrieve the current status of a run along with other relevant run details. A run becomes available immediately after it is created through the `POST /v1/runs/` endpoint. To download the output results, use `GET /v1/runs/{run_id}/` items to get outputs for all slides. Access to a run is restricted to the user who created it. + + :param run_id: Run id, returned by `POST /v1/runs/` endpoint (required) + :type run_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_run_v1_runs_run_id_get_serialize( + run_id=run_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "RunReadResponse", + "404": None, + "403": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def get_run_v1_runs_run_id_get_without_preload_content( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /v1/runs/` endpoint")], + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Get run details + + This endpoint allows the caller to retrieve the current status of a run along with other relevant run details. A run becomes available immediately after it is created through the `POST /v1/runs/` endpoint. To download the output results, use `GET /v1/runs/{run_id}/` items to get outputs for all slides. Access to a run is restricted to the user who created it. + + :param run_id: Run id, returned by `POST /v1/runs/` endpoint (required) + :type run_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_run_v1_runs_run_id_get_serialize( + run_id=run_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "RunReadResponse", + "404": None, + "403": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _get_run_v1_runs_run_id_get_serialize( + self, + run_id, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + if run_id is not None: + _path_params["run_id"] = run_id + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="GET", + resource_path="/api/v1/runs/{run_id}", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def get_version_document( + self, + application_id: StrictStr, + version: StrictStr, + name: StrictStr, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> VersionDocumentResponse: + """Get version document metadata + + Return metadata for a single public document attached to an application version. + + :param application_id: (required) + :type application_id: str + :param version: (required) + :type version: str + :param name: (required) + :type name: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ + _param = self._get_version_document_serialize( + application_id=application_id, + version=version, + name=name, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "VersionDocumentResponse", + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def get_version_document_with_http_info( + self, + application_id: StrictStr, + version: StrictStr, + name: StrictStr, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[VersionDocumentResponse]: + """Get version document metadata + + Return metadata for a single public document attached to an application version. + + :param application_id: (required) + :type application_id: str + :param version: (required) + :type version: str + :param name: (required) + :type name: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ + _param = self._get_version_document_serialize( + application_id=application_id, + version=version, + name=name, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "VersionDocumentResponse", + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def get_version_document_without_preload_content( + self, + application_id: StrictStr, + version: StrictStr, + name: StrictStr, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Get version document metadata + + Return metadata for a single public document attached to an application version. + + :param application_id: (required) + :type application_id: str + :param version: (required) + :type version: str + :param name: (required) + :type name: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ + _param = self._get_version_document_serialize( + application_id=application_id, + version=version, + name=name, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "VersionDocumentResponse", + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _get_version_document_serialize( + self, + application_id, + version, + name, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + if application_id is not None: + _path_params["application_id"] = application_id + if version is not None: + _path_params["version"] = version + if name is not None: + _path_params["name"] = name + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="GET", + resource_path="/api/v1/applications/{application_id}/versions/{version}/documents/{name}", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def get_version_document_content( + self, + application_id: StrictStr, + version: StrictStr, + name: StrictStr, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> None: + """Stream version document content (programmatic) + + 307 redirect to a short-lived GCS signed URL for streaming document content. Unlike ``/file``, no ``Content-Disposition`` override is set — GCS serves the object body with its stored ``Content-Type``. Intended for programmatic clients that follow redirects and consume the content directly. Response carries ``Cache-Control: no-store``. + + :param application_id: (required) + :type application_id: str + :param version: (required) + :type version: str + :param name: (required) + :type name: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_version_document_content_serialize( + application_id=application_id, + version=version, + name=name, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "307": None, + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def get_version_document_content_with_http_info( + self, + application_id: StrictStr, + version: StrictStr, + name: StrictStr, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[None]: + """Stream version document content (programmatic) + + 307 redirect to a short-lived GCS signed URL for streaming document content. Unlike ``/file``, no ``Content-Disposition`` override is set — GCS serves the object body with its stored ``Content-Type``. Intended for programmatic clients that follow redirects and consume the content directly. Response carries ``Cache-Control: no-store``. + + :param application_id: (required) + :type application_id: str + :param version: (required) + :type version: str + :param name: (required) + :type name: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_version_document_content_serialize( + application_id=application_id, + version=version, + name=name, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "307": None, + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def get_version_document_content_without_preload_content( + self, + application_id: StrictStr, + version: StrictStr, + name: StrictStr, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Stream version document content (programmatic) + + 307 redirect to a short-lived GCS signed URL for streaming document content. Unlike ``/file``, no ``Content-Disposition`` override is set — GCS serves the object body with its stored ``Content-Type``. Intended for programmatic clients that follow redirects and consume the content directly. Response carries ``Cache-Control: no-store``. + + :param application_id: (required) + :type application_id: str + :param version: (required) + :type version: str + :param name: (required) + :type name: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_version_document_content_serialize( + application_id=application_id, + version=version, + name=name, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "307": None, + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _get_version_document_content_serialize( + self, + application_id, + version, + name, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + if application_id is not None: + _path_params["application_id"] = application_id + if version is not None: + _path_params["version"] = version + if name is not None: + _path_params["name"] = name + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="GET", + resource_path="/api/v1/applications/{application_id}/versions/{version}/documents/{name}/content", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def get_version_document_file( + self, + application_id: StrictStr, + version: StrictStr, + name: StrictStr, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> None: + """Download version document (browser) + + 307 redirect to a short-lived GCS signed URL for downloading a document. The signed URL includes ``response-content-disposition=attachment; filename=\"\"`` so browsers prompt a save-as dialog rather than rendering inline. Response carries ``Cache-Control: no-store``. + + :param application_id: (required) + :type application_id: str + :param version: (required) + :type version: str + :param name: (required) + :type name: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_version_document_file_serialize( + application_id=application_id, + version=version, + name=name, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "307": None, + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def get_version_document_file_with_http_info( + self, + application_id: StrictStr, + version: StrictStr, + name: StrictStr, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[None]: + """Download version document (browser) + + 307 redirect to a short-lived GCS signed URL for downloading a document. The signed URL includes ``response-content-disposition=attachment; filename=\"\"`` so browsers prompt a save-as dialog rather than rendering inline. Response carries ``Cache-Control: no-store``. + + :param application_id: (required) + :type application_id: str + :param version: (required) + :type version: str + :param name: (required) + :type name: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_version_document_file_serialize( + application_id=application_id, + version=version, + name=name, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "307": None, + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def get_version_document_file_without_preload_content( + self, + application_id: StrictStr, + version: StrictStr, + name: StrictStr, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Download version document (browser) + + 307 redirect to a short-lived GCS signed URL for downloading a document. The signed URL includes ``response-content-disposition=attachment; filename=\"\"`` so browsers prompt a save-as dialog rather than rendering inline. Response carries ``Cache-Control: no-store``. + + :param application_id: (required) + :type application_id: str + :param version: (required) + :type version: str + :param name: (required) + :type name: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._get_version_document_file_serialize( + application_id=application_id, + version=version, + name=name, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "307": None, + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _get_version_document_file_serialize( + self, + application_id, + version, + name, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + if application_id is not None: + _path_params["application_id"] = application_id + if version is not None: + _path_params["version"] = version + if name is not None: + _path_params["name"] = name + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="GET", + resource_path="/api/v1/applications/{application_id}/versions/{version}/documents/{name}/file", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def list_applications_v1_applications_get( + self, + page: Annotated[int, Field(strict=True, ge=1)] | None = None, + page_size: Annotated[int, Field(le=100, strict=True, ge=5)] | None = None, + sort: Annotated[list[StrictStr] | None, Field(description="Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `application_id` - `name` - `description` - `regulatory_classes` **Examples:** - `?sort=application_id` - Sort by application_id ascending - `?sort=-name` - Sort by name descending - `?sort=+description&sort=name` - Sort by description ascending, then name descending")] = None, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> list[ApplicationReadShortResponse]: + """List available applications + + Returns the list of the applications, available to the caller. The application is available if any of the versions of the application is assigned to the caller's organization. The response is paginated and sorted according to the provided parameters. + + :param page: + :type page: int + :param page_size: + :type page_size: int + :param sort: Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `application_id` - `name` - `description` - `regulatory_classes` **Examples:** - `?sort=application_id` - Sort by application_id ascending - `?sort=-name` - Sort by name descending - `?sort=+description&sort=name` - Sort by description ascending, then name descending + :type sort: List[str] + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._list_applications_v1_applications_get_serialize( + page=page, + page_size=page_size, + sort=sort, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "List[ApplicationReadShortResponse]", + "401": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def list_applications_v1_applications_get_with_http_info( + self, + page: Annotated[int, Field(strict=True, ge=1)] | None = None, + page_size: Annotated[int, Field(le=100, strict=True, ge=5)] | None = None, + sort: Annotated[list[StrictStr] | None, Field(description="Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `application_id` - `name` - `description` - `regulatory_classes` **Examples:** - `?sort=application_id` - Sort by application_id ascending - `?sort=-name` - Sort by name descending - `?sort=+description&sort=name` - Sort by description ascending, then name descending")] = None, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[list[ApplicationReadShortResponse]]: + """List available applications + + Returns the list of the applications, available to the caller. The application is available if any of the versions of the application is assigned to the caller's organization. The response is paginated and sorted according to the provided parameters. + + :param page: + :type page: int + :param page_size: + :type page_size: int + :param sort: Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `application_id` - `name` - `description` - `regulatory_classes` **Examples:** - `?sort=application_id` - Sort by application_id ascending - `?sort=-name` - Sort by name descending - `?sort=+description&sort=name` - Sort by description ascending, then name descending + :type sort: List[str] + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._list_applications_v1_applications_get_serialize( + page=page, + page_size=page_size, + sort=sort, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "List[ApplicationReadShortResponse]", + "401": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def list_applications_v1_applications_get_without_preload_content( + self, + page: Annotated[int, Field(strict=True, ge=1)] | None = None, + page_size: Annotated[int, Field(le=100, strict=True, ge=5)] | None = None, + sort: Annotated[list[StrictStr] | None, Field(description="Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `application_id` - `name` - `description` - `regulatory_classes` **Examples:** - `?sort=application_id` - Sort by application_id ascending - `?sort=-name` - Sort by name descending - `?sort=+description&sort=name` - Sort by description ascending, then name descending")] = None, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """List available applications + + Returns the list of the applications, available to the caller. The application is available if any of the versions of the application is assigned to the caller's organization. The response is paginated and sorted according to the provided parameters. + + :param page: + :type page: int + :param page_size: + :type page_size: int + :param sort: Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `application_id` - `name` - `description` - `regulatory_classes` **Examples:** - `?sort=application_id` - Sort by application_id ascending - `?sort=-name` - Sort by name descending - `?sort=+description&sort=name` - Sort by description ascending, then name descending + :type sort: List[str] + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._list_applications_v1_applications_get_serialize( + page=page, + page_size=page_size, + sort=sort, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "List[ApplicationReadShortResponse]", + "401": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _list_applications_v1_applications_get_serialize( + self, + page, + page_size, + sort, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + "sort": "multi", + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + # process the query parameters + if page is not None: + + _query_params.append(("page", page)) + + if page_size is not None: + + _query_params.append(("page-size", page_size)) + + if sort is not None: + + _query_params.append(("sort", sort)) + + # process the header parameters + # process the form parameters + # process the body parameter + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="GET", + resource_path="/api/v1/applications", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def list_run_items_v1_runs_run_id_items_get( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /v1/runs/` endpoint")], + item_id__in: Annotated[list[StrictStr] | None, Field(description="Filter for item ids")] = None, + external_id__in: Annotated[list[StrictStr] | None, Field(description="Filter for items by their external_id from the input payload")] = None, + state: Annotated[ItemState | None, Field(description="Filter items by their state")] = None, + termination_reason: Annotated[ItemTerminationReason | None, Field(description="Filter items by their termination reason. Only applies to TERMINATED items.")] = None, + custom_metadata: Annotated[Annotated[str, Field(strict=True, max_length=1000)] | None, Field(description="JSONPath expression to filter items by their custom_metadata")] = None, + page: Annotated[int, Field(strict=True, ge=1)] | None = None, + page_size: Annotated[int, Field(le=100, strict=True, ge=5)] | None = None, + sort: Annotated[list[StrictStr] | None, Field(description="Sort the items by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `item_id` - `external_id` - `custom_metadata` - `terminated_at` - `termination_reason` **Examples:** - `?sort=item_id` - Sort by id of the item (ascending) - `?sort=-external_id` - Sort by external ID (descending) - `?sort=custom_metadata&sort=-external_id` - Sort by metadata, then by external ID (descending)")] = None, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> list[ItemResultReadResponse]: + """List Run Items + + List items in a run with filtering, sorting, and pagination capabilities. Returns paginated items within a specific run. Results can be filtered by `item_id`, `external_ids`, `custom_metadata`, `terminated_at`, and `termination_reason` using JSONPath expressions. ## JSONPath Metadata Filtering Use PostgreSQL JSONPath expressions to filter items using their custom_metadata. ### Examples: - **Field existence**: `$.case_id` - Results that have a case_id field defined - **Exact value match**: `$.priority ? (@ == \"high\")` - Results with high priority - **Numeric comparison**: `$.confidence_score ? (@ > 0.95)` - Results with high confidence - **Array operations**: `$.flags[*] ? (@ == \"reviewed\")` - Results flagged as reviewed - **Complex conditions**: `$.metrics ? (@.accuracy > 0.9 && @.recall > 0.8)` - Results meeting performance thresholds ## Notes - JSONPath expressions are evaluated using PostgreSQL's `@?` operator - The `$.` prefix is automatically added to root-level field references if missing - String values in conditions must be enclosed in double quotes - Use `&&` for AND operations and `||` for OR operations + + :param run_id: Run id, returned by `POST /v1/runs/` endpoint (required) + :type run_id: str + :param item_id__in: Filter for item ids + :type item_id__in: List[str] + :param external_id__in: Filter for items by their external_id from the input payload + :type external_id__in: List[str] + :param state: Filter items by their state + :type state: ItemState + :param termination_reason: Filter items by their termination reason. Only applies to TERMINATED items. + :type termination_reason: ItemTerminationReason + :param custom_metadata: JSONPath expression to filter items by their custom_metadata + :type custom_metadata: str + :param page: + :type page: int + :param page_size: + :type page_size: int + :param sort: Sort the items by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `item_id` - `external_id` - `custom_metadata` - `terminated_at` - `termination_reason` **Examples:** - `?sort=item_id` - Sort by id of the item (ascending) - `?sort=-external_id` - Sort by external ID (descending) - `?sort=custom_metadata&sort=-external_id` - Sort by metadata, then by external ID (descending) + :type sort: List[str] + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._list_run_items_v1_runs_run_id_items_get_serialize( + run_id=run_id, + item_id__in=item_id__in, + external_id__in=external_id__in, + state=state, + termination_reason=termination_reason, + custom_metadata=custom_metadata, + page=page, + page_size=page_size, + sort=sort, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "List[ItemResultReadResponse]", + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def list_run_items_v1_runs_run_id_items_get_with_http_info( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /v1/runs/` endpoint")], + item_id__in: Annotated[list[StrictStr] | None, Field(description="Filter for item ids")] = None, + external_id__in: Annotated[list[StrictStr] | None, Field(description="Filter for items by their external_id from the input payload")] = None, + state: Annotated[ItemState | None, Field(description="Filter items by their state")] = None, + termination_reason: Annotated[ItemTerminationReason | None, Field(description="Filter items by their termination reason. Only applies to TERMINATED items.")] = None, + custom_metadata: Annotated[Annotated[str, Field(strict=True, max_length=1000)] | None, Field(description="JSONPath expression to filter items by their custom_metadata")] = None, + page: Annotated[int, Field(strict=True, ge=1)] | None = None, + page_size: Annotated[int, Field(le=100, strict=True, ge=5)] | None = None, + sort: Annotated[list[StrictStr] | None, Field(description="Sort the items by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `item_id` - `external_id` - `custom_metadata` - `terminated_at` - `termination_reason` **Examples:** - `?sort=item_id` - Sort by id of the item (ascending) - `?sort=-external_id` - Sort by external ID (descending) - `?sort=custom_metadata&sort=-external_id` - Sort by metadata, then by external ID (descending)")] = None, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[list[ItemResultReadResponse]]: + """List Run Items + + List items in a run with filtering, sorting, and pagination capabilities. Returns paginated items within a specific run. Results can be filtered by `item_id`, `external_ids`, `custom_metadata`, `terminated_at`, and `termination_reason` using JSONPath expressions. ## JSONPath Metadata Filtering Use PostgreSQL JSONPath expressions to filter items using their custom_metadata. ### Examples: - **Field existence**: `$.case_id` - Results that have a case_id field defined - **Exact value match**: `$.priority ? (@ == \"high\")` - Results with high priority - **Numeric comparison**: `$.confidence_score ? (@ > 0.95)` - Results with high confidence - **Array operations**: `$.flags[*] ? (@ == \"reviewed\")` - Results flagged as reviewed - **Complex conditions**: `$.metrics ? (@.accuracy > 0.9 && @.recall > 0.8)` - Results meeting performance thresholds ## Notes - JSONPath expressions are evaluated using PostgreSQL's `@?` operator - The `$.` prefix is automatically added to root-level field references if missing - String values in conditions must be enclosed in double quotes - Use `&&` for AND operations and `||` for OR operations + + :param run_id: Run id, returned by `POST /v1/runs/` endpoint (required) + :type run_id: str + :param item_id__in: Filter for item ids + :type item_id__in: List[str] + :param external_id__in: Filter for items by their external_id from the input payload + :type external_id__in: List[str] + :param state: Filter items by their state + :type state: ItemState + :param termination_reason: Filter items by their termination reason. Only applies to TERMINATED items. + :type termination_reason: ItemTerminationReason + :param custom_metadata: JSONPath expression to filter items by their custom_metadata + :type custom_metadata: str + :param page: + :type page: int + :param page_size: + :type page_size: int + :param sort: Sort the items by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `item_id` - `external_id` - `custom_metadata` - `terminated_at` - `termination_reason` **Examples:** - `?sort=item_id` - Sort by id of the item (ascending) - `?sort=-external_id` - Sort by external ID (descending) - `?sort=custom_metadata&sort=-external_id` - Sort by metadata, then by external ID (descending) + :type sort: List[str] + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._list_run_items_v1_runs_run_id_items_get_serialize( + run_id=run_id, + item_id__in=item_id__in, + external_id__in=external_id__in, + state=state, + termination_reason=termination_reason, + custom_metadata=custom_metadata, + page=page, + page_size=page_size, + sort=sort, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "List[ItemResultReadResponse]", + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def list_run_items_v1_runs_run_id_items_get_without_preload_content( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /v1/runs/` endpoint")], + item_id__in: Annotated[list[StrictStr] | None, Field(description="Filter for item ids")] = None, + external_id__in: Annotated[list[StrictStr] | None, Field(description="Filter for items by their external_id from the input payload")] = None, + state: Annotated[ItemState | None, Field(description="Filter items by their state")] = None, + termination_reason: Annotated[ItemTerminationReason | None, Field(description="Filter items by their termination reason. Only applies to TERMINATED items.")] = None, + custom_metadata: Annotated[Annotated[str, Field(strict=True, max_length=1000)] | None, Field(description="JSONPath expression to filter items by their custom_metadata")] = None, + page: Annotated[int, Field(strict=True, ge=1)] | None = None, + page_size: Annotated[int, Field(le=100, strict=True, ge=5)] | None = None, + sort: Annotated[list[StrictStr] | None, Field(description="Sort the items by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `item_id` - `external_id` - `custom_metadata` - `terminated_at` - `termination_reason` **Examples:** - `?sort=item_id` - Sort by id of the item (ascending) - `?sort=-external_id` - Sort by external ID (descending) - `?sort=custom_metadata&sort=-external_id` - Sort by metadata, then by external ID (descending)")] = None, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """List Run Items + + List items in a run with filtering, sorting, and pagination capabilities. Returns paginated items within a specific run. Results can be filtered by `item_id`, `external_ids`, `custom_metadata`, `terminated_at`, and `termination_reason` using JSONPath expressions. ## JSONPath Metadata Filtering Use PostgreSQL JSONPath expressions to filter items using their custom_metadata. ### Examples: - **Field existence**: `$.case_id` - Results that have a case_id field defined - **Exact value match**: `$.priority ? (@ == \"high\")` - Results with high priority - **Numeric comparison**: `$.confidence_score ? (@ > 0.95)` - Results with high confidence - **Array operations**: `$.flags[*] ? (@ == \"reviewed\")` - Results flagged as reviewed - **Complex conditions**: `$.metrics ? (@.accuracy > 0.9 && @.recall > 0.8)` - Results meeting performance thresholds ## Notes - JSONPath expressions are evaluated using PostgreSQL's `@?` operator - The `$.` prefix is automatically added to root-level field references if missing - String values in conditions must be enclosed in double quotes - Use `&&` for AND operations and `||` for OR operations + + :param run_id: Run id, returned by `POST /v1/runs/` endpoint (required) + :type run_id: str + :param item_id__in: Filter for item ids + :type item_id__in: List[str] + :param external_id__in: Filter for items by their external_id from the input payload + :type external_id__in: List[str] + :param state: Filter items by their state + :type state: ItemState + :param termination_reason: Filter items by their termination reason. Only applies to TERMINATED items. + :type termination_reason: ItemTerminationReason + :param custom_metadata: JSONPath expression to filter items by their custom_metadata + :type custom_metadata: str + :param page: + :type page: int + :param page_size: + :type page_size: int + :param sort: Sort the items by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `item_id` - `external_id` - `custom_metadata` - `terminated_at` - `termination_reason` **Examples:** - `?sort=item_id` - Sort by id of the item (ascending) - `?sort=-external_id` - Sort by external ID (descending) - `?sort=custom_metadata&sort=-external_id` - Sort by metadata, then by external ID (descending) + :type sort: List[str] + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._list_run_items_v1_runs_run_id_items_get_serialize( + run_id=run_id, + item_id__in=item_id__in, + external_id__in=external_id__in, + state=state, + termination_reason=termination_reason, + custom_metadata=custom_metadata, + page=page, + page_size=page_size, + sort=sort, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "List[ItemResultReadResponse]", + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _list_run_items_v1_runs_run_id_items_get_serialize( + self, + run_id, + item_id__in, + external_id__in, + state, + termination_reason, + custom_metadata, + page, + page_size, + sort, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + "item_id__in": "multi", + "external_id__in": "multi", + "sort": "multi", + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + if run_id is not None: + _path_params["run_id"] = run_id + # process the query parameters + if item_id__in is not None: + + _query_params.append(("item_id__in", item_id__in)) + + if external_id__in is not None: + + _query_params.append(("external_id__in", external_id__in)) + + if state is not None: + + _query_params.append(("state", state.value)) + + if termination_reason is not None: + + _query_params.append(("termination_reason", termination_reason.value)) + + if custom_metadata is not None: + + _query_params.append(("custom_metadata", custom_metadata)) + + if page is not None: + + _query_params.append(("page", page)) + + if page_size is not None: + + _query_params.append(("page_size", page_size)) + + if sort is not None: + + _query_params.append(("sort", sort)) + + # process the header parameters + # process the form parameters + # process the body parameter + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="GET", + resource_path="/api/v1/runs/{run_id}/items", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def list_runs_v1_runs_get( + self, + application_id: Annotated[StrictStr | None, Field(description="Optional application ID filter")] = None, + application_version: Annotated[StrictStr | None, Field(description="Optional Version Name")] = None, + external_id: Annotated[StrictStr | None, Field(description="Optionally filter runs by items with this external ID")] = None, + custom_metadata: Annotated[Annotated[str, Field(strict=True, max_length=1000)] | None, Field(description="Use PostgreSQL JSONPath expressions to filter runs by their custom_metadata. #### URL Encoding Required **Important**: JSONPath expressions contain special characters that must be URL-encoded when used in query parameters. Most HTTP clients handle this automatically, but when constructing URLs manually, please ensure proper encoding. #### Examples (Clear Format): - **Field existence**: `$.study` - Runs that have a study field defined - **Exact value match**: `$.study ? (@ == \"high\")` - Runs with specific study value - **Numeric comparison**: `$.confidence_score ? (@ > 0.75)` - Runs with confidence score greater than 0.75 - **Array operations**: `$.tags[*] ? (@ == \"draft\")` - Runs with tags array containing \"draft\" - **Complex conditions**: `$.resources ? (@.gpu_count > 2 && @.memory_gb >= 16)` - Runs with high resource requirements #### Examples (URL-Encoded Format): - **Field existence**: `%24.study` - **Exact value match**: `%24.study%20%3F%20(%40%20%3D%3D%20%22high%22)` - **Numeric comparison**: `%24.confidence_score%20%3F%20(%40%20%3E%200.75)` - **Array operations**: `%24.tags%5B*%5D%20%3F%20(%40%20%3D%3D%20%22draft%22)` - **Complex conditions**: `%24.resources%20%3F%20(%40.gpu_count%20%3E%202%20%26%26%20%40.memory_gb%20%3E%3D%2016)` #### Notes - JSONPath expressions are evaluated using PostgreSQL's `@?` operator - The `$.` prefix is automatically added to root-level field references if missing - String values in conditions must be enclosed in double quotes - Use `&&` for AND operations and `||` for OR operations - Regular expressions use `like_regex` with standard regex syntax - **Please remember to URL-encode the entire JSONPath expression when making HTTP requests** ")] = None, + page: Annotated[int, Field(strict=True, ge=1)] | None = None, + page_size: Annotated[int, Field(le=100, strict=True, ge=5)] | None = None, + for_organization: Annotated[StrictStr | None, Field(description="Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs.")] = None, + sort: Annotated[list[StrictStr] | None, Field(description="Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) ")] = None, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> list[RunReadResponse]: + """List Runs + + List runs with filtering, sorting, and pagination capabilities. Returns paginated runs that were submitted by the user. + + :param application_id: Optional application ID filter + :type application_id: str + :param application_version: Optional Version Name + :type application_version: str + :param external_id: Optionally filter runs by items with this external ID + :type external_id: str + :param custom_metadata: Use PostgreSQL JSONPath expressions to filter runs by their custom_metadata. #### URL Encoding Required **Important**: JSONPath expressions contain special characters that must be URL-encoded when used in query parameters. Most HTTP clients handle this automatically, but when constructing URLs manually, please ensure proper encoding. #### Examples (Clear Format): - **Field existence**: `$.study` - Runs that have a study field defined - **Exact value match**: `$.study ? (@ == \"high\")` - Runs with specific study value - **Numeric comparison**: `$.confidence_score ? (@ > 0.75)` - Runs with confidence score greater than 0.75 - **Array operations**: `$.tags[*] ? (@ == \"draft\")` - Runs with tags array containing \"draft\" - **Complex conditions**: `$.resources ? (@.gpu_count > 2 && @.memory_gb >= 16)` - Runs with high resource requirements #### Examples (URL-Encoded Format): - **Field existence**: `%24.study` - **Exact value match**: `%24.study%20%3F%20(%40%20%3D%3D%20%22high%22)` - **Numeric comparison**: `%24.confidence_score%20%3F%20(%40%20%3E%200.75)` - **Array operations**: `%24.tags%5B*%5D%20%3F%20(%40%20%3D%3D%20%22draft%22)` - **Complex conditions**: `%24.resources%20%3F%20(%40.gpu_count%20%3E%202%20%26%26%20%40.memory_gb%20%3E%3D%2016)` #### Notes - JSONPath expressions are evaluated using PostgreSQL's `@?` operator - The `$.` prefix is automatically added to root-level field references if missing - String values in conditions must be enclosed in double quotes - Use `&&` for AND operations and `||` for OR operations - Regular expressions use `like_regex` with standard regex syntax - **Please remember to URL-encode the entire JSONPath expression when making HTTP requests** + :type custom_metadata: str + :param page: + :type page: int + :param page_size: + :type page_size: int + :param for_organization: Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs. + :type for_organization: str + :param sort: Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) + :type sort: List[str] + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._list_runs_v1_runs_get_serialize( + application_id=application_id, + application_version=application_version, + external_id=external_id, + custom_metadata=custom_metadata, + page=page, + page_size=page_size, + for_organization=for_organization, + sort=sort, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "List[RunReadResponse]", + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def list_runs_v1_runs_get_with_http_info( + self, + application_id: Annotated[StrictStr | None, Field(description="Optional application ID filter")] = None, + application_version: Annotated[StrictStr | None, Field(description="Optional Version Name")] = None, + external_id: Annotated[StrictStr | None, Field(description="Optionally filter runs by items with this external ID")] = None, + custom_metadata: Annotated[Annotated[str, Field(strict=True, max_length=1000)] | None, Field(description="Use PostgreSQL JSONPath expressions to filter runs by their custom_metadata. #### URL Encoding Required **Important**: JSONPath expressions contain special characters that must be URL-encoded when used in query parameters. Most HTTP clients handle this automatically, but when constructing URLs manually, please ensure proper encoding. #### Examples (Clear Format): - **Field existence**: `$.study` - Runs that have a study field defined - **Exact value match**: `$.study ? (@ == \"high\")` - Runs with specific study value - **Numeric comparison**: `$.confidence_score ? (@ > 0.75)` - Runs with confidence score greater than 0.75 - **Array operations**: `$.tags[*] ? (@ == \"draft\")` - Runs with tags array containing \"draft\" - **Complex conditions**: `$.resources ? (@.gpu_count > 2 && @.memory_gb >= 16)` - Runs with high resource requirements #### Examples (URL-Encoded Format): - **Field existence**: `%24.study` - **Exact value match**: `%24.study%20%3F%20(%40%20%3D%3D%20%22high%22)` - **Numeric comparison**: `%24.confidence_score%20%3F%20(%40%20%3E%200.75)` - **Array operations**: `%24.tags%5B*%5D%20%3F%20(%40%20%3D%3D%20%22draft%22)` - **Complex conditions**: `%24.resources%20%3F%20(%40.gpu_count%20%3E%202%20%26%26%20%40.memory_gb%20%3E%3D%2016)` #### Notes - JSONPath expressions are evaluated using PostgreSQL's `@?` operator - The `$.` prefix is automatically added to root-level field references if missing - String values in conditions must be enclosed in double quotes - Use `&&` for AND operations and `||` for OR operations - Regular expressions use `like_regex` with standard regex syntax - **Please remember to URL-encode the entire JSONPath expression when making HTTP requests** ")] = None, + page: Annotated[int, Field(strict=True, ge=1)] | None = None, + page_size: Annotated[int, Field(le=100, strict=True, ge=5)] | None = None, + for_organization: Annotated[StrictStr | None, Field(description="Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs.")] = None, + sort: Annotated[list[StrictStr] | None, Field(description="Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) ")] = None, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[list[RunReadResponse]]: + """List Runs + + List runs with filtering, sorting, and pagination capabilities. Returns paginated runs that were submitted by the user. + + :param application_id: Optional application ID filter + :type application_id: str + :param application_version: Optional Version Name + :type application_version: str + :param external_id: Optionally filter runs by items with this external ID + :type external_id: str + :param custom_metadata: Use PostgreSQL JSONPath expressions to filter runs by their custom_metadata. #### URL Encoding Required **Important**: JSONPath expressions contain special characters that must be URL-encoded when used in query parameters. Most HTTP clients handle this automatically, but when constructing URLs manually, please ensure proper encoding. #### Examples (Clear Format): - **Field existence**: `$.study` - Runs that have a study field defined - **Exact value match**: `$.study ? (@ == \"high\")` - Runs with specific study value - **Numeric comparison**: `$.confidence_score ? (@ > 0.75)` - Runs with confidence score greater than 0.75 - **Array operations**: `$.tags[*] ? (@ == \"draft\")` - Runs with tags array containing \"draft\" - **Complex conditions**: `$.resources ? (@.gpu_count > 2 && @.memory_gb >= 16)` - Runs with high resource requirements #### Examples (URL-Encoded Format): - **Field existence**: `%24.study` - **Exact value match**: `%24.study%20%3F%20(%40%20%3D%3D%20%22high%22)` - **Numeric comparison**: `%24.confidence_score%20%3F%20(%40%20%3E%200.75)` - **Array operations**: `%24.tags%5B*%5D%20%3F%20(%40%20%3D%3D%20%22draft%22)` - **Complex conditions**: `%24.resources%20%3F%20(%40.gpu_count%20%3E%202%20%26%26%20%40.memory_gb%20%3E%3D%2016)` #### Notes - JSONPath expressions are evaluated using PostgreSQL's `@?` operator - The `$.` prefix is automatically added to root-level field references if missing - String values in conditions must be enclosed in double quotes - Use `&&` for AND operations and `||` for OR operations - Regular expressions use `like_regex` with standard regex syntax - **Please remember to URL-encode the entire JSONPath expression when making HTTP requests** + :type custom_metadata: str + :param page: + :type page: int + :param page_size: + :type page_size: int + :param for_organization: Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs. + :type for_organization: str + :param sort: Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) + :type sort: List[str] + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._list_runs_v1_runs_get_serialize( + application_id=application_id, + application_version=application_version, + external_id=external_id, + custom_metadata=custom_metadata, + page=page, + page_size=page_size, + for_organization=for_organization, + sort=sort, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "List[RunReadResponse]", + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def list_runs_v1_runs_get_without_preload_content( + self, + application_id: Annotated[StrictStr | None, Field(description="Optional application ID filter")] = None, + application_version: Annotated[StrictStr | None, Field(description="Optional Version Name")] = None, + external_id: Annotated[StrictStr | None, Field(description="Optionally filter runs by items with this external ID")] = None, + custom_metadata: Annotated[Annotated[str, Field(strict=True, max_length=1000)] | None, Field(description="Use PostgreSQL JSONPath expressions to filter runs by their custom_metadata. #### URL Encoding Required **Important**: JSONPath expressions contain special characters that must be URL-encoded when used in query parameters. Most HTTP clients handle this automatically, but when constructing URLs manually, please ensure proper encoding. #### Examples (Clear Format): - **Field existence**: `$.study` - Runs that have a study field defined - **Exact value match**: `$.study ? (@ == \"high\")` - Runs with specific study value - **Numeric comparison**: `$.confidence_score ? (@ > 0.75)` - Runs with confidence score greater than 0.75 - **Array operations**: `$.tags[*] ? (@ == \"draft\")` - Runs with tags array containing \"draft\" - **Complex conditions**: `$.resources ? (@.gpu_count > 2 && @.memory_gb >= 16)` - Runs with high resource requirements #### Examples (URL-Encoded Format): - **Field existence**: `%24.study` - **Exact value match**: `%24.study%20%3F%20(%40%20%3D%3D%20%22high%22)` - **Numeric comparison**: `%24.confidence_score%20%3F%20(%40%20%3E%200.75)` - **Array operations**: `%24.tags%5B*%5D%20%3F%20(%40%20%3D%3D%20%22draft%22)` - **Complex conditions**: `%24.resources%20%3F%20(%40.gpu_count%20%3E%202%20%26%26%20%40.memory_gb%20%3E%3D%2016)` #### Notes - JSONPath expressions are evaluated using PostgreSQL's `@?` operator - The `$.` prefix is automatically added to root-level field references if missing - String values in conditions must be enclosed in double quotes - Use `&&` for AND operations and `||` for OR operations - Regular expressions use `like_regex` with standard regex syntax - **Please remember to URL-encode the entire JSONPath expression when making HTTP requests** ")] = None, + page: Annotated[int, Field(strict=True, ge=1)] | None = None, + page_size: Annotated[int, Field(le=100, strict=True, ge=5)] | None = None, + for_organization: Annotated[StrictStr | None, Field(description="Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs.")] = None, + sort: Annotated[list[StrictStr] | None, Field(description="Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) ")] = None, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """List Runs + + List runs with filtering, sorting, and pagination capabilities. Returns paginated runs that were submitted by the user. + + :param application_id: Optional application ID filter + :type application_id: str + :param application_version: Optional Version Name + :type application_version: str + :param external_id: Optionally filter runs by items with this external ID + :type external_id: str + :param custom_metadata: Use PostgreSQL JSONPath expressions to filter runs by their custom_metadata. #### URL Encoding Required **Important**: JSONPath expressions contain special characters that must be URL-encoded when used in query parameters. Most HTTP clients handle this automatically, but when constructing URLs manually, please ensure proper encoding. #### Examples (Clear Format): - **Field existence**: `$.study` - Runs that have a study field defined - **Exact value match**: `$.study ? (@ == \"high\")` - Runs with specific study value - **Numeric comparison**: `$.confidence_score ? (@ > 0.75)` - Runs with confidence score greater than 0.75 - **Array operations**: `$.tags[*] ? (@ == \"draft\")` - Runs with tags array containing \"draft\" - **Complex conditions**: `$.resources ? (@.gpu_count > 2 && @.memory_gb >= 16)` - Runs with high resource requirements #### Examples (URL-Encoded Format): - **Field existence**: `%24.study` - **Exact value match**: `%24.study%20%3F%20(%40%20%3D%3D%20%22high%22)` - **Numeric comparison**: `%24.confidence_score%20%3F%20(%40%20%3E%200.75)` - **Array operations**: `%24.tags%5B*%5D%20%3F%20(%40%20%3D%3D%20%22draft%22)` - **Complex conditions**: `%24.resources%20%3F%20(%40.gpu_count%20%3E%202%20%26%26%20%40.memory_gb%20%3E%3D%2016)` #### Notes - JSONPath expressions are evaluated using PostgreSQL's `@?` operator - The `$.` prefix is automatically added to root-level field references if missing - String values in conditions must be enclosed in double quotes - Use `&&` for AND operations and `||` for OR operations - Regular expressions use `like_regex` with standard regex syntax - **Please remember to URL-encode the entire JSONPath expression when making HTTP requests** + :type custom_metadata: str + :param page: + :type page: int + :param page_size: + :type page_size: int + :param for_organization: Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs. + :type for_organization: str + :param sort: Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) + :type sort: List[str] + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._list_runs_v1_runs_get_serialize( + application_id=application_id, + application_version=application_version, + external_id=external_id, + custom_metadata=custom_metadata, + page=page, + page_size=page_size, + for_organization=for_organization, + sort=sort, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "List[RunReadResponse]", + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _list_runs_v1_runs_get_serialize( + self, + application_id, + application_version, + external_id, + custom_metadata, + page, + page_size, + for_organization, + sort, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + "sort": "multi", + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + # process the query parameters + if application_id is not None: + + _query_params.append(("application_id", application_id)) + + if application_version is not None: + + _query_params.append(("application_version", application_version)) + + if external_id is not None: + + _query_params.append(("external_id", external_id)) + + if custom_metadata is not None: + + _query_params.append(("custom_metadata", custom_metadata)) + + if page is not None: + + _query_params.append(("page", page)) + + if page_size is not None: + + _query_params.append(("page_size", page_size)) + + if for_organization is not None: + + _query_params.append(("for_organization", for_organization)) + + if sort is not None: + + _query_params.append(("sort", sort)) + + # process the header parameters + # process the form parameters + # process the body parameter + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="GET", + resource_path="/api/v1/runs", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def list_version_documents( + self, + application_id: StrictStr, + version: StrictStr, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> list[VersionDocumentResponse]: + """List version documents + + List public documents attached to an application version. Returns only documents with ``visibility=public`` and ``status=uploaded``. + + :param application_id: (required) + :type application_id: str + :param version: (required) + :type version: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._list_version_documents_serialize( + application_id=application_id, + version=version, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "List[VersionDocumentResponse]", + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def list_version_documents_with_http_info( + self, + application_id: StrictStr, + version: StrictStr, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[list[VersionDocumentResponse]]: + """List version documents + + List public documents attached to an application version. Returns only documents with ``visibility=public`` and ``status=uploaded``. + + :param application_id: (required) + :type application_id: str + :param version: (required) + :type version: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._list_version_documents_serialize( + application_id=application_id, + version=version, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "List[VersionDocumentResponse]", + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def list_version_documents_without_preload_content( + self, + application_id: StrictStr, + version: StrictStr, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """List version documents + + List public documents attached to an application version. Returns only documents with ``visibility=public`` and ``status=uploaded``. + + :param application_id: (required) + :type application_id: str + :param version: (required) + :type version: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._list_version_documents_serialize( + application_id=application_id, + version=version, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "List[VersionDocumentResponse]", + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _list_version_documents_serialize( + self, + application_id, + version, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + if application_id is not None: + _path_params["application_id"] = application_id + if version is not None: + _path_params["version"] = version + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="GET", + resource_path="/api/v1/applications/{application_id}/versions/{version}/documents", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def put_item_custom_metadata_by_run_v1_runs_run_id_items_external_id_custom_metadata_put( + self, + run_id: Annotated[StrictStr, Field(description="The run id, returned by `POST /runs/` endpoint")], + external_id: Annotated[StrictStr, Field(description="The `external_id` that was defined for the item by the customer that triggered the run.")], + custom_metadata_update_request: CustomMetadataUpdateRequest, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> CustomMetadataUpdateResponse: + """Put Item Custom Metadata By Run + + Update the custom metadata of the item with the specified `external_id`, belonging to the specified run. Optionally, a checksum may be provided along the custom metadata JSON. It can be used to verify if the custom metadata was updated since the last time it was accessed. If the checksum is provided, it must match the existing custom metadata in the system, ensuring that the current custom metadata value to be overwritten is acknowledged by the user. If no checksum is provided, submitted metadata directly overwrites the existing metadata, without any checks. The latest custom metadata and checksum can be retrieved for individual items via `GET /v1/runs/{run_id}/items/{external_id}`, and for all items of a run via `GET /v1/runs/{run_id}/items`. + + :param run_id: The run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param external_id: The `external_id` that was defined for the item by the customer that triggered the run. (required) + :type external_id: str + :param custom_metadata_update_request: (required) + :type custom_metadata_update_request: CustomMetadataUpdateRequest + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._put_item_custom_metadata_by_run_v1_runs_run_id_items_external_id_custom_metadata_put_serialize( + run_id=run_id, + external_id=external_id, + custom_metadata_update_request=custom_metadata_update_request, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "CustomMetadataUpdateResponse", + "403": None, + "404": None, + "412": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def put_item_custom_metadata_by_run_v1_runs_run_id_items_external_id_custom_metadata_put_with_http_info( + self, + run_id: Annotated[StrictStr, Field(description="The run id, returned by `POST /runs/` endpoint")], + external_id: Annotated[StrictStr, Field(description="The `external_id` that was defined for the item by the customer that triggered the run.")], + custom_metadata_update_request: CustomMetadataUpdateRequest, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[CustomMetadataUpdateResponse]: + """Put Item Custom Metadata By Run + + Update the custom metadata of the item with the specified `external_id`, belonging to the specified run. Optionally, a checksum may be provided along the custom metadata JSON. It can be used to verify if the custom metadata was updated since the last time it was accessed. If the checksum is provided, it must match the existing custom metadata in the system, ensuring that the current custom metadata value to be overwritten is acknowledged by the user. If no checksum is provided, submitted metadata directly overwrites the existing metadata, without any checks. The latest custom metadata and checksum can be retrieved for individual items via `GET /v1/runs/{run_id}/items/{external_id}`, and for all items of a run via `GET /v1/runs/{run_id}/items`. + + :param run_id: The run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param external_id: The `external_id` that was defined for the item by the customer that triggered the run. (required) + :type external_id: str + :param custom_metadata_update_request: (required) + :type custom_metadata_update_request: CustomMetadataUpdateRequest + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._put_item_custom_metadata_by_run_v1_runs_run_id_items_external_id_custom_metadata_put_serialize( + run_id=run_id, + external_id=external_id, + custom_metadata_update_request=custom_metadata_update_request, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "CustomMetadataUpdateResponse", + "403": None, + "404": None, + "412": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def put_item_custom_metadata_by_run_v1_runs_run_id_items_external_id_custom_metadata_put_without_preload_content( + self, + run_id: Annotated[StrictStr, Field(description="The run id, returned by `POST /runs/` endpoint")], + external_id: Annotated[StrictStr, Field(description="The `external_id` that was defined for the item by the customer that triggered the run.")], + custom_metadata_update_request: CustomMetadataUpdateRequest, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Put Item Custom Metadata By Run + + Update the custom metadata of the item with the specified `external_id`, belonging to the specified run. Optionally, a checksum may be provided along the custom metadata JSON. It can be used to verify if the custom metadata was updated since the last time it was accessed. If the checksum is provided, it must match the existing custom metadata in the system, ensuring that the current custom metadata value to be overwritten is acknowledged by the user. If no checksum is provided, submitted metadata directly overwrites the existing metadata, without any checks. The latest custom metadata and checksum can be retrieved for individual items via `GET /v1/runs/{run_id}/items/{external_id}`, and for all items of a run via `GET /v1/runs/{run_id}/items`. + + :param run_id: The run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param external_id: The `external_id` that was defined for the item by the customer that triggered the run. (required) + :type external_id: str + :param custom_metadata_update_request: (required) + :type custom_metadata_update_request: CustomMetadataUpdateRequest + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._put_item_custom_metadata_by_run_v1_runs_run_id_items_external_id_custom_metadata_put_serialize( + run_id=run_id, + external_id=external_id, + custom_metadata_update_request=custom_metadata_update_request, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "CustomMetadataUpdateResponse", + "403": None, + "404": None, + "412": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _put_item_custom_metadata_by_run_v1_runs_run_id_items_external_id_custom_metadata_put_serialize( + self, + run_id, + external_id, + custom_metadata_update_request, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + if run_id is not None: + _path_params["run_id"] = run_id + if external_id is not None: + _path_params["external_id"] = external_id + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + if custom_metadata_update_request is not None: + _body_params = custom_metadata_update_request + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # set the HTTP header `Content-Type` + if _content_type: + _header_params["Content-Type"] = _content_type + else: + _default_content_type = ( + self.api_client.select_header_content_type( + [ + "application/json" + ] + ) + ) + if _default_content_type is not None: + _header_params["Content-Type"] = _default_content_type + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="PUT", + resource_path="/api/v1/runs/{run_id}/items/{external_id}/custom-metadata", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def put_run_custom_metadata_v1_runs_run_id_custom_metadata_put( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /runs/` endpoint")], + custom_metadata_update_request: CustomMetadataUpdateRequest, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> CustomMetadataUpdateResponse: + """Put Run Custom Metadata + + Update the custom metadata of a run with the specified `run_id`. Optionally, a checksum may be provided along the custom metadata JSON. It can be used to verify if the custom metadata was updated since the last time it was accessed. If the checksum is provided, it must match the existing custom metadata in the system, ensuring that the current custom metadata value to be overwritten is acknowledged by the user. If no checksum is provided, submitted metadata directly overwrites the existing metadata, without any checks. The latest custom metadata and checksum can be retrieved for the run via the `GET /v1/runs/{run_id}` endpoint. **Note on deadlines:** Run deadlines must be set during run creation and cannot be modified afterward. Any deadline changes in custom metadata will be ignored by the system. + + :param run_id: Run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param custom_metadata_update_request: (required) + :type custom_metadata_update_request: CustomMetadataUpdateRequest + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._put_run_custom_metadata_v1_runs_run_id_custom_metadata_put_serialize( + run_id=run_id, + custom_metadata_update_request=custom_metadata_update_request, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "CustomMetadataUpdateResponse", + "404": None, + "403": None, + "412": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def put_run_custom_metadata_v1_runs_run_id_custom_metadata_put_with_http_info( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /runs/` endpoint")], + custom_metadata_update_request: CustomMetadataUpdateRequest, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[CustomMetadataUpdateResponse]: + """Put Run Custom Metadata + + Update the custom metadata of a run with the specified `run_id`. Optionally, a checksum may be provided along the custom metadata JSON. It can be used to verify if the custom metadata was updated since the last time it was accessed. If the checksum is provided, it must match the existing custom metadata in the system, ensuring that the current custom metadata value to be overwritten is acknowledged by the user. If no checksum is provided, submitted metadata directly overwrites the existing metadata, without any checks. The latest custom metadata and checksum can be retrieved for the run via the `GET /v1/runs/{run_id}` endpoint. **Note on deadlines:** Run deadlines must be set during run creation and cannot be modified afterward. Any deadline changes in custom metadata will be ignored by the system. + + :param run_id: Run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param custom_metadata_update_request: (required) + :type custom_metadata_update_request: CustomMetadataUpdateRequest + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._put_run_custom_metadata_v1_runs_run_id_custom_metadata_put_serialize( + run_id=run_id, + custom_metadata_update_request=custom_metadata_update_request, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "CustomMetadataUpdateResponse", + "404": None, + "403": None, + "412": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def put_run_custom_metadata_v1_runs_run_id_custom_metadata_put_without_preload_content( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /runs/` endpoint")], + custom_metadata_update_request: CustomMetadataUpdateRequest, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Put Run Custom Metadata + + Update the custom metadata of a run with the specified `run_id`. Optionally, a checksum may be provided along the custom metadata JSON. It can be used to verify if the custom metadata was updated since the last time it was accessed. If the checksum is provided, it must match the existing custom metadata in the system, ensuring that the current custom metadata value to be overwritten is acknowledged by the user. If no checksum is provided, submitted metadata directly overwrites the existing metadata, without any checks. The latest custom metadata and checksum can be retrieved for the run via the `GET /v1/runs/{run_id}` endpoint. **Note on deadlines:** Run deadlines must be set during run creation and cannot be modified afterward. Any deadline changes in custom metadata will be ignored by the system. + + :param run_id: Run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param custom_metadata_update_request: (required) + :type custom_metadata_update_request: CustomMetadataUpdateRequest + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + _param = self._put_run_custom_metadata_v1_runs_run_id_custom_metadata_put_serialize( + run_id=run_id, + custom_metadata_update_request=custom_metadata_update_request, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "CustomMetadataUpdateResponse", + "404": None, + "403": None, + "412": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _put_run_custom_metadata_v1_runs_run_id_custom_metadata_put_serialize( + self, + run_id, + custom_metadata_update_request, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + if run_id is not None: + _path_params["run_id"] = run_id + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + if custom_metadata_update_request is not None: + _body_params = custom_metadata_update_request + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # set the HTTP header `Content-Type` + if _content_type: + _header_params["Content-Type"] = _content_type + else: + _default_content_type = ( + self.api_client.select_header_content_type( + [ + "application/json" + ] + ) + ) + if _default_content_type is not None: + _header_params["Content-Type"] = _default_content_type + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="PUT", + resource_path="/api/v1/runs/{run_id}/custom-metadata", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + @validate_call + def read_application_by_id_v1_applications_application_id_get( + self, + application_id: StrictStr, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApplicationReadResponse: + """Read Application By Id + + Retrieve details of a specific application by its ID. + + :param application_id: (required) + :type application_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ + _param = self._read_application_by_id_v1_applications_application_id_get_serialize( + application_id=application_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "ApplicationReadResponse", + "403": None, + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + @validate_call + def read_application_by_id_v1_applications_application_id_get_with_http_info( + self, + application_id: StrictStr, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[ApplicationReadResponse]: + """Read Application By Id + + Retrieve details of a specific application by its ID. + + :param application_id: (required) + :type application_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ + _param = self._read_application_by_id_v1_applications_application_id_get_serialize( + application_id=application_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "ApplicationReadResponse", + "403": None, + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + @validate_call + def read_application_by_id_v1_applications_application_id_get_without_preload_content( + self, + application_id: StrictStr, + _request_timeout: None | Annotated[StrictFloat, Field(gt=0)] | tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]] = None, + _request_auth: dict[StrictStr, Any] | None = None, + _content_type: StrictStr | None = None, + _headers: dict[StrictStr, Any] | None = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Read Application By Id + + Retrieve details of a specific application by its ID. + + :param application_id: (required) + :type application_id: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ + _param = self._read_application_by_id_v1_applications_application_id_get_serialize( + application_id=application_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: dict[str, str | None] = { + "200": "ApplicationReadResponse", + "403": None, + "404": None, + "422": "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + def _read_application_by_id_v1_applications_application_id_get_serialize( + self, + application_id, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: dict[str, str] = { + } + + _path_params: dict[str, str] = {} + _query_params: list[tuple[str, str]] = [] + _header_params: dict[str, str | None] = _headers or {} + _form_params: list[tuple[str, str]] = [] + _files: dict[ + str, str | bytes | list[str] | list[bytes] | list[tuple[str, bytes]] + ] = {} + _body_params: bytes | None = None + + # process the path parameters + if application_id is not None: + _path_params["application_id"] = application_id + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + # set the HTTP header `Accept` + if "Accept" not in _header_params: + _header_params["Accept"] = self.api_client.select_header_accept( + [ + "application/json" + ] + ) + + # authentication setting + _auth_settings: list[str] = [ + "OAuth2AuthorizationCodeBearer" + ] + + return self.api_client.param_serialize( + method="GET", + resource_path="/api/v1/applications/{application_id}", + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) diff --git a/src/aignostics_sdk/_codegen/api_client.py b/src/aignostics_sdk/_codegen/api_client.py new file mode 100644 index 000000000..7f32177aa --- /dev/null +++ b/src/aignostics_sdk/_codegen/api_client.py @@ -0,0 +1,782 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +import datetime +import decimal +import json +import mimetypes +import os +import pathlib +import re +import tempfile +from enum import Enum +from urllib.parse import quote + +from dateutil.parser import parse +from pydantic import SecretStr + +import aignostics_sdk._codegen.models +from aignostics_sdk._codegen import rest +from aignostics_sdk._codegen.api_response import ApiResponse +from aignostics_sdk._codegen.api_response import T as ApiResponseT +from aignostics_sdk._codegen.configuration import Configuration +from aignostics_sdk._codegen.exceptions import ( + ApiException, + ApiValueError, +) + +RequestSerialized = tuple[str, str, dict[str, str], str | None, list[str]] + + +class ApiClient: + """Generic API client for OpenAPI client library builds. + + OpenAPI generic API client. This client handles the client- + server communication, and is invariant across implementations. Specifics of + the methods and models for each application are generated from the OpenAPI + templates. + + :param configuration: .Configuration object for this client + :param header_name: a header to pass when making calls to the API. + :param header_value: a header value to pass when making calls to + the API. + :param cookie: a cookie to include in the header when making calls + to the API + """ + + PRIMITIVE_TYPES = (float, bool, bytes, str, int) + NATIVE_TYPES_MAPPING = { + "int": int, + "long": int, # TODO remove as only py3 is supported? + "float": float, + "str": str, + "bool": bool, + "date": datetime.date, + "datetime": datetime.datetime, + "decimal": decimal.Decimal, + "object": object, + } + _pool = None + + def __init__( + self, + configuration=None, + header_name=None, + header_value=None, + cookie=None + ) -> None: + # use default configuration if none is provided + if configuration is None: + configuration = Configuration.get_default() + self.configuration = configuration + + self.rest_client = rest.RESTClientObject(configuration) + self.default_headers = {} + if header_name is not None: + self.default_headers[header_name] = header_value + self.cookie = cookie + # Set default User-Agent. + self.user_agent = "OpenAPI-Generator/1.0.0/python" + self.client_side_validation = configuration.client_side_validation + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + pass + + @property + def user_agent(self): + """User agent for this API client""" + return self.default_headers["User-Agent"] + + @user_agent.setter + def user_agent(self, value): + self.default_headers["User-Agent"] = value + + def set_default_header(self, header_name, header_value): + self.default_headers[header_name] = header_value + + _default = None + + @classmethod + def get_default(cls): + """Return new instance of ApiClient. + + This method returns newly created, based on default constructor, + object of ApiClient class or returns a copy of default + ApiClient. + + :return: The ApiClient object. + """ + if cls._default is None: + cls._default = ApiClient() + return cls._default + + @classmethod + def set_default(cls, default): + """Set default instance of ApiClient. + + It stores default ApiClient. + + :param default: object of ApiClient. + """ + cls._default = default + + def param_serialize( + self, + method, + resource_path, + path_params=None, + query_params=None, + header_params=None, + body=None, + post_params=None, + files=None, auth_settings=None, + collection_formats=None, + _host=None, + _request_auth=None + ) -> RequestSerialized: + """Builds the HTTP request params needed by the request. + :param method: Method to call. + :param resource_path: Path to method endpoint. + :param path_params: Path parameters in the url. + :param query_params: Query parameters in the url. + :param header_params: Header parameters to be + placed in the request header. + :param body: Request body. + :param post_params dict: Request post form parameters, + for `application/x-www-form-urlencoded`, `multipart/form-data`. + :param auth_settings list: Auth Settings names for the request. + :param files dict: key -> filename, value -> filepath, + for `multipart/form-data`. + :param collection_formats: dict of collection formats for path, query, + header, and post parameters. + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + :return: tuple of form (path, http_method, query_params, header_params, + body, post_params, files) + """ + config = self.configuration + + # header parameters + header_params = header_params or {} + header_params.update(self.default_headers) + if self.cookie: + header_params["Cookie"] = self.cookie + if header_params: + header_params = self.sanitize_for_serialization(header_params) + header_params = dict( + self.parameters_to_tuples(header_params, collection_formats) + ) + + # path parameters + if path_params: + path_params = self.sanitize_for_serialization(path_params) + path_params = self.parameters_to_tuples( + path_params, + collection_formats + ) + for k, v in path_params: + # specified safe chars, encode everything + resource_path = resource_path.replace( + "{%s}" % k, + quote(str(v), safe=config.safe_chars_for_path_param) + ) + + # post parameters + if post_params or files: + post_params = post_params or [] + post_params = self.sanitize_for_serialization(post_params) + post_params = self.parameters_to_tuples( + post_params, + collection_formats + ) + if files: + post_params.extend(self.files_parameters(files)) + + # auth setting + self.update_params_for_auth( + header_params, + query_params, + auth_settings, + resource_path, + method, + body, + request_auth=_request_auth + ) + + # body + if body: + body = self.sanitize_for_serialization(body) + + # request url + if _host is None or self.configuration.ignore_operation_servers: + url = self.configuration.host + resource_path + else: + # use server/host defined in path or operation instead + url = _host + resource_path + + # query parameters + if query_params: + query_params = self.sanitize_for_serialization(query_params) + url_query = self.parameters_to_url_query( + query_params, + collection_formats + ) + url += "?" + url_query + + return method, url, header_params, body, post_params + + def call_api( + self, + method, + url, + header_params=None, + body=None, + post_params=None, + _request_timeout=None + ) -> rest.RESTResponse: + """Makes the HTTP request (synchronous) + :param method: Method to call. + :param url: Path to method endpoint. + :param header_params: Header parameters to be + placed in the request header. + :param body: Request body. + :param post_params dict: Request post form parameters, + for `application/x-www-form-urlencoded`, `multipart/form-data`. + :param _request_timeout: timeout setting for this request. + :return: RESTResponse + """ + try: + # perform request and return response + response_data = self.rest_client.request( + method, url, + headers=header_params, + body=body, post_params=post_params, + _request_timeout=_request_timeout + ) + + except ApiException as e: + raise e + + return response_data + + def response_deserialize( + self, + response_data: rest.RESTResponse, + response_types_map: dict[str, ApiResponseT] | None = None + ) -> ApiResponse[ApiResponseT]: + """Deserializes response into an object. + :param response_data: RESTResponse object to be deserialized. + :param response_types_map: dict of response types. + :return: ApiResponse + """ + msg = "RESTResponse.read() must be called before passing it to response_deserialize()" + assert response_data.data is not None, msg + + response_type = response_types_map.get(str(response_data.status), None) + if not response_type and isinstance(response_data.status, int) and 100 <= response_data.status <= 599: + # if not found, look for '1XX', '2XX', etc. + response_type = response_types_map.get(str(response_data.status)[0] + "XX", None) + + # deserialize response data + response_text = None + return_data = None + try: + if response_type == "bytearray": + return_data = response_data.data + elif response_type == "file": + return_data = self.__deserialize_file(response_data) + elif response_type is not None: + match = None + content_type = response_data.getheader("content-type") + if content_type is not None: + match = re.search(r"charset=([a-zA-Z\-\d]+)[\s;]?", content_type) + encoding = match.group(1) if match else "utf-8" + response_text = response_data.data.decode(encoding) + return_data = self.deserialize(response_text, response_type, content_type) + finally: + if not 200 <= response_data.status <= 299: + raise ApiException.from_response( + http_resp=response_data, + body=response_text, + data=return_data, + ) + + return ApiResponse( + status_code=response_data.status, + data=return_data, + headers=response_data.getheaders(), + raw_data=response_data.data + ) + + def sanitize_for_serialization(self, obj): + """Builds a JSON POST object. + + If obj is None, return None. + If obj is SecretStr, return obj.get_secret_value() + If obj is str, int, long, float, bool, return directly. + If obj is datetime.datetime, datetime.date + convert to string in iso8601 format. + If obj is decimal.Decimal return string representation. + If obj is list, sanitize each element in the list. + If obj is dict, return the dict. + If obj is OpenAPI model, return the properties dict. + + :param obj: The data to serialize. + :return: The serialized form of data. + """ + if obj is None: + return None + if isinstance(obj, Enum): + return obj.value + if isinstance(obj, SecretStr): + return obj.get_secret_value() + if isinstance(obj, self.PRIMITIVE_TYPES): + return obj + if isinstance(obj, list): + return [ + self.sanitize_for_serialization(sub_obj) for sub_obj in obj + ] + if isinstance(obj, tuple): + return tuple( + self.sanitize_for_serialization(sub_obj) for sub_obj in obj + ) + if isinstance(obj, (datetime.datetime, datetime.date)): + return obj.isoformat() + if isinstance(obj, decimal.Decimal): + return str(obj) + + if isinstance(obj, dict): + obj_dict = obj + # Convert model obj to dict except + # attributes `openapi_types`, `attribute_map` + # and attributes which value is not None. + # Convert attribute name to json key in + # model definition for request. + elif hasattr(obj, "to_dict") and callable(obj.to_dict): + obj_dict = obj.to_dict() + else: + obj_dict = obj.__dict__ + + return { + key: self.sanitize_for_serialization(val) + for key, val in obj_dict.items() + } + + def deserialize(self, response_text: str, response_type: str, content_type: str | None): + """Deserializes response into an object. + + :param response: RESTResponse object to be deserialized. + :param response_type: class literal for + deserialized object, or string of class name. + :param content_type: content type of response. + + :return: deserialized object. + """ + # fetch data from response object + if content_type is None: + try: + data = json.loads(response_text) + except ValueError: + data = response_text + elif re.match(r"^application/(json|[\w!#$&.+-^_]+\+json)\s*(;|$)", content_type, re.IGNORECASE): + if response_text == "": + data = "" + else: + data = json.loads(response_text) + elif re.match(r"^text\/[a-z.+-]+\s*(;|$)", content_type, re.IGNORECASE): + data = response_text + else: + raise ApiException( + status=0, + reason=f"Unsupported content type: {content_type}" + ) + + return self.__deserialize(data, response_type) + + def __deserialize(self, data, klass): + """Deserializes dict, list, str into an object. + + :param data: dict, list or str. + :param klass: class literal, or string of class name. + + :return: object. + """ + if data is None: + return None + + if isinstance(klass, str): + if klass.startswith("List["): + m = re.match(r"List\[(.*)]", klass) + assert m is not None, "Malformed List type definition" + sub_kls = m.group(1) + return [self.__deserialize(sub_data, sub_kls) + for sub_data in data] + + if klass.startswith("Dict["): + m = re.match(r"Dict\[([^,]*), (.*)]", klass) + assert m is not None, "Malformed Dict type definition" + sub_kls = m.group(2) + return {k: self.__deserialize(v, sub_kls) + for k, v in data.items()} + + # convert str to class + if klass in self.NATIVE_TYPES_MAPPING: + klass = self.NATIVE_TYPES_MAPPING[klass] + else: + klass = getattr(aignostics_sdk._codegen.models, klass) + + if klass in self.PRIMITIVE_TYPES: + return self.__deserialize_primitive(data, klass) + if klass == object: + return self.__deserialize_object(data) + if klass == datetime.date: + return self.__deserialize_date(data) + if klass == datetime.datetime: + return self.__deserialize_datetime(data) + if klass == decimal.Decimal: + return decimal.Decimal(data) + if issubclass(klass, Enum): + return self.__deserialize_enum(data, klass) + return self.__deserialize_model(data, klass) + + def parameters_to_tuples(self, params, collection_formats): + """Get parameters as list of tuples, formatting collections. + + :param params: Parameters as dict or list of two-tuples + :param dict collection_formats: Parameter collection formats + :return: Parameters as list of tuples, collections formatted + """ + new_params: list[tuple[str, str]] = [] + if collection_formats is None: + collection_formats = {} + for k, v in params.items() if isinstance(params, dict) else params: + if k in collection_formats: + collection_format = collection_formats[k] + if collection_format == "multi": + new_params.extend((k, value) for value in v) + else: + if collection_format == "ssv": + delimiter = " " + elif collection_format == "tsv": + delimiter = "\t" + elif collection_format == "pipes": + delimiter = "|" + else: # csv is the default + delimiter = "," + new_params.append( + (k, delimiter.join(str(value) for value in v))) + else: + new_params.append((k, v)) + return new_params + + def parameters_to_url_query(self, params, collection_formats): + """Get parameters as list of tuples, formatting collections. + + :param params: Parameters as dict or list of two-tuples + :param dict collection_formats: Parameter collection formats + :return: URL query string (e.g. a=Hello%20World&b=123) + """ + new_params: list[tuple[str, str]] = [] + if collection_formats is None: + collection_formats = {} + for k, v in params.items() if isinstance(params, dict) else params: + if isinstance(v, bool): + v = str(v).lower() + if isinstance(v, (int, float)): + v = str(v) + if isinstance(v, dict): + v = json.dumps(v) + + if k in collection_formats: + collection_format = collection_formats[k] + if collection_format == "multi": + new_params.extend((k, str(value)) for value in v) + else: + if collection_format == "ssv": + delimiter = " " + elif collection_format == "tsv": + delimiter = "\t" + elif collection_format == "pipes": + delimiter = "|" + else: # csv is the default + delimiter = "," + new_params.append( + (k, delimiter.join(quote(str(value)) for value in v)) + ) + else: + new_params.append((k, quote(str(v)))) + + return "&".join(["=".join(map(str, item)) for item in new_params]) + + def files_parameters( + self, + files: dict[str, str | bytes | list[str] | list[bytes] | tuple[str, bytes]], + ): + """Builds form parameters. + + :param files: File parameters. + :return: Form parameters with files. + """ + params = [] + for k, v in files.items(): + if isinstance(v, str): + with open(v, "rb") as f: + filename = os.path.basename(f.name) + filedata = f.read() + elif isinstance(v, bytes): + filename = k + filedata = v + elif isinstance(v, tuple): + filename, filedata = v + elif isinstance(v, list): + for file_param in v: + params.extend(self.files_parameters({k: file_param})) + continue + else: + raise ValueError("Unsupported file value") + mimetype = ( + mimetypes.guess_type(filename)[0] + or "application/octet-stream" + ) + params.append( + tuple([k, tuple([filename, filedata, mimetype])]) + ) + return params + + def select_header_accept(self, accepts: list[str]) -> str | None: + """Returns `Accept` based on an array of accepts provided. + + :param accepts: List of headers. + :return: Accept (e.g. application/json). + """ + if not accepts: + return None + + for accept in accepts: + if re.search(r"json", accept, re.IGNORECASE): + return accept + + return accepts[0] + + def select_header_content_type(self, content_types): + """Returns `Content-Type` based on an array of content_types provided. + + :param content_types: List of content-types. + :return: Content-Type (e.g. application/json). + """ + if not content_types: + return None + + for content_type in content_types: + if re.search(r"json", content_type, re.IGNORECASE): + return content_type + + return content_types[0] + + def update_params_for_auth( + self, + headers, + queries, + auth_settings, + resource_path, + method, + body, + request_auth=None + ) -> None: + """Updates header and query params based on authentication setting. + + :param headers: Header parameters dict to be updated. + :param queries: Query parameters tuple list to be updated. + :param auth_settings: Authentication setting identifiers list. + :resource_path: A string representation of the HTTP request resource path. + :method: A string representation of the HTTP request method. + :body: A object representing the body of the HTTP request. + The object type is the return value of sanitize_for_serialization(). + :param request_auth: if set, the provided settings will + override the token in the configuration. + """ + if not auth_settings: + return + + if request_auth: + self._apply_auth_params( + headers, + queries, + resource_path, + method, + body, + request_auth + ) + else: + for auth in auth_settings: + auth_setting = self.configuration.auth_settings().get(auth) + if auth_setting: + self._apply_auth_params( + headers, + queries, + resource_path, + method, + body, + auth_setting + ) + + def _apply_auth_params( + self, + headers, + queries, + resource_path, + method, + body, + auth_setting + ) -> None: + """Updates the request parameters based on a single auth_setting + + :param headers: Header parameters dict to be updated. + :param queries: Query parameters tuple list to be updated. + :resource_path: A string representation of the HTTP request resource path. + :method: A string representation of the HTTP request method. + :body: A object representing the body of the HTTP request. + The object type is the return value of sanitize_for_serialization(). + :param auth_setting: auth settings for the endpoint + """ + if auth_setting["in"] == "cookie": + headers["Cookie"] = auth_setting["value"] + elif auth_setting["in"] == "header": + if auth_setting["type"] != "http-signature": + headers[auth_setting["key"]] = auth_setting["value"] + elif auth_setting["in"] == "query": + queries.append((auth_setting["key"], auth_setting["value"])) + else: + raise ApiValueError( + "Authentication token must be in `query` or `header`" + ) + + def __deserialize_file(self, response): + """Deserializes body to file + + Saves response body into a file in a temporary folder, + using the filename from the `Content-Disposition` header if provided. + + handle file downloading + save response body into a tmp file and return the instance + + :param response: RESTResponse. + :return: file path. + """ + fd, path = tempfile.mkstemp(dir=self.configuration.temp_folder_path) + os.close(fd) + pathlib.Path(path).unlink() + + content_disposition = response.getheader("Content-Disposition") + if content_disposition: + m = re.search( + r'filename=[\'"]?([^\'"\s]+)[\'"]?', + content_disposition + ) + assert m is not None, "Unexpected 'content-disposition' header value" + filename = m.group(1) + path = os.path.join(os.path.dirname(path), filename) + + pathlib.Path(path).write_bytes(response.data) + + return path + + def __deserialize_primitive(self, data, klass): + """Deserializes string to primitive type. + + :param data: str. + :param klass: class literal. + + :return: int, long, float, str, bool. + """ + try: + return klass(data) + except UnicodeEncodeError: + return str(data) + except TypeError: + return data + + def __deserialize_object(self, value): + """Return an original value. + + :return: object. + """ + return value + + def __deserialize_date(self, string): + """Deserializes string to date. + + :param string: str. + :return: date. + """ + try: + return parse(string).date() + except ImportError: + return string + except ValueError: + raise rest.ApiException( + status=0, + reason=f"Failed to parse `{string}` as date object" + ) + + def __deserialize_datetime(self, string): + """Deserializes string to datetime. + + The string should be in iso8601 datetime format. + + :param string: str. + :return: datetime. + """ + try: + return parse(string) + except ImportError: + return string + except ValueError: + raise rest.ApiException( + status=0, + reason=( + f"Failed to parse `{string}` as datetime object" + + ) + ) + + def __deserialize_enum(self, data, klass): + """Deserializes primitive type to enum. + + :param data: primitive type. + :param klass: class literal. + :return: enum value. + """ + try: + return klass(data) + except ValueError: + raise rest.ApiException( + status=0, + reason=( + f"Failed to parse `{data}` as `{klass}`" + + ) + ) + + def __deserialize_model(self, data, klass): + """Deserializes list or dict to model. + + :param data: dict, list. + :param klass: class literal. + :return: model object. + """ + return klass.from_dict(data) diff --git a/src/aignostics_sdk/_codegen/api_response.py b/src/aignostics_sdk/_codegen/api_response.py new file mode 100644 index 000000000..fbdf12c93 --- /dev/null +++ b/src/aignostics_sdk/_codegen/api_response.py @@ -0,0 +1,25 @@ +"""API response object.""" + +from __future__ import annotations + +from collections.abc import Mapping +from typing import Generic, TypeVar + +from pydantic import BaseModel, Field, StrictBytes, StrictInt + +T = TypeVar("T") + + +class ApiResponse(BaseModel, Generic[T]): + """ + API response object + """ + + status_code: StrictInt = Field(description="HTTP status code") + headers: Mapping[str, str] | None = Field(None, description="HTTP headers") + data: T = Field(description="Deserialized data given the data type") + raw_data: StrictBytes = Field(description="Raw data (HTTP response body)") + + model_config = { + "arbitrary_types_allowed": True + } diff --git a/src/aignostics_sdk/_codegen/configuration.py b/src/aignostics_sdk/_codegen/configuration.py new file mode 100644 index 000000000..7879ae332 --- /dev/null +++ b/src/aignostics_sdk/_codegen/configuration.py @@ -0,0 +1,564 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +import copy +import http.client as httplib +import logging +import multiprocessing +import sys +from logging import FileHandler +from typing import Any, ClassVar, Literal, NotRequired, Self, TypedDict + +import urllib3 + +JSON_SCHEMA_VALIDATION_KEYWORDS = { + "multipleOf", "maximum", "exclusiveMaximum", + "minimum", "exclusiveMinimum", "maxLength", + "minLength", "pattern", "maxItems", "minItems" +} + +ServerVariablesT = dict[str, str] + +GenericAuthSetting = TypedDict( + "GenericAuthSetting", + { + "type": str, + "in": str, + "key": str, + "value": str, + }, +) + + +OAuth2AuthSetting = TypedDict( + "OAuth2AuthSetting", + { + "type": Literal["oauth2"], + "in": Literal["header"], + "key": Literal["Authorization"], + "value": str, + }, +) + + +APIKeyAuthSetting = TypedDict( + "APIKeyAuthSetting", + { + "type": Literal["api_key"], + "in": str, + "key": str, + "value": str | None, + }, +) + + +BasicAuthSetting = TypedDict( + "BasicAuthSetting", + { + "type": Literal["basic"], + "in": Literal["header"], + "key": Literal["Authorization"], + "value": str | None, + }, +) + + +BearerFormatAuthSetting = TypedDict( + "BearerFormatAuthSetting", + { + "type": Literal["bearer"], + "in": Literal["header"], + "format": Literal["JWT"], + "key": Literal["Authorization"], + "value": str, + }, +) + + +BearerAuthSetting = TypedDict( + "BearerAuthSetting", + { + "type": Literal["bearer"], + "in": Literal["header"], + "key": Literal["Authorization"], + "value": str, + }, +) + + +HTTPSignatureAuthSetting = TypedDict( + "HTTPSignatureAuthSetting", + { + "type": Literal["http-signature"], + "in": Literal["header"], + "key": Literal["Authorization"], + "value": None, + }, +) + + +class AuthSettings(TypedDict, total=False): + OAuth2AuthorizationCodeBearer: OAuth2AuthSetting + + +class HostSettingVariable(TypedDict): + description: str + default_value: str + enum_values: list[str] + + +class HostSetting(TypedDict): + url: str + description: str + variables: NotRequired[dict[str, HostSettingVariable]] + + +class Configuration: + """This class contains various settings of the API client. + + :param host: Base url. + :param ignore_operation_servers + Boolean to ignore operation servers for the API client. + Config will use `host` as the base url regardless of the operation servers. + :param api_key: Dict to store API key(s). + Each entry in the dict specifies an API key. + The dict key is the name of the security scheme in the OAS specification. + The dict value is the API key secret. + :param api_key_prefix: Dict to store API prefix (e.g. Bearer). + The dict key is the name of the security scheme in the OAS specification. + The dict value is an API key prefix when generating the auth data. + :param username: Username for HTTP basic authentication. + :param password: Password for HTTP basic authentication. + :param access_token: Access token. + :param server_index: Index to servers configuration. + :param server_variables: Mapping with string values to replace variables in + templated server configuration. The validation of enums is performed for + variables with defined enum values before. + :param server_operation_index: Mapping from operation ID to an index to server + configuration. + :param server_operation_variables: Mapping from operation ID to a mapping with + string values to replace variables in templated server configuration. + The validation of enums is performed for variables with defined enum + values before. + :param ssl_ca_cert: str - the path to a file of concatenated CA certificates + in PEM format. + :param retries: Number of retries for API requests. + + :Example: + """ + + _default: ClassVar[Self | None] = None + + def __init__( + self, + host: str | None = None, + api_key: dict[str, str] | None = None, + api_key_prefix: dict[str, str] | None = None, + username: str | None = None, + password: str | None = None, + access_token: str | None = None, + server_index: int | None = None, + server_variables: ServerVariablesT | None = None, + server_operation_index: dict[int, int] | None = None, + server_operation_variables: dict[int, ServerVariablesT] | None = None, + ignore_operation_servers: bool = False, + ssl_ca_cert: str | None = None, + retries: int | None = None, + *, + debug: bool | None = None, + ) -> None: + """Constructor + """ + self._base_path = "/api" if host is None else host + """Default Base url + """ + self.server_index = 0 if server_index is None and host is None else server_index + self.server_operation_index = server_operation_index or {} + """Default server index + """ + self.server_variables = server_variables or {} + self.server_operation_variables = server_operation_variables or {} + """Default server variables + """ + self.ignore_operation_servers = ignore_operation_servers + """Ignore operation servers + """ + self.temp_folder_path = None + """Temp file folder for downloading files + """ + # Authentication Settings + self.api_key = {} + if api_key: + self.api_key = api_key + """dict to store API key(s) + """ + self.api_key_prefix = {} + if api_key_prefix: + self.api_key_prefix = api_key_prefix + """dict to store API prefix (e.g. Bearer) + """ + self.refresh_api_key_hook = None + """function hook to refresh API key if expired + """ + self.username = username + """Username for HTTP basic authentication + """ + self.password = password + """Password for HTTP basic authentication + """ + self.access_token = access_token + """Access token + """ + self.logger = {} + """Logging Settings + """ + self.logger["package_logger"] = logging.getLogger("aignostics_sdk._codegen") + self.logger["urllib3_logger"] = logging.getLogger("urllib3") + self.logger_format = "%(asctime)s %(levelname)s %(message)s" + """Log format + """ + self.logger_stream_handler = None + """Log stream handler + """ + self.logger_file_handler: FileHandler | None = None + """Log file handler + """ + self.logger_file = None + """Debug file location + """ + if debug is not None: + self.debug = debug + else: + self.__debug = False + """Debug switch + """ + + self.verify_ssl = True + """SSL/TLS verification + Set this to false to skip verifying SSL certificate when calling API + from https server. + """ + self.ssl_ca_cert = ssl_ca_cert + """Set this to customize the certificate file to verify the peer. + """ + self.cert_file = None + """client certificate file + """ + self.key_file = None + """client key file + """ + self.assert_hostname = None + """Set this to True/False to enable/disable SSL hostname verification. + """ + self.tls_server_name = None + """SSL/TLS Server Name Indication (SNI) + Set this to the SNI value expected by the server. + """ + + self.connection_pool_maxsize = multiprocessing.cpu_count() * 5 + """urllib3 connection pool's maximum number of connections saved + per pool. urllib3 uses 1 connection as default value, but this is + not the best value when you are making a lot of possibly parallel + requests to the same host, which is often the case here. + cpu_count * 5 is used as default value to increase performance. + """ + + self.proxy: str | None = None + """Proxy URL + """ + self.proxy_headers = None + """Proxy headers + """ + self.safe_chars_for_path_param = "" + """Safe chars for path_param + """ + self.retries = retries + """Adding retries to override urllib3 default value 3 + """ + # Enable client side validation + self.client_side_validation = True + + self.socket_options = None + """Options to pass down to the underlying urllib3 socket + """ + + self.datetime_format = "%Y-%m-%dT%H:%M:%S.%f%z" + """datetime format + """ + + self.date_format = "%Y-%m-%d" + """date format + """ + + def __deepcopy__(self, memo: dict[int, Any]) -> Self: + cls = self.__class__ + result = cls.__new__(cls) + memo[id(self)] = result + for k, v in self.__dict__.items(): + if k not in ("logger", "logger_file_handler"): + setattr(result, k, copy.deepcopy(v, memo)) + # shallow copy of loggers + result.logger = copy.copy(self.logger) + # use setters to configure loggers + result.logger_file = self.logger_file + result.debug = self.debug + return result + + def __setattr__(self, name: str, value: Any) -> None: + object.__setattr__(self, name, value) + + @classmethod + def set_default(cls, default: Self | None) -> None: + """Set default instance of configuration. + + It stores default configuration, which can be + returned by get_default_copy method. + + :param default: object of Configuration + """ + cls._default = default + + @classmethod + def get_default_copy(cls) -> Self: + """Deprecated. Please use `get_default` instead. + + Deprecated. Please use `get_default` instead. + + :return: The configuration object. + """ + return cls.get_default() + + @classmethod + def get_default(cls) -> Self: + """Return the default configuration. + + This method returns newly created, based on default constructor, + object of Configuration class or returns a copy of default + configuration. + + :return: The configuration object. + """ + if cls._default is None: + cls._default = cls() + return cls._default + + @property + def logger_file(self) -> str | None: + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str + """ + return self.__logger_file + + @logger_file.setter + def logger_file(self, value: str | None) -> None: + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str + """ + self.__logger_file = value + if self.__logger_file: + # If set logging file, + # then add file handler and remove stream handler. + self.logger_file_handler = logging.FileHandler(self.__logger_file) + self.logger_file_handler.setFormatter(self.logger_formatter) + for _, logger in self.logger.items(): + logger.addHandler(self.logger_file_handler) + + @property + def debug(self) -> bool: + """Debug status + + :param value: The debug status, True or False. + :type: bool + """ + return self.__debug + + @debug.setter + def debug(self, value: bool) -> None: + """Debug status + + :param value: The debug status, True or False. + :type: bool + """ + self.__debug = value + if self.__debug: + # if debug status is True, turn on debug logging + for _, logger in self.logger.items(): + logger.setLevel(logging.DEBUG) + # turn on httplib debug + httplib.HTTPConnection.debuglevel = 1 + else: + # if debug status is False, turn off debug logging, + # setting log level to default `logging.WARNING` + for _, logger in self.logger.items(): + logger.setLevel(logging.WARNING) + # turn off httplib debug + httplib.HTTPConnection.debuglevel = 0 + + @property + def logger_format(self) -> str: + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str + """ + return self.__logger_format + + @logger_format.setter + def logger_format(self, value: str) -> None: + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str + """ + self.__logger_format = value + self.logger_formatter = logging.Formatter(self.__logger_format) + + def get_api_key_with_prefix(self, identifier: str, alias: str | None = None) -> str | None: + """Gets API key (with prefix if set). + + :param identifier: The identifier of apiKey. + :param alias: The alternative identifier of apiKey. + :return: The token for api key authentication. + """ + if self.refresh_api_key_hook is not None: + self.refresh_api_key_hook(self) + key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None) + if key: + prefix = self.api_key_prefix.get(identifier) + if prefix: + return "%s %s" % (prefix, key) + return key + + return None + + def get_basic_auth_token(self) -> str | None: + """Gets HTTP basic authentication header (string). + + :return: The token for basic HTTP authentication. + """ + username = "" + if self.username is not None: + username = self.username + password = "" + if self.password is not None: + password = self.password + return urllib3.util.make_headers( + basic_auth=username + ":" + password + ).get("authorization") + + def auth_settings(self) -> AuthSettings: + """Gets Auth Settings dict for api client. + + :return: The Auth Settings information dict. + """ + auth: AuthSettings = {} + if self.access_token is not None: + auth["OAuth2AuthorizationCodeBearer"] = { + "type": "oauth2", + "in": "header", + "key": "Authorization", + "value": "Bearer " + self.access_token + } + return auth + + def to_debug_report(self) -> str: + """Gets the essential information for debugging. + + :return: The report for debugging. + """ + return "Python SDK Debug Report:\n"\ + f"OS: {sys.platform}\n"\ + f"Python Version: {sys.version}\n"\ + "Version of the API: 1.5.0\n"\ + "SDK Package Version: 1.0.0" + + def get_host_settings(self) -> list[HostSetting]: + """Gets an array of host settings + + :return: An array of host settings + """ + return [ + { + "url": "/api", + "description": "No description provided", + } + ] + + def get_host_from_settings( + self, + index: int | None, + variables: ServerVariablesT | None = None, + servers: list[HostSetting] | None = None, + ) -> str: + """Gets host URL based on the index and variables + :param index: array index of the host settings + :param variables: hash of variable and the corresponding value + :param servers: an array of host settings or None + :return: URL based on host settings + """ + if index is None: + return self._base_path + + variables = {} if variables is None else variables + servers = self.get_host_settings() if servers is None else servers + + try: + server = servers[index] + except IndexError: + raise ValueError( + f"Invalid index {index} when selecting the host settings. " + f"Must be less than {len(servers)}") + + url = server["url"] + + # go through variables and replace placeholders + for variable_name, variable in server.get("variables", {}).items(): + used_value = variables.get( + variable_name, variable["default_value"]) + + if "enum_values" in variable \ + and used_value not in variable["enum_values"]: + raise ValueError( + "The variable `{0}` in the host URL has invalid value " + "{1}. Must be {2}.".format( + variable_name, variables[variable_name], + variable["enum_values"])) + + url = url.replace("{" + variable_name + "}", used_value) + + return url + + @property + def host(self) -> str: + """Return generated host.""" + return self.get_host_from_settings(self.server_index, variables=self.server_variables) + + @host.setter + def host(self, value: str) -> None: + """Fix base path.""" + self._base_path = value + self.server_index = None diff --git a/src/aignostics_sdk/_codegen/exceptions.py b/src/aignostics_sdk/_codegen/exceptions.py new file mode 100644 index 000000000..615a75162 --- /dev/null +++ b/src/aignostics_sdk/_codegen/exceptions.py @@ -0,0 +1,196 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + +from typing import Any, Self + + +class OpenApiException(Exception): + """The base exception class for all OpenAPIExceptions""" + + +class ApiTypeError(OpenApiException, TypeError): + def __init__(self, msg, path_to_item=None, valid_classes=None, + key_type=None) -> None: + """Raises an exception for TypeErrors + + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (list): a list of keys an indices to get to the + current_item + None if unset + valid_classes (tuple): the primitive classes that current item + should be an instance of + None if unset + key_type (bool): False if our value is a value in a dict + True if it is a key in a dict + False if our item is an item in a list + None if unset + """ + self.path_to_item = path_to_item + self.valid_classes = valid_classes + self.key_type = key_type + full_msg = msg + if path_to_item: + full_msg = f"{msg} at {render_path(path_to_item)}" + super().__init__(full_msg) + + +class ApiValueError(OpenApiException, ValueError): + def __init__(self, msg, path_to_item=None) -> None: + """ + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (list) the path to the exception in the + received_data dict. None if unset + """ + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = f"{msg} at {render_path(path_to_item)}" + super().__init__(full_msg) + + +class ApiAttributeError(OpenApiException, AttributeError): + def __init__(self, msg, path_to_item=None) -> None: + """ + Raised when an attribute reference or assignment fails. + + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (None/list) the path to the exception in the + received_data dict + """ + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = f"{msg} at {render_path(path_to_item)}" + super().__init__(full_msg) + + +class ApiKeyError(OpenApiException, KeyError): + def __init__(self, msg, path_to_item=None) -> None: + """ + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (None/list) the path to the exception in the + received_data dict + """ + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = f"{msg} at {render_path(path_to_item)}" + super().__init__(full_msg) + + +class ApiException(OpenApiException): + + def __init__( + self, + status=None, + reason=None, + http_resp=None, + *, + body: str | None = None, + data: Any | None = None, + ) -> None: + self.status = status + self.reason = reason + self.body = body + self.data = data + self.headers = None + + if http_resp: + if self.status is None: + self.status = http_resp.status + if self.reason is None: + self.reason = http_resp.reason + if self.body is None: + try: + self.body = http_resp.data.decode("utf-8") + except Exception: + pass + self.headers = http_resp.getheaders() + + @classmethod + def from_response( + cls, + *, + http_resp, + body: str | None, + data: Any | None, + ) -> Self: + if http_resp.status == 400: + raise BadRequestException(http_resp=http_resp, body=body, data=data) + + if http_resp.status == 401: + raise UnauthorizedException(http_resp=http_resp, body=body, data=data) + + if http_resp.status == 403: + raise ForbiddenException(http_resp=http_resp, body=body, data=data) + + if http_resp.status == 404: + raise NotFoundException(http_resp=http_resp, body=body, data=data) + + if 500 <= http_resp.status <= 599: + raise ServiceException(http_resp=http_resp, body=body, data=data) + raise ApiException(http_resp=http_resp, body=body, data=data) + + def __str__(self): + """Custom error messages for exception""" + error_message = f"({self.status})\n"\ + f"Reason: {self.reason}\n" + if self.headers: + error_message += f"HTTP response headers: {self.headers}\n" + + if self.data or self.body: + error_message += f"HTTP response body: {self.data or self.body}\n" + + return error_message + + +class BadRequestException(ApiException): + pass + + +class NotFoundException(ApiException): + pass + + +class UnauthorizedException(ApiException): + pass + + +class ForbiddenException(ApiException): + pass + + +class ServiceException(ApiException): + pass + + +def render_path(path_to_item): + """Returns a string representation of a path""" + result = "" + for pth in path_to_item: + if isinstance(pth, int): + result += f"[{pth}]" + else: + result += f"['{pth}']" + return result diff --git a/src/aignostics_sdk/_codegen/models/__init__.py b/src/aignostics_sdk/_codegen/models/__init__.py new file mode 100644 index 000000000..ac14906f3 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/__init__.py @@ -0,0 +1,40 @@ +from .application_read_response import * +from .application_read_short_response import * +from .application_version import * +from .artifact_output import * +from .artifact_state import * +from .artifact_termination_reason import * +from .auth0_organization import * +from .auth0_user import * +from .custom_metadata_update_request import * +from .custom_metadata_update_response import * +from .http_validation_error import * +from .input_artifact import * +from .input_artifact_creation_request import * +from .input_artifact_result_read_response import * +from .item_creation_request import * +from .item_output import * +from .item_result_read_response import * +from .item_state import * +from .item_termination_reason import * +from .me_read_response import * +from .organization_read_response import * +from .output_artifact import * +from .output_artifact_result_read_response import * +from .output_artifact_scope import * +from .output_artifact_visibility import * +from .run_creation_request import * +from .run_creation_response import * +from .run_item_statistics import * +from .run_output import * +from .run_read_response import * +from .run_state import * +from .run_termination_reason import * +from .scheduling_request import * +from .scheduling_response import * +from .user_read_response import * +from .validation_error import * +from .validation_error_loc_inner import * +from .version_document_response import * +from .version_document_visibility import * +from .version_read_response import * diff --git a/src/aignostics_sdk/_codegen/models/application_read_response.py b/src/aignostics_sdk/_codegen/models/application_read_response.py new file mode 100644 index 000000000..a6e0df58c --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/application_read_response.py @@ -0,0 +1,100 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, StrictStr + +from aignostics_sdk._codegen.models.application_version import ApplicationVersion + + +class ApplicationReadResponse(BaseModel): + """ + Response schema for `List available applications` and `Read Application by Id` endpoints + """ + application_id: StrictStr = Field(description="Application ID") + name: StrictStr = Field(description="Application display name") + regulatory_classes: list[StrictStr] = Field(description="Regulatory classes, to which the applications comply with. Possible values include: RUO, IVDR, FDA.") + description: StrictStr = Field(description="Describing what the application can do ") + versions: list[ApplicationVersion] = Field(description="All version numbers available to the user") + __properties: ClassVar[list[str]] = ["application_id", "name", "regulatory_classes", "description", "versions"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of ApplicationReadResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in versions (list) + _items = [] + if self.versions: + for _item_versions in self.versions: + if _item_versions: + _items.append(_item_versions.to_dict()) + _dict["versions"] = _items + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of ApplicationReadResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "application_id": obj.get("application_id"), + "name": obj.get("name"), + "regulatory_classes": obj.get("regulatory_classes"), + "description": obj.get("description"), + "versions": [ApplicationVersion.from_dict(_item) for _item in obj["versions"]] if obj.get("versions") is not None else None + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/application_read_short_response.py b/src/aignostics_sdk/_codegen/models/application_read_short_response.py new file mode 100644 index 000000000..b3026e260 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/application_read_short_response.py @@ -0,0 +1,101 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, StrictStr + +from aignostics_sdk._codegen.models.application_version import ApplicationVersion + + +class ApplicationReadShortResponse(BaseModel): + """ + Response schema for `List available applications` and `Read Application by Id` endpoints + """ + application_id: StrictStr = Field(description="Application ID") + name: StrictStr = Field(description="Application display name") + regulatory_classes: list[StrictStr] = Field(description="Regulatory classes, to which the applications comply with. Possible values include: RUO, IVDR, FDA.") + description: StrictStr = Field(description="Describing what the application can do ") + latest_version: ApplicationVersion | None = None + __properties: ClassVar[list[str]] = ["application_id", "name", "regulatory_classes", "description", "latest_version"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of ApplicationReadShortResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of latest_version + if self.latest_version: + _dict["latest_version"] = self.latest_version.to_dict() + # set to None if latest_version (nullable) is None + # and model_fields_set contains the field + if self.latest_version is None and "latest_version" in self.model_fields_set: + _dict["latest_version"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of ApplicationReadShortResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "application_id": obj.get("application_id"), + "name": obj.get("name"), + "regulatory_classes": obj.get("regulatory_classes"), + "description": obj.get("description"), + "latest_version": ApplicationVersion.from_dict(obj["latest_version"]) if obj.get("latest_version") is not None else None + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/application_version.py b/src/aignostics_sdk/_codegen/models/application_version.py new file mode 100644 index 000000000..3bb77fb3b --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/application_version.py @@ -0,0 +1,93 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re +from datetime import datetime +from typing import Annotated, Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, field_validator + + +class ApplicationVersion(BaseModel): + """ + ApplicationVersion + """ + number: Annotated[str, Field(strict=True)] = Field(description="The number of the latest version") + released_at: datetime = Field(description="The timestamp for when the application version was made available in the Platform") + __properties: ClassVar[list[str]] = ["number", "released_at"] + + @field_validator("number") + def number_validate_regular_expression(cls, value): + """Validates the regular expression""" + if not re.match(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$", value): + raise ValueError(r"must validate the regular expression /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/") + return value + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of ApplicationVersion from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of ApplicationVersion from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "number": obj.get("number"), + "released_at": obj.get("released_at") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/artifact_output.py b/src/aignostics_sdk/_codegen/models/artifact_output.py new file mode 100644 index 000000000..5d555eca4 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/artifact_output.py @@ -0,0 +1,37 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +from enum import Enum +from typing import Self + + +class ArtifactOutput(str, Enum): + """ + ArtifactOutput + """ + + """ + allowed enum values + """ + NONE = "NONE" + AVAILABLE = "AVAILABLE" + DELETED_BY_USER = "DELETED_BY_USER" + DELETED_BY_SYSTEM = "DELETED_BY_SYSTEM" + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ArtifactOutput from a JSON string""" + return cls(json.loads(json_str)) diff --git a/src/aignostics_sdk/_codegen/models/artifact_state.py b/src/aignostics_sdk/_codegen/models/artifact_state.py new file mode 100644 index 000000000..d9b06b319 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/artifact_state.py @@ -0,0 +1,36 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +from enum import Enum +from typing import Self + + +class ArtifactState(str, Enum): + """ + ArtifactState + """ + + """ + allowed enum values + """ + PENDING = "PENDING" + PROCESSING = "PROCESSING" + TERMINATED = "TERMINATED" + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ArtifactState from a JSON string""" + return cls(json.loads(json_str)) diff --git a/src/aignostics_sdk/_codegen/models/artifact_termination_reason.py b/src/aignostics_sdk/_codegen/models/artifact_termination_reason.py new file mode 100644 index 000000000..ee2c79fac --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/artifact_termination_reason.py @@ -0,0 +1,37 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +from enum import Enum +from typing import Self + + +class ArtifactTerminationReason(str, Enum): + """ + ArtifactTerminationReason + """ + + """ + allowed enum values + """ + SUCCEEDED = "SUCCEEDED" + USER_ERROR = "USER_ERROR" + SYSTEM_ERROR = "SYSTEM_ERROR" + SKIPPED = "SKIPPED" + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ArtifactTerminationReason from a JSON string""" + return cls(json.loads(json_str)) diff --git a/src/aignostics_sdk/_codegen/models/auth0_organization.py b/src/aignostics_sdk/_codegen/models/auth0_organization.py new file mode 100644 index 000000000..380c4b6e5 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/auth0_organization.py @@ -0,0 +1,109 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.0.0.beta7 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, StrictStr + + +class Auth0Organization(BaseModel): + """ + Model for Auth0 Organization object returned from Auth0 API. For details, see: https://auth0.com/docs/api/management/v2#!/Organizations/get_organizations_by_id Aignostics-specific metadata fields are extracted from the `metadata` field. + """ # noqa: E501 + id: StrictStr = Field(description="Unique organization identifier") + name: StrictStr | None = None + display_name: StrictStr | None = None + aignostics_bucket_hmac_access_key_id: StrictStr = Field(description="HMAC access key ID for the Aignostics-provided storage bucket. Used to authenticate requests for uploading files and generating signed URLs") + aignostics_bucket_hmac_secret_access_key: StrictStr = Field(description="HMAC secret access key paired with the access key ID. Keep this credential secure.") + aignostics_bucket_name: StrictStr = Field(description="Name of the bucket provided by Aignostics for storing input artifacts (slide images)") + aignostics_bucket_protocol: StrictStr = Field(description="Protocol to use for bucket access. Defines the URL scheme for connecting to the storage service") + aignostics_logfire_token: StrictStr = Field(description="Authentication token for Logfire observability service. Enables sending application logs and performance metrics to Aignostics for monitoring and support") + aignostics_sentry_dsn: StrictStr = Field(description="Data Source Name (DSN) for Sentry error tracking service. Allows automatic reporting of errors and exceptions to Aignostics support team") + __properties: ClassVar[list[str]] = ["id", "name", "display_name", "aignostics_bucket_hmac_access_key_id", "aignostics_bucket_hmac_secret_access_key", "aignostics_bucket_name", "aignostics_bucket_protocol", "aignostics_logfire_token", "aignostics_sentry_dsn"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of Auth0Organization from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if name (nullable) is None + # and model_fields_set contains the field + if self.name is None and "name" in self.model_fields_set: + _dict["name"] = None + + # set to None if display_name (nullable) is None + # and model_fields_set contains the field + if self.display_name is None and "display_name" in self.model_fields_set: + _dict["display_name"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of Auth0Organization from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "id": obj.get("id"), + "name": obj.get("name"), + "display_name": obj.get("display_name"), + "aignostics_bucket_hmac_access_key_id": obj.get("aignostics_bucket_hmac_access_key_id"), + "aignostics_bucket_hmac_secret_access_key": obj.get("aignostics_bucket_hmac_secret_access_key"), + "aignostics_bucket_name": obj.get("aignostics_bucket_name"), + "aignostics_bucket_protocol": obj.get("aignostics_bucket_protocol"), + "aignostics_logfire_token": obj.get("aignostics_logfire_token"), + "aignostics_sentry_dsn": obj.get("aignostics_sentry_dsn") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/auth0_user.py b/src/aignostics_sdk/_codegen/models/auth0_user.py new file mode 100644 index 000000000..8d83c47d9 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/auth0_user.py @@ -0,0 +1,140 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.0.0.beta7 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from datetime import datetime +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictStr + + +class Auth0User(BaseModel): + """ + Model for Auth0 User object returned from Auth0 API. For details, see: https://auth0.com/docs/api/management/v2/users/get-users-by-id + """ + id: StrictStr = Field(description="Unique user identifier") + email: StrictStr | None = None + email_verified: StrictBool | None = None + name: StrictStr | None = None + given_name: StrictStr | None = None + family_name: StrictStr | None = None + nickname: StrictStr | None = None + picture: StrictStr | None = None + updated_at: datetime | None = None + __properties: ClassVar[list[str]] = ["id", "email", "email_verified", "name", "given_name", "family_name", "nickname", "picture", "updated_at"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of Auth0User from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if email (nullable) is None + # and model_fields_set contains the field + if self.email is None and "email" in self.model_fields_set: + _dict["email"] = None + + # set to None if email_verified (nullable) is None + # and model_fields_set contains the field + if self.email_verified is None and "email_verified" in self.model_fields_set: + _dict["email_verified"] = None + + # set to None if name (nullable) is None + # and model_fields_set contains the field + if self.name is None and "name" in self.model_fields_set: + _dict["name"] = None + + # set to None if given_name (nullable) is None + # and model_fields_set contains the field + if self.given_name is None and "given_name" in self.model_fields_set: + _dict["given_name"] = None + + # set to None if family_name (nullable) is None + # and model_fields_set contains the field + if self.family_name is None and "family_name" in self.model_fields_set: + _dict["family_name"] = None + + # set to None if nickname (nullable) is None + # and model_fields_set contains the field + if self.nickname is None and "nickname" in self.model_fields_set: + _dict["nickname"] = None + + # set to None if picture (nullable) is None + # and model_fields_set contains the field + if self.picture is None and "picture" in self.model_fields_set: + _dict["picture"] = None + + # set to None if updated_at (nullable) is None + # and model_fields_set contains the field + if self.updated_at is None and "updated_at" in self.model_fields_set: + _dict["updated_at"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of Auth0User from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "id": obj.get("id"), + "email": obj.get("email"), + "email_verified": obj.get("email_verified"), + "name": obj.get("name"), + "given_name": obj.get("given_name"), + "family_name": obj.get("family_name"), + "nickname": obj.get("nickname"), + "picture": obj.get("picture"), + "updated_at": obj.get("updated_at") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/custom_metadata_update_request.py b/src/aignostics_sdk/_codegen/models/custom_metadata_update_request.py new file mode 100644 index 000000000..14dd36f2e --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/custom_metadata_update_request.py @@ -0,0 +1,95 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, StrictStr + + +class CustomMetadataUpdateRequest(BaseModel): + """ + CustomMetadataUpdateRequest + """ + custom_metadata: dict[str, Any] | None = None + custom_metadata_checksum: StrictStr | None = None + __properties: ClassVar[list[str]] = ["custom_metadata", "custom_metadata_checksum"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of CustomMetadataUpdateRequest from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if custom_metadata (nullable) is None + # and model_fields_set contains the field + if self.custom_metadata is None and "custom_metadata" in self.model_fields_set: + _dict["custom_metadata"] = None + + # set to None if custom_metadata_checksum (nullable) is None + # and model_fields_set contains the field + if self.custom_metadata_checksum is None and "custom_metadata_checksum" in self.model_fields_set: + _dict["custom_metadata_checksum"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of CustomMetadataUpdateRequest from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "custom_metadata": obj.get("custom_metadata"), + "custom_metadata_checksum": obj.get("custom_metadata_checksum") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/custom_metadata_update_response.py b/src/aignostics_sdk/_codegen/models/custom_metadata_update_response.py new file mode 100644 index 000000000..99bb12d17 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/custom_metadata_update_response.py @@ -0,0 +1,88 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, StrictStr + + +class CustomMetadataUpdateResponse(BaseModel): + """ + CustomMetadataUpdateResponse + """ + custom_metadata_checksum: StrictStr | None + __properties: ClassVar[list[str]] = ["custom_metadata_checksum"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of CustomMetadataUpdateResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if custom_metadata_checksum (nullable) is None + # and model_fields_set contains the field + if self.custom_metadata_checksum is None and "custom_metadata_checksum" in self.model_fields_set: + _dict["custom_metadata_checksum"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of CustomMetadataUpdateResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "custom_metadata_checksum": obj.get("custom_metadata_checksum") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/http_validation_error.py b/src/aignostics_sdk/_codegen/models/http_validation_error.py new file mode 100644 index 000000000..a9442e08b --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/http_validation_error.py @@ -0,0 +1,92 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict + +from aignostics_sdk._codegen.models.validation_error import ValidationError + + +class HTTPValidationError(BaseModel): + """ + HTTPValidationError + """ + detail: list[ValidationError] | None = None + __properties: ClassVar[list[str]] = ["detail"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of HTTPValidationError from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in detail (list) + _items = [] + if self.detail: + for _item_detail in self.detail: + if _item_detail: + _items.append(_item_detail.to_dict()) + _dict["detail"] = _items + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of HTTPValidationError from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "detail": [ValidationError.from_dict(_item) for _item in obj["detail"]] if obj.get("detail") is not None else None + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/input_artifact.py b/src/aignostics_sdk/_codegen/models/input_artifact.py new file mode 100644 index 000000000..d2d1cf21e --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/input_artifact.py @@ -0,0 +1,94 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re +from typing import Annotated, Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, StrictStr, field_validator + + +class InputArtifact(BaseModel): + """ + InputArtifact + """ + name: StrictStr + mime_type: Annotated[str, Field(strict=True)] + metadata_schema: dict[str, Any] + __properties: ClassVar[list[str]] = ["name", "mime_type", "metadata_schema"] + + @field_validator("mime_type") + def mime_type_validate_regular_expression(cls, value): + """Validates the regular expression""" + if not re.match(r"^\w+\/\w+[-+.|\w+]+\w+$", value): + raise ValueError(r"must validate the regular expression /^\w+\/\w+[-+.|\w+]+\w+$/") + return value + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of InputArtifact from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of InputArtifact from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "name": obj.get("name"), + "mime_type": obj.get("mime_type"), + "metadata_schema": obj.get("metadata_schema") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/input_artifact_creation_request.py b/src/aignostics_sdk/_codegen/models/input_artifact_creation_request.py new file mode 100644 index 000000000..01a3bb693 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/input_artifact_creation_request.py @@ -0,0 +1,87 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Annotated, Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, StrictStr + + +class InputArtifactCreationRequest(BaseModel): + """ + Input artifact containing the slide image and associated metadata. + """ + name: StrictStr = Field(description='Type of artifact. For Atlas H&E-TME, use "input_slide"') + download_url: Annotated[str, Field(min_length=1, strict=True, max_length=2083)] = Field(description="[Signed URL](https://cloud.google.com/cdn/docs/using-signed-urls) to the input artifact file. The URL should be valid for at least 6 days from the payload submission time.") + metadata: dict[str, Any] = Field(description="The metadata of the artifact, required by the application version. The JSON schema of the metadata can be requested by `/v1/versions/{application_version_id}`. The schema is located in `input_artifacts.[].metadata_schema`") + __properties: ClassVar[list[str]] = ["name", "download_url", "metadata"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of InputArtifactCreationRequest from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of InputArtifactCreationRequest from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "name": obj.get("name"), + "download_url": obj.get("download_url"), + "metadata": obj.get("metadata") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/input_artifact_result_read_response.py b/src/aignostics_sdk/_codegen/models/input_artifact_result_read_response.py new file mode 100644 index 000000000..3eb6d9ab5 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/input_artifact_result_read_response.py @@ -0,0 +1,99 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Annotated, Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, StrictStr + + +class InputArtifactResultReadResponse(BaseModel): + """ + InputArtifactResultReadResponse + """ + input_artifact_id: StrictStr = Field(description="The Id of the artifact. Used internally") + name: StrictStr = Field(description="Name of the input from the schema from the `/v1/versions/{version_id}` endpoint.") + metadata: dict[str, Any] | None = None + download_url: Annotated[str, Field(min_length=1, strict=True, max_length=2083)] | None = None + __properties: ClassVar[list[str]] = ["input_artifact_id", "name", "metadata", "download_url"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of InputArtifactResultReadResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if metadata (nullable) is None + # and model_fields_set contains the field + if self.metadata is None and "metadata" in self.model_fields_set: + _dict["metadata"] = None + + # set to None if download_url (nullable) is None + # and model_fields_set contains the field + if self.download_url is None and "download_url" in self.model_fields_set: + _dict["download_url"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of InputArtifactResultReadResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "input_artifact_id": obj.get("input_artifact_id"), + "name": obj.get("name"), + "metadata": obj.get("metadata"), + "download_url": obj.get("download_url") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/item_creation_request.py b/src/aignostics_sdk/_codegen/models/item_creation_request.py new file mode 100644 index 000000000..66fd46264 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/item_creation_request.py @@ -0,0 +1,101 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Annotated, Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field + +from aignostics_sdk._codegen.models.input_artifact_creation_request import InputArtifactCreationRequest + + +class ItemCreationRequest(BaseModel): + """ + Individual item (slide) to be processed in a run. + """ + external_id: Annotated[str, Field(strict=True, max_length=255)] = Field(description="Unique identifier for this item within the run. Used for referencing items. Must be unique across all items in the same run") + custom_metadata: dict[str, Any] | None = None + input_artifacts: list[InputArtifactCreationRequest] = Field(description="List of input artifacts for this item. For Atlas H&E-TME, typically contains one artifact (the slide image)") + __properties: ClassVar[list[str]] = ["external_id", "custom_metadata", "input_artifacts"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of ItemCreationRequest from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in input_artifacts (list) + _items = [] + if self.input_artifacts: + for _item_input_artifacts in self.input_artifacts: + if _item_input_artifacts: + _items.append(_item_input_artifacts.to_dict()) + _dict["input_artifacts"] = _items + # set to None if custom_metadata (nullable) is None + # and model_fields_set contains the field + if self.custom_metadata is None and "custom_metadata" in self.model_fields_set: + _dict["custom_metadata"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of ItemCreationRequest from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "external_id": obj.get("external_id"), + "custom_metadata": obj.get("custom_metadata"), + "input_artifacts": [InputArtifactCreationRequest.from_dict(_item) for _item in obj["input_artifacts"]] if obj.get("input_artifacts") is not None else None + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/item_output.py b/src/aignostics_sdk/_codegen/models/item_output.py new file mode 100644 index 000000000..7309b5220 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/item_output.py @@ -0,0 +1,35 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +from enum import Enum +from typing import Self + + +class ItemOutput(str, Enum): + """ + ItemOutput + """ + + """ + allowed enum values + """ + NONE = "NONE" + FULL = "FULL" + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ItemOutput from a JSON string""" + return cls(json.loads(json_str)) diff --git a/src/aignostics_sdk/_codegen/models/item_result_read_response.py b/src/aignostics_sdk/_codegen/models/item_result_read_response.py new file mode 100644 index 000000000..4bbcfaa42 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/item_result_read_response.py @@ -0,0 +1,170 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from datetime import datetime +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr + +from aignostics_sdk._codegen.models.input_artifact_result_read_response import InputArtifactResultReadResponse +from aignostics_sdk._codegen.models.item_output import ItemOutput +from aignostics_sdk._codegen.models.item_state import ItemState +from aignostics_sdk._codegen.models.item_termination_reason import ItemTerminationReason +from aignostics_sdk._codegen.models.output_artifact_result_read_response import OutputArtifactResultReadResponse + + +class ItemResultReadResponse(BaseModel): + """ + Response schema for items in `List Run Items` endpoint + """ + item_id: StrictStr = Field(description="Item UUID generated by the Platform") + external_id: StrictStr = Field(description="The external_id of the item from the user payload") + custom_metadata: dict[str, Any] | None + custom_metadata_checksum: StrictStr | None = None + queue_position_org: StrictInt | None = None + queue_position_platform: StrictInt | None = None + state: ItemState = Field(description=" The item moves from `PENDING` to `PROCESSING` to `TERMINATED` state. When terminated, consult the `termination_reason` property to see whether it was successful. ") + output: ItemOutput = Field(description="The output status of the item (NONE, FULL)") + termination_reason: ItemTerminationReason | None = None + error_code: StrictStr | None = None + error_message: StrictStr | None = None + terminated_at: datetime | None = None + input_artifacts: list[InputArtifactResultReadResponse] = Field(description=" The input artifact(s) provided by the user. For most applications, this will be one artifact that defines the whole slide image to be processed. ") + output_artifacts: list[OutputArtifactResultReadResponse] = Field(description=" The list of the results generated by the application algorithm. The number of files and their types depend on the particular application version, call `/v1/versions/{version_id}` to get the details. ") + __properties: ClassVar[list[str]] = ["item_id", "external_id", "custom_metadata", "custom_metadata_checksum", "queue_position_org", "queue_position_platform", "state", "output", "termination_reason", "error_code", "error_message", "terminated_at", "input_artifacts", "output_artifacts"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of ItemResultReadResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in input_artifacts (list) + _items = [] + if self.input_artifacts: + for _item_input_artifacts in self.input_artifacts: + if _item_input_artifacts: + _items.append(_item_input_artifacts.to_dict()) + _dict["input_artifacts"] = _items + # override the default output from pydantic by calling `to_dict()` of each item in output_artifacts (list) + _items = [] + if self.output_artifacts: + for _item_output_artifacts in self.output_artifacts: + if _item_output_artifacts: + _items.append(_item_output_artifacts.to_dict()) + _dict["output_artifacts"] = _items + # set to None if custom_metadata (nullable) is None + # and model_fields_set contains the field + if self.custom_metadata is None and "custom_metadata" in self.model_fields_set: + _dict["custom_metadata"] = None + + # set to None if custom_metadata_checksum (nullable) is None + # and model_fields_set contains the field + if self.custom_metadata_checksum is None and "custom_metadata_checksum" in self.model_fields_set: + _dict["custom_metadata_checksum"] = None + + # set to None if queue_position_org (nullable) is None + # and model_fields_set contains the field + if self.queue_position_org is None and "queue_position_org" in self.model_fields_set: + _dict["queue_position_org"] = None + + # set to None if queue_position_platform (nullable) is None + # and model_fields_set contains the field + if self.queue_position_platform is None and "queue_position_platform" in self.model_fields_set: + _dict["queue_position_platform"] = None + + # set to None if termination_reason (nullable) is None + # and model_fields_set contains the field + if self.termination_reason is None and "termination_reason" in self.model_fields_set: + _dict["termination_reason"] = None + + # set to None if error_code (nullable) is None + # and model_fields_set contains the field + if self.error_code is None and "error_code" in self.model_fields_set: + _dict["error_code"] = None + + # set to None if error_message (nullable) is None + # and model_fields_set contains the field + if self.error_message is None and "error_message" in self.model_fields_set: + _dict["error_message"] = None + + # set to None if terminated_at (nullable) is None + # and model_fields_set contains the field + if self.terminated_at is None and "terminated_at" in self.model_fields_set: + _dict["terminated_at"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of ItemResultReadResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "item_id": obj.get("item_id"), + "external_id": obj.get("external_id"), + "custom_metadata": obj.get("custom_metadata"), + "custom_metadata_checksum": obj.get("custom_metadata_checksum"), + "queue_position_org": obj.get("queue_position_org"), + "queue_position_platform": obj.get("queue_position_platform"), + "state": obj.get("state"), + "output": obj.get("output"), + "termination_reason": obj.get("termination_reason"), + "error_code": obj.get("error_code"), + "error_message": obj.get("error_message"), + "terminated_at": obj.get("terminated_at"), + "input_artifacts": [InputArtifactResultReadResponse.from_dict(_item) for _item in obj["input_artifacts"]] if obj.get("input_artifacts") is not None else None, + "output_artifacts": [OutputArtifactResultReadResponse.from_dict(_item) for _item in obj["output_artifacts"]] if obj.get("output_artifacts") is not None else None + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/item_state.py b/src/aignostics_sdk/_codegen/models/item_state.py new file mode 100644 index 000000000..d59d789dc --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/item_state.py @@ -0,0 +1,36 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +from enum import Enum +from typing import Self + + +class ItemState(str, Enum): + """ + ItemState + """ + + """ + allowed enum values + """ + PENDING = "PENDING" + PROCESSING = "PROCESSING" + TERMINATED = "TERMINATED" + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ItemState from a JSON string""" + return cls(json.loads(json_str)) diff --git a/src/aignostics_sdk/_codegen/models/item_termination_reason.py b/src/aignostics_sdk/_codegen/models/item_termination_reason.py new file mode 100644 index 000000000..a933bbae0 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/item_termination_reason.py @@ -0,0 +1,37 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +from enum import Enum +from typing import Self + + +class ItemTerminationReason(str, Enum): + """ + ItemTerminationReason + """ + + """ + allowed enum values + """ + SUCCEEDED = "SUCCEEDED" + USER_ERROR = "USER_ERROR" + SYSTEM_ERROR = "SYSTEM_ERROR" + SKIPPED = "SKIPPED" + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ItemTerminationReason from a JSON string""" + return cls(json.loads(json_str)) diff --git a/src/aignostics_sdk/_codegen/models/me_read_response.py b/src/aignostics_sdk/_codegen/models/me_read_response.py new file mode 100644 index 000000000..4f81df1f8 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/me_read_response.py @@ -0,0 +1,94 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict + +from aignostics_sdk._codegen.models.organization_read_response import OrganizationReadResponse +from aignostics_sdk._codegen.models.user_read_response import UserReadResponse + + +class MeReadResponse(BaseModel): + """ + Response schema for `Get current user` endpoint + """ + user: UserReadResponse + organization: OrganizationReadResponse + __properties: ClassVar[list[str]] = ["user", "organization"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of MeReadResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of user + if self.user: + _dict["user"] = self.user.to_dict() + # override the default output from pydantic by calling `to_dict()` of organization + if self.organization: + _dict["organization"] = self.organization.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of MeReadResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "user": UserReadResponse.from_dict(obj["user"]) if obj.get("user") is not None else None, + "organization": OrganizationReadResponse.from_dict(obj["organization"]) if obj.get("organization") is not None else None + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/organization_read_response.py b/src/aignostics_sdk/_codegen/models/organization_read_response.py new file mode 100644 index 000000000..19fc28627 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/organization_read_response.py @@ -0,0 +1,109 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, StrictStr + + +class OrganizationReadResponse(BaseModel): + """ + Part of response schema for Organization object in `Get current user` endpoint. This model corresponds to the response schema returned from Auth0 GET /v2/organizations/{id} endpoint, flattens out the metadata out and doesn't return branding or token_quota objects. For details, see: https://auth0.com/docs/api/management/v2/organizations/get-organizations-by-id #### Configuration for integrating with Aignostics Platform services. The Aignostics Platform API requires signed URLs for input artifacts (slide images). To simplify this process, Aignostics provides a dedicated storage bucket. The HMAC credentials below grant read and write access to this bucket, allowing you to upload files and generate the signed URLs needed for API calls. Additionally, logging and error reporting tokens enable Aignostics to provide better support and monitor system performance for your integration. + """ # noqa: E501 + id: StrictStr = Field(description="Unique organization identifier") + name: StrictStr | None = None + display_name: StrictStr | None = None + aignostics_bucket_hmac_access_key_id: StrictStr = Field(description="HMAC access key ID for the Aignostics-provided storage bucket. Used to authenticate requests for uploading files and generating signed URLs") + aignostics_bucket_hmac_secret_access_key: StrictStr = Field(description="HMAC secret access key paired with the access key ID. Keep this credential secure.") + aignostics_bucket_name: StrictStr = Field(description="Name of the bucket provided by Aignostics for storing input artifacts (slide images)") + aignostics_bucket_protocol: StrictStr = Field(description="Protocol to use for bucket access. Defines the URL scheme for connecting to the storage service") + aignostics_logfire_token: StrictStr = Field(description="Authentication token for Logfire observability service. Enables sending application logs and performance metrics to Aignostics for monitoring and support") + aignostics_sentry_dsn: StrictStr = Field(description="Data Source Name (DSN) for Sentry error tracking service. Allows automatic reporting of errors and exceptions to Aignostics support team") + __properties: ClassVar[list[str]] = ["id", "name", "display_name", "aignostics_bucket_hmac_access_key_id", "aignostics_bucket_hmac_secret_access_key", "aignostics_bucket_name", "aignostics_bucket_protocol", "aignostics_logfire_token", "aignostics_sentry_dsn"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of OrganizationReadResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if name (nullable) is None + # and model_fields_set contains the field + if self.name is None and "name" in self.model_fields_set: + _dict["name"] = None + + # set to None if display_name (nullable) is None + # and model_fields_set contains the field + if self.display_name is None and "display_name" in self.model_fields_set: + _dict["display_name"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of OrganizationReadResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "id": obj.get("id"), + "name": obj.get("name"), + "display_name": obj.get("display_name"), + "aignostics_bucket_hmac_access_key_id": obj.get("aignostics_bucket_hmac_access_key_id"), + "aignostics_bucket_hmac_secret_access_key": obj.get("aignostics_bucket_hmac_secret_access_key"), + "aignostics_bucket_name": obj.get("aignostics_bucket_name"), + "aignostics_bucket_protocol": obj.get("aignostics_bucket_protocol"), + "aignostics_logfire_token": obj.get("aignostics_logfire_token"), + "aignostics_sentry_dsn": obj.get("aignostics_sentry_dsn") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/output_artifact.py b/src/aignostics_sdk/_codegen/models/output_artifact.py new file mode 100644 index 000000000..f75ab1329 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/output_artifact.py @@ -0,0 +1,101 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re +from typing import Annotated, Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, StrictStr, field_validator + +from aignostics_sdk._codegen.models.output_artifact_scope import OutputArtifactScope +from aignostics_sdk._codegen.models.output_artifact_visibility import OutputArtifactVisibility + + +class OutputArtifact(BaseModel): + """ + OutputArtifact + """ + name: StrictStr + mime_type: Annotated[str, Field(strict=True)] + metadata_schema: dict[str, Any] + scope: OutputArtifactScope + visibility: OutputArtifactVisibility + __properties: ClassVar[list[str]] = ["name", "mime_type", "metadata_schema", "scope", "visibility"] + + @field_validator("mime_type") + def mime_type_validate_regular_expression(cls, value): + """Validates the regular expression""" + if not re.match(r"^\w+\/\w+[-+.|\w+]+\w+$", value): + raise ValueError(r"must validate the regular expression /^\w+\/\w+[-+.|\w+]+\w+$/") + return value + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of OutputArtifact from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of OutputArtifact from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "name": obj.get("name"), + "mime_type": obj.get("mime_type"), + "metadata_schema": obj.get("metadata_schema"), + "scope": obj.get("scope"), + "visibility": obj.get("visibility") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/output_artifact_result_read_response.py b/src/aignostics_sdk/_codegen/models/output_artifact_result_read_response.py new file mode 100644 index 000000000..15f86bccc --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/output_artifact_result_read_response.py @@ -0,0 +1,128 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Annotated, Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, StrictStr + +from aignostics_sdk._codegen.models.artifact_output import ArtifactOutput +from aignostics_sdk._codegen.models.artifact_state import ArtifactState +from aignostics_sdk._codegen.models.artifact_termination_reason import ArtifactTerminationReason + + +class OutputArtifactResultReadResponse(BaseModel): + """ + OutputArtifactResultReadResponse + """ + output_artifact_id: StrictStr = Field(description="The Id of the artifact. Used internally") + name: StrictStr = Field(description=" Name of the output from the output schema from the `/v1/versions/{version_id}` endpoint. ") + metadata: dict[str, Any] | None = None + state: ArtifactState = Field(description="The current state of the artifact (PENDING, PROCESSING, TERMINATED)") + termination_reason: ArtifactTerminationReason | None = None + output: ArtifactOutput = Field(description="The output status of the artifact (NONE, FULL)") + error_code: StrictStr | None = None + error_message: StrictStr | None = None + download_url: Annotated[str, Field(min_length=1, strict=True, max_length=2083)] | None = None + __properties: ClassVar[list[str]] = ["output_artifact_id", "name", "metadata", "state", "termination_reason", "output", "error_code", "error_message", "download_url"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of OutputArtifactResultReadResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if metadata (nullable) is None + # and model_fields_set contains the field + if self.metadata is None and "metadata" in self.model_fields_set: + _dict["metadata"] = None + + # set to None if termination_reason (nullable) is None + # and model_fields_set contains the field + if self.termination_reason is None and "termination_reason" in self.model_fields_set: + _dict["termination_reason"] = None + + # set to None if error_code (nullable) is None + # and model_fields_set contains the field + if self.error_code is None and "error_code" in self.model_fields_set: + _dict["error_code"] = None + + # set to None if error_message (nullable) is None + # and model_fields_set contains the field + if self.error_message is None and "error_message" in self.model_fields_set: + _dict["error_message"] = None + + # set to None if download_url (nullable) is None + # and model_fields_set contains the field + if self.download_url is None and "download_url" in self.model_fields_set: + _dict["download_url"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of OutputArtifactResultReadResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "output_artifact_id": obj.get("output_artifact_id"), + "name": obj.get("name"), + "metadata": obj.get("metadata"), + "state": obj.get("state"), + "termination_reason": obj.get("termination_reason"), + "output": obj.get("output"), + "error_code": obj.get("error_code"), + "error_message": obj.get("error_message"), + "download_url": obj.get("download_url") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/output_artifact_scope.py b/src/aignostics_sdk/_codegen/models/output_artifact_scope.py new file mode 100644 index 000000000..842eaf35d --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/output_artifact_scope.py @@ -0,0 +1,35 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +from enum import Enum +from typing import Self + + +class OutputArtifactScope(str, Enum): + """ + OutputArtifactScope + """ + + """ + allowed enum values + """ + ITEM = "ITEM" + GLOBAL = "GLOBAL" + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of OutputArtifactScope from a JSON string""" + return cls(json.loads(json_str)) diff --git a/src/aignostics_sdk/_codegen/models/output_artifact_visibility.py b/src/aignostics_sdk/_codegen/models/output_artifact_visibility.py new file mode 100644 index 000000000..ab3b62556 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/output_artifact_visibility.py @@ -0,0 +1,35 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +from enum import Enum +from typing import Self + + +class OutputArtifactVisibility(str, Enum): + """ + OutputArtifactVisibility + """ + + """ + allowed enum values + """ + INTERNAL = "INTERNAL" + EXTERNAL = "EXTERNAL" + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of OutputArtifactVisibility from a JSON string""" + return cls(json.loads(json_str)) diff --git a/src/aignostics_sdk/_codegen/models/run_creation_request.py b/src/aignostics_sdk/_codegen/models/run_creation_request.py new file mode 100644 index 000000000..c6c7e822f --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/run_creation_request.py @@ -0,0 +1,119 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Annotated, Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, StrictStr + +from aignostics_sdk._codegen.models.item_creation_request import ItemCreationRequest +from aignostics_sdk._codegen.models.scheduling_request import SchedulingRequest + + +class RunCreationRequest(BaseModel): + """ + Request schema for `Initiate Run` endpoint. It describes which application version is chosen, and which user data should be processed. + """ # noqa: E501 + application_id: StrictStr = Field(description="Unique ID for the application to use for processing") + version_number: StrictStr | None = None + custom_metadata: dict[str, Any] | None = None + scheduling: SchedulingRequest | None = None + items: Annotated[list[ItemCreationRequest], Field(min_length=1)] = Field(description="List of items (slides) to process. Each item represents a whole slide image (WSI) with its associated metadata and artifacts") + __properties: ClassVar[list[str]] = ["application_id", "version_number", "custom_metadata", "scheduling", "items"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of RunCreationRequest from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of scheduling + if self.scheduling: + _dict["scheduling"] = self.scheduling.to_dict() + # override the default output from pydantic by calling `to_dict()` of each item in items (list) + _items = [] + if self.items: + for _item_items in self.items: + if _item_items: + _items.append(_item_items.to_dict()) + _dict["items"] = _items + # set to None if version_number (nullable) is None + # and model_fields_set contains the field + if self.version_number is None and "version_number" in self.model_fields_set: + _dict["version_number"] = None + + # set to None if custom_metadata (nullable) is None + # and model_fields_set contains the field + if self.custom_metadata is None and "custom_metadata" in self.model_fields_set: + _dict["custom_metadata"] = None + + # set to None if scheduling (nullable) is None + # and model_fields_set contains the field + if self.scheduling is None and "scheduling" in self.model_fields_set: + _dict["scheduling"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of RunCreationRequest from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "application_id": obj.get("application_id"), + "version_number": obj.get("version_number"), + "custom_metadata": obj.get("custom_metadata"), + "scheduling": SchedulingRequest.from_dict(obj["scheduling"]) if obj.get("scheduling") is not None else None, + "items": [ItemCreationRequest.from_dict(_item) for _item in obj["items"]] if obj.get("items") is not None else None + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/run_creation_response.py b/src/aignostics_sdk/_codegen/models/run_creation_response.py new file mode 100644 index 000000000..43b330caf --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/run_creation_response.py @@ -0,0 +1,83 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, StrictStr + + +class RunCreationResponse(BaseModel): + """ + RunCreationResponse + """ + run_id: StrictStr + __properties: ClassVar[list[str]] = ["run_id"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of RunCreationResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of RunCreationResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "run_id": obj.get("run_id") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/run_item_statistics.py b/src/aignostics_sdk/_codegen/models/run_item_statistics.py new file mode 100644 index 000000000..33c83e40d --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/run_item_statistics.py @@ -0,0 +1,95 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, StrictInt + + +class RunItemStatistics(BaseModel): + """ + RunItemStatistics + """ + item_count: StrictInt = Field(description="Total number of the items in the run") + item_pending_count: StrictInt = Field(description="The number of items in `PENDING` state") + item_processing_count: StrictInt = Field(description="The number of items in `PROCESSING` state") + item_user_error_count: StrictInt = Field(description="The number of items in `TERMINATED` state, and the item termination reason is `USER_ERROR`") + item_system_error_count: StrictInt = Field(description="The number of items in `TERMINATED` state, and the item termination reason is `SYSTEM_ERROR`") + item_skipped_count: StrictInt = Field(description="The number of items in `TERMINATED` state, and the item termination reason is `SKIPPED`") + item_succeeded_count: StrictInt = Field(description="The number of items in `TERMINATED` state, and the item termination reason is `SUCCEEDED`") + __properties: ClassVar[list[str]] = ["item_count", "item_pending_count", "item_processing_count", "item_user_error_count", "item_system_error_count", "item_skipped_count", "item_succeeded_count"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of RunItemStatistics from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of RunItemStatistics from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "item_count": obj.get("item_count"), + "item_pending_count": obj.get("item_pending_count"), + "item_processing_count": obj.get("item_processing_count"), + "item_user_error_count": obj.get("item_user_error_count"), + "item_system_error_count": obj.get("item_system_error_count"), + "item_skipped_count": obj.get("item_skipped_count"), + "item_succeeded_count": obj.get("item_succeeded_count") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/run_output.py b/src/aignostics_sdk/_codegen/models/run_output.py new file mode 100644 index 000000000..4b2a3ae86 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/run_output.py @@ -0,0 +1,36 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +from enum import Enum +from typing import Self + + +class RunOutput(str, Enum): + """ + RunOutput + """ + + """ + allowed enum values + """ + NONE = "NONE" + PARTIAL = "PARTIAL" + FULL = "FULL" + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of RunOutput from a JSON string""" + return cls(json.loads(json_str)) diff --git a/src/aignostics_sdk/_codegen/models/run_read_response.py b/src/aignostics_sdk/_codegen/models/run_read_response.py new file mode 100644 index 000000000..925aac005 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/run_read_response.py @@ -0,0 +1,173 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from datetime import datetime +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr + +from aignostics_sdk._codegen.models.run_item_statistics import RunItemStatistics +from aignostics_sdk._codegen.models.run_output import RunOutput +from aignostics_sdk._codegen.models.run_state import RunState +from aignostics_sdk._codegen.models.run_termination_reason import RunTerminationReason +from aignostics_sdk._codegen.models.scheduling_response import SchedulingResponse + + +class RunReadResponse(BaseModel): + """ + Response schema for `Get run details` endpoint + """ + run_id: StrictStr = Field(description="UUID of the application") + application_id: StrictStr = Field(description="Application id") + version_number: StrictStr = Field(description="Application version number") + state: RunState = Field(description="When the run request is received by the Platform, the `state` of it is set to `PENDING`. The state changes to `PROCESSING` when at least one item is being processed. After `PROCESSING`, the state of the run can switch back to `PENDING` if there are no processing items, or to `TERMINATED` when the run finished processing.") + output: RunOutput = Field(description="The status of the output of the run. When 0 items are successfully processed the output is `NONE`, after one item is successfully processed, the value is set to `PARTIAL`. When all items of the run are successfully processed, the output is set to `FULL`.") + termination_reason: RunTerminationReason | None + error_code: StrictStr | None + error_message: StrictStr | None + statistics: RunItemStatistics = Field(description="Aggregated statistics of the run execution") + custom_metadata: dict[str, Any] | None = None + custom_metadata_checksum: StrictStr | None = None + submitted_at: datetime = Field(description="Timestamp showing when the run was triggered") + submitted_by: StrictStr = Field(description="Id of the user who triggered the run") + terminated_at: datetime | None = None + num_preceding_items_org: StrictInt | None = None + num_preceding_items_platform: StrictInt | None = None + scheduling: SchedulingResponse | None = None + __properties: ClassVar[list[str]] = ["run_id", "application_id", "version_number", "state", "output", "termination_reason", "error_code", "error_message", "statistics", "custom_metadata", "custom_metadata_checksum", "submitted_at", "submitted_by", "terminated_at", "num_preceding_items_org", "num_preceding_items_platform", "scheduling"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of RunReadResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of statistics + if self.statistics: + _dict["statistics"] = self.statistics.to_dict() + # override the default output from pydantic by calling `to_dict()` of scheduling + if self.scheduling: + _dict["scheduling"] = self.scheduling.to_dict() + # set to None if termination_reason (nullable) is None + # and model_fields_set contains the field + if self.termination_reason is None and "termination_reason" in self.model_fields_set: + _dict["termination_reason"] = None + + # set to None if error_code (nullable) is None + # and model_fields_set contains the field + if self.error_code is None and "error_code" in self.model_fields_set: + _dict["error_code"] = None + + # set to None if error_message (nullable) is None + # and model_fields_set contains the field + if self.error_message is None and "error_message" in self.model_fields_set: + _dict["error_message"] = None + + # set to None if custom_metadata (nullable) is None + # and model_fields_set contains the field + if self.custom_metadata is None and "custom_metadata" in self.model_fields_set: + _dict["custom_metadata"] = None + + # set to None if custom_metadata_checksum (nullable) is None + # and model_fields_set contains the field + if self.custom_metadata_checksum is None and "custom_metadata_checksum" in self.model_fields_set: + _dict["custom_metadata_checksum"] = None + + # set to None if terminated_at (nullable) is None + # and model_fields_set contains the field + if self.terminated_at is None and "terminated_at" in self.model_fields_set: + _dict["terminated_at"] = None + + # set to None if num_preceding_items_org (nullable) is None + # and model_fields_set contains the field + if self.num_preceding_items_org is None and "num_preceding_items_org" in self.model_fields_set: + _dict["num_preceding_items_org"] = None + + # set to None if num_preceding_items_platform (nullable) is None + # and model_fields_set contains the field + if self.num_preceding_items_platform is None and "num_preceding_items_platform" in self.model_fields_set: + _dict["num_preceding_items_platform"] = None + + # set to None if scheduling (nullable) is None + # and model_fields_set contains the field + if self.scheduling is None and "scheduling" in self.model_fields_set: + _dict["scheduling"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of RunReadResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "run_id": obj.get("run_id"), + "application_id": obj.get("application_id"), + "version_number": obj.get("version_number"), + "state": obj.get("state"), + "output": obj.get("output"), + "termination_reason": obj.get("termination_reason"), + "error_code": obj.get("error_code"), + "error_message": obj.get("error_message"), + "statistics": RunItemStatistics.from_dict(obj["statistics"]) if obj.get("statistics") is not None else None, + "custom_metadata": obj.get("custom_metadata"), + "custom_metadata_checksum": obj.get("custom_metadata_checksum"), + "submitted_at": obj.get("submitted_at"), + "submitted_by": obj.get("submitted_by"), + "terminated_at": obj.get("terminated_at"), + "num_preceding_items_org": obj.get("num_preceding_items_org"), + "num_preceding_items_platform": obj.get("num_preceding_items_platform"), + "scheduling": SchedulingResponse.from_dict(obj["scheduling"]) if obj.get("scheduling") is not None else None + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/run_state.py b/src/aignostics_sdk/_codegen/models/run_state.py new file mode 100644 index 000000000..b9e681e9e --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/run_state.py @@ -0,0 +1,36 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +from enum import Enum +from typing import Self + + +class RunState(str, Enum): + """ + RunState + """ + + """ + allowed enum values + """ + PENDING = "PENDING" + PROCESSING = "PROCESSING" + TERMINATED = "TERMINATED" + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of RunState from a JSON string""" + return cls(json.loads(json_str)) diff --git a/src/aignostics_sdk/_codegen/models/run_termination_reason.py b/src/aignostics_sdk/_codegen/models/run_termination_reason.py new file mode 100644 index 000000000..02e784689 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/run_termination_reason.py @@ -0,0 +1,36 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +from enum import Enum +from typing import Self + + +class RunTerminationReason(str, Enum): + """ + RunTerminationReason + """ + + """ + allowed enum values + """ + ALL_ITEMS_PROCESSED = "ALL_ITEMS_PROCESSED" + CANCELED_BY_SYSTEM = "CANCELED_BY_SYSTEM" + CANCELED_BY_USER = "CANCELED_BY_USER" + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of RunTerminationReason from a JSON string""" + return cls(json.loads(json_str)) diff --git a/src/aignostics_sdk/_codegen/models/scheduling_request.py b/src/aignostics_sdk/_codegen/models/scheduling_request.py new file mode 100644 index 000000000..7e3fddd7c --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/scheduling_request.py @@ -0,0 +1,96 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from datetime import datetime +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict + + +class SchedulingRequest(BaseModel): + """ + Scheduling constraints for a run. + """ + due_date: datetime | None = None + deadline: datetime | None = None + __properties: ClassVar[list[str]] = ["due_date", "deadline"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of SchedulingRequest from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if due_date (nullable) is None + # and model_fields_set contains the field + if self.due_date is None and "due_date" in self.model_fields_set: + _dict["due_date"] = None + + # set to None if deadline (nullable) is None + # and model_fields_set contains the field + if self.deadline is None and "deadline" in self.model_fields_set: + _dict["deadline"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of SchedulingRequest from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "due_date": obj.get("due_date"), + "deadline": obj.get("deadline") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/scheduling_response.py b/src/aignostics_sdk/_codegen/models/scheduling_response.py new file mode 100644 index 000000000..0c5536d15 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/scheduling_response.py @@ -0,0 +1,96 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from datetime import datetime +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict + + +class SchedulingResponse(BaseModel): + """ + Scheduling fields returned in run responses. + """ + due_date: datetime | None = None + deadline: datetime | None = None + __properties: ClassVar[list[str]] = ["due_date", "deadline"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of SchedulingResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if due_date (nullable) is None + # and model_fields_set contains the field + if self.due_date is None and "due_date" in self.model_fields_set: + _dict["due_date"] = None + + # set to None if deadline (nullable) is None + # and model_fields_set contains the field + if self.deadline is None and "deadline" in self.model_fields_set: + _dict["deadline"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of SchedulingResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "due_date": obj.get("due_date"), + "deadline": obj.get("deadline") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/user_read_response.py b/src/aignostics_sdk/_codegen/models/user_read_response.py new file mode 100644 index 000000000..aa990b1c5 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/user_read_response.py @@ -0,0 +1,140 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from datetime import datetime +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictStr + + +class UserReadResponse(BaseModel): + """ + Part of response schema for User object in `Get current user` endpoint. This model corresponds to the response schema returned from Auth0 GET /v2/users/{id} endpoint. For details, see: https://auth0.com/docs/api/management/v2/users/get-users-by-id + """ # noqa: E501 + id: StrictStr = Field(description="Unique user identifier") + email: StrictStr | None = None + email_verified: StrictBool | None = None + name: StrictStr | None = None + given_name: StrictStr | None = None + family_name: StrictStr | None = None + nickname: StrictStr | None = None + picture: StrictStr | None = None + updated_at: datetime | None = None + __properties: ClassVar[list[str]] = ["id", "email", "email_verified", "name", "given_name", "family_name", "nickname", "picture", "updated_at"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of UserReadResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if email (nullable) is None + # and model_fields_set contains the field + if self.email is None and "email" in self.model_fields_set: + _dict["email"] = None + + # set to None if email_verified (nullable) is None + # and model_fields_set contains the field + if self.email_verified is None and "email_verified" in self.model_fields_set: + _dict["email_verified"] = None + + # set to None if name (nullable) is None + # and model_fields_set contains the field + if self.name is None and "name" in self.model_fields_set: + _dict["name"] = None + + # set to None if given_name (nullable) is None + # and model_fields_set contains the field + if self.given_name is None and "given_name" in self.model_fields_set: + _dict["given_name"] = None + + # set to None if family_name (nullable) is None + # and model_fields_set contains the field + if self.family_name is None and "family_name" in self.model_fields_set: + _dict["family_name"] = None + + # set to None if nickname (nullable) is None + # and model_fields_set contains the field + if self.nickname is None and "nickname" in self.model_fields_set: + _dict["nickname"] = None + + # set to None if picture (nullable) is None + # and model_fields_set contains the field + if self.picture is None and "picture" in self.model_fields_set: + _dict["picture"] = None + + # set to None if updated_at (nullable) is None + # and model_fields_set contains the field + if self.updated_at is None and "updated_at" in self.model_fields_set: + _dict["updated_at"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of UserReadResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "id": obj.get("id"), + "email": obj.get("email"), + "email_verified": obj.get("email_verified"), + "name": obj.get("name"), + "given_name": obj.get("given_name"), + "family_name": obj.get("family_name"), + "nickname": obj.get("nickname"), + "picture": obj.get("picture"), + "updated_at": obj.get("updated_at") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/validation_error.py b/src/aignostics_sdk/_codegen/models/validation_error.py new file mode 100644 index 000000000..8e6bdc7b3 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/validation_error.py @@ -0,0 +1,105 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, StrictStr + +from aignostics_sdk._codegen.models.validation_error_loc_inner import ValidationErrorLocInner + + +class ValidationError(BaseModel): + """ + ValidationError + """ + loc: list[ValidationErrorLocInner] + msg: StrictStr + type: StrictStr + input: Any | None = None + ctx: dict[str, Any] | None = None + __properties: ClassVar[list[str]] = ["loc", "msg", "type", "input", "ctx"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of ValidationError from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in loc (list) + _items = [] + if self.loc: + for _item_loc in self.loc: + if _item_loc: + _items.append(_item_loc.to_dict()) + _dict["loc"] = _items + # set to None if input (nullable) is None + # and model_fields_set contains the field + if self.input is None and "input" in self.model_fields_set: + _dict["input"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of ValidationError from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "loc": [ValidationErrorLocInner.from_dict(_item) for _item in obj["loc"]] if obj.get("loc") is not None else None, + "msg": obj.get("msg"), + "type": obj.get("type"), + "input": obj.get("input"), + "ctx": obj.get("ctx") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/validation_error_loc_inner.py b/src/aignostics_sdk/_codegen/models/validation_error_loc_inner.py new file mode 100644 index 000000000..fceabc07d --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/validation_error_loc_inner.py @@ -0,0 +1,130 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import TYPE_CHECKING, Any, Self + +from pydantic import BaseModel, StrictInt, StrictStr, ValidationError, field_validator + +VALIDATIONERRORLOCINNER_ANY_OF_SCHEMAS = ["int", "str"] + + +class ValidationErrorLocInner(BaseModel): + """ + ValidationErrorLocInner + """ + + # data type: str + anyof_schema_1_validator: StrictStr | None = None + # data type: int + anyof_schema_2_validator: StrictInt | None = None + if TYPE_CHECKING: + actual_instance: int | str | None = None + else: + actual_instance: Any = None + any_of_schemas: set[str] = {"int", "str"} + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + if kwargs: + raise ValueError("If a position argument is used, keyword arguments cannot be used.") + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_anyof(cls, v): + instance = ValidationErrorLocInner.model_construct() + error_messages = [] + # validate data type: str + try: + instance.anyof_schema_1_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: int + try: + instance.anyof_schema_2_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if error_messages: + # no match + raise ValueError("No match found when setting the actual_instance in ValidationErrorLocInner with anyOf schemas: int, str. Details: " + ", ".join(error_messages)) + return v + + @classmethod + def from_dict(cls, obj: dict[str, Any]) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + # deserialize data into str + try: + # validation + instance.anyof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_1_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into int + try: + # validation + instance.anyof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_2_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if error_messages: + # no match + raise ValueError("No match found when deserializing the JSON string into ValidationErrorLocInner with anyOf schemas: int, str. Details: " + ", ".join(error_messages)) + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): + return self.actual_instance.to_json() + return json.dumps(self.actual_instance) + + def to_dict(self) -> dict[str, Any] | int | str | None: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): + return self.actual_instance.to_dict() + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/src/aignostics_sdk/_codegen/models/version_document_response.py b/src/aignostics_sdk/_codegen/models/version_document_response.py new file mode 100644 index 000000000..4024c6c37 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/version_document_response.py @@ -0,0 +1,96 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from datetime import datetime +from typing import Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, StrictStr + +from aignostics_sdk._codegen.models.version_document_visibility import VersionDocumentVisibility + + +class VersionDocumentResponse(BaseModel): + """ + VersionDocumentResponse + """ + id: StrictStr + name: StrictStr + mime_type: StrictStr + visibility: VersionDocumentVisibility + created_at: datetime + updated_at: datetime + __properties: ClassVar[list[str]] = ["id", "name", "mime_type", "visibility", "created_at", "updated_at"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of VersionDocumentResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of VersionDocumentResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "id": obj.get("id"), + "name": obj.get("name"), + "mime_type": obj.get("mime_type"), + "visibility": obj.get("visibility"), + "created_at": obj.get("created_at"), + "updated_at": obj.get("updated_at") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/models/version_document_visibility.py b/src/aignostics_sdk/_codegen/models/version_document_visibility.py new file mode 100644 index 000000000..7627422d8 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/version_document_visibility.py @@ -0,0 +1,35 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +from enum import Enum +from typing import Self + + +class VersionDocumentVisibility(str, Enum): + """ + VersionDocumentVisibility + """ + + """ + allowed enum values + """ + PUBLIC = "public" + INTERNAL = "internal" + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of VersionDocumentVisibility from a JSON string""" + return cls(json.loads(json_str)) diff --git a/src/aignostics_sdk/_codegen/models/version_read_response.py b/src/aignostics_sdk/_codegen/models/version_read_response.py new file mode 100644 index 000000000..35650fcf6 --- /dev/null +++ b/src/aignostics_sdk/_codegen/models/version_read_response.py @@ -0,0 +1,116 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re +from datetime import datetime +from typing import Annotated, Any, ClassVar, Self + +from pydantic import BaseModel, ConfigDict, Field, StrictStr, field_validator + +from aignostics_sdk._codegen.models.input_artifact import InputArtifact +from aignostics_sdk._codegen.models.output_artifact import OutputArtifact + + +class VersionReadResponse(BaseModel): + """ + Base Response schema for the `Application Version Details` endpoint + """ + version_number: Annotated[str, Field(strict=True)] = Field(description="Semantic version of the application") + changelog: StrictStr = Field(description="Description of the changes relative to the previous version") + input_artifacts: list[InputArtifact] = Field(description="List of the input fields, provided by the User") + output_artifacts: list[OutputArtifact] = Field(description="List of the output fields, generated by the application") + released_at: datetime = Field(description="The timestamp when the application version was registered") + __properties: ClassVar[list[str]] = ["version_number", "changelog", "input_artifacts", "output_artifacts", "released_at"] + + @field_validator("version_number") + def version_number_validate_regular_expression(cls, value): + """Validates the regular expression""" + if not re.match(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$", value): + raise ValueError(r"must validate the regular expression /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/") + return value + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self | None: + """Create an instance of VersionReadResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in input_artifacts (list) + _items = [] + if self.input_artifacts: + for _item_input_artifacts in self.input_artifacts: + if _item_input_artifacts: + _items.append(_item_input_artifacts.to_dict()) + _dict["input_artifacts"] = _items + # override the default output from pydantic by calling `to_dict()` of each item in output_artifacts (list) + _items = [] + if self.output_artifacts: + for _item_output_artifacts in self.output_artifacts: + if _item_output_artifacts: + _items.append(_item_output_artifacts.to_dict()) + _dict["output_artifacts"] = _items + return _dict + + @classmethod + def from_dict(cls, obj: dict[str, Any] | None) -> Self | None: + """Create an instance of VersionReadResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "version_number": obj.get("version_number"), + "changelog": obj.get("changelog"), + "input_artifacts": [InputArtifact.from_dict(_item) for _item in obj["input_artifacts"]] if obj.get("input_artifacts") is not None else None, + "output_artifacts": [OutputArtifact.from_dict(_item) for _item in obj["output_artifacts"]] if obj.get("output_artifacts") is not None else None, + "released_at": obj.get("released_at") + }) + return _obj diff --git a/src/aignostics_sdk/_codegen/rest.py b/src/aignostics_sdk/_codegen/rest.py new file mode 100644 index 000000000..f0d33288b --- /dev/null +++ b/src/aignostics_sdk/_codegen/rest.py @@ -0,0 +1,254 @@ + +""" +Aignostics Platform API + +The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + +The version of the OpenAPI document: 1.5.0 +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + + +import io +import json +import re +import ssl + +import urllib3 + +from aignostics_sdk._codegen.exceptions import ApiException, ApiValueError + +SUPPORTED_SOCKS_PROXIES = {"socks5", "socks5h", "socks4", "socks4a"} +RESTResponseType = urllib3.HTTPResponse + + +def is_socks_proxy_url(url): + if url is None: + return False + split_section = url.split("://") + if len(split_section) < 2: + return False + return split_section[0].lower() in SUPPORTED_SOCKS_PROXIES + + +class RESTResponse(io.IOBase): + + def __init__(self, resp) -> None: + self.response = resp + self.status = resp.status + self.reason = resp.reason + self.data = None + + def read(self): + if self.data is None: + self.data = self.response.data + return self.data + + def getheaders(self): + """Returns a dictionary of the response headers.""" + return self.response.headers + + def getheader(self, name, default=None): + """Returns a given response header.""" + return self.response.headers.get(name, default) + + +class RESTClientObject: + + def __init__(self, configuration) -> None: + # urllib3.PoolManager will pass all kw parameters to connectionpool + # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75 + # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680 + # Custom SSL certificates and client certificates: http://urllib3.readthedocs.io/en/latest/advanced-usage.html + + # cert_reqs + if configuration.verify_ssl: + cert_reqs = ssl.CERT_REQUIRED + else: + cert_reqs = ssl.CERT_NONE + + pool_args = { + "cert_reqs": cert_reqs, + "ca_certs": configuration.ssl_ca_cert, + "cert_file": configuration.cert_file, + "key_file": configuration.key_file, + } + if configuration.assert_hostname is not None: + pool_args["assert_hostname"] = ( + configuration.assert_hostname + ) + + if configuration.retries is not None: + pool_args["retries"] = configuration.retries + + if configuration.tls_server_name: + pool_args["server_hostname"] = configuration.tls_server_name + + if configuration.socket_options is not None: + pool_args["socket_options"] = configuration.socket_options + + if configuration.connection_pool_maxsize is not None: + pool_args["maxsize"] = configuration.connection_pool_maxsize + + # https pool manager + self.pool_manager: urllib3.PoolManager + + if configuration.proxy: + if is_socks_proxy_url(configuration.proxy): + from urllib3.contrib.socks import SOCKSProxyManager + pool_args["proxy_url"] = configuration.proxy + pool_args["headers"] = configuration.proxy_headers + self.pool_manager = SOCKSProxyManager(**pool_args) + else: + pool_args["proxy_url"] = configuration.proxy + pool_args["proxy_headers"] = configuration.proxy_headers + self.pool_manager = urllib3.ProxyManager(**pool_args) + else: + self.pool_manager = urllib3.PoolManager(**pool_args) + + def request( + self, + method, + url, + headers=None, + body=None, + post_params=None, + _request_timeout=None + ): + """Perform requests. + + :param method: http request method + :param url: http request url + :param headers: http request headers + :param body: request json body, for `application/json` + :param post_params: request post parameters, + `application/x-www-form-urlencoded` + and `multipart/form-data` + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + """ + method = method.upper() + assert method in [ + "GET", + "HEAD", + "DELETE", + "POST", + "PUT", + "PATCH", + "OPTIONS" + ] + + if post_params and body: + raise ApiValueError( + "body parameter cannot be used with post_params parameter." + ) + + post_params = post_params or {} + headers = headers or {} + + timeout = None + if _request_timeout: + if isinstance(_request_timeout, (int, float)): + timeout = urllib3.Timeout(total=_request_timeout) + elif ( + isinstance(_request_timeout, tuple) + and len(_request_timeout) == 2 + ): + timeout = urllib3.Timeout( + connect=_request_timeout[0], + read=_request_timeout[1] + ) + + try: + # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE` + if method in ["POST", "PUT", "PATCH", "OPTIONS", "DELETE"]: + + # no content type provided or payload is json + content_type = headers.get("Content-Type") + if ( + not content_type + or re.search(r"json", content_type, re.IGNORECASE) + ): + request_body = None + if body is not None: + request_body = json.dumps(body) + r = self.pool_manager.request( + method, + url, + body=request_body, + timeout=timeout, + headers=headers, + preload_content=False + ) + elif content_type == "application/x-www-form-urlencoded": + r = self.pool_manager.request( + method, + url, + fields=post_params, + encode_multipart=False, + timeout=timeout, + headers=headers, + preload_content=False + ) + elif content_type == "multipart/form-data": + # must del headers['Content-Type'], or the correct + # Content-Type which generated by urllib3 will be + # overwritten. + del headers["Content-Type"] + # Ensures that dict objects are serialized + post_params = [(a, json.dumps(b)) if isinstance(b, dict) else (a, b) for a, b in post_params] + r = self.pool_manager.request( + method, + url, + fields=post_params, + encode_multipart=True, + timeout=timeout, + headers=headers, + preload_content=False + ) + # Pass a `string` parameter directly in the body to support + # other content types than JSON when `body` argument is + # provided in serialized form. + elif isinstance(body, str) or isinstance(body, bytes): + r = self.pool_manager.request( + method, + url, + body=body, + timeout=timeout, + headers=headers, + preload_content=False + ) + elif headers["Content-Type"].startswith("text/") and isinstance(body, bool): + request_body = "true" if body else "false" + r = self.pool_manager.request( + method, + url, + body=request_body, + preload_content=False, + timeout=timeout, + headers=headers) + else: + # Cannot generate the request from given parameters + msg = """Cannot prepare a request message for provided + arguments. Please check that your arguments match + declared content type.""" + raise ApiException(status=0, reason=msg) + # For `GET`, `HEAD` + else: + r = self.pool_manager.request( + method, + url, + fields={}, + timeout=timeout, + headers=headers, + preload_content=False + ) + except urllib3.exceptions.SSLError as e: + msg = "\n".join([type(e).__name__, str(e)]) + raise ApiException(status=0, reason=msg) + + return RESTResponse(r) diff --git a/src/aignostics/application/CLAUDE.md b/src/aignostics_sdk/application/CLAUDE.md similarity index 100% rename from src/aignostics/application/CLAUDE.md rename to src/aignostics_sdk/application/CLAUDE.md diff --git a/src/aignostics_sdk/application/__init__.py b/src/aignostics_sdk/application/__init__.py new file mode 100644 index 000000000..16c894cba --- /dev/null +++ b/src/aignostics_sdk/application/__init__.py @@ -0,0 +1,34 @@ +"""Application module.""" + +from __future__ import annotations + +import importlib +from typing import TYPE_CHECKING + +from ._cli import cli + +if TYPE_CHECKING: + from ._models import DownloadProgress, DownloadProgressState + from ._service import Service + from ._settings import Settings + +_LAZY: dict[str, tuple[str, str]] = { + "DownloadProgress": ("aignostics_sdk.application._models", "DownloadProgress"), + "DownloadProgressState": ("aignostics_sdk.application._models", "DownloadProgressState"), + "Service": ("aignostics_sdk.application._service", "Service"), + "Settings": ("aignostics_sdk.application._settings", "Settings"), +} + + +def __getattr__(name: str) -> object: + if name in _LAZY: + module_path, attr_name = _LAZY[name] + mod = importlib.import_module(module_path) + obj = getattr(mod, attr_name) + globals()[name] = obj + return obj + msg = f"module {__name__!r} has no attribute {name!r}" + raise AttributeError(msg) + + +__all__ = ["DownloadProgress", "DownloadProgressState", "Service", "Settings", "cli"] diff --git a/src/aignostics/application/_cli.py b/src/aignostics_sdk/application/_cli.py similarity index 96% rename from src/aignostics/application/_cli.py rename to src/aignostics_sdk/application/_cli.py index 9ee527859..f90091eed 100644 --- a/src/aignostics/application/_cli.py +++ b/src/aignostics_sdk/application/_cli.py @@ -1,18 +1,19 @@ """CLI of application module.""" +from __future__ import annotations + import asyncio import json import sys import time import zipfile from pathlib import Path -from typing import Annotated +from typing import TYPE_CHECKING, Annotated import typer from loguru import logger -from aignostics.bucket import Service as BucketService -from aignostics.platform import ( +from aignostics_sdk.platform import ( DEFAULT_CPU_PROVISIONING_MODE, DEFAULT_FLEX_START_MAX_RUN_DURATION_MINUTES, DEFAULT_GPU_PROVISIONING_MODE, @@ -22,14 +23,14 @@ Client, ForbiddenException, NotFoundException, - RunState, ) -from aignostics.platform import Service as PlatformService -from aignostics.system import Service as SystemService -from aignostics.utils import console, get_user_data_directory, sanitize_path +from aignostics_sdk.system import Service as SystemService +from aignostics_sdk.utils import console, get_user_data_directory, sanitize_path + +if TYPE_CHECKING: + from ._models import DownloadProgress + from ._service import Service -from ._models import DownloadProgress, DownloadProgressState -from ._service import Service from ._utils import ( application_run_status_to_str, get_mime_type_for_artifact, @@ -45,6 +46,17 @@ PROGRESS_TASK_DESCRIPTION = "[progress.description]{task.description}" +def _Service() -> Service: # noqa: N802 + """Lazily import and instantiate the application Service. + + Returns: + Service: A new Service instance. + """ + from ._service import Service as ApplicationService # noqa: PLC0415 + + return ApplicationService() + + ApplicationVersionOption = Annotated[ str | None, typer.Option( @@ -162,7 +174,7 @@ def application_list( # noqa: C901 ) -> None: """List available applications.""" try: - apps = Service().applications() + apps = _Service().applications() except Exception as e: logger.exception("Could not load applications") if format == "json": @@ -190,7 +202,7 @@ def application_list( # noqa: C901 console.print(f"[bold]Regulatory Classes:[/bold] {', '.join(app.regulatory_classes)}") try: - details = Service().application(app.application_id) + details = _Service().application(app.application_id) except Exception as e: logger.exception(f"Failed to get application details for application '{app.application_id}'") console.print( @@ -202,7 +214,7 @@ def application_list( # noqa: C901 for version in details.versions: console.print(f" - {version.number} ({version.released_at})") - app_version = Service().application_version(app.application_id, version.number) + app_version = _Service().application_version(app.application_id, version.number) console.print(f" Changelog: {app_version.changelog}") num_inputs = len(app_version.input_artifacts) @@ -261,8 +273,8 @@ def application_dump_schemata( # noqa: C901 ) -> None: """Output the input schema of the application in JSON format.""" try: - app = Service().application(application_id) - app_version = Service().application_version(application_id, application_version) + app = _Service().application(application_id) + app_version = _Service().application_version(application_id, application_version) except (NotFoundException, ValueError) as e: message = f"Failed to load application version with ID '{id}', check your input: : {e!s}." logger.warning(message) @@ -346,7 +358,7 @@ def application_describe( # noqa: C901, PLR0912 ) -> None: """Describe application.""" try: - app = Service().application(application_id) + app = _Service().application(application_id) except NotFoundException: logger.warning(f"Application with ID '{application_id}' not found.") if format == "json": @@ -385,7 +397,7 @@ def application_describe( # noqa: C901, PLR0912 if not verbose: continue try: - app_version = Service().application_version(app.application_id, version.number) + app_version = _Service().application_version(app.application_id, version.number) except Exception as e: logger.exception(f"Failed to get application version for '{application_id}', '{version.number}'") console.print( @@ -614,7 +626,7 @@ def run_prepare( write_metadata_dict_to_csv( metadata_csv=metadata_csv, - metadata_dict=Service().generate_metadata_from_source_directory( + metadata_dict=_Service().generate_metadata_from_source_directory( source_directory=source_directory, application_id=application_id, application_version=application_version, @@ -670,6 +682,7 @@ def run_upload( # noqa: PLR0913, PLR0917 2. Uploads the files referenced in the CSV file to the Aignostics platform. 3. Incrementally updates the CSV file with upload progress and the signed URLs for the uploaded files. """ + from aignostics.bucket import Service as BucketService # noqa: PLC0415 from rich.progress import ( # noqa: PLC0415 BarColumn, FileSizeColumn, @@ -725,7 +738,7 @@ def update_progress(bytes_uploaded: int, source: Path, platform_bucket_url: str) metadata_dict=metadata_dict, ) - Service().application_run_upload( + _Service().application_run_upload( application_id=application_id, application_version=application_version, metadata=metadata_dict, @@ -784,7 +797,7 @@ def run_submit( # noqa: PLR0913, PLR0917 _abort_if_system_unhealthy() try: - app_version = Service().application_version( + app_version = _Service().application_version( application_id=application_id, application_version=application_version ) except ValueError as e: @@ -826,7 +839,7 @@ def run_submit( # noqa: PLR0913, PLR0917 ) # Submit run with pipeline configuration - application_run = Service().application_run_submit_from_metadata( + application_run = _Service().application_run_submit_from_metadata( application_id=application_id, metadata=metadata_dict, application_version=application_version, @@ -901,7 +914,7 @@ def run_list( # noqa: PLR0913, PLR0917 ) -> None: """List runs.""" try: - runs = Service().application_runs( + runs = _Service().application_runs( limit=limit, tags={tag.strip() for tag in tags.split(",") if tag.strip()} if tags else None, note_regex=note_regex, @@ -963,10 +976,11 @@ def run_describe( ) -> None: """Describe run.""" logger.trace("Describing run with ID '{}'", run_id) + from aignostics_sdk.platform import Service as PlatformService # noqa: PLC0415 try: user_info = PlatformService.get_user_info() - run = Service().application_run(run_id) + run = _Service().application_run(run_id) if format == "json": # Get run details and items, output as JSON run_details = run.details(hide_platform_queue_position=not user_info.is_internal_user) @@ -1003,7 +1017,7 @@ def run_dump_metadata( logger.trace("Dumping custom metadata for run with ID '{}'", run_id) try: - run = Service().application_run(run_id).details() + run = _Service().application_run(run_id).details() custom_metadata = run.custom_metadata if hasattr(run, "custom_metadata") else {} # Output JSON to stdout @@ -1033,7 +1047,7 @@ def run_dump_item_metadata( logger.trace("Dumping custom metadata for item '{}' in run with ID '{}'", external_id, run_id) try: - run = Service().application_run(run_id) + run = _Service().application_run(run_id) # Find the item with the matching external_id in the results item = None @@ -1080,7 +1094,7 @@ def run_cancel( logger.trace("Canceling run with ID '{}'", run_id) try: - Service().application_run_cancel(run_id) + _Service().application_run_cancel(run_id) logger.debug("Canceled run with ID '{}'.", run_id) console.print(f"Run with ID '{run_id}' has been canceled.") except NotFoundException: @@ -1142,7 +1156,7 @@ def run_cancel_by_filter( # noqa: C901, PLR0912, PLR0915 try: # Get runs matching the tag filter first - runs = Service().application_runs( + runs = _Service().application_runs( limit=limit, tags={tag.strip() for tag in tags.split(",") if tag.strip()} if tags else None, ) @@ -1182,7 +1196,7 @@ def run_cancel_by_filter( # noqa: C901, PLR0912, PLR0915 failed_count = 0 for run in filtered_runs: try: - Service().application_run_cancel(run.run_id) + _Service().application_run_cancel(run.run_id) canceled_count += 1 logger.debug(f"Canceled run with ID '{run.run_id}'.") except NotFoundException: @@ -1230,7 +1244,7 @@ def run_update_metadata( console.print(f"[error]Error:[/error] Invalid JSON: {e}") sys.exit(1) - Service().application_run_update_custom_metadata(run_id, custom_metadata) + _Service().application_run_update_custom_metadata(run_id, custom_metadata) logger.debug("Updated custom metadata for run with ID '{}'.", run_id) console.print(f"Successfully updated custom metadata for run with ID '{run_id}'.") except NotFoundException: @@ -1271,7 +1285,7 @@ def run_update_item_metadata( console.print(f"[error]Error:[/error] Invalid JSON: {e}") sys.exit(1) - Service().application_run_update_item_custom_metadata(run_id, external_id, custom_metadata) + _Service().application_run_update_item_custom_metadata(run_id, external_id, custom_metadata) logger.debug("Updated custom metadata for item '{}' in run with ID '{}'.", external_id, run_id) console.print(f"Successfully updated custom metadata for item '{external_id}' in run with ID '{run_id}'.") except NotFoundException: @@ -1350,6 +1364,10 @@ def result_download( # noqa: C901, PLR0913, PLR0915, PLR0917 ] = False, ) -> None: """Download results of a run.""" + from aignostics_sdk.platform import RunState # noqa: PLC0415 + + from ._models import DownloadProgressState # noqa: PLC0415 + logger.trace( "Downloading results for run with ID '{}' to '{}' with options: " "create_subdirectory_for_run={}, create_subdirectory_per_item={}, wait_for_completion={}, qupath_project={}", @@ -1487,7 +1505,7 @@ def update_progress(progress: DownloadProgress) -> None: # noqa: C901 total=float(progress.total_artifact_count) if progress.total_artifact_count else 0.0, ) - destination_directory = Service().application_run_download( + destination_directory = _Service().application_run_download( run_id=run_id, destination_directory=destination_directory, create_subdirectory_for_run=create_subdirectory_for_run, @@ -1526,7 +1544,7 @@ def result_delete( logger.trace("Deleting results for run with ID '{}'", run_id) try: - Service().application_run_delete(run_id) + _Service().application_run_delete(run_id) logger.debug("Deleted run with ID '{}'.", run_id) console.print(f"Results for run with ID '{run_id}' have been deleted.") except NotFoundException: diff --git a/src/aignostics/application/_download.py b/src/aignostics_sdk/application/_download.py similarity index 98% rename from src/aignostics/application/_download.py rename to src/aignostics_sdk/application/_download.py index b902f45da..c2431bd57 100644 --- a/src/aignostics/application/_download.py +++ b/src/aignostics_sdk/application/_download.py @@ -10,8 +10,8 @@ import requests from loguru import logger -from aignostics.platform import ArtifactOutput, ItemOutput, ItemState, Run, generate_signed_url -from aignostics.utils import sanitize_path_component +from aignostics_sdk.platform import ArtifactOutput, ItemOutput, ItemState, Run, generate_signed_url +from aignostics_sdk.utils import sanitize_path_component from ._models import DownloadProgress, DownloadProgressState from ._utils import get_file_extension_for_artifact diff --git a/src/aignostics/application/_models.py b/src/aignostics_sdk/application/_models.py similarity index 98% rename from src/aignostics/application/_models.py rename to src/aignostics_sdk/application/_models.py index a95365abf..de526e680 100644 --- a/src/aignostics/application/_models.py +++ b/src/aignostics_sdk/application/_models.py @@ -6,7 +6,7 @@ from pydantic import BaseModel, computed_field -from aignostics.platform import ItemResult, OutputArtifactElement, RunData +from aignostics_sdk.platform import ItemResult, OutputArtifactElement, RunData has_qupath_extra = find_spec("ijson") if has_qupath_extra: diff --git a/src/aignostics/application/_service.py b/src/aignostics_sdk/application/_service.py similarity index 99% rename from src/aignostics/application/_service.py rename to src/aignostics_sdk/application/_service.py index 315c674f6..651aef927 100644 --- a/src/aignostics/application/_service.py +++ b/src/aignostics_sdk/application/_service.py @@ -14,9 +14,8 @@ import requests from loguru import logger -from aignostics.bucket import Service as BucketService -from aignostics.constants import TEST_APP_APPLICATION_ID -from aignostics.platform import ( +from aignostics_sdk.constants import TEST_APP_APPLICATION_ID +from aignostics_sdk.platform import ( LIST_APPLICATION_RUNS_MAX_PAGE_SIZE, ApiException, Application, @@ -32,9 +31,8 @@ RunOutput, RunState, ) -from aignostics.platform import Service as PlatformService -from aignostics.utils import BaseService, Health, sanitize_path_component -from aignostics.wsi import Service as WSIService +from aignostics_sdk.platform import Service as PlatformService +from aignostics_sdk.utils import BaseService, Health, sanitize_path_component from ._download import ( download_available_items, @@ -361,6 +359,8 @@ def generate_metadata_from_source_directory( # noqa: PLR0913, PLR0917 _ = Service().application_version(application_id, application_version) metadata: list[dict[str, Any]] = [] + from aignostics.wsi import Service as WSIService # noqa: PLC0415 + wsi_service = WSIService() try: @@ -455,6 +455,7 @@ def application_run_upload( # noqa: PLR0913, PLR0917 requests.HTTPError: If the upload fails with an HTTP error. """ import psutil # noqa: PLC0415 + from aignostics.bucket import Service as BucketService # noqa: PLC0415 logger.trace("Uploading files with upload ID '{}'", upload_prefix) app_version = Service().application_version(application_id, application_version=application_version) @@ -864,6 +865,8 @@ def application_run_submit_from_metadata( # noqa: PLR0913, PLR0917 or if due_date not in the future. RuntimeError: If submitting the run failed unexpectedly. """ + from aignostics.bucket import Service as BucketService # noqa: PLC0415 + validate_due_date(due_date) logger.trace("Submitting application run with metadata: {}", metadata) app_version = self.application_version(application_id, application_version=application_version) @@ -1052,7 +1055,7 @@ def application_run_submit( # noqa: PLR0913, PLR0917, PLR0912, C901, PLR0915 # Build scheduling payload for the top-level request field (not custom_metadata) scheduling = None if due_date or deadline: - from aignx.codegen.models import SchedulingRequest # noqa: PLC0415 + from aignostics_sdk._codegen.models import SchedulingRequest # noqa: PLC0415 def _parse_iso(value: str | None) -> datetime | None: if value is None: @@ -1089,7 +1092,7 @@ def _parse_iso(value: str | None) -> datetime | None: # Validate pipeline configuration if present if "pipeline" in sdk_metadata: - from aignostics.platform._sdk_metadata import PipelineConfig # noqa: PLC0415 + from aignostics_sdk.platform._sdk_metadata import PipelineConfig # noqa: PLC0415 try: PipelineConfig.model_validate(sdk_metadata["pipeline"]) diff --git a/src/aignostics/application/_settings.py b/src/aignostics_sdk/application/_settings.py similarity index 65% rename from src/aignostics/application/_settings.py rename to src/aignostics_sdk/application/_settings.py index 93430b4a0..4c593ec31 100644 --- a/src/aignostics/application/_settings.py +++ b/src/aignostics_sdk/application/_settings.py @@ -2,14 +2,14 @@ from pydantic_settings import SettingsConfigDict -from ..utils import OpaqueSettings, __env_file__, __project_name__ # noqa: TID252 +from aignostics_sdk.utils import ENV_PREFIX, OpaqueSettings, __env_file__ class Settings(OpaqueSettings): """Settings.""" model_config = SettingsConfigDict( - env_prefix=f"{__project_name__.upper()}_APPLICATION_", + env_prefix=f"{ENV_PREFIX}_APPLICATION_", extra="ignore", env_file=__env_file__, env_file_encoding="utf-8", diff --git a/src/aignostics/application/_utils.py b/src/aignostics_sdk/application/_utils.py similarity index 96% rename from src/aignostics/application/_utils.py rename to src/aignostics_sdk/application/_utils.py index 9c52d0647..787c512db 100644 --- a/src/aignostics/application/_utils.py +++ b/src/aignostics_sdk/application/_utils.py @@ -7,34 +7,38 @@ 5. Mapping format validation. """ +from __future__ import annotations + import csv import mimetypes import re from datetime import UTC, datetime from enum import StrEnum -from pathlib import Path -from typing import Any, Protocol +from pathlib import Path # noqa: TC003 +from typing import TYPE_CHECKING, Any, Protocol import humanize from loguru import logger -from aignostics.constants import ( +from aignostics_sdk.constants import ( HETA_APPLICATION_ID, TEST_APP_APPLICATION_ID, WSI_SUPPORTED_FILE_EXTENSIONS, WSI_SUPPORTED_FILE_EXTENSIONS_TEST_APP, ) -from aignostics.platform import ( - InputArtifactData, - ItemState, - OutputArtifactData, - OutputArtifactElement, - Run, - RunData, - RunItemStatistics, - RunState, -) -from aignostics.utils import console +from aignostics_sdk.utils import console + +if TYPE_CHECKING: + from aignostics_sdk.platform import ( + InputArtifactData, + ItemState, + OutputArtifactData, + OutputArtifactElement, + Run, + RunData, + RunItemStatistics, + RunState, + ) RUN_FAILED_MESSAGE = "Failed to get status for run with ID '%s'" @@ -224,6 +228,8 @@ def is_not_terminated_with_deadline_exceeded( None if run is terminated, no deadline set, or invalid deadline format. """ # If run is already terminated, return None (deadline is no longer relevant) + from aignostics_sdk.platform import RunState # noqa: PLC0415 + if run_state == RunState.TERMINATED: return None @@ -279,6 +285,8 @@ def _format_status_string(state: RunState | ItemState, termination_reason: str | Returns: str: Formatted status string """ + from aignostics_sdk.platform import ItemState, RunState # noqa: PLC0415 + if state.value in {RunState.TERMINATED, ItemState.TERMINATED} and termination_reason: return f"{state.value} ({termination_reason})" return f"{state.value}" @@ -524,6 +532,8 @@ def application_run_status_to_str( Raises: RuntimeError: If the status is invalid or unknown """ + from aignostics_sdk.platform import RunState # noqa: PLC0415 + status_mapping = { RunState.PENDING: "pending", RunState.PROCESSING: "processing", @@ -547,6 +557,8 @@ def get_mime_type_for_artifact(artifact: OutputArtifactData | InputArtifactData Returns: str: The MIME type of the artifact. """ + from aignostics_sdk.platform import InputArtifactData, OutputArtifactData # noqa: PLC0415 + if isinstance(artifact, InputArtifactData): return str(artifact.mime_type) if isinstance(artifact, OutputArtifactData): diff --git a/src/aignostics_sdk/cli.py b/src/aignostics_sdk/cli.py new file mode 100644 index 000000000..ff25fd2f1 --- /dev/null +++ b/src/aignostics_sdk/cli.py @@ -0,0 +1,38 @@ +"""CLI entry point for aignostics-sdk slim distribution. + +This module wires the platform sub-commands (user, sdk) and the domain +sub-commands (application, system) into a trimmed Typer application. +It deliberately does *not* call ``prepare_cli`` because that function uses +``locate_implementations`` to deep-scan the ``aignostics`` package and would +pull in the heavy domain modules (wsi, dataset, bucket, qupath, …) that are +absent from this slim distribution. +""" + +from __future__ import annotations + +import typer + +from aignostics_sdk.application._cli import cli as application_cli +from aignostics_sdk.platform import cli_sdk, cli_user +from aignostics_sdk.system._cli import cli as system_cli +from aignostics_sdk.utils import __python_version__, __version__ +from aignostics_sdk.utils._cli import _add_epilog_recursively, _no_args_is_help_recursively + +_EPILOG = f"🔬 aignostics-sdk v{__version__} - built with love in Berlin 🐻 // Python v{__python_version__}" + +cli = typer.Typer( + help="Command Line Interface (CLI) of aignostics-sdk providing access to Aignostics Platform.", +) + +cli.add_typer(cli_user) +cli.add_typer(cli_sdk) +cli.add_typer(application_cli) +cli.add_typer(system_cli) + +# Apply epilog and no-args-is-help without the auto-discovery step that +# prepare_cli performs via locate_implementations(typer.Typer). +_add_epilog_recursively(cli, _EPILOG) +_no_args_is_help_recursively(cli) + +if __name__ == "__main__": # pragma: no cover + cli() diff --git a/src/aignostics_sdk/constants.py b/src/aignostics_sdk/constants.py new file mode 100644 index 000000000..0a291e7de --- /dev/null +++ b/src/aignostics_sdk/constants.py @@ -0,0 +1,17 @@ +"""Static configuration of Aignostics SDK (slim package).""" + +# Organizations with internal/advanced access (e.g., platform-wide queue visibility, GPU config) +INTERNAL_ORGS = {"aignostics", "pre-alpha-org", "lmu", "charite"} + +# Application IDs +HETA_APPLICATION_ID = "he-tme" +TEST_APP_APPLICATION_ID = "test-app" + +# WSI supported file extensions +WSI_SUPPORTED_FILE_EXTENSIONS = {".dcm", ".tiff", ".tif", ".svs"} +WSI_SUPPORTED_FILE_EXTENSIONS_TEST_APP = {".tiff"} + +# API versions (keyed by version name, value is the SDK version string) +from aignostics_sdk.utils._constants import __version__ # noqa: E402 + +API_VERSIONS: dict[str, str] = {"v1": __version__} diff --git a/src/aignostics/platform/CLAUDE.md b/src/aignostics_sdk/platform/CLAUDE.md similarity index 100% rename from src/aignostics/platform/CLAUDE.md rename to src/aignostics_sdk/platform/CLAUDE.md diff --git a/src/aignostics_sdk/platform/__init__.py b/src/aignostics_sdk/platform/__init__.py new file mode 100644 index 000000000..7df5544f4 --- /dev/null +++ b/src/aignostics_sdk/platform/__init__.py @@ -0,0 +1,260 @@ +"""This module provides the low-level client interface for interacting with the API of the Aignostics Platform. + +The primary class in this module is the `Client` class, serving as the entry point +for authenticated API operations. Login and token management are handled +automatically. + +Further operations are encapsulated in the `Service` class, which provides methods +for manual login, logout and getting information about the authenticated user. + +Higher level abstractions are provided in the application module. +""" + +from __future__ import annotations + +import importlib +from typing import TYPE_CHECKING + +from ._cli import cli_sdk, cli_user +from ._client import Client +from ._constants import ( + API_ROOT_DEV, + API_ROOT_PRODUCTION, + API_ROOT_STAGING, + API_ROOT_TEST, + AUDIENCE_DEV, + AUDIENCE_PRODUCTION, + AUDIENCE_STAGING, + AUDIENCE_TEST, + AUTHORIZATION_BASE_URL_DEV, + AUTHORIZATION_BASE_URL_PRODUCTION, + AUTHORIZATION_BASE_URL_STAGING, + AUTHORIZATION_BASE_URL_TEST, + CLIENT_ID_INTERACTIVE_DEV, + CLIENT_ID_INTERACTIVE_PRODUCTION, + CLIENT_ID_INTERACTIVE_STAGING, + CLIENT_ID_INTERACTIVE_TEST, + DEFAULT_CPU_PROVISIONING_MODE, + DEFAULT_FLEX_START_MAX_RUN_DURATION_MINUTES, + DEFAULT_GPU_PROVISIONING_MODE, + DEFAULT_GPU_TYPE, + DEFAULT_MAX_GPUS_PER_SLIDE, + DEFAULT_NODE_ACQUISITION_TIMEOUT_MINUTES, + DEVICE_URL_DEV, + DEVICE_URL_PRODUCTION, + DEVICE_URL_STAGING, + DEVICE_URL_TEST, + JWS_JSON_URL_DEV, + JWS_JSON_URL_PRODUCTION, + JWS_JSON_URL_STAGING, + JWS_JSON_URL_TEST, + REDIRECT_URI_DEV, + REDIRECT_URI_PRODUCTION, + REDIRECT_URI_STAGING, + REDIRECT_URI_TEST, + STATUS_PAGE_URL_DEV, + STATUS_PAGE_URL_PRODUCTION, + STATUS_PAGE_URL_STAGING, + STATUS_PAGE_URL_TEST, + TOKEN_URL_DEV, + TOKEN_URL_PRODUCTION, + TOKEN_URL_STAGING, + TOKEN_URL_TEST, +) +from ._messages import AUTHENTICATION_FAILED, NOT_YET_IMPLEMENTED, UNKNOWN_ENDPOINT_URL +from ._sdk_metadata import ( + PipelineConfig, + RunSdkMetadata, + SchedulingMetadata, +) +from ._settings import Settings, settings +from ._utils import ( + calculate_file_crc32c, + download_file, + generate_signed_url, + get_mime_type_for_artifact, + mime_type_to_file_ending, +) + +if TYPE_CHECKING: + from aignostics_sdk._codegen.exceptions import ApiException, ForbiddenException, NotFoundException + from aignostics_sdk._codegen.models import ApplicationReadResponse as Application + from aignostics_sdk._codegen.models import ApplicationReadShortResponse as ApplicationSummary + from aignostics_sdk._codegen.models import ( + ArtifactOutput, + ItemOutput, + ItemState, + ItemTerminationReason, + RunItemStatistics, + RunOutput, + RunState, + RunTerminationReason, + ) + from aignostics_sdk._codegen.models import InputArtifact as InputArtifactData + from aignostics_sdk._codegen.models import InputArtifactCreationRequest as InputArtifact + from aignostics_sdk._codegen.models import ItemCreationRequest as InputItem + from aignostics_sdk._codegen.models import ItemResultReadResponse as ItemResult + from aignostics_sdk._codegen.models import MeReadResponse as Me + from aignostics_sdk._codegen.models import OrganizationReadResponse as Organization + from aignostics_sdk._codegen.models import OutputArtifact as OutputArtifactData + from aignostics_sdk._codegen.models import OutputArtifactResultReadResponse as OutputArtifactElement + from aignostics_sdk._codegen.models import RunReadResponse as RunData + from aignostics_sdk._codegen.models import UserReadResponse as User + from aignostics_sdk._codegen.models import VersionReadResponse as ApplicationVersion + + from ._service import Service, TokenInfo, UserInfo + from .resources.applications import ApplicationVersionDocument, Documents + from .resources.runs import LIST_APPLICATION_RUNS_MAX_PAGE_SIZE, LIST_APPLICATION_RUNS_MIN_PAGE_SIZE, Artifact, Run + +# Lazy export map: public_name -> (module_path, original_name_in_module) +_LAZY: dict[str, tuple[str, str]] = { + # service types (loaded lazily because _service.py imports codegen at module level) + "Service": ("aignostics_sdk.platform._service", "Service"), + "TokenInfo": ("aignostics_sdk.platform._service", "TokenInfo"), + "UserInfo": ("aignostics_sdk.platform._service", "UserInfo"), + # exceptions + "ApiException": ("aignostics_sdk._codegen.exceptions", "ApiException"), + "ForbiddenException": ("aignostics_sdk._codegen.exceptions", "ForbiddenException"), + "NotFoundException": ("aignostics_sdk._codegen.exceptions", "NotFoundException"), + # codegen models + "Application": ("aignostics_sdk._codegen.models", "ApplicationReadResponse"), + "ApplicationSummary": ("aignostics_sdk._codegen.models", "ApplicationReadShortResponse"), + "ApplicationVersion": ("aignostics_sdk._codegen.models", "VersionReadResponse"), + "ArtifactOutput": ("aignostics_sdk._codegen.models", "ArtifactOutput"), + "InputArtifact": ("aignostics_sdk._codegen.models", "InputArtifactCreationRequest"), + "InputArtifactData": ("aignostics_sdk._codegen.models", "InputArtifact"), + "InputItem": ("aignostics_sdk._codegen.models", "ItemCreationRequest"), + "ItemOutput": ("aignostics_sdk._codegen.models", "ItemOutput"), + "ItemResult": ("aignostics_sdk._codegen.models", "ItemResultReadResponse"), + "ItemState": ("aignostics_sdk._codegen.models", "ItemState"), + "ItemTerminationReason": ("aignostics_sdk._codegen.models", "ItemTerminationReason"), + "Me": ("aignostics_sdk._codegen.models", "MeReadResponse"), + "Organization": ("aignostics_sdk._codegen.models", "OrganizationReadResponse"), + "OutputArtifactData": ("aignostics_sdk._codegen.models", "OutputArtifact"), + "OutputArtifactElement": ("aignostics_sdk._codegen.models", "OutputArtifactResultReadResponse"), + "RunData": ("aignostics_sdk._codegen.models", "RunReadResponse"), + "RunItemStatistics": ("aignostics_sdk._codegen.models", "RunItemStatistics"), + "RunOutput": ("aignostics_sdk._codegen.models", "RunOutput"), + "RunState": ("aignostics_sdk._codegen.models", "RunState"), + "RunTerminationReason": ("aignostics_sdk._codegen.models", "RunTerminationReason"), + "User": ("aignostics_sdk._codegen.models", "UserReadResponse"), + # resource types + "Artifact": ("aignostics_sdk.platform.resources.runs", "Artifact"), + "Run": ("aignostics_sdk.platform.resources.runs", "Run"), + "LIST_APPLICATION_RUNS_MAX_PAGE_SIZE": ( + "aignostics_sdk.platform.resources.runs", + "LIST_APPLICATION_RUNS_MAX_PAGE_SIZE", + ), + "LIST_APPLICATION_RUNS_MIN_PAGE_SIZE": ( + "aignostics_sdk.platform.resources.runs", + "LIST_APPLICATION_RUNS_MIN_PAGE_SIZE", + ), + "ApplicationVersionDocument": ("aignostics_sdk.platform.resources.applications", "ApplicationVersionDocument"), + "Documents": ("aignostics_sdk.platform.resources.applications", "Documents"), +} + + +def __getattr__(name: str) -> object: + if name in _LAZY: + module_path, attr_name = _LAZY[name] + mod = importlib.import_module(module_path) + obj = getattr(mod, attr_name) + globals()[name] = obj # cache so __getattr__ is not called again + return obj + msg = f"module {__name__!r} has no attribute {name!r}" + raise AttributeError(msg) + + +__all__ = [ + "API_ROOT_DEV", + "API_ROOT_PRODUCTION", + "API_ROOT_STAGING", + "API_ROOT_TEST", + "AUDIENCE_DEV", + "AUDIENCE_PRODUCTION", + "AUDIENCE_STAGING", + "AUDIENCE_TEST", + "AUTHENTICATION_FAILED", + "AUTHORIZATION_BASE_URL_DEV", + "AUTHORIZATION_BASE_URL_PRODUCTION", + "AUTHORIZATION_BASE_URL_STAGING", + "AUTHORIZATION_BASE_URL_TEST", + "CLIENT_ID_INTERACTIVE_DEV", + "CLIENT_ID_INTERACTIVE_PRODUCTION", + "CLIENT_ID_INTERACTIVE_STAGING", + "CLIENT_ID_INTERACTIVE_TEST", + "DEFAULT_CPU_PROVISIONING_MODE", + "DEFAULT_FLEX_START_MAX_RUN_DURATION_MINUTES", + "DEFAULT_GPU_PROVISIONING_MODE", + "DEFAULT_GPU_TYPE", + "DEFAULT_MAX_GPUS_PER_SLIDE", + "DEFAULT_NODE_ACQUISITION_TIMEOUT_MINUTES", + "DEVICE_URL_DEV", + "DEVICE_URL_PRODUCTION", + "DEVICE_URL_STAGING", + "DEVICE_URL_TEST", + "JWS_JSON_URL_DEV", + "JWS_JSON_URL_PRODUCTION", + "JWS_JSON_URL_STAGING", + "JWS_JSON_URL_TEST", + "LIST_APPLICATION_RUNS_MAX_PAGE_SIZE", + "LIST_APPLICATION_RUNS_MIN_PAGE_SIZE", + "NOT_YET_IMPLEMENTED", + "REDIRECT_URI_DEV", + "REDIRECT_URI_PRODUCTION", + "REDIRECT_URI_STAGING", + "REDIRECT_URI_TEST", + "STATUS_PAGE_URL_DEV", + "STATUS_PAGE_URL_PRODUCTION", + "STATUS_PAGE_URL_STAGING", + "STATUS_PAGE_URL_TEST", + "TOKEN_URL_DEV", + "TOKEN_URL_PRODUCTION", + "TOKEN_URL_STAGING", + "TOKEN_URL_TEST", + "UNKNOWN_ENDPOINT_URL", + "ApiException", + "Application", + "ApplicationSummary", + "ApplicationVersion", + "ApplicationVersionDocument", + "Artifact", + "ArtifactOutput", + "Client", + "Documents", + "ForbiddenException", + "InputArtifact", + "InputArtifactData", + "InputItem", + "ItemOutput", + "ItemResult", + "ItemState", + "ItemTerminationReason", + "Me", + "NotFoundException", + "Organization", + "OutputArtifactData", + "OutputArtifactElement", + "PipelineConfig", + "Run", + "RunData", + "RunItemStatistics", + "RunOutput", + "RunSdkMetadata", + "RunState", + "RunTerminationReason", + "SchedulingMetadata", + "Service", + "Settings", + "TokenInfo", + "User", + "UserInfo", + "calculate_file_crc32c", + "cli_sdk", + "cli_user", + "download_file", + "generate_signed_url", + "get_mime_type_for_artifact", + "mime_type_to_file_ending", + "settings", +] diff --git a/src/aignostics/platform/_api.py b/src/aignostics_sdk/platform/_api.py similarity index 94% rename from src/aignostics/platform/_api.py rename to src/aignostics_sdk/platform/_api.py index ab250a55a..669d2cce2 100644 --- a/src/aignostics/platform/_api.py +++ b/src/aignostics_sdk/platform/_api.py @@ -11,15 +11,16 @@ from collections.abc import Callable -from aignx.codegen.api.public_api import PublicApi -from aignx.codegen.api_client import ApiClient -from aignx.codegen.configuration import AuthSettings, Configuration -from aignx.codegen.exceptions import ServiceException from loguru import logger from tenacity import RetryCallState from urllib3.exceptions import IncompleteRead, PoolError, ProtocolError, ProxyError from urllib3.exceptions import TimeoutError as Urllib3TimeoutError +from aignostics_sdk._codegen.api.public_api import PublicApi +from aignostics_sdk._codegen.api_client import ApiClient +from aignostics_sdk._codegen.configuration import AuthSettings, Configuration +from aignostics_sdk._codegen.exceptions import ServiceException + RETRYABLE_EXCEPTIONS = ( ServiceException, Urllib3TimeoutError, diff --git a/src/aignostics/platform/_authentication.py b/src/aignostics_sdk/platform/_authentication.py similarity index 98% rename from src/aignostics/platform/_authentication.py rename to src/aignostics_sdk/platform/_authentication.py index 90e695816..7ee2227e4 100644 --- a/src/aignostics/platform/_authentication.py +++ b/src/aignostics_sdk/platform/_authentication.py @@ -23,14 +23,13 @@ wait_exponential_jitter, ) -from aignostics.platform._api import _log_retry_attempt -from aignostics.platform._messages import ( +from aignostics_sdk.platform._messages import ( AUTHENTICATION_FAILED, AUTHENTICATION_FAILED_ACCESS_TOKEN_FROM_REFRESH_TOKEN, AUTHENTICATION_FAILED_TOKEN_VERIFICATION, INVALID_REDIRECT_URI, ) -from aignostics.platform._settings import settings +from aignostics_sdk.platform._settings import settings CALLBACK_PORT_RETRY_COUNT = 20 CALLBACK_PORT_BACKOFF_DELAY = 1 @@ -194,6 +193,8 @@ def verify_and_decode_token(token: str) -> dict[str, str]: Raises: RuntimeError: If token verification or decoding fails. """ + from aignostics_sdk.platform._api import _log_retry_attempt # noqa: PLC0415 + return Retrying( # We are not using Tenacity annotations as settings can change at runtime retry=retry_if_exception( # Have to unpack wrapped exception lambda e: isinstance(e, RuntimeError) and isinstance(e.__cause__, jwt.PyJWKClientConnectionError) @@ -526,6 +527,8 @@ def _access_token_from_refresh_token(refresh_token: SecretStr) -> str: Raises: RuntimeError: If token exchange fails. Message indicates if "Client Error". """ + from aignostics_sdk.platform._api import _log_retry_attempt # noqa: PLC0415 + return Retrying( # We are not using Tenacity annotations as settings can change at runtime retry=retry_if_exception(_is_not_client_or_key_error), stop=stop_after_attempt(settings().auth_retry_attempts), diff --git a/src/aignostics/platform/_cli.py b/src/aignostics_sdk/platform/_cli.py similarity index 94% rename from src/aignostics/platform/_cli.py rename to src/aignostics_sdk/platform/_cli.py index 649783601..9a9e422a2 100644 --- a/src/aignostics/platform/_cli.py +++ b/src/aignostics_sdk/platform/_cli.py @@ -1,16 +1,20 @@ """CLI of platform module.""" +from __future__ import annotations + import json import sys -from typing import Annotated +from typing import TYPE_CHECKING, Annotated import typer from loguru import logger -from aignostics.utils import console +from aignostics_sdk.utils import console from ._sdk_metadata import get_item_sdk_metadata_json_schema, get_run_sdk_metadata_json_schema -from ._service import Service + +if TYPE_CHECKING: + from ._service import Service cli_user = typer.Typer(name="user", help="User operations such as login, logout and whoami.") @@ -23,6 +27,8 @@ def _get_service() -> Service: Returns: Service: The service instance. """ + from ._service import Service # noqa: PLC0415 + global service # noqa: PLW0603 if service is None: service = Service() diff --git a/src/aignostics/platform/_client.py b/src/aignostics_sdk/platform/_client.py similarity index 87% rename from src/aignostics/platform/_client.py rename to src/aignostics_sdk/platform/_client.py index 5968c0e79..2e602cea8 100644 --- a/src/aignostics/platform/_client.py +++ b/src/aignostics_sdk/platform/_client.py @@ -1,14 +1,11 @@ +from __future__ import annotations + import os -from collections.abc import Callable -from typing import ClassVar +from collections.abc import Callable # noqa: TC003 +from typing import TYPE_CHECKING, ClassVar from urllib.request import getproxies import semver -from aignx.codegen.api_client import ApiClient -from aignx.codegen.exceptions import NotFoundException -from aignx.codegen.models import ApplicationReadResponse as Application -from aignx.codegen.models import MeReadResponse as Me -from aignx.codegen.models import VersionReadResponse as ApplicationVersion from loguru import logger from tenacity import ( Retrying, @@ -17,20 +14,22 @@ wait_exponential_jitter, ) -from aignostics.platform._api import ( - RETRYABLE_EXCEPTIONS, - _AuthenticatedApi, - _log_retry_attempt, - _OAuth2TokenProviderConfiguration, -) -from aignostics.platform._authentication import get_token -from aignostics.platform._operation_cache import cached_operation -from aignostics.platform.resources.applications import Applications, Versions -from aignostics.platform.resources.runs import Run, Runs -from aignostics.utils import user_agent +from aignostics_sdk.platform._authentication import get_token +from aignostics_sdk.platform._operation_cache import cached_operation +from aignostics_sdk.utils import user_agent from ._settings import settings +if TYPE_CHECKING: + from aignostics_sdk._codegen.models import ApplicationReadResponse as Application + from aignostics_sdk._codegen.models import MeReadResponse as Me + from aignostics_sdk._codegen.models import VersionReadResponse as ApplicationVersion + from aignostics_sdk.platform._api import ( + _AuthenticatedApi, + ) + from aignostics_sdk.platform.resources.applications import Applications, Versions + from aignostics_sdk.platform.resources.runs import Run, Runs + # Safety bound for the external token-provider cache. In normal usage callers # reuse a single provider reference, so this limit should never be reached. _MAX_EXTERNAL_CLIENTS = 16 @@ -69,6 +68,9 @@ def __init__(self, cache_token: bool = True, token_provider: Callable[[], str] | Sets up resource accessors for applications, versions, and runs. """ + from aignostics_sdk.platform.resources.applications import Applications, Versions # noqa: PLC0415 + from aignostics_sdk.platform.resources.runs import Runs # noqa: PLC0415 + try: logger.trace( "Initializing client with cache_token={}, token_provider={}", @@ -103,6 +105,7 @@ def me(self, nocache: bool = False) -> Me: Raises: aignx.codegen.exceptions.ApiException: If the API call fails. """ + from aignostics_sdk.platform._api import RETRYABLE_EXCEPTIONS, _log_retry_attempt # noqa: PLC0415 @cached_operation(ttl=settings().me_cache_ttl, token_provider=self._api.token_provider) def me_with_retry() -> Me: @@ -137,6 +140,7 @@ def application(self, application_id: str, nocache: bool = False) -> Application NotFoundException: If the application with the given ID is not found. aignx.codegen.exceptions.ApiException: If the API call fails. """ + from aignostics_sdk.platform._api import RETRYABLE_EXCEPTIONS, _log_retry_attempt # noqa: PLC0415 @cached_operation(ttl=settings().application_cache_ttl, token_provider=self._api.token_provider) def application_with_retry(application_id: str) -> Application: @@ -180,6 +184,10 @@ def application_version( ValueError: If the version is not valid semver. aignx.codegen.exceptions.ApiException: If the API call fails. """ + from aignostics_sdk._codegen.exceptions import NotFoundException # noqa: PLC0415 + from aignostics_sdk.platform._api import RETRYABLE_EXCEPTIONS, _log_retry_attempt # noqa: PLC0415 + from aignostics_sdk.platform.resources.applications import Versions # noqa: PLC0415 + # Handle version resolution and validation first (not retried) if version_number is None: # Get the latest version - this call already has its own retry logic in Versions @@ -226,6 +234,8 @@ def run(self, run_id: str) -> Run: Returns: Run: The run object. """ + from aignostics_sdk.platform.resources.runs import Run # noqa: PLC0415 + return Run(self._api, run_id) @staticmethod @@ -248,6 +258,12 @@ def get_api_client(cache_token: bool = True, token_provider: Callable[[], str] | Raises: RuntimeError: If authentication fails. """ + from aignostics_sdk._codegen.api_client import ApiClient # noqa: PLC0415 + from aignostics_sdk.platform._api import ( # noqa: PLC0415 + _AuthenticatedApi, + _OAuth2TokenProviderConfiguration, + ) + # Check singleton caches first if token_provider is not None: if token_provider in Client._api_client_external: diff --git a/src/aignostics/platform/_constants.py b/src/aignostics_sdk/platform/_constants.py similarity index 100% rename from src/aignostics/platform/_constants.py rename to src/aignostics_sdk/platform/_constants.py diff --git a/src/aignostics/platform/_messages.py b/src/aignostics_sdk/platform/_messages.py similarity index 100% rename from src/aignostics/platform/_messages.py rename to src/aignostics_sdk/platform/_messages.py diff --git a/src/aignostics/platform/_operation_cache.py b/src/aignostics_sdk/platform/_operation_cache.py similarity index 100% rename from src/aignostics/platform/_operation_cache.py rename to src/aignostics_sdk/platform/_operation_cache.py diff --git a/src/aignostics/platform/_sdk_metadata.py b/src/aignostics_sdk/platform/_sdk_metadata.py similarity index 99% rename from src/aignostics/platform/_sdk_metadata.py rename to src/aignostics_sdk/platform/_sdk_metadata.py index f980bc4fd..bd06a6530 100644 --- a/src/aignostics/platform/_sdk_metadata.py +++ b/src/aignostics_sdk/platform/_sdk_metadata.py @@ -13,7 +13,7 @@ from loguru import logger from pydantic import BaseModel, Field, ValidationError, field_validator, model_validator -from aignostics.utils import user_agent +from aignostics_sdk.utils import user_agent from ._constants import ( DEFAULT_CPU_PROVISIONING_MODE, @@ -293,7 +293,7 @@ def build_run_sdk_metadata(existing_metadata: dict[str, Any] | None = None) -> d dict[str, Any]: Dictionary containing SDK metadata including user agent, user information, and optionally CI information (GitHub workflow and pytest test context). """ - from aignostics.platform._client import Client # noqa: PLC0415 + from aignostics_sdk.platform._client import Client # noqa: PLC0415 submission_initiator = "user" # who/what initiated the run (user, test, bridge) submission_interface = "script" # how the SDK was accessed (script, cli, launchpad) diff --git a/src/aignostics/platform/_service.py b/src/aignostics_sdk/platform/_service.py similarity index 95% rename from src/aignostics/platform/_service.py rename to src/aignostics_sdk/platform/_service.py index 50bb409c5..1728e6f82 100644 --- a/src/aignostics/platform/_service.py +++ b/src/aignostics_sdk/platform/_service.py @@ -1,25 +1,29 @@ """Service of the platform module.""" +from __future__ import annotations + import json import time from functools import cached_property from http import HTTPStatus -from typing import Any +from typing import TYPE_CHECKING, Any import httpx -from aignx.codegen.models import MeReadResponse as Me -from aignx.codegen.models import OrganizationReadResponse as Organization -from aignx.codegen.models import UserReadResponse as User from loguru import logger from pydantic import BaseModel, computed_field -from aignostics.constants import INTERNAL_ORGS -from aignostics.utils import BaseService, Health, user_agent +from aignostics_sdk._codegen.models import OrganizationReadResponse as Organization # noqa: TC001 +from aignostics_sdk._codegen.models import UserReadResponse as User # noqa: TC001 +from aignostics_sdk.constants import INTERNAL_ORGS +from aignostics_sdk.utils import BaseService, Health, user_agent from ._authentication import get_token, remove_cached_token, verify_and_decode_token from ._client import Client from ._settings import Settings +if TYPE_CHECKING: + from aignostics_sdk._codegen.models import MeReadResponse as Me + class TokenInfo(BaseModel): """Class to store token information.""" @@ -45,7 +49,7 @@ def expires_in(self) -> int: return self.expires_at - int(time.time()) @classmethod - def from_claims(cls, claims: dict[str, Any]) -> "TokenInfo": + def from_claims(cls, claims: dict[str, Any]) -> TokenInfo: """Create TokenInfo from JWT claims. Args: @@ -77,7 +81,7 @@ class UserInfo(BaseModel): organization: Organization @classmethod - def from_claims_and_me(cls, claims: dict[str, Any], me: Me) -> "UserInfo": + def from_claims_and_me(cls, claims: dict[str, Any], me: Me) -> UserInfo: """Create UserInfo from JWT claims and optional auth0 userinfo. Args: diff --git a/src/aignostics/platform/_settings.py b/src/aignostics_sdk/platform/_settings.py similarity index 99% rename from src/aignostics/platform/_settings.py rename to src/aignostics_sdk/platform/_settings.py index 5edfccb4c..711958189 100644 --- a/src/aignostics/platform/_settings.py +++ b/src/aignostics_sdk/platform/_settings.py @@ -19,7 +19,7 @@ ) from pydantic_settings import BaseSettings, SettingsConfigDict -from aignostics.utils import OpaqueSettings, __project_name__, load_settings +from aignostics_sdk.utils import ENV_PREFIX, OpaqueSettings, __project_name__, load_settings from ._constants import ( API_ROOT_DEV, @@ -152,9 +152,9 @@ class Settings(OpaqueSettings): """ model_config = SettingsConfigDict( - env_prefix=f"{__project_name__.upper()}_", + env_prefix=f"{ENV_PREFIX}_", env_file=( - os.getenv(f"{__project_name__.upper()}_ENV_FILE", Path.home() / f".{__project_name__}/.env"), + os.getenv(f"{ENV_PREFIX}_ENV_FILE", Path.home() / f".{__project_name__}/.env"), Path(".env"), ), env_file_encoding="utf-8", diff --git a/src/aignostics/platform/_utils.py b/src/aignostics_sdk/platform/_utils.py similarity index 89% rename from src/aignostics/platform/_utils.py rename to src/aignostics_sdk/platform/_utils.py index df6fb3189..7c44d6c75 100644 --- a/src/aignostics/platform/_utils.py +++ b/src/aignostics_sdk/platform/_utils.py @@ -8,24 +8,31 @@ interactions to support the main client functionality. """ +from __future__ import annotations + import base64 import contextlib import datetime import re import tempfile import typing as t -from collections.abc import Generator -from pathlib import Path -from typing import IO, Any +from typing import TYPE_CHECKING, Any import crc32c import requests -from aignx.codegen.models import InputArtifact as InputArtifactData -from aignx.codegen.models import OutputArtifact as OutputArtifactData -from aignx.codegen.models import OutputArtifactResultReadResponse as OutputArtifactElement from loguru import logger from tqdm.auto import tqdm +if TYPE_CHECKING: + from collections.abc import Generator + from pathlib import Path + from typing import IO + + from aignostics_sdk._codegen.models import InputArtifact as InputArtifactData + from aignostics_sdk._codegen.models import OutputArtifact as OutputArtifactData + from aignostics_sdk._codegen.models import OutputArtifactResultReadResponse as OutputArtifactElement + + EIGHT_MB = 8_388_608 SIGNED_DOWNLOAD_URL_EXPIRES_SECONDS_DEFAULT = 6 * 60 * 60 # 6 hours @@ -85,7 +92,7 @@ def mime_type_to_file_ending(mime_type: str) -> str: raise ValueError(msg) -def get_mime_type_for_artifact(artifact: OutputArtifactData | InputArtifactData | OutputArtifactElement) -> str: +def get_mime_type_for_artifact(artifact: InputArtifactData | OutputArtifactData | OutputArtifactElement) -> str: """Get the MIME type for a given artifact. Args: @@ -94,9 +101,12 @@ def get_mime_type_for_artifact(artifact: OutputArtifactData | InputArtifactData Returns: str: The MIME type of the artifact. """ - if isinstance(artifact, InputArtifactData): + from aignostics_sdk._codegen.models import InputArtifact as _InputArtifactData # noqa: PLC0415 + from aignostics_sdk._codegen.models import OutputArtifact as _OutputArtifactData # noqa: PLC0415 + + if isinstance(artifact, _InputArtifactData): return str(artifact.mime_type) - if isinstance(artifact, OutputArtifactData): + if isinstance(artifact, _OutputArtifactData): return str(artifact.mime_type) metadata = artifact.metadata or {} return str(metadata.get("media_type", metadata.get("mime_type", "application/octet-stream"))) diff --git a/src/aignostics/platform/resources/__init__.py b/src/aignostics_sdk/platform/resources/__init__.py similarity index 100% rename from src/aignostics/platform/resources/__init__.py rename to src/aignostics_sdk/platform/resources/__init__.py diff --git a/src/aignostics/platform/resources/applications.py b/src/aignostics_sdk/platform/resources/applications.py similarity index 97% rename from src/aignostics/platform/resources/applications.py rename to src/aignostics_sdk/platform/resources/applications.py index 827147d12..e9ab632de 100644 --- a/src/aignostics/platform/resources/applications.py +++ b/src/aignostics_sdk/platform/resources/applications.py @@ -16,12 +16,6 @@ import requests import semver -from aignx.codegen.exceptions import NotFoundException, ServiceException -from aignx.codegen.models import ApplicationReadResponse as Application -from aignx.codegen.models import ApplicationReadShortResponse as ApplicationSummary -from aignx.codegen.models import ApplicationVersion as VersionTuple -from aignx.codegen.models import VersionDocumentResponse as VersionDocumentData -from aignx.codegen.models import VersionReadResponse as ApplicationVersion from pydantic import BaseModel, ConfigDict from tenacity import ( Retrying, @@ -30,17 +24,23 @@ wait_exponential_jitter, ) -from aignostics.platform._api import ( +from aignostics_sdk._codegen.exceptions import NotFoundException, ServiceException +from aignostics_sdk._codegen.models import ApplicationReadResponse as Application +from aignostics_sdk._codegen.models import ApplicationReadShortResponse as ApplicationSummary +from aignostics_sdk._codegen.models import ApplicationVersion as VersionTuple +from aignostics_sdk._codegen.models import VersionDocumentResponse as VersionDocumentData +from aignostics_sdk._codegen.models import VersionReadResponse as ApplicationVersion +from aignostics_sdk.platform._api import ( RETRYABLE_EXCEPTIONS, _AuthenticatedApi, _AuthenticatedResource, _log_retry_attempt, ) -from aignostics.platform._authentication import get_token -from aignostics.platform._operation_cache import cached_operation -from aignostics.platform._settings import settings -from aignostics.platform.resources.utils import paginate -from aignostics.utils import user_agent +from aignostics_sdk.platform._authentication import get_token +from aignostics_sdk.platform._operation_cache import cached_operation +from aignostics_sdk.platform._settings import settings +from aignostics_sdk.platform.resources.utils import paginate +from aignostics_sdk.utils import user_agent _DOCUMENT_DOWNLOAD_CHUNK_SIZE = 1024 * 1024 # 1 MB diff --git a/src/aignostics/platform/resources/runs.py b/src/aignostics_sdk/platform/resources/runs.py similarity index 97% rename from src/aignostics/platform/resources/runs.py rename to src/aignostics_sdk/platform/resources/runs.py index 7a861c4b4..3f5833afa 100644 --- a/src/aignostics/platform/resources/runs.py +++ b/src/aignostics_sdk/platform/resources/runs.py @@ -14,8 +14,20 @@ from typing import Any, cast import requests -from aignx.codegen.exceptions import ApiException, NotFoundException, ServiceException -from aignx.codegen.models import ( +from jsonschema.exceptions import ValidationError +from jsonschema.validators import validate +from loguru import logger +from sentry_sdk import metrics +from tenacity import ( + Retrying, + retry_if_exception_type, + stop_after_attempt, + stop_after_delay, + wait_exponential_jitter, +) + +from aignostics_sdk._codegen.exceptions import ApiException, NotFoundException, ServiceException +from aignostics_sdk._codegen.models import ( ArtifactOutput, CustomMetadataUpdateRequest, ItemCreationRequest, @@ -28,52 +40,40 @@ RunState, SchedulingRequest, ) -from aignx.codegen.models import ( +from aignostics_sdk._codegen.models import ( ItemResultReadResponse as ItemResultData, ) -from aignx.codegen.models import ( +from aignostics_sdk._codegen.models import ( RunReadResponse as RunData, ) -from aignx.codegen.models import ( +from aignostics_sdk._codegen.models import ( VersionReadResponse as ApplicationVersion, ) -from jsonschema.exceptions import ValidationError -from jsonschema.validators import validate -from loguru import logger -from sentry_sdk import metrics -from tenacity import ( - Retrying, - retry_if_exception_type, - stop_after_attempt, - stop_after_delay, - wait_exponential_jitter, -) - -from aignostics.platform._api import ( +from aignostics_sdk.platform._api import ( RETRYABLE_EXCEPTIONS, _AuthenticatedApi, _AuthenticatedResource, _log_retry_attempt, ) -from aignostics.platform._authentication import get_token -from aignostics.platform._operation_cache import cached_operation, operation_cache_clear -from aignostics.platform._sdk_metadata import ( +from aignostics_sdk.platform._authentication import get_token +from aignostics_sdk.platform._operation_cache import cached_operation, operation_cache_clear +from aignostics_sdk.platform._sdk_metadata import ( build_item_sdk_metadata, build_run_sdk_metadata, validate_item_sdk_metadata, validate_run_sdk_metadata, ) -from aignostics.platform._settings import settings -from aignostics.platform._utils import ( +from aignostics_sdk.platform._settings import settings +from aignostics_sdk.platform._utils import ( calculate_file_crc32c, convert_to_json_serializable, download_file, get_mime_type_for_artifact, mime_type_to_file_ending, ) -from aignostics.platform.resources.applications import Versions -from aignostics.platform.resources.utils import paginate -from aignostics.utils import user_agent +from aignostics_sdk.platform.resources.applications import Versions +from aignostics_sdk.platform.resources.utils import paginate +from aignostics_sdk.utils import user_agent LIST_APPLICATION_RUNS_MAX_PAGE_SIZE = 100 LIST_APPLICATION_RUNS_MIN_PAGE_SIZE = 5 @@ -263,7 +263,7 @@ def for_run_id(cls, run_id: str, cache_token: bool = True) -> "Run": Returns: Run: The initialized Run instance. """ - from aignostics.platform._client import Client # noqa: PLC0415 + from aignostics_sdk.platform._client import Client # noqa: PLC0415 return cls(Client.get_api_client(cache_token=cache_token), run_id) diff --git a/src/aignostics/platform/resources/utils.py b/src/aignostics_sdk/platform/resources/utils.py similarity index 96% rename from src/aignostics/platform/resources/utils.py rename to src/aignostics_sdk/platform/resources/utils.py index aaaf23649..672a1a329 100644 --- a/src/aignostics/platform/resources/utils.py +++ b/src/aignostics_sdk/platform/resources/utils.py @@ -9,8 +9,6 @@ from collections.abc import Callable, Iterator from typing import TypeVar -from aignx.codegen.exceptions import NotFoundException - T = TypeVar("T") PAGE_SIZE = 20 @@ -45,6 +43,8 @@ def paginate(func: Callable[..., list[T]], *args: object, page_size: int = PAGE_ >>> print(len(items)) 25 """ + from aignostics_sdk._codegen.exceptions import NotFoundException # noqa: PLC0415 + if page_size <= 0: message = f"page_size must be a positive integer, got {page_size}" raise ValueError(message) diff --git a/src/aignostics/system/CLAUDE.md b/src/aignostics_sdk/system/CLAUDE.md similarity index 100% rename from src/aignostics/system/CLAUDE.md rename to src/aignostics_sdk/system/CLAUDE.md diff --git a/src/aignostics_sdk/system/__init__.py b/src/aignostics_sdk/system/__init__.py new file mode 100644 index 000000000..4f52e1336 --- /dev/null +++ b/src/aignostics_sdk/system/__init__.py @@ -0,0 +1,11 @@ +"""System module.""" + +from ._cli import cli +from ._service import Service +from ._settings import Settings + +__all__ = [ + "Service", + "Settings", + "cli", +] diff --git a/src/aignostics/system/_cli.py b/src/aignostics_sdk/system/_cli.py similarity index 98% rename from src/aignostics/system/_cli.py rename to src/aignostics_sdk/system/_cli.py index 836e648cb..f979161e9 100644 --- a/src/aignostics/system/_cli.py +++ b/src/aignostics_sdk/system/_cli.py @@ -11,8 +11,9 @@ import typer import yaml +from aignostics_sdk.utils import console + from ..constants import API_VERSIONS # noqa: TID252 -from ..utils import console # noqa: TID252 from ._service import Service cli = typer.Typer(name="system", help="Determine health, info and further utillities.") @@ -113,7 +114,6 @@ def dump_dot_env_file( if find_spec("nicegui"): - from ..utils import gui_run # noqa: TID252 @cli.command() def serve( @@ -128,6 +128,8 @@ def serve( port (int): Port to bind the server to. open_browser (bool): Open app in browser after starting the server. """ + from aignostics.utils import gui_run # noqa: PLC0415 + console.print(f"Starting web application server at http://{host}:{port}") gui_run(native=False, host=host, port=port, with_api=False, show=open_browser) diff --git a/src/aignostics/system/_exceptions.py b/src/aignostics_sdk/system/_exceptions.py similarity index 100% rename from src/aignostics/system/_exceptions.py rename to src/aignostics_sdk/system/_exceptions.py diff --git a/src/aignostics/system/_service.py b/src/aignostics_sdk/system/_service.py similarity index 94% rename from src/aignostics/system/_service.py rename to src/aignostics_sdk/system/_service.py index 9f881041b..f382e7ba8 100644 --- a/src/aignostics/system/_service.py +++ b/src/aignostics_sdk/system/_service.py @@ -22,7 +22,8 @@ from loguru import logger from pydantic_settings import BaseSettings -from ..utils import ( # noqa: TID252 +from aignostics_sdk.utils import ( + ENV_PREFIX, UNHIDE_SENSITIVE_INFO, BaseService, Health, @@ -34,9 +35,9 @@ __version__, get_process_info, load_settings, - locate_subclasses, user_agent, ) + from ._exceptions import OpenAPISchemaError from ._settings import Settings @@ -141,9 +142,14 @@ async def health(self) -> Health: Health: The aggregate health of the system. """ components: dict[str, Health] = {} - for service_class in locate_subclasses(BaseService): - if service_class is not Service: - components[f"{service_class.__module__}.{service_class.__name__}"] = await service_class().health() + try: + from aignostics.utils import locate_subclasses # noqa: PLC0415 + + for service_class in locate_subclasses(BaseService): + if service_class is not Service: + components[f"{service_class.__module__}.{service_class.__name__}"] = await service_class().health() + except ImportError: + pass components["network"] = await self._determine_network_health() # Set the system health status based on is_healthy attribute @@ -260,6 +266,10 @@ def _collect_all_settings(mask_secrets: bool = True) -> dict[str, Any]: dict[str, Any]: Flattened settings dictionary with env_prefix + key as the key. """ settings: dict[str, Any] = {} + try: + from aignostics.utils import locate_subclasses # noqa: PLC0415 + except ImportError: + return {} for settings_class in locate_subclasses(BaseSettings): settings_instance = load_settings(settings_class) env_prefix = settings_instance.model_config.get("env_prefix", "") @@ -409,10 +419,15 @@ async def info(include_environ: bool = False, mask_secrets: bool = True) -> dict # Convert the TypedDict to a regular dict before adding dynamic service keys result_dict: dict[str, Any] = dict(rtn) - for service_class in locate_subclasses(BaseService): - if service_class is not Service: - service = service_class() - result_dict[service.key()] = await service.info(mask_secrets=mask_secrets) + try: + from aignostics.utils import locate_subclasses # noqa: PLC0415 + + for service_class in locate_subclasses(BaseService): + if service_class is not Service: + service = service_class() + result_dict[service.key()] = await service.info(mask_secrets=mask_secrets) + except ImportError: + pass logger.debug("Service info: {}", result_dict) return result_dict @@ -522,8 +537,8 @@ def remote_diagnostics_enabled() -> bool: bool: True if remote diagnostics are enabled, False otherwise. """ return ( - Service.dotenv_get(f"{__project_name__.upper()}_SENTRY_ENABLED") == "1" - and Service.dotenv_get(f"{__project_name__.upper()}_LOGFIRE_ENABLED") == "1" + Service.dotenv_get(f"{ENV_PREFIX}_SENTRY_ENABLED") == "1" + and Service.dotenv_get(f"{ENV_PREFIX}_LOGFIRE_ENABLED") == "1" ) @staticmethod @@ -533,14 +548,14 @@ def remote_diagnostics_enable() -> None: Raises: ValueError: If the environment variable cannot be set. """ - Service.dotenv_set(f"{__project_name__.upper()}_SENTRY_ENABLED", "1") - Service.dotenv_set(f"{__project_name__.upper()}_LOGFIRE_ENABLED", "1") + Service.dotenv_set(f"{ENV_PREFIX}_SENTRY_ENABLED", "1") + Service.dotenv_set(f"{ENV_PREFIX}_LOGFIRE_ENABLED", "1") @staticmethod def remote_diagnostics_disable() -> None: """Disable remote diagnostics.""" - Service.dotenv_unset(f"{__project_name__.upper()}_SENTRY_ENABLED") - Service.dotenv_unset(f"{__project_name__.upper()}_LOGFIRE_ENABLED") + Service.dotenv_unset(f"{ENV_PREFIX}_SENTRY_ENABLED") + Service.dotenv_unset(f"{ENV_PREFIX}_LOGFIRE_ENABLED") @staticmethod def http_proxy_enable( diff --git a/src/aignostics/system/_settings.py b/src/aignostics_sdk/system/_settings.py similarity index 84% rename from src/aignostics/system/_settings.py rename to src/aignostics_sdk/system/_settings.py index a97992de6..f717f077a 100644 --- a/src/aignostics/system/_settings.py +++ b/src/aignostics_sdk/system/_settings.py @@ -5,14 +5,14 @@ from pydantic import Field, PlainSerializer, SecretStr from pydantic_settings import SettingsConfigDict -from ..utils import OpaqueSettings, __env_file__, __project_name__ # noqa: TID252 +from aignostics_sdk.utils import ENV_PREFIX, OpaqueSettings, __env_file__ class Settings(OpaqueSettings): """Settings.""" model_config = SettingsConfigDict( - env_prefix=f"{__project_name__.upper()}_SYSTEM_", + env_prefix=f"{ENV_PREFIX}_SYSTEM_", extra="ignore", env_file=__env_file__, env_file_encoding="utf-8", diff --git a/src/aignostics/system/assets/system.lottie b/src/aignostics_sdk/system/assets/system.lottie similarity index 100% rename from src/aignostics/system/assets/system.lottie rename to src/aignostics_sdk/system/assets/system.lottie diff --git a/src/aignostics/utils/CLAUDE.md b/src/aignostics_sdk/utils/CLAUDE.md similarity index 100% rename from src/aignostics/utils/CLAUDE.md rename to src/aignostics_sdk/utils/CLAUDE.md diff --git a/src/aignostics_sdk/utils/__init__.py b/src/aignostics_sdk/utils/__init__.py new file mode 100644 index 000000000..8088a9936 --- /dev/null +++ b/src/aignostics_sdk/utils/__init__.py @@ -0,0 +1,88 @@ +"""Utilities module.""" + +from ._cli import prepare_cli +from ._console import console +from ._constants import ( + ENV_PREFIX, + __author_email__, + __author_name__, + __base__url__, + __build_number__, + __documentation__url__, + __env__, + __env_file__, + __is_development_mode__, + __is_library_mode__, + __is_running_in_container__, + __is_running_in_read_only_environment__, + __is_test_mode__, + __project_name__, + __project_path__, + __python_version__, + __repository_url__, + __version__, + __version_full__, +) +from ._fs import get_user_data_directory, open_user_data_directory, sanitize_path, sanitize_path_component +from ._health import Health, HealthStatus +from ._log import LogSettings +from ._process import SUBPROCESS_CREATION_FLAGS, ProcessInfo, get_process_info +from ._service import BaseService +from ._settings import UNHIDE_SENSITIVE_INFO, OpaqueSettings, load_settings, strip_to_none_before_validator +from ._user_agent import user_agent +from .boot import boot + +__all__ = [ + "ENV_PREFIX", + "SUBPROCESS_CREATION_FLAGS", + "UNHIDE_SENSITIVE_INFO", + "BaseService", + "Health", + "HealthStatus", + "LogSettings", + "OpaqueSettings", + "ProcessInfo", + "__author_email__", + "__author_name__", + "__base__url__", + "__build_number__", + "__documentation__url__", + "__env__", + "__env_file__", + "__is_development_mode__", + "__is_library_mode__", + "__is_running_in_container__", + "__is_running_in_read_only_environment__", + "__is_test_mode__", + "__project_name__", + "__project_path__", + "__python_version__", + "__repository_url__", + "__version__", + "__version_full__", + "boot", + "console", + "get_process_info", + "get_user_data_directory", + "load_settings", + "open_user_data_directory", + "prepare_cli", + "sanitize_path", + "sanitize_path_component", + "strip_to_none_before_validator", + "user_agent", +] + +from importlib.util import find_spec + +if find_spec("sentry"): + from ._sentry import SentrySettings + + __all__ += ["SentrySettings"] + +if find_spec("marimo"): + from ._notebook import create_marimo_app + + __all__ += [ + "create_marimo_app", + ] diff --git a/src/aignostics/utils/_cli.py b/src/aignostics_sdk/utils/_cli.py similarity index 87% rename from src/aignostics/utils/_cli.py rename to src/aignostics_sdk/utils/_cli.py index 94230fc3b..60f60e686 100644 --- a/src/aignostics/utils/_cli.py +++ b/src/aignostics_sdk/utils/_cli.py @@ -5,8 +5,6 @@ import typer -from ._di import locate_implementations - def prepare_cli(cli: typer.Typer, epilog: str) -> None: """ @@ -16,8 +14,15 @@ def prepare_cli(cli: typer.Typer, epilog: str) -> None: cli (typer.Typer): Typer instance epilog (str): Epilog to add """ + # Collect already-registered sub-typers to avoid duplicate registration + # (e.g. when entry-point CLIs were added before prepare_cli is called) + from aignostics.utils import locate_implementations # noqa: PLC0415 + + already_registered = { + mounted.typer_instance for mounted in cli.registered_groups if mounted.typer_instance is not None + } for subcli in locate_implementations(typer.Typer): - if subcli != cli: + if subcli != cli and subcli not in already_registered: cli.add_typer(subcli) cli.info.epilog = epilog diff --git a/src/aignostics/utils/_console.py b/src/aignostics_sdk/utils/_console.py similarity index 100% rename from src/aignostics/utils/_console.py rename to src/aignostics_sdk/utils/_console.py diff --git a/src/aignostics/utils/_constants.py b/src/aignostics_sdk/utils/_constants.py similarity index 73% rename from src/aignostics/utils/_constants.py rename to src/aignostics_sdk/utils/_constants.py index 43726a296..91049602b 100644 --- a/src/aignostics/utils/_constants.py +++ b/src/aignostics_sdk/utils/_constants.py @@ -8,31 +8,38 @@ from dotenv import load_dotenv -__project_name__ = __name__.split(".")[0] +# __project_name__ is the distribution name (PYSDK-136: migrated to "aignostics-sdk"). +__project_name__ = "aignostics-sdk" # distribution name (hyphenated) +# _package_name is the importable Python module name. +# Used for importlib.metadata calls so they target the correct installed package. +_package_name = "aignostics_sdk" # importable name (underscored) +# ENV_PREFIX is the uppercase env var prefix — kept as "AIGNOSTICS" for backward compatibility. +ENV_PREFIX = "AIGNOSTICS" load_dotenv(str(Path(".env"))) -load_dotenv(os.getenv(f"{__project_name__.upper()}_ENV_FILE", Path.home() / f".{__project_name__}/.env")) +load_dotenv(os.getenv(f"{ENV_PREFIX}_ENV_FILE", Path.home() / f".{__project_name__}/.env")) __project_path__ = str(Path(__file__).parent.parent.parent) -__version__ = metadata.version(__project_name__) +__version__ = metadata.version(_package_name) __build_number__ = os.getenv("GITHUB_RUN_NUMBER") or os.getenv("BUILD_NUMBER") or None __version_full__ = f"{__version__}+{__build_number__}" if __build_number__ else __version__ __python_version__ = platform.python_version() __is_development_mode__ = "uvx" not in sys.argv[0].lower() -__is_running_in_container__ = os.getenv(f"{__project_name__.upper()}_RUNNING_IN_CONTAINER") +__is_running_in_container__ = os.getenv(f"{ENV_PREFIX}_RUNNING_IN_CONTAINER") __is_cli_mode__ = ( sys.argv[0].endswith(__project_name__) - or (len(sys.argv) > 1 and sys.argv[1] == __project_name__) + or sys.argv[0].endswith(_package_name) + or (len(sys.argv) > 1 and sys.argv[1] in {__project_name__, _package_name}) or sys.argv[0].endswith("gui_watch.py") or (len(sys.argv) > 1 and sys.argv[1] == "gui_watch.py") ) -__is_library_mode__ = not __is_cli_mode__ and not os.getenv(f"PYTEST_RUNNING_{__project_name__.upper()}") -__is_test_mode__ = "pytest" in sys.modules and os.getenv(f"PYTEST_RUNNING_{__project_name__.upper()}") +__is_library_mode__ = not __is_cli_mode__ and not os.getenv(f"PYTEST_RUNNING_{ENV_PREFIX}") +__is_test_mode__ = "pytest" in sys.modules and os.getenv(f"PYTEST_RUNNING_{ENV_PREFIX}") # Determine if we're running in a read-only runtime environment READ_ONLY_ENV_INDICATORS = [ - f"{__project_name__.upper()}_RUNNING_IN_CONTAINER", + f"{ENV_PREFIX}_RUNNING_IN_CONTAINER", "VERCEL_ENV", "RAILWAY_ENVIRONMENT", ] @@ -40,7 +47,7 @@ # Determine environment we are deployed on ENV_VAR_MAPPINGS = { - f"{__project_name__.upper()}_ENVIRONMENT": lambda env: env, + f"{ENV_PREFIX}_ENVIRONMENT": lambda env: env, "ENV": lambda env: env, "VERCEL_ENV": lambda env: env, # See https://vercel.com/docs/environment-variables/system-environment-variables "RAILWAY_ENVIRONMENT": lambda env: ( @@ -61,7 +68,7 @@ Path(".env"), Path(f".env.{__env__}"), ] -env_file_path = os.getenv(f"{__project_name__.upper()}_ENV_FILE") +env_file_path = os.getenv(f"{ENV_PREFIX}_ENV_FILE") if env_file_path: __env_file__.insert(2, Path(env_file_path)) @@ -74,7 +81,7 @@ f"https://{url}" ), # See https://docs.railway.com/reference/variables#railway-provided-variables } -__base__url__ = os.getenv(f"{__project_name__.upper()}_BASE_URL") +__base__url__ = os.getenv(f"{ENV_PREFIX}_BASE_URL") if not __base__url__: for env_var, mappers in PLATFORM_URL_MAPPINGS.items(): env_value = os.getenv(env_var) @@ -94,13 +101,13 @@ def get_project_url_by_label(prefix: str) -> str: Returns: The extracted URL string if found, or an empty string if not found. """ - for url_entry in metadata.metadata(__project_name__).get_all("Project-URL", []): + for url_entry in metadata.metadata(_package_name).get_all("Project-URL", []): if url_entry.startswith(prefix): return str(url_entry.split(", ", 1)[1]) return "" -_authors = metadata.metadata(__project_name__).get_all("Author-email", []) +_authors = metadata.metadata(_package_name).get_all("Author-email", []) _author = _authors[0] if _authors else None __author_name__ = _author.split("<")[0].strip() if _author else None __author_email__ = _author.split("<")[1].strip(" >") if _author else None diff --git a/src/aignostics/utils/_fs.py b/src/aignostics_sdk/utils/_fs.py similarity index 95% rename from src/aignostics/utils/_fs.py rename to src/aignostics_sdk/utils/_fs.py index d11a54a0b..7ef8dc617 100644 --- a/src/aignostics/utils/_fs.py +++ b/src/aignostics_sdk/utils/_fs.py @@ -6,8 +6,6 @@ import platformdirs from loguru import logger -from aignostics.third_party.showinfm.showinfm import show_in_file_manager - from ._constants import __is_running_in_read_only_environment__, __project_name__ # Constants @@ -107,7 +105,11 @@ def open_user_data_directory(scope: str | None = None) -> Path: directory = get_user_data_directory(scope) try: + from aignostics.third_party.showinfm.showinfm import show_in_file_manager # noqa: PLC0415 + show_in_file_manager(str(directory.resolve())) + except ImportError: + pass # full aignostics package not installed; skip file-manager open except (OSError, RuntimeError, FileNotFoundError) as error: logger.warning( "Failed to open user data directory in file manager: %s. Directory path: %s", diff --git a/src/aignostics/utils/_health.py b/src/aignostics_sdk/utils/_health.py similarity index 100% rename from src/aignostics/utils/_health.py rename to src/aignostics_sdk/utils/_health.py diff --git a/src/aignostics/utils/_log.py b/src/aignostics_sdk/utils/_log.py similarity index 98% rename from src/aignostics/utils/_log.py rename to src/aignostics_sdk/utils/_log.py index 3822613a1..1b1e44b21 100644 --- a/src/aignostics/utils/_log.py +++ b/src/aignostics_sdk/utils/_log.py @@ -18,7 +18,7 @@ from pydantic import ValidationInfo, field_validator from pydantic_settings import BaseSettings, SettingsConfigDict -from ._constants import __env_file__, __is_library_mode__, __project_name__ +from ._constants import ENV_PREFIX, __env_file__, __is_library_mode__, __project_name__ from ._settings import load_settings @@ -107,7 +107,7 @@ class LogSettings(BaseSettings): """Settings for configuring logging behavior.""" model_config = SettingsConfigDict( - env_prefix=f"{__project_name__.upper()}_LOG_", + env_prefix=f"{ENV_PREFIX}_LOG_", extra="ignore", env_file=__env_file__, env_file_encoding="utf-8", diff --git a/src/aignostics/utils/_notebook.py b/src/aignostics_sdk/utils/_notebook.py similarity index 100% rename from src/aignostics/utils/_notebook.py rename to src/aignostics_sdk/utils/_notebook.py diff --git a/src/aignostics/utils/_process.py b/src/aignostics_sdk/utils/_process.py similarity index 100% rename from src/aignostics/utils/_process.py rename to src/aignostics_sdk/utils/_process.py diff --git a/src/aignostics/utils/_sentry.py b/src/aignostics_sdk/utils/_sentry.py similarity index 98% rename from src/aignostics/utils/_sentry.py rename to src/aignostics_sdk/utils/_sentry.py index 7d30d250a..8dac23f66 100644 --- a/src/aignostics/utils/_sentry.py +++ b/src/aignostics_sdk/utils/_sentry.py @@ -9,7 +9,7 @@ from pydantic import AfterValidator, BeforeValidator, Field, PlainSerializer, SecretStr from pydantic_settings import SettingsConfigDict -from ._constants import __env__, __env_file__, __project_name__, __version__ +from ._constants import ENV_PREFIX, __env__, __env_file__, __project_name__, __version__ from ._settings import OpaqueSettings, load_settings, strip_to_none_before_validator if TYPE_CHECKING: @@ -118,7 +118,7 @@ class SentrySettings(OpaqueSettings): """Configuration settings for Sentry integration.""" model_config = SettingsConfigDict( - env_prefix=f"{__project_name__.upper()}_SENTRY_", + env_prefix=f"{ENV_PREFIX}_SENTRY_", env_file=__env_file__, env_file_encoding="utf-8", extra="ignore", diff --git a/src/aignostics/utils/_service.py b/src/aignostics_sdk/utils/_service.py similarity index 100% rename from src/aignostics/utils/_service.py rename to src/aignostics_sdk/utils/_service.py diff --git a/src/aignostics/utils/_settings.py b/src/aignostics_sdk/utils/_settings.py similarity index 100% rename from src/aignostics/utils/_settings.py rename to src/aignostics_sdk/utils/_settings.py diff --git a/src/aignostics/utils/_user_agent.py b/src/aignostics_sdk/utils/_user_agent.py similarity index 100% rename from src/aignostics/utils/_user_agent.py rename to src/aignostics_sdk/utils/_user_agent.py diff --git a/src/aignostics/utils/boot.py b/src/aignostics_sdk/utils/boot.py similarity index 97% rename from src/aignostics/utils/boot.py rename to src/aignostics_sdk/utils/boot.py index fb4c2195c..b1d5454d7 100644 --- a/src/aignostics/utils/boot.py +++ b/src/aignostics_sdk/utils/boot.py @@ -41,7 +41,7 @@ if "DYLD_FALLBACK_LIBRARY_PATH" not in os.environ: os.environ["DYLD_FALLBACK_LIBRARY_PATH"] = f"{os.getenv('HOMEBREW_PREFIX', '/opt/homebrew')}/lib/" -from ._constants import __project_name__, __version__ # noqa: E402 +from ._constants import ENV_PREFIX, __project_name__, __version__ # noqa: E402 from ._process import get_process_info # noqa: E402 _boot_called = False @@ -78,7 +78,7 @@ def _parse_env_args() -> None: """ i = 1 # Start after script name to_remove = [] - prefix = f"{__project_name__.upper()}_" + prefix = f"{ENV_PREFIX}_" while i < len(sys.argv): current_arg = sys.argv[i] diff --git a/tests/aignostics/application/cli_pipeline_validation_test.py b/tests/aignostics/application/cli_pipeline_validation_test.py index 86b891b1c..5ea4a6106 100644 --- a/tests/aignostics/application/cli_pipeline_validation_test.py +++ b/tests/aignostics/application/cli_pipeline_validation_test.py @@ -5,9 +5,9 @@ from pathlib import Path import pytest +from aignostics.cli import cli from typer.testing import CliRunner -from aignostics.cli import cli from tests.conftest import normalize_output from tests.constants_test import HETA_APPLICATION_ID diff --git a/tests/aignostics/application/cli_test.py b/tests/aignostics/application/cli_test.py index c5d4a2e90..85280b151 100644 --- a/tests/aignostics/application/cli_test.py +++ b/tests/aignostics/application/cli_test.py @@ -11,14 +11,14 @@ from unittest.mock import MagicMock, patch import pytest +from aignostics.application import Service as ApplicationService +from aignostics.cli import cli from loguru import logger from tenacity import Retrying, retry, stop_after_attempt, wait_exponential from typer.testing import CliRunner -from aignostics.application import Service as ApplicationService -from aignostics.cli import cli -from aignostics.platform import LIST_APPLICATION_RUNS_MAX_PAGE_SIZE -from aignostics.utils import Health, sanitize_path +from aignostics_sdk.platform import LIST_APPLICATION_RUNS_MAX_PAGE_SIZE +from aignostics_sdk.utils import Health, sanitize_path from tests.conftest import normalize_output, print_directory_structure from tests.constants_test import ( HETA_APPLICATION_ID, @@ -52,7 +52,7 @@ DOCUMENT_OUTPUT_DESCRIPTION_PDF = "output_description.pdf" DOCUMENT_MODEL_CARD_PDF = "model_card.pdf" DOCUMENT_MISSING_PDF = "missing.pdf" -APPLICATION_CLI_CLIENT_PATCH_TARGET = "aignostics.application._cli.Client" +APPLICATION_CLI_CLIENT_PATCH_TARGET = "aignostics_sdk.application._cli.Client" # Stub values reused across the document CLI tests. DOCUMENT_TEST_FAILURE_MESSAGE = "kaboom" # canonical exception body for unexpected-failure paths @@ -296,7 +296,7 @@ def test_cli_application_run_upload_fails_on_missing_source(runner: CliRunner, t @pytest.mark.e2e @pytest.mark.timeout(timeout=10) -@patch("aignostics.application._cli.SystemService.health_static") +@patch("aignostics_sdk.application._cli.SystemService.health_static") def test_cli_run_submit_fails_when_system_unhealthy_and_no_force( mock_health: MagicMock, runner: CliRunner, tmp_path: Path ) -> None: @@ -329,7 +329,7 @@ def test_cli_run_submit_fails_when_system_unhealthy_and_no_force( @pytest.mark.e2e @pytest.mark.timeout(timeout=60) -@patch("aignostics.application._cli.SystemService.health_static") +@patch("aignostics_sdk.application._cli.SystemService.health_static") def test_cli_run_submit_succeeds_when_system_degraded_and_no_force( mock_health: MagicMock, runner: CliRunner, tmp_path: Path ) -> None: @@ -344,7 +344,7 @@ def test_cli_run_submit_succeeds_when_system_degraded_and_no_force( @pytest.mark.e2e @pytest.mark.timeout(timeout=10) -@patch("aignostics.application._cli.SystemService.health_static") +@patch("aignostics_sdk.application._cli.SystemService.health_static") def test_cli_run_upload_fails_when_system_unhealthy_and_no_force( mock_health: MagicMock, runner: CliRunner, tmp_path: Path ) -> None: @@ -375,7 +375,7 @@ def test_cli_run_upload_fails_when_system_unhealthy_and_no_force( @pytest.mark.e2e @pytest.mark.timeout(timeout=10) -@patch("aignostics.application._cli.SystemService.health_static") +@patch("aignostics_sdk.application._cli.SystemService.health_static") def test_cli_run_execute_fails_when_system_unhealthy_and_no_force( mock_health: MagicMock, runner: CliRunner, tmp_path: Path ) -> None: @@ -847,7 +847,7 @@ def test_cli_run_list_for_organization(runner: CliRunner) -> None: @pytest.mark.unit def test_cli_run_list_forbidden_with_organization(runner: CliRunner) -> None: """Check ForbiddenException with --for-organization shows org-specific access denied message.""" - from aignx.codegen.exceptions import ForbiddenException + from aignostics_sdk._codegen.exceptions import ForbiddenException with patch.object( ApplicationService, "application_runs", side_effect=ForbiddenException(status=403, reason="Forbidden") @@ -862,7 +862,7 @@ def test_cli_run_list_forbidden_with_organization(runner: CliRunner) -> None: @pytest.mark.unit def test_cli_run_list_forbidden_without_organization(runner: CliRunner) -> None: """Check ForbiddenException without --for-organization shows generic access denied message.""" - from aignx.codegen.exceptions import ForbiddenException + from aignostics_sdk._codegen.exceptions import ForbiddenException with patch.object( ApplicationService, "application_runs", side_effect=ForbiddenException(status=403, reason="Forbidden") @@ -897,7 +897,7 @@ def test_cli_run_describe_not_found(runner: CliRunner, record_property) -> None: @pytest.mark.integration def test_cli_run_describe_json_includes_items(runner: CliRunner) -> None: """Check run describe --format=json includes items in output.""" - from aignx.codegen.models import ( + from aignostics_sdk._codegen.models import ( ItemOutput, ItemResultReadResponse, ItemState, @@ -954,8 +954,8 @@ def test_cli_run_describe_json_includes_items(runner: CliRunner) -> None: mock_run_handle.results.return_value = iter([mock_item]) with ( - patch("aignostics.application._cli.PlatformService.get_user_info", return_value=mock_user_info), - patch("aignostics.application._cli.Service") as mock_service_cls, + patch("aignostics_sdk.application._cli.PlatformService.get_user_info", return_value=mock_user_info), + patch("aignostics_sdk.application._cli.Service") as mock_service_cls, ): mock_service_cls.return_value.application_run.return_value = mock_run_handle @@ -1773,7 +1773,7 @@ def test_cli_application_version_document_describe_success(runner: CliRunner, re def test_cli_application_version_document_describe_not_found(runner: CliRunner, record_property) -> None: """`application version document describe` exits 2 with a clear message on 404.""" record_property("tested-item-id", "TC-APPLICATION-CLI-05-03") - from aignx.codegen.exceptions import NotFoundException as ApiNotFound + from aignostics_sdk._codegen.exceptions import NotFoundException as ApiNotFound fake_documents = MagicMock() fake_documents.details.side_effect = ApiNotFound(status=404, reason=API_REASON_NOT_FOUND) @@ -1870,7 +1870,7 @@ def test_cli_application_version_document_list_json_empty(runner: CliRunner, rec def test_cli_application_version_document_list_resolve_not_found_text(runner: CliRunner, record_property) -> None: """`application version document list` exits 2 when the application version cannot be resolved.""" record_property("tested-item-id", "TC-APPLICATION-CLI-05-01") - from aignx.codegen.exceptions import NotFoundException as ApiNotFound + from aignostics_sdk._codegen.exceptions import NotFoundException as ApiNotFound fake_client = MagicMock() fake_client.applications.versions.documents.side_effect = ApiNotFound(status=404, reason=API_REASON_NOT_FOUND) @@ -1888,7 +1888,7 @@ def test_cli_application_version_document_list_resolve_not_found_text(runner: Cl def test_cli_application_version_document_list_resolve_not_found_json(runner: CliRunner, record_property) -> None: """`application version document list --format json` emits structured error on 404.""" record_property("tested-item-id", "TC-APPLICATION-CLI-05-01") - from aignx.codegen.exceptions import NotFoundException as ApiNotFound + from aignostics_sdk._codegen.exceptions import NotFoundException as ApiNotFound fake_client = MagicMock() fake_client.applications.versions.documents.side_effect = ApiNotFound(status=404, reason=API_REASON_NOT_FOUND) @@ -1976,7 +1976,7 @@ def test_cli_application_version_document_describe_json_success(runner: CliRunne def test_cli_application_version_document_describe_resolve_not_found_text(runner: CliRunner, record_property) -> None: """`describe` exits 2 when the application version cannot be resolved (text format).""" record_property("tested-item-id", "TC-APPLICATION-CLI-05-03") - from aignx.codegen.exceptions import NotFoundException as ApiNotFound + from aignostics_sdk._codegen.exceptions import NotFoundException as ApiNotFound fake_client = MagicMock() fake_client.applications.versions.documents.side_effect = ApiNotFound(status=404, reason=API_REASON_NOT_FOUND) @@ -1996,7 +1996,7 @@ def test_cli_application_version_document_describe_resolve_not_found_text(runner def test_cli_application_version_document_describe_resolve_not_found_json(runner: CliRunner, record_property) -> None: """`describe --format json` emits structured error when version cannot be resolved.""" record_property("tested-item-id", "TC-APPLICATION-CLI-05-03") - from aignx.codegen.exceptions import NotFoundException as ApiNotFound + from aignostics_sdk._codegen.exceptions import NotFoundException as ApiNotFound fake_client = MagicMock() fake_client.applications.versions.documents.side_effect = ApiNotFound(status=404, reason=API_REASON_NOT_FOUND) @@ -2026,7 +2026,7 @@ def test_cli_application_version_document_describe_resolve_not_found_json(runner def test_cli_application_version_document_describe_not_found_json(runner: CliRunner, record_property) -> None: """`describe --format json` emits structured error when the document is missing.""" record_property("tested-item-id", "TC-APPLICATION-CLI-05-03") - from aignx.codegen.exceptions import NotFoundException as ApiNotFound + from aignostics_sdk._codegen.exceptions import NotFoundException as ApiNotFound fake_documents = MagicMock() fake_documents.details.side_effect = ApiNotFound(status=404, reason=API_REASON_NOT_FOUND) @@ -2111,7 +2111,7 @@ def test_cli_application_version_document_download_resolve_not_found( ) -> None: """`download` exits 2 when the application version cannot be resolved.""" record_property("tested-item-id", "TC-APPLICATION-CLI-05-04") - from aignx.codegen.exceptions import NotFoundException as ApiNotFound + from aignostics_sdk._codegen.exceptions import NotFoundException as ApiNotFound fake_client = MagicMock() fake_client.applications.versions.documents.side_effect = ApiNotFound(status=404, reason=API_REASON_NOT_FOUND) @@ -2142,7 +2142,7 @@ def test_cli_application_version_document_download_not_found( ) -> None: """`download` exits 2 with a clear message when the document does not exist.""" record_property("tested-item-id", "TC-APPLICATION-CLI-05-04") - from aignx.codegen.exceptions import NotFoundException as ApiNotFound + from aignostics_sdk._codegen.exceptions import NotFoundException as ApiNotFound fake_documents = MagicMock() fake_documents.download_to_path.side_effect = ApiNotFound(status=404, reason=API_REASON_NOT_FOUND) diff --git a/tests/aignostics/application/download_test.py b/tests/aignostics/application/download_test.py index f8fee1adb..1dd9d6a1d 100644 --- a/tests/aignostics/application/download_test.py +++ b/tests/aignostics/application/download_test.py @@ -6,14 +6,14 @@ import pytest import requests -from aignostics.application._download import ( +from aignostics_sdk.application._download import ( download_available_items, download_item_artifact, download_url_to_file_with_progress, extract_filename_from_url, ) -from aignostics.application._models import DownloadProgress, DownloadProgressState -from aignostics.platform import ArtifactOutput +from aignostics_sdk.application._models import DownloadProgress, DownloadProgressState +from aignostics_sdk.platform import ArtifactOutput @pytest.mark.unit @@ -74,10 +74,10 @@ def progress_callback(p: DownloadProgress) -> None: "input_slide_downloaded_chunk_size": p.input_slide_downloaded_chunk_size, }) - with patch("aignostics.application._download.generate_signed_url") as mock_generate_signed_url: + with patch("aignostics_sdk.application._download.generate_signed_url") as mock_generate_signed_url: mock_generate_signed_url.return_value = signed_url - with patch("aignostics.application._download.requests.get") as mock_get: + with patch("aignostics_sdk.application._download.requests.get") as mock_get: mock_response = Mock() mock_response.raise_for_status = Mock() mock_response.headers = {"content-length": str(len(file_content))} @@ -124,10 +124,10 @@ def test_download_url_to_file_with_progress_queue(tmp_path: Path) -> None: progress_queue = Mock() progress_queue.put_nowait = Mock() # Mock the put_nowait method - with patch("aignostics.application._download.generate_signed_url") as mock_generate_signed_url: + with patch("aignostics_sdk.application._download.generate_signed_url") as mock_generate_signed_url: mock_generate_signed_url.return_value = signed_url - with patch("aignostics.application._download.requests.get") as mock_get: + with patch("aignostics_sdk.application._download.requests.get") as mock_get: mock_response = Mock() mock_response.raise_for_status = Mock() mock_response.headers = {"content-length": str(len(file_content))} @@ -160,10 +160,10 @@ def test_download_url_to_file_with_progress_chunked(tmp_path: Path) -> None: def progress_callback(p: DownloadProgress) -> None: progress_updates.append(p.input_slide_downloaded_size) - with patch("aignostics.application._download.generate_signed_url") as mock_generate_signed_url: + with patch("aignostics_sdk.application._download.generate_signed_url") as mock_generate_signed_url: mock_generate_signed_url.return_value = signed_url - with patch("aignostics.application._download.requests.get") as mock_get: + with patch("aignostics_sdk.application._download.requests.get") as mock_get: mock_response = Mock() mock_response.raise_for_status = Mock() mock_response.headers = {"content-length": str(total_size)} @@ -194,10 +194,10 @@ def test_download_url_to_file_with_progress_http_error(tmp_path: Path) -> None: progress = DownloadProgress() - with patch("aignostics.application._download.generate_signed_url") as mock_generate_signed_url: + with patch("aignostics_sdk.application._download.generate_signed_url") as mock_generate_signed_url: mock_generate_signed_url.return_value = signed_url - with patch("aignostics.application._download.requests.get") as mock_get: + with patch("aignostics_sdk.application._download.requests.get") as mock_get: mock_response = Mock() mock_response.raise_for_status = Mock(side_effect=requests.HTTPError("404 Not Found")) mock_get.return_value = mock_response @@ -234,10 +234,10 @@ def progress_callback(p: DownloadProgress) -> None: "artifact_progress": p.artifact_progress_normalized, }) - with patch("aignostics.application._download.generate_signed_url") as mock_generate_signed_url: + with patch("aignostics_sdk.application._download.generate_signed_url") as mock_generate_signed_url: mock_generate_signed_url.return_value = signed_url - with patch("aignostics.application._download.requests.get") as mock_get: + with patch("aignostics_sdk.application._download.requests.get") as mock_get: mock_response = Mock() mock_response.raise_for_status = Mock() mock_response.headers = {"content-length": str(file_size)} @@ -299,7 +299,7 @@ def progress_callback(p: DownloadProgress) -> None: "input_slide_downloaded_size": p.input_slide_downloaded_size, }) - with patch("aignostics.application._download.requests.get") as mock_get: + with patch("aignostics_sdk.application._download.requests.get") as mock_get: mock_response = Mock() mock_response.raise_for_status = Mock() mock_response.headers = {"content-length": str(len(file_content))} @@ -334,7 +334,7 @@ def test_download_url_to_file_with_progress_http_url_success(tmp_path: Path) -> progress = DownloadProgress() - with patch("aignostics.application._download.requests.get") as mock_get: + with patch("aignostics_sdk.application._download.requests.get") as mock_get: mock_response = Mock() mock_response.raise_for_status = Mock() mock_response.headers = {"content-length": str(len(file_content))} @@ -382,7 +382,7 @@ def test_download_url_to_file_with_progress_https_with_chunked(tmp_path: Path) - def progress_callback(p: DownloadProgress) -> None: progress_updates.append(p.input_slide_downloaded_size) - with patch("aignostics.application._download.requests.get") as mock_get: + with patch("aignostics_sdk.application._download.requests.get") as mock_get: mock_response = Mock() mock_response.raise_for_status = Mock() mock_response.headers = {"content-length": str(total_size)} @@ -414,8 +414,8 @@ def progress_callback(p: DownloadProgress) -> None: # Patch _download.get_file_extension_for_artifact (NOT _utils.*) — the function # is imported by name into _download, so re-binding it on _utils does nothing. # Copilot called this out on PR #478 (comments #3 + #4). -_PATCH_GET_EXT = "aignostics.application._download.get_file_extension_for_artifact" -_PATCH_DOWNLOAD_FILE_WITH_PROGRESS = "aignostics.application._download.download_file_with_progress" +_PATCH_GET_EXT = "aignostics_sdk.application._download.get_file_extension_for_artifact" +_PATCH_DOWNLOAD_FILE_WITH_PROGRESS = "aignostics_sdk.application._download.download_file_with_progress" def _mock_artifact( @@ -526,7 +526,7 @@ def test_download_available_items_skips_non_available_artifacts(tmp_path: Path) artifacts that aren't AVAILABLE. Calling it for a NONE artifact would fail the whole download. This test pins the guard. """ - from aignostics.platform import ItemOutput, ItemState + from aignostics_sdk.platform import ItemOutput, ItemState available = _mock_artifact(output_artifact_id="art-ok", output=ArtifactOutput.AVAILABLE) none_artifact = _mock_artifact(output_artifact_id="art-skip", output=ArtifactOutput.NONE) @@ -541,7 +541,7 @@ def test_download_available_items_skips_non_available_artifacts(tmp_path: Path) run.run_id = "run-xyz" run.results.return_value = [item] - with patch("aignostics.application._download.download_item_artifact") as mock_dia: + with patch("aignostics_sdk.application._download.download_item_artifact") as mock_dia: download_available_items( progress=DownloadProgress(), application_run=run, @@ -563,7 +563,7 @@ def test_download_available_items_passes_run_to_download_item_artifact(tmp_path: so the calling site must pass it through. Pinning the call shape keeps the contract explicit. """ - from aignostics.platform import ItemOutput, ItemState + from aignostics_sdk.platform import ItemOutput, ItemState artifact = _mock_artifact() item = MagicMock() @@ -576,7 +576,7 @@ def test_download_available_items_passes_run_to_download_item_artifact(tmp_path: run.run_id = "run-xyz" run.results.return_value = [item] - with patch("aignostics.application._download.download_item_artifact") as mock_dia: + with patch("aignostics_sdk.application._download.download_item_artifact") as mock_dia: download_available_items( progress=DownloadProgress(), application_run=run, diff --git a/tests/aignostics/application/gui_test.py b/tests/aignostics/application/gui_test.py index 59ba189e2..39fe48127 100644 --- a/tests/aignostics/application/gui_test.py +++ b/tests/aignostics/application/gui_test.py @@ -10,10 +10,6 @@ from unittest.mock import AsyncMock, MagicMock, Mock, patch import pytest -from nicegui.testing import User -from typer.testing import CliRunner - -from aignostics import WSI_SUPPORTED_FILE_EXTENSIONS from aignostics.application import Service from aignostics.application._gui._page_application_run_describe import ( RESULTS_PAGE_SIZE, @@ -21,6 +17,10 @@ _resolve_artifact_url_or_notify, ) from aignostics.cli import cli +from nicegui.testing import User +from typer.testing import CliRunner + +from aignostics import WSI_SUPPORTED_FILE_EXTENSIONS from tests.conftest import assert_notified, normalize_output, print_directory_structure from tests.constants_test import ( HETA_APPLICATION_ID, diff --git a/tests/aignostics/application/service_test.py b/tests/aignostics/application/service_test.py index ea023455b..5328d451e 100644 --- a/tests/aignostics/application/service_test.py +++ b/tests/aignostics/application/service_test.py @@ -4,10 +4,10 @@ from unittest.mock import MagicMock, patch import pytest +from aignostics.application import Service as ApplicationService from typer.testing import CliRunner -from aignostics.application import Service as ApplicationService -from aignostics.platform import NotFoundException, RunData, RunOutput +from aignostics_sdk.platform import NotFoundException, RunData, RunOutput from tests.constants_test import ( HETA_APPLICATION_ID, HETA_APPLICATION_VERSION, @@ -133,7 +133,7 @@ def test_application_runs_query_with_tags_raises() -> None: @pytest.mark.unit -@patch("aignostics.application._service.Service._get_platform_client") +@patch("aignostics_sdk.application._service.Service._get_platform_client") def test_application_runs_query_searches_note_and_tags(mock_get_client: MagicMock) -> None: """Test that query parameter searches both note and tags with union semantics.""" # Create mock runs @@ -196,7 +196,7 @@ def test_application_runs_query_searches_note_and_tags(mock_get_client: MagicMoc @pytest.mark.unit -@patch("aignostics.application._service.Service._get_platform_client") +@patch("aignostics_sdk.application._service.Service._get_platform_client") def test_application_runs_query_deduplicates_results(mock_get_client: MagicMock) -> None: """Test that query parameter deduplicates runs that match both note and tags.""" # Create mock run that matches both searches @@ -227,7 +227,7 @@ def test_application_runs_query_deduplicates_results(mock_get_client: MagicMock) @pytest.mark.unit -@patch("aignostics.application._service.Service._get_platform_client") +@patch("aignostics_sdk.application._service.Service._get_platform_client") def test_application_runs_query_respects_limit(mock_get_client: MagicMock) -> None: """Test that query parameter respects the limit parameter and returns the newest runs.""" base_time = datetime(2024, 1, 10, tzinfo=UTC) @@ -272,7 +272,7 @@ def test_application_runs_query_respects_limit(mock_get_client: MagicMock) -> No @pytest.mark.unit -@patch("aignostics.application._service.Service._get_platform_client") +@patch("aignostics_sdk.application._service.Service._get_platform_client") def test_application_runs_query_tag_search_has_independent_limit(mock_get_client: MagicMock) -> None: """Tag search gets its own N-slot budget; a full note search does not starve the tag search.""" base_time = datetime(2024, 1, 1, tzinfo=UTC) @@ -317,7 +317,7 @@ def test_application_runs_query_tag_search_has_independent_limit(mock_get_client @pytest.mark.unit -@patch("aignostics.application._service.Service._get_platform_client") +@patch("aignostics_sdk.application._service.Service._get_platform_client") def test_application_runs_query_sorts_results_by_submitted_at(mock_get_client: MagicMock) -> None: """No-limit case: mixed note+tag results are returned newest-first regardless of which search found them.""" base_time = datetime(2024, 3, 1, tzinfo=UTC) @@ -356,7 +356,7 @@ def test_application_runs_query_sorts_results_by_submitted_at(mock_get_client: M @pytest.mark.unit -@patch("aignostics.application._service.Service._get_platform_client") +@patch("aignostics_sdk.application._service.Service._get_platform_client") def test_application_runs_query_deduplicates_with_independent_budget(mock_get_client: MagicMock) -> None: """A run matching both note and tag appears exactly once. @@ -404,7 +404,7 @@ def test_application_runs_query_deduplicates_with_independent_budget(mock_get_cl @pytest.mark.unit -@patch("aignostics.application._service.Service._get_platform_client") +@patch("aignostics_sdk.application._service.Service._get_platform_client") def test_application_runs_query_escapes_special_characters(mock_get_client: MagicMock) -> None: """Test that query parameter properly escapes special regex characters.""" # Mock the platform client @@ -432,7 +432,7 @@ def test_application_runs_query_escapes_special_characters(mock_get_client: Magi @pytest.mark.unit -@patch("aignostics.application._service.Service._get_platform_client") +@patch("aignostics_sdk.application._service.Service._get_platform_client") def test_application_runs_tags_single_generates_equality_jsonpath(mock_get_client: MagicMock) -> None: """Test that a single tag generates a JSONPath equality expression instead of like_regex.""" mock_client = MagicMock() @@ -449,7 +449,7 @@ def test_application_runs_tags_single_generates_equality_jsonpath(mock_get_clien @pytest.mark.unit -@patch("aignostics.application._service.Service._get_platform_client") +@patch("aignostics_sdk.application._service.Service._get_platform_client") def test_application_runs_tags_multiple_generates_or_equality_jsonpath(mock_get_client: MagicMock) -> None: """Test that multiple tags generate a JSONPath OR equality expression.""" mock_client = MagicMock() @@ -473,7 +473,7 @@ def test_application_runs_tags_multiple_generates_or_equality_jsonpath(mock_get_ @pytest.mark.unit -@patch("aignostics.application._service.Service._get_platform_client") +@patch("aignostics_sdk.application._service.Service._get_platform_client") def test_application_runs_tags_escapes_quotes_and_backslashes(mock_get_client: MagicMock) -> None: """Test that tag values with quotes and backslashes are properly escaped in JSONPath.""" mock_client = MagicMock() @@ -494,7 +494,7 @@ def test_application_runs_tags_escapes_quotes_and_backslashes(mock_get_client: M @pytest.mark.unit -@patch("aignostics.application._service.Service._get_platform_client") +@patch("aignostics_sdk.application._service.Service._get_platform_client") def test_application_run_update_custom_metadata_success(mock_get_client: MagicMock) -> None: """Test successful update of run custom metadata.""" mock_client = MagicMock() @@ -515,7 +515,7 @@ def test_application_run_update_custom_metadata_success(mock_get_client: MagicMo @pytest.mark.unit -@patch("aignostics.application._service.Service._get_platform_client") +@patch("aignostics_sdk.application._service.Service._get_platform_client") def test_application_run_update_custom_metadata_not_found(mock_get_client: MagicMock) -> None: """Test update metadata with non-existent run.""" mock_client = MagicMock() @@ -531,7 +531,7 @@ def test_application_run_update_custom_metadata_not_found(mock_get_client: Magic @pytest.mark.unit -@patch("aignostics.application._service.Service._get_platform_client") +@patch("aignostics_sdk.application._service.Service._get_platform_client") def test_application_run_update_item_custom_metadata_success(mock_get_client: MagicMock) -> None: """Test successful update of item custom metadata.""" mock_client = MagicMock() @@ -552,7 +552,7 @@ def test_application_run_update_item_custom_metadata_success(mock_get_client: Ma @pytest.mark.unit -@patch("aignostics.application._service.Service._get_platform_client") +@patch("aignostics_sdk.application._service.Service._get_platform_client") def test_application_run_update_item_custom_metadata_not_found(mock_get_client: MagicMock) -> None: """Test update item metadata with non-existent run or item.""" mock_client = MagicMock() diff --git a/tests/aignostics/application/utils_test.py b/tests/aignostics/application/utils_test.py index 1b30a006c..0f2e66c93 100644 --- a/tests/aignostics/application/utils_test.py +++ b/tests/aignostics/application/utils_test.py @@ -6,9 +6,15 @@ from unittest.mock import MagicMock, Mock, patch import pytest -from aignx.codegen.models import ArtifactOutput, ArtifactState, ArtifactTerminationReason, ItemOutput +from aignostics.constants import ( + HETA_APPLICATION_ID, + TEST_APP_APPLICATION_ID, + WSI_SUPPORTED_FILE_EXTENSIONS, + WSI_SUPPORTED_FILE_EXTENSIONS_TEST_APP, +) -from aignostics.application._utils import ( +from aignostics_sdk._codegen.models import ArtifactOutput, ArtifactState, ArtifactTerminationReason, ItemOutput +from aignostics_sdk.application._utils import ( application_run_status_to_str, get_mime_type_for_artifact, get_supported_extensions_for_application, @@ -24,13 +30,7 @@ validate_scheduling_constraints, write_metadata_dict_to_csv, ) -from aignostics.constants import ( - HETA_APPLICATION_ID, - TEST_APP_APPLICATION_ID, - WSI_SUPPORTED_FILE_EXTENSIONS, - WSI_SUPPORTED_FILE_EXTENSIONS_TEST_APP, -) -from aignostics.platform import ( +from aignostics_sdk.platform import ( ItemResult, ItemState, ItemTerminationReason, @@ -203,9 +203,10 @@ def test_get_supported_extensions_returns_different_sets() -> None: heta_extensions = get_supported_extensions_for_application(HETA_APPLICATION_ID) test_extensions = get_supported_extensions_for_application(TEST_APP_APPLICATION_ID) - # Verify they are separate sets (even if they might have the same contents) - assert heta_extensions is WSI_SUPPORTED_FILE_EXTENSIONS - assert test_extensions is WSI_SUPPORTED_FILE_EXTENSIONS_TEST_APP + # Verify they are the correct sets (using value equality; object identity not guaranteed + # now that constants may come from different modules) + assert heta_extensions == WSI_SUPPORTED_FILE_EXTENSIONS + assert test_extensions == WSI_SUPPORTED_FILE_EXTENSIONS_TEST_APP # Tests for application_run_status_to_str @@ -542,7 +543,7 @@ def test_get_mime_type_for_output_artifact_element_default() -> None: @pytest.mark.unit -@patch("aignostics.application._utils.console") +@patch("aignostics_sdk.application._utils.console") def test_print_runs_verbose_with_single_run(mock_console: Mock) -> None: """Test verbose printing of a single run.""" run = _make_run_data( @@ -566,7 +567,7 @@ def test_print_runs_verbose_with_single_run(mock_console: Mock) -> None: @pytest.mark.unit -@patch("aignostics.application._utils.console") +@patch("aignostics_sdk.application._utils.console") def test_print_runs_non_verbose_with_error(mock_console: Mock) -> None: """Test non-verbose printing of runs with errors.""" run = _make_run_data( @@ -590,7 +591,7 @@ def test_print_runs_non_verbose_with_error(mock_console: Mock) -> None: @pytest.mark.unit -@patch("aignostics.application._utils.console") +@patch("aignostics_sdk.application._utils.console") def test_retrieve_and_print_run_details_with_items(mock_console: Mock) -> None: """Test retrieving and printing run details with items.""" terminated_at = datetime(2025, 1, 1, 13, 0, 0, tzinfo=UTC) @@ -630,7 +631,7 @@ def test_retrieve_and_print_run_details_with_items(mock_console: Mock) -> None: @pytest.mark.unit -@patch("aignostics.application._utils.console") +@patch("aignostics_sdk.application._utils.console") def test_retrieve_and_print_run_details_no_items(mock_console: Mock) -> None: """Test retrieving and printing run details with no items.""" run_data = _make_run_data(run_id="run-empty") @@ -652,7 +653,7 @@ def test_retrieve_and_print_run_details_no_items(mock_console: Mock) -> None: "hide_platform_queue_position", [True, False], ) -@patch("aignostics.application._utils.console") +@patch("aignostics_sdk.application._utils.console") def test_retrieve_and_print_run_details_can_hide_platform_position( mock_console: Mock, hide_platform_queue_position: bool ) -> None: @@ -846,7 +847,7 @@ def test_queue_position_string_from_run_with_only_platform_position() -> None: @pytest.mark.unit -@patch("aignostics.application._utils.console") +@patch("aignostics_sdk.application._utils.console") def test_retrieve_and_print_run_details_summarize_mode(mock_console: Mock) -> None: """Test summarize mode shows concise output with external ID, state, and errors.""" terminated_at = datetime(2025, 1, 1, 13, 0, 0, tzinfo=UTC) @@ -901,7 +902,7 @@ def test_retrieve_and_print_run_details_summarize_mode(mock_console: Mock) -> No @pytest.mark.unit -@patch("aignostics.application._utils.console") +@patch("aignostics_sdk.application._utils.console") def test_retrieve_and_print_run_details_summarize_no_items(mock_console: Mock) -> None: """Test summarize mode with no items shows appropriate message.""" run_data = _make_run_data(run_id="run-no-items") @@ -918,7 +919,7 @@ def test_retrieve_and_print_run_details_summarize_no_items(mock_console: Mock) - @pytest.mark.unit -@patch("aignostics.application._utils.console") +@patch("aignostics_sdk.application._utils.console") def test_retrieve_and_print_run_details_summarize_with_run_error(mock_console: Mock) -> None: """Test summarize mode shows run-level errors.""" run_data = _make_run_data( @@ -943,7 +944,7 @@ def test_retrieve_and_print_run_details_summarize_with_run_error(mock_console: M @pytest.mark.unit -@patch("aignostics.application._utils.console") +@patch("aignostics_sdk.application._utils.console") def test_retrieve_and_print_run_details_default_is_detailed(mock_console: Mock) -> None: """Test that default mode (summarize=False) shows detailed output with artifacts.""" terminated_at = datetime(2025, 1, 1, 13, 0, 0, tzinfo=UTC) diff --git a/tests/aignostics/bucket/cli_test.py b/tests/aignostics/bucket/cli_test.py index 6561f192b..59e786c9b 100644 --- a/tests/aignostics/bucket/cli_test.py +++ b/tests/aignostics/bucket/cli_test.py @@ -5,9 +5,9 @@ from pathlib import Path import pytest +from aignostics.cli import cli from typer.testing import CliRunner -from aignostics.cli import cli from tests.conftest import normalize_output MESSAGE_NOT_YET_IMPLEMENTED = "NOT YET IMPLEMENTED" diff --git a/tests/aignostics/bucket/gui_test.py b/tests/aignostics/bucket/gui_test.py index 3d7a141f3..2b33a5096 100644 --- a/tests/aignostics/bucket/gui_test.py +++ b/tests/aignostics/bucket/gui_test.py @@ -9,10 +9,10 @@ import psutil import pytest +from aignostics.cli import cli from nicegui.testing import User from typer.testing import CliRunner -from aignostics.cli import cli from tests.conftest import assert_notified diff --git a/tests/aignostics/bucket/service_test.py b/tests/aignostics/bucket/service_test.py index 939910296..c2f065223 100644 --- a/tests/aignostics/bucket/service_test.py +++ b/tests/aignostics/bucket/service_test.py @@ -4,7 +4,6 @@ from unittest import mock import pytest - from aignostics.bucket._service import Service # --------------------------------------------------------------------------- diff --git a/tests/aignostics/bucket/settings_test.py b/tests/aignostics/bucket/settings_test.py index 10f04ec29..629c4538d 100644 --- a/tests/aignostics/bucket/settings_test.py +++ b/tests/aignostics/bucket/settings_test.py @@ -3,11 +3,10 @@ import json import pytest -from pydantic import ValidationError -from typer.testing import CliRunner - from aignostics.bucket._settings import Settings from aignostics.cli import cli +from pydantic import ValidationError +from typer.testing import CliRunner @pytest.mark.unit diff --git a/tests/aignostics/cli_test.py b/tests/aignostics/cli_test.py index af8214d89..87e067841 100644 --- a/tests/aignostics/cli_test.py +++ b/tests/aignostics/cli_test.py @@ -8,12 +8,12 @@ from unittest.mock import patch import pytest +from aignostics.cli import cli +from aignostics.constants import WINDOW_TITLE from fastmcp import FastMCP from typer.testing import CliRunner -from aignostics.cli import cli -from aignostics.constants import WINDOW_TITLE -from aignostics.utils import ( +from aignostics_sdk.utils import ( __python_version__, __version__, ) diff --git a/tests/aignostics/dataset/cli_test.py b/tests/aignostics/dataset/cli_test.py index 23e1f5236..598df66be 100644 --- a/tests/aignostics/dataset/cli_test.py +++ b/tests/aignostics/dataset/cli_test.py @@ -7,9 +7,9 @@ from unittest.mock import MagicMock, patch import pytest +from aignostics.cli import cli from typer.testing import CliRunner -from aignostics.cli import cli from tests.conftest import normalize_output from tests.constants_test import SPOT_1_FILENAME, SPOT_1_GS_URL diff --git a/tests/aignostics/dataset/service_test.py b/tests/aignostics/dataset/service_test.py index d550ab8a5..452ac8506 100644 --- a/tests/aignostics/dataset/service_test.py +++ b/tests/aignostics/dataset/service_test.py @@ -4,7 +4,6 @@ from unittest import mock import pytest - from aignostics.dataset._service import _active_processes, _cleanup_processes, _terminate_process diff --git a/tests/aignostics/notebook/service_test.py b/tests/aignostics/notebook/service_test.py index 25ea7b032..40fc3700c 100644 --- a/tests/aignostics/notebook/service_test.py +++ b/tests/aignostics/notebook/service_test.py @@ -6,9 +6,8 @@ from unittest.mock import MagicMock, patch import pytest -from nicegui.testing import User - from aignostics.notebook._service import MARIMO_SERVER_STARTUP_TIMEOUT, Service, _get_runner, _Runner +from nicegui.testing import User @pytest.mark.integration diff --git a/tests/aignostics/platform/authentication_test.py b/tests/aignostics/platform/authentication_test.py index 5e21a2278..5d77b342f 100644 --- a/tests/aignostics/platform/authentication_test.py +++ b/tests/aignostics/platform/authentication_test.py @@ -16,7 +16,7 @@ from pydantic import SecretStr from requests_oauthlib import OAuth2Session -from aignostics.platform._authentication import ( +from aignostics_sdk.platform._authentication import ( _access_token_from_refresh_token, _authenticate, _can_open_browser, @@ -27,7 +27,7 @@ remove_cached_token, verify_and_decode_token, ) -from aignostics.platform._messages import ( +from aignostics_sdk.platform._messages import ( AUTHENTICATION_FAILED, AUTHENTICATION_FAILED_ACCESS_TOKEN_FROM_REFRESH_TOKEN, AUTHENTICATION_FAILED_TOKEN_VERIFICATION, @@ -42,7 +42,7 @@ def mock_settings() -> MagicMock: Yields: MagicMock: A mock of the authentication settings. """ - with patch("aignostics.platform._authentication.settings") as mock_settings: + with patch("aignostics_sdk.platform._authentication.settings") as mock_settings: settings = MagicMock() # Using tmp_path in a controlled test environment is acceptable for testing settings.token_file = Path("mock_token_path") # Avoid hardcoded /tmp path @@ -109,7 +109,7 @@ def mock_can_open_browser() -> None: Yields: None: This fixture doesn't yield a value. """ - with patch("aignostics.platform._authentication._can_open_browser", return_value=False): + with patch("aignostics_sdk.platform._authentication._can_open_browser", return_value=False): yield @@ -156,9 +156,9 @@ def test_get_token_from_cache_missing_expiry(mock_settings: MagicMock, cached_to with ( patch.object(Path, "exists", return_value=True), patch.object(Path, "read_text", return_value=cached_token_missing_expiry), - patch("aignostics.platform._authentication._authenticate", return_value="new.token"), + patch("aignostics_sdk.platform._authentication._authenticate", return_value="new.token"), patch( - "aignostics.platform._authentication.verify_and_decode_token", + "aignostics_sdk.platform._authentication.verify_and_decode_token", return_value={"exp": int(time.time()) + 3600}, ), patch.object(Path, "write_text", mock_write_text), @@ -179,9 +179,9 @@ def test_get_token_from_cache_expired(record_property, mock_settings, expired_to with ( patch.object(Path, "exists", return_value=True), patch.object(Path, "read_text", return_value=expired_token), - patch("aignostics.platform._authentication._authenticate", return_value="new.token"), + patch("aignostics_sdk.platform._authentication._authenticate", return_value="new.token"), patch( - "aignostics.platform._authentication.verify_and_decode_token", + "aignostics_sdk.platform._authentication.verify_and_decode_token", return_value={"exp": int(time.time()) + 3600}, ), patch.object(Path, "write_text", mock_write_text), @@ -200,9 +200,9 @@ def test_get_token_no_cache(record_property, mock_settings) -> None: mock_write_text = MagicMock() with ( - patch("aignostics.platform._authentication._authenticate", return_value="new.token"), + patch("aignostics_sdk.platform._authentication._authenticate", return_value="new.token"), patch( - "aignostics.platform._authentication.verify_and_decode_token", + "aignostics_sdk.platform._authentication.verify_and_decode_token", return_value={"exp": int(time.time()) + 3600}, ), patch.object(Path, "write_text", mock_write_text), @@ -221,7 +221,7 @@ def test_authenticate_uses_refresh_token_when_available(record_property, mock_se mock_settings.return_value.refresh_token = SecretStr("test-refresh-token") with patch( - "aignostics.platform._authentication._access_token_from_refresh_token", return_value="refreshed.token" + "aignostics_sdk.platform._authentication._access_token_from_refresh_token", return_value="refreshed.token" ) as mock_refresh: token = _authenticate(use_device_flow=False) assert token == "refreshed.token" # noqa: S105 - Test credential @@ -235,9 +235,9 @@ def test_authenticate_uses_browser_flow_when_available(record_property, mock_set mock_settings.return_value.refresh_token = None with ( - patch("aignostics.platform._authentication._can_open_browser", return_value=True), + patch("aignostics_sdk.platform._authentication._can_open_browser", return_value=True), patch( - "aignostics.platform._authentication._perform_authorization_code_with_pkce_flow", + "aignostics_sdk.platform._authentication._perform_authorization_code_with_pkce_flow", return_value="browser.token", ) as mock_browser, ): @@ -253,9 +253,9 @@ def test_authenticate_falls_back_to_device_flow(record_property, mock_settings) mock_settings.return_value.refresh_token = None with ( - patch("aignostics.platform._authentication._can_open_browser", return_value=False), + patch("aignostics_sdk.platform._authentication._can_open_browser", return_value=False), patch( - "aignostics.platform._authentication._perform_device_flow", return_value="device.token" + "aignostics_sdk.platform._authentication._perform_device_flow", return_value="device.token" ) as mock_device, ): token = _authenticate(use_device_flow=True) @@ -270,8 +270,8 @@ def test_authenticate_raises_error_on_failure(record_property, mock_settings) -> mock_settings.return_value.refresh_token = None with ( - patch("aignostics.platform._authentication._can_open_browser", return_value=False), - patch("aignostics.platform._authentication._perform_device_flow", return_value=None), + patch("aignostics_sdk.platform._authentication._can_open_browser", return_value=False), + patch("aignostics_sdk.platform._authentication._perform_device_flow", return_value=None), pytest.raises(RuntimeError, match=AUTHENTICATION_FAILED), ): _authenticate(use_device_flow=True) @@ -324,7 +324,7 @@ def test_can_open_browser_true(record_property) -> None: # We need to override the autouse fixture here with ( patch("webbrowser.get", return_value=MagicMock()), - patch("aignostics.platform._authentication._can_open_browser", wraps=_can_open_browser), + patch("aignostics_sdk.platform._authentication._can_open_browser", wraps=_can_open_browser), ): assert _can_open_browser() is True @@ -371,10 +371,10 @@ def __exit__(self, *args) -> None: mock_auth_result.error = None with ( - patch("aignostics.platform._authentication.OAuth2Session", return_value=mock_session), - patch("aignostics.platform._authentication.HTTPServer", MockHTTPServer), + patch("aignostics_sdk.platform._authentication.OAuth2Session", return_value=mock_session), + patch("aignostics_sdk.platform._authentication.HTTPServer", MockHTTPServer), patch("urllib.parse.urlparse", return_value=mock_redirect_parsed), - patch("aignostics.platform._authentication.AuthenticationResult", return_value=mock_auth_result), + patch("aignostics_sdk.platform._authentication.AuthenticationResult", return_value=mock_auth_result), ): mock_server.handle_request.side_effect = lambda: None token = _perform_authorization_code_with_pkce_flow() @@ -399,7 +399,7 @@ def test_perform_authorization_code_flow_invalid_redirect(record_property, mock_ mock_session = MagicMock(spec=OAuth2Session) mock_session.authorization_url.return_value = ("https://test.auth/authorize?code_challenge=abc", None) - with patch("aignostics.platform._authentication.OAuth2Session", return_value=mock_session): + with patch("aignostics_sdk.platform._authentication.OAuth2Session", return_value=mock_session): # Create a mock redirect URI with invalid hostname/port mock_redirect_parsed = MagicMock() mock_redirect_parsed.hostname = None # Invalid hostname @@ -445,10 +445,10 @@ def __exit__(self, *args) -> None: mock_auth_result.error = "Authentication failed" with ( - patch("aignostics.platform._authentication.OAuth2Session", return_value=mock_session), - patch("aignostics.platform._authentication.HTTPServer", MockHTTPServer), + patch("aignostics_sdk.platform._authentication.OAuth2Session", return_value=mock_session), + patch("aignostics_sdk.platform._authentication.HTTPServer", MockHTTPServer), patch("urllib.parse.urlparse", return_value=mock_redirect_parsed), - patch("aignostics.platform._authentication.AuthenticationResult", return_value=mock_auth_result), + patch("aignostics_sdk.platform._authentication.AuthenticationResult", return_value=mock_auth_result), ): # Simulate a failed server response def handle_request_side_effect(): @@ -588,10 +588,10 @@ def test_authorization_flow_sets_socket_reuse(record_property, mock_settings) -> # Mock the HTTPServer context manager with ( - patch("aignostics.platform._authentication.HTTPServer") as mock_http_server, + patch("aignostics_sdk.platform._authentication.HTTPServer") as mock_http_server, patch("urllib.parse.urlparse") as mock_urlparse, - patch("aignostics.platform._authentication.OAuth2Session") as mock_oauth, - patch("aignostics.platform._authentication.webbrowser"), + patch("aignostics_sdk.platform._authentication.OAuth2Session") as mock_oauth, + patch("aignostics_sdk.platform._authentication.webbrowser"), ): # Setup mocks mock_urlparse.return_value.hostname = "localhost" @@ -639,11 +639,11 @@ def http_server_side_effect(*args, **kwargs): mock_auth_result.error = None with ( - patch("aignostics.platform._authentication.OAuth2Session", return_value=mock_session), - patch("aignostics.platform._authentication.HTTPServer", side_effect=http_server_side_effect), + patch("aignostics_sdk.platform._authentication.OAuth2Session", return_value=mock_session), + patch("aignostics_sdk.platform._authentication.HTTPServer", side_effect=http_server_side_effect), patch("urllib.parse.urlparse") as mock_urlparse, patch("time.sleep") as mock_sleep, - patch("aignostics.platform._authentication.AuthenticationResult", return_value=mock_auth_result), + patch("aignostics_sdk.platform._authentication.AuthenticationResult", return_value=mock_auth_result), ): mock_urlparse.return_value.hostname = "localhost" mock_urlparse.return_value.port = 8000 @@ -718,10 +718,10 @@ def test_get_token_calls_sentry_set_user(record_property, mock_settings) -> None mock_sentry_sdk = MagicMock() with ( - patch("aignostics.platform._authentication.sentry_sdk", mock_sentry_sdk), - patch("aignostics.platform._authentication._authenticate", return_value="test.token"), + patch("aignostics_sdk.platform._authentication.sentry_sdk", mock_sentry_sdk), + patch("aignostics_sdk.platform._authentication._authenticate", return_value="test.token"), patch( - "aignostics.platform._authentication.verify_and_decode_token", + "aignostics_sdk.platform._authentication.verify_and_decode_token", return_value=mock_claims, ), patch.object(Path, "exists", return_value=False), # Force authentication @@ -750,10 +750,10 @@ def test_get_token_sentry_unavailable(record_property, mock_settings) -> None: } with ( - patch("aignostics.platform._authentication.sentry_sdk", None), # Simulate sentry_sdk not available - patch("aignostics.platform._authentication._authenticate", return_value="test.token"), + patch("aignostics_sdk.platform._authentication.sentry_sdk", None), # Simulate sentry_sdk not available + patch("aignostics_sdk.platform._authentication._authenticate", return_value="test.token"), patch( - "aignostics.platform._authentication.verify_and_decode_token", + "aignostics_sdk.platform._authentication.verify_and_decode_token", return_value=mock_claims, ), patch.object(Path, "exists", return_value=False), # Force authentication @@ -781,10 +781,10 @@ def test_get_token_sentry_missing_sub_claim(record_property, mock_settings) -> N mock_sentry_sdk = MagicMock() with ( - patch("aignostics.platform._authentication.sentry_sdk", mock_sentry_sdk), - patch("aignostics.platform._authentication._authenticate", return_value="test.token"), + patch("aignostics_sdk.platform._authentication.sentry_sdk", mock_sentry_sdk), + patch("aignostics_sdk.platform._authentication._authenticate", return_value="test.token"), patch( - "aignostics.platform._authentication.verify_and_decode_token", + "aignostics_sdk.platform._authentication.verify_and_decode_token", return_value=mock_claims, ), patch.object(Path, "exists", return_value=False), # Force authentication @@ -807,10 +807,10 @@ def test_get_token_sentry_handles_token_verification_error(record_property, mock mock_sentry_sdk = MagicMock() with ( - patch("aignostics.platform._authentication.sentry_sdk", mock_sentry_sdk), - patch("aignostics.platform._authentication._authenticate", return_value="test.token"), + patch("aignostics_sdk.platform._authentication.sentry_sdk", mock_sentry_sdk), + patch("aignostics_sdk.platform._authentication._authenticate", return_value="test.token"), patch( - "aignostics.platform._authentication.verify_and_decode_token", + "aignostics_sdk.platform._authentication.verify_and_decode_token", side_effect=RuntimeError("Token verification failed"), ), patch.object(Path, "exists", return_value=False), # Force authentication @@ -878,7 +878,8 @@ def test_no_retry_on_client_error(mock_settings, caplog) -> None: retry_logs = [ record for record in caplog.records - if "Retrying aignostics.platform._authentication._access_token_from_refresh_token" in record.getMessage() + if "Retrying aignostics_sdk.platform._authentication._access_token_from_refresh_token" + in record.getMessage() ] assert len(retry_logs) == 0, "Should not log retry attempts for 4xx errors" @@ -916,7 +917,8 @@ def side_effect(*args, **kwargs): retry_logs = [ record for record in caplog.records - if "Retrying aignostics.platform._authentication._do_access_token_from_refresh_token" in record.getMessage() + if "Retrying aignostics_sdk.platform._authentication._do_access_token_from_refresh_token" + in record.getMessage() ] assert len(retry_logs) > 0, "Should log retry attempts for 5xx errors" @@ -950,7 +952,8 @@ def side_effect(*args, **kwargs): retry_logs = [ record for record in caplog.records - if "Retrying aignostics.platform._authentication._do_access_token_from_refresh_token" in record.getMessage() + if "Retrying aignostics_sdk.platform._authentication._do_access_token_from_refresh_token" + in record.getMessage() ] assert len(retry_logs) > 0, "Should log retry attempts for connection errors" @@ -966,7 +969,7 @@ def test_jwk_client_cache_returns_same_instance(clear_jwk_cache, mock_settings) The LRU cache should ensure that multiple calls with the same URL return the same PyJWKClient instance, avoiding redundant client creation. """ - from aignostics.platform._authentication import _get_jwk_client + from aignostics_sdk.platform._authentication import _get_jwk_client url = "https://test.auth/.well-known/jwks.json" timeout = mock_settings.return_value.auth_timeout @@ -993,7 +996,7 @@ def test_jwk_client_cache_different_urls(clear_jwk_cache, mock_settings) -> None The cache should distinguish between different URLs and create separate PyJWKClient instances for each unique URL. """ - from aignostics.platform._authentication import _get_jwk_client + from aignostics_sdk.platform._authentication import _get_jwk_client url1 = "https://test1.auth/.well-known/jwks.json" url2 = "https://test2.auth/.well-known/jwks.json" @@ -1021,7 +1024,7 @@ def test_jwk_client_cache_info(clear_jwk_cache, mock_settings) -> None: This verifies that the LRU cache is tracking hits and misses correctly. """ - from aignostics.platform._authentication import _get_jwk_client + from aignostics_sdk.platform._authentication import _get_jwk_client info = _get_jwk_client.cache_info() assert info.hits == 0 @@ -1057,7 +1060,7 @@ def test_jwk_client_cache_respects_settings(clear_jwk_cache, mock_settings) -> N The cache should use settings values when creating PyJWKClient instances. """ - from aignostics.platform._authentication import _get_jwk_client + from aignostics_sdk.platform._authentication import _get_jwk_client url = "https://test.auth/.well-known/jwks.json" timeout = mock_settings.return_value.auth_timeout @@ -1081,7 +1084,7 @@ def test_jwk_client_cache_used_in_verification(clear_jwk_cache, mock_settings) - Multiple token verifications should reuse the cached PyJWKClient instance, reducing the overhead of client creation. """ - from aignostics.platform._authentication import _get_jwk_client + from aignostics_sdk.platform._authentication import _get_jwk_client mock_jwt_client = MagicMock() mock_signing_key = MagicMock() @@ -1116,7 +1119,7 @@ def test_jwk_client_cache_size_limit(clear_jwk_cache, mock_settings) -> None: When more than 4 unique parameter combinations are cached, the least recently used ones should be evicted. """ - from aignostics.platform._authentication import _get_jwk_client + from aignostics_sdk.platform._authentication import _get_jwk_client timeout = mock_settings.return_value.auth_timeout lifespan = mock_settings.return_value.auth_jwk_set_cache_ttl @@ -1162,7 +1165,9 @@ def test_successful_token_verification_no_retry(mock_settings) -> None: mock_jwt_client.get_signing_key_from_jwt.return_value = mock_signing_key with ( - patch("aignostics.platform._authentication._get_jwk_client", return_value=mock_jwt_client) as mock_get_jwk, + patch( + "aignostics_sdk.platform._authentication._get_jwk_client", return_value=mock_jwt_client + ) as mock_get_jwk, patch("jwt.decode", return_value={"sub": "user-id", "exp": int(time.time()) + 3600}), ): result = verify_and_decode_token("valid.token") @@ -1196,7 +1201,7 @@ def decode_side_effect(*args, **kwargs): start_time = time.time() with ( - patch("aignostics.platform._authentication._get_jwk_client", return_value=mock_jwt_client), + patch("aignostics_sdk.platform._authentication._get_jwk_client", return_value=mock_jwt_client), patch("jwt.decode", side_effect=decode_side_effect), pytest.raises(RuntimeError, match=AUTHENTICATION_FAILED_TOKEN_VERIFICATION), caplog.at_level(logging.DEBUG), @@ -1215,7 +1220,7 @@ def decode_side_effect(*args, **kwargs): retry_logs = [ record for record in caplog.records - if "Retrying aignostics.platform._authentication._do_verify_and_decode_token" in record.getMessage() + if "Retrying aignostics_sdk.platform._authentication._do_verify_and_decode_token" in record.getMessage() ] assert len(retry_logs) == 0, "Should not log retry attempts for JWT decode errors" @@ -1239,7 +1244,7 @@ def get_signing_key_side_effect(*args, **kwargs): mock_jwt_client.get_signing_key_from_jwt.side_effect = get_signing_key_side_effect with ( - patch("aignostics.platform._authentication._get_jwk_client", return_value=mock_jwt_client), + patch("aignostics_sdk.platform._authentication._get_jwk_client", return_value=mock_jwt_client), pytest.raises(RuntimeError, match=AUTHENTICATION_FAILED_TOKEN_VERIFICATION), caplog.at_level(logging.DEBUG), ): @@ -1252,7 +1257,7 @@ def get_signing_key_side_effect(*args, **kwargs): retry_logs = [ record for record in caplog.records - if "Retrying aignostics.platform._authentication._do_verify_and_decode_token" in record.getMessage() + if "Retrying aignostics_sdk.platform._authentication._do_verify_and_decode_token" in record.getMessage() ] assert len(retry_logs) > 0, "Should log retry attempts for JWK connection errors" @@ -1281,7 +1286,7 @@ def get_signing_key_side_effect(*args, **kwargs): mock_jwt_client.get_signing_key_from_jwt.side_effect = get_signing_key_side_effect with ( - patch("aignostics.platform._authentication._get_jwk_client", return_value=mock_jwt_client), + patch("aignostics_sdk.platform._authentication._get_jwk_client", return_value=mock_jwt_client), patch("jwt.decode", return_value={"sub": "user-id", "exp": int(time.time()) + 3600}), caplog.at_level(logging.DEBUG), ): @@ -1297,7 +1302,7 @@ def get_signing_key_side_effect(*args, **kwargs): retry_logs = [ record for record in caplog.records - if "Retrying aignostics.platform._authentication._do_verify_and_decode_token" in record.getMessage() + if "Retrying aignostics_sdk.platform._authentication._do_verify_and_decode_token" in record.getMessage() ] assert len(retry_logs) == 1, "Should log exactly one retry attempt" @@ -1321,7 +1326,7 @@ def get_signing_key_side_effect(*args, **kwargs): mock_jwt_client.get_signing_key_from_jwt.side_effect = get_signing_key_side_effect with ( - patch("aignostics.platform._authentication._get_jwk_client", return_value=mock_jwt_client), + patch("aignostics_sdk.platform._authentication._get_jwk_client", return_value=mock_jwt_client), pytest.raises(RuntimeError, match=AUTHENTICATION_FAILED_TOKEN_VERIFICATION), caplog.at_level(logging.DEBUG), ): @@ -1337,6 +1342,6 @@ def get_signing_key_side_effect(*args, **kwargs): retry_logs = [ record for record in caplog.records - if "Retrying aignostics.platform._authentication._do_verify_and_decode_token" in record.getMessage() + if "Retrying aignostics_sdk.platform._authentication._do_verify_and_decode_token" in record.getMessage() ] assert len(retry_logs) == 0, "Should not log retry attempts for non-connection JWK errors" diff --git a/tests/aignostics/platform/cli_test.py b/tests/aignostics/platform/cli_test.py index 0395f5987..d41f9c880 100644 --- a/tests/aignostics/platform/cli_test.py +++ b/tests/aignostics/platform/cli_test.py @@ -3,11 +3,11 @@ from unittest.mock import patch import pytest +from aignostics.cli import cli from typer.testing import CliRunner -from aignostics.cli import cli -from aignostics.platform import Me -from aignostics.platform._service import Organization, TokenInfo, User, UserInfo +from aignostics_sdk.platform import Me +from aignostics_sdk.platform._service import Organization, TokenInfo, User, UserInfo from tests.conftest import normalize_output @@ -189,7 +189,7 @@ def test_login_out_info_e2e(record_property, runner: CliRunner) -> None: """Test successful logout command.""" record_property("tested-item-id", "SPEC-PLATFORM-SERVICE") with ( - patch("aignostics.platform._service.Service.logout", return_value=True), + patch("aignostics_sdk.platform._service.Service.logout", return_value=True), ): result = runner.invoke(cli, ["user", "login", "--relogin"]) assert result.exit_code == 0 @@ -213,7 +213,7 @@ def test_login_out_info_e2e(record_property, runner: CliRunner) -> None: def test_logout_success(record_property, runner: CliRunner) -> None: """Test successful logout command.""" record_property("tested-item-id", "SPEC-PLATFORM-SERVICE") - with patch("aignostics.platform._service.Service.logout", return_value=True): + with patch("aignostics_sdk.platform._service.Service.logout", return_value=True): result = runner.invoke(cli, ["user", "logout"]) assert result.exit_code == 0 @@ -224,7 +224,7 @@ def test_logout_success(record_property, runner: CliRunner) -> None: def test_logout_not_logged_in(record_property, runner: CliRunner) -> None: """Test logout command when not logged in.""" record_property("tested-item-id", "SPEC-PLATFORM-SERVICE") - with patch("aignostics.platform._service.Service.logout", return_value=False): + with patch("aignostics_sdk.platform._service.Service.logout", return_value=False): result = runner.invoke(cli, ["user", "logout"]) assert result.exit_code == 2 @@ -235,7 +235,7 @@ def test_logout_not_logged_in(record_property, runner: CliRunner) -> None: def test_logout_error(record_property, runner: CliRunner) -> None: """Test logout command when an error occurs.""" record_property("tested-item-id", "SPEC-PLATFORM-SERVICE") - with patch("aignostics.platform._service.Service.logout", side_effect=RuntimeError("Test error")): + with patch("aignostics_sdk.platform._service.Service.logout", side_effect=RuntimeError("Test error")): result = runner.invoke(cli, ["user", "logout"]) assert result.exit_code == 1 @@ -246,7 +246,7 @@ def test_logout_error(record_property, runner: CliRunner) -> None: def test_login_success(record_property, runner: CliRunner) -> None: """Test successful login command.""" record_property("tested-item-id", "SPEC-PLATFORM-SERVICE") - with patch("aignostics.platform._service.Service.login", return_value=True): + with patch("aignostics_sdk.platform._service.Service.login", return_value=True): result = runner.invoke(cli, ["user", "login"]) assert result.exit_code == 0 @@ -257,7 +257,7 @@ def test_login_success(record_property, runner: CliRunner) -> None: def test_login_with_relogin_flag(record_property, runner: CliRunner) -> None: """Test login command with relogin flag.""" record_property("tested-item-id", "SPEC-PLATFORM-SERVICE") - with patch("aignostics.platform._service.Service.login", return_value=True) as mock_login: + with patch("aignostics_sdk.platform._service.Service.login", return_value=True) as mock_login: result = runner.invoke(cli, ["user", "login", "--relogin"]) assert result.exit_code == 0 @@ -269,7 +269,7 @@ def test_login_with_relogin_flag(record_property, runner: CliRunner) -> None: def test_login_failure(record_property, runner: CliRunner) -> None: """Test login command when login fails.""" record_property("tested-item-id", "SPEC-PLATFORM-SERVICE") - with patch("aignostics.platform._service.Service.login", return_value=False): + with patch("aignostics_sdk.platform._service.Service.login", return_value=False): result = runner.invoke(cli, ["user", "login"]) assert result.exit_code == 1 @@ -280,7 +280,7 @@ def test_login_failure(record_property, runner: CliRunner) -> None: def test_login_error(record_property, runner: CliRunner) -> None: """Test login command when an error occurs.""" record_property("tested-item-id", "SPEC-PLATFORM-SERVICE") - with patch("aignostics.platform._service.Service.login", side_effect=RuntimeError("Test error")): + with patch("aignostics_sdk.platform._service.Service.login", side_effect=RuntimeError("Test error")): result = runner.invoke(cli, ["user", "login"]) assert result.exit_code == 1 @@ -322,7 +322,7 @@ def test_whoami_success(record_property, runner: CliRunner) -> None: organization=mock_organization, ) - with patch("aignostics.platform._service.Service.get_user_info", return_value=mock_user_info): + with patch("aignostics_sdk.platform._service.Service.get_user_info", return_value=mock_user_info): result = runner.invoke(cli, ["user", "whoami"]) assert result.exit_code == 0 @@ -369,7 +369,7 @@ def test_whoami_with_relogin_flag(record_property, runner: CliRunner) -> None: ) with patch( - "aignostics.platform._service.Service.get_user_info", return_value=mock_user_info + "aignostics_sdk.platform._service.Service.get_user_info", return_value=mock_user_info ) as mock_get_user_info: result = runner.invoke(cli, ["user", "whoami", "--relogin"]) @@ -382,7 +382,7 @@ def test_whoami_not_logged_in(record_property, runner: CliRunner) -> None: """Test whoami command when not logged in.""" record_property("tested-item-id", "SPEC-PLATFORM-SERVICE") with patch( - "aignostics.platform._service.Service.get_user_info", + "aignostics_sdk.platform._service.Service.get_user_info", side_effect=RuntimeError("Could not retrieve user info"), ): result = runner.invoke(cli, ["user", "whoami"]) @@ -395,7 +395,7 @@ def test_whoami_not_logged_in(record_property, runner: CliRunner) -> None: def test_whoami_error(record_property, runner: CliRunner) -> None: """Test whoami command when an error occurs.""" record_property("tested-item-id", "SPEC-PLATFORM-SERVICE") - with patch("aignostics.platform._service.Service.get_user_info", side_effect=RuntimeError("Test error")): + with patch("aignostics_sdk.platform._service.Service.get_user_info", side_effect=RuntimeError("Test error")): result = runner.invoke(cli, ["user", "whoami"]) assert result.exit_code == 1 @@ -436,7 +436,7 @@ def test_whoami_success_with_no_org_name(record_property, runner: CliRunner) -> user=mock_user, organization=mock_organization, ) - with patch("aignostics.platform._service.Service.get_user_info", return_value=mock_user_info): + with patch("aignostics_sdk.platform._service.Service.get_user_info", return_value=mock_user_info): result = runner.invoke(cli, ["user", "whoami"]) assert result.exit_code == 0 @@ -481,7 +481,7 @@ def test_whoami_masks_secrets_by_default(record_property, runner: CliRunner) -> organization=mock_organization, ) - with patch("aignostics.platform._service.Service.get_user_info", return_value=mock_user_info): + with patch("aignostics_sdk.platform._service.Service.get_user_info", return_value=mock_user_info): result = runner.invoke(cli, ["user", "whoami"]) assert result.exit_code == 0 @@ -528,7 +528,7 @@ def test_whoami_shows_secrets_with_no_mask_flag(record_property, runner: CliRunn organization=mock_organization, ) - with patch("aignostics.platform._service.Service.get_user_info", return_value=mock_user_info): + with patch("aignostics_sdk.platform._service.Service.get_user_info", return_value=mock_user_info): result = runner.invoke(cli, ["user", "whoami", "--no-mask-secrets"]) assert result.exit_code == 0 diff --git a/tests/aignostics/platform/client_cache_test.py b/tests/aignostics/platform/client_cache_test.py index bea8011ba..a5df08ada 100644 --- a/tests/aignostics/platform/client_cache_test.py +++ b/tests/aignostics/platform/client_cache_test.py @@ -12,8 +12,8 @@ import pytest -from aignostics.platform._client import Client -from aignostics.platform._operation_cache import _operation_cache, cache_key_with_token +from aignostics_sdk.platform._client import Client +from aignostics_sdk.platform._operation_cache import _operation_cache, cache_key_with_token class TestCacheKeyGeneration: @@ -292,8 +292,8 @@ def test_different_tokens_use_different_cache_entries(mock_settings: MagicMock, # Client with token-1 mock_api_client.token_provider = lambda: "token-1" with ( - patch("aignostics.platform._client.get_token", return_value="token-1"), - patch("aignostics.platform._client.Client.get_api_client", return_value=mock_api_client), + patch("aignostics_sdk.platform._client.get_token", return_value="token-1"), + patch("aignostics_sdk.platform._client.Client.get_api_client", return_value=mock_api_client), ): client1 = Client(cache_token=False) client1._api = mock_api_client @@ -306,8 +306,8 @@ def test_different_tokens_use_different_cache_entries(mock_settings: MagicMock, # Client with token-2 mock_api_client.token_provider = lambda: "token-2" with ( - patch("aignostics.platform._client.get_token", return_value="token-2"), - patch("aignostics.platform._client.Client.get_api_client", return_value=mock_api_client), + patch("aignostics_sdk.platform._client.get_token", return_value="token-2"), + patch("aignostics_sdk.platform._client.Client.get_api_client", return_value=mock_api_client), ): client2 = Client(cache_token=False) client2._api = mock_api_client @@ -336,8 +336,8 @@ def test_token_change_invalidates_cache(mock_settings: MagicMock, mock_api_clien # First call with token-1 with ( - patch("aignostics.platform._client.get_token", return_value="token-1"), - patch("aignostics.platform._client.Client.get_api_client", return_value=mock_api_client), + patch("aignostics_sdk.platform._client.get_token", return_value="token-1"), + patch("aignostics_sdk.platform._client.Client.get_api_client", return_value=mock_api_client), ): client = Client(cache_token=False) client._api = mock_api_client @@ -366,8 +366,8 @@ def test_same_token_reuses_cache(mock_settings: MagicMock, mock_api_client: Magi mock_api_client.token_provider = lambda: "token-123" with ( - patch("aignostics.platform._client.get_token", return_value="token-123"), - patch("aignostics.platform._client.Client.get_api_client", return_value=mock_api_client), + patch("aignostics_sdk.platform._client.get_token", return_value="token-123"), + patch("aignostics_sdk.platform._client.Client.get_api_client", return_value=mock_api_client), ): # First client with token-123 client1 = Client(cache_token=False) @@ -404,7 +404,7 @@ def test_cache_not_populated_on_failure( """ from http import HTTPStatus - from aignx.codegen.exceptions import ServiceException + from aignostics_sdk._codegen.exceptions import ServiceException # First call fails def side_effect(*args, **kwargs): @@ -431,7 +431,7 @@ def test_exceptions_not_cached_subsequent_call_retries( """ from http import HTTPStatus - from aignx.codegen.exceptions import ServiceException + from aignostics_sdk._codegen.exceptions import ServiceException call_count = 0 mock_me_response = {"user_id": "test-user", "org_id": "test-org"} @@ -518,7 +518,7 @@ def test_cache_used_before_retry_logic( # Configure API to fail on subsequent calls from http import HTTPStatus - from aignx.codegen.exceptions import ServiceException + from aignostics_sdk._codegen.exceptions import ServiceException mock_api_client.get_me_v1_me_get.side_effect = ServiceException( status=HTTPStatus.INTERNAL_SERVER_ERROR, reason="Server error" @@ -544,8 +544,8 @@ def test_cache_is_class_level(mock_settings: MagicMock, mock_api_client: MagicMo mock_api_client.token_provider = lambda: "token-123" with ( - patch("aignostics.platform._client.get_token", return_value="token-123"), - patch("aignostics.platform._client.Client.get_api_client", return_value=mock_api_client), + patch("aignostics_sdk.platform._client.get_token", return_value="token-123"), + patch("aignostics_sdk.platform._client.Client.get_api_client", return_value=mock_api_client), ): # Create first client and call me() client1 = Client(cache_token=False) @@ -575,8 +575,8 @@ def test_cache_cleared_affects_all_clients(mock_settings: MagicMock, mock_api_cl mock_api_client.token_provider = lambda: "token-123" with ( - patch("aignostics.platform._client.get_token", return_value="token-123"), - patch("aignostics.platform._client.Client.get_api_client", return_value=mock_api_client), + patch("aignostics_sdk.platform._client.get_token", return_value="token-123"), + patch("aignostics_sdk.platform._client.Client.get_api_client", return_value=mock_api_client), ): client1 = Client(cache_token=False) client1._api = mock_api_client @@ -685,8 +685,8 @@ def test_cache_with_very_long_token(mock_settings: MagicMock, mock_api_client: M mock_api_client.token_provider = lambda: long_token with ( - patch("aignostics.platform._client.get_token", return_value=long_token), - patch("aignostics.platform._client.Client.get_api_client", return_value=mock_api_client), + patch("aignostics_sdk.platform._client.get_token", return_value=long_token), + patch("aignostics_sdk.platform._client.Client.get_api_client", return_value=mock_api_client), ): client = Client(cache_token=False) client._api = mock_api_client @@ -714,8 +714,8 @@ def test_cache_uses_current_token_from_token_provider(mock_settings: MagicMock, mock_api_client.token_provider = lambda: token_holder[0] with ( - patch("aignostics.platform._client.get_token", return_value="token-1"), - patch("aignostics.platform._client.Client.get_api_client", return_value=mock_api_client), + patch("aignostics_sdk.platform._client.get_token", return_value="token-1"), + patch("aignostics_sdk.platform._client.Client.get_api_client", return_value=mock_api_client), ): mock_api_client.get_me_v1_me_get.return_value = mock_me_response @@ -748,8 +748,8 @@ def test_cache_with_token_refresh_scenario(mock_settings: MagicMock, mock_api_cl mock_api_client.token_provider = lambda: token_holder[0] with ( - patch("aignostics.platform._client.get_token", return_value="token-initial"), - patch("aignostics.platform._client.Client.get_api_client", return_value=mock_api_client), + patch("aignostics_sdk.platform._client.get_token", return_value="token-initial"), + patch("aignostics_sdk.platform._client.Client.get_api_client", return_value=mock_api_client), ): mock_api_client.get_me_v1_me_get.return_value = mock_me_response_1 diff --git a/tests/aignostics/platform/client_me_retry_test.py b/tests/aignostics/platform/client_me_retry_test.py index 22d863a20..58b495338 100644 --- a/tests/aignostics/platform/client_me_retry_test.py +++ b/tests/aignostics/platform/client_me_retry_test.py @@ -6,11 +6,11 @@ from unittest.mock import ANY, MagicMock, Mock, patch import pytest -from aignx.codegen.exceptions import ServiceException from urllib3.exceptions import IncompleteRead, PoolError, ProtocolError, ProxyError from urllib3.exceptions import TimeoutError as Urllib3TimeoutError -from aignostics.platform._client import Client +from aignostics_sdk._codegen.exceptions import ServiceException +from aignostics_sdk.platform._client import Client class TestMeSuccess: @@ -498,7 +498,7 @@ def test_me_uses_settings_from_settings(client_with_mock_api: Client) -> None: mock_me_response = {"user_id": "test-user", "org_id": "test-org"} client_with_mock_api._api.get_me_v1_me_get.return_value = mock_me_response - with patch("aignostics.platform._client.settings") as mock_settings: + with patch("aignostics_sdk.platform._client.settings") as mock_settings: settings_obj = MagicMock() settings_obj.me_retry_attempts = 3 settings_obj.me_retry_wait_min = 0.1 @@ -535,7 +535,7 @@ def side_effect(*args, **kwargs): client_with_mock_api._api.get_me_v1_me_get.side_effect = side_effect - with patch("aignostics.platform._client.settings") as mock_settings: + with patch("aignostics_sdk.platform._client.settings") as mock_settings: # First call with max_attempts = 1 (will fail) settings_obj_1 = MagicMock() settings_obj_1.me_retry_attempts = 1 diff --git a/tests/aignostics/platform/client_pooling_test.py b/tests/aignostics/platform/client_pooling_test.py index 2741d84bf..88e645950 100644 --- a/tests/aignostics/platform/client_pooling_test.py +++ b/tests/aignostics/platform/client_pooling_test.py @@ -2,7 +2,7 @@ import pytest -from aignostics.platform._client import Client +from aignostics_sdk.platform._client import Client @pytest.mark.unit diff --git a/tests/aignostics/platform/client_token_provider_test.py b/tests/aignostics/platform/client_token_provider_test.py index d14b51621..5c09a2181 100644 --- a/tests/aignostics/platform/client_token_provider_test.py +++ b/tests/aignostics/platform/client_token_provider_test.py @@ -5,8 +5,8 @@ import pytest -from aignostics.platform._api import _AuthenticatedApi, _AuthenticatedResource, _OAuth2TokenProviderConfiguration -from aignostics.platform._client import Client +from aignostics_sdk.platform._api import _AuthenticatedApi, _AuthenticatedResource, _OAuth2TokenProviderConfiguration +from aignostics_sdk.platform._client import Client # Module-level constants for repeated string literals (extracted to satisfy # SonarQube python:S1192 — "Define a constant instead of duplicating this literal"). @@ -16,8 +16,8 @@ # a false positive for a patch-target string. The bare `# noqa: S105` form keeps # the suppression syntactically valid for SonarQube python:S7632. _DUMMY_HOST = "https://dummy" -_GET_TOKEN_PATCH = "aignostics.platform._client.get_token" # noqa: S105 -_APICLIENT_PATCH = "aignostics.platform._client.ApiClient" +_GET_TOKEN_PATCH = "aignostics_sdk.platform._client.get_token" # noqa: S105 +_APICLIENT_PATCH = "aignostics_sdk._codegen.api_client.ApiClient" @pytest.fixture(autouse=True) @@ -197,7 +197,7 @@ def test_falsy_token_provider_logs_warning() -> None: empty_provider = _make_provider("") config = _OAuth2TokenProviderConfiguration(host=_DUMMY_HOST, token_provider=empty_provider) - with patch("aignostics.platform._api.logger") as mock_logger: + with patch("aignostics_sdk.platform._api.logger") as mock_logger: result = config.auth_settings() assert result == {} @@ -211,7 +211,7 @@ def test_none_token_provider_no_warning() -> None: """Test that no warning is logged when token_provider is not set (None).""" config = _OAuth2TokenProviderConfiguration(host=_DUMMY_HOST) - with patch("aignostics.platform._api.logger") as mock_logger: + with patch("aignostics_sdk.platform._api.logger") as mock_logger: result = config.auth_settings() assert result == {} @@ -221,12 +221,12 @@ def test_none_token_provider_no_warning() -> None: @pytest.mark.unit def test_external_provider_cache_bounded() -> None: """Test that _api_client_external is bounded to _MAX_EXTERNAL_CLIENTS entries.""" - from aignostics.platform._client import _MAX_EXTERNAL_CLIENTS + from aignostics_sdk.platform._client import _MAX_EXTERNAL_CLIENTS with ( patch(_APICLIENT_PATCH), patch.object(_AuthenticatedApi, "__init__", lambda self, *a, **kw: None), - patch("aignostics.platform._client.logger") as mock_logger, + patch("aignostics_sdk.platform._client.logger") as mock_logger, ): # Create more clients than the limit, each with a distinct provider for i in range(_MAX_EXTERNAL_CLIENTS + 5): diff --git a/tests/aignostics/platform/conftest.py b/tests/aignostics/platform/conftest.py index c4e8e5275..005615cb6 100644 --- a/tests/aignostics/platform/conftest.py +++ b/tests/aignostics/platform/conftest.py @@ -5,10 +5,10 @@ import pytest -from aignostics.platform._api import _AuthenticatedApi -from aignostics.platform._client import Client -from aignostics.platform._operation_cache import _operation_cache -from aignostics.platform._service import Service +from aignostics_sdk.platform._api import _AuthenticatedApi +from aignostics_sdk.platform._client import Client +from aignostics_sdk.platform._operation_cache import _operation_cache +from aignostics_sdk.platform._service import Service @pytest.fixture @@ -18,7 +18,7 @@ def mock_settings() -> MagicMock: Yields: MagicMock: A mock of the settings. """ - with patch("aignostics.platform._client.settings") as mock_settings: + with patch("aignostics_sdk.platform._client.settings") as mock_settings: settings = MagicMock() settings.me_retry_attempts = 3 settings.me_retry_wait_min = 0.1 @@ -93,9 +93,9 @@ def client_with_mock_api(mock_api_client: MagicMock) -> t.Generator[Client, None } mock_api_client.token_provider = lambda: "test-token-123" with ( - patch("aignostics.platform._client.get_token", return_value="test-token-123"), - patch("aignostics.platform._authentication.verify_and_decode_token", return_value=mock_token_claims), - patch("aignostics.platform._client.Client.get_api_client", return_value=mock_api_client), + patch("aignostics_sdk.platform._client.get_token", return_value="test-token-123"), + patch("aignostics_sdk.platform._authentication.verify_and_decode_token", return_value=mock_token_claims), + patch("aignostics_sdk.platform._client.Client.get_api_client", return_value=mock_api_client), ): client = Client(cache_token=False) client._api = mock_api_client @@ -112,7 +112,7 @@ def clear_jwk_cache() -> t.Generator[None, None, None]: Yields: None: This fixture doesn't yield a value. """ - from aignostics.platform._authentication import _get_jwk_client + from aignostics_sdk.platform._authentication import _get_jwk_client _get_jwk_client.cache_clear() yield diff --git a/tests/aignostics/platform/e2e_test.py b/tests/aignostics/platform/e2e_test.py index 634e85da0..a701a788e 100644 --- a/tests/aignostics/platform/e2e_test.py +++ b/tests/aignostics/platform/e2e_test.py @@ -12,7 +12,11 @@ from pathlib import Path import pytest -from aignx.codegen.models import ( +from loguru import logger +from sentry_sdk import metrics + +from aignostics_sdk import platform +from aignostics_sdk._codegen.models import ( ArtifactOutput, ArtifactState, ItemOutput, @@ -21,12 +25,8 @@ RunState, SchedulingRequest, ) -from aignx.codegen.models.run_read_response import RunReadResponse -from loguru import logger -from sentry_sdk import metrics - -from aignostics import platform -from aignostics.platform import Run, RunSdkMetadata +from aignostics_sdk._codegen.models.run_read_response import RunReadResponse +from aignostics_sdk.platform import Run, RunSdkMetadata from tests.constants_test import ( HETA_APPLICATION_ID, HETA_APPLICATION_VERSION, diff --git a/tests/aignostics/platform/nocache_test.py b/tests/aignostics/platform/nocache_test.py index 7a50a9c3a..79a2b75db 100644 --- a/tests/aignostics/platform/nocache_test.py +++ b/tests/aignostics/platform/nocache_test.py @@ -12,8 +12,8 @@ import pytest -from aignostics.platform._client import Client -from aignostics.platform._operation_cache import _operation_cache, cached_operation, operation_cache_clear +from aignostics_sdk.platform._client import Client +from aignostics_sdk.platform._operation_cache import _operation_cache, cached_operation, operation_cache_clear class TestNocacheDecoratorBehavior: @@ -440,7 +440,7 @@ def test_run_details_supports_nocache_parameter() -> None: """Test that Run.details() method signature supports nocache parameter.""" from inspect import signature - from aignostics.platform.resources.runs import Run + from aignostics_sdk.platform.resources.runs import Run # Verify the method has nocache parameter sig = signature(Run.details) @@ -459,7 +459,7 @@ def test_runs_list_supports_nocache_parameter() -> None: """Test that Runs.list() method signature supports nocache parameter.""" from inspect import signature - from aignostics.platform.resources.runs import Runs + from aignostics_sdk.platform.resources.runs import Runs # Verify the method has nocache parameter sig = signature(Runs.list) @@ -478,7 +478,7 @@ def test_versions_list_supports_nocache_parameter() -> None: """Test that Versions.list() method signature supports nocache parameter.""" from inspect import signature - from aignostics.platform.resources.applications import Versions + from aignostics_sdk.platform.resources.applications import Versions # Verify the method has nocache parameter sig = signature(Versions.list) @@ -493,7 +493,7 @@ def test_applications_details_supports_nocache_parameter() -> None: """Test that Applications.details() method signature supports nocache parameter.""" from inspect import signature - from aignostics.platform.resources.applications import Applications + from aignostics_sdk.platform.resources.applications import Applications # Verify the method has nocache parameter sig = signature(Applications.details) diff --git a/tests/aignostics/platform/resources/applications_test.py b/tests/aignostics/platform/resources/applications_test.py index 6067339df..85e701386 100644 --- a/tests/aignostics/platform/resources/applications_test.py +++ b/tests/aignostics/platform/resources/applications_test.py @@ -11,20 +11,20 @@ from unittest.mock import MagicMock, Mock, patch import pytest -from aignx.codegen.exceptions import NotFoundException -from aignx.codegen.models.application_read_response import ApplicationReadResponse -from aignx.codegen.models.version_document_response import VersionDocumentResponse -from aignx.codegen.models.version_document_visibility import VersionDocumentVisibility - -from aignostics.platform._api import _AuthenticatedApi -from aignostics.platform._operation_cache import operation_cache_clear -from aignostics.platform.resources.applications import ( + +from aignostics_sdk._codegen.exceptions import NotFoundException +from aignostics_sdk._codegen.models.application_read_response import ApplicationReadResponse +from aignostics_sdk._codegen.models.version_document_response import VersionDocumentResponse +from aignostics_sdk._codegen.models.version_document_visibility import VersionDocumentVisibility +from aignostics_sdk.platform._api import _AuthenticatedApi +from aignostics_sdk.platform._operation_cache import operation_cache_clear +from aignostics_sdk.platform.resources.applications import ( Applications, ApplicationVersionDocument, Documents, Versions, ) -from aignostics.platform.resources.utils import PAGE_SIZE +from aignostics_sdk.platform.resources.utils import PAGE_SIZE API_ERROR = "API error" API_REASON_NOT_FOUND = "Not Found" @@ -32,7 +32,7 @@ DOCUMENT_OUTPUT_DESCRIPTION_PDF = "output_description.pdf" DOCUMENT_MISSING_PDF = "missing.pdf" DOC_FILENAME_A = "a.pdf" -REQUESTS_GET_PATCH_TARGET = "aignostics.platform.resources.applications.requests.get" +REQUESTS_GET_PATCH_TARGET = "aignostics_sdk.platform.resources.applications.requests.get" @pytest.fixture @@ -410,8 +410,8 @@ def test_versions_documents_resolves_none_to_latest(mock_api: Mock) -> None: """Versions.documents(None) resolves to the latest version number.""" from unittest.mock import patch - from aignostics.platform.resources.applications import Versions as _Versions - from aignostics.platform.resources.applications import VersionTuple + from aignostics_sdk.platform.resources.applications import Versions as _Versions + from aignostics_sdk.platform.resources.applications import VersionTuple latest = Mock(spec=VersionTuple) latest.number = "2.3.1" diff --git a/tests/aignostics/platform/resources/resource_utils_test.py b/tests/aignostics/platform/resources/resource_utils_test.py index ef602409f..f58969969 100644 --- a/tests/aignostics/platform/resources/resource_utils_test.py +++ b/tests/aignostics/platform/resources/resource_utils_test.py @@ -8,7 +8,7 @@ import pytest -from aignostics.platform.resources.utils import PAGE_SIZE, paginate +from aignostics_sdk.platform.resources.utils import PAGE_SIZE, paginate @pytest.mark.unit diff --git a/tests/aignostics/platform/resources/runs_test.py b/tests/aignostics/platform/resources/runs_test.py index e2427c308..b9e35ae96 100644 --- a/tests/aignostics/platform/resources/runs_test.py +++ b/tests/aignostics/platform/resources/runs_test.py @@ -9,26 +9,26 @@ import pytest import requests -from aignx.codegen.exceptions import ApiException, NotFoundException, ServiceException -from aignx.codegen.models import ( + +from aignostics_sdk._codegen.exceptions import ApiException, NotFoundException, ServiceException +from aignostics_sdk._codegen.models import ( InputArtifactCreationRequest, ItemCreationRequest, ItemResultReadResponse, RunCreationResponse, RunReadResponse, ) - -from aignostics.platform._api import _AuthenticatedApi -from aignostics.platform.resources.runs import LIST_APPLICATION_RUNS_MAX_PAGE_SIZE, Artifact, Run, Runs -from aignostics.platform.resources.utils import PAGE_SIZE +from aignostics_sdk.platform._api import _AuthenticatedApi +from aignostics_sdk.platform.resources.runs import LIST_APPLICATION_RUNS_MAX_PAGE_SIZE, Artifact, Run, Runs +from aignostics_sdk.platform.resources.utils import PAGE_SIZE _PLATFORM_HOST = "https://platform-staging.aignostics.com" _RUN_ID = "test-run-id" _ARTIFACT_ID = "artifact-123" _PRESIGNED_URL = "https://storage.googleapis.com/bucket/file?sig=abc123" -_PATCH_REQUESTS_GET = "aignostics.platform.resources.runs.requests.get" -_PATCH_GET_TOKEN = "aignostics.platform.resources.runs.get_token" # noqa: S105 -_PATCH_SETTINGS = "aignostics.platform.resources.runs.settings" +_PATCH_REQUESTS_GET = "aignostics_sdk.platform.resources.runs.requests.get" +_PATCH_GET_TOKEN = "aignostics_sdk.platform.resources.runs.get_token" # noqa: S105 +_PATCH_SETTINGS = "aignostics_sdk.platform.resources.runs.settings" def _redirect_response(location: str | None, status: int = HTTPStatus.TEMPORARY_REDIRECT) -> MagicMock: @@ -283,7 +283,7 @@ def test_paginate_with_not_found_exception_on_first_page(runs, mock_api) -> None mock_api: Mock ExternalsApi instance. """ # Arrange - from aignx.codegen.exceptions import NotFoundException + from aignostics_sdk._codegen.exceptions import NotFoundException # Make the API throw NotFoundException on the first call mock_api.list_runs_v1_runs_get.side_effect = NotFoundException() @@ -314,7 +314,7 @@ def test_paginate_with_not_found_exception_after_full_page(runs, mock_api) -> No mock_api: Mock ExternalsApi instance. """ # Arrange - from aignx.codegen.exceptions import NotFoundException + from aignostics_sdk._codegen.exceptions import NotFoundException # Return exactly LIST_APPLICATION_RUNS_MAX_PAGE_SIZE items for first page, then throw NotFoundException full_page = [Mock(spec=RunReadResponse, run_id=f"run-{i}") for i in range(LIST_APPLICATION_RUNS_MAX_PAGE_SIZE)] @@ -731,7 +731,7 @@ def test_run_details_retries_on_not_found_then_succeeds(app_run, mock_api) -> No app_run: Run instance with mock API. mock_api: Mock ExternalsApi instance. """ - from aignx.codegen.exceptions import NotFoundException + from aignostics_sdk._codegen.exceptions import NotFoundException run_data = RunReadResponse.model_construct(run_id="test-run-id") mock_api.get_run_v1_runs_run_id_get.side_effect = [ @@ -757,7 +757,7 @@ def test_run_details_raises_not_found_after_timeout(app_run, mock_api) -> None: app_run: Run instance with mock API. mock_api: Mock ExternalsApi instance. """ - from aignx.codegen.exceptions import NotFoundException + from aignostics_sdk._codegen.exceptions import NotFoundException mock_api.get_run_v1_runs_run_id_get.side_effect = NotFoundException() @@ -778,7 +778,7 @@ def test_run_details_does_not_retry_other_exceptions(app_run, mock_api) -> None: app_run: Run instance with mock API. mock_api: Mock ExternalsApi instance. """ - from aignx.codegen.exceptions import ForbiddenException + from aignostics_sdk._codegen.exceptions import ForbiddenException mock_api.get_run_v1_runs_run_id_get.side_effect = ForbiddenException() @@ -1039,9 +1039,9 @@ def test_run_artifact_returns_artifact_handle(app_run) -> None: # Run.ensure_artifacts_downloaded — instance method, AVAILABLE gating, fresh URL # --------------------------------------------------------------------------- -_PATCH_DOWNLOAD_FILE_RUNS = "aignostics.platform.resources.runs.download_file" -_PATCH_CALC_CRC32C = "aignostics.platform.resources.runs.calculate_file_crc32c" -_PATCH_MIME_TYPE_TO_FILE_ENDING = "aignostics.platform.resources.runs.mime_type_to_file_ending" +_PATCH_DOWNLOAD_FILE_RUNS = "aignostics_sdk.platform.resources.runs.download_file" +_PATCH_CALC_CRC32C = "aignostics_sdk.platform.resources.runs.calculate_file_crc32c" +_PATCH_MIME_TYPE_TO_FILE_ENDING = "aignostics_sdk.platform.resources.runs.mime_type_to_file_ending" def _make_artifact_mock( @@ -1052,7 +1052,7 @@ def _make_artifact_mock( metadata: dict | None = None, ) -> MagicMock: """Build a mock OutputArtifactResultReadResponse for ensure_artifacts_downloaded tests.""" - from aignx.codegen.models import ArtifactOutput as _ArtifactOutput + from aignostics_sdk._codegen.models import ArtifactOutput as _ArtifactOutput a = MagicMock() a.name = name @@ -1087,7 +1087,7 @@ def test_ensure_artifacts_downloaded_resolves_fresh_url_per_artifact(app_run, tm @pytest.mark.unit def test_ensure_artifacts_downloaded_skips_non_available_artifacts(app_run, tmp_path) -> None: """Non-AVAILABLE artifacts must be skipped.""" - from aignx.codegen.models import ArtifactOutput as _ArtifactOutput + from aignostics_sdk._codegen.models import ArtifactOutput as _ArtifactOutput none_artifact = _make_artifact_mock(output_artifact_id="art-none", output=_ArtifactOutput.NONE) item = _make_item_mock(artifacts=[none_artifact]) @@ -1158,7 +1158,7 @@ def test_ensure_artifacts_downloaded_skips_artifact_with_no_metadata(app_run, tm @pytest.mark.unit def test_download_to_folder_post_termination_loop_filters_by_item_state(app_run, mock_api, tmp_path) -> None: """Post-termination loop must filter items by state==TERMINATED and output==FULL.""" - from aignx.codegen.models import ItemOutput, ItemState, RunState + from aignostics_sdk._codegen.models import ItemOutput, ItemState, RunState terminated_full_item = MagicMock(state=ItemState.TERMINATED, output=ItemOutput.FULL, external_id="ok") terminated_none_item = MagicMock(state=ItemState.TERMINATED, output=ItemOutput.NONE, external_id="empty") @@ -1191,7 +1191,7 @@ def test_ensure_artifacts_downloaded_is_instance_method_not_static(app_run, tmp_ @pytest.mark.unit def test_results_passes_state_filter(app_run, mock_api) -> None: """state= is forwarded to the API call on every page.""" - from aignx.codegen.models import ItemState + from aignostics_sdk._codegen.models import ItemState mock_api.list_run_items_v1_runs_run_id_items_get.return_value = [] @@ -1204,7 +1204,7 @@ def test_results_passes_state_filter(app_run, mock_api) -> None: @pytest.mark.unit def test_results_passes_termination_reason_filter(app_run, mock_api) -> None: """termination_reason= is forwarded to the API call on every page.""" - from aignx.codegen.models import ItemTerminationReason + from aignostics_sdk._codegen.models import ItemTerminationReason mock_api.list_run_items_v1_runs_run_id_items_get.return_value = [] @@ -1228,7 +1228,7 @@ def test_results_passes_custom_metadata_filter(app_run, mock_api) -> None: @pytest.mark.unit def test_results_combines_all_filters(app_run, mock_api) -> None: """All three new filters are forwarded together when all are provided.""" - from aignx.codegen.models import ItemState, ItemTerminationReason + from aignostics_sdk._codegen.models import ItemState, ItemTerminationReason mock_api.list_run_items_v1_runs_run_id_items_get.return_value = [] diff --git a/tests/aignostics/platform/sdk_metadata_test.py b/tests/aignostics/platform/sdk_metadata_test.py index 54b514135..4de089c1c 100644 --- a/tests/aignostics/platform/sdk_metadata_test.py +++ b/tests/aignostics/platform/sdk_metadata_test.py @@ -7,7 +7,7 @@ import pytest from pydantic import ValidationError -from aignostics.platform._sdk_metadata import ( +from aignostics_sdk.platform._sdk_metadata import ( ITEM_SDK_METADATA_SCHEMA_VERSION, SDK_METADATA_SCHEMA_VERSION, VALIDATION_CASE_TAG_PREFIX, @@ -51,7 +51,7 @@ class TestBuildRunSdkMetadata: @staticmethod def test_basic_metadata_structure(clean_env: None) -> None: """Test that basic metadata structure is created correctly.""" - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -66,7 +66,7 @@ def test_basic_metadata_structure(clean_env: None) -> None: def test_submission_metadata_default(clean_env: None) -> None: """Test default submission metadata when no special environment is detected.""" with ( - patch("aignostics.platform._client.Client") as mock_client, + patch("aignostics_sdk.platform._client.Client") as mock_client, patch("os.environ.get", return_value=None), ): mock_client.return_value.me.side_effect = Exception("No client available") @@ -85,7 +85,7 @@ def test_submission_initiator_bridge(clean_env: None, monkeypatch: pytest.Monkey """Test that bridge initiator is detected when AIGNOSTICS_BRIDGE_VERSION is set.""" monkeypatch.setenv("AIGNOSTICS_BRIDGE_VERSION", "1.0.0") - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -98,7 +98,7 @@ def test_submission_initiator_test(clean_env: None, monkeypatch: pytest.MonkeyPa """Test that test initiator is detected when PYTEST_CURRENT_TEST is set.""" monkeypatch.setenv("PYTEST_CURRENT_TEST", "tests/test_example.py::test_func") - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -112,7 +112,7 @@ def test_submission_initiator_bridge_takes_precedence(clean_env: None, monkeypat monkeypatch.setenv("AIGNOSTICS_BRIDGE_VERSION", "1.0.0") monkeypatch.setenv("PYTEST_CURRENT_TEST", "tests/test_example.py::test_func") - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -127,7 +127,7 @@ def test_submission_interface_cli_typer(clean_env: None) -> None: try: sys.argv = ["/path/to/typer", "command"] - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -144,7 +144,7 @@ def test_submission_interface_cli_aignostics(clean_env: None) -> None: try: sys.argv = ["/path/to/aignostics", "command"] - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -159,7 +159,7 @@ def test_submission_interface_launchpad(clean_env: None, monkeypatch: pytest.Mon """Test that launchpad interface is detected when NICEGUI_HOST is set.""" monkeypatch.setenv("NICEGUI_HOST", "localhost") - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -176,7 +176,7 @@ def test_user_metadata_success(clean_env: None) -> None: mock_me.user.email = "test@example.com" mock_me.user.id = "user-456" - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.return_value = mock_me metadata = build_run_sdk_metadata() @@ -191,7 +191,7 @@ def test_user_metadata_success(clean_env: None) -> None: @staticmethod def test_user_metadata_failure(clean_env: None) -> None: """Test that user metadata is omitted when Client().me() fails.""" - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("Auth failed") metadata = build_run_sdk_metadata() @@ -218,7 +218,7 @@ def test_github_ci_metadata(clean_env: None, monkeypatch: pytest.MonkeyPatch) -> monkeypatch.setenv("GITHUB_WORKFLOW", "CI") monkeypatch.setenv("GITHUB_WORKFLOW_REF", "owner/repo/.github/workflows/ci.yml@main") - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -251,7 +251,7 @@ def test_github_ci_metadata_custom_server(clean_env: None, monkeypatch: pytest.M monkeypatch.setenv("GITHUB_SERVER_URL", "https://github.enterprise.com") monkeypatch.setenv("GITHUB_REPOSITORY", "owner/repo") - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -266,7 +266,7 @@ def test_pytest_metadata_basic(clean_env: None, monkeypatch: pytest.MonkeyPatch) """Test that pytest metadata is collected correctly.""" monkeypatch.setenv("PYTEST_CURRENT_TEST", "tests/test_example.py::test_func (call)") - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -282,7 +282,7 @@ def test_pytest_metadata_with_markers(clean_env: None, monkeypatch: pytest.Monke monkeypatch.setenv("PYTEST_CURRENT_TEST", "tests/test_example.py::test_func (call)") monkeypatch.setenv("PYTEST_MARKERS", "slow,integration,unit") - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -298,7 +298,7 @@ def test_combined_github_and_pytest_metadata(clean_env: None, monkeypatch: pytes monkeypatch.setenv("GITHUB_REPOSITORY", "owner/repo") monkeypatch.setenv("PYTEST_CURRENT_TEST", "tests/test_example.py::test_func (call)") - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -312,7 +312,7 @@ def test_combined_github_and_pytest_metadata(clean_env: None, monkeypatch: pytes def test_no_ci_metadata_when_not_in_ci(clean_env: None) -> None: """Test that ci field is omitted when not in CI environment.""" with ( - patch("aignostics.platform._client.Client") as mock_client, + patch("aignostics_sdk.platform._client.Client") as mock_client, patch("os.environ.get", return_value=None), ): mock_client.return_value.me.side_effect = Exception("No client available") @@ -325,9 +325,9 @@ def test_no_ci_metadata_when_not_in_ci(clean_env: None) -> None: @staticmethod def test_user_agent_included(clean_env: None) -> None: """Test that user_agent is included in metadata.""" - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") - with patch("aignostics.platform._sdk_metadata.user_agent", return_value=TEST_USER_AGENT): + with patch("aignostics_sdk.platform._sdk_metadata.user_agent", return_value=TEST_USER_AGENT): metadata = build_run_sdk_metadata() assert metadata["user_agent"] == TEST_USER_AGENT @@ -336,7 +336,7 @@ def test_user_agent_included(clean_env: None) -> None: @staticmethod def test_metadata_date_format(clean_env: None) -> None: """Test that submission date is in correct ISO format with seconds precision.""" - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -357,7 +357,7 @@ class TestRunSdkMetadataValidation: @staticmethod def test_validate_basic_metadata(clean_env: None) -> None: """Test validation of basic metadata structure.""" - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -367,7 +367,7 @@ def test_validate_basic_metadata(clean_env: None) -> None: @staticmethod def test_validate_metadata_with_user(clean_env: None) -> None: """Test validation of metadata with user information.""" - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_user = MagicMock() mock_user.organization.id = "org-123" mock_user.organization.name = "Test Org" @@ -385,7 +385,7 @@ def test_validate_metadata_with_github_ci(clean_env: None, monkeypatch: pytest.M monkeypatch.setenv("GITHUB_RUN_ID", "123456") monkeypatch.setenv("GITHUB_REPOSITORY", "owner/repo") - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -398,7 +398,7 @@ def test_validate_metadata_with_pytest_ci(clean_env: None, monkeypatch: pytest.M monkeypatch.setenv("PYTEST_CURRENT_TEST", "tests/test_example.py::test_func") monkeypatch.setenv("PYTEST_MARKERS", "unit,integration") - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -408,7 +408,7 @@ def test_validate_metadata_with_pytest_ci(clean_env: None, monkeypatch: pytest.M @staticmethod def test_validate_metadata_with_workflow(clean_env: None) -> None: """Test validation of metadata with workflow fields.""" - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -657,7 +657,7 @@ def test_validate_with_tags_non_string_values() -> None: @staticmethod def test_validate_sdk_metadata_silent_valid(clean_env: None) -> None: """Test silent validation with valid metadata.""" - with patch("aignostics.platform._client.Client") as mock_client: + with patch("aignostics_sdk.platform._client.Client") as mock_client: mock_client.return_value.me.side_effect = Exception("No client available") metadata = build_run_sdk_metadata() @@ -994,7 +994,7 @@ class TestPipelineConfiguration: @staticmethod def test_pipeline_config_defaults() -> None: """Test that pipeline configuration uses correct defaults.""" - from aignostics.platform import ( + from aignostics_sdk.platform import ( DEFAULT_CPU_PROVISIONING_MODE, DEFAULT_GPU_PROVISIONING_MODE, DEFAULT_GPU_TYPE, @@ -1013,7 +1013,7 @@ def test_pipeline_config_defaults() -> None: @staticmethod def test_pipeline_config_custom_values() -> None: """Test pipeline configuration with custom values.""" - from aignostics.platform._sdk_metadata import GPUType, PipelineConfig, ProvisioningMode + from aignostics_sdk.platform._sdk_metadata import GPUType, PipelineConfig, ProvisioningMode config = PipelineConfig( gpu={ @@ -1033,7 +1033,7 @@ def test_pipeline_config_custom_values() -> None: @staticmethod def test_gpu_type_enum() -> None: """Test GPUType enum values.""" - from aignostics.platform._sdk_metadata import GPUType + from aignostics_sdk.platform._sdk_metadata import GPUType assert GPUType.L4.value == "L4" assert GPUType.A100.value == "A100" @@ -1043,7 +1043,7 @@ def test_gpu_type_enum() -> None: @staticmethod def test_provisioning_mode_enum() -> None: """Test ProvisioningMode enum values.""" - from aignostics.platform._sdk_metadata import ProvisioningMode + from aignostics_sdk.platform._sdk_metadata import ProvisioningMode assert ProvisioningMode.SPOT.value == "SPOT" assert ProvisioningMode.ON_DEMAND.value == "ON_DEMAND" @@ -1054,7 +1054,7 @@ def test_provisioning_mode_enum() -> None: @staticmethod def test_metadata_with_pipeline_config() -> None: """Test that metadata validates with pipeline configuration.""" - from aignostics.platform._sdk_metadata import GPUType, ProvisioningMode + from aignostics_sdk.platform._sdk_metadata import GPUType, ProvisioningMode metadata = { "schema_version": SDK_METADATA_SCHEMA_VERSION, @@ -1100,7 +1100,7 @@ def test_metadata_without_pipeline_config() -> None: @staticmethod def test_gpu_config_invalid_max_gpus() -> None: """Test that invalid max_gpus_per_slide value is rejected.""" - from aignostics.platform._sdk_metadata import GPUConfig + from aignostics_sdk.platform._sdk_metadata import GPUConfig with pytest.raises(ValidationError): GPUConfig(max_gpus_per_slide=0) # Must be positive @@ -1112,7 +1112,7 @@ def test_gpu_config_invalid_max_gpus() -> None: @staticmethod def test_flex_start_provisioning_mode_sets_default_duration() -> None: """Test that FLEX_START mode automatically sets default duration when not specified.""" - from aignostics.platform._sdk_metadata import ( + from aignostics_sdk.platform._sdk_metadata import ( DEFAULT_FLEX_START_MAX_RUN_DURATION_MINUTES, GPUConfig, ProvisioningMode, @@ -1127,7 +1127,7 @@ def test_flex_start_provisioning_mode_sets_default_duration() -> None: @staticmethod def test_flex_start_with_custom_duration() -> None: """Test FLEX_START mode with custom duration.""" - from aignostics.platform._sdk_metadata import GPUConfig, ProvisioningMode + from aignostics_sdk.platform._sdk_metadata import GPUConfig, ProvisioningMode config = GPUConfig( provisioning_mode=ProvisioningMode.FLEX_START, @@ -1141,7 +1141,7 @@ def test_flex_start_with_custom_duration() -> None: @staticmethod def test_non_flex_start_mode_rejects_duration() -> None: """Test that non-FLEX_START modes reject flex_start_max_run_duration_minutes.""" - from aignostics.platform._sdk_metadata import GPUConfig, ProvisioningMode + from aignostics_sdk.platform._sdk_metadata import GPUConfig, ProvisioningMode # SPOT mode should not allow flex_start_max_run_duration_minutes with pytest.raises(ValidationError) as exc_info: @@ -1163,7 +1163,7 @@ def test_non_flex_start_mode_rejects_duration() -> None: @staticmethod def test_flex_start_duration_out_of_range() -> None: """Test that flex_start_max_run_duration_minutes validates range.""" - from aignostics.platform._sdk_metadata import GPUConfig, ProvisioningMode + from aignostics_sdk.platform._sdk_metadata import GPUConfig, ProvisioningMode # Too low with pytest.raises(ValidationError): @@ -1183,7 +1183,7 @@ def test_flex_start_duration_out_of_range() -> None: @staticmethod def test_metadata_with_flex_start_pipeline_config() -> None: """Test that metadata validates with FLEX_START pipeline configuration.""" - from aignostics.platform._sdk_metadata import GPUType, ProvisioningMode + from aignostics_sdk.platform._sdk_metadata import GPUType, ProvisioningMode metadata = { "schema_version": SDK_METADATA_SCHEMA_VERSION, @@ -1212,7 +1212,7 @@ def test_metadata_with_flex_start_pipeline_config() -> None: @staticmethod def test_default_flex_start_duration_value() -> None: """Test that DEFAULT_FLEX_START_MAX_RUN_DURATION_MINUTES is 12 hours (720 minutes).""" - from aignostics.platform._sdk_metadata import DEFAULT_FLEX_START_MAX_RUN_DURATION_MINUTES + from aignostics_sdk.platform._sdk_metadata import DEFAULT_FLEX_START_MAX_RUN_DURATION_MINUTES assert DEFAULT_FLEX_START_MAX_RUN_DURATION_MINUTES == 12 * 60 # 720 minutes diff --git a/tests/aignostics/platform/service_test.py b/tests/aignostics/platform/service_test.py index 74b0da3fd..cb492e4e0 100644 --- a/tests/aignostics/platform/service_test.py +++ b/tests/aignostics/platform/service_test.py @@ -5,11 +5,11 @@ import pytest -from aignostics.platform._service import Service, UserInfo -from aignostics.utils import Health +from aignostics_sdk.platform._service import Service, UserInfo +from aignostics_sdk.utils import Health -_PATCH_AUTH_GETTER = "aignostics.platform._service.get_token" -_PATCH_HTTPX_ASYNC_CLIENT = "aignostics.platform._service.httpx.AsyncClient" +_PATCH_AUTH_GETTER = "aignostics_sdk.platform._service.get_token" +_PATCH_HTTPX_ASYNC_CLIENT = "aignostics_sdk.platform._service.httpx.AsyncClient" @pytest.mark.unit diff --git a/tests/aignostics/platform/settings_test.py b/tests/aignostics/platform/settings_test.py index 5f1645c82..aae0e2da9 100644 --- a/tests/aignostics/platform/settings_test.py +++ b/tests/aignostics/platform/settings_test.py @@ -8,7 +8,7 @@ from pydantic import SecretStr from pydantic import ValidationError as PydanticValidationError -from aignostics.platform import ( +from aignostics_sdk.platform import ( API_ROOT_DEV, API_ROOT_PRODUCTION, API_ROOT_STAGING, @@ -49,7 +49,7 @@ Settings, settings, ) -from aignostics.utils import __project_name__ +from aignostics_sdk.utils import ENV_PREFIX @pytest.fixture @@ -58,7 +58,7 @@ def mock_env_vars(): # noqa: ANN201 with mock.patch.dict( os.environ, { - f"{__project_name__.upper()}_CLIENT_ID_DEVICE": "test-client-id-device", + f"{ENV_PREFIX}_CLIENT_ID_DEVICE": "test-client-id-device", }, ): yield @@ -67,7 +67,7 @@ def mock_env_vars(): # noqa: ANN201 @pytest.fixture def reset_cached_settings(): # noqa: ANN201 """Reset the cached authentication settings.""" - import aignostics.platform._settings as _settings_module + import aignostics_sdk.platform._settings as _settings_module original = _settings_module.__cached_settings _settings_module.__cached_settings = None @@ -251,7 +251,7 @@ def test_custom_env_file_location(reset_cached_settings, record_property) -> Non record_property("tested-item-id", "SPEC-PLATFORM-SERVICE") - settings_module = "aignostics.platform._settings" + settings_module = "aignostics_sdk.platform._settings" @contextmanager def temp_env_file(content: str): # type: ignore[misc] @@ -276,10 +276,10 @@ def temp_env_file(content: str): # type: ignore[misc] # Set the custom env file location BEFORE importing Settings # This requires reimporting the module to pick up the new env var # Clear ALL AIGNOSTICS_ environment variables to ensure clean state - env_patch = {k: v for k, v in os.environ.items() if not k.startswith(f"{__project_name__.upper()}_")} + env_patch = {k: v for k, v in os.environ.items() if not k.startswith(f"{ENV_PREFIX}_")} # Now set only the variables we want for this test - env_patch[f"{__project_name__.upper()}_ENV_FILE"] = custom_env_file + env_patch[f"{ENV_PREFIX}_ENV_FILE"] = custom_env_file try: with mock.patch.dict(os.environ, env_patch, clear=True): @@ -288,7 +288,7 @@ def temp_env_file(content: str): # type: ignore[misc] del sys.modules[settings_module] # Now import Settings fresh - it should read from the custom env file - from aignostics.platform._settings import Settings + from aignostics_sdk.platform._settings import Settings assert custom_env_file in Settings.model_config["env_file"] test_settings = Settings() @@ -544,7 +544,7 @@ def test_status_page_url_env_override_takes_precedence(record_property, monkeypa """User-supplied AIGNOSTICS_STATUS_PAGE_URL overrides the per-environment default.""" record_property("tested-item-id", "SPEC-PLATFORM-SERVICE") custom_url = "https://custom-status.example.com" - monkeypatch.setenv(f"{__project_name__.upper()}_STATUS_PAGE_URL", custom_url) + monkeypatch.setenv(f"{ENV_PREFIX}_STATUS_PAGE_URL", custom_url) settings = Settings(api_root=API_ROOT_PRODUCTION) @@ -573,7 +573,7 @@ def test_status_page_url_empty_string_coerced_to_none(record_property, monkeypat dev/test default behaviour (no badge, no menu link). """ record_property("tested-item-id", "SPEC-PLATFORM-SERVICE") - monkeypatch.setenv(f"{__project_name__.upper()}_STATUS_PAGE_URL", "") + monkeypatch.setenv(f"{ENV_PREFIX}_STATUS_PAGE_URL", "") settings = Settings(api_root=API_ROOT_PRODUCTION) diff --git a/tests/aignostics/platform/utils_test.py b/tests/aignostics/platform/utils_test.py index ba50eb6c3..80b5c2b46 100644 --- a/tests/aignostics/platform/utils_test.py +++ b/tests/aignostics/platform/utils_test.py @@ -4,8 +4,8 @@ import pytest -from aignostics.platform import mime_type_to_file_ending -from aignostics.platform._utils import convert_to_json_serializable +from aignostics_sdk.platform import mime_type_to_file_ending +from aignostics_sdk.platform._utils import convert_to_json_serializable class TestConvertToJsonSerializable: diff --git a/tests/aignostics/qupath/cli_test.py b/tests/aignostics/qupath/cli_test.py index 87b7b91e5..343870e3d 100644 --- a/tests/aignostics/qupath/cli_test.py +++ b/tests/aignostics/qupath/cli_test.py @@ -7,10 +7,10 @@ import psutil import pytest -from typer.testing import CliRunner - from aignostics.cli import cli from aignostics.qupath import QUPATH_VERSION +from typer.testing import CliRunner + from tests.conftest import normalize_output _SKIP_IF_WINDOWS = pytest.mark.skipif(platform.system() == "Windows", reason="not supported on Windows") diff --git a/tests/aignostics/qupath/conftest.py b/tests/aignostics/qupath/conftest.py index 2ef9a52c7..70683369f 100644 --- a/tests/aignostics/qupath/conftest.py +++ b/tests/aignostics/qupath/conftest.py @@ -3,9 +3,8 @@ from collections.abc import Generator import pytest -from typer.testing import CliRunner - from aignostics.cli import cli +from typer.testing import CliRunner @pytest.fixture diff --git a/tests/aignostics/qupath/gui_test.py b/tests/aignostics/qupath/gui_test.py index 01d9a1b6d..26a11f37b 100644 --- a/tests/aignostics/qupath/gui_test.py +++ b/tests/aignostics/qupath/gui_test.py @@ -11,13 +11,13 @@ import platformdirs import psutil import pytest -from nicegui.testing import User -from typer.testing import CliRunner - from aignostics.application import Service from aignostics.cli import cli from aignostics.qupath import QUPATH_LAUNCH_MAX_WAIT_TIME, QUPATH_VERSION -from aignostics.utils import __project_name__ +from nicegui.testing import User +from typer.testing import CliRunner + +from aignostics_sdk.utils import __project_name__ from tests.conftest import assert_notified, normalize_output, print_directory_structure from tests.constants_test import ( HETA_APPLICATION_ID, diff --git a/tests/aignostics/system/cli_test.py b/tests/aignostics/system/cli_test.py index f3254b499..62d62bde2 100644 --- a/tests/aignostics/system/cli_test.py +++ b/tests/aignostics/system/cli_test.py @@ -6,21 +6,21 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest +from aignostics.cli import cli from typer.testing import CliRunner -from aignostics.cli import cli -from aignostics.utils import __project_name__ +from aignostics_sdk.utils import __project_name__ from tests.conftest import normalize_output THE_VALUE = "test_secret_value_not_real_for_testing_only" @pytest.mark.unit -@patch("aignostics.system._cli._service") +@patch("aignostics_sdk.system._cli._service") def test_cli_health_json_format(mock_service: MagicMock, runner: CliRunner, record_property) -> None: """Check health CLI renders UP status correctly as JSON.""" record_property("tested-item-id", "TEST-SYSTEM-CLI-HEALTH-JSON") - from aignostics.utils import Health + from aignostics_sdk.utils import Health mock_service.health = AsyncMock(return_value=Health(status=Health.Code.UP)) result = runner.invoke(cli, ["system", "health"]) @@ -29,11 +29,11 @@ def test_cli_health_json_format(mock_service: MagicMock, runner: CliRunner, reco @pytest.mark.unit -@patch("aignostics.system._cli._service") +@patch("aignostics_sdk.system._cli._service") def test_cli_health_yaml_format(mock_service: MagicMock, runner: CliRunner, record_property) -> None: """Check health CLI renders UP status correctly as YAML.""" record_property("tested-item-id", "TEST-SYSTEM-CLI-HEALTH-YAML") - from aignostics.utils import Health + from aignostics_sdk.utils import Health mock_service.health = AsyncMock(return_value=Health(status=Health.Code.UP)) result = runner.invoke(cli, ["system", "health", "--output-format", "yaml"]) @@ -113,10 +113,10 @@ def test_cli_info_secrets(runner: CliRunner, caplog: pytest.LogCaptureFixture, r @pytest.mark.unit -@patch("aignostics.system._cli._service") +@patch("aignostics_sdk.system._cli._service") def test_cli_health_up_exits_zero(mock_service: MagicMock, runner: CliRunner) -> None: """Check health command exits with code 0 when status is UP.""" - from aignostics.utils import Health + from aignostics_sdk.utils import Health mock_service.health = AsyncMock(return_value=Health(status=Health.Code.UP)) result = runner.invoke(cli, ["system", "health"]) @@ -124,10 +124,10 @@ def test_cli_health_up_exits_zero(mock_service: MagicMock, runner: CliRunner) -> @pytest.mark.unit -@patch("aignostics.system._cli._service") +@patch("aignostics_sdk.system._cli._service") def test_cli_health_degraded_exits_zero(mock_service: MagicMock, runner: CliRunner) -> None: """Check health command exits with code 0 when status is DEGRADED.""" - from aignostics.utils import Health + from aignostics_sdk.utils import Health mock_service.health = AsyncMock(return_value=Health(status=Health.Code.DEGRADED, reason="some component degraded")) result = runner.invoke(cli, ["system", "health"]) @@ -135,10 +135,10 @@ def test_cli_health_degraded_exits_zero(mock_service: MagicMock, runner: CliRunn @pytest.mark.unit -@patch("aignostics.system._cli._service") +@patch("aignostics_sdk.system._cli._service") def test_cli_health_down_exits_one(mock_service: MagicMock, runner: CliRunner) -> None: """Check health command exits with code 1 when status is DOWN.""" - from aignostics.utils import Health + from aignostics_sdk.utils import Health mock_service.health = AsyncMock(return_value=Health(status=Health.Code.DOWN, reason="service unavailable")) result = runner.invoke(cli, ["system", "health"]) @@ -224,7 +224,7 @@ def test_cli_install(runner: CliRunner, record_property) -> None: def test_cli_set_unset_get(runner: CliRunner, silent_logging, tmp_path, record_property) -> None: """Check set, unset, and get commands.""" record_property("tested-item-id", "SPEC-SYSTEM-SERVICE") - with patch("aignostics.system.Service._get_env_files_paths", return_value=[tmp_path / ".env"]): + with patch("aignostics_sdk.system.Service._get_env_files_paths", return_value=[tmp_path / ".env"]): (tmp_path / ".env").touch() result = runner.invoke(cli, ["system", "config", "unset", "test_key"]) @@ -259,7 +259,7 @@ def test_cli_set_unset_get(runner: CliRunner, silent_logging, tmp_path, record_p def test_cli_remote_diagnostics(runner: CliRunner, silent_logging, tmp_path: Path, record_property) -> None: """Check disable/enable remote diagnostics.""" record_property("tested-item-id", "SPEC-SYSTEM-SERVICE") - with patch("aignostics.system.Service._get_env_files_paths", return_value=[tmp_path / ".env"]): + with patch("aignostics_sdk.system.Service._get_env_files_paths", return_value=[tmp_path / ".env"]): (tmp_path / ".env").touch() result = runner.invoke(cli, ["system", "config", "remote-diagnostics-disable"]) @@ -303,7 +303,7 @@ def test_cli_remote_diagnostics(runner: CliRunner, silent_logging, tmp_path: Pat def test_cli_http_proxy(runner: CliRunner, silent_logging, tmp_path: Path, record_property) -> None: # noqa: PLR0915 """Check disable/enable remote diagnostics.""" record_property("tested-item-id", "SPEC-SYSTEM-SERVICE") - with patch("aignostics.system.Service._get_env_files_paths", return_value=[tmp_path / ".env"]): + with patch("aignostics_sdk.system.Service._get_env_files_paths", return_value=[tmp_path / ".env"]): # Set up a mock .env file (tmp_path / ".env").touch() @@ -450,7 +450,7 @@ def test_cli_http_proxy(runner: CliRunner, silent_logging, tmp_path: Path, recor @pytest.mark.integration def test_cli_dump_dot_env_file(runner: CliRunner, silent_logging, tmp_path: Path) -> None: """Check dump-dot-env-file command creates a file with all settings.""" - with patch("aignostics.system.Service._get_env_files_paths", return_value=[tmp_path / ".env"]): + with patch("aignostics_sdk.system.Service._get_env_files_paths", return_value=[tmp_path / ".env"]): # Create the .env file that the system expects to exist (tmp_path / ".env").touch() diff --git a/tests/aignostics/system/gui_test.py b/tests/aignostics/system/gui_test.py index 8fd3fc156..ea197cbe5 100644 --- a/tests/aignostics/system/gui_test.py +++ b/tests/aignostics/system/gui_test.py @@ -10,7 +10,7 @@ from nicegui.testing import User, UserInteraction from nicegui.ui import switch -from aignostics.utils import __project_name__ +from aignostics_sdk.utils import __project_name__ @pytest.mark.integration diff --git a/tests/aignostics/system/service_test.py b/tests/aignostics/system/service_test.py index 55457d5f1..c8913200f 100644 --- a/tests/aignostics/system/service_test.py +++ b/tests/aignostics/system/service_test.py @@ -6,7 +6,7 @@ import pytest -from aignostics.system._service import Service +from aignostics_sdk.system._service import Service # --------------------------------------------------------------------------- # Helpers shared by uptime tests @@ -45,7 +45,7 @@ def _patch_info_dependencies(boot_time: float = FIXED_BOOT_TIME): cpu_times.system = 10.0 cpu_times.idle = 70.0 - from aignostics.utils._process import ParentProcessInfo, ProcessInfo + from aignostics_sdk.utils._process import ParentProcessInfo, ProcessInfo mock_process_info = ProcessInfo( project_root="/fake/root", @@ -63,11 +63,11 @@ def _ctx(): mock.patch("psutil.cpu_times_percent", return_value=cpu_times), mock.patch("psutil.getloadavg", return_value=(1.0, 1.0, 1.0)), mock.patch("psutil.Process", return_value=_make_mock_process()), - mock.patch("aignostics.system._service.get_process_info", return_value=mock_process_info), + mock.patch("aignostics_sdk.system._service.get_process_info", return_value=mock_process_info), mock.patch("asyncio.sleep"), mock.patch.object(Service, "_get_public_ipv4", return_value=None), mock.patch.object(Service, "_collect_all_settings", return_value={}), - mock.patch("aignostics.system._service.locate_subclasses", return_value=[]), + mock.patch("aignostics.utils.locate_subclasses", return_value=[]), mock.patch("time.time", return_value=now), ): yield diff --git a/tests/aignostics/utils/cli_test.py b/tests/aignostics/utils/cli_test.py index 01b83951b..42f6db019 100644 --- a/tests/aignostics/utils/cli_test.py +++ b/tests/aignostics/utils/cli_test.py @@ -6,10 +6,10 @@ import pytest from typer.models import CommandInfo, TyperInfo -from aignostics.utils._cli import prepare_cli +from aignostics_sdk.utils._cli import prepare_cli # Constants to avoid duplication -LOCATE_IMPLEMENTATIONS_PATH = "aignostics.utils._cli.locate_implementations" +LOCATE_IMPLEMENTATIONS_PATH = "aignostics.utils.locate_implementations" TEST_EPILOG = "Test Epilog" @@ -128,8 +128,8 @@ def test_prepare_cli_conditional_epilog_recursion( record_property("tested-item-id", "SPEC-UTILS-SERVICE") with ( patch(LOCATE_IMPLEMENTATIONS_PATH, return_value=[]), - patch("aignostics.utils._cli.Path") as mock_path, - patch("aignostics.utils._cli._add_epilog_recursively") as mock_add_epilog, + patch("aignostics_sdk.utils._cli.Path") as mock_path, + patch("aignostics_sdk.utils._cli._add_epilog_recursively") as mock_add_epilog, ): mock_path.return_value.parts = argv_parts prepare_cli(mock_typer, TEST_EPILOG) diff --git a/tests/aignostics/utils/console_test.py b/tests/aignostics/utils/console_test.py index 741f2415c..6b6db29cc 100644 --- a/tests/aignostics/utils/console_test.py +++ b/tests/aignostics/utils/console_test.py @@ -2,7 +2,7 @@ import pytest -from aignostics.utils._console import _get_console +from aignostics_sdk.utils._console import _get_console @pytest.mark.unit diff --git a/tests/aignostics/utils/constants_test.py b/tests/aignostics/utils/constants_test.py index 5ba42c597..2c48a93f7 100644 --- a/tests/aignostics/utils/constants_test.py +++ b/tests/aignostics/utils/constants_test.py @@ -4,7 +4,7 @@ import pytest -from aignostics.utils import __python_version__, __version__ +from aignostics_sdk.utils import __python_version__, __version__ @pytest.mark.unit diff --git a/tests/aignostics/utils/di_test.py b/tests/aignostics/utils/di_test.py index 13439ea44..8ada1b77b 100644 --- a/tests/aignostics/utils/di_test.py +++ b/tests/aignostics/utils/di_test.py @@ -6,16 +6,9 @@ from types import ModuleType from unittest.mock import MagicMock, Mock, patch +import aignostics.utils._di as di_module import pytest import typer - -import aignostics.utils._di as di_module -from aignostics.utils._cli import ( - _add_epilog_recursively, - _no_args_is_help_recursively, - prepare_cli, -) -from aignostics.utils._constants import __project_name__ from aignostics.utils._di import ( PLUGIN_ENTRY_POINT_GROUP, _implementation_cache, @@ -25,15 +18,23 @@ locate_subclasses, ) +from aignostics_sdk.utils._cli import ( + _add_epilog_recursively, + _no_args_is_help_recursively, + prepare_cli, +) + # Constants to avoid duplication TEST_EPILOG = "Test epilog" SCRIPT_FILENAME = "script.py" PLUGIN = "plugin" MYMODULE = "mymodule" +# _di.py scans "aignostics" and "aignostics_sdk" (importable names, not hyphenated distribution name) +MAIN_PACKAGE = "aignostics" @pytest.mark.unit -@patch("aignostics.utils._cli.locate_implementations") +@patch("aignostics.utils.locate_implementations") def test_prepare_cli_registers_subcommands(mock_locate_implementations: Mock, record_property) -> None: """Test that prepare_cli registers all located implementations.""" record_property("tested-item-id", "SPEC-UTILS-SERVICE") @@ -51,7 +52,7 @@ def test_prepare_cli_registers_subcommands(mock_locate_implementations: Mock, re @pytest.mark.unit -@patch("aignostics.utils._cli.locate_implementations") +@patch("aignostics.utils.locate_implementations") def test_prepare_cli_sets_epilog_and_no_args_help(mock_locate_implementations: Mock, record_property) -> None: """Test that prepare_cli sets epilog and no_args_is_help on the cli instance.""" record_property("tested-item-id", "SPEC-UTILS-SERVICE") @@ -69,8 +70,8 @@ def test_prepare_cli_sets_epilog_and_no_args_help(mock_locate_implementations: M @pytest.mark.unit -@patch("aignostics.utils._cli.Path") -@patch("aignostics.utils._cli.locate_implementations") +@patch("aignostics_sdk.utils._cli.Path") +@patch("aignostics.utils.locate_implementations") def test_prepare_cli_adds_epilog_to_commands_when_not_running_from_typer( mock_locate_implementations: Mock, mock_path: Mock, record_property ) -> None: @@ -92,9 +93,9 @@ def test_prepare_cli_adds_epilog_to_commands_when_not_running_from_typer( @pytest.mark.unit -@patch("aignostics.utils._cli._add_epilog_recursively") -@patch("aignostics.utils._cli.Path") -@patch("aignostics.utils._cli.locate_implementations") +@patch("aignostics_sdk.utils._cli._add_epilog_recursively") +@patch("aignostics_sdk.utils._cli.Path") +@patch("aignostics.utils.locate_implementations") def test_prepare_cli_calls_add_epilog_recursively_when_not_running_from_typer( mock_locate_implementations: Mock, mock_path: Mock, mock_add_epilog_recursively: Mock, record_property ) -> None: @@ -114,8 +115,8 @@ def test_prepare_cli_calls_add_epilog_recursively_when_not_running_from_typer( @pytest.mark.unit -@patch("aignostics.utils._cli._no_args_is_help_recursively") -@patch("aignostics.utils._cli.locate_implementations") +@patch("aignostics_sdk.utils._cli._no_args_is_help_recursively") +@patch("aignostics.utils.locate_implementations") def test_prepare_cli_calls_no_args_is_help_recursively( mock_locate_implementations: Mock, mock_no_args_is_help_recursively: Mock, record_property ) -> None: @@ -288,8 +289,8 @@ def _broken_plugin_package_patches( "import_module", side_effect=_make_import_side_effect({ PLUGIN: ImportError("broken"), - __project_name__: main_pkg, - f"{__project_name__}.{MYMODULE}": main_mod, + MAIN_PACKAGE: main_pkg, + f"{MAIN_PACKAGE}.{MYMODULE}": main_mod, }), ), patch.object(di_module.pkgutil, "iter_modules", return_value=[("", MYMODULE, False)]), @@ -321,8 +322,8 @@ def _no_match_plugin_patches( "import_module", side_effect=_make_import_side_effect({ PLUGIN: plugin_pkg, - __project_name__: main_pkg, - f"{__project_name__}.{MYMODULE}": main_mod, + MAIN_PACKAGE: main_pkg, + f"{MAIN_PACKAGE}.{MYMODULE}": main_mod, }), ), patch.object(di_module.pkgutil, "iter_modules", return_value=[("", MYMODULE, False)]), @@ -350,8 +351,8 @@ def _no_plugins_patches( """ searched: list[str] = [] base_side_effect = _make_import_side_effect({ - __project_name__: main_pkg, - f"{__project_name__}.{MYMODULE}": main_mod, + MAIN_PACKAGE: main_pkg, + f"{MAIN_PACKAGE}.{MYMODULE}": main_mod, }) def tracking_import(name: str) -> ModuleType: @@ -534,7 +535,7 @@ class _Base: main_instance = _Base() main_pkg = _mock_package() - main_mod = ModuleType(f"{__project_name__}.{MYMODULE}") + main_mod = ModuleType(f"{MAIN_PACKAGE}.{MYMODULE}") main_mod.main_instance = main_instance # type: ignore[attr-defined] with _broken_plugin_package_patches(main_pkg, main_mod): @@ -556,7 +557,7 @@ class _Base: main_instance = _Base() plugin_pkg = _mock_package() main_pkg = _mock_package() - main_mod = ModuleType(f"{__project_name__}.{MYMODULE}") + main_mod = ModuleType(f"{MAIN_PACKAGE}.{MYMODULE}") main_mod.main_instance = main_instance # type: ignore[attr-defined] with _no_match_plugin_patches(plugin_pkg, main_pkg, main_mod): @@ -575,7 +576,7 @@ class _Base: main_instance = _Base() main_pkg = _mock_package() - main_mod = ModuleType(f"{__project_name__}.{MYMODULE}") + main_mod = ModuleType(f"{MAIN_PACKAGE}.{MYMODULE}") main_mod.main_instance = main_instance # type: ignore[attr-defined] with ( @@ -584,8 +585,8 @@ class _Base: di_module.importlib, "import_module", side_effect=_make_import_side_effect({ - __project_name__: main_pkg, - f"{__project_name__}.{MYMODULE}": main_mod, + MAIN_PACKAGE: main_pkg, + f"{MAIN_PACKAGE}.{MYMODULE}": main_mod, }), ), patch.object(di_module.pkgutil, "iter_modules", return_value=[("", MYMODULE, False)]), @@ -625,14 +626,17 @@ class _Base: instance = _Base() main_pkg = _mock_package() - main_mod = ModuleType(f"{__project_name__}.{MYMODULE}") + main_mod = ModuleType(f"{MAIN_PACKAGE}.{MYMODULE}") main_mod.instance = instance # type: ignore[attr-defined] with _no_plugins_patches(main_pkg, main_mod) as searched: result = locate_implementations(_Base) assert instance in result - assert not any(p != __project_name__ and not p.startswith(f"{__project_name__}.") for p in searched) + assert not any( + p not in {"aignostics", "aignostics_sdk"} and not p.startswith(("aignostics.", "aignostics_sdk.")) + for p in searched + ) # --------------------------------------------------------------------------- @@ -720,7 +724,7 @@ class MainSub(_Base): pass main_pkg = _mock_package() - main_mod = ModuleType(f"{__project_name__}.{MYMODULE}") + main_mod = ModuleType(f"{MAIN_PACKAGE}.{MYMODULE}") main_mod.MainSub = MainSub # type: ignore[attr-defined] with _broken_plugin_package_patches(main_pkg, main_mod): @@ -742,7 +746,7 @@ class MainSub(_Base): plugin_pkg = _mock_package() main_pkg = _mock_package() - main_mod = ModuleType(f"{__project_name__}.{MYMODULE}") + main_mod = ModuleType(f"{MAIN_PACKAGE}.{MYMODULE}") main_mod.MainSub = MainSub # type: ignore[attr-defined] with _no_match_plugin_patches(plugin_pkg, main_pkg, main_mod): @@ -763,7 +767,7 @@ class MainSub(_Base): pass main_pkg = _mock_package() - main_mod = ModuleType(f"{__project_name__}.{MYMODULE}") + main_mod = ModuleType(f"{MAIN_PACKAGE}.{MYMODULE}") main_mod.MainSub = MainSub # type: ignore[attr-defined] with ( @@ -772,8 +776,8 @@ class MainSub(_Base): di_module.importlib, "import_module", side_effect=_make_import_side_effect({ - __project_name__: main_pkg, - f"{__project_name__}.{MYMODULE}": main_mod, + MAIN_PACKAGE: main_pkg, + f"{MAIN_PACKAGE}.{MYMODULE}": main_mod, }), ), patch.object(di_module.pkgutil, "iter_modules", return_value=[("", MYMODULE, False)]), @@ -793,9 +797,14 @@ def test_locate_subclasses_excludes_base_class(clear_di_caches, record_property) mock_module = ModuleType("aignostics.testmodule") mock_module.AnotherDummyBase = AnotherDummyBase # type: ignore[attr-defined] + def import_side_effect(name: str) -> ModuleType: + if name == "aignostics.testmodule": + return mock_module + return mock_package + with ( patch.object(di_module, "discover_plugin_packages", return_value=()), - patch.object(di_module.importlib, "import_module", side_effect=[mock_package, mock_module]), + patch.object(di_module.importlib, "import_module", side_effect=import_side_effect), patch.object(di_module.pkgutil, "iter_modules", return_value=[("", "testmodule", False)]), ): result = locate_subclasses(AnotherDummyBase) @@ -881,14 +890,17 @@ class LocalSub(_Base): pass main_pkg = _mock_package() - main_mod = ModuleType(f"{__project_name__}.{MYMODULE}") + main_mod = ModuleType(f"{MAIN_PACKAGE}.{MYMODULE}") main_mod.LocalSub = LocalSub # type: ignore[attr-defined] with _no_plugins_patches(main_pkg, main_mod) as searched: result = locate_subclasses(_Base) assert LocalSub in result - assert not any(p != __project_name__ and not p.startswith(f"{__project_name__}.") for p in searched) + assert not any( + p not in {"aignostics", "aignostics_sdk"} and not p.startswith(("aignostics.", "aignostics_sdk.")) + for p in searched + ) @pytest.mark.unit @@ -914,10 +926,10 @@ def track_imports(name: str) -> MagicMock: patch.object(di_module.pkgutil, "iter_modules", return_value=[]), ): locate_implementations(AnotherDummyBase) - assert import_order == ["plugin_a", "plugin_b", __project_name__] + assert import_order == ["plugin_a", "plugin_b", "aignostics_sdk", "aignostics"] _implementation_cache.clear() import_order.clear() locate_subclasses(AnotherDummySub) - assert import_order == ["plugin_a", "plugin_b", __project_name__] + assert import_order == ["plugin_a", "plugin_b", "aignostics_sdk", "aignostics"] diff --git a/tests/aignostics/utils/fs_test.py b/tests/aignostics/utils/fs_test.py index d1089402b..3163aef73 100644 --- a/tests/aignostics/utils/fs_test.py +++ b/tests/aignostics/utils/fs_test.py @@ -5,7 +5,7 @@ import pytest -from aignostics.utils._fs import ( +from aignostics_sdk.utils._fs import ( get_user_data_directory, open_user_data_directory, sanitize_path, @@ -275,9 +275,9 @@ def test_get_user_data_directory_without_scope(record_property, tmp_path) -> Non """Test get_user_data_directory returns correct path without scope.""" record_property("tested-item-id", "SPEC-UTILS-SERVICE") with ( - patch("aignostics.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, - patch("aignostics.utils._fs.__project_name__", "test_project"), - patch("aignostics.utils._fs.__is_running_in_read_only_environment__", False), + patch("aignostics_sdk.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, + patch("aignostics_sdk.utils._fs.__project_name__", "test_project"), + patch("aignostics_sdk.utils._fs.__is_running_in_read_only_environment__", False), patch("pathlib.Path.mkdir") as mock_mkdir, ): mock_user_data_dir.return_value = str(tmp_path / "test_project") @@ -295,9 +295,9 @@ def test_get_user_data_directory_with_scope(record_property, tmp_path) -> None: """Test get_user_data_directory returns correct path with scope.""" record_property("tested-item-id", "SPEC-UTILS-SERVICE") with ( - patch("aignostics.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, - patch("aignostics.utils._fs.__project_name__", "test_project"), - patch("aignostics.utils._fs.__is_running_in_read_only_environment__", False), + patch("aignostics_sdk.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, + patch("aignostics_sdk.utils._fs.__project_name__", "test_project"), + patch("aignostics_sdk.utils._fs.__is_running_in_read_only_environment__", False), patch("pathlib.Path.mkdir") as mock_mkdir, ): mock_user_data_dir.return_value = str(tmp_path / "test_project") @@ -315,9 +315,9 @@ def test_get_user_data_directory_with_nested_scope(record_property, tmp_path) -> """Test get_user_data_directory returns correct path with nested scope.""" record_property("tested-item-id", "SPEC-UTILS-SERVICE") with ( - patch("aignostics.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, - patch("aignostics.utils._fs.__project_name__", "test_project"), - patch("aignostics.utils._fs.__is_running_in_read_only_environment__", False), + patch("aignostics_sdk.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, + patch("aignostics_sdk.utils._fs.__project_name__", "test_project"), + patch("aignostics_sdk.utils._fs.__is_running_in_read_only_environment__", False), patch("pathlib.Path.mkdir") as mock_mkdir, ): mock_user_data_dir.return_value = str(tmp_path / "test_project") @@ -335,9 +335,9 @@ def test_get_user_data_directory_read_only_environment_no_mkdir(record_property, """Test get_user_data_directory doesn't create directory in read-only environment.""" record_property("tested-item-id", "SPEC-UTILS-SERVICE") with ( - patch("aignostics.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, - patch("aignostics.utils._fs.__project_name__", "test_project"), - patch("aignostics.utils._fs.__is_running_in_read_only_environment__", True), + patch("aignostics_sdk.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, + patch("aignostics_sdk.utils._fs.__project_name__", "test_project"), + patch("aignostics_sdk.utils._fs.__is_running_in_read_only_environment__", True), patch("pathlib.Path.mkdir") as mock_mkdir, ): mock_user_data_dir.return_value = str(tmp_path / "test_project") @@ -355,9 +355,9 @@ def test_get_user_data_directory_empty_scope(record_property, tmp_path) -> None: """Test get_user_data_directory handles empty scope string.""" record_property("tested-item-id", "SPEC-UTILS-SERVICE") with ( - patch("aignostics.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, - patch("aignostics.utils._fs.__project_name__", "test_project"), - patch("aignostics.utils._fs.__is_running_in_read_only_environment__", False), + patch("aignostics_sdk.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, + patch("aignostics_sdk.utils._fs.__project_name__", "test_project"), + patch("aignostics_sdk.utils._fs.__is_running_in_read_only_environment__", False), patch("pathlib.Path.mkdir") as mock_mkdir, ): mock_user_data_dir.return_value = str(tmp_path / "test_project") @@ -375,9 +375,9 @@ def test_get_user_data_directory_none_scope(record_property, tmp_path) -> None: """Test get_user_data_directory handles None scope.""" record_property("tested-item-id", "SPEC-UTILS-SERVICE") with ( - patch("aignostics.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, - patch("aignostics.utils._fs.__project_name__", "test_project"), - patch("aignostics.utils._fs.__is_running_in_read_only_environment__", False), + patch("aignostics_sdk.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, + patch("aignostics_sdk.utils._fs.__project_name__", "test_project"), + patch("aignostics_sdk.utils._fs.__is_running_in_read_only_environment__", False), patch("pathlib.Path.mkdir") as mock_mkdir, ): mock_user_data_dir.return_value = str(tmp_path / "test_project") @@ -396,11 +396,11 @@ def test_open_user_data_directory_without_scope(record_property, tmp_path) -> No """Test open_user_data_directory opens correct directory without scope.""" record_property("tested-item-id", "SPEC-UTILS-SERVICE") with ( - patch("aignostics.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, - patch("aignostics.utils._fs.__project_name__", "test_project"), - patch("aignostics.utils._fs.__is_running_in_read_only_environment__", False), + patch("aignostics_sdk.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, + patch("aignostics_sdk.utils._fs.__project_name__", "test_project"), + patch("aignostics_sdk.utils._fs.__is_running_in_read_only_environment__", False), patch("pathlib.Path.mkdir") as mock_mkdir, - patch("aignostics.utils._fs.show_in_file_manager") as mock_show_in_file_manager, + patch("aignostics_sdk.utils._fs.show_in_file_manager") as mock_show_in_file_manager, ): mock_user_data_dir.return_value = str(tmp_path / "test_project") @@ -418,11 +418,11 @@ def test_open_user_data_directory_with_scope(record_property, tmp_path) -> None: """Test open_user_data_directory opens correct directory with scope.""" record_property("tested-item-id", "SPEC-UTILS-SERVICE") with ( - patch("aignostics.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, - patch("aignostics.utils._fs.__project_name__", "test_project"), - patch("aignostics.utils._fs.__is_running_in_read_only_environment__", False), + patch("aignostics_sdk.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, + patch("aignostics_sdk.utils._fs.__project_name__", "test_project"), + patch("aignostics_sdk.utils._fs.__is_running_in_read_only_environment__", False), patch("pathlib.Path.mkdir") as mock_mkdir, - patch("aignostics.utils._fs.show_in_file_manager") as mock_show_in_file_manager, + patch("aignostics_sdk.utils._fs.show_in_file_manager") as mock_show_in_file_manager, ): mock_user_data_dir.return_value = str(tmp_path / "test_project") @@ -440,11 +440,11 @@ def test_open_user_data_directory_with_nested_scope(record_property, tmp_path) - """Test open_user_data_directory opens correct directory with nested scope.""" record_property("tested-item-id", "SPEC-UTILS-SERVICE") with ( - patch("aignostics.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, - patch("aignostics.utils._fs.__project_name__", "test_project"), - patch("aignostics.utils._fs.__is_running_in_read_only_environment__", False), + patch("aignostics_sdk.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, + patch("aignostics_sdk.utils._fs.__project_name__", "test_project"), + patch("aignostics_sdk.utils._fs.__is_running_in_read_only_environment__", False), patch("pathlib.Path.mkdir") as mock_mkdir, - patch("aignostics.utils._fs.show_in_file_manager") as mock_show_in_file_manager, + patch("aignostics_sdk.utils._fs.show_in_file_manager") as mock_show_in_file_manager, ): mock_user_data_dir.return_value = str(tmp_path / "test_project") @@ -462,11 +462,11 @@ def test_open_user_data_directory_read_only_environment(record_property, tmp_pat """Test open_user_data_directory works in read-only environment.""" record_property("tested-item-id", "SPEC-UTILS-SERVICE") with ( - patch("aignostics.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, - patch("aignostics.utils._fs.__project_name__", "test_project"), - patch("aignostics.utils._fs.__is_running_in_read_only_environment__", True), + patch("aignostics_sdk.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, + patch("aignostics_sdk.utils._fs.__project_name__", "test_project"), + patch("aignostics_sdk.utils._fs.__is_running_in_read_only_environment__", True), patch("pathlib.Path.mkdir") as mock_mkdir, - patch("aignostics.utils._fs.show_in_file_manager") as mock_show_in_file_manager, + patch("aignostics_sdk.utils._fs.show_in_file_manager") as mock_show_in_file_manager, ): mock_user_data_dir.return_value = str(tmp_path / "test_project") @@ -484,11 +484,11 @@ def test_open_user_data_directory_show_in_file_manager_exception(record_property """Test open_user_data_directory handles show_in_file_manager exceptions gracefully.""" record_property("tested-item-id", "SPEC-UTILS-SERVICE") with ( - patch("aignostics.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, - patch("aignostics.utils._fs.__project_name__", "test_project"), - patch("aignostics.utils._fs.__is_running_in_read_only_environment__", False), + patch("aignostics_sdk.utils._fs.platformdirs.user_data_dir") as mock_user_data_dir, + patch("aignostics_sdk.utils._fs.__project_name__", "test_project"), + patch("aignostics_sdk.utils._fs.__is_running_in_read_only_environment__", False), patch("pathlib.Path.mkdir") as _mock_mkdir, - patch("aignostics.utils._fs.show_in_file_manager") as mock_show_in_file_manager, + patch("aignostics_sdk.utils._fs.show_in_file_manager") as mock_show_in_file_manager, ): mock_user_data_dir.return_value = str(tmp_path / "test_project") mock_show_in_file_manager.side_effect = Exception("File manager not available") diff --git a/tests/aignostics/utils/gui_test.py b/tests/aignostics/utils/gui_test.py index 9ec2c9b9c..0bf177d72 100644 --- a/tests/aignostics/utils/gui_test.py +++ b/tests/aignostics/utils/gui_test.py @@ -5,14 +5,14 @@ from unittest import mock import pytest - -from aignostics.utils._constants import __project_name__ from aignostics.utils._gui import ( BasePageBuilder, gui_register_pages, gui_run, ) +from aignostics_sdk.utils._constants import __project_name__ + @pytest.mark.unit def test_base_page_builder_is_abstract(record_property) -> None: diff --git a/tests/aignostics/utils/health_test.py b/tests/aignostics/utils/health_test.py index 2e9b6eb4a..c3445a20f 100644 --- a/tests/aignostics/utils/health_test.py +++ b/tests/aignostics/utils/health_test.py @@ -2,7 +2,7 @@ import pytest -from aignostics.utils._health import Health +from aignostics_sdk.utils._health import Health DB_FAILURE = "DB failure" CACHE_SLOW = "cache slow" diff --git a/tests/aignostics/utils/log_test.py b/tests/aignostics/utils/log_test.py index a33a4b537..4fc186a43 100644 --- a/tests/aignostics/utils/log_test.py +++ b/tests/aignostics/utils/log_test.py @@ -7,7 +7,7 @@ import pytest -from aignostics.utils._log import _validate_file_name, logging_initialize +from aignostics_sdk.utils._log import _validate_file_name, logging_initialize @pytest.mark.unit @@ -105,10 +105,10 @@ def test_validate_file_name_invalid_path(record_property) -> None: def test_logging_initialize_with_defaults(record_property, caplog: pytest.LogCaptureFixture) -> None: """Test logging_initialize with default settings.""" record_property("tested-item-id", "SPEC-UTILS-SERVICE") - from aignostics.utils._log import logger + from aignostics_sdk.utils._log import logger with ( - mock.patch("aignostics.utils._log.load_settings") as mock_load_settings, + mock.patch("aignostics_sdk.utils._log.load_settings") as mock_load_settings, mock.patch.object(logger, "remove") as mock_remove, mock.patch.object(logger, "configure") as mock_configure, mock.patch.object(logger, "add") as mock_add, diff --git a/tests/aignostics/utils/mcp_test.py b/tests/aignostics/utils/mcp_test.py index 4e0276e6b..a92a2abcf 100644 --- a/tests/aignostics/utils/mcp_test.py +++ b/tests/aignostics/utils/mcp_test.py @@ -7,8 +7,6 @@ from unittest.mock import patch import pytest -from fastmcp import Client, FastMCP - from aignostics.utils import ( MCP_SERVER_NAME, discover_plugin_packages, @@ -17,6 +15,7 @@ mcp_list_tools, ) from aignostics.utils._di import _implementation_cache +from fastmcp import Client, FastMCP if TYPE_CHECKING: from collections.abc import Iterator diff --git a/tests/aignostics/utils/plugin_test.py b/tests/aignostics/utils/plugin_test.py index 87ccef490..9e143a5ec 100644 --- a/tests/aignostics/utils/plugin_test.py +++ b/tests/aignostics/utils/plugin_test.py @@ -6,7 +6,6 @@ import pytest import typer - from aignostics.utils import BaseNavBuilder from aignostics.utils._di import _implementation_cache, _subclass_cache, discover_plugin_packages diff --git a/tests/aignostics/utils/sentry_test.py b/tests/aignostics/utils/sentry_test.py index 0a8a70204..255c28dea 100644 --- a/tests/aignostics/utils/sentry_test.py +++ b/tests/aignostics/utils/sentry_test.py @@ -11,7 +11,7 @@ import pytest from pydantic import SecretStr - from aignostics.utils._sentry import ( + from aignostics_sdk.utils._sentry import ( _ERR_MSG_INVALID_DOMAIN, _ERR_MSG_MISSING_NETLOC, _ERR_MSG_MISSING_SCHEME, @@ -145,7 +145,7 @@ def test_validate_https_dsn_invalid_cases(record_property) -> None: def test_sentry_initialize_with_no_dsn(record_property, mock_environment: None) -> None: """Test sentry_initialize with no DSN.""" record_property("tested-item-id", "SPEC-UTILS-SERVICE") - with mock.patch("aignostics.utils._sentry.load_settings") as mock_load_settings: + with mock.patch("aignostics_sdk.utils._sentry.load_settings") as mock_load_settings: mock_settings = mock.MagicMock() mock_settings.dsn = None mock_load_settings.return_value = mock_settings @@ -158,7 +158,7 @@ def test_sentry_initialize_with_valid_dsn(record_property, mock_environment: Non """Test sentry_initialize with a valid DSN.""" record_property("tested-item-id", "SPEC-UTILS-SERVICE") with ( - mock.patch("aignostics.utils._sentry.load_settings") as mock_load_settings, + mock.patch("aignostics_sdk.utils._sentry.load_settings") as mock_load_settings, mock.patch("sentry_sdk.init") as mock_sentry_init, ): mock_settings = mock.MagicMock() diff --git a/tests/aignostics/utils/service_test.py b/tests/aignostics/utils/service_test.py index c88528ae1..22a111f96 100644 --- a/tests/aignostics/utils/service_test.py +++ b/tests/aignostics/utils/service_test.py @@ -5,8 +5,8 @@ import pytest -from aignostics.utils._health import Health -from aignostics.utils._service import BaseService +from aignostics_sdk.utils._health import Health +from aignostics_sdk.utils._service import BaseService class _ConcreteService(BaseService): diff --git a/tests/aignostics/utils/settings_test.py b/tests/aignostics/utils/settings_test.py index 8cbbd9a8f..dee431c60 100644 --- a/tests/aignostics/utils/settings_test.py +++ b/tests/aignostics/utils/settings_test.py @@ -7,7 +7,7 @@ import pytest from pydantic import SecretStr -from aignostics.utils._settings import ( +from aignostics_sdk.utils._settings import ( UNHIDE_SENSITIVE_INFO, OpaqueSettings, load_settings, diff --git a/tests/aignostics/utils/user_agent_test.py b/tests/aignostics/utils/user_agent_test.py index ab182ecad..67b7bae56 100644 --- a/tests/aignostics/utils/user_agent_test.py +++ b/tests/aignostics/utils/user_agent_test.py @@ -5,7 +5,7 @@ import pytest -from aignostics.utils._user_agent import user_agent +from aignostics_sdk.utils._user_agent import user_agent @pytest.mark.unit @@ -17,9 +17,9 @@ def test_user_agent_basic_format(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("GITHUB_REPOSITORY", raising=False) with ( - patch("aignostics.utils._user_agent.__project_name__", "aignostics"), - patch("aignostics.utils._user_agent.__version_full__", "1.0.0"), - patch("aignostics.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), + patch("aignostics_sdk.utils._user_agent.__project_name__", "aignostics"), + patch("aignostics_sdk.utils._user_agent.__version_full__", "1.0.0"), + patch("aignostics_sdk.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), ): result = user_agent() @@ -39,9 +39,9 @@ def test_user_agent_with_pytest_current_test(monkeypatch: pytest.MonkeyPatch) -> monkeypatch.delenv("GITHUB_REPOSITORY", raising=False) with ( - patch("aignostics.utils._user_agent.__project_name__", "aignostics"), - patch("aignostics.utils._user_agent.__version_full__", "1.0.0"), - patch("aignostics.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), + patch("aignostics_sdk.utils._user_agent.__project_name__", "aignostics"), + patch("aignostics_sdk.utils._user_agent.__version_full__", "1.0.0"), + patch("aignostics_sdk.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), ): result = user_agent() @@ -59,9 +59,9 @@ def test_user_agent_with_github_run_info(monkeypatch: pytest.MonkeyPatch) -> Non monkeypatch.setenv("GITHUB_REPOSITORY", repository) with ( - patch("aignostics.utils._user_agent.__project_name__", "aignostics"), - patch("aignostics.utils._user_agent.__version_full__", "1.0.0"), - patch("aignostics.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), + patch("aignostics_sdk.utils._user_agent.__project_name__", "aignostics"), + patch("aignostics_sdk.utils._user_agent.__version_full__", "1.0.0"), + patch("aignostics_sdk.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), ): result = user_agent() @@ -78,9 +78,9 @@ def test_user_agent_with_github_run_id_only(monkeypatch: pytest.MonkeyPatch) -> monkeypatch.delenv("GITHUB_REPOSITORY", raising=False) with ( - patch("aignostics.utils._user_agent.__project_name__", "aignostics"), - patch("aignostics.utils._user_agent.__version_full__", "1.0.0"), - patch("aignostics.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), + patch("aignostics_sdk.utils._user_agent.__project_name__", "aignostics"), + patch("aignostics_sdk.utils._user_agent.__version_full__", "1.0.0"), + patch("aignostics_sdk.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), ): result = user_agent() @@ -97,9 +97,9 @@ def test_user_agent_with_github_repository_only(monkeypatch: pytest.MonkeyPatch) monkeypatch.setenv("GITHUB_REPOSITORY", "aignostics/python-sdk") with ( - patch("aignostics.utils._user_agent.__project_name__", "aignostics"), - patch("aignostics.utils._user_agent.__version_full__", "1.0.0"), - patch("aignostics.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), + patch("aignostics_sdk.utils._user_agent.__project_name__", "aignostics"), + patch("aignostics_sdk.utils._user_agent.__version_full__", "1.0.0"), + patch("aignostics_sdk.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), ): result = user_agent() @@ -120,9 +120,9 @@ def test_user_agent_with_all_optional_variables(monkeypatch: pytest.MonkeyPatch) monkeypatch.setenv("GITHUB_REPOSITORY", repository) with ( - patch("aignostics.utils._user_agent.__project_name__", "aignostics"), - patch("aignostics.utils._user_agent.__version_full__", "1.0.0"), - patch("aignostics.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), + patch("aignostics_sdk.utils._user_agent.__project_name__", "aignostics"), + patch("aignostics_sdk.utils._user_agent.__version_full__", "1.0.0"), + patch("aignostics_sdk.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), ): result = user_agent() @@ -148,9 +148,9 @@ def test_user_agent_version_with_build_number(monkeypatch: pytest.MonkeyPatch) - monkeypatch.delenv("GITHUB_REPOSITORY", raising=False) with ( - patch("aignostics.utils._user_agent.__project_name__", "aignostics"), - patch("aignostics.utils._user_agent.__version_full__", "1.0.0+42"), - patch("aignostics.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), + patch("aignostics_sdk.utils._user_agent.__project_name__", "aignostics"), + patch("aignostics_sdk.utils._user_agent.__version_full__", "1.0.0+42"), + patch("aignostics_sdk.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), ): result = user_agent() @@ -166,9 +166,9 @@ def test_user_agent_special_characters_in_test_name(monkeypatch: pytest.MonkeyPa monkeypatch.delenv("GITHUB_REPOSITORY", raising=False) with ( - patch("aignostics.utils._user_agent.__project_name__", "aignostics"), - patch("aignostics.utils._user_agent.__version_full__", "1.0.0"), - patch("aignostics.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), + patch("aignostics_sdk.utils._user_agent.__project_name__", "aignostics"), + patch("aignostics_sdk.utils._user_agent.__version_full__", "1.0.0"), + patch("aignostics_sdk.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), ): result = user_agent() @@ -183,9 +183,9 @@ def test_user_agent_format_consistency(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("GITHUB_REPOSITORY", raising=False) with ( - patch("aignostics.utils._user_agent.__project_name__", "aignostics"), - patch("aignostics.utils._user_agent.__version_full__", "1.0.0"), - patch("aignostics.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), + patch("aignostics_sdk.utils._user_agent.__project_name__", "aignostics"), + patch("aignostics_sdk.utils._user_agent.__version_full__", "1.0.0"), + patch("aignostics_sdk.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), ): result = user_agent() @@ -205,9 +205,9 @@ def test_user_agent_empty_environment_variables(monkeypatch: pytest.MonkeyPatch) monkeypatch.setenv("GITHUB_REPOSITORY", "") with ( - patch("aignostics.utils._user_agent.__project_name__", "aignostics"), - patch("aignostics.utils._user_agent.__version_full__", "1.0.0"), - patch("aignostics.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), + patch("aignostics_sdk.utils._user_agent.__project_name__", "aignostics"), + patch("aignostics_sdk.utils._user_agent.__version_full__", "1.0.0"), + patch("aignostics_sdk.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), ): result = user_agent() @@ -230,9 +230,9 @@ def test_user_agent_different_repository_urls(monkeypatch: pytest.MonkeyPatch) - custom_repo_url = "https://gitlab.com/custom-org/custom-project" with ( - patch("aignostics.utils._user_agent.__project_name__", "aignostics"), - patch("aignostics.utils._user_agent.__version_full__", "1.0.0"), - patch("aignostics.utils._user_agent.__repository_url__", custom_repo_url), + patch("aignostics_sdk.utils._user_agent.__project_name__", "aignostics"), + patch("aignostics_sdk.utils._user_agent.__version_full__", "1.0.0"), + patch("aignostics_sdk.utils._user_agent.__repository_url__", custom_repo_url), ): result = user_agent() @@ -247,9 +247,9 @@ def test_user_agent_platform_info_included(monkeypatch: pytest.MonkeyPatch) -> N monkeypatch.delenv("GITHUB_REPOSITORY", raising=False) with ( - patch("aignostics.utils._user_agent.__project_name__", "aignostics"), - patch("aignostics.utils._user_agent.__version_full__", "1.0.0"), - patch("aignostics.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), + patch("aignostics_sdk.utils._user_agent.__project_name__", "aignostics"), + patch("aignostics_sdk.utils._user_agent.__version_full__", "1.0.0"), + patch("aignostics_sdk.utils._user_agent.__repository_url__", "https://github.com/aignostics/python-sdk"), ): result = user_agent() diff --git a/tests/aignostics/wsi/cli_test.py b/tests/aignostics/wsi/cli_test.py index 38d7b14b6..3951e1aba 100644 --- a/tests/aignostics/wsi/cli_test.py +++ b/tests/aignostics/wsi/cli_test.py @@ -6,9 +6,9 @@ from unittest.mock import MagicMock, patch import pytest +from aignostics.cli import cli from typer.testing import CliRunner -from aignostics.cli import cli from tests.conftest import normalize_output SERIES_UID = "1.3.6.1.4.1.5962.99.1.1069745200.1645485340.1637452317744.2.0" diff --git a/tests/aignostics/wsi/service_test.py b/tests/aignostics/wsi/service_test.py index 64074ae4f..ec81f92cd 100644 --- a/tests/aignostics/wsi/service_test.py +++ b/tests/aignostics/wsi/service_test.py @@ -10,13 +10,12 @@ import pydicom import pytest +from aignostics.wsi import Service as WSIService from fastapi.testclient import TestClient from nicegui import app from nicegui.testing import User from PIL import Image -from aignostics.wsi import Service as WSIService - CONTENT_LENGTH_FALLBACK = 32066 # Fallback image size in bytes diff --git a/tests/aignostics_sdk/__init__.py b/tests/aignostics_sdk/__init__.py new file mode 100644 index 000000000..5f230681e --- /dev/null +++ b/tests/aignostics_sdk/__init__.py @@ -0,0 +1 @@ +"""Tests for the aignostics-sdk slim package distribution.""" diff --git a/tests/aignostics_sdk/smoke_test.py b/tests/aignostics_sdk/smoke_test.py new file mode 100644 index 000000000..2f337fa5e --- /dev/null +++ b/tests/aignostics_sdk/smoke_test.py @@ -0,0 +1,87 @@ +"""Smoke tests for the aignostics-sdk slim package. + +These tests verify the slim distribution works in isolation: +- Correct imports resolve +- Core constants are accessible + +Note: The aignostics-sdk CLI entry point (aignostics_sdk.cli) is pending +PYSDK-137 (CLI carve-out). The test_slim_cli_entry_point test is marked +xfail until that phase lands. + +Note: Dependency slimming (removal of heavy deps such as openslide, nicegui, +etc.) is pending PYSDK-138 (dependency split). Until that phase merges, +aignostics-sdk carries the full dependency tree. +""" + +from __future__ import annotations + +import subprocess +import sys + +import pytest + + +@pytest.mark.unit +@pytest.mark.slim +def test_platform_client_importable() -> None: + """Core import from aignostics_sdk.platform works.""" + from aignostics_sdk.platform import Client + + assert Client is not None + + +@pytest.mark.unit +@pytest.mark.slim +def test_utils_importable() -> None: + """Core imports from aignostics_sdk.utils work.""" + from aignostics_sdk.utils import BaseService, Health + + assert BaseService is not None + assert Health is not None + + +@pytest.mark.unit +@pytest.mark.slim +def test_aignx_codegen_importable() -> None: + """Bundled codegen is accessible.""" + from aignostics_sdk._codegen.exceptions import ApiException + + assert ApiException is not None + + +@pytest.mark.unit +@pytest.mark.slim +def test_slim_cli_entry_point() -> None: + """aignostics-sdk CLI entry point exits 0.""" + result = subprocess.run( + [sys.executable, "-m", "aignostics_sdk.cli", "--help"], + capture_output=True, + text=True, + timeout=30, + check=False, + ) + assert result.returncode == 0 + assert "user" in result.stdout + assert "sdk" in result.stdout + assert "application" in result.stdout + assert "system" in result.stdout + + +@pytest.mark.unit +@pytest.mark.slim +def test_project_name_preserved() -> None: + """__project_name__ is the slim distribution name; ENV_PREFIX preserves backward compat for env vars.""" + from aignostics_sdk.utils._constants import ENV_PREFIX, __project_name__ + + assert __project_name__ == "aignostics-sdk" + assert ENV_PREFIX == "AIGNOSTICS" + + +@pytest.mark.unit +@pytest.mark.slim +def test_version_available() -> None: + """Package version is accessible.""" + from aignostics_sdk.utils._constants import __version__ + + assert __version__ is not None + assert len(__version__) > 0 diff --git a/tests/main.py b/tests/main.py index c607dea18..444589bd4 100644 --- a/tests/main.py +++ b/tests/main.py @@ -1,7 +1,8 @@ """Start script for pytest.""" from aignostics.constants import WINDOW_TITLE -from aignostics.utils import ( + +from aignostics_sdk.utils import ( gui_run, ) diff --git a/tests/resources/mcp_dummy_plugin/src/mcp_dummy_plugin/_nav.py b/tests/resources/mcp_dummy_plugin/src/mcp_dummy_plugin/_nav.py index 53c14dd4e..70aafab05 100644 --- a/tests/resources/mcp_dummy_plugin/src/mcp_dummy_plugin/_nav.py +++ b/tests/resources/mcp_dummy_plugin/src/mcp_dummy_plugin/_nav.py @@ -1,6 +1,6 @@ """Dummy nav builder for integration testing of plugin GUI page registration.""" -from aignostics.utils import BaseNavBuilder, NavItem +from aignostics_sdk.utils import BaseNavBuilder, NavItem class DummyPluginNavBuilder(BaseNavBuilder): diff --git a/uv.lock b/uv.lock index 9b0b3b220..eed7d0192 100644 --- a/uv.lock +++ b/uv.lock @@ -14,8 +14,67 @@ resolution-markers = [ ] [manifest] +members = [ + "aignostics", + "aignostics-sdk", +] overrides = [{ name = "pytest", specifier = ">=9.0.1" }] +[manifest.dependency-groups] +dev = [ + { name = "autodoc-pydantic", specifier = ">=2.2.0,<3" }, + { name = "bump-my-version", specifier = ">=1.2.4,<2" }, + { name = "coverage", specifier = ">=7.12.0,<7.13" }, + { name = "cyclonedx-py", specifier = ">=1.0.1,<2" }, + { name = "detect-secrets", specifier = ">=1.5.0,<2" }, + { name = "enum-tools", specifier = ">=0.13.0,<1" }, + { name = "fonttools", specifier = ">=4.60.2" }, + { name = "furo", specifier = ">=2025.9.25" }, + { name = "git-cliff", specifier = ">=2.10.1,<3" }, + { name = "linkify-it-py", specifier = ">=2.0.3,<3" }, + { name = "mypy", specifier = ">=1.19.0,<2" }, + { name = "myst-parser", specifier = ">=5,<6" }, + { name = "nox", extras = ["uv"], specifier = ">=2025.11.12" }, + { name = "pip", specifier = ">=26.1" }, + { name = "pip-audit", specifier = ">=2.10.0,<3" }, + { name = "pip-licenses", git = "https://github.com/neXenio/pip-licenses.git?rev=master" }, + { name = "pre-commit", specifier = ">=4.5.0,<5" }, + { name = "pyright", specifier = ">=1.1.408,<1.1.409" }, + { name = "pytest", specifier = ">=9.0.3,<10" }, + { name = "pytest-asyncio", specifier = ">=1.3.0,<2" }, + { name = "pytest-cov", specifier = ">=7.0.0,<8" }, + { name = "pytest-docker", specifier = ">=3.2.5,<4" }, + { name = "pytest-durations", specifier = ">=1.6.1,<2" }, + { name = "pytest-env", specifier = ">=1.2.0,<2" }, + { name = "pytest-md-report", specifier = ">=0.7.0,<1" }, + { name = "pytest-regressions", specifier = ">=2.8.3,<3" }, + { name = "pytest-retry", specifier = ">=1.7.0,<2" }, + { name = "pytest-selenium", specifier = ">=4.1.0,<5" }, + { name = "pytest-subprocess", specifier = ">=1.5.3,<2" }, + { name = "pytest-timeout", specifier = ">=2.4.0,<3" }, + { name = "pytest-watcher", specifier = ">=0.4.3,<1" }, + { name = "pytest-xdist", extras = ["psutil"], specifier = ">=3.8.0,<4" }, + { name = "ruff", specifier = ">=0.14.8,<1" }, + { name = "scalene", specifier = ">=2.0.1,<3" }, + { name = "sphinx", specifier = ">=8.2.3,<9" }, + { name = "sphinx-autobuild", specifier = ">=2025.8.25,<2026" }, + { name = "sphinx-click", specifier = ">=6.2.0,<7" }, + { name = "sphinx-copybutton", specifier = ">=0.5.2,<1" }, + { name = "sphinx-inline-tabs", specifier = ">=2025.12.21,<2026" }, + { name = "sphinx-rtd-theme", specifier = ">=3.0.2,<4" }, + { name = "sphinx-selective-exclude", specifier = ">=1.0.3,<2" }, + { name = "sphinx-toolbox", specifier = ">=4.1.0,<5" }, + { name = "sphinxcontrib-mermaid" }, + { name = "sphinxext-opengraph", specifier = ">=0.9.1,<1" }, + { name = "swagger-plugin-for-sphinx", specifier = ">=7,<8" }, + { name = "tomli", specifier = ">=2.3.0,<3" }, + { name = "types-pyyaml", specifier = ">=6.0.12.20250915,<7" }, + { name = "types-requests", specifier = ">=2.32.4.20250913,<3" }, + { name = "uv", specifier = ">=0.11.6" }, + { name = "virtualenv", specifier = ">=20.36.1" }, + { name = "watchdog", specifier = ">=6.0.0,<7" }, +] + [[package]] name = "accessible-pygments" version = "0.0.5" @@ -31,69 +90,38 @@ wheels = [ [[package]] name = "aignostics" version = "1.4.0" -source = { editable = "." } +source = { editable = "packages/aignostics" } dependencies = [ + { name = "aignostics-sdk" }, { name = "aiohttp" }, { name = "boto3" }, - { name = "certifi" }, { name = "crc32c" }, - { name = "cryptography" }, { name = "defusedxml" }, { name = "dicom-validator" }, { name = "dicomweb-client", extra = ["gcp"] }, { name = "duckdb" }, - { name = "fastapi", extra = ["all", "standard"] }, - { name = "fastmcp" }, { name = "fastparquet", marker = "python_full_version < '3.14'" }, - { name = "filelock" }, { name = "google-cloud-storage" }, - { name = "h11" }, { name = "highdicom", marker = "python_full_version < '3.14'" }, { name = "html-sanitizer" }, - { name = "httpx" }, { name = "humanize" }, { name = "idc-index-data" }, { name = "ijson" }, - { name = "jsf" }, - { name = "jsonschema", extra = ["format-nongpl"] }, - { name = "loguru" }, { name = "lxml" }, { name = "lxml-html-clean" }, { name = "marshmallow" }, - { name = "nicegui", extra = ["native"] }, { name = "openslide-bin" }, { name = "openslide-python" }, { name = "packaging" }, { name = "pandas" }, - { name = "pillow" }, - { name = "platformdirs" }, { name = "procrastinate" }, { name = "protobuf" }, - { name = "psutil" }, { name = "pyarrow", marker = "python_full_version >= '3.14'" }, - { name = "pyasn1" }, - { name = "pydantic-settings" }, { name = "pydicom" }, - { name = "pygments" }, - { name = "pyjwt", extra = ["crypto"] }, { name = "python-dateutil" }, - { name = "python-multipart" }, - { name = "pywin32", marker = "sys_platform == 'win32'" }, { name = "pyyaml" }, - { name = "requests" }, - { name = "requests-oauthlib" }, - { name = "rfc3987", marker = "sys_platform == 'never'" }, { name = "s5cmd" }, - { name = "semver" }, - { name = "sentry-sdk" }, { name = "shapely" }, - { name = "starlette" }, - { name = "tenacity" }, - { name = "tornado" }, - { name = "tqdm" }, - { name = "truststore" }, - { name = "typer" }, - { name = "urllib3" }, { name = "wsidicom" }, ] @@ -115,205 +143,143 @@ pyinstaller = [ { name = "pyinstaller" }, ] -[package.dev-dependencies] -dev = [ - { name = "autodoc-pydantic" }, - { name = "bump-my-version" }, - { name = "coverage" }, - { name = "cyclonedx-py" }, - { name = "detect-secrets" }, - { name = "enum-tools" }, - { name = "fonttools" }, - { name = "furo" }, - { name = "git-cliff" }, - { name = "linkify-it-py" }, - { name = "mypy" }, - { name = "myst-parser" }, - { name = "nox", extra = ["uv"] }, - { name = "pip" }, - { name = "pip-audit" }, - { name = "pip-licenses" }, - { name = "pre-commit" }, - { name = "pyright" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "pytest-docker" }, - { name = "pytest-durations" }, - { name = "pytest-env" }, - { name = "pytest-md-report" }, - { name = "pytest-regressions" }, - { name = "pytest-retry" }, - { name = "pytest-selenium" }, - { name = "pytest-subprocess" }, - { name = "pytest-timeout" }, - { name = "pytest-watcher" }, - { name = "pytest-xdist", extra = ["psutil"] }, - { name = "ruff" }, - { name = "scalene" }, - { name = "sphinx" }, - { name = "sphinx-autobuild" }, - { name = "sphinx-click" }, - { name = "sphinx-copybutton" }, - { name = "sphinx-inline-tabs" }, - { name = "sphinx-rtd-theme" }, - { name = "sphinx-selective-exclude" }, - { name = "sphinx-toolbox" }, - { name = "sphinxcontrib-mermaid" }, - { name = "sphinxext-opengraph" }, - { name = "swagger-plugin-for-sphinx" }, - { name = "tomli" }, - { name = "types-pyyaml" }, - { name = "types-requests" }, - { name = "uv" }, - { name = "virtualenv" }, - { name = "watchdog" }, -] - [package.metadata] requires-dist = [ + { name = "aignostics-sdk", editable = "packages/aignostics-sdk" }, { name = "aiohttp", specifier = ">=3.13.4" }, { name = "boto3", specifier = ">=1.42.4,<2" }, - { name = "certifi", specifier = ">=2025.11.12" }, { name = "cloudpathlib", marker = "extra == 'marimo'", specifier = ">=0.23.0,<1" }, { name = "crc32c", specifier = ">=2.8,<3" }, - { name = "cryptography", specifier = ">=46.0.7" }, { name = "defusedxml", specifier = ">=0.7.1" }, { name = "dicom-validator", specifier = ">=0.7.3,<1" }, { name = "dicomweb-client", extras = ["gcp"], specifier = ">=0.59.3,<1" }, { name = "duckdb", specifier = ">=1.4.2,<=2" }, - { name = "fastapi", extras = ["all", "standard"], specifier = ">=0.123.10" }, - { name = "fastmcp", specifier = ">=3.2.0,<4" }, { name = "fastparquet", marker = "python_full_version < '3.14'", specifier = ">=2026.3.0,<2026.4.0" }, - { name = "filelock", specifier = ">=3.20.3" }, { name = "google-cloud-storage", specifier = ">=3.6.0,<4" }, - { name = "h11", specifier = ">=0.16.0" }, { name = "highdicom", marker = "python_full_version < '3.14'", specifier = ">=0.26.1,<1" }, { name = "html-sanitizer", specifier = ">=2.6.0,<3" }, - { name = "httpx", specifier = ">=0.28.1,<1" }, { name = "humanize", specifier = ">=4.14.0,<5" }, { name = "idc-index-data", specifier = "==24.0.3" }, { name = "ijson", specifier = ">=3.4.0.post0,<4" }, { name = "ipython", marker = "extra == 'marimo'", specifier = ">=9.8.0,<10" }, - { name = "jsf", specifier = ">=0.11.2,<1" }, - { name = "jsonschema", extras = ["format-nongpl"], specifier = ">=4.25.1,<5" }, { name = "jupyter", marker = "extra == 'jupyter'", specifier = ">=1.1.1,<2" }, { name = "jupyter-core", marker = "extra == 'jupyter'", specifier = ">=5.8.1" }, { name = "jupyterlab", marker = "extra == 'jupyter'", specifier = ">=4.4.9" }, - { name = "loguru", specifier = ">=0.7.3,<1" }, { name = "lxml", specifier = ">=6.1.0" }, { name = "lxml-html-clean", specifier = ">=0.4.4" }, { name = "marimo", marker = "extra == 'marimo'", specifier = ">=0.23.0,<1" }, { name = "marshmallow", specifier = ">=3.26.2" }, { name = "matplotlib", marker = "extra == 'marimo'", specifier = ">=3.10.7,<4" }, { name = "nbconvert", marker = "extra == 'jupyter'", specifier = ">=7.17.1" }, - { name = "nicegui", extras = ["native"], specifier = ">=3.11.0,<4" }, { name = "openslide-bin", specifier = ">=4.0.0.10,<5" }, { name = "openslide-python", specifier = ">=1.4.3,<2" }, { name = "packaging", specifier = ">=26,<27" }, { name = "pandas", specifier = ">=2.3.3,<4" }, - { name = "pillow", specifier = ">=12.2.0" }, - { name = "platformdirs", specifier = ">=4.3.2,<5" }, - { name = "platformdirs", specifier = ">=4.5.1,<5" }, { name = "procrastinate", specifier = ">=3.5.3" }, { name = "protobuf", specifier = ">=6.33.5" }, - { name = "psutil", specifier = ">=7.1.3,<8" }, { name = "pyarrow", marker = "python_full_version >= '3.14'", specifier = ">=23.0.1,<24" }, + { name = "pydicom", specifier = ">=3.0.2" }, + { name = "pyinstaller", marker = "extra == 'pyinstaller'", specifier = ">=6.14.0,<7" }, + { name = "python-dateutil", specifier = ">=2.9.0.post0,<3" }, + { name = "pyyaml", specifier = ">=6.0.3,<7" }, + { name = "s5cmd", specifier = ">=0.3.3,<1" }, + { name = "shapely", specifier = ">=2.1.2,<3" }, + { name = "shapely", marker = "extra == 'marimo'", specifier = ">=2.1.0,<3" }, + { name = "wsidicom", specifier = ">=0.28.1,<1" }, +] +provides-extras = ["pyinstaller", "jupyter", "marimo", "qupath"] + +[[package]] +name = "aignostics-sdk" +version = "1.4.3" +source = { editable = "packages/aignostics-sdk" } +dependencies = [ + { name = "certifi" }, + { name = "crc32c" }, + { name = "cryptography" }, + { name = "fastapi", extra = ["all", "standard"] }, + { name = "fastmcp" }, + { name = "filelock" }, + { name = "h11" }, + { name = "httpx" }, + { name = "humanize" }, + { name = "jsf" }, + { name = "jsonschema", extra = ["format-nongpl"] }, + { name = "loguru" }, + { name = "nicegui", extra = ["native"] }, + { name = "pillow" }, + { name = "platformdirs" }, + { name = "psutil" }, + { name = "pyasn1" }, + { name = "pydantic-settings" }, + { name = "pygments" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "requests-oauthlib" }, + { name = "rfc3987", marker = "sys_platform == 'never'" }, + { name = "semver" }, + { name = "sentry-sdk" }, + { name = "starlette" }, + { name = "tenacity" }, + { name = "tornado" }, + { name = "tqdm" }, + { name = "truststore" }, + { name = "typer" }, + { name = "urllib3" }, +] + +[package.metadata] +requires-dist = [ + { name = "certifi", specifier = ">=2025.11.12" }, + { name = "crc32c", specifier = ">=2.8,<3" }, + { name = "cryptography", specifier = ">=46.0.7" }, + { name = "fastapi", extras = ["all", "standard"], specifier = ">=0.123.10" }, + { name = "fastmcp", specifier = ">=3.2.0,<4" }, + { name = "filelock", specifier = ">=3.20.3" }, + { name = "h11", specifier = ">=0.16.0" }, + { name = "httpx", specifier = ">=0.28.1,<1" }, + { name = "humanize", specifier = ">=4.14.0,<5" }, + { name = "jsf", specifier = ">=0.11.2,<1" }, + { name = "jsonschema", extras = ["format-nongpl"], specifier = ">=4.25.1,<5" }, + { name = "loguru", specifier = ">=0.7.3,<1" }, + { name = "nicegui", extras = ["native"], specifier = ">=3.11.0,<4" }, + { name = "pillow", specifier = ">=12.2.0" }, + { name = "platformdirs", specifier = ">=4.5.1,<5" }, + { name = "psutil", specifier = ">=7.1.3,<8" }, { name = "pyasn1", specifier = ">=0.6.3" }, { name = "pydantic-settings", specifier = ">=2.12.0,<3" }, - { name = "pydicom", specifier = ">=3.0.2" }, { name = "pygments", specifier = ">=2.20.0" }, - { name = "pyinstaller", marker = "extra == 'pyinstaller'", specifier = ">=6.14.0,<7" }, { name = "pyjwt", extras = ["crypto"], specifier = ">=2.12.0,<3" }, - { name = "python-dateutil", specifier = ">=2.9.0.post0,<3" }, { name = "python-multipart", specifier = ">=0.0.26" }, { name = "pywin32", marker = "sys_platform == 'win32'", specifier = ">=311,<312" }, { name = "pyyaml", specifier = ">=6.0.3,<7" }, { name = "requests", specifier = ">=2.33.0,<3" }, { name = "requests-oauthlib", specifier = ">=2.0.0,<3" }, { name = "rfc3987", marker = "sys_platform == 'never'" }, - { name = "s5cmd", specifier = ">=0.3.3,<1" }, { name = "semver", specifier = ">=3.0.4,<4" }, { name = "sentry-sdk", specifier = ">=2.47.0,<3" }, - { name = "shapely", specifier = ">=2.1.2,<3" }, - { name = "shapely", marker = "extra == 'marimo'", specifier = ">=2.1.0,<3" }, { name = "starlette", specifier = ">=1.0.1" }, { name = "tenacity", specifier = ">=9.1.2,<10" }, { name = "tornado", specifier = ">=6.5.5" }, { name = "tqdm", specifier = ">=4.67.1,<5" }, { name = "truststore", specifier = ">=0.10.4,<1" }, { name = "typer", specifier = ">=0.20.0,<1" }, - { name = "urllib3", specifier = ">=2.5.0" }, { name = "urllib3", specifier = ">=2.6.3,<3" }, - { name = "wsidicom", specifier = ">=0.28.1,<1" }, -] -provides-extras = ["pyinstaller", "jupyter", "marimo", "qupath"] - -[package.metadata.requires-dev] -dev = [ - { name = "autodoc-pydantic", specifier = ">=2.2.0,<3" }, - { name = "bump-my-version", specifier = ">=1.2.4,<2" }, - { name = "coverage", specifier = ">=7.12.0,<7.13" }, - { name = "cyclonedx-py", specifier = ">=1.0.1,<2" }, - { name = "detect-secrets", specifier = ">=1.5.0,<2" }, - { name = "enum-tools", specifier = ">=0.13.0,<1" }, - { name = "fonttools", specifier = ">=4.60.2" }, - { name = "furo", specifier = ">=2025.9.25" }, - { name = "git-cliff", specifier = ">=2.10.1,<3" }, - { name = "linkify-it-py", specifier = ">=2.0.3,<3" }, - { name = "mypy", specifier = ">=1.19.0,<2" }, - { name = "myst-parser", specifier = ">=5,<6" }, - { name = "nox", extras = ["uv"], specifier = ">=2025.11.12" }, - { name = "pip", specifier = ">=26.1" }, - { name = "pip-audit", specifier = ">=2.10.0,<3" }, - { name = "pip-licenses", git = "https://github.com/neXenio/pip-licenses.git?rev=master" }, - { name = "pre-commit", specifier = ">=4.5.0,<5" }, - { name = "pyright", specifier = ">=1.1.408,<1.1.409" }, - { name = "pytest", specifier = ">=9.0.3,<10" }, - { name = "pytest-asyncio", specifier = ">=1.3.0,<2" }, - { name = "pytest-cov", specifier = ">=7.0.0,<8" }, - { name = "pytest-docker", specifier = ">=3.2.5,<4" }, - { name = "pytest-durations", specifier = ">=1.6.1,<2" }, - { name = "pytest-env", specifier = ">=1.2.0,<2" }, - { name = "pytest-md-report", specifier = ">=0.7.0,<1" }, - { name = "pytest-regressions", specifier = ">=2.8.3,<3" }, - { name = "pytest-retry", specifier = ">=1.7.0,<2" }, - { name = "pytest-selenium", specifier = ">=4.1.0,<5" }, - { name = "pytest-subprocess", specifier = ">=1.5.3,<2" }, - { name = "pytest-timeout", specifier = ">=2.4.0,<3" }, - { name = "pytest-watcher", specifier = ">=0.4.3,<1" }, - { name = "pytest-xdist", extras = ["psutil"], specifier = ">=3.8.0,<4" }, - { name = "ruff", specifier = ">=0.14.8,<1" }, - { name = "scalene", specifier = ">=2.0.1,<3" }, - { name = "sphinx", specifier = ">=8.2.3,<9" }, - { name = "sphinx-autobuild", specifier = ">=2025.8.25,<2026" }, - { name = "sphinx-click", specifier = ">=6.2.0,<7" }, - { name = "sphinx-copybutton", specifier = ">=0.5.2,<1" }, - { name = "sphinx-inline-tabs", specifier = ">=2025.12.21,<2026" }, - { name = "sphinx-rtd-theme", specifier = ">=3.0.2,<4" }, - { name = "sphinx-selective-exclude", specifier = ">=1.0.3,<2" }, - { name = "sphinx-toolbox", specifier = ">=4.1.0,<5" }, - { name = "sphinxcontrib-mermaid" }, - { name = "sphinxext-opengraph", specifier = ">=0.9.1,<1" }, - { name = "swagger-plugin-for-sphinx", specifier = ">=7,<8" }, - { name = "tomli", specifier = ">=2.3.0,<3" }, - { name = "types-pyyaml", specifier = ">=6.0.12.20250915,<7" }, - { name = "types-requests", specifier = ">=2.32.4.20250913,<3" }, - { name = "uv", specifier = ">=0.11.6" }, - { name = "virtualenv", specifier = ">=20.36.1" }, - { name = "watchdog", specifier = ">=6.0.0,<7" }, ] [[package]] name = "aiofile" -version = "3.9.0" +version = "3.11.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "caio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/67/e2/d7cb819de8df6b5c1968a2756c3cb4122d4fa2b8fc768b53b7c9e5edb646/aiofile-3.9.0.tar.gz", hash = "sha256:e5ad718bb148b265b6df1b3752c4d1d83024b93da9bd599df74b9d9ffcf7919b", size = 17943, upload-time = "2024-10-08T10:39:35.846Z" } +sdist = { url = "https://files.pythonhosted.org/packages/48/41/2fea7e193e061ce54eacc3b7bc0e6a99e4fcff43c78cf0a76dd781ed8334/aiofile-3.11.1.tar.gz", hash = "sha256:1f91912c6643d2a4e49ca4ae3514f0bf3867ce948a36d99a6411b8f4755f4cf9", size = 19342, upload-time = "2026-05-16T08:18:33.538Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/25/da1f0b4dd970e52bf5a36c204c107e11a0c6d3ed195eba0bfbc664c312b2/aiofile-3.9.0-py3-none-any.whl", hash = "sha256:ce2f6c1571538cbdfa0143b04e16b208ecb0e9cb4148e528af8a640ed51cc8aa", size = 19539, upload-time = "2024-10-08T10:39:32.955Z" }, + { url = "https://files.pythonhosted.org/packages/67/cd/0d76dfc5de72bde52f55f53e925c7d152d9c7906634ec1e0cbc7e8d4ad93/aiofile-3.11.1-py3-none-any.whl", hash = "sha256:ce77d14ac07f77bc2b757834a5c129321f3f705c474593deed5ab209079a52c9", size = 20446, upload-time = "2026-05-16T08:18:32.051Z" }, ] [[package]] @@ -327,11 +293,11 @@ wheels = [ [[package]] name = "aiohappyeyeballs" -version = "2.6.1" +version = "2.6.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +sdist = { url = "https://files.pythonhosted.org/packages/33/c6/61a2d7b7572279226bb2e7f61d7a19ca7c90da0329c93fa0d560cbf288d8/aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64", size = 22591, upload-time = "2026-05-20T15:12:24.631Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, + { url = "https://files.pythonhosted.org/packages/5f/fc/a7bf5b6e4e617b45f90f2d9d2a68519c249c81dd4fc2658c7a2a61c4f4b7/aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4", size = 15062, upload-time = "2026-05-20T15:12:23.328Z" }, ] [[package]] @@ -638,15 +604,15 @@ wheels = [ [[package]] name = "authlib" -version = "1.7.1" +version = "1.7.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, { name = "joserfc" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3c/f2/e05664d5275ce811fd4e9df0a2b3f0086ee19a8a80358d95499fa82fd50c/authlib-1.7.1.tar.gz", hash = "sha256:8c09b0f9d080c823e594b52316af70f79a1fa4eed64d0363a076233c04ef063a", size = 175884, upload-time = "2026-05-04T08:11:25.033Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/98/7d93f30d029643c0275dbc0bd6d5a6f670661ee6c9a94d93af7ab4887600/authlib-1.7.2.tar.gz", hash = "sha256:2cea25fefcd4e7173bdf1372c0afc265c8034b23a8cd5dcb6a9164b826c64231", size = 176511, upload-time = "2026-05-06T08:10:23.116Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/82/730650ee5e5b598b7bfdc291b784bc2f6fe02a5671695485403365101088/authlib-1.7.1-py2.py3-none-any.whl", hash = "sha256:8470f4aa6b5590ac41bd81d6e6ee12448ce36a0da0af19bbed69fb53fb4e8ad9", size = 258826, upload-time = "2026-05-04T08:11:23.208Z" }, + { url = "https://files.pythonhosted.org/packages/fb/95/adcb68e20c34162e9135f370d6e31737719c2b6f94bc953fe7ed1f10fe21/authlib-1.7.2-py2.py3-none-any.whl", hash = "sha256:3e1faedc9d87e7d56a164eca3ccb6ace0d61b94abe83e92242f8dc8bba9b4a9f", size = 259548, upload-time = "2026-05-06T08:10:21.436Z" }, ] [[package]] @@ -751,30 +717,30 @@ wheels = [ [[package]] name = "boto3" -version = "1.43.4" +version = "1.43.16" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/17/04/f391b6e5b607bc00b633e3f18932bf1ef7b92e9a03f8fcc5bb09a561234b/boto3-1.43.4.tar.gz", hash = "sha256:00f40e9ca704c0f860b70ef47b042813c8d9a66b3d5e7bf81d578f79ae17dcd3", size = 113174, upload-time = "2026-05-05T19:34:37.859Z" } +sdist = { url = "https://files.pythonhosted.org/packages/48/4f/f13d80d377b54dd2973e243e4eb7ce748706cd53876361cc72506006fd8b/boto3-1.43.16.tar.gz", hash = "sha256:6c337bbe608aacc7d335c79e671f0c893870293b74d652f7a7af22ccd0dfef16", size = 113152, upload-time = "2026-05-27T19:31:39.899Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/85/b25499be693449df63c2c91e4e966ee81e46eb86071747e699f458583ebc/boto3-1.43.4-py3-none-any.whl", hash = "sha256:c4910b54c1f2401ab55d9fbca3d92df0a68e4ac64a69f2bab47a079e384a9d4f", size = 140502, upload-time = "2026-05-05T19:34:36.356Z" }, + { url = "https://files.pythonhosted.org/packages/af/c0/7d687e40f4b7046ede66026ecd1b0a93d47afe26d9170f5926a1605c8641/boto3-1.43.16-py3-none-any.whl", hash = "sha256:dffc8a3cd3edbc0ad95b9c6b983e873b76ede46d3aa0709f94db253f2ff2388f", size = 140537, upload-time = "2026-05-27T19:31:36.453Z" }, ] [[package]] name = "botocore" -version = "1.43.4" +version = "1.43.16" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c1/45/f73f2a126752ed4c762a46210315c80cb7e67a96887aaa2f9c32d41631c6/botocore-1.43.4.tar.gz", hash = "sha256:1a95c90fa9ca6ee85c1a02b04c8e05e948661d201b85b443000d676857905019", size = 15317239, upload-time = "2026-05-05T19:34:27.317Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/74/140451a1fe027cb5e387cc7b1ec56224616ca742c330f1492f71c5cba3fb/botocore-1.43.16.tar.gz", hash = "sha256:813dae233d8b365c19aaf7865b32070e34d7e793654881bf86ecbbef3f4ad5c6", size = 15388648, upload-time = "2026-05-27T19:31:25.172Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/53/8dfb1f8f0be68cce735181d876d8f7c537c5c44656b3b9ab4b3b9e973320/botocore-1.43.4-py3-none-any.whl", hash = "sha256:f1897d3254965cacac7514cfed1b6ff016166b165ee2e06232b4a3954cf9d713", size = 14999193, upload-time = "2026-05-05T19:34:23.893Z" }, + { url = "https://files.pythonhosted.org/packages/46/8f/25933240485c0662bb3fa430ed0c6b8b8124ab3bc136154c07ce12644cb0/botocore-1.43.16-py3-none-any.whl", hash = "sha256:8ab05b1346d26a3c6d69c7338051f07bd4739a090f414d2cff43c0dbc1e18ca7", size = 15067437, upload-time = "2026-05-27T19:31:19.212Z" }, ] [[package]] @@ -835,11 +801,11 @@ filecache = [ [[package]] name = "cachetools" -version = "7.1.1" +version = "7.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ff/e2/85f227594656000ff4d8adadae91a21f536d4a84c6c716a86bd6685874be/cachetools-7.1.1.tar.gz", hash = "sha256:27bdf856d68fd3c71c26c01b5edc312124ed427524d1ddb31aa2b7746fe20d4b", size = 40202, upload-time = "2026-05-03T20:00:29.391Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f4/8b/0d3945a13955303b81272f759a0331e54c5c793da455e6f5706b89d2639c/cachetools-7.1.4.tar.gz", hash = "sha256:437f55a4e0c1b01a4f3077cc470e6991d47430970e36fbcb77e2be0df4fc1cd6", size = 40085, upload-time = "2026-05-21T22:40:43.376Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/0f/f897abe4ea0a8c408ae65c8c83bffab4936ad65d6032d4fb4cd35bbdc3ee/cachetools-7.1.1-py3-none-any.whl", hash = "sha256:0335cd7a0952d2b22327441fb0628139e234c565559eeb91a8a4ac7551c5353d", size = 16775, upload-time = "2026-05-03T20:00:27.857Z" }, + { url = "https://files.pythonhosted.org/packages/8c/7b/1fc1c09cc0756cf25861a3be10565915953876da48bb228fb9a672b20a42/cachetools-7.1.4-py3-none-any.whl", hash = "sha256:323dc4127934744db5b54eb4924482d7edafbf9554e820d1531c2e08c0e4ef54", size = 16761, upload-time = "2026-05-21T22:40:41.845Z" }, ] [[package]] @@ -869,11 +835,11 @@ wheels = [ [[package]] name = "certifi" -version = "2026.4.22" +version = "2026.5.20" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" }, + { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, ] [[package]] @@ -1085,14 +1051,14 @@ wheels = [ [[package]] name = "clr-loader" -version = "0.2.10" +version = "0.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/18/24/c12faf3f61614b3131b5c98d3bf0d376b49c7feaa73edca559aeb2aee080/clr_loader-0.2.10.tar.gz", hash = "sha256:81f114afbc5005bafc5efe5af1341d400e22137e275b042a8979f3feb9fc9446", size = 83605, upload-time = "2026-01-03T23:13:06.984Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/46/7eea92b6aa2d68af78e049cbecec5f757f1aad44ecdecdc16bbad7eead51/clr_loader-0.3.1.tar.gz", hash = "sha256:2e073e9aaf49d1ae2f56ecba27987ad5fb68be4bcd9dd34a5bed8f0e4e128366", size = 86805, upload-time = "2026-04-18T17:49:44.287Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/61/cf819f8e8bb4d4c74661acf2498ba8d4a296714be3478d21eaabf64f5b9b/clr_loader-0.2.10-py3-none-any.whl", hash = "sha256:ebbbf9d511a7fe95fa28a95a4e04cd195b097881dfe66158dc2c281d3536f282", size = 56483, upload-time = "2026-01-03T23:13:05.439Z" }, + { url = "https://files.pythonhosted.org/packages/5e/da/ec1a6e36624000b6df0dd61183c42342ee5814c073315e802cadaad04d2f/clr_loader-0.3.1-py3-none-any.whl", hash = "sha256:cbad189de20d202a7d621956b0fc38049e13c9bf7ca2923441eff725cd121aa1", size = 55730, upload-time = "2026-04-18T17:49:42.99Z" }, ] [[package]] @@ -1526,19 +1492,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a2/ca/7e8365deec19afb2b2c7be7c1c0aa8f99633b54e90c570999acda93260fc/cryptography-48.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:db63bf618e5dea46c07de12e900fe1cdd2541e6dc9dbae772a70b7d4d4765f6a", size = 3739536, upload-time = "2026-05-04T22:59:29.61Z" }, ] -[[package]] -name = "cssutils" -version = "2.15.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "encutils" }, - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/01/00/7e89107ea389e952eea73b1b90ac6633e15a519c4a518ee90bb93a2f83dc/cssutils-2.15.0.tar.gz", hash = "sha256:e9739237f3915037dacba787c4b58f280e3ec5d9864953e185bf23d40ff7d021", size = 716080, upload-time = "2026-04-27T20:40:35.615Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/bf/5669e08a6f53e0d4b4648a989e77163ad36d4718195319c8c5af08ded654/cssutils-2.15.0-py3-none-any.whl", hash = "sha256:207faa466810a1aef109261673f2458356d0839ddedaebc0ee553376290fb6a9", size = 180638, upload-time = "2026-04-27T20:40:34.178Z" }, -] - [[package]] name = "cycler" version = "0.12.1" @@ -1601,7 +1554,7 @@ validation = [ [[package]] name = "cyclopts" -version = "4.11.2" +version = "4.16.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -1609,22 +1562,22 @@ dependencies = [ { name = "rich" }, { name = "rich-rst" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e4/f7/3ee212c1bc314551094fc8fda7b4b63c647ac5c32d06daa285d04d33edfc/cyclopts-4.11.2.tar.gz", hash = "sha256:8c9b77921660fa1ee52c150e2217ced672323efb3434e9b338077de1bc551ff4", size = 175935, upload-time = "2026-05-04T00:11:57.857Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/07/bf61d13de86d96a4c46aff00c9ca0eced44bcc8c3e16280605c1253e5720/cyclopts-4.16.1.tar.gz", hash = "sha256:8aa47bf92a5fb33abca5af05e576eecdb0d2f79893ad29238046df78370fc4a8", size = 181196, upload-time = "2026-05-25T15:29:08.518Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/23/18/4cedda786e7da429e7489549a9e5461530d4133130e541f25fb94f015776/cyclopts-4.11.2-py3-none-any.whl", hash = "sha256:838020120b939549ff7c8423aca29c86764b5dd1d8a5d7f3753a6327861f537b", size = 213537, upload-time = "2026-05-04T00:11:56.103Z" }, + { url = "https://files.pythonhosted.org/packages/00/8d/7f362c2fb8ef4decd2160bc24d4292c6ca658cc6d9a161b89ca5122bbdbf/cyclopts-4.16.1-py3-none-any.whl", hash = "sha256:617795392c4113a2c2cc7af716f20244900e87f23daa05442d1268d81472a592", size = 219020, upload-time = "2026-05-25T15:29:09.646Z" }, ] [[package]] name = "dataproperty" -version = "1.1.0" +version = "1.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mbstrdecoder" }, { name = "typepy", extra = ["datetime"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0b/81/8c8b64ae873cb9014815214c07b63b12e3b18835780fb342223cfe3fe7d8/dataproperty-1.1.0.tar.gz", hash = "sha256:b038437a4097d1a1c497695c3586ea34bea67fdd35372b9a50f30bf044d77d04", size = 42574, upload-time = "2024-12-31T14:37:26.033Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/6f/a801320bb388d965be9c370ec753cc33120e6cbe0069fa05644f05821975/dataproperty-1.1.1.tar.gz", hash = "sha256:a83af82a234edda5378a36fb092bc90dd554646c5e58202a310acf468ae81bc8", size = 42954, upload-time = "2026-05-09T10:33:42.212Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/21/c2/e12e95e289e6081a40454199ab213139ef16a528c7c86432de545b05a23a/DataProperty-1.1.0-py3-none-any.whl", hash = "sha256:c61fcb2e2deca35e6d1eb1f251a7f22f0dcde63e80e61f0cc18c19f42abfd25b", size = 27581, upload-time = "2024-12-31T14:37:22.657Z" }, + { url = "https://files.pythonhosted.org/packages/03/41/eab7fe313820578b341a2a1d6aeeedd2c38ec1e3f3d51e57e2735b5beac0/dataproperty-1.1.1-py3-none-any.whl", hash = "sha256:cf026aa002dbd6c57c619ec6741ffd61ae7bf2f20481951d8af2dff44480340e", size = 27691, upload-time = "2026-05-09T10:33:40.468Z" }, ] [[package]] @@ -1654,11 +1607,11 @@ wheels = [ [[package]] name = "decorator" -version = "5.2.1" +version = "5.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/60/8b/32f9823da46cde7df2087faa08cd98d01b908f8dcab982cdba9c84e85355/decorator-5.3.1.tar.gz", hash = "sha256:4cbcdd55a6efadb9dbea26b858f4fb3264567b52d69ca0d25b721b553f60ea82", size = 58084, upload-time = "2026-05-18T06:03:28.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, + { url = "https://files.pythonhosted.org/packages/05/7f/798705f5296a58ca505d600456748d1be48078eac8a7050d8a98bc9edb89/decorator-5.3.1-py3-none-any.whl", hash = "sha256:f47fe6fdbd2edd623ecfe36875d37aba411624e2670dd395dddae1358689bb3c", size = 10365, upload-time = "2026-05-18T06:03:26.517Z" }, ] [[package]] @@ -1682,6 +1635,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/99/c7/d1ec24fb280caa5a79b6b950db565dab30210a66259d17d5bb2b3a9f878d/dependency_groups-1.3.1-py3-none-any.whl", hash = "sha256:51aeaa0dfad72430fcfb7bcdbefbd75f3792e5919563077f30bc0d73f4493030", size = 8664, upload-time = "2025-05-02T00:34:27.085Z" }, ] +[[package]] +name = "detect-installer" +version = "0.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/ce/6897d812825e9d4c53e3c7112726e800cc5231b013b2223bf64f653ff362/detect_installer-0.1.0.tar.gz", hash = "sha256:00ad7ba0a36e3cf7d08a40d3643011746dbc112597c7d475cc91c416710ca4e7", size = 3049, upload-time = "2026-02-23T10:40:22.567Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/34/8cc73273414405086c58852916e4031812a6a30fe04c057e37ad99397b7f/detect_installer-0.1.0-py3-none-any.whl", hash = "sha256:034fb20fd665c36e6ba52b8821525ea07fb4f7f938cac459df889fb33801528a", size = 4539, upload-time = "2026-02-23T10:40:23.807Z" }, +] + [[package]] name = "detect-secrets" version = "1.5.0" @@ -1732,15 +1694,15 @@ gcp = [ [[package]] name = "dict2css" -version = "0.3.0.post1" +version = "0.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cssutils" }, { name = "domdf-python-tools" }, + { name = "tinycss2" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/24/eb/776eef1f1aa0188c0fc165c3a60b71027539f71f2eedc43ad21b060e9c39/dict2css-0.3.0.post1.tar.gz", hash = "sha256:89c544c21c4ca7472c3fffb9d37d3d926f606329afdb751dc1de67a411b70719", size = 7845, upload-time = "2023-11-22T11:09:20.958Z" } +sdist = { url = "https://files.pythonhosted.org/packages/83/ae/242596e550f79aa85ab6b5310caadd0b592063dc0c20c397d25707981f65/dict2css-0.6.0.tar.gz", hash = "sha256:143e55cb71c98a88c79f2c41e08a5fa4d875659275756f794e31ccd69936ce88", size = 9268, upload-time = "2026-05-21T08:34:29.598Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/47/290daabcf91628f4fc0e17c75a1690b354ba067066cd14407712600e609f/dict2css-0.3.0.post1-py3-none-any.whl", hash = "sha256:f006a6b774c3e31869015122ae82c491fd25e7de4a75607a62aa3e798f837e0d", size = 25647, upload-time = "2023-11-22T11:09:19.221Z" }, + { url = "https://files.pythonhosted.org/packages/f7/68/0fbc6124cdd4f5a92599d18345bd24c67977988d0bb277f3ea284321d836/dict2css-0.6.0-py3-none-any.whl", hash = "sha256:5251f1df1c78ffdf09313657a7f88add0ad219127d9aeb18fb343b052d6bfbbe", size = 11874, upload-time = "2026-05-21T08:34:28.548Z" }, ] [[package]] @@ -1794,38 +1756,38 @@ wheels = [ [[package]] name = "duckdb" -version = "1.5.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0c/66/744b4931b799a42f8cb9bc7a6f169e7b8e51195b62b246db407fd90bf15f/duckdb-1.5.2.tar.gz", hash = "sha256:638da0d5102b6cb6f7d47f83d0600708ac1d3cb46c5e9aaabc845f9ba4d69246", size = 18017166, upload-time = "2026-04-13T11:30:09.065Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/b0/d13e7e396d86c245290b3e93f692a2d27c2fe99f857aaf9205003c00c978/duckdb-1.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7f69164b048e498b9e9140a24343108a5ae5f17bfb3485185f55fdf9b1aa924d", size = 30020978, upload-time = "2026-04-13T11:28:52.486Z" }, - { url = "https://files.pythonhosted.org/packages/70/7b/ae1ec7f516394aa55501d1949af1f731be8d9d7433f0acc3f4632a0ba484/duckdb-1.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:81fc4fbf0b5e25840b39ba2a10b78c6953c0314d5d0434191e7898f34ab1bba3", size = 15947821, upload-time = "2026-04-13T11:28:55.981Z" }, - { url = "https://files.pythonhosted.org/packages/8a/a5/cae0105e01a85f85ead61723bb42dab14c2f8ec49f91e67a2372c02574a4/duckdb-1.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56d38b3c4e0ef2abb58898d0fd423933999ed535c45e75e9d9f72e1d5fed69b8", size = 14201656, upload-time = "2026-04-13T11:28:58.316Z" }, - { url = "https://files.pythonhosted.org/packages/50/db/46c57e8813ac33762bddc9545610ed648751c5b6a379abf2dc6035505ce4/duckdb-1.5.2-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:376856066c65ccd55fcb3a380bbe33a71ce089fc4623d229ffc6e82251afdb6d", size = 19285181, upload-time = "2026-04-13T11:29:01.041Z" }, - { url = "https://files.pythonhosted.org/packages/dc/a2/67694010693ec8c8c975e6991f48ef886d35ecbdaa2f287234882a403c21/duckdb-1.5.2-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c69907354ffee94ba8cf782daf0480dab7557f21ce27fffa6c0ea8f74ed4b8e2", size = 21394852, upload-time = "2026-04-13T11:29:03.814Z" }, - { url = "https://files.pythonhosted.org/packages/52/9f/2b1618c5a93949a70dcf105293db7e27bb2b2cc4aeb1ff46b806f430ec81/duckdb-1.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:d9b4f5430bf4f05d4c0dc4c55c75def3a5af4be0343be20fa2bfc577343fbfc9", size = 13095526, upload-time = "2026-04-13T11:29:06.265Z" }, - { url = "https://files.pythonhosted.org/packages/b8/e9/cb39e0d94a32f5333e819112fd01439a31f541f9c56a31b66f9bd209704b/duckdb-1.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:2323c1195c10fb2bb982fc0218c730b43d1b92a355d61e68e3c5f3ac9d44c34f", size = 13946215, upload-time = "2026-04-13T11:29:08.672Z" }, - { url = "https://files.pythonhosted.org/packages/41/de/ebe66bbe78125fc610f4fd415447a65349d94245950f3b3dfb31d028af02/duckdb-1.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e6495b00cad16888384119842797c49316a96ae1cb132bb03856d980d95afee1", size = 30064950, upload-time = "2026-04-13T11:29:11.468Z" }, - { url = "https://files.pythonhosted.org/packages/2d/8a/3e25b5d03bcf1fb99d189912f8ce92b1db4f9c8778e1b1f55745973a855a/duckdb-1.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d72b8856b1839d35648f38301b058f6232f4d36b463fe4dc8f4d3fdff2df1a2e", size = 15969113, upload-time = "2026-04-13T11:29:14.139Z" }, - { url = "https://files.pythonhosted.org/packages/19/bb/58001f0815002b1a93431bf907f77854085c7d049b83d521814a07b9db0b/duckdb-1.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2a1de4f4d454b8c97aec546c82003fc834d3422ce4bc6a19902f3462ef293bed", size = 14224774, upload-time = "2026-04-13T11:29:16.758Z" }, - { url = "https://files.pythonhosted.org/packages/d3/2f/a7f0de9509d1cef35608aeb382919041cdd70f58c173865c3da6a0d87979/duckdb-1.5.2-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce0b8141a10d37ecef729c45bc41d334854013f4389f1488bd6035c5579aaac1", size = 19313510, upload-time = "2026-04-13T11:29:19.574Z" }, - { url = "https://files.pythonhosted.org/packages/26/78/eb1e064ea8b9df3b87b167bfd7a407b2f615a4291e06cba756727adfa06c/duckdb-1.5.2-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99ef73a277c8921bc0a1f16dee38d924484251d9cfd20951748c20fcd5ed855", size = 21429692, upload-time = "2026-04-13T11:29:22.575Z" }, - { url = "https://files.pythonhosted.org/packages/5b/12/05b0c47d14839925c5e35b79081d918ca82e3f236bb724a6f58409dd5291/duckdb-1.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:8d599758b4e48bf12e18c9b960cf491d219f0c4972d19a45489c05cc5ab36f83", size = 13107594, upload-time = "2026-04-13T11:29:25.43Z" }, - { url = "https://files.pythonhosted.org/packages/0b/2c/80558a82b236e044330e84a154b96aacddb343316b479f3d49be03ea11cb/duckdb-1.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:fc85a5dbcbe6eccac1113c72370d1d3aacfdd49198d63950bdf7d8638a307f00", size = 13927537, upload-time = "2026-04-13T11:29:27.842Z" }, - { url = "https://files.pythonhosted.org/packages/98/f2/e3d742808f138d374be4bb516fade3d1f33749b813650810ab7885cdc363/duckdb-1.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:4420b3f47027a7849d0e1815532007f377fa95ee5810b47ea717d35525c12f79", size = 30064879, upload-time = "2026-04-13T11:29:30.763Z" }, - { url = "https://files.pythonhosted.org/packages/72/0d/f3dc1cf97e1267ca15e4307d456f96ce583961f0703fd75e62b2ad8d64fa/duckdb-1.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bb42e6ed543902e14eae647850da24103a89f0bc2587dec5601b1c1f213bd2ed", size = 15969327, upload-time = "2026-04-13T11:29:33.481Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e0/d5418def53ae4e05a63075705ff44ed5af5a1a5932627eb2b600c5df1c93/duckdb-1.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:98c0535cd6d901f61a5ea3c2e26a1fd28482953d794deb183daf568e3aa5dda6", size = 14225107, upload-time = "2026-04-13T11:29:35.882Z" }, - { url = "https://files.pythonhosted.org/packages/16/a7/15aaa59dbecc35e9711980fcdbf525b32a52470b32d18ef678193a146213/duckdb-1.5.2-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:486c862bf7f163c0110b6d85b3e5c031d224a671cca468f12ebb1d3a348f6b39", size = 19313433, upload-time = "2026-04-13T11:29:38.367Z" }, - { url = "https://files.pythonhosted.org/packages/bd/21/d903cc63a5140c822b7b62b373a87dc557e60c29b321dfb435061c5e67cf/duckdb-1.5.2-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70631c847ca918ee710ec874241b00cf9d2e5be90762cbb2a0389f17823c08f7", size = 21429837, upload-time = "2026-04-13T11:29:41.135Z" }, - { url = "https://files.pythonhosted.org/packages/e3/0a/b770d1f60c70597302130d6247f418549b7094251a02348fbaf1c7e147ae/duckdb-1.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:52a21823f3fbb52f0f0e5425e20b07391ad882464b955879499b5ff0b45a376b", size = 13107699, upload-time = "2026-04-13T11:29:43.905Z" }, - { url = "https://files.pythonhosted.org/packages/d9/cf/e200fe431d700962d1a908d2ce89f53ccee1cc8db260174ae663ba09686b/duckdb-1.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:411ad438bd4140f189a10e7f515781335962c5d18bd07837dc6d202e3985253d", size = 13927646, upload-time = "2026-04-13T11:29:46.598Z" }, - { url = "https://files.pythonhosted.org/packages/83/a1/f6286c67726cc1ea60a6e3c0d9fbc66527dde24ae089a51bbe298b13ca78/duckdb-1.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:6b0fe75c148000f060aa1a27b293cacc0ea08cc1cad724fbf2143d56070a3785", size = 30078598, upload-time = "2026-04-13T11:29:49.828Z" }, - { url = "https://files.pythonhosted.org/packages/de/6a/59febb02f21a4a5c6b0b0099ef7c965fdd5e61e4904cf813809bb792e35f/duckdb-1.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:35579b8e3a064b5eaf15b0eafc558056a13f79a0a62e34cc4baf57119daecfec", size = 15975120, upload-time = "2026-04-13T11:29:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/09/70/ce750854d37bb5a45cccbb2c3cb04df4af56aea8fc30a2499bb643b4a9c0/duckdb-1.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ea58ff5b0880593a280cf5511734b17711b32ee1f58b47d726e8600848358160", size = 14227762, upload-time = "2026-04-13T11:29:55.564Z" }, - { url = "https://files.pythonhosted.org/packages/28/dc/ad45ac3c0b6c4687dc649e8f6cf01af1c8b0443932a39b2abb4ebcb3babd/duckdb-1.5.2-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef461bca07313412dc09961c4a4757a851f56b95ac01c58fac6007632b7b94f2", size = 19315668, upload-time = "2026-04-13T11:29:58.427Z" }, - { url = "https://files.pythonhosted.org/packages/cc/b1/1464f468d2e5813f5808de95df9d3113a645a5bfa2ffcaecbc542ddae272/duckdb-1.5.2-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be37680ddb380015cb37318e378c53511c45c4f0d8fac5599d22b7d092b9217a", size = 21434056, upload-time = "2026-04-13T11:30:01.238Z" }, - { url = "https://files.pythonhosted.org/packages/ce/32/6673607e024722473fa7aafdd29c0e3dd231dd528f6cd8b5797fbeeb229d/duckdb-1.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:0b291786014df1133f8f18b9df4d004484613146e858d71a21791e0fcca16cf4", size = 13633667, upload-time = "2026-04-13T11:30:04.05Z" }, - { url = "https://files.pythonhosted.org/packages/7a/e3/9d34173ec068631faea3ea6e73050700729363e7e33306a9a3218e5cdc61/duckdb-1.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:c9f3e0b71b8a50fccfb42794899285d9d318ce2503782b9dd54868e5ecd0ad31", size = 14402513, upload-time = "2026-04-13T11:30:06.609Z" }, +version = "1.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/69/00/d579dcb2a536b6ea3a2563cdad6844f77d81a9b2d4b22a858097f2468acf/duckdb-1.5.3.tar.gz", hash = "sha256:df39428eb130faa35ae96fd35245bdeae6ecf43936250b116b5fead568eb9f16", size = 18026640, upload-time = "2026-05-20T11:55:31.901Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/fc/a8a89c6c73f31c2b58c6abbc2f543e0b736042dd5ef7cc1784c24ec31428/duckdb-1.5.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:341a2672e2551ba51c95c1898f0ade983e76675e79038ccb16342c3d6cfb82d7", size = 32583465, upload-time = "2026-05-20T11:54:13.132Z" }, + { url = "https://files.pythonhosted.org/packages/63/f1/3423a2f523dd034e505d4a5dd8e210ae577212e152598dc13b6a5e736e1b/duckdb-1.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c9e8fa408705081160ede7ead238d16e73a36b8561b700f2bf2d650ae48e7b92", size = 17278520, upload-time = "2026-05-20T11:54:16.368Z" }, + { url = "https://files.pythonhosted.org/packages/e1/1a/7bf5ba1b7ea520557e6b2dbee1c85abab016bdac0c1779d9d0ef76c87300/duckdb-1.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70a18f932cf6d87bd0e554613657a515c1443a1724aacfc7ec5137dd28698b03", size = 15424794, upload-time = "2026-05-20T11:54:19.891Z" }, + { url = "https://files.pythonhosted.org/packages/ad/16/ce4b1e386e45fab0268edbf1b85bace20e9437589e9edb2bd5f9a226fa44/duckdb-1.5.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e80eb4d0fb59869cb2c7d7ef494c07fb92014fe8e77d96c170cd1ebc1488a708", size = 19306666, upload-time = "2026-05-20T11:54:22.77Z" }, + { url = "https://files.pythonhosted.org/packages/99/1f/651f8453f26931e8061b7e27b3090f868868185814ecb9216d0bd71ec8ef/duckdb-1.5.3-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3248b49cd835ea322574bc6aac0ae7a83be85547f49d4f5f5777cb380ee6627f", size = 21418306, upload-time = "2026-05-20T11:54:25.616Z" }, + { url = "https://files.pythonhosted.org/packages/bc/64/e1ffebf010b1631a6fef8d1508f46d4eab3e97c18729af986bb796fa8452/duckdb-1.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:f4eff89c12c3a362efa012262e57b7b4ab904a7f79bad9178fe365510077abe8", size = 13101423, upload-time = "2026-05-20T11:54:28.107Z" }, + { url = "https://files.pythonhosted.org/packages/e7/42/b1d4e34f9658cc0e13d7aae581ab82643f50a548d5aee8767f0c587cc3a4/duckdb-1.5.3-cp311-cp311-win_arm64.whl", hash = "sha256:75d13308c9da3ee431d1e72b8ab720aa74a1b3e9159d4124cb62435924496334", size = 13951740, upload-time = "2026-05-20T11:54:30.886Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c4/2e34929b16c8d544ef664fad8f7f3a2a9db05746aae1e7c8c4ee3a8b23e4/duckdb-1.5.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ff11a457258148337ef9a392148a8cdbd1069b6c27c21958816c7b67fe6c542d", size = 32626494, upload-time = "2026-05-20T11:54:33.738Z" }, + { url = "https://files.pythonhosted.org/packages/3a/53/3af681793d03771365ae3e2215331151c196a3ac8193f613344840694671/duckdb-1.5.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fd25f533cb1b6b2c84cc767a9a9bab7769bb1aa44571a2a0bfc91ac3e4a38ac", size = 17301121, upload-time = "2026-05-20T11:54:36.928Z" }, + { url = "https://files.pythonhosted.org/packages/15/e2/c80af1eac2ab5d35fc2c372ef0a84668842e549fbbf7799277b3fccf3e39/duckdb-1.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:10960400ed60cdf0fe05bab2086fa8eb733889cb0ceca18d07ff9a00c0e0be7b", size = 15449283, upload-time = "2026-05-20T11:54:39.777Z" }, + { url = "https://files.pythonhosted.org/packages/2d/9a/c63af233c9f761bf5178a5210437e1bc6bcb30fa8a9073de6398cfb12c03/duckdb-1.5.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5f18e7561403054433706c187589e86629a7af09a7efc23a06a8b308e6acc68", size = 19332762, upload-time = "2026-05-20T11:54:42.51Z" }, + { url = "https://files.pythonhosted.org/packages/21/cc/2d77af4fff86012f334ef82e6d54a995a86c8745e58074f1218ed7d25171/duckdb-1.5.3-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fb7516255a8764545e30f7efacea408cc847764a3027b3b0b3e7d1a7bebbc5c", size = 21453290, upload-time = "2026-05-20T11:54:45.272Z" }, + { url = "https://files.pythonhosted.org/packages/8d/5e/9bc4817a98feb4dab83e56f2245cd3a30d00ee646d4dec7926464e2b3f28/duckdb-1.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:8001eccbc28be244dfd04d708526f34ddd6460b47a8aeb5d0e39d6f7f9e3fe15", size = 13118308, upload-time = "2026-05-20T11:54:48.058Z" }, + { url = "https://files.pythonhosted.org/packages/81/35/e3f32e4e53e2450ddb1db8312a17d1ce455d60cc4941b6ad2cfc908794b0/duckdb-1.5.3-cp312-cp312-win_arm64.whl", hash = "sha256:6d2835e39bb6af73891f73c0f8d4324f98afe00d0b00c6d34b2a582c2256cbb0", size = 13927187, upload-time = "2026-05-20T11:54:50.584Z" }, + { url = "https://files.pythonhosted.org/packages/cc/9c/a528eb09d8be51954c485864bd06753e616939a080cbc3dd4417e8c94a57/duckdb-1.5.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e75a6122c12579a99848517f6f00a4e342aebda3590c30fe9b5cc5f39d5e6afc", size = 32626254, upload-time = "2026-05-20T11:54:53.65Z" }, + { url = "https://files.pythonhosted.org/packages/ec/3c/1534c0a6db347c05eb7d0f6ecfb7aefbe74cbff398e4892a8fd1903a20e8/duckdb-1.5.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fd3963c1cb9d9567777f4a898a9dbe388a2fe9724681801b1e7d6d93eecf1b76", size = 17300917, upload-time = "2026-05-20T11:54:56.628Z" }, + { url = "https://files.pythonhosted.org/packages/23/fa/beafb91e6e152d2161c4a9cbc472334c87607eb61ad7104b5a7fa8d8d7b1/duckdb-1.5.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3d5db8c0b55e072cf437948ebb5d7e23d7b9d03d905fa5f9145583e65aa447f7", size = 15449411, upload-time = "2026-05-20T11:54:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/50/0a/49b6fe04e2fcd63729eb607dadd44818dde77342a4f5ce086c6c92f1dd4d/duckdb-1.5.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ce80aed7a538422129a57eaca9141e3afb51f8bf562b1908b1576c9725b5b22", size = 19333120, upload-time = "2026-05-20T11:55:01.727Z" }, + { url = "https://files.pythonhosted.org/packages/63/4c/0907c3f76adb9dd90e67610b31e0304a35814e65c4c41a354a262c09b885/duckdb-1.5.3-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:787df63824f07bf18022dbc3b8ca4b2bfab0ebe616464f55c6e8cd0f59ea762e", size = 21453266, upload-time = "2026-05-20T11:55:04.5Z" }, + { url = "https://files.pythonhosted.org/packages/6d/9c/d2f23a7803ddbbd9413f7572ecf66a15120ed5ced7ce5c73e698c1406b76/duckdb-1.5.3-cp313-cp313-win_amd64.whl", hash = "sha256:bb5bb5dcdd09d62ee60f0ddbbef918e71cce304ffe28428b1131949d39ffaabf", size = 13118640, upload-time = "2026-05-20T11:55:07.389Z" }, + { url = "https://files.pythonhosted.org/packages/27/d5/7ba2316415bcdab6edd765bbbe35c2ca8a3800f2fe695cd70e3cdb997f09/duckdb-1.5.3-cp313-cp313-win_arm64.whl", hash = "sha256:2fa17ecdd5d3db122836cb71bb93601c2106a3be883c17dffddc02fbf3fa7888", size = 13926409, upload-time = "2026-05-20T11:55:10.166Z" }, + { url = "https://files.pythonhosted.org/packages/a5/c2/d4b6f8a5e4d3bc25773be6da76a99d9661ebbf3552c007c460d2dd59dbf8/duckdb-1.5.3-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:4bfa9a4dadf71e83e2c4eaca2f9421c82a54defecc1b0b4c0be95e2389dec4fe", size = 32636685, upload-time = "2026-05-20T11:55:13.158Z" }, + { url = "https://files.pythonhosted.org/packages/42/58/e835c8298979d29db7a62cb5acc29e9b57aeaca7cdde2fcd3ac980f5cb18/duckdb-1.5.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:aea7baf67ad7e1829ac76f67d7dcbd7fb1f57c3eb179d55ac30952df4709ae30", size = 17308134, upload-time = "2026-05-20T11:55:16.194Z" }, + { url = "https://files.pythonhosted.org/packages/c9/46/617b51363f5613418c8b224b3cce16b58e6dde80904566bec232579c1d4e/duckdb-1.5.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b0b4f088a65d77e1217ce5d7eff889e63fedc44281200d899ff47c84d8ff836", size = 15449891, upload-time = "2026-05-20T11:55:18.687Z" }, + { url = "https://files.pythonhosted.org/packages/b3/72/354146656e8d9ba3853d3a5ee80a481b8c5f70edfc3d5ae80a8c4479c967/duckdb-1.5.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe8d0c1f6a120aa03fa6e0d03897c71a1842e6cf7afd31d181348391f7108fe1", size = 19338499, upload-time = "2026-05-20T11:55:21.34Z" }, + { url = "https://files.pythonhosted.org/packages/56/8f/65fc623b51448f2bfba1a9ec6ab3debb4664c0876c0113a5e782600b53ac/duckdb-1.5.3-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0405eae18ec6e8210a471c97dbfe87a7e4d605274b7fe572a1f276e92158f13", size = 21455828, upload-time = "2026-05-20T11:55:23.847Z" }, + { url = "https://files.pythonhosted.org/packages/2b/db/d0274cbe9f5fe219f77c0bdf900ac77103569e83c102a4225ce04cbc607d/duckdb-1.5.3-cp314-cp314-win_amd64.whl", hash = "sha256:33ae08b3e818d7613d8936744b67718c2062c2f530376895bfd89efb51b81538", size = 13640011, upload-time = "2026-05-20T11:55:26.276Z" }, + { url = "https://files.pythonhosted.org/packages/07/5d/8f1899b8bef291caf953992fcd6c24df9f29387a35645e58c2504a5ca473/duckdb-1.5.3-cp314-cp314-win_arm64.whl", hash = "sha256:746433e49bbc667b4df283153415fbe37e9083e0eff6c3cd6e54de7536869cd4", size = 14411554, upload-time = "2026-05-20T11:55:29.037Z" }, ] [[package]] @@ -1841,18 +1803,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, ] -[[package]] -name = "encutils" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "chardet" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/86/24/15d0f368875e53fb02bf7475e8a8c9aec36dee5a3dcb23efc77f585f9eb6/encutils-1.0.0.tar.gz", hash = "sha256:38eca5af18cebabd8be43c17f14c9d3fbba83cc5f7ac8e3ab1c86e24c4b2b91a", size = 22831, upload-time = "2026-04-19T16:27:19.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/cb/27d1c167d7b6607316c0c4ec5869b256104eb2c9607f76ef2ffa10806d3e/encutils-1.0.0-py3-none-any.whl", hash = "sha256:605297da19a23d1b2da7d3b9bd75513acc979e9facf03aa7ec7ba04b5f567a79", size = 21231, upload-time = "2026-04-19T16:27:18.778Z" }, -] - [[package]] name = "enum-tools" version = "0.13.0" @@ -1898,19 +1848,19 @@ wheels = [ [[package]] name = "faker" -version = "40.15.0" +version = "40.19.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7f/13/6741787bd91c4109c7bed047d68273965cd52ce8a5f773c471b949334b6d/faker-40.15.0.tar.gz", hash = "sha256:20f3a6ec8c266b74d4c554e34118b21c3c2056c0b4a519d15c8decb3a4e6e795", size = 1967447, upload-time = "2026-04-17T20:05:27.555Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/01/28c8ddae8caaf82c929655000963d83e3f01265a9af34e823c2ef2eee8ac/faker-40.19.1.tar.gz", hash = "sha256:76fa71fd3bf320db25e5504eb356f9a76b8a95cd6098524d006f446035b6b89d", size = 1969318, upload-time = "2026-05-22T15:57:37.433Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/a7/a600f8f30d4505e89166de51dd121bd540ab8e560e8cf0901de00a81de8c/faker-40.15.0-py3-none-any.whl", hash = "sha256:71ab3c3370da9d2205ab74ffb0fd51273063ad562b3a3bb69d0026a20923e318", size = 2004447, upload-time = "2026-04-17T20:05:25.437Z" }, + { url = "https://files.pythonhosted.org/packages/49/b4/40a1ec12ec834604f3848143343baf1c67bc9a1096e401907eaa0d25876a/faker-40.19.1-py3-none-any.whl", hash = "sha256:265259b37c013838baaae34940207288170df385d6c5281413fce56a3504d580", size = 2007643, upload-time = "2026-05-22T15:57:35.867Z" }, ] [[package]] name = "fastapi" -version = "0.136.1" +version = "0.136.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -1919,9 +1869,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5d/45/c130091c2dfa061bbfe3150f2a5091ef1adf149f2a8d2ae769ecaf6e99a2/fastapi-0.136.1.tar.gz", hash = "sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f", size = 397448, upload-time = "2026-04-23T16:49:44.046Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/2d/ff8d91d7b564d464629a0fd50a4489c97fcb836ac230bf3a7269232a9b1f/fastapi-0.136.3.tar.gz", hash = "sha256:e487fae93ad408e6f47641ee4dfe389864fd7bec92e547ea8498fc13f43e83ab", size = 396410, upload-time = "2026-05-23T18:53:15.192Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/ff/2e4eca3ade2c22fe1dea7043b8ee9dabe47753349eb1b56a202de8af6349/fastapi-0.136.1-py3-none-any.whl", hash = "sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f", size = 117683, upload-time = "2026-04-23T16:49:42.437Z" }, + { url = "https://files.pythonhosted.org/packages/e0/82/45359b62a067409bd929ae8a56b8ed13e5a8c8a61194b3c236920999ab83/fastapi-0.136.3-py3-none-any.whl", hash = "sha256:3d2a69bdf04b7e9f3afa292c3bc7a98816bbfafa10bc9b45f3f3700d2f761620", size = 117481, upload-time = "2026-05-23T18:53:16.924Z" }, ] [package.optional-dependencies] @@ -1971,9 +1921,10 @@ standard = [ [[package]] name = "fastapi-cloud-cli" -version = "0.17.1" +version = "0.18.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "detect-installer" }, { name = "fastar" }, { name = "httpx" }, { name = "pydantic", extra = ["email"] }, @@ -1983,9 +1934,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/57/cee8e91b83f39e75ae5562a2237261442a8179dcb3b631c7398113157398/fastapi_cloud_cli-0.17.1.tar.gz", hash = "sha256:0baece208fa88063bec46dccb5fb512f3199162092165e57654b44e64adbc44d", size = 47409, upload-time = "2026-04-27T13:38:07.094Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/1d/57221a834b0f62dfa510c2b3db6e9b682cfbc280cef41919a8811ce1ff89/fastapi_cloud_cli-0.18.0.tar.gz", hash = "sha256:95f7a79200e3a90a005e068a4d8ede49d4b04accb095ccd4fd47da998fc28c74", size = 53320, upload-time = "2026-05-22T09:53:54.462Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/a0/e252b68cf155409afabea037ab2971f41509481838847f6503fe890884ea/fastapi_cloud_cli-0.17.1-py3-none-any.whl", hash = "sha256:325e0199bdac7cb86f5df4f4a1d2070054095588088ef7b923a60cec458dcd63", size = 34046, upload-time = "2026-04-27T13:38:08.319Z" }, + { url = "https://files.pythonhosted.org/packages/78/1e/1d54aabf71c003e89e73df92c3dfded311228e68db7cea5db90b3e0ef2b5/fastapi_cloud_cli-0.18.0-py3-none-any.whl", hash = "sha256:1f136fc651b0b6e2f4a9679e23c56e1c3be3405e74469c14ba6e2d5b87fdc113", size = 37087, upload-time = "2026-05-22T09:53:53.001Z" }, ] [[package]] @@ -2100,9 +2051,43 @@ wheels = [ [[package]] name = "fastmcp" -version = "3.2.4" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastmcp-slim", extra = ["client", "server"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/a9/5c5a01b6abd5346bf60b97cfd29e4a86661940c27dd562bfcda07fd03519/fastmcp-3.3.1.tar.gz", hash = "sha256:979362ea557de42a5f40342563c7e4b236bcc8e7cd192715f50030695d1a71cd", size = 28681699, upload-time = "2026-05-15T15:50:39.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/11/6b1bdada6ccfe647d615ae63f9106f8136aec17971e9361546af01c7d38e/fastmcp-3.3.1-py3-none-any.whl", hash = "sha256:862440c5c4d281363a5995eee59d77f0f7cac1f18869038729cecf03b02fc522", size = 7903, upload-time = "2026-05-15T15:50:36.424Z" }, +] + +[[package]] +name = "fastmcp-slim" +version = "3.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "platformdirs" }, + { name = "pydantic", extra = ["email"] }, + { name = "pydantic-settings" }, + { name = "python-dotenv" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/a0/627103e517e1d0d6f1eec633d5662d13e776f01b45ad188e4f5f7478b438/fastmcp_slim-3.3.1.tar.gz", hash = "sha256:0957835fc59452e143ab2f4b7836d2d2df9b2d9958408edc79ba8b56232b2a88", size = 567007, upload-time = "2026-05-15T15:50:10.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/ee/97047f4cc2d7b1d46670d08d8ad01a96e7a748cc01c0b4b351ad8eddbc7a/fastmcp_slim-3.3.1-py3-none-any.whl", hash = "sha256:6cf1c2d77e3adb0d409d6825ed6b0b2a999062973e00b8eea03bd48bf9b4c043", size = 738644, upload-time = "2026-05-15T15:50:08.336Z" }, +] + +[package.optional-dependencies] +client = [ + { name = "authlib" }, + { name = "exceptiongroup" }, + { name = "httpx" }, + { name = "mcp" }, + { name = "opentelemetry-api" }, + { name = "py-key-value-aio", extra = ["filetree", "keyring", "memory"] }, +] +server = [ { name = "authlib" }, { name = "cyclopts" }, { name = "exceptiongroup" }, @@ -2114,22 +2099,15 @@ dependencies = [ { name = "openapi-pydantic" }, { name = "opentelemetry-api" }, { name = "packaging" }, - { name = "platformdirs" }, { name = "py-key-value-aio", extra = ["filetree", "keyring", "memory"] }, - { name = "pydantic", extra = ["email"] }, { name = "pyperclip" }, - { name = "python-dotenv" }, + { name = "python-multipart" }, { name = "pyyaml" }, - { name = "rich" }, { name = "uncalled-for" }, { name = "uvicorn" }, { name = "watchfiles" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9c/13/29544fbc6dfe45ea38046af0067311e0bad7acc7d1f2ad38bb08f2409fe2/fastmcp-3.2.4.tar.gz", hash = "sha256:083ecb75b44a4169e7fc0f632f94b781bdb0ff877c6b35b9877cbb566fd4d4d1", size = 28746127, upload-time = "2026-04-14T01:42:24.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/76/b310d52fa0e30d39bd937eb58ec2c1f1ea1b5f519f0575e9dd9612f01deb/fastmcp-3.2.4-py3-none-any.whl", hash = "sha256:e6c9c429171041455e47ab94bb3f83c4657622a0ec28922f6940053959bd58a9", size = 728599, upload-time = "2026-04-14T01:42:26.85Z" }, -] [[package]] name = "fastparquet" @@ -2201,51 +2179,51 @@ wheels = [ [[package]] name = "fonttools" -version = "4.62.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9a/08/7012b00a9a5874311b639c3920270c36ee0c445b69d9989a85e5c92ebcb0/fonttools-4.62.1.tar.gz", hash = "sha256:e54c75fd6041f1122476776880f7c3c3295ffa31962dc6ebe2543c00dca58b5d", size = 3580737, upload-time = "2026-03-13T13:54:25.52Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/23ff32561ec8d45a4d48578b4d241369d9270dc50926c017570e60893701/fonttools-4.62.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:40975849bac44fb0b9253d77420c6d8b523ac4dcdcefeff6e4d706838a5b80f7", size = 2871039, upload-time = "2026-03-13T13:52:33.127Z" }, - { url = "https://files.pythonhosted.org/packages/24/7f/66d3f8a9338a9b67fe6e1739f47e1cd5cee78bd3bc1206ef9b0b982289a5/fonttools-4.62.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9dde91633f77fa576879a0c76b1d89de373cae751a98ddf0109d54e173b40f14", size = 2416346, upload-time = "2026-03-13T13:52:35.676Z" }, - { url = "https://files.pythonhosted.org/packages/aa/53/5276ceba7bff95da7793a07c5284e1da901cf00341ce5e2f3273056c0cca/fonttools-4.62.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6acb4109f8bee00fec985c8c7afb02299e35e9c94b57287f3ea542f28bd0b0a7", size = 5100897, upload-time = "2026-03-13T13:52:38.102Z" }, - { url = "https://files.pythonhosted.org/packages/cc/a1/40a5c4d8e28b0851d53a8eeeb46fbd73c325a2a9a165f290a5ed90e6c597/fonttools-4.62.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1c5c25671ce8805e0d080e2ffdeca7f1e86778c5cbfbeae86d7f866d8830517b", size = 5071078, upload-time = "2026-03-13T13:52:41.305Z" }, - { url = "https://files.pythonhosted.org/packages/e3/be/d378fca4c65ea1956fee6d90ace6e861776809cbbc5af22388a090c3c092/fonttools-4.62.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a5d8825e1140f04e6c99bb7d37a9e31c172f3bc208afbe02175339e699c710e1", size = 5076908, upload-time = "2026-03-13T13:52:44.122Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d9/ae6a1d0693a4185a84605679c8a1f719a55df87b9c6e8e817bfdd9ef5936/fonttools-4.62.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:268abb1cb221e66c014acc234e872b7870d8b5d4657a83a8f4205094c32d2416", size = 5202275, upload-time = "2026-03-13T13:52:46.591Z" }, - { url = "https://files.pythonhosted.org/packages/54/6c/af95d9c4efb15cabff22642b608342f2bd67137eea6107202d91b5b03184/fonttools-4.62.1-cp311-cp311-win32.whl", hash = "sha256:942b03094d7edbb99bdf1ae7e9090898cad7bf9030b3d21f33d7072dbcb51a53", size = 2293075, upload-time = "2026-03-13T13:52:48.711Z" }, - { url = "https://files.pythonhosted.org/packages/d3/97/bf54c5b3f2be34e1f143e6db838dfdc54f2ffa3e68c738934c82f3b2a08d/fonttools-4.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:e8514f4924375f77084e81467e63238b095abda5107620f49421c368a6017ed2", size = 2344593, upload-time = "2026-03-13T13:52:50.725Z" }, - { url = "https://files.pythonhosted.org/packages/47/d4/dbacced3953544b9a93088cc10ef2b596d348c983d5c67a404fa41ec51ba/fonttools-4.62.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:90365821debbd7db678809c7491ca4acd1e0779b9624cdc6ddaf1f31992bf974", size = 2870219, upload-time = "2026-03-13T13:52:53.664Z" }, - { url = "https://files.pythonhosted.org/packages/66/9e/a769c8e99b81e5a87ab7e5e7236684de4e96246aae17274e5347d11ebd78/fonttools-4.62.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12859ff0b47dd20f110804c3e0d0970f7b832f561630cd879969011541a464a9", size = 2414891, upload-time = "2026-03-13T13:52:56.493Z" }, - { url = "https://files.pythonhosted.org/packages/69/64/f19a9e3911968c37e1e620e14dfc5778299e1474f72f4e57c5ec771d9489/fonttools-4.62.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c125ffa00c3d9003cdaaf7f2c79e6e535628093e14b5de1dccb08859b680936", size = 5033197, upload-time = "2026-03-13T13:52:59.179Z" }, - { url = "https://files.pythonhosted.org/packages/9b/8a/99c8b3c3888c5c474c08dbfd7c8899786de9604b727fcefb055b42c84bba/fonttools-4.62.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:149f7d84afca659d1a97e39a4778794a2f83bf344c5ee5134e09995086cc2392", size = 4988768, upload-time = "2026-03-13T13:53:02.761Z" }, - { url = "https://files.pythonhosted.org/packages/d1/c6/0f904540d3e6ab463c1243a0d803504826a11604c72dd58c2949796a1762/fonttools-4.62.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0aa72c43a601cfa9273bb1ae0518f1acadc01ee181a6fc60cd758d7fdadffc04", size = 4971512, upload-time = "2026-03-13T13:53:05.678Z" }, - { url = "https://files.pythonhosted.org/packages/29/0b/5cbef6588dc9bd6b5c9ad6a4d5a8ca384d0cea089da31711bbeb4f9654a6/fonttools-4.62.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:19177c8d96c7c36359266e571c5173bcee9157b59cfc8cb0153c5673dc5a3a7d", size = 5122723, upload-time = "2026-03-13T13:53:08.662Z" }, - { url = "https://files.pythonhosted.org/packages/4a/47/b3a5342d381595ef439adec67848bed561ab7fdb1019fa522e82101b7d9c/fonttools-4.62.1-cp312-cp312-win32.whl", hash = "sha256:a24decd24d60744ee8b4679d38e88b8303d86772053afc29b19d23bb8207803c", size = 2281278, upload-time = "2026-03-13T13:53:10.998Z" }, - { url = "https://files.pythonhosted.org/packages/28/b1/0c2ab56a16f409c6c8a68816e6af707827ad5d629634691ff60a52879792/fonttools-4.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:9e7863e10b3de72376280b515d35b14f5eeed639d1aa7824f4cf06779ec65e42", size = 2331414, upload-time = "2026-03-13T13:53:13.992Z" }, - { url = "https://files.pythonhosted.org/packages/3b/56/6f389de21c49555553d6a5aeed5ac9767631497ac836c4f076273d15bd72/fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c22b1014017111c401469e3acc5433e6acf6ebcc6aa9efb538a533c800971c79", size = 2865155, upload-time = "2026-03-13T13:53:16.132Z" }, - { url = "https://files.pythonhosted.org/packages/03/c5/0e3966edd5ec668d41dfe418787726752bc07e2f5fd8c8f208615e61fa89/fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68959f5fc58ed4599b44aad161c2837477d7f35f5f79402d97439974faebfebe", size = 2412802, upload-time = "2026-03-13T13:53:18.878Z" }, - { url = "https://files.pythonhosted.org/packages/52/94/e6ac4b44026de7786fe46e3bfa0c87e51d5d70a841054065d49cd62bb909/fonttools-4.62.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef46db46c9447103b8f3ff91e8ba009d5fe181b1920a83757a5762551e32bb68", size = 5013926, upload-time = "2026-03-13T13:53:21.379Z" }, - { url = "https://files.pythonhosted.org/packages/e2/98/8b1e801939839d405f1f122e7d175cebe9aeb4e114f95bfc45e3152af9a7/fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6706d1cb1d5e6251a97ad3c1b9347505c5615c112e66047abbef0f8545fa30d1", size = 4964575, upload-time = "2026-03-13T13:53:23.857Z" }, - { url = "https://files.pythonhosted.org/packages/46/76/7d051671e938b1881670528fec69cc4044315edd71a229c7fd712eaa5119/fonttools-4.62.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e7abd2b1e11736f58c1de27819e1955a53267c21732e78243fa2fa2e5c1e069", size = 4953693, upload-time = "2026-03-13T13:53:26.569Z" }, - { url = "https://files.pythonhosted.org/packages/1f/ae/b41f8628ec0be3c1b934fc12b84f4576a5c646119db4d3bdd76a217c90b5/fonttools-4.62.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:403d28ce06ebfc547fbcb0cb8b7f7cc2f7a2d3e1a67ba9a34b14632df9e080f9", size = 5094920, upload-time = "2026-03-13T13:53:29.329Z" }, - { url = "https://files.pythonhosted.org/packages/f2/f6/53a1e9469331a23dcc400970a27a4caa3d9f6edbf5baab0260285238b884/fonttools-4.62.1-cp313-cp313-win32.whl", hash = "sha256:93c316e0f5301b2adbe6a5f658634307c096fd5aae60a5b3412e4f3e1728ab24", size = 2279928, upload-time = "2026-03-13T13:53:32.352Z" }, - { url = "https://files.pythonhosted.org/packages/38/60/35186529de1db3c01f5ad625bde07c1f576305eab6d86bbda4c58445f721/fonttools-4.62.1-cp313-cp313-win_amd64.whl", hash = "sha256:7aa21ff53e28a9c2157acbc44e5b401149d3c9178107130e82d74ceb500e5056", size = 2330514, upload-time = "2026-03-13T13:53:34.991Z" }, - { url = "https://files.pythonhosted.org/packages/36/f0/2888cdac391807d68d90dcb16ef858ddc1b5309bfc6966195a459dd326e2/fonttools-4.62.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fa1d16210b6b10a826d71bed68dd9ec24a9e218d5a5e2797f37c573e7ec215ca", size = 2864442, upload-time = "2026-03-13T13:53:37.509Z" }, - { url = "https://files.pythonhosted.org/packages/4b/b2/e521803081f8dc35990816b82da6360fa668a21b44da4b53fc9e77efcd62/fonttools-4.62.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:aa69d10ed420d8121118e628ad47d86e4caa79ba37f968597b958f6cceab7eca", size = 2410901, upload-time = "2026-03-13T13:53:40.55Z" }, - { url = "https://files.pythonhosted.org/packages/00/a4/8c3511ff06e53110039358dbbdc1a65d72157a054638387aa2ada300a8b8/fonttools-4.62.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd13b7999d59c5eb1c2b442eb2d0c427cb517a0b7a1f5798fc5c9e003f5ff782", size = 4999608, upload-time = "2026-03-13T13:53:42.798Z" }, - { url = "https://files.pythonhosted.org/packages/28/63/cd0c3b26afe60995a5295f37c246a93d454023726c3261cfbb3559969bb9/fonttools-4.62.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8d337fdd49a79b0d51c4da87bc38169d21c3abbf0c1aa9367eff5c6656fb6dae", size = 4912726, upload-time = "2026-03-13T13:53:45.405Z" }, - { url = "https://files.pythonhosted.org/packages/70/b9/ac677cb07c24c685cf34f64e140617d58789d67a3dd524164b63648c6114/fonttools-4.62.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d241cdc4a67b5431c6d7f115fdf63335222414995e3a1df1a41e1182acd4bcc7", size = 4951422, upload-time = "2026-03-13T13:53:48.326Z" }, - { url = "https://files.pythonhosted.org/packages/e6/10/11c08419a14b85b7ca9a9faca321accccc8842dd9e0b1c8a72908de05945/fonttools-4.62.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c05557a78f8fa514da0f869556eeda40887a8abc77c76ee3f74cf241778afd5a", size = 5060979, upload-time = "2026-03-13T13:53:51.366Z" }, - { url = "https://files.pythonhosted.org/packages/4e/3c/12eea4a4cf054e7ab058ed5ceada43b46809fce2bf319017c4d63ae55bb4/fonttools-4.62.1-cp314-cp314-win32.whl", hash = "sha256:49a445d2f544ce4a69338694cad575ba97b9a75fff02720da0882d1a73f12800", size = 2283733, upload-time = "2026-03-13T13:53:53.606Z" }, - { url = "https://files.pythonhosted.org/packages/6b/67/74b070029043186b5dd13462c958cb7c7f811be0d2e634309d9a1ffb1505/fonttools-4.62.1-cp314-cp314-win_amd64.whl", hash = "sha256:1eecc128c86c552fb963fe846ca4e011b1be053728f798185a1687502f6d398e", size = 2335663, upload-time = "2026-03-13T13:53:56.23Z" }, - { url = "https://files.pythonhosted.org/packages/42/c5/4d2ed3ca6e33617fc5624467da353337f06e7f637707478903c785bd8e20/fonttools-4.62.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1596aeaddf7f78e21e68293c011316a25267b3effdaccaf4d59bc9159d681b82", size = 2947288, upload-time = "2026-03-13T13:53:59.397Z" }, - { url = "https://files.pythonhosted.org/packages/1f/e9/7ab11ddfda48ed0f89b13380e5595ba572619c27077be0b2c447a63ff351/fonttools-4.62.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8f8fca95d3bb3208f59626a4b0ea6e526ee51f5a8ad5d91821c165903e8d9260", size = 2449023, upload-time = "2026-03-13T13:54:01.642Z" }, - { url = "https://files.pythonhosted.org/packages/b2/10/a800fa090b5e8819942e54e19b55fc7c21fe14a08757c3aa3ca8db358939/fonttools-4.62.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee91628c08e76f77b533d65feb3fbe6d9dad699f95be51cf0d022db94089cdc4", size = 5137599, upload-time = "2026-03-13T13:54:04.495Z" }, - { url = "https://files.pythonhosted.org/packages/37/dc/8ccd45033fffd74deb6912fa1ca524643f584b94c87a16036855b498a1ed/fonttools-4.62.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f37df1cac61d906e7b836abe356bc2f34c99d4477467755c216b72aa3dc748b", size = 4920933, upload-time = "2026-03-13T13:54:07.557Z" }, - { url = "https://files.pythonhosted.org/packages/99/eb/e618adefb839598d25ac8136cd577925d6c513dc0d931d93b8af956210f0/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:92bb00a947e666169c99b43753c4305fc95a890a60ef3aeb2a6963e07902cc87", size = 5016232, upload-time = "2026-03-13T13:54:10.611Z" }, - { url = "https://files.pythonhosted.org/packages/d9/5f/9b5c9bfaa8ec82def8d8168c4f13615990d6ce5996fe52bd49bfb5e05134/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bdfe592802ef939a0e33106ea4a318eeb17822c7ee168c290273cbd5fabd746c", size = 5042987, upload-time = "2026-03-13T13:54:13.569Z" }, - { url = "https://files.pythonhosted.org/packages/90/aa/dfbbe24c6a6afc5c203d90cc0343e24bcbb09e76d67c4d6eef8c2558d7ba/fonttools-4.62.1-cp314-cp314t-win32.whl", hash = "sha256:b820fcb92d4655513d8402d5b219f94481c4443d825b4372c75a2072aa4b357a", size = 2348021, upload-time = "2026-03-13T13:54:16.98Z" }, - { url = "https://files.pythonhosted.org/packages/13/6f/ae9c4e4dd417948407b680855c2c7790efb52add6009aaecff1e3bc50e8e/fonttools-4.62.1-cp314-cp314t-win_amd64.whl", hash = "sha256:59b372b4f0e113d3746b88985f1c796e7bf830dd54b28374cd85c2b8acd7583e", size = 2414147, upload-time = "2026-03-13T13:54:19.416Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ba/56147c165442cc5ba7e82ecf301c9a68353cede498185869e6e02b4c264f/fonttools-4.62.1-py3-none-any.whl", hash = "sha256:7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd", size = 1152647, upload-time = "2026-03-13T13:54:22.735Z" }, +version = "4.63.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/84/69/c97f2c18e0db87d2c7b15da1974dace76ae938f1cfa22e2727a648b7ed43/fonttools-4.63.0.tar.gz", hash = "sha256:caeb583deeb5168e694b65cda8b4ee62abedfa66cf88488734466f2366b9c4e0", size = 3597189, upload-time = "2026-05-14T12:04:30.958Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/2b/a7f1545bdf5da69c4bda0cea2a5781f0ad2a6623e0277267672db43c5fe6/fonttools-4.63.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b8ae05d9eacf6081414d759c0a352769ac28ce31280d6bb8e77b03f9e3c449f", size = 2881793, upload-time = "2026-05-14T12:02:56.645Z" }, + { url = "https://files.pythonhosted.org/packages/49/50/965308c703f085f225db2886813b27e015b8b3438c350b22dd65b52c2a2c/fonttools-4.63.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79cdc9f567aec74a72918fd060283911406750cbc9fd28c1316023deb6ce31a9", size = 2428130, upload-time = "2026-05-14T12:02:58.891Z" }, + { url = "https://files.pythonhosted.org/packages/d8/38/6937fbd7f2dc3a6b48725851bc2c15ec949b9af14d9bbcb5fe83cdf9bdf9/fonttools-4.63.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c14b4fd138c4bafcca294765c547914e1aa431ae1ca94ab99d8db08c958bd3b", size = 5111952, upload-time = "2026-05-14T12:03:01.263Z" }, + { url = "https://files.pythonhosted.org/packages/0b/43/a81f20050a3115b57d62c8e781446949512eac36690dc384ccea65ff4cc1/fonttools-4.63.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76ac49f929aecaf82d83250b8347e099d7aecba0f4726c1d9b6df3b8bb5fe18", size = 5082308, upload-time = "2026-05-14T12:03:03.211Z" }, + { url = "https://files.pythonhosted.org/packages/67/00/cdd9d4944ca6ae280d01e69cc37bde3bf663630b837a6fc6d2cd65d80e0e/fonttools-4.63.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dcf076a4474fe0d7367e5bbf5b052c7284fa1feca729c04176ce513521afd8a0", size = 5087932, upload-time = "2026-05-14T12:03:05.147Z" }, + { url = "https://files.pythonhosted.org/packages/f5/f1/0aa0dbea778c75adbef223c42019fd47d22262b905974d62d829545d485f/fonttools-4.63.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7dd683fef0663e9f0f45cf541d788d24caa3ec9db50796b588e1757d8b3bc007", size = 5213271, upload-time = "2026-05-14T12:03:07.238Z" }, + { url = "https://files.pythonhosted.org/packages/a8/99/253e4056e1f0e67b9390125a154b73b5eb73ad521bece95c004858fdeec2/fonttools-4.63.0-cp311-cp311-win32.whl", hash = "sha256:afefc1ed0a59785a7fb06ea7e1678e849c193e1e387db783579bc7b3056fcfcb", size = 2304473, upload-time = "2026-05-14T12:03:09.271Z" }, + { url = "https://files.pythonhosted.org/packages/08/60/defa5e69641db890a63be281f41345f4c33b157824eaf0b9fad3e08b0dcb/fonttools-4.63.0-cp311-cp311-win_amd64.whl", hash = "sha256:063e08bd17bd5a90127a14123de0d6a952dbc847695fd98b63c043d58057f90c", size = 2356389, upload-time = "2026-05-14T12:03:11.53Z" }, + { url = "https://files.pythonhosted.org/packages/08/ef/b3c6b9b5be2f82416d73fe2ed2e96e2793cd80e7510bd6a17ca79cdd88ec/fonttools-4.63.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:37dd23e621e3b0aef1baa70a303b80aaf38449632cfc8fd2a55fb285bbccfc02", size = 2881131, upload-time = "2026-05-14T12:03:13.386Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/c815bea63117fa63e4e1c01f8a1110d2112fa003f838e6467094ec2432ce/fonttools-4.63.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a9faff9e0c1f76f9fd55899d2ce785832efebab37eb8ae13995853aef178bef0", size = 2426704, upload-time = "2026-05-14T12:03:15.801Z" }, + { url = "https://files.pythonhosted.org/packages/44/04/0b91d8e916e92ad1fac9e4624760baf0fd5ff2ead614c2f68fb21373f03f/fonttools-4.63.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef3048ef05dbb552b89817713d9cac912e00d0fde4a3105c00d29e52e10c89af", size = 5044298, upload-time = "2026-05-14T12:03:18.085Z" }, + { url = "https://files.pythonhosted.org/packages/77/c7/2342da9830e3e9d4870305ca5d2091d2a83284f2953079b7bdd3b5e029d8/fonttools-4.63.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58dc6bb86a78d782f00f9190ca02c119cf5bbe2807536e361e18d42019f877d8", size = 4999800, upload-time = "2026-05-14T12:03:20.161Z" }, + { url = "https://files.pythonhosted.org/packages/e6/6d/67fe16c48d7ce050979b33f47e0d28a318f02da030602e944c34f7a16ef3/fonttools-4.63.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee08ebfa58f6e1aeff5697ab9582105bb620008c1caafb681e4c557e7483027b", size = 4982666, upload-time = "2026-05-14T12:03:22.87Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/3bbab338c07c71fa56269953845e92c951a61457bbbb0f1022551ea266d9/fonttools-4.63.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:27fdc65af8da6f88b9c6121c47a464cbe359fcfff7ff6fc2d37a1f395d755b78", size = 5133598, upload-time = "2026-05-14T12:03:25.168Z" }, + { url = "https://files.pythonhosted.org/packages/62/f2/aa27c7f98db5b064883dadcc5283947e81e034de42e22a33675878d98b54/fonttools-4.63.0-cp312-cp312-win32.whl", hash = "sha256:af2fd1664d00a397d75f806985ddb36282091c2131a73a6485c23b4a34722263", size = 2292575, upload-time = "2026-05-14T12:03:27.496Z" }, + { url = "https://files.pythonhosted.org/packages/87/36/cccb9bc2a6ab63d1b2980374f0dca72ce95ae267c9b4cfe77455bb70d0d4/fonttools-4.63.0-cp312-cp312-win_amd64.whl", hash = "sha256:59ac449f8cca9b4ffa08d2e7bbadad87ce710d69d1eda5c3c1ce579baa987272", size = 2343211, upload-time = "2026-05-14T12:03:30.057Z" }, + { url = "https://files.pythonhosted.org/packages/0f/8d/d8fec3dcde2963f8c908fb315e5ff2cd0ac34f82394bbbf73a2aa5145ce3/fonttools-4.63.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd7e9857e5e63738b9d9fd707bc1f59c8b09e5177726d23664db393c59bb08bd", size = 2876062, upload-time = "2026-05-14T12:03:32.554Z" }, + { url = "https://files.pythonhosted.org/packages/ef/71/d935dc54e4ff121bfdd11e08702db63a7e6f25af21d8a3d7b7212df53641/fonttools-4.63.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c2a2a42198b696a6f48fad91709afb55176e66a5e566131219dba372fb7f8c59", size = 2424594, upload-time = "2026-05-14T12:03:34.86Z" }, + { url = "https://files.pythonhosted.org/packages/8e/40/e76320afa1df918e146155ef239b1719ee266092e96f5423bfd075affba1/fonttools-4.63.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e874792a8212b44583ea02189d9e693906b2f78b261f372f95d6c563210ac1d", size = 5024840, upload-time = "2026-05-14T12:03:36.745Z" }, + { url = "https://files.pythonhosted.org/packages/ce/36/0b805d8c485f872f65a509cbe3b58a5d0d17bee855333b54a150c79d3061/fonttools-4.63.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22135da48a348785c5e2d5d2d9d6bec5ed44adacbaeb9db12d9493bf6c6bfa68", size = 4975801, upload-time = "2026-05-14T12:03:38.833Z" }, + { url = "https://files.pythonhosted.org/packages/c8/26/2cee03d0aa083ab022da5c07aff9ed3f689da1defb81ad6917c9627896da/fonttools-4.63.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ccf41f2efdf56994d22d73bef4ced1052161958169428d06ba9724ea9e9a64be", size = 4965009, upload-time = "2026-05-14T12:03:41.494Z" }, + { url = "https://files.pythonhosted.org/packages/7e/48/cc4b66d9058c0d0982c833fad10127c4b0e9324606aafa41382295ca4102/fonttools-4.63.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9ced0bd02ac751dd6319b0da88aaef24414e3b0dbc32bb4f24944821a3741a27", size = 5105892, upload-time = "2026-05-14T12:03:43.525Z" }, + { url = "https://files.pythonhosted.org/packages/d8/1f/a98a30a814b9ddef3a2e706025f90b9e0bc94890e6cb15254bc86547d11a/fonttools-4.63.0-cp313-cp313-win32.whl", hash = "sha256:85be818f5506e8a7753153def2c9550178f0ecae6a47b5e0e8dbb23f7cc90380", size = 2291313, upload-time = "2026-05-14T12:03:45.594Z" }, + { url = "https://files.pythonhosted.org/packages/92/46/5177b01f3b4abfdd4409f31cca4ab279c9343a26efbe9ec78c97fc612e02/fonttools-4.63.0-cp313-cp313-win_amd64.whl", hash = "sha256:ba04cb5891d4c0c21b6da95eda8d7b090021508a294fff33464fc7d241e0856b", size = 2342299, upload-time = "2026-05-14T12:03:47.414Z" }, + { url = "https://files.pythonhosted.org/packages/27/d2/23d25e3f247b328be58d04a4c9f894178a0d1eda7d42867cfb388adaf416/fonttools-4.63.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fd1e3094f42d806d3d7c79162fc59e5910fcbe3a7360c385b8da969bc4493745", size = 2875338, upload-time = "2026-05-14T12:03:50.052Z" }, + { url = "https://files.pythonhosted.org/packages/cd/58/7dfa0c761cb3b2964e2a84c4dc986c926a87de0cb9fb60d5b28ded3f2914/fonttools-4.63.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6e528da43bc3791085f8cb6141b1d13e459226790240340fcbb4625649238b03", size = 2422661, upload-time = "2026-05-14T12:03:52.154Z" }, + { url = "https://files.pythonhosted.org/packages/dd/87/64cfa18a7a1621d17b7f4502b2b0ed8a135a90c3db51ea590ee99043e76b/fonttools-4.63.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b2248c5decb223562f7902ff6325077a073f608ee8e33e88ad88db734eb9f49", size = 5010526, upload-time = "2026-05-14T12:03:54.647Z" }, + { url = "https://files.pythonhosted.org/packages/36/e1/a8933a72c45a87177fbde2696e0d0755c8c9062f8c077a961c6215fa27b1/fonttools-4.63.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:308f957cdeaf8abe4e5f2f124902ef405448af92c90f80e302a3b771c2e6116b", size = 4923946, upload-time = "2026-05-14T12:03:56.984Z" }, + { url = "https://files.pythonhosted.org/packages/27/60/872e6e233b8c5e8b41413796ff18b7fe479661bd40147e071b450dfad7a1/fonttools-4.63.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bf00f21eb5fb721dbaf73d1e9da6d02a1af7768f2ebcf9798be98beab8ba90f6", size = 4962489, upload-time = "2026-05-14T12:03:59.443Z" }, + { url = "https://files.pythonhosted.org/packages/30/c4/83c24f2ec38b90cfda84bf4b1a1f49df80e84a1db4e7ac6e0d41bf23bc39/fonttools-4.63.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c1aaa4b9c75798400ac043ce04d74e7830376c85095a5a6ed7cba2f17a266bf4", size = 5071870, upload-time = "2026-05-14T12:04:02.122Z" }, + { url = "https://files.pythonhosted.org/packages/de/40/3ae22b60ff1d41ce0bd044b31238cdc72cef99f28b976f1e128ebd618c9b/fonttools-4.63.0-cp314-cp314-win32.whl", hash = "sha256:22693918177bd9ceabec4736d338045f357769416fc6b0b2508eefef75b08616", size = 2295026, upload-time = "2026-05-14T12:04:04.47Z" }, + { url = "https://files.pythonhosted.org/packages/c3/d4/98078064ccc76b45cb0f6c002452011e93c4bd26f6850344f0951cc1fe89/fonttools-4.63.0-cp314-cp314-win_amd64.whl", hash = "sha256:7d782fac32985914c351556f68ac0855391572bcd87de50e05970d3cd4c96fc5", size = 2347454, upload-time = "2026-05-14T12:04:06.752Z" }, + { url = "https://files.pythonhosted.org/packages/49/4e/652d1580c5f4e39f7d103b0c793e4773129ad633dce4addd0cf4dfebde02/fonttools-4.63.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6db5140a60a5d731d21ec076745b40a310607731b0a565b50776393188649001", size = 2958152, upload-time = "2026-05-14T12:04:08.706Z" }, + { url = "https://files.pythonhosted.org/packages/0e/55/ad864c9a9b219f552eb46b32cd7906c466e5a578ba0c3abfcc0fe7413eb6/fonttools-4.63.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d76edbff9014094dbf03bd2d074709dfa6ec7aba13d838c937a2b33d2d6a86e", size = 2460809, upload-time = "2026-05-14T12:04:10.783Z" }, + { url = "https://files.pythonhosted.org/packages/ea/2b/0aa8db70f18cf52e49b4ed5ecec68547f981160bf5ded3b5aed6faa0a6f9/fonttools-4.63.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0eac00b9118c3c2f87d272e45341871c5b3066baa3c86897fa634a7c3fb59096", size = 5148649, upload-time = "2026-05-14T12:04:12.747Z" }, + { url = "https://files.pythonhosted.org/packages/7f/63/18e4369c25043096f1048e0c9915951adc4f842bd81c6b18155824d6fa99/fonttools-4.63.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51394295f1a51de8b5f30bdb1e1b9a4231536c7064ef5c6e211eec19fa36036f", size = 4932147, upload-time = "2026-05-14T12:04:14.806Z" }, + { url = "https://files.pythonhosted.org/packages/a1/3f/67f3eac2ffd8a98446c5022f8ed3864eac878a5ff7af8df4c8286dba16cc/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9e12f105d2b6342c559c298afb674006bb2893afc7102dcf8a1b55b0486b4e40", size = 5027237, upload-time = "2026-05-14T12:04:17.675Z" }, + { url = "https://files.pythonhosted.org/packages/1a/ba/4e6214cb38a7b04779e97bb7636de9a5c7f20af7018d03dee0b64c08510a/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:796f27556dbe094c4824f75ca85267e4df776c79036c8441469a4df37038c196", size = 5053933, upload-time = "2026-05-14T12:04:20.818Z" }, + { url = "https://files.pythonhosted.org/packages/34/3b/214dcc19ee31d3d38fb5ad2755c11ef0514e5dc300bbaf41c0b69f393799/fonttools-4.63.0-cp314-cp314t-win32.whl", hash = "sha256:948428a275741f0b64b113c955425a953314f4b9ab9997f73a72c83e68e569c8", size = 2359326, upload-time = "2026-05-14T12:04:24.22Z" }, + { url = "https://files.pythonhosted.org/packages/dd/1e/3ff1a9b523058c2eeb6a9d50f5574e2a738200d0d94107d5bc4105e8da3f/fonttools-4.63.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6d4741eb179121cab9eea4cb2393d24492373a260d7945006358c08cfbf45419", size = 2425829, upload-time = "2026-05-14T12:04:26.829Z" }, + { url = "https://files.pythonhosted.org/packages/2c/47/c99d5268f354002ce80f8d029cd9d7d872969da1de8b93d32de4dc56d6f4/fonttools-4.63.0-py3-none-any.whl", hash = "sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d", size = 1164562, upload-time = "2026-05-14T12:04:29.092Z" }, ] [[package]] @@ -2425,28 +2403,28 @@ wheels = [ [[package]] name = "google-auth" -version = "2.50.0" +version = "2.53.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, { name = "pyasn1-modules" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5f/18/238d7021d151bdab868f23433817b027dd759135202f4dfce0670d1230ca/google_auth-2.50.0.tar.gz", hash = "sha256:f35eafb191195328e8ce10a7883970877e7aeb49c2bfaa54aa0e394316d353d0", size = 336523, upload-time = "2026-04-30T21:19:29.659Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/ad/ff781329bbbdc0974a098d996e89c9e1f7024262f9e3eec442fbb9ad1ac6/google_auth-2.53.0.tar.gz", hash = "sha256:e7e6aa16f6bee7b2b264830fd04f08087a1d5a836df516251a5d15327b246c9c", size = 335844, upload-time = "2026-05-15T20:53:07.928Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/cf/4880c2137c14280b2f59975cdf12cc442bc0ae1f9ea473a26eaa0c146786/google_auth-2.50.0-py3-none-any.whl", hash = "sha256:04382175e28b94f49694977f0a792688b59a668def1499e9d8de996dc9ce5b15", size = 246495, upload-time = "2026-04-30T21:19:27.664Z" }, + { url = "https://files.pythonhosted.org/packages/4a/c9/db44165ba7c581268c6d46017ef63339110378305062830104fc7fa144cb/google_auth-2.53.0-py3-none-any.whl", hash = "sha256:6e7449917c599b35126a99ec268ec6880301f2fea41dce198fe8fd83ff642b68", size = 246071, upload-time = "2026-05-15T20:53:05.609Z" }, ] [[package]] name = "google-cloud-core" -version = "2.5.1" +version = "2.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core" }, { name = "google-auth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dc/24/6ca08b0a03c7b0c620427503ab00353a4ae806b848b93bcea18b6b76fde6/google_cloud_core-2.5.1.tar.gz", hash = "sha256:3dc94bdec9d05a31d9f355045ed0f369fbc0d8c665076c734f065d729800f811", size = 36078, upload-time = "2026-03-30T22:50:08.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/dd/1eef226e470369b26824a505c34482c0b493bc35fe8e0c6b003b5feca21a/google_cloud_core-2.6.0.tar.gz", hash = "sha256:e76149739f90fac1fc6757c09f47eaccb3145b54adbd7759b0f7c4b235f46c83", size = 36001, upload-time = "2026-05-07T08:04:04.124Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/d9/5bb050cb32826466aa9b25f79e2ca2879fe66cb76782d4ed798dd7506151/google_cloud_core-2.5.1-py3-none-any.whl", hash = "sha256:ea62cdf502c20e3e14be8a32c05ed02113d7bef454e40ff3fab6fe1ec9f1f4e7", size = 29452, upload-time = "2026-03-30T22:48:31.567Z" }, + { url = "https://files.pythonhosted.org/packages/84/4a/98da8930ab109c73d9a5d13782a9ebb81ea8c111f6d534a567b71d23e52b/google_cloud_core-2.6.0-py3-none-any.whl", hash = "sha256:6d63ac8e5eca6d9e4319d0a1e2265fadcd7f1049904378caecfa01cf52dd869e", size = 29390, upload-time = "2026-05-07T08:02:34.672Z" }, ] [[package]] @@ -2498,26 +2476,26 @@ wheels = [ [[package]] name = "google-resumable-media" -version = "2.8.2" +version = "2.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-crc32c" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/d1/b1ea14b93b6b78f57fc580125de44e9f593ab88dd2460f1a8a8d18f74754/google_resumable_media-2.8.2.tar.gz", hash = "sha256:f3354a182ebd193ae3f42e3ef95e6c9b10f128320de23ac7637236713b1acd70", size = 2164510, upload-time = "2026-03-30T23:34:25.369Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/4b/0b235beccc310d0a48adbc7246b719d173cca6c88c572dfa4b090e39143c/google_resumable_media-2.9.0.tar.gz", hash = "sha256:f7cfb224846a9dd444d125115dfbe8ef02a2b893e78f087762fe716a255a734b", size = 2164534, upload-time = "2026-05-07T08:04:44.236Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/f8/50bfaf4658431ff9de45c5c3935af7ab01157a4903c603cd0eee6e78e087/google_resumable_media-2.8.2-py3-none-any.whl", hash = "sha256:82b6d8ccd11765268cdd2a2123f417ec806b8eef3000a9a38dfe3033da5fb220", size = 81511, upload-time = "2026-03-30T23:34:09.671Z" }, + { url = "https://files.pythonhosted.org/packages/07/73/3518e63deb1667c5409a4579e28daf5e84479a87a72c547e0487f7883dcd/google_resumable_media-2.9.0-py3-none-any.whl", hash = "sha256:c8901e88e389af8bed64d9696c74d8bad961865eb2236e13e0bfca9bb0a65ca3", size = 81507, upload-time = "2026-05-07T08:03:23.809Z" }, ] [[package]] name = "googleapis-common-protos" -version = "1.74.0" +version = "1.75.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/18/a746c8344152d368a5aac738d4c857012f2c5d1fd2eac7e17b647a7861bd/googleapis_common_protos-1.74.0.tar.gz", hash = "sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1", size = 151254, upload-time = "2026-04-02T21:23:26.679Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/c8/f439cffde755cffa462bfbb156278fa6f9d09119719af9814b858fd4f81f/googleapis_common_protos-1.75.0.tar.gz", hash = "sha256:53a062ff3c32552fbd62c11fe23768b78e4ddf0494d5e5fd97d3f4689c75fbbd", size = 151035, upload-time = "2026-05-07T08:04:49.423Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/b0/be5d3329badb9230b765de6eea66b73abd5944bdeb5afb3562ddcd80ae84/googleapis_common_protos-1.74.0-py3-none-any.whl", hash = "sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5", size = 300743, upload-time = "2026-04-02T21:22:49.108Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c8/e2645aa8ed02fd4c7a2f59d68783b65b1f3cbdfe39a6308e156509d1fee8/googleapis_common_protos-1.75.0-py3-none-any.whl", hash = "sha256:961ed60399c457ceb0ee8f285a84c870aabc9c6a832b9d37bb281b5bebde43ed", size = 300631, upload-time = "2026-05-07T08:03:30.345Z" }, ] [[package]] @@ -2596,38 +2574,45 @@ wheels = [ [[package]] name = "httptools" -version = "0.7.1" +version = "0.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/08/17e07e8d89ab8f343c134616d72eebfe03798835058e2ab579dcc8353c06/httptools-0.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:474d3b7ab469fefcca3697a10d11a32ee2b9573250206ba1e50d5980910da657", size = 206521, upload-time = "2025-10-10T03:54:31.002Z" }, - { url = "https://files.pythonhosted.org/packages/aa/06/c9c1b41ff52f16aee526fd10fbda99fa4787938aa776858ddc4a1ea825ec/httptools-0.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3c3b7366bb6c7b96bd72d0dbe7f7d5eead261361f013be5f6d9590465ea1c70", size = 110375, upload-time = "2025-10-10T03:54:31.941Z" }, - { url = "https://files.pythonhosted.org/packages/cc/cc/10935db22fda0ee34c76f047590ca0a8bd9de531406a3ccb10a90e12ea21/httptools-0.7.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:379b479408b8747f47f3b253326183d7c009a3936518cdb70db58cffd369d9df", size = 456621, upload-time = "2025-10-10T03:54:33.176Z" }, - { url = "https://files.pythonhosted.org/packages/0e/84/875382b10d271b0c11aa5d414b44f92f8dd53e9b658aec338a79164fa548/httptools-0.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cad6b591a682dcc6cf1397c3900527f9affef1e55a06c4547264796bbd17cf5e", size = 454954, upload-time = "2025-10-10T03:54:34.226Z" }, - { url = "https://files.pythonhosted.org/packages/30/e1/44f89b280f7e46c0b1b2ccee5737d46b3bb13136383958f20b580a821ca0/httptools-0.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eb844698d11433d2139bbeeb56499102143beb582bd6c194e3ba69c22f25c274", size = 440175, upload-time = "2025-10-10T03:54:35.942Z" }, - { url = "https://files.pythonhosted.org/packages/6f/7e/b9287763159e700e335028bc1824359dc736fa9b829dacedace91a39b37e/httptools-0.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f65744d7a8bdb4bda5e1fa23e4ba16832860606fcc09d674d56e425e991539ec", size = 440310, upload-time = "2025-10-10T03:54:37.1Z" }, - { url = "https://files.pythonhosted.org/packages/b3/07/5b614f592868e07f5c94b1f301b5e14a21df4e8076215a3bccb830a687d8/httptools-0.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:135fbe974b3718eada677229312e97f3b31f8a9c8ffa3ae6f565bf808d5b6bcb", size = 86875, upload-time = "2025-10-10T03:54:38.421Z" }, - { url = "https://files.pythonhosted.org/packages/53/7f/403e5d787dc4942316e515e949b0c8a013d84078a915910e9f391ba9b3ed/httptools-0.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:38e0c83a2ea9746ebbd643bdfb521b9aa4a91703e2cd705c20443405d2fd16a5", size = 206280, upload-time = "2025-10-10T03:54:39.274Z" }, - { url = "https://files.pythonhosted.org/packages/2a/0d/7f3fd28e2ce311ccc998c388dd1c53b18120fda3b70ebb022b135dc9839b/httptools-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f25bbaf1235e27704f1a7b86cd3304eabc04f569c828101d94a0e605ef7205a5", size = 110004, upload-time = "2025-10-10T03:54:40.403Z" }, - { url = "https://files.pythonhosted.org/packages/84/a6/b3965e1e146ef5762870bbe76117876ceba51a201e18cc31f5703e454596/httptools-0.7.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c15f37ef679ab9ecc06bfc4e6e8628c32a8e4b305459de7cf6785acd57e4d03", size = 517655, upload-time = "2025-10-10T03:54:41.347Z" }, - { url = "https://files.pythonhosted.org/packages/11/7d/71fee6f1844e6fa378f2eddde6c3e41ce3a1fb4b2d81118dd544e3441ec0/httptools-0.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7fe6e96090df46b36ccfaf746f03034e5ab723162bc51b0a4cf58305324036f2", size = 511440, upload-time = "2025-10-10T03:54:42.452Z" }, - { url = "https://files.pythonhosted.org/packages/22/a5/079d216712a4f3ffa24af4a0381b108aa9c45b7a5cc6eb141f81726b1823/httptools-0.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362", size = 495186, upload-time = "2025-10-10T03:54:43.937Z" }, - { url = "https://files.pythonhosted.org/packages/e9/9e/025ad7b65278745dee3bd0ebf9314934c4592560878308a6121f7f812084/httptools-0.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e99c7b90a29fd82fea9ef57943d501a16f3404d7b9ee81799d41639bdaae412c", size = 499192, upload-time = "2025-10-10T03:54:45.003Z" }, - { url = "https://files.pythonhosted.org/packages/6d/de/40a8f202b987d43afc4d54689600ff03ce65680ede2f31df348d7f368b8f/httptools-0.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3e14f530fefa7499334a79b0cf7e7cd2992870eb893526fb097d51b4f2d0f321", size = 86694, upload-time = "2025-10-10T03:54:45.923Z" }, - { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889, upload-time = "2025-10-10T03:54:47.089Z" }, - { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180, upload-time = "2025-10-10T03:54:48.052Z" }, - { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596, upload-time = "2025-10-10T03:54:48.919Z" }, - { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268, upload-time = "2025-10-10T03:54:49.993Z" }, - { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517, upload-time = "2025-10-10T03:54:51.066Z" }, - { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337, upload-time = "2025-10-10T03:54:52.196Z" }, - { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743, upload-time = "2025-10-10T03:54:53.448Z" }, - { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619, upload-time = "2025-10-10T03:54:54.321Z" }, - { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714, upload-time = "2025-10-10T03:54:55.163Z" }, - { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909, upload-time = "2025-10-10T03:54:56.056Z" }, - { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831, upload-time = "2025-10-10T03:54:57.219Z" }, - { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631, upload-time = "2025-10-10T03:54:58.219Z" }, - { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910, upload-time = "2025-10-10T03:54:59.366Z" }, - { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/43/e5/d471fcb0e14523fe1c3f4ba58ca52480e7bd70ad7109a3846bc75892f7fb/httptools-0.8.0.tar.gz", hash = "sha256:6b2a32f18d97e16e90827d7a819ffa8dbd8cc245fc4e1fa9d1095b54ef4bd999", size = 271342, upload-time = "2026-05-25T22:17:48.841Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/d2/c3eedaef57de65c3cc5f8dc244cf12d09c84ad258a479055aad6db23206c/httptools-0.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ed377e64805bdba4943c82717333f8f8603a13b09aff9cead2717c6c817fb168", size = 208428, upload-time = "2026-05-25T22:16:59.717Z" }, + { url = "https://files.pythonhosted.org/packages/f1/94/dfe435d90d0ef61ec0f2cc3d480eef78c59727c6c2ce039f433882f6131a/httptools-0.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9518c406d7b310f05adb1a37f80acabac40504a575d7c0da6d3e365c695ac20d", size = 113366, upload-time = "2026-05-25T22:17:00.795Z" }, + { url = "https://files.pythonhosted.org/packages/cc/d4/13025f1a56e615dcb331e0bbe2d9a1143212b58c263385fc5d2e558f5bac/httptools-0.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:57278e6fa0424c42a8a3e454828ab4f0aff27b40cddf9679579b98c6dce6a376", size = 464676, upload-time = "2026-05-25T22:17:02.014Z" }, + { url = "https://files.pythonhosted.org/packages/bf/95/4c1c26c0b985f8a3331682d802598f14e32dc41bf7509266eb2c04ad4801/httptools-0.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bbb8caadb2b742d293169d2b458b5c001ef70e3158704aa3d3ef9597624c5d1d", size = 464235, upload-time = "2026-05-25T22:17:03.109Z" }, + { url = "https://files.pythonhosted.org/packages/a2/82/6735be2b0ca527718c431cdb8e5f70c3862c0844a687df0f572c51e11497/httptools-0.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:52dd695b865fe96d9d2b16b64a895f3f57bf3cb064e8383cd3b5713a069e8085", size = 449809, upload-time = "2026-05-25T22:17:04.443Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f9/5811c74f37a758c8a4aa3dc430375119d335947e883efc4664d8f3559a41/httptools-0.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:20b4aac66ff65f7db06a375808b78f42a94970aa22e826b3cb2b43eb09174124", size = 452174, upload-time = "2026-05-25T22:17:05.476Z" }, + { url = "https://files.pythonhosted.org/packages/cc/94/97b75870dea07b71e3ec535cebe525b08d723152e4c7d13fa887e51f4de2/httptools-0.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:a1b4c8e7a489a0d750d91894e9a8cdc295838f1924c0ca903ae993456fddec07", size = 90991, upload-time = "2026-05-25T22:17:06.75Z" }, + { url = "https://files.pythonhosted.org/packages/14/88/1d21a36da8f5cb0fa49eafd4b169eba5608d57e75bbcf61845cbc6243216/httptools-0.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:880490234c10f70a9830743097e8958d6e4b9f5a0ffc24515023afeef984054d", size = 208247, upload-time = "2026-05-25T22:17:07.843Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/cc4feea2945cb3051038f090c9b36bd5b8a9d7f5a894a506a8983e33fd1c/httptools-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5931891fb7b441b8a3853cf1b85c82c903defce084dd5f6771ca46e31bf862c5", size = 113064, upload-time = "2026-05-25T22:17:09.136Z" }, + { url = "https://files.pythonhosted.org/packages/e3/a6/febbb8b8db0f58b38e44ad6cb946e6a255ae49b55f2e8543408fb7501ccd/httptools-0.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b15fc622b0f869d19207c4089a501d9bcc63ca5e071ffdd2f03f922df882dcb2", size = 523851, upload-time = "2026-05-25T22:17:10.106Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e4/f90a0df0b83beff265b7e3b65f2a4cefd95792d4be0ac3e16049f2acd3c2/httptools-0.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:425f83884fd6343828d8c565f046cb72b6d19063f6924093e11bcd8e1548cd09", size = 518842, upload-time = "2026-05-25T22:17:11.218Z" }, + { url = "https://files.pythonhosted.org/packages/9e/2d/0c9ac76dd2c893841fbf6498d6acec4f2442e1b7067f6e3e316a80e494e8/httptools-0.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ef7c3c97f4311c7be57e2986629df89d49cb434dbff78eafcd48c2bff986b15a", size = 501238, upload-time = "2026-05-25T22:17:12.728Z" }, + { url = "https://files.pythonhosted.org/packages/ca/42/906adc91ae3a5fa9c59c0a2f21c139725bd7e5b41ae6acd485cd14123ebf/httptools-0.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a1afd7c9fbff0d9f5d489c4ce2768bd09c84a46ddefc7161e6aa82ae35c85745", size = 509567, upload-time = "2026-05-25T22:17:13.842Z" }, + { url = "https://files.pythonhosted.org/packages/05/0b/4240efeb672751ee5b9b380cb0e3fdc050bc05f68adc7a8aefc4fcd9a69a/httptools-0.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd96f29b4bab1d42fa6e3d008711c75e0f79e94e06827330160e3a304227f150", size = 90918, upload-time = "2026-05-25T22:17:15.155Z" }, + { url = "https://files.pythonhosted.org/packages/5e/e5/8cfcabc5546e8022f168be28bcdaa128a240a0befdd03b59d558b4f18bd6/httptools-0.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:614ceea8ea606848bece2338ac03b3ce5324bcb4be8dc7d377ed708012fa4db8", size = 205148, upload-time = "2026-05-25T22:17:16.333Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0e/0fb14848c19a686c8062ff9067c1a48793e3224b47bc5b201535b6036fce/httptools-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2d689918c15a013c65ef52d9fd495d766893ab831a2c8d89f2ac5940a5df847c", size = 111368, upload-time = "2026-05-25T22:17:17.586Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1b/46f1cecf06b9bbde8e4b8c88034ac7908989e5ff7a3a388ef38392949c1f/httptools-0.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:eb3028cca2fc0a6d720e52ef61d8ebb62fcbfeb1de56874546d858d3f25a26b7", size = 486447, upload-time = "2026-05-25T22:17:18.564Z" }, + { url = "https://files.pythonhosted.org/packages/77/00/258bfc0837221f81d9725c45f9b948a6a6b2994a147a4fb66e85100c668f/httptools-0.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88bdd940f2b5d487b4d032c6afa5489a7dc4694410d43de3c38c4fb3af0dc45d", size = 482448, upload-time = "2026-05-25T22:17:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/04/ab/d1cef3b5523f4d272a70f42a776c3169a2dddfe3a54de4b2ce4a36341528/httptools-0.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a43c9dd399758ccc0531acb0a3c4a6c299ee893ee9400e9c893b7bdcfae0681", size = 464460, upload-time = "2026-05-25T22:17:20.882Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/5d1d072442277bb2b3434e0e60690b8e8c23840ef7de8b6ea54040a536d3/httptools-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0770728beb05094c809b98e814edff5fef69d26ad7d21185f2f6d5884a0ba683", size = 471312, upload-time = "2026-05-25T22:17:22.085Z" }, + { url = "https://files.pythonhosted.org/packages/0d/66/b96623b27e51a68199ef4efdda0613cced9233fe3062ac74e50749c5ad37/httptools-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:7685df791fad561384bfb139e77fde27a1ffd93134e016f95a0db424ffbf77b1", size = 90117, upload-time = "2026-05-25T22:17:23.074Z" }, + { url = "https://files.pythonhosted.org/packages/1a/12/fa3fbf5f9517b273edea2dc982aa82a8c634091e67c590792b729017bc6f/httptools-0.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:de242a49b5d18e0a8776e654e9f6bf6d89f3875a5c35b425a0e7ce940feb3fd6", size = 206183, upload-time = "2026-05-25T22:17:24.004Z" }, + { url = "https://files.pythonhosted.org/packages/30/fc/5e7c4cb443370f2090a3aba0453a07384d29ff66b7435bb90e77e1037599/httptools-0.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:159e9ab5f701ccd42e555a12f1ad8ff69702910fc1c996cf2bb66e5fcb7a231b", size = 112079, upload-time = "2026-05-25T22:17:25.216Z" }, + { url = "https://files.pythonhosted.org/packages/ba/53/771bd891eb0f236f32145d6a1775777ec85745f3cc983a1f23d1a3b8ddfe/httptools-0.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c4a9f1707e4823d54dfec6c33fa3697d302aed536ed352a7ebb5a061ddb869d0", size = 481596, upload-time = "2026-05-25T22:17:26.186Z" }, + { url = "https://files.pythonhosted.org/packages/62/42/94e15bc68ce3d423243c45d7f1b0c7561f13844f97dc52ae23182fb65628/httptools-0.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d76ad7b951387e3632c8716a9bb03ac5b45c5f16119aa409db0459520887944e", size = 480865, upload-time = "2026-05-25T22:17:27.542Z" }, + { url = "https://files.pythonhosted.org/packages/1c/7c/fe2980fc03723272e30f135b62360b075f513dfe7cc73aef36c7f04012bd/httptools-0.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a3b7387147361c3fd47a0bde763c5c91b5b4cd4dc9989b8ece84ff436c99843b", size = 463189, upload-time = "2026-05-25T22:17:28.546Z" }, + { url = "https://files.pythonhosted.org/packages/15/1b/47fc5fff68acd1bfa20b4734059c9a06cadb88119dcd5258b5b0d21d91c8/httptools-0.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f256d6ce930c52ca1cb2a960b7da03548c454e7d28b06059ad41bfe789036ce0", size = 466610, upload-time = "2026-05-25T22:17:29.816Z" }, + { url = "https://files.pythonhosted.org/packages/60/bd/07b13c93ffd9bec9546e0d43f8e19378dd696dbd278511406bc07371ef1f/httptools-0.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:19d1ee275bb59ba2643ba9a3a1e51cc0c788caf2b8df506368e03f56fdd08527", size = 92705, upload-time = "2026-05-25T22:17:31.133Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c4/121648f68ce066d7bd762d6b6d97e620847642d38d54f3d90ff11d947629/httptools-0.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:de1ed58a974e75d56560acc7e7fed01a454994429456f65209789992e41f2568", size = 215023, upload-time = "2026-05-25T22:17:32.401Z" }, + { url = "https://files.pythonhosted.org/packages/b9/b0/312a062ae741ae3e8baa8c8bf20be81b2e67337b259ab4349bebc7b6142e/httptools-0.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e93c227b595c6926c1acee96891dd9da4be338cfbe82e5cd3bb9d8dd7dc4ac0b", size = 117405, upload-time = "2026-05-25T22:17:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/fc/37/fccd705f795386bb05bf413012fecff2a33e5aa8c2f069096de3e9fd8702/httptools-0.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2a021c3a8e65cc125390d72f59b968afca3bdcaff25bd67965e0a055a14946ca", size = 558497, upload-time = "2026-05-25T22:17:34.732Z" }, + { url = "https://files.pythonhosted.org/packages/bd/39/f172e8003576de35f5ba77ff417cf0e34429d35dc014deef15afa337a72c/httptools-0.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48774d39cbb70e2b1f71f88852a3087ae1d3a1eb80482bb48c13067ab080c14f", size = 571585, upload-time = "2026-05-25T22:17:35.813Z" }, + { url = "https://files.pythonhosted.org/packages/3e/b9/f5564760af99f3dbbf3f9104dc00e5da27e96cf433c6bdcf77617f70bf3f/httptools-0.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:88eead8ec8680a9f146c655bc88445a325bd7921cfd8194c7337e9467282427d", size = 543297, upload-time = "2026-05-25T22:17:37.08Z" }, + { url = "https://files.pythonhosted.org/packages/99/67/8d9f2c313618e161b82f3873188e7196126da1d6e29688df40eb3997c77a/httptools-0.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2c032fa028f46871ec7e1fc59fc15e8023eab3e6bbe6ece786a1611719a5d081", size = 539535, upload-time = "2026-05-25T22:17:38.032Z" }, + { url = "https://files.pythonhosted.org/packages/48/63/b906c01e53f50d432c0defe43ce52764a111dc1bdd028bafbeb54dcfd008/httptools-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:384c17174464c8e873398b7af24f0b1f44d992c820328413951a625323155d77", size = 108209, upload-time = "2026-05-25T22:17:39.473Z" }, ] [[package]] @@ -2683,11 +2668,11 @@ wheels = [ [[package]] name = "idna" -version = "3.15" +version = "3.16" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/88/bcf9709822fe69d02c2a6a77956c98ce6ea8ca8767a9aadcedc7eb6a2390/idna-3.16.tar.gz", hash = "sha256:d7a6da03db833450fca25d2358ac9ff06cd624577a4aea3a596d5c0f77b8e03d", size = 203770, upload-time = "2026-05-22T00:16:18.781Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" }, + { url = "https://files.pythonhosted.org/packages/94/16/70255075a9859a0e3adb789b68ceb0e210dec03934245fd98d248226572f/idna-3.16-py3-none-any.whl", hash = "sha256:cc246e3a3f89580c3a951b5ad298ca4638078b2cdd4f115654332b5c26daded5", size = 74165, upload-time = "2026-05-22T00:16:16.698Z" }, ] [[package]] @@ -2790,14 +2775,14 @@ wheels = [ [[package]] name = "importlib-metadata" -version = "8.7.1" +version = "9.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp" }, + { name = "zipp", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/01/15bb152d77b21318514a96f43af312635eb2500c96b55398d020c93d86ea/importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc", size = 56405, upload-time = "2026-03-20T06:42:56.999Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, + { url = "https://files.pythonhosted.org/packages/38/3d/2d244233ac4f76e38533cfcb2991c9eb4c7bf688ae0a036d30725b8faafe/importlib_metadata-9.0.0-py3-none-any.whl", hash = "sha256:2d21d1cc5a017bd0559e36150c21c830ab1dc304dedd1b7ea85d20f45ef3edd7", size = 27789, upload-time = "2026-03-20T06:42:55.665Z" }, ] [[package]] @@ -2931,14 +2916,14 @@ wheels = [ [[package]] name = "jaraco-functools" -version = "4.4.0" +version = "4.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "more-itertools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/cf/ea4ef2920830dea3f5ab2ea4da6fb67724e6dca80ee2553788c3607243d0/jaraco_functools-4.5.0.tar.gz", hash = "sha256:3bb5665ea4a020cf78a7040e89154c77edadb3ca74f366479669c5999aa70b03", size = 20272, upload-time = "2026-05-15T21:34:10.025Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, + { url = "https://files.pythonhosted.org/packages/96/9a/982e48afcffcd727a9144506720ffd4224b6b7e355c98641866f38b7c043/jaraco_functools-4.5.0-py3-none-any.whl", hash = "sha256:79ce39246eddbde4b3a03b77ea5f0f7878dc669b166a66cf3fa8e266aa3fa2f4", size = 10594, upload-time = "2026-05-15T21:34:08.595Z" }, ] [[package]] @@ -2985,14 +2970,14 @@ wheels = [ [[package]] name = "joserfc" -version = "1.6.5" +version = "1.6.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3b/dc/5f768c2e391e9afabe5d18e3221346deb5fb6338565f1ccc9e7c6d7befdd/joserfc-1.6.5.tar.gz", hash = "sha256:1482a7db78fb4602e44ed89e51b599d052e091288c7c532c5b694e20149dec48", size = 231881, upload-time = "2026-05-06T04:58:13.408Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5d/ac/d4fd5b30f82900eac60d765f179f0ba005825ac462cc8ced6e13ec685ab3/joserfc-1.6.8.tar.gz", hash = "sha256:878620c553a6ebdd76ccdc356782fee3f735f21a356d079a546b42a4670ace5f", size = 232930, upload-time = "2026-05-27T03:22:37.819Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/3b/ad1cb22e75c963b1f07c8a2329bf47227ce7e4361df5eb2fb101b2ce33ef/joserfc-1.6.5-py3-none-any.whl", hash = "sha256:e9878a0f8243fe7b95e11fdda81374ca9f7a689e302751579d3dfdeec559675e", size = 70464, upload-time = "2026-05-06T04:58:11.668Z" }, + { url = "https://files.pythonhosted.org/packages/98/8c/5cdce2cf3ce8155849baf9a5e2ce77e89dc87ec3bdb38259e5d85fbc45bd/joserfc-1.6.8-py3-none-any.whl", hash = "sha256:22fb31a69094a5e6f44632002a9df2c30c941fc6c8ce1b037e92c03de954cf9f", size = 70927, upload-time = "2026-05-27T03:22:35.796Z" }, ] [[package]] @@ -3069,16 +3054,17 @@ format-nongpl = [ [[package]] name = "jsonschema-path" -version = "0.4.6" +version = "0.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "attrs" }, { name = "pathable" }, { name = "pyyaml" }, { name = "referencing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/01/86/cfee6dd25843bec0760f456599a4f7e7e40221a934b9229fda0662c859bc/jsonschema_path-0.4.6.tar.gz", hash = "sha256:c89eb635f4d497c9ac328eeff359c489755838806a7d033510a692e9576f5c4b", size = 15302, upload-time = "2026-04-27T18:57:08.412Z" } +sdist = { url = "https://files.pythonhosted.org/packages/39/79/cd02a4df6d9270efdc7d3feefe6edd730b0820c39eeaa107a2faee8322d5/jsonschema_path-0.5.0.tar.gz", hash = "sha256:493b156ba895c97602655b620a8456caa2ce08c1aa389f5a7addec065e6e855c", size = 19597, upload-time = "2026-05-19T20:45:00.971Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/43/3d3065c05a04bb550c143bfbb8e4fd7022cd327e1082bf257bac74923783/jsonschema_path-0.4.6-py3-none-any.whl", hash = "sha256:451354b5311fa955c3144e6e4e255388c751c0121c5570ec5bb9291dd42d08c9", size = 19565, upload-time = "2026-04-27T18:57:06.792Z" }, + { url = "https://files.pythonhosted.org/packages/04/2c/9e69d73c4297508be9e3b64a970ea3971b3eb8db64ffc5802d40bd25981f/jsonschema_path-0.5.0-py3-none-any.whl", hash = "sha256:2790a070bc7abb08ea3dbe4d340ece4efadf639223001f020c7503229ba068e2", size = 24077, upload-time = "2026-05-19T20:44:59.225Z" }, ] [[package]] @@ -3191,7 +3177,7 @@ wheels = [ [[package]] name = "jupyter-server" -version = "2.18.1" +version = "2.18.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -3214,9 +3200,9 @@ dependencies = [ { name = "traitlets" }, { name = "websocket-client" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/b0/666586d557a71a58cd9960b154fb9aee0ed81dd62a50371195ab95731909/jupyter_server-2.18.1.tar.gz", hash = "sha256:f62be526369b791625e03bd658070563c1a4e9a0a2f439ea1f9dbacea5f7191a", size = 752024, upload-time = "2026-05-05T09:17:51.101Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/15/1eacb0fcb79ef86e8a0a79a708e6ad7435f6f223097dd29a4ce861fabc44/jupyter_server-2.18.2.tar.gz", hash = "sha256:06b4f40d8a7a00bb39d5216859c81374a0e7cfefe6d8a5a7facc5a5c37c679a7", size = 753177, upload-time = "2026-05-06T07:04:36.274Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/45/bfe3779fd06714a379128f2c4eaf7c99414f0eb081f9f34c135f6b3d511c/jupyter_server-2.18.1-py3-none-any.whl", hash = "sha256:db0374d52a975f88a92a7f20de44e08ef5be9763ba7e99630baf16c46ac8dbf0", size = 391844, upload-time = "2026-05-05T09:17:48.521Z" }, + { url = "https://files.pythonhosted.org/packages/e2/50/ecf4f70d65bdb7519b28a33d1b2fee8a4b4ba1ae1a92f15d97e877c5de21/jupyter_server-2.18.2-py3-none-any.whl", hash = "sha256:fa5e46539ded65791838035a2b6001f13e54d5f64b8b3752eb1e91fdd641a5b8", size = 391907, upload-time = "2026-05-06T07:04:34.014Z" }, ] [[package]] @@ -3427,75 +3413,75 @@ wheels = [ [[package]] name = "librt" -version = "0.10.0" +version = "0.11.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/39/cb/c1945e506893b5b8577fb45a60c80e3ffe4a82092a04a6f29b0b951d9a24/librt-0.10.0.tar.gz", hash = "sha256:1aba1e8aa4e3307a7be68a74149545fde7451964dc0235a8bec5704a17bdda42", size = 191799, upload-time = "2026-05-05T16:31:23.535Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/a3/1472717d2325adacc8d335ba2e4078015c09d75b599f3cf48e967b3d306e/librt-0.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01b4500ca3a625450c032a9142a8e843923ce263fa8a92ad1b38927cabe2fe72", size = 76045, upload-time = "2026-05-05T16:29:18.731Z" }, - { url = "https://files.pythonhosted.org/packages/a6/31/bfe32355d4b369aef3d7aa442df663bb5558c2ffa2de286cb2956346bc24/librt-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b7e42d1b3e300d20bfc87e72ffd62f0a92a2cb3c35f7bf90df90c9d2a49f74c", size = 79466, upload-time = "2026-05-05T16:29:20.052Z" }, - { url = "https://files.pythonhosted.org/packages/e9/f1/83f8a2c715ba2cac9b7387a5a5cea25f717f7184320cfe48b36bed9c58e9/librt-0.10.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8ef7b8c61ce3a1b597cd3e15348ff1574325165c2e7ce09a718154cde2a7950", size = 242283, upload-time = "2026-05-05T16:29:21.596Z" }, - { url = "https://files.pythonhosted.org/packages/cc/94/c3a4ce94857f0004a542f86662806383611858f522722db58efaec0a1472/librt-0.10.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:e73c84f72d1fa0d6eaa7a1930b436ba8d2c90c58d77bfabb09995a69ad35f6c0", size = 230735, upload-time = "2026-05-05T16:29:23.335Z" }, - { url = "https://files.pythonhosted.org/packages/d1/41/e962bb26c7728eb7b3a69e490d0c800fd9968a6970e390c1f18ddb56093d/librt-0.10.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9728cb98713bd862fb8f4fd6a642d1896c86058a41d77c70f3d5cee75e725275", size = 256606, upload-time = "2026-05-05T16:29:24.91Z" }, - { url = "https://files.pythonhosted.org/packages/66/3a/4e46a707b1ecc993fd691071623b9beab89703a63bd21cc7807e06c28209/librt-0.10.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:648b7e941d20acd72f9652115e0e53facd98156d61f9ebf7a812bdef8bdccea9", size = 249739, upload-time = "2026-05-05T16:29:26.648Z" }, - { url = "https://files.pythonhosted.org/packages/b2/f5/dc5b7eb294656ad23d4ff4cf8514208d54fe1026b909d726a0dc026689c9/librt-0.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c3e33747c068e86a9007c20fdb777eb5ba8d3d19136d7812f88e69a713041b6f", size = 261414, upload-time = "2026-05-05T16:29:28.702Z" }, - { url = "https://files.pythonhosted.org/packages/58/e4/990ed8d12c7f114ac8f8ccd47f7d9bd9704ef61acfcb1df4a05047da7710/librt-0.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d509c745bf7e77d1107cf05e6abb249dc03fad13eb39f2286a49deedaeb2bcd7", size = 256614, upload-time = "2026-05-05T16:29:30.357Z" }, - { url = "https://files.pythonhosted.org/packages/60/eb/52d2726c7fb22818507dc3cc166c8f36dd4a4b68a7be67f12006ac8777c1/librt-0.10.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:786ad5a15e99d0e0e74f3adbeecc198a5ac58f340be07e984723d1e0074838de", size = 255144, upload-time = "2026-05-05T16:29:32.106Z" }, - { url = "https://files.pythonhosted.org/packages/bc/df/bd5591a78f7531fce4b6eb9962aadc6adc9560a01570442a884b6e554abe/librt-0.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:075582d877a97ee3d8e77bda3689dbe617b14f6469224a2d80b4b6c38e3951aa", size = 279121, upload-time = "2026-05-05T16:29:33.688Z" }, - { url = "https://files.pythonhosted.org/packages/fd/df/7c2b838dfc89a1762dd156d8b0c39848a7a2845d725a50be5a6e021fb8ba/librt-0.10.0-cp311-cp311-win32.whl", hash = "sha256:75ecdc3f5a90065aa2af2e574706c5495adc392520762dcf10b1aa716f0b8090", size = 62593, upload-time = "2026-05-05T16:29:35.152Z" }, - { url = "https://files.pythonhosted.org/packages/91/19/22ff572981049a9d436a083dbea1572d0f5dc068b7353637d2dd9977c8f1/librt-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:b6f6084884131d8a52cb9d7095ff2aa52c1e786d9fdaefab1fb4515415e9e083", size = 70914, upload-time = "2026-05-05T16:29:36.407Z" }, - { url = "https://files.pythonhosted.org/packages/12/22/1697cc64f4a5c7e9bce55e99c6d234a346beaedaefcd1e2ca90dd285f98c/librt-0.10.0-cp311-cp311-win_arm64.whl", hash = "sha256:0140bd62151160047e89b2730cb6f8506cdac5127baa1afb9231e4dd3fe7f681", size = 61176, upload-time = "2026-05-05T16:29:37.62Z" }, - { url = "https://files.pythonhosted.org/packages/12/8e/cbb5b6f6e45e65c10a42449a69eaccc44d73e6a081ea752fbc5221c6dc1c/librt-0.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b4b58a44b407e91f633dafee008de9ddea6aa2a555ed94929c099260910bd0ba", size = 77327, upload-time = "2026-05-05T16:29:38.919Z" }, - { url = "https://files.pythonhosted.org/packages/e9/3d/8233cbee8e99e6a8992f02bfc2dec8d787509566a511d1fde2574ee7473f/librt-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:950b79b11762531bdf45a9df909d2f9a2a8445c70c88665c01d14c8511a27dc5", size = 79971, upload-time = "2026-05-05T16:29:40.96Z" }, - { url = "https://files.pythonhosted.org/packages/87/6f/5264b298cef2b72fc97d2dde56c66181eda35204bf5dcd1ed0c3d0a0a782/librt-0.10.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4538453f51be197633b425912c150e25b0667252d3741c53e8368176d98d9d37", size = 246559, upload-time = "2026-05-05T16:29:42.701Z" }, - { url = "https://files.pythonhosted.org/packages/07/7b/19b1b859cc60d5f99276cc2b3144d91556c6d1b1e4ebb50359696bebf7a8/librt-0.10.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:70b955f091beac93e994a0b7ec616934f63b3ea5c3d6d7af847562f935aceca7", size = 235216, upload-time = "2026-05-05T16:29:44.193Z" }, - { url = "https://files.pythonhosted.org/packages/6e/56/a2f40717142a8af46289f57874ef914353d8faccd5e4f8e594ab1e16e8c7/librt-0.10.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:483e685e06b6163728ba6c85d74315176be7190f432ec2a41226e5e14355d5f0", size = 263108, upload-time = "2026-05-05T16:29:46.365Z" }, - { url = "https://files.pythonhosted.org/packages/67/ca/15c625c3bdc0167c01e04ef8878317e9713f3bfa788438342f7a94c7b22c/librt-0.10.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ac53d946a009d1a38c44a60812708c9458fb2a239a5f630d8e625571386650f", size = 255280, upload-time = "2026-05-05T16:29:48.087Z" }, - { url = "https://files.pythonhosted.org/packages/ed/c5/ba301d571d9e05844e2435b73aba30bee77bb75ce155c9affcfd2173dd03/librt-0.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc8771c9fcf0ea894ca41fdc2abd83572c2fbda221f232d86e718614e57ff513", size = 268829, upload-time = "2026-05-05T16:29:49.628Z" }, - { url = "https://files.pythonhosted.org/packages/8b/60/af70e135bc1f1fe15dd3894b1e4bbefc7ecdf911749a925a39eb86ceb2a1/librt-0.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:70805dbc5257892ac572f86290a61e3c8d90224ecce1a8b2d1f7ed51965417f4", size = 262051, upload-time = "2026-05-05T16:29:51.244Z" }, - { url = "https://files.pythonhosted.org/packages/83/c2/c8236eb8b421bac5a172ba208f965abaa89805da2a3fa112bdf1764caf8f/librt-0.10.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d3b4f300f7bcba6e2ff73fb8bef1898479e9772bfa2682998c636391633ec826", size = 264347, upload-time = "2026-05-05T16:29:53.013Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f5/15b6d32bc25dacd4a60886a683d8128d6219910c122202b995a40dd4f8d2/librt-0.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:943bc943f92f4fb3408fae62485c6a3ad68ce4f2ee205643a39641525c19a276", size = 286482, upload-time = "2026-05-05T16:29:54.675Z" }, - { url = "https://files.pythonhosted.org/packages/fb/8e/b1b959bacd323eb4360579db992513e1406d1c6ef7edb57b5511fd0666fd/librt-0.10.0-cp312-cp312-win32.whl", hash = "sha256:6065c1a758fba1010b41401013903d3d5d2750eab425ddedd584abac31d0630e", size = 62955, upload-time = "2026-05-05T16:29:56.39Z" }, - { url = "https://files.pythonhosted.org/packages/9e/4c/d4cd6e4b9fc24098e63cc85537d1b6689682aee96809c38f08072067cc2b/librt-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:d788ecbe208ab352dab0e105cc06057bf9a2fc7e58cabb0d751ad9e30062b9e2", size = 71191, upload-time = "2026-05-05T16:29:57.682Z" }, - { url = "https://files.pythonhosted.org/packages/2b/19/8641da1f63d24b92354a492f893c022d6b3a0df44e70c8eff49364613983/librt-0.10.0-cp312-cp312-win_arm64.whl", hash = "sha256:6003d1f295bdba02656dc81308208fc060d0a51d8c0d0a6db70f7f3c57b9ba0a", size = 61432, upload-time = "2026-05-05T16:29:58.971Z" }, - { url = "https://files.pythonhosted.org/packages/e5/29/681a75c82f4cc90d29e4b257a3299b79fe13fe927a04c57b8109d70b6957/librt-0.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f0ede79d682e73f91c1b599a76d78b7464b9b5d213754cedb13372d9df36e596", size = 77299, upload-time = "2026-05-05T16:30:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/62/24/0c7ca445a55d04be79cac19819437fd094782347fa116f6681844fa6143e/librt-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0ba0b131fdb336c8b9c948e397f4a7e649d0f783b529f07b647bf4961df392e", size = 79930, upload-time = "2026-05-05T16:30:01.555Z" }, - { url = "https://files.pythonhosted.org/packages/fe/1f/1e2b8f6443ef9e9a81e89486ca70e22f3684f93db003ce6eaefc3d0839b9/librt-0.10.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2728117da2afb96fb957768725ee43dc9a2d73b031e02da424b818a3cdd3a275", size = 246195, upload-time = "2026-05-05T16:30:03.261Z" }, - { url = "https://files.pythonhosted.org/packages/74/61/9dc9e03de0439ad84c1c240aac8b747f12c90cb797ea6042f7bdb8d3410f/librt-0.10.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:723ba80594c49cdf0584196fc430752262605dc9449902fc9bd3d9b79976cb77", size = 234951, upload-time = "2026-05-05T16:30:04.881Z" }, - { url = "https://files.pythonhosted.org/packages/55/f4/635223117d7590875bca441275065a3bf491203ad4208bd1cc3ffd90c5a1/librt-0.10.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7292edaaca294a61a978c53a3c7d6130d099b0dfbc8f0a65916cdc6b891b9852", size = 262768, upload-time = "2026-05-05T16:30:06.638Z" }, - { url = "https://files.pythonhosted.org/packages/e5/66/b04152d0cd8b6ca2b428a8bd3230343230c35ed304a932f35b5375f2f828/librt-0.10.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:89fe9d539f2c10a1666633eeeac507ce95dd06d9ecc58de3c6390dba156a3d3a", size = 255075, upload-time = "2026-05-05T16:30:08.216Z" }, - { url = "https://files.pythonhosted.org/packages/35/1e/25bac4c7f2ca36f0e612cade186970683cf79153d96beccc3a11a9e19b97/librt-0.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4efa7b9587503fa5b67f40593302b9c8836d211d222ff9f7cafe67be5f8f0b10", size = 268559, upload-time = "2026-05-05T16:30:10.1Z" }, - { url = "https://files.pythonhosted.org/packages/18/54/4601faab35b6632a13200faa146ca62bfd111ffbe2568be430d65c89493a/librt-0.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:22dc982ef59df0136df36092ccbdbb570ced8aafb33e49585739b2f1de1c13b6", size = 261753, upload-time = "2026-05-05T16:30:11.912Z" }, - { url = "https://files.pythonhosted.org/packages/1b/cf/39f4023509e94fade8b074666fa3292db9cb6b34ea5dcbe7af53df9fca1d/librt-0.10.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6f2e5f3606253a84cea719c94a3bb1c54487b5d617d0254d46e0920d8a06be3f", size = 264055, upload-time = "2026-05-05T16:30:13.465Z" }, - { url = "https://files.pythonhosted.org/packages/8e/00/40247209fc46a8e308a91412d5206aedf8efb667ee89eb625820106a5c2f/librt-0.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:40884bfaa1e29f6b6a9be255007d8f359bfc9e61d68bdef8ed3158bfcbc95df9", size = 286190, upload-time = "2026-05-05T16:30:15.073Z" }, - { url = "https://files.pythonhosted.org/packages/d8/6e/5566beb94431a985abe1787af5ef86e087750172ff9d0bbf20f93e88132d/librt-0.10.0-cp313-cp313-win32.whl", hash = "sha256:3cd34cd8254eba756660bff6c2da91278248184301054fe3e4feb073bdd49b14", size = 62949, upload-time = "2026-05-05T16:30:16.503Z" }, - { url = "https://files.pythonhosted.org/packages/d0/c2/3ea3301d6c8dff51d39dbe8ed75db3dc92896947d4afb5eeadf821c1e67f/librt-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:7baac5313e2d8dce1386f97777a8d03ab28f5fe1e780b3b9ac2ee7544551fedc", size = 71152, upload-time = "2026-05-05T16:30:17.766Z" }, - { url = "https://files.pythonhosted.org/packages/3c/de/5d49cb92cadcbc77d3abc27b93fd6030ed8437487dde2eae38cab5e6704d/librt-0.10.0-cp313-cp313-win_arm64.whl", hash = "sha256:afc5b4406c8e2515698d922a5c7823a009312835ea58196671fff40e35cb8166", size = 61336, upload-time = "2026-05-05T16:30:19.021Z" }, - { url = "https://files.pythonhosted.org/packages/6a/64/7165e08108cc185a13a9c069f0685e6ef92e70e07fddf7edf5e7348c6316/librt-0.10.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f09588a30e6a22ec624090d72a3ab1a6d4d5485c3ed739603e76aa3c16efa688", size = 76794, upload-time = "2026-05-05T16:30:20.392Z" }, - { url = "https://files.pythonhosted.org/packages/ae/ef/bf8613febf651b90c5222ee79dea5ae58d4cc2b544df69d3033424448934/librt-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:131ade118d12bd7a0adc4e655474a553f1b76cf78385868885944d21d51e45e0", size = 79662, upload-time = "2026-05-05T16:30:22.025Z" }, - { url = "https://files.pythonhosted.org/packages/b6/67/9eddd165c1d8397bdf99b38bf12b5a55b3def5035b49eedb49f2775d1430/librt-0.10.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8b9ab28e40d011c373a189eae900c916e66d6fbecf7983e9e4883089ee085ef", size = 242390, upload-time = "2026-05-05T16:30:23.51Z" }, - { url = "https://files.pythonhosted.org/packages/10/d1/d95da80334501866cd37004ab5d7483220d05862fab4b5405394f0264f0d/librt-0.10.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:67c39bb30da73bae1f293d1ed8bc2f8f6642649dd0928d3600aeff3041ac23d6", size = 232603, upload-time = "2026-05-05T16:30:25.198Z" }, - { url = "https://files.pythonhosted.org/packages/0c/fa/e6d64d28718bc1be4e1736fcb037ca1c4dfca927e7167df75a7d5215665e/librt-0.10.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8c3273c6b774614f093c8927c2bf1b077d0fefde988fe98f46a333734e5597ab", size = 259187, upload-time = "2026-05-05T16:30:26.772Z" }, - { url = "https://files.pythonhosted.org/packages/72/3f/3fdb77e7f937dad59cfd76b720be7e7643400ec76b2da35befab8d66ba30/librt-0.10.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9dd7c1b86a4baa583ab5db977484b93a2c474e69e96ef3e9538387ea54229cb9", size = 251846, upload-time = "2026-05-05T16:30:28.56Z" }, - { url = "https://files.pythonhosted.org/packages/18/ca/f4d49133dd86a6f55d79eca30bf412fa722f511a9abe67f62f57aa64e66a/librt-0.10.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a77385c5a202e831149f7ad03be9e67cf80e957e52c614e83dcb822c95222eb8", size = 264936, upload-time = "2026-05-05T16:30:30.491Z" }, - { url = "https://files.pythonhosted.org/packages/de/66/a8df2fbadc1f6c1827a096d11c40175bd526133480bd3bc88ec64a03d257/librt-0.10.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c6a5eafa74b5655bad59886138ed68426f098a6beb8cb95a71f2cc3cd8bb33fe", size = 258699, upload-time = "2026-05-05T16:30:32.002Z" }, - { url = "https://files.pythonhosted.org/packages/bb/73/1e3c83613fe05451bb969e27b68a573d177f08d5f63533cc29fec0989658/librt-0.10.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:1fc93d0439204c50ab4d1512611ce2c206f1b369b419f69c7c27c761561e3291", size = 259825, upload-time = "2026-05-05T16:30:35.077Z" }, - { url = "https://files.pythonhosted.org/packages/09/24/5e2f926ee9d3ef348d9339526d7062abb5c44d8419e3179528c01d78c102/librt-0.10.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:79e713c178bc7a744adfbee6b4619a288eecc0c914da2a9313a20255abe2f0cf", size = 282548, upload-time = "2026-05-05T16:30:36.639Z" }, - { url = "https://files.pythonhosted.org/packages/fc/7d/3e89ed6ad0162561fa8bef9df3195e24263104c955713cd0237d3711fad2/librt-0.10.0-cp314-cp314-win32.whl", hash = "sha256:2eba9d955a68c41d9f326be3da42f163ec3518b7ab20f1c826224e7bed71e0bf", size = 58970, upload-time = "2026-05-05T16:30:38.183Z" }, - { url = "https://files.pythonhosted.org/packages/76/25/579e731c94a7086a268bfa3e7a4945cd47836bebd3cbf3faeafd2e7eaef9/librt-0.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:cbfaf7f5145e9917f5d18bffa298eff6a19d74e7b8b11dabdca95785befe8dbf", size = 67260, upload-time = "2026-05-05T16:30:39.804Z" }, - { url = "https://files.pythonhosted.org/packages/6e/f8/235822b7ae0b2334f12ee18bcf2476d07924077a5efeea57dbe927704be2/librt-0.10.0-cp314-cp314-win_arm64.whl", hash = "sha256:8d6d385d1969849a6b1397114df22714b6ded917bada98668e3e974dc663477e", size = 57156, upload-time = "2026-05-05T16:30:41.412Z" }, - { url = "https://files.pythonhosted.org/packages/9f/e3/9b919cbf1e8eb770bf91bb7df28125e0f1daf4587169afefd95402636e9a/librt-0.10.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:6c3a82d3bd32631ef5c79922dfc028520c9ad840255979ab4d908271818039ee", size = 79150, upload-time = "2026-05-05T16:30:42.761Z" }, - { url = "https://files.pythonhosted.org/packages/6a/f5/72a944aa3bc3498169a168087eff58ca48b58bf1b704e59d091fd30739f3/librt-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d64cc66005dc324c9bb1fa3fc2841f529002f6eb15966d55e46d430f56955a6a", size = 82304, upload-time = "2026-05-05T16:30:44.082Z" }, - { url = "https://files.pythonhosted.org/packages/9c/e3/fcc290a33e295019759472dfa794d204e43504b276ac65eab7fd9da20ea3/librt-0.10.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9bb562cd28c88cd2c6a9a6c78f99dc39348d6b16c94adc25de0e574acf1176e9", size = 272556, upload-time = "2026-05-05T16:30:45.497Z" }, - { url = "https://files.pythonhosted.org/packages/fd/54/546975e4c997573885e7f040a05012f8838e06fb12b0c3c1fbb76254e9d7/librt-0.10.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:b809aa2854d019c28773b03605df22adc675ee4f3f4402d673581313e8906119", size = 256941, upload-time = "2026-05-05T16:30:47.059Z" }, - { url = "https://files.pythonhosted.org/packages/70/8c/f1d03401571b331653acddbd4e8cd955c06d945241dd08b25192fac0d04b/librt-0.10.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc15acabdd519bd4176fdadc2119e5e3093485d86f89138daf47e5b4cedb983a", size = 285855, upload-time = "2026-05-05T16:30:48.86Z" }, - { url = "https://files.pythonhosted.org/packages/0c/08/62cf80ff046c339faf56718b3a940244d4beb70f1c6407289b5830ec11e9/librt-0.10.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b1b2d835307d08ddadd94568e2369648ec9173bd3eea6d7f52a1abe717c81f98", size = 275321, upload-time = "2026-05-05T16:30:50.63Z" }, - { url = "https://files.pythonhosted.org/packages/d9/ea/da5918d4070362e9a4d2ee9cd34f9dc84902daad8fd4275f8504a727ff4e/librt-0.10.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d261c6a2f93335a5167887fb0223e8b98ffce20ee3fde242e8e58a37ece6d0e5", size = 293993, upload-time = "2026-05-05T16:30:52.577Z" }, - { url = "https://files.pythonhosted.org/packages/c9/8d/68b6086bed1fcdc314c640ea04e31e52d18052e08059fa595409d66a51a9/librt-0.10.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e2ffd44963f8e7f68995504d90f9881d64e94dc1d8e310039b9526108fc0c0f7", size = 284254, upload-time = "2026-05-05T16:30:55.086Z" }, - { url = "https://files.pythonhosted.org/packages/06/c8/b810f1d84ec34a5a7ed93d7b510ab04164d75fbdf23088d5c3fbe6b08357/librt-0.10.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5f285f6455ed495791c4d8630e5af732960adea93cac4c893d15619f2eae53e8", size = 284925, upload-time = "2026-05-05T16:30:56.728Z" }, - { url = "https://files.pythonhosted.org/packages/5a/00/3c82d4158c5a2c62528b8fccce65a8c9ad700e480e86f9389387435089a5/librt-0.10.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f6034ff52e663d34c7b82ef2aa2f94ad7c1d939e2368e63b06844bc4d127d2e1", size = 307830, upload-time = "2026-05-05T16:30:58.377Z" }, - { url = "https://files.pythonhosted.org/packages/99/3a/9c635ac3e8a00383ff689161d3eac8a30b3b2ddc711b40471e6b8983ea29/librt-0.10.0-cp314-cp314t-win32.whl", hash = "sha256:657860fd877fba6a241ea088ef99f63ca819945d3c715265da670bad56c37ebe", size = 60147, upload-time = "2026-05-05T16:31:00.293Z" }, - { url = "https://files.pythonhosted.org/packages/dc/e8/6f65f3e565d4ac212cddddd552eacc8035ffdf941ca0ad6fe945a211d41f/librt-0.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:56ded2d66010203a0cb5af063b609e3f079531a0e5e576d618dece859fd2e1af", size = 68649, upload-time = "2026-05-05T16:31:01.778Z" }, - { url = "https://files.pythonhosted.org/packages/51/78/a0705a67cacd81e5fa01a5035b3adbdfbb43a7b8d4bd27e2b282ae61baf2/librt-0.10.0-cp314-cp314t-win_arm64.whl", hash = "sha256:1ee63f30abf18ed4830fdbaf87b2b6f4bba1e198d46085c314edde4045e56715", size = 58247, upload-time = "2026-05-05T16:31:03.191Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/40/08/9e7f6b5d2b5bed6ad055cdd5925f192bb403a51280f86b56554d9d0699a2/librt-0.11.0.tar.gz", hash = "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1", size = 200139, upload-time = "2026-05-10T18:17:25.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/87/2bf31fe17587b29e3f93ec31421e2b1e1c3e349b8bf6c7c313dbad1d5340/librt-0.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:93d95bd45b7d58343d8b90d904450a545144eec19a002511163426f8ab1fae29", size = 141092, upload-time = "2026-05-10T18:15:34.795Z" }, + { url = "https://files.pythonhosted.org/packages/cf/08/5c5bf772920b7ebac6e32bc91a643e0ab3870199c0b542356d3baa83970a/librt-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ee278c769a713638cdacd4c0436d72156e75df3ebc0166ab2b9dc43acc386c9", size = 142035, upload-time = "2026-05-10T18:15:36.242Z" }, + { url = "https://files.pythonhosted.org/packages/06/20/662a03d254e5b000d838e8b345d83303ddb768c080fd488e40634c0fa66b/librt-0.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f230cb1cbc9faaa616f9a678f530ebcf186e414b6bcbd88b960e4ba1b92428d5", size = 475022, upload-time = "2026-05-10T18:15:37.56Z" }, + { url = "https://files.pythonhosted.org/packages/de/f3/aa81523e45184c6ec23dc7f63263362ec55f80a09d424c012359ecbe7e35/librt-0.11.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5d63c855d86938d9de93e265c9bd8c705b51ec494de5738340ee93767a686e4b", size = 467273, upload-time = "2026-05-10T18:15:39.182Z" }, + { url = "https://files.pythonhosted.org/packages/6b/6f/59c74b560ca8853834d5501d589c8a2519f4184f273a085ffd0f37a1cc47/librt-0.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f028be9e96a08d31df3479ac80d99be374d17f3b78e4796b3fd3c913d4e89", size = 497083, upload-time = "2026-05-10T18:15:40.634Z" }, + { url = "https://files.pythonhosted.org/packages/fe/7b/5aa4d2c9600a719401160bf7055417df0b2a47439b9d88286ce45e56b65f/librt-0.11.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:258d73a0aa66a055e65b2e4d1b8cdb23b9d132c5bb915d9547d804fcaed116cc", size = 489139, upload-time = "2026-05-10T18:15:41.934Z" }, + { url = "https://files.pythonhosted.org/packages/d6/31/9143803d7da6856a69153785768c4936864430eec0fd9461c3ea527d9922/librt-0.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0827efe7854718f04aaddf6496e96960a956e676fe1d0f04eb41511fd8ad06d5", size = 508442, upload-time = "2026-05-10T18:15:43.206Z" }, + { url = "https://files.pythonhosted.org/packages/2f/5a/bce08184488426bda4ccc2c4964ac048c8f68ae89bd7120082eef4233cfd/librt-0.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7753e57d6e12d019c0d8786f1c09c709f4c3fcc57c3887b24e36e6c06ec938b7", size = 514230, upload-time = "2026-05-10T18:15:44.761Z" }, + { url = "https://files.pythonhosted.org/packages/89/8c/bb5e213d254b7505a0e658da199d8ab719086632ce09eef311ab27976523/librt-0.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:11bd19822431cc21af9f27374e7ae2e58103c7d98bda823536a6c47f6bb2bb3d", size = 494231, upload-time = "2026-05-10T18:15:46.308Z" }, + { url = "https://files.pythonhosted.org/packages/9d/fb/541cdad5b1ab1300398c74c4c9a497b88e5074c21b1244c8f49731d3a284/librt-0.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:22bdf239b219d3993761a148ffa134b19e52e9989c84f845d5d7b71d70a17412", size = 537585, upload-time = "2026-05-10T18:15:47.629Z" }, + { url = "https://files.pythonhosted.org/packages/8f/f2/464bb69295c320cb06bddb4f14a4ec67934ee14b2bffb12b19fb7ab287ba/librt-0.11.0-cp311-cp311-win32.whl", hash = "sha256:46c60b61e308eb535fbd6fa622b1ee1bb2815691c1ad9c98bf7b84952ec3bc8d", size = 100509, upload-time = "2026-05-10T18:15:49.157Z" }, + { url = "https://files.pythonhosted.org/packages/6d/e7/a17ee1788f9e4fbf548c19f4afa07c92089b9e24fef6cb2410863781ef4c/librt-0.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:902e546ff044f579ff1c953ff5fce97b636fe9e3943996b2177710c6ef076f73", size = 118628, upload-time = "2026-05-10T18:15:50.345Z" }, + { url = "https://files.pythonhosted.org/packages/cc/c7/6c766214f9f9903bcfcfbef97d807af8d8f5aa3502d247858ab17582d212/librt-0.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:65ac3bc20f78aa0ee5ae84baa68917f89fef4af63e941084dd019a0d0e749f0c", size = 103122, upload-time = "2026-05-10T18:15:52.068Z" }, + { url = "https://files.pythonhosted.org/packages/8b/d0/07c77e067f0838949b43bd89232c29d72efebb9d2801a9750184eb706b71/librt-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b87504f1690a23b9a2cca841191a04f83895d4fc2dd04df91d82b1a04ca2ad46", size = 144147, upload-time = "2026-05-10T18:15:53.227Z" }, + { url = "https://files.pythonhosted.org/packages/7a/24/8493538fa4f62f982686398a5b8f68008138a75086abdea19ade64bf4255/librt-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40071fc5fe0ce8daa6de616702314a01e1250711682b0523d6ab8d4525910cb3", size = 143614, upload-time = "2026-05-10T18:15:54.657Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1e/f8bad050810d9171f34a1648ed910e56814c2ba61639f2bd53c6377ae24b/librt-0.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:137e79445c896a0ea7b265f52d23954e05b64222ee1af69e2cb34219067cbb67", size = 485538, upload-time = "2026-05-10T18:15:56.117Z" }, + { url = "https://files.pythonhosted.org/packages/c0/fe/3594ebfbaf03084ba4b120c9ba5c3183fd938a48725e9bbe6ff0a5159ad8/librt-0.11.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:cca6644054e78746d8d4ef238681f9c34ff8b584fe6b988ecebb8db3b15e622a", size = 479623, upload-time = "2026-05-10T18:15:57.544Z" }, + { url = "https://files.pythonhosted.org/packages/b0/da/5d1876984b3746c85dbd219dbfcb73c85f54ee263fd32e5b2a632ec14571/librt-0.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5b0eea49f5562861ee8d757a32ef7d559c1d35be2aaaa1ec28941d74c9ffc8a", size = 513082, upload-time = "2026-05-10T18:15:58.805Z" }, + { url = "https://files.pythonhosted.org/packages/19/6e/55bdf5d5ca00c3e18430690bf2c953d8d3ffd3c337418173d33dec985dc9/librt-0.11.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0d1029d7e1ae1a7e647ed6fb5df8c4ce2dffefb7a9f5fd1376a4554d96dac09f", size = 508105, upload-time = "2026-05-10T18:16:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/07/10/f1f23a7c595ee90ece4d35c851e5d104b1311a887ed1b4ac4c35bbd13da8/librt-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc3ce6b33c5828d9e80592011a5c584cb2ce86edbc4088405f70da47dc1d1b3b", size = 522268, upload-time = "2026-05-10T18:16:01.708Z" }, + { url = "https://files.pythonhosted.org/packages/b6/02/5720f5697a7f54b78b3aefbe20df3a48cedcff1276618c4aa481177942ed/librt-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:936c5995f3514a42111f20099397d8177c79b4d7e70961e396c6f5a0a3566766", size = 527348, upload-time = "2026-05-10T18:16:03.496Z" }, + { url = "https://files.pythonhosted.org/packages/50/db/b4a47c6f91db4ff76348a0b3dd0cc65e090a078b765a810a62ff9434c3d3/librt-0.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9bc0ca6ad9381cbe8e4aa6e5726e4c80c78115a6e9723c599ed1d73e092bc49d", size = 516294, upload-time = "2026-05-10T18:16:05.173Z" }, + { url = "https://files.pythonhosted.org/packages/9e/58/9384b2f4eb1ed1d273d40948a7c5c4b2360213b402ef3be4641c06299f9c/librt-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:070aa8c26c0a74774317a72df8851facc7f0f012a5b406557ac56992d92e1ec8", size = 553608, upload-time = "2026-05-10T18:16:06.839Z" }, + { url = "https://files.pythonhosted.org/packages/21/7b/5aa8848a7c6a9278c79375146da1812e695754ceec5f005e6043461a7315/librt-0.11.0-cp312-cp312-win32.whl", hash = "sha256:6bf14feb84b05ae945277395451998c89c54d0def4070eb5c08de544930b245a", size = 101879, upload-time = "2026-05-10T18:16:08.103Z" }, + { url = "https://files.pythonhosted.org/packages/37/33/8a745436944947575b584231750a41417de1a38cf6a2e9251d1065651c09/librt-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:75672f0bc524ede266287d532d7923dbce94c7514ad07627bac3d0c6d92cc4d9", size = 119831, upload-time = "2026-05-10T18:16:09.174Z" }, + { url = "https://files.pythonhosted.org/packages/59/67/a6739ac96e28b7855808bdb0370e250606104a859750d209e5a0716fe7ab/librt-0.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f10cf143e4a9bb0f4f5af568a00df94a2d69ef41c2579584454bb0fe5cc642c", size = 103470, upload-time = "2026-05-10T18:16:10.369Z" }, + { url = "https://files.pythonhosted.org/packages/82/61/e59168d4d0bf2bf90f4f0caf7a001bfc60254c3af4586013b04dc3ef517b/librt-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78dc31f7fdfe9c9d0eb0e8f42d139db230e826415bbcabd9f0e9faaaee909894", size = 144119, upload-time = "2026-05-10T18:16:11.771Z" }, + { url = "https://files.pythonhosted.org/packages/61/fd/caa1d60b12f7dd79ccea23054e06eeaebe266a5f52c40a6b651069200ce5/librt-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fa475675db22290c3158e1d42326d0f5a65f04f44a0e68c3630a25b53560fb9c", size = 143565, upload-time = "2026-05-10T18:16:13.334Z" }, + { url = "https://files.pythonhosted.org/packages/b8/a9/dc744f5c2b4978d48db970be29f22716d3413d28b14ad99740817315cf2c/librt-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:621db29691044bdeda22e789e482e1b0f3a985d90e3426c9c6d17606416205ea", size = 485395, upload-time = "2026-05-10T18:16:14.729Z" }, + { url = "https://files.pythonhosted.org/packages/8f/21/7f8e97a1e4dae952a5a95948f6f8507a173bc1e669f54340bba6ca1ca31b/librt-0.11.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a9010e2ed5b3a9e158c5fd966b3ab7e834bb3d3aacc8f66c91dd4b57a3799230", size = 479383, upload-time = "2026-05-10T18:16:16.321Z" }, + { url = "https://files.pythonhosted.org/packages/a6/6d/d8ee9c114bebf2c50e29ec2aa940826fccb62a645c3e4c18760987d0e16d/librt-0.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c39513d8b7477a2e1ed8c43fc21c524e8d5a0f8d4e8b7b074dbdbe7820a08e2", size = 513010, upload-time = "2026-05-10T18:16:17.647Z" }, + { url = "https://files.pythonhosted.org/packages/f0/43/0b5708af2bd30a46400e72ba6bdaa8f066f15fb9a688527e34220e8d6c06/librt-0.11.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7aef3cf1d5af86e770ab04bfd993dfc4ae8b8c17f66fb77dd4a7d50de7bbb1a3", size = 508433, upload-time = "2026-05-10T18:16:19.309Z" }, + { url = "https://files.pythonhosted.org/packages/4a/50/356187247d09013490481033183b3532b58acf8028bcb34b2b56a375c9b2/librt-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:557183ddc36babe46b27dd60facbd5adb4492181a5be887587d57cda6e092f21", size = 522595, upload-time = "2026-05-10T18:16:20.642Z" }, + { url = "https://files.pythonhosted.org/packages/40/e7/c6ac4240899c7f3248079d5a9900debe0dadb3fdeaf856684c987105ba47/librt-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83d3e1f72bd42f6c5c0b7daec530c3f829bd02db42c70b8ddf0c2d90a2459930", size = 527255, upload-time = "2026-05-10T18:16:22.352Z" }, + { url = "https://files.pythonhosted.org/packages/eb/b5/a81322dbeedeeaf9c1ee6f001734d28a09d8383ac9e6779bc24bbd0743c6/librt-0.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4ce1f21fbe589bc1afd7872dece84fb0e1144f794a288e58a10d2c54a55c43be", size = 516847, upload-time = "2026-05-10T18:16:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/ae/66/6e6323787d592b55204a42595ff1102da5115601b53a7e9ddebc889a6da5/librt-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b09f7044ea2b64c9da42fd3d335666518cfd1c6e8a182c95da73d0214b41e", size = 553920, upload-time = "2026-05-10T18:16:25.025Z" }, + { url = "https://files.pythonhosted.org/packages/9c/21/623f8ca230857102066d9ca8c6c1734995908c4d0d1bee7bb2ef0021cb33/librt-0.11.0-cp313-cp313-win32.whl", hash = "sha256:78fddc31cd4d3caa897ad5d31f856b1faadc9474021ad6cb182b9018793e254e", size = 101898, upload-time = "2026-05-10T18:16:26.649Z" }, + { url = "https://files.pythonhosted.org/packages/b3/1d/b4ebd44dd723f768469007515cb92251e0ae286c94c140f374801140fa74/librt-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ca8aa88751a775870b764e93bad5135385f563cb8dcee399abf034ea4d3cb47", size = 119812, upload-time = "2026-05-10T18:16:27.859Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e4/b2f4ca7965ca373b491cdb4bc25cdb30c1649ca81a8782056a83850292a9/librt-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:96f044bb325fd9cf1a723015638c219e9143f0dfbc0ca54c565df2b7fc748b44", size = 103448, upload-time = "2026-05-10T18:16:29.066Z" }, + { url = "https://files.pythonhosted.org/packages/29/eb/dbce197da4e227779e56b5735f2decc3eb36e55a1cdbf1bd65d6639d76c1/librt-0.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4a017a95e5837dc15a8c5661d60e05daa96b90908b1aa6b7acdf443cd25c8ebd", size = 143345, upload-time = "2026-05-10T18:16:30.674Z" }, + { url = "https://files.pythonhosted.org/packages/76/a3/254bebd0c11c8ba684018efb8006ff22e466abce445215cca6c778e7d9de/librt-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ecbd9819deccc39b7542bf4d2a740d8a620694d39989e58661d3763458f8d4", size = 143131, upload-time = "2026-05-10T18:16:32.037Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3f/f77d6122d21ac7bf6ae8a7dfced1bd2a7ac545d3273ebdcaf8042f6d619f/librt-0.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7da327dacd7be8f8ec36547373550744a3cc0e536d54665cd83f8bcd961200e8", size = 477024, upload-time = "2026-05-10T18:16:33.493Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0a/2c996dadebaa7d9bbbd43ef2d4f3e66b6da545f838a41694ef6172cebec8/librt-0.11.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0dc56b1f8d06e60db362cc3fdae206681817f86ce4725d34511473487f12a34b", size = 474221, upload-time = "2026-05-10T18:16:34.864Z" }, + { url = "https://files.pythonhosted.org/packages/0a/7e/f5d92af8486b8272c23b3e686b46ff72d89c8169585eb61eef01a2ac7147/librt-0.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05fb8fb2ab90e21c8d12ea240d744ad514da9baf381ebfa70d91d20d21713175", size = 505174, upload-time = "2026-05-10T18:16:36.705Z" }, + { url = "https://files.pythonhosted.org/packages/af/1a/cb0734fe86398eb33193ab753b7326255c74cac5eb09e76b9b16536e7adb/librt-0.11.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cae74872be221df4374d10fec61f93ed1513b9546ea84f2c0bf73ab3e9bd0b03", size = 497216, upload-time = "2026-05-10T18:16:38.418Z" }, + { url = "https://files.pythonhosted.org/packages/18/06/094820f91558b66e29943c0ec41c9914f460f48dd51fc503c3101e10842d/librt-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32bcc918c0148eb7e3d57385125bac7e5f9e4359d05f07448b09f6f778c2f31c", size = 513921, upload-time = "2026-05-10T18:16:39.848Z" }, + { url = "https://files.pythonhosted.org/packages/0b/c2/00de9018871a282f530cacb457d5ec0428f6ac7e6fedde9aff7468d9fb04/librt-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f9743fc99135d5f78d2454435615f6dec0473ca507c26ce9d92b10b562a280d3", size = 520850, upload-time = "2026-05-10T18:16:41.471Z" }, + { url = "https://files.pythonhosted.org/packages/51/9d/64631832348fd1834fb3a61b996434edddaaf25a31d03b0a76273159d2cf/librt-0.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5ba067f4aadae8fda802d91d2124c90c42195ff32d9161d3549e6d05cfe26f96", size = 504237, upload-time = "2026-05-10T18:16:43.15Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ec/ae5525eb16edc827a044e7bb8777a455ff95d4bca9379e7e6bddd7383647/librt-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:de3bf945454d032f9e390b85c4072e0a0570bf825421c8be0e71209fa65e1abe", size = 546261, upload-time = "2026-05-10T18:16:44.408Z" }, + { url = "https://files.pythonhosted.org/packages/5a/09/adce371f27ca039411da9659f7430fcc2ba6cd0c7b3e4467a0f091be7fa9/librt-0.11.0-cp314-cp314-win32.whl", hash = "sha256:d2277a05f6dcb9fd13db9566aac4fabd68c3ea1ea46ee5567d4eef8efa495a2f", size = 96965, upload-time = "2026-05-10T18:16:46.039Z" }, + { url = "https://files.pythonhosted.org/packages/d6/ee/8ac720d98548f173c7ce2e632a7ca94673f74cacd5c8162a84af5b35958a/librt-0.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:ab73e8db5e3f564d812c1f5c3a175930a5f9bc96ccb5e3b22a34d7858b401cf7", size = 115151, upload-time = "2026-05-10T18:16:47.133Z" }, + { url = "https://files.pythonhosted.org/packages/94/20/c900cf14efeb09b6bef2b2dff20779f73464b97fd58d1c6bccc379588ae3/librt-0.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:aea3caa317752e3a466fa8af45d91ee0ea8c7fdd96e42b0a8dd9b76a7931eba1", size = 98850, upload-time = "2026-05-10T18:16:48.597Z" }, + { url = "https://files.pythonhosted.org/packages/0c/71/944bfe4b64e12abffcd3c15e1cce07f72f3d55655083786285f4dedeb532/librt-0.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d1b36540d7aaf9b9101b3a6f376c8d8e9f7a9aec93ed05918f2c69d493ffef72", size = 151138, upload-time = "2026-05-10T18:16:49.839Z" }, + { url = "https://files.pythonhosted.org/packages/b6/10/99e64a5c86989357fda078c8143c533389585f6473b7439172dd8f3b3b2d/librt-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:efbb343ab2ce3540f4ecbe6315d677ed70f37cd9a72b1e58066c918ca83acbaa", size = 151976, upload-time = "2026-05-10T18:16:51.062Z" }, + { url = "https://files.pythonhosted.org/packages/21/31/5072ad880946d83e5ea4147d6d018c78eefce85b77819b19bdd0ee229435/librt-0.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0dd688aab3f7914d3e6e5e3554978e0383312fb8e771d84be008a35b9ee548", size = 557927, upload-time = "2026-05-10T18:16:52.632Z" }, + { url = "https://files.pythonhosted.org/packages/5e/8d/70b5fb7cfbab60edbe7381614ab985da58e144fbf465c86d44c95f43cdca/librt-0.11.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f5fb36b8c6c63fdcbb1d526d94c0d1331610d43f4118cc1beb4efef4f3faacb2", size = 539698, upload-time = "2026-05-10T18:16:53.934Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a3/ba3495a0b3edbd24a4cae0d1d3c64f39a9fc45d06e812101289b50c1a619/librt-0.11.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a9a237d13addb93715b6fee74023d5ee3469b53fce527626c0e088aa585805f", size = 577162, upload-time = "2026-05-10T18:16:55.589Z" }, + { url = "https://files.pythonhosted.org/packages/f7/db/36e25fb81f99937ff1b96612a1dc9fd66f039cb9cc3aee12c01fac31aab9/librt-0.11.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5ddd17bd87b2c56ddd60e546a7984a2e64c4e8eab92fb4cf3830a48ad5469d51", size = 566494, upload-time = "2026-05-10T18:16:56.975Z" }, + { url = "https://files.pythonhosted.org/packages/33/0d/3f622b47f0b013eeb9cf4cc07ae9bfe378d832a4eec998b2b209fe84244d/librt-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd43992b4473d42f12ff9e68326079f0696d9d4e6000e8f39a0238d482ba6ee2", size = 596858, upload-time = "2026-05-10T18:16:58.374Z" }, + { url = "https://files.pythonhosted.org/packages/a9/02/71b90bc93039c46a2000651f6ad60122b114c8f54c4ad306e0e96f5b75ad/librt-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f8e3e8056dd674e279741485e2e512d6e9a751c7455809d0114e6ebf8d781085", size = 590318, upload-time = "2026-05-10T18:16:59.676Z" }, + { url = "https://files.pythonhosted.org/packages/04/04/418cb3f75621e2b761fb1ab0f017f4d70a1a72a6e7c74ee4f7e8d198c2f3/librt-0.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c1f708d8ae9c56cf38a903c44297243d2ec83fd82b396b977e0144a3e76217e3", size = 575115, upload-time = "2026-05-10T18:17:01.007Z" }, + { url = "https://files.pythonhosted.org/packages/cc/2c/5a2183ac58dd911f26b5d7e7d7d8f1d87fcecdddd99d6c12169a258ff62c/librt-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0add982e0e7b9fc14cf4b33789d5f13f66581889b88c2f58099f6ce8f92617bd", size = 617918, upload-time = "2026-05-10T18:17:02.682Z" }, + { url = "https://files.pythonhosted.org/packages/15/1f/dc6771a52592a4451be6effa200cbfc9cec61e4393d3033d81a9d307961d/librt-0.11.0-cp314-cp314t-win32.whl", hash = "sha256:2b481d846ac894c4e8403c5fd0e87c5d11d6499e404b474602508a224ff531c8", size = 103562, upload-time = "2026-05-10T18:17:03.99Z" }, + { url = "https://files.pythonhosted.org/packages/62/4a/7d1415567027286a75ba1093ec4aca11f073e0f559c530cf3e0a757ad55c/librt-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:28edb433edde181112a908c78907af28f964eabc15f4dd16c9d66c834302677c", size = 124327, upload-time = "2026-05-10T18:17:05.465Z" }, + { url = "https://files.pythonhosted.org/packages/ce/62/b40b382fa0c66fee1478073eb8db352a4a6beda4a1adccf1df911d8c289c/librt-0.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dee008f20b542e3cd162ba338a7f9ec0f6d23d395f66fe8aeeec3c9d067ea253", size = 102572, upload-time = "2026-05-10T18:17:06.809Z" }, ] [[package]] @@ -3627,116 +3613,116 @@ wheels = [ [[package]] name = "lxml" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/28/30/9abc9e34c657c33834eaf6cd02124c61bdf5944d802aa48e69be8da3585d/lxml-6.1.0.tar.gz", hash = "sha256:bfd57d8008c4965709a919c3e9a98f76c2c7cb319086b3d26858250620023b13", size = 4197006, upload-time = "2026-04-18T04:32:51.613Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/5d/3bccad330292946f97962df9d5f2d3ae129cce6e212732a781e856b91e07/lxml-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cec05be8c876f92a5aa07b01d60bbb4d11cfbdd654cad0561c0d7b5c043a61b9", size = 8526232, upload-time = "2026-04-18T04:27:40.389Z" }, - { url = "https://files.pythonhosted.org/packages/a7/51/adc8826570a112f83bb4ddb3a2ab510bbc2ccd62c1b9fe1f34fae2d90b57/lxml-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9c03e048b6ce8e77b09c734e931584894ecd58d08296804ca2d0b184c933ce50", size = 4595448, upload-time = "2026-04-18T04:27:44.208Z" }, - { url = "https://files.pythonhosted.org/packages/54/84/5a9ec07cbe1d2334a6465f863b949a520d2699a755738986dcd3b6b89e3f/lxml-6.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:942454ff253da14218f972b23dc72fa4edf6c943f37edd19cd697618b626fac5", size = 4923771, upload-time = "2026-04-18T04:32:17.402Z" }, - { url = "https://files.pythonhosted.org/packages/a7/23/851cfa33b6b38adb628e45ad51fb27105fa34b2b3ba9d1d4aa7a9428dfe0/lxml-6.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d036ee7b99d5148072ac7c9b847193decdfeac633db350363f7bce4fff108f0e", size = 5068101, upload-time = "2026-04-18T04:32:21.437Z" }, - { url = "https://files.pythonhosted.org/packages/b0/38/41bf99c2023c6b79916ba057d83e9db21d642f473cac210201222882d38b/lxml-6.1.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ae5d8d5427f3cc317e7950f2da7ad276df0cfa37b8de2f5658959e618ea8512", size = 5002573, upload-time = "2026-04-18T04:32:25.373Z" }, - { url = "https://files.pythonhosted.org/packages/c2/20/053aa10bdc39747e1e923ce2d45413075e84f70a136045bb09e5eaca41d3/lxml-6.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:363e47283bde87051b821826e71dde47f107e08614e1aa312ba0c5711e77738c", size = 5202816, upload-time = "2026-04-18T04:32:29.393Z" }, - { url = "https://files.pythonhosted.org/packages/9a/da/bc710fad8bf04b93baee752c192eaa2210cd3a84f969d0be7830fea55802/lxml-6.1.0-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:f504d861d9f2a8f94020130adac88d66de93841707a23a86244263d1e54682f5", size = 5329999, upload-time = "2026-04-18T04:32:34.019Z" }, - { url = "https://files.pythonhosted.org/packages/b3/cb/bf035dedbdf7fab49411aa52e4236f3445e98d38647d85419e6c0d2806b9/lxml-6.1.0-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:23a5dc68e08ed13331d61815c08f260f46b4a60fdd1640bbeb82cf89a9d90289", size = 4659643, upload-time = "2026-04-18T04:32:37.932Z" }, - { url = "https://files.pythonhosted.org/packages/5c/4f/22be31f33727a5e4c7b01b0a874503026e50329b259d3587e0b923cf964b/lxml-6.1.0-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f15401d8d3dbf239e23c818afc10c7207f7b95f9a307e092122b6f86dd43209a", size = 5265963, upload-time = "2026-04-18T04:32:41.881Z" }, - { url = "https://files.pythonhosted.org/packages/c8/2b/d44d0e5c79226017f4ab8c87a802ebe4f89f97e6585a8e4166dffcdd7b6e/lxml-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fcf3da95e93349e0647d48d4b36a12783105bcc74cb0c416952f9988410846a3", size = 5045444, upload-time = "2026-04-18T04:32:44.512Z" }, - { url = "https://files.pythonhosted.org/packages/d3/c3/3f034fec1594c331a6dbf9491238fdcc9d66f68cc529e109ec75b97197e1/lxml-6.1.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:0d082495c5fcf426e425a6e28daaba1fcb6d8f854a4ff01effb1f1f381203eb9", size = 4712703, upload-time = "2026-04-18T04:32:47.16Z" }, - { url = "https://files.pythonhosted.org/packages/12/16/0b83fccc158218aca75a7aa33e97441df737950734246b9fffa39301603d/lxml-6.1.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:e3c4f84b24a1fcba435157d111c4b755099c6ff00a3daee1ad281817de75ed11", size = 5252745, upload-time = "2026-04-18T04:32:50.427Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ee/12e6c1b39a77666c02eaa77f94a870aaf63c4ac3a497b2d52319448b01c6/lxml-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:976a6b39b1b13e8c354ad8d3f261f3a4ac6609518af91bdb5094760a08f132c4", size = 5226822, upload-time = "2026-04-18T04:32:53.437Z" }, - { url = "https://files.pythonhosted.org/packages/34/20/c7852904858b4723af01d2fc14b5d38ff57cb92f01934a127ebd9a9e51aa/lxml-6.1.0-cp311-cp311-win32.whl", hash = "sha256:857efde87d365706590847b916baff69c0bc9252dc5af030e378c9800c0b10e3", size = 3594026, upload-time = "2026-04-18T04:27:31.903Z" }, - { url = "https://files.pythonhosted.org/packages/02/05/d60c732b56da5085175c07c74b2df4e6d181b0c9a61e1691474f06ef4b39/lxml-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:183bfb45a493081943be7ea2b5adfc2b611e1cf377cefa8b8a8be404f45ef9a7", size = 4025114, upload-time = "2026-04-18T04:27:34.077Z" }, - { url = "https://files.pythonhosted.org/packages/c2/df/c84dcc175fd690823436d15b41cb920cd5ba5e14cd8bfb00949d5903b320/lxml-6.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:19f4164243fc206d12ed3d866e80e74f5bc3627966520da1a5f97e42c32a3f39", size = 3667742, upload-time = "2026-04-18T04:27:38.45Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d4/9326838b59dc36dfae42eec9656b97520f9997eee1de47b8316aaeed169c/lxml-6.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d2f17a16cd8751e8eb233a7e41aecdf8e511712e00088bf9be455f604cd0d28d", size = 8570663, upload-time = "2026-04-18T04:27:48.253Z" }, - { url = "https://files.pythonhosted.org/packages/d8/a4/053745ce1f8303ccbb788b86c0db3a91b973675cefc42566a188637b7c40/lxml-6.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0cea5b1d3e6e77d71bd2b9972eb2446221a69dc52bb0b9c3c6f6e5700592d93", size = 4624024, upload-time = "2026-04-18T04:27:52.594Z" }, - { url = "https://files.pythonhosted.org/packages/90/97/a517944b20f8fd0932ad2109482bee4e29fe721416387a363306667941f6/lxml-6.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc46da94826188ed45cb53bd8e3fc076ae22675aea2087843d4735627f867c6d", size = 4930895, upload-time = "2026-04-18T04:32:56.29Z" }, - { url = "https://files.pythonhosted.org/packages/94/7c/e08a970727d556caa040a44773c7b7e3ad0f0d73dedc863543e9a8b931f2/lxml-6.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9147d8e386ec3b82c3b15d88927f734f565b0aaadef7def562b853adca45784a", size = 5093820, upload-time = "2026-04-18T04:32:58.94Z" }, - { url = "https://files.pythonhosted.org/packages/88/ee/2a5c2aa2c32016a226ca25d3e1056a8102ea6e1fe308bf50213586635400/lxml-6.1.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5715e0e28736a070f3f34a7ccc09e2fdcba0e3060abbcf61a1a5718ff6d6b105", size = 5005790, upload-time = "2026-04-18T04:33:01.272Z" }, - { url = "https://files.pythonhosted.org/packages/e3/38/a0db9be8f38ad6043ab9429487c128dd1d30f07956ef43040402f8da49e8/lxml-6.1.0-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4937460dc5df0cdd2f06a86c285c28afda06aefa3af949f9477d3e8df430c485", size = 5630827, upload-time = "2026-04-18T04:33:04.036Z" }, - { url = "https://files.pythonhosted.org/packages/31/ba/3c13d3fc24b7cacf675f808a3a1baabf43a30d0cd24c98f94548e9aa58eb/lxml-6.1.0-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bc783ee3147e60a25aa0445ea82b3e8aabb83b240f2b95d32cb75587ff781814", size = 5240445, upload-time = "2026-04-18T04:33:06.87Z" }, - { url = "https://files.pythonhosted.org/packages/55/ba/eeef4ccba09b2212fe239f46c1692a98db1878e0872ae320756488878a94/lxml-6.1.0-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:40d9189f80075f2e1f88db21ef815a2b17b28adf8e50aaf5c789bfe737027f32", size = 5350121, upload-time = "2026-04-18T04:33:09.365Z" }, - { url = "https://files.pythonhosted.org/packages/7e/01/1da87c7b587c38d0cbe77a01aae3b9c1c49ed47d76918ef3db8fc151b1ca/lxml-6.1.0-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:05b9b8787e35bec69e68daf4952b2e6dfcfb0db7ecf1a06f8cdfbbac4eb71aad", size = 4694949, upload-time = "2026-04-18T04:33:11.628Z" }, - { url = "https://files.pythonhosted.org/packages/a1/88/7db0fe66d5aaf128443ee1623dec3db1576f3e4c17751ec0ef5866468590/lxml-6.1.0-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0f0f08beb0182e3e9a86fae124b3c47a7b41b7b69b225e1377db983802404e54", size = 5243901, upload-time = "2026-04-18T04:33:13.95Z" }, - { url = "https://files.pythonhosted.org/packages/00/a8/1346726af7d1f6fca1f11223ba34001462b0a3660416986d37641708d57c/lxml-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73becf6d8c81d4c76b1014dbd3584cb26d904492dcf73ca85dc8bff08dcd6d2d", size = 5048054, upload-time = "2026-04-18T04:33:16.965Z" }, - { url = "https://files.pythonhosted.org/packages/2e/b7/85057012f035d1a0c87e02f8c723ca3c3e6e0728bcf4cb62080b21b1c1e3/lxml-6.1.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1ae225f66e5938f4fa29d37e009a3bb3b13032ac57eb4eb42afa44f6e4054e69", size = 4777324, upload-time = "2026-04-18T04:33:19.832Z" }, - { url = "https://files.pythonhosted.org/packages/75/6c/ad2f94a91073ef570f33718040e8e160d5fb93331cf1ab3ca1323f939e2d/lxml-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:690022c7fae793b0489aa68a658822cea83e0d5933781811cabbf5ea3bcfe73d", size = 5645702, upload-time = "2026-04-18T04:33:22.436Z" }, - { url = "https://files.pythonhosted.org/packages/3b/89/0bb6c0bd549c19004c60eea9dc554dd78fd647b72314ef25d460e0d208c6/lxml-6.1.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:63aeafc26aac0be8aff14af7871249e87ea1319be92090bfd632ec68e03b16a5", size = 5232901, upload-time = "2026-04-18T04:33:26.21Z" }, - { url = "https://files.pythonhosted.org/packages/a1/d9/d609a11fb567da9399f525193e2b49847b5a409cdebe737f06a8b7126bdc/lxml-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:264c605ab9c0e4aa1a679636f4582c4d3313700009fac3ec9c3412ed0d8f3e1d", size = 5261333, upload-time = "2026-04-18T04:33:28.984Z" }, - { url = "https://files.pythonhosted.org/packages/a6/3a/ac3f99ec8ac93089e7dd556f279e0d14c24de0a74a507e143a2e4b496e7c/lxml-6.1.0-cp312-cp312-win32.whl", hash = "sha256:56971379bc5ee8037c5a0f09fa88f66cdb7d37c3e38af3e45cf539f41131ac1f", size = 3596289, upload-time = "2026-04-18T04:27:42.819Z" }, - { url = "https://files.pythonhosted.org/packages/f2/a7/0a915557538593cb1bbeedcd40e13c7a261822c26fecbbdb71dad0c2f540/lxml-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:bba078de0031c219e5dd06cf3e6bf8fb8e6e64a77819b358f53bb132e3e03366", size = 3997059, upload-time = "2026-04-18T04:27:46.764Z" }, - { url = "https://files.pythonhosted.org/packages/92/96/a5dc078cf0126fbfbc35611d77ecd5da80054b5893e28fb213a5613b9e1d/lxml-6.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:c3592631e652afa34999a088f98ba7dfc7d6aff0d535c410bea77a71743f3819", size = 3659552, upload-time = "2026-04-18T04:27:51.133Z" }, - { url = "https://files.pythonhosted.org/packages/08/03/69347590f1cf4a6d5a4944bb6099e6d37f334784f16062234e1f892fdb1d/lxml-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a0092f2b107b69601adf562a57c956fbb596e05e3e6651cabd3054113b007e45", size = 8559689, upload-time = "2026-04-18T04:31:57.785Z" }, - { url = "https://files.pythonhosted.org/packages/3f/58/25e00bb40b185c974cfe156c110474d9a8a8390d5f7c92a4e328189bb60e/lxml-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fc7140d7a7386e6b545d41b7358f4d02b656d4053f5fa6859f92f4b9c2572c4d", size = 4617892, upload-time = "2026-04-18T04:32:01.78Z" }, - { url = "https://files.pythonhosted.org/packages/f5/54/92ad98a94ac318dc4f97aaac22ff8d1b94212b2ae8af5b6e9b354bf825f7/lxml-6.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:419c58fc92cc3a2c3fa5f78c63dbf5da70c1fa9c1b25f25727ecee89a96c7de2", size = 4923489, upload-time = "2026-04-18T04:33:31.401Z" }, - { url = "https://files.pythonhosted.org/packages/15/3b/a20aecfab42bdf4f9b390590d345857ad3ffd7c51988d1c89c53a0c73faf/lxml-6.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:37fabd1452852636cf38ecdcc9dd5ca4bba7a35d6c53fa09725deeb894a87491", size = 5082162, upload-time = "2026-04-18T04:33:34.262Z" }, - { url = "https://files.pythonhosted.org/packages/45/26/2cdb3d281ac1bd175603e290cbe4bad6eff127c0f8de90bafd6f8548f0fd/lxml-6.1.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2853c8b2170cc6cd54a6b4d50d2c1a8a7aeca201f23804b4898525c7a152cfc", size = 4993247, upload-time = "2026-04-18T04:33:36.674Z" }, - { url = "https://files.pythonhosted.org/packages/f6/05/d735aef963740022a08185c84821f689fc903acb3d50326e6b1e9886cc22/lxml-6.1.0-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8e369cbd690e788c8d15e56222d91a09c6a417f49cbc543040cba0fe2e25a79e", size = 5613042, upload-time = "2026-04-18T04:33:39.205Z" }, - { url = "https://files.pythonhosted.org/packages/ee/b8/ead7c10efff731738c72e59ed6eb5791854879fbed7ae98781a12006263a/lxml-6.1.0-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e69aa6805905807186eb00e66c6d97a935c928275182eb02ee40ba00da9623b2", size = 5228304, upload-time = "2026-04-18T04:33:41.647Z" }, - { url = "https://files.pythonhosted.org/packages/6b/10/e9842d2ec322ea65f0a7270aa0315a53abed06058b88ef1b027f620e7a5f/lxml-6.1.0-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:4bd1bdb8a9e0e2dd229de19b5f8aebac80e916921b4b2c6ef8a52bc131d0c1f9", size = 5341578, upload-time = "2026-04-18T04:33:44.596Z" }, - { url = "https://files.pythonhosted.org/packages/89/54/40d9403d7c2775fa7301d3ddd3464689bfe9ba71acc17dfff777071b4fdc/lxml-6.1.0-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:cbd7b79cdcb4986ad78a2662625882747f09db5e4cd7b2ae178a88c9c51b3dfe", size = 4700209, upload-time = "2026-04-18T04:33:47.552Z" }, - { url = "https://files.pythonhosted.org/packages/85/b2/bbdcc2cf45dfc7dfffef4fd97e5c47b15919b6a365247d95d6f684ef5e82/lxml-6.1.0-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:43e4d297f11080ec9d64a4b1ad7ac02b4484c9f0e2179d9c4ef78e886e747b88", size = 5232365, upload-time = "2026-04-18T04:33:50.249Z" }, - { url = "https://files.pythonhosted.org/packages/48/5a/b06875665e53aaba7127611a7bed3b7b9658e20b22bc2dd217a0b7ab0091/lxml-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cc16682cc987a3da00aa56a3aa3075b08edb10d9b1e476938cfdbee8f3b67181", size = 5043654, upload-time = "2026-04-18T04:33:52.71Z" }, - { url = "https://files.pythonhosted.org/packages/e9/9c/e71a069d09641c1a7abeb30e693f828c7c90a41cbe3d650b2d734d876f85/lxml-6.1.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d6d8efe71429635f0559579092bb5e60560d7b9115ee38c4adbea35632e7fa24", size = 4769326, upload-time = "2026-04-18T04:33:55.244Z" }, - { url = "https://files.pythonhosted.org/packages/cc/06/7a9cd84b3d4ed79adf35f874750abb697dec0b4a81a836037b36e47c091a/lxml-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e39ab3a28af7784e206d8606ec0e4bcad0190f63a492bca95e94e5a4aef7f6e", size = 5635879, upload-time = "2026-04-18T04:33:58.509Z" }, - { url = "https://files.pythonhosted.org/packages/cc/f0/9d57916befc1e54c451712c7ee48e9e74e80ae4d03bdce49914e0aee42cd/lxml-6.1.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:9eb667bf50856c4a58145f8ca2d5e5be160191e79eb9e30855a476191b3c3495", size = 5224048, upload-time = "2026-04-18T04:34:00.943Z" }, - { url = "https://files.pythonhosted.org/packages/99/75/90c4eefda0c08c92221fe0753db2d6699a4c628f76ff4465ec20dea84cc1/lxml-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7f4a77d6f7edf9230cee3e1f7f6764722a41604ee5681844f18db9a81ea0ec33", size = 5250241, upload-time = "2026-04-18T04:34:03.365Z" }, - { url = "https://files.pythonhosted.org/packages/5e/73/16596f7e4e38fa33084b9ccbccc22a15f82a290a055126f2c1541236d2ff/lxml-6.1.0-cp313-cp313-win32.whl", hash = "sha256:28902146ffbe5222df411c5d19e5352490122e14447e98cd118907ee3fd6ee62", size = 3596938, upload-time = "2026-04-18T04:31:56.206Z" }, - { url = "https://files.pythonhosted.org/packages/8e/63/981401c5680c1eb30893f00a19641ac80db5d1e7086c62cb4b13ed813038/lxml-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:4a1503c56e4e2b38dc76f2f2da7bae69670c0f1933e27cfa34b2fa5876410b16", size = 3995728, upload-time = "2026-04-18T04:31:58.763Z" }, - { url = "https://files.pythonhosted.org/packages/e7/e8/c358a38ac3e541d16a1b527e4e9cb78c0419b0506a070ace11777e5e8404/lxml-6.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:e0af85773850417d994d019741239b901b22c6680206f46a34766926e466141d", size = 3658372, upload-time = "2026-04-18T04:32:03.629Z" }, - { url = "https://files.pythonhosted.org/packages/eb/45/cee4cf203ef0bab5c52afc118da61d6b460c928f2893d40023cfa27e0b80/lxml-6.1.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:ab863fd37458fed6456525f297d21239d987800c46e67da5ef04fc6b3dd93ac8", size = 8576713, upload-time = "2026-04-18T04:32:06.831Z" }, - { url = "https://files.pythonhosted.org/packages/8a/a7/eda05babeb7e046839204eaf254cd4d7c9130ce2bbf0d9e90ea41af5654d/lxml-6.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6fd8b1df8254ff4fd93fd31da1fc15770bde23ac045be9bb1f87425702f61cc9", size = 4623874, upload-time = "2026-04-18T04:32:10.755Z" }, - { url = "https://files.pythonhosted.org/packages/e7/e9/db5846de9b436b91890a62f29d80cd849ea17948a49bf532d5278ee69a9e/lxml-6.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:47024feaae386a92a146af0d2aeed65229bf6fff738e6a11dda6b0015fb8fd03", size = 4949535, upload-time = "2026-04-18T04:34:06.657Z" }, - { url = "https://files.pythonhosted.org/packages/5a/ba/0d3593373dcae1d68f40dc3c41a5a92f2544e68115eb2f62319a4c2a6500/lxml-6.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3f00972f84450204cd5d93a5395965e348956aaceaadec693a22ec743f8ae3eb", size = 5086881, upload-time = "2026-04-18T04:34:09.556Z" }, - { url = "https://files.pythonhosted.org/packages/43/76/759a7484539ad1af0d125a9afe9c3fb5f82a8779fd1f5f56319d9e4ea2fd/lxml-6.1.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97faa0860e13b05b15a51fb4986421ef7a30f0b3334061c416e0981e9450ca4c", size = 5031305, upload-time = "2026-04-18T04:34:12.336Z" }, - { url = "https://files.pythonhosted.org/packages/dc/b9/c1f0daf981a11e47636126901fd4ab82429e18c57aeb0fc3ad2940b42d8b/lxml-6.1.0-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:972a6451204798675407beaad97b868d0c733d9a74dafefc63120b81b8c2de28", size = 5647522, upload-time = "2026-04-18T04:34:14.89Z" }, - { url = "https://files.pythonhosted.org/packages/31/e6/1f533dcd205275363d9ba3511bcec52fa2df86abf8abe6a5f2c599f0dc31/lxml-6.1.0-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fe022f20bc4569ec66b63b3fb275a3d628d9d32da6326b2982584104db6d3086", size = 5239310, upload-time = "2026-04-18T04:34:17.652Z" }, - { url = "https://files.pythonhosted.org/packages/c3/8c/4175fb709c78a6e315ed814ed33be3defd8b8721067e70419a6cf6f971da/lxml-6.1.0-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:75c4c7c619a744f972f4451bf5adf6d0fb00992a1ffc9fd78e13b0bc817cc99f", size = 5350799, upload-time = "2026-04-18T04:34:20.529Z" }, - { url = "https://files.pythonhosted.org/packages/fd/77/6ffdebc5994975f0dde4acb59761902bd9d9bb84422b9a0bd239a7da9ca8/lxml-6.1.0-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:3648f20d25102a22b6061c688beb3a805099ea4beb0a01ce62975d926944d292", size = 4697693, upload-time = "2026-04-18T04:34:23.541Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f1/565f36bd5c73294602d48e04d23f81ff4c8736be6ba5e1d1ec670ac9be80/lxml-6.1.0-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:77b9f99b17cbf14026d1e618035077060fc7195dd940d025149f3e2e830fbfcb", size = 5250708, upload-time = "2026-04-18T04:34:26.001Z" }, - { url = "https://files.pythonhosted.org/packages/5a/11/a68ab9dd18c5c499404deb4005f4bc4e0e88e5b72cd755ad96efec81d18d/lxml-6.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32662519149fd7a9db354175aa5e417d83485a8039b8aaa62f873ceee7ea4cad", size = 5084737, upload-time = "2026-04-18T04:34:28.32Z" }, - { url = "https://files.pythonhosted.org/packages/ab/78/e8f41e2c74f4af564e6a0348aea69fb6daaefa64bc071ef469823d22cc18/lxml-6.1.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:73d658216fc173cf2c939e90e07b941c5e12736b0bf6a99e7af95459cfe8eabb", size = 4737817, upload-time = "2026-04-18T04:34:30.784Z" }, - { url = "https://files.pythonhosted.org/packages/06/2d/aa4e117aa2ce2f3b35d9ff246be74a2f8e853baba5d2a92c64744474603a/lxml-6.1.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ac4db068889f8772a4a698c5980ec302771bb545e10c4b095d4c8be26749616f", size = 5670753, upload-time = "2026-04-18T04:34:33.675Z" }, - { url = "https://files.pythonhosted.org/packages/08/f5/dd745d50c0409031dbfcc4881740542a01e54d6f0110bd420fa7782110b8/lxml-6.1.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:45e9dfbd1b661eb64ba0d4dbe762bd210c42d86dd1e5bd2bdf89d634231beb43", size = 5238071, upload-time = "2026-04-18T04:34:36.12Z" }, - { url = "https://files.pythonhosted.org/packages/3e/74/ad424f36d0340a904665867dab310a3f1f4c96ff4039698de83b77f44c1f/lxml-6.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:89e8d73d09ac696a5ba42ec69787913d53284f12092f651506779314f10ba585", size = 5264319, upload-time = "2026-04-18T04:34:39.035Z" }, - { url = "https://files.pythonhosted.org/packages/53/36/a15d8b3514ec889bfd6aa3609107fcb6c9189f8dc347f1c0b81eded8d87c/lxml-6.1.0-cp314-cp314-win32.whl", hash = "sha256:ebe33f4ec1b2de38ceb225a1749a2965855bffeef435ba93cd2d5d540783bf2f", size = 3657139, upload-time = "2026-04-18T04:32:20.006Z" }, - { url = "https://files.pythonhosted.org/packages/1a/a4/263ebb0710851a3c6c937180a9a86df1206fdfe53cc43005aa2237fd7736/lxml-6.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:398443df51c538bd578529aa7e5f7afc6c292644174b47961f3bf87fe5741120", size = 4064195, upload-time = "2026-04-18T04:32:23.876Z" }, - { url = "https://files.pythonhosted.org/packages/80/68/2000f29d323b6c286de077ad20b429fc52272e44eae6d295467043e56012/lxml-6.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:8c8984e1d8c4b3949e419158fda14d921ff703a9ed8a47236c6eb7a2b6cb4946", size = 3741870, upload-time = "2026-04-18T04:32:27.922Z" }, - { url = "https://files.pythonhosted.org/packages/30/e9/21383c7c8d43799f0da90224c0d7c921870d476ec9b3e01e1b2c0b8237c5/lxml-6.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1081dd10bc6fa437db2500e13993abf7cc30716d0a2f40e65abb935f02ec559c", size = 8827548, upload-time = "2026-04-18T04:32:15.094Z" }, - { url = "https://files.pythonhosted.org/packages/a5/01/c6bc11cd587030dd4f719f65c5657960649fe3e19196c844c75bf32cd0d6/lxml-6.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:dabecc48db5f42ba348d1f5d5afdc54c6c4cc758e676926c7cd327045749517d", size = 4735866, upload-time = "2026-04-18T04:32:18.924Z" }, - { url = "https://files.pythonhosted.org/packages/f3/01/757132fff5f4acf25463b5298f1a46099f3a94480b806547b29ce5e385de/lxml-6.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e3dd5fe19c9e0ac818a9c7f132a5e43c1339ec1cbbfecb1a938bd3a47875b7c9", size = 4969476, upload-time = "2026-04-18T04:34:41.889Z" }, - { url = "https://files.pythonhosted.org/packages/fd/fb/1bc8b9d27ed64be7c8903db6c89e74dc8c2cd9ec630a7462e4654316dc5b/lxml-6.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9e7b0a4ca6dcc007a4cef00a761bba2dea959de4bd2df98f926b33c92ca5dfb9", size = 5103719, upload-time = "2026-04-18T04:34:44.797Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e7/5bf82fa28133536a54601aae633b14988e89ed61d4c1eb6b899b023233aa/lxml-6.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d27bbe326c6b539c64b42638b18bc6003a8d88f76213a97ac9ed4f885efeab7", size = 5027890, upload-time = "2026-04-18T04:34:47.634Z" }, - { url = "https://files.pythonhosted.org/packages/2d/20/e048db5d4b4ea0366648aa595f26bb764b2670903fc585b87436d0a5032c/lxml-6.1.0-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4e425db0c5445ef0ad56b0eec54f89b88b2d884656e536a90b2f52aecb4ca86", size = 5596008, upload-time = "2026-04-18T04:34:51.503Z" }, - { url = "https://files.pythonhosted.org/packages/9a/c2/d10807bc8da4824b39e5bd01b5d05c077b6fd01bd91584167edf6b269d22/lxml-6.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4b89b098105b8599dc57adac95d1813409ac476d3c948a498775d3d0c6124bfb", size = 5224451, upload-time = "2026-04-18T04:34:54.263Z" }, - { url = "https://files.pythonhosted.org/packages/3c/15/2ebea45bea427e7f0057e9ce7b2d62c5aba20c6b001cca89ed0aadb3ad41/lxml-6.1.0-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:c4a699432846df86cc3de502ee85f445ebad748a1c6021d445f3e514d2cd4b1c", size = 5312135, upload-time = "2026-04-18T04:34:56.818Z" }, - { url = "https://files.pythonhosted.org/packages/31/e2/87eeae151b0be2a308d49a7ec444ff3eb192b14251e62addb29d0bf3778f/lxml-6.1.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:30e7b2ed63b6c8e97cca8af048589a788ab5c9c905f36d9cf1c2bb549f450d2f", size = 4639126, upload-time = "2026-04-18T04:34:59.704Z" }, - { url = "https://files.pythonhosted.org/packages/a3/51/8a3f6a20902ad604dd746ec7b4000311b240d389dac5e9d95adefd349e0c/lxml-6.1.0-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:022981127642fe19866d2907d76241bb07ed21749601f727d5d5dd1ce5d1b773", size = 5232579, upload-time = "2026-04-18T04:35:02.658Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d2/650d619bdbe048d2c3f2c31edb00e35670a5e2d65b4fe3b61bce37b19121/lxml-6.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:23cad0cc86046d4222f7f418910e46b89971c5a45d3c8abfad0f64b7b05e4a9b", size = 5084206, upload-time = "2026-04-18T04:35:05.175Z" }, - { url = "https://files.pythonhosted.org/packages/dd/8a/672ca1a3cbeabd1f511ca275a916c0514b747f4b85bdaae103b8fa92f307/lxml-6.1.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:21c3302068f50d1e8728c67c87ba92aa87043abee517aa2576cca1855326b405", size = 4758906, upload-time = "2026-04-18T04:35:08.098Z" }, - { url = "https://files.pythonhosted.org/packages/be/f1/ef4b691da85c916cb2feb1eec7414f678162798ac85e042fa164419ac05c/lxml-6.1.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:be10838781cb3be19251e276910cd508fe127e27c3242e50521521a0f3781690", size = 5620553, upload-time = "2026-04-18T04:35:11.23Z" }, - { url = "https://files.pythonhosted.org/packages/59/17/94e81def74107809755ac2782fdad4404420f1c92ca83433d117a6d5acf0/lxml-6.1.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2173a7bffe97667bbf0767f8a99e587740a8c56fdf3befac4b09cb29a80276fd", size = 5229458, upload-time = "2026-04-18T04:35:14.254Z" }, - { url = "https://files.pythonhosted.org/packages/21/55/c4be91b0f830a871fc1b0d730943d56013b683d4671d5198260e2eae722b/lxml-6.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c6854e9cf99c84beb004eecd7d3a3868ef1109bf2b1df92d7bc11e96a36c2180", size = 5247861, upload-time = "2026-04-18T04:35:17.006Z" }, - { url = "https://files.pythonhosted.org/packages/c2/ca/77123e4d77df3cb1e968ade7b1f808f5d3a5c1c96b18a33895397de292c1/lxml-6.1.0-cp314-cp314t-win32.whl", hash = "sha256:00750d63ef0031a05331b9223463b1c7c02b9004cef2346a5b2877f0f9494dd2", size = 3897377, upload-time = "2026-04-18T04:32:07.656Z" }, - { url = "https://files.pythonhosted.org/packages/64/ce/3554833989d074267c063209bae8b09815e5656456a2d332b947806b05ff/lxml-6.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:80410c3a7e3c617af04de17caa9f9f20adaa817093293d69eae7d7d0522836f5", size = 4392701, upload-time = "2026-04-18T04:32:12.113Z" }, - { url = "https://files.pythonhosted.org/packages/2b/a0/9b916c68c0e57752c07f8f64b30138d9d4059dbeb27b90274dedbea128ff/lxml-6.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:26dd9f57ee3bd41e7d35b4c98a2ffd89ed11591649f421f0ec19f67d50ec67ac", size = 3817120, upload-time = "2026-04-18T04:32:15.803Z" }, - { url = "https://files.pythonhosted.org/packages/f2/88/55143966481409b1740a3ac669e611055f49efd68087a5ce41582325db3e/lxml-6.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:546b66c0dd1bb8d9fa89d7123e5fa19a8aff3a1f2141eb22df96112afb17b842", size = 3930134, upload-time = "2026-04-18T04:32:35.008Z" }, - { url = "https://files.pythonhosted.org/packages/b5/97/28b985c2983938d3cb696dd5501423afb90a8c3e869ef5d3c62569282c0f/lxml-6.1.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5cfa1a34df366d9dc0d5eaf420f4cf2bb1e1bebe1066d1c2fc28c179f8a4004c", size = 4210749, upload-time = "2026-04-18T04:36:03.626Z" }, - { url = "https://files.pythonhosted.org/packages/29/67/dfab2b7d58214921935ccea7ce9b3df9b7d46f305d12f0f532ac7cf6b804/lxml-6.1.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db88156fcf544cdbf0d95588051515cfdfd4c876fc66444eb98bceb5d6db76de", size = 4318463, upload-time = "2026-04-18T04:36:06.309Z" }, - { url = "https://files.pythonhosted.org/packages/32/a2/4ac7eb32a4d997dd352c32c32399aae27b3f268d440e6f9cfa405b575d2f/lxml-6.1.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:07f98f5496f96bf724b1e3c933c107f0cbf2745db18c03d2e13a291c3afd2635", size = 4251124, upload-time = "2026-04-18T04:36:09.056Z" }, - { url = "https://files.pythonhosted.org/packages/33/ef/d6abd850bb4822f9b720cfe36b547a558e694881010ff7d012191e8769c6/lxml-6.1.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4642e04449a1e164b5ff71ffd901ddb772dfabf5c9adf1b7be5dffe1212bc037", size = 4401758, upload-time = "2026-04-18T04:36:11.803Z" }, - { url = "https://files.pythonhosted.org/packages/40/44/3ee09a5b60cb44c4f2fbc1c9015cfd6ff5afc08f991cab295d3024dcbf2d/lxml-6.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7da13bb6fbadfafb474e0226a30570a3445cfd47c86296f2446dafbd77079ace", size = 3508860, upload-time = "2026-04-18T04:32:48.619Z" }, +version = "6.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/3b/aab6728cae887456f409b4d75e8a01856e4f04bd510de38052a47768b680/lxml-6.1.1.tar.gz", hash = "sha256:ba96ae44888e0185281e937633a743ea90d5a196c6000f82565ebb0580012d40", size = 4197430, upload-time = "2026-05-18T19:19:06.424Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/b0/83f481780d1548750b8ce2ec824073deef2f452d9cd1a6faff8507e3d16d/lxml-6.1.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:53b7d2b7a10b1c35c0a5e21e9224accf60c1bbfba523990732e521b2b73adef2", size = 8526461, upload-time = "2026-05-18T19:17:25.862Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d5/30fa0f808002c7329397bfbb24e306789c0b29f04aa5842c07b174b4216f/lxml-6.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ff3f333630ab480244a1bff72043e511a91eb22e7595dead8653ee5612dd8f3d", size = 4595375, upload-time = "2026-05-18T19:17:34.555Z" }, + { url = "https://files.pythonhosted.org/packages/4f/d2/edb71cf0e561581a7c5eb2626244320eb04e9f8ce6d563184fd668b45073/lxml-6.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a4bbea04c97f6d78a48e3fbc1cb9116d2780b1b39e03a23f6eb9b603fd61f510", size = 4923654, upload-time = "2026-05-18T19:17:42.917Z" }, + { url = "https://files.pythonhosted.org/packages/4c/77/1bc7eeb0de4577d783fb625aa092cc9357883bba35845a3666bf1259f3dc/lxml-6.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db1d75f6617a49c1c01bc7023713e0ff59ab32c9579ae62a7674c0e34f3b0b0a", size = 5067921, upload-time = "2026-05-18T19:17:49.175Z" }, + { url = "https://files.pythonhosted.org/packages/1b/3c/c0690d74bd2bc17bc03b5b0d093569ead597dd0bfa088bf99eef8c24e19c/lxml-6.1.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a12689be69a28ddaa0ab99a5a1137da2afd5f8f16df7b5680b66f616d3eda1d", size = 5002456, upload-time = "2026-05-18T19:17:59.715Z" }, + { url = "https://files.pythonhosted.org/packages/66/8d/d1b3271af0c0f1e27e8472a849e4d2c65bc7766884b9ad2da9e76e145c88/lxml-6.1.1-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b73c339ae29b90fd2d06e58ebd555a751bde9cd6bbd36cc0281b9a2c94e9d8", size = 5202776, upload-time = "2026-05-18T19:18:08.924Z" }, + { url = "https://files.pythonhosted.org/packages/7a/45/689824ffb237fd10125ad273f32b28ff04dc6203c2822c85ff65a93df65e/lxml-6.1.1-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:752d3bbfe874715ccd0aec7f88d7fc623c0f1fd7aa7b3238a084e017bad2a009", size = 5329945, upload-time = "2026-05-18T19:18:13.673Z" }, + { url = "https://files.pythonhosted.org/packages/5d/c0/ef73af53767e958fd87d437c170f272e2f6e6c0f854939f133a895f1e711/lxml-6.1.1-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:6b1761fbf9ec984e2e9d9c589ef5f5fd684b7c19f92aadd567a26c5224958db6", size = 4659237, upload-time = "2026-05-18T19:18:18.657Z" }, + { url = "https://files.pythonhosted.org/packages/a0/5e/e1158e40397585e91cb0472374a1f63d0926a1ddeaa92f13d1a1ffe306d5/lxml-6.1.1-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d680fbcb768404c601ecb43519ecd8461f6954cb11c06a78962f666832ccfca8", size = 5265904, upload-time = "2026-05-18T19:18:24.883Z" }, + { url = "https://files.pythonhosted.org/packages/a0/16/8687e5d1400ed1c0bc41dace232ebb7553952b618ea1f2e5fb6e2cfbbe23/lxml-6.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:162af1091cd785f2f27e62d3547ae9bc58ec5c86dd314d67021fd02463708d83", size = 5045225, upload-time = "2026-05-18T19:17:20.073Z" }, + { url = "https://files.pythonhosted.org/packages/ca/18/d877bd1ae2e5ffdfd4836565aba350db31feb2f2656d6ce70316ed66a05e/lxml-6.1.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e9308ff8241c532df3f3e570f9a5aeed6c853f888512ba4b75638d7c11c95ef6", size = 4712721, upload-time = "2026-05-18T19:17:40.512Z" }, + { url = "https://files.pythonhosted.org/packages/44/4d/1f44fd1d770b10dacbf6b5c6e520f4d6e0708744930f719dc04e67cab981/lxml-6.1.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5f6994074ebae6ffb04447268e37dc16edc304f9859cf91acb86e0af6c1b395c", size = 5252549, upload-time = "2026-05-18T19:17:51.236Z" }, + { url = "https://files.pythonhosted.org/packages/64/5d/1d66b84f850089254c230ef6ea6b267a5a54e2e179a5d960036a05d501d7/lxml-6.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:80c2dfadb855da477cf73373ad29a333535dedb9b12bad02c9814c8e2b43bf08", size = 5226877, upload-time = "2026-05-18T19:18:00.875Z" }, + { url = "https://files.pythonhosted.org/packages/ad/00/84c4b5302d42a2d0184f38d538c8a197f33b52a50bd4f7bcfe990bce3036/lxml-6.1.1-cp311-cp311-win32.whl", hash = "sha256:30a89d3ac8faec007453fb541f3f46807eeec88edd5826f6e3fe001752a2c621", size = 3594072, upload-time = "2026-05-18T19:17:12.714Z" }, + { url = "https://files.pythonhosted.org/packages/61/9d/2e2f7d876349f45e0f3e29f72da311668853d59b58d473a2dea4f0160135/lxml-6.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:abbefa31eee84842140f67acef1c828e28bba8bbf0c3bc6e5492a9af88152c28", size = 4025469, upload-time = "2026-05-18T19:17:50.566Z" }, + { url = "https://files.pythonhosted.org/packages/b0/d5/570e6390e4110331e6208b2ba83d1482cc9146808ee118b22824a34c1070/lxml-6.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:dcb292aa7fe485ceff7af4f92e46c5af397daec5dff64871a528f0fc47a3cc5b", size = 3667640, upload-time = "2026-05-19T19:22:48.293Z" }, + { url = "https://files.pythonhosted.org/packages/6a/6e/c4add832b6fc1e887125b96f880d7b9b70aae5248718e046b1704bcac4b9/lxml-6.1.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:104c09bda8d2a562824c0e319d0768ce26a779b7601e0931d33b09b53c392ef7", size = 8570821, upload-time = "2026-05-18T19:17:42.068Z" }, + { url = "https://files.pythonhosted.org/packages/22/00/ff3009c88e65de8011630acf8ab5a09cb2becd2aaf47fba2f3449f6224e9/lxml-6.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:25c6997a9a534e016695a0ba06b2f07945de682731ff01065b6d5a4474179da1", size = 4624252, upload-time = "2026-05-18T19:17:47.897Z" }, + { url = "https://files.pythonhosted.org/packages/42/95/bb63f0fd62e554fe078e1fb3c8fe9083c14ddc7ad7fa178d10e57e071ac7/lxml-6.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c921ba5c51e4e9f63b8b00267d06566e1f63407408a0496da2d1d0bfc819c7fc", size = 4930746, upload-time = "2026-05-18T19:18:29.637Z" }, + { url = "https://files.pythonhosted.org/packages/eb/99/0013e8d9b5960f4f041cf0b73e2f80c23eb5205b1f7bfb20203243651359/lxml-6.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:54a7f95e4de5fb94e2f9f4b9055c6ba33bf3d628fd77a1d647c5923caa2cdcdc", size = 5093723, upload-time = "2026-05-18T19:18:34.168Z" }, + { url = "https://files.pythonhosted.org/packages/29/91/317b332636bfc7bddcff828d41b3307f50043f4b237e40849c333d80fa1a/lxml-6.1.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f2ec43df44b1f76249ee0a615334f9b5b060e1c8bd90e706dad2d14d02f383", size = 5005557, upload-time = "2026-05-18T19:18:39.798Z" }, + { url = "https://files.pythonhosted.org/packages/42/2f/cc9bf06afe70f9c9093ae60855d9759da9db601ec4080f7473319666ffd7/lxml-6.1.1-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:70ef8a7e102a1508f8121aae5b0867abd663f72c14f0a9c937e6554cb4587b7b", size = 5631036, upload-time = "2026-05-18T19:18:44.858Z" }, + { url = "https://files.pythonhosted.org/packages/08/f6/af32e23e563971ffb0fb86be52bc5be5c2c118858ffc119bf6a9039b173d/lxml-6.1.1-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ebe6af670449830d6d9b752c256a983291c766a1365ba5d5460048f9e33a7818", size = 5240367, upload-time = "2026-05-18T19:18:49.217Z" }, + { url = "https://files.pythonhosted.org/packages/78/83/8555d40948b09ce86f1bd0c68a7ac31d07b1929f92cc1b074006c97ef2d2/lxml-6.1.1-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:27acc820660aaffa4f7c087f29120e12980f7779d56d8492d263170111284740", size = 5350171, upload-time = "2026-05-18T19:18:52.779Z" }, + { url = "https://files.pythonhosted.org/packages/63/75/5d92da93729b7bad783689e6496049fa40927b45bec7bf183c981de3ca70/lxml-6.1.1-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:1db753c9115ec7100d073b744d17e25e88a8f90f5c39b2f5dd878149af59671f", size = 4694874, upload-time = "2026-05-18T19:18:55.139Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b5/3aad415a9a25b822e783f15deeb4dffccf5113030f1afa2222dd929313d9/lxml-6.1.1-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4f469aebd783bb741c2ecb2a681008fd26bfe5c16a9a72ed5467f834e810df2", size = 5244492, upload-time = "2026-05-18T19:19:01.28Z" }, + { url = "https://files.pythonhosted.org/packages/f1/a1/5fcf7eb9904b80086aa47dcf0027de07b1bb990afad2e6823144c368ae04/lxml-6.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:766b010012d59470072c1816b5b6c69f1d243e5db36ea5968e94accf430a4635", size = 5048232, upload-time = "2026-05-18T19:18:12.67Z" }, + { url = "https://files.pythonhosted.org/packages/77/74/1f601b63c7a69fcdf10fa9b148c81da8442204194f6c55509cc485c786b9/lxml-6.1.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b8d812c6011c08b8111a15e54dd990b8923692d80adf35488bee34026c35accf", size = 4777023, upload-time = "2026-05-18T19:18:15.928Z" }, + { url = "https://files.pythonhosted.org/packages/a2/b9/7a78f51aec95b1bf780d78e12705a9f6533284f8693dc5c0e6724fa53d3f/lxml-6.1.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:fe0306bd29505a9177aac19f1877174b0e7422c222a59f70b2cd41633448c3dc", size = 5645773, upload-time = "2026-05-18T19:18:23.223Z" }, + { url = "https://files.pythonhosted.org/packages/a5/6e/98a7b7ad54e4e74fa1f20fff776913980619d0ebe5558232d7da6580bdd8/lxml-6.1.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5ba186ad207446c65d3bb3d3e0412b032b1d9f595e59861e2354798c5703d955", size = 5233088, upload-time = "2026-05-18T19:18:31.433Z" }, + { url = "https://files.pythonhosted.org/packages/65/d1/bc0ed2427bf609f2ee10da303a6a226f9c8bce94f945dc29a32ce55de6e4/lxml-6.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aa366a1e55b8ebfe8ca8ddc3cfe75c8ebade181aeb0f661d0cb05986b647f72a", size = 5260995, upload-time = "2026-05-18T19:18:37.091Z" }, + { url = "https://files.pythonhosted.org/packages/69/8b/6772e1a4b513fc50a8d931f19edde0e13ae6918510a1e13ff67864f3e5ed/lxml-6.1.1-cp312-cp312-win32.whl", hash = "sha256:126c93f7f56f0eda92f6d8c619edc463a4f23d9252f1c9d0405a76f25fa9f11a", size = 3596382, upload-time = "2026-05-18T19:17:18.37Z" }, + { url = "https://files.pythonhosted.org/packages/1b/89/45198e9624762af2dfd2cb8782598477ceb29f6e59caab560388ae1f4ec1/lxml-6.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:26e6eda8d38c1fcab1090dd196ee87cbd13788e531937610e2589085de074e77", size = 3997255, upload-time = "2026-05-18T19:17:56.781Z" }, + { url = "https://files.pythonhosted.org/packages/90/a9/7a54b6834088d9ae528a7b780584ba6a39a9457b0ac330479f20ffbc9449/lxml-6.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:6540377fbd53fe1b629172288c464fb18db11ce1fa7dc15891da10aa9dcc3e7f", size = 3659610, upload-time = "2026-05-19T19:22:50.843Z" }, + { url = "https://files.pythonhosted.org/packages/a5/eb/7e6f37c5584ccbb2ff267f56fd0339016938c1c8684cfefab9b33ffc2f36/lxml-6.1.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:68a9198d0fc122d14bb76837de9aa80cf84caed990b5b237f532ed87d3706736", size = 8559780, upload-time = "2026-05-18T19:17:57.661Z" }, + { url = "https://files.pythonhosted.org/packages/a1/36/587c2521cf23a2cd6c9c22108aa7528f683a1f195ed7ccd23a4b1786ad36/lxml-6.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7d47866cb32fb503450b6edc9df355d10dc49836af2e89901bd6ac6b0896d9d9", size = 4618006, upload-time = "2026-05-18T19:18:04.452Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ca/ab7bfe2bf4c972af5e7878262845ead3a24a929a9b04bc11c7c1ece6c82a/lxml-6.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb7c9811bfaa8b1ed5ed319f5d370dfbcaa59d52ea64be2a5a85e18195930354", size = 4924139, upload-time = "2026-05-18T19:19:04.873Z" }, + { url = "https://files.pythonhosted.org/packages/6b/55/a0c72851dfee5ecc689f949723a73dea457758912542cb955b108eaf0d8f/lxml-6.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:762ff394d5bd56da0cf034a23dcce4e13923f15321a2adfa2ac00201dc6d3fca", size = 5082329, upload-time = "2026-05-18T19:19:09.728Z" }, + { url = "https://files.pythonhosted.org/packages/f0/b6/0608f7d61a3b96cc67e5648a3d906e31a5082093e10e7be65b3886289938/lxml-6.1.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a088f287f7d8275a33c07f2cac6c50b9319309a0200a39e7e75d80c707723099", size = 4993564, upload-time = "2026-05-18T19:19:13.608Z" }, + { url = "https://files.pythonhosted.org/packages/4c/66/ae227524b066d29d55bf0b453d93d2d793c40218657d643dcbbca13b8faf/lxml-6.1.1-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e902da4b04e6b52e5893900d4b8ab46068f75f3561f01bf1080957f9fd932ed6", size = 5613467, upload-time = "2026-05-18T19:19:16.228Z" }, + { url = "https://files.pythonhosted.org/packages/a6/76/dbe4a00b50385e40194231dcfe5a12c059de7cf90e89c83407d2b085b719/lxml-6.1.1-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d4962d4c66bf830a7e59ed6cfc17d148149898a3aefa8ec6e59763e6e3ed085", size = 5228304, upload-time = "2026-05-18T19:19:19.354Z" }, + { url = "https://files.pythonhosted.org/packages/1c/01/00b1b8442ed2041793336868ba0b9ea4b13d7da7c085c6404c207a63bf79/lxml-6.1.1-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:581d4c8ae690a6609e64862dd6b7c2489635c2d13907fc2b20f2bc200ff1d21e", size = 5341607, upload-time = "2026-05-18T19:19:22.297Z" }, + { url = "https://files.pythonhosted.org/packages/63/36/1ad29931e9a4638bb707869f01d423a6c815f82152138d1a40dfcfde2b95/lxml-6.1.1-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:876e1ff5930ed8bf295ec5ef9a8155e9b6b1876bbf1deed8b3a8069311875a8f", size = 4700168, upload-time = "2026-05-18T19:19:25.133Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d1/a9536cecf9be18a0dc72d32bead283a2332d1ffebd2dd3ac70ce444686e5/lxml-6.1.1-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9eb9b5a968f6e0f6d640092a567e14529ff8cea2e29d00da6f78a79fa49f013c", size = 5232487, upload-time = "2026-05-18T19:19:28.603Z" }, + { url = "https://files.pythonhosted.org/packages/0e/77/b4fb1e03bf5d130e879214d3100092e386418807fb74dd0adc4b0a48f351/lxml-6.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:aa49e06d94aba782c6a02eecb7e507969e7e7a41b267f1b359bb35585f295d5b", size = 5044231, upload-time = "2026-05-18T19:18:42.246Z" }, + { url = "https://files.pythonhosted.org/packages/26/4c/d00daeeb0a5530c4028a9232aa1b93db3ef4ed2158c116ea73c79a9765b3/lxml-6.1.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:70cdfd80589d59e43e18005dd7244e8895e93db8ab6a620b7e23df5445a4e3d2", size = 4769450, upload-time = "2026-05-18T19:18:48.013Z" }, + { url = "https://files.pythonhosted.org/packages/ed/6a/715a3a8d156ce42f29cf014706f5410c2ff3b02267774110fc23266409fe/lxml-6.1.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:aad9aa39483ed8ec44d6d2e59e5b98a0d80676ef0d92f44bfc374836111f62f5", size = 5635874, upload-time = "2026-05-18T19:18:51.914Z" }, + { url = "https://files.pythonhosted.org/packages/45/37/0544bc21dde2a88f3a17b504e6fc79c0e01d25a33c2f6079724e9e72b9c7/lxml-6.1.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:d49514be2f28d895c38cf9d2b72d7b9a07d00314519f456c0b50b53cfcf4c785", size = 5223987, upload-time = "2026-05-18T19:18:59.715Z" }, + { url = "https://files.pythonhosted.org/packages/4d/f8/f6a5e8185bcb28c2befae3d31f8e3df3b811cb0f47746517a81279fcafe1/lxml-6.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:47402e62c52ff5988c1e8c6c63177f5708bccf48e366dea4e3dcf1e645e04947", size = 5250276, upload-time = "2026-05-18T19:19:03.834Z" }, + { url = "https://files.pythonhosted.org/packages/c7/f2/1a2b9f1b7a49d45495369be7ef9ad05b262930f2eab3e3145706fca8083f/lxml-6.1.1-cp313-cp313-win32.whl", hash = "sha256:3483644525531e1d5762b0c44a8e18b6efba321b6dcf8a8952de10b037618bca", size = 3596903, upload-time = "2026-05-18T19:17:29.863Z" }, + { url = "https://files.pythonhosted.org/packages/e6/99/f4ffb024f238eec2131aaa09f3278fb6129cf892741bf68e1fc1afb8c100/lxml-6.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:a10bd2fd62e8ce916ececb342f348f190724a098c1faa056fdfb2a22ad5e8660", size = 3995869, upload-time = "2026-05-18T19:18:02.596Z" }, + { url = "https://files.pythonhosted.org/packages/d1/53/70eb8c5c6037f27448f1e3c54ebede9545a801ae63f0a7254afca4fe8e45/lxml-6.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:424aa57aca0897eb922aef34395bd1289b3b6f04e6bae20ea123c0c7e333cffc", size = 3658490, upload-time = "2026-05-19T19:22:53.846Z" }, + { url = "https://files.pythonhosted.org/packages/13/e2/2e325795566de01d0d7c3bb57d3c370616b2d07b01214e84eec5d3b10963/lxml-6.1.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:19b7ab10b210b0b3ad7985d9ac4eb66ab09a90b20fe6e2f7ba55d01a234345d0", size = 8577146, upload-time = "2026-05-18T19:18:17.765Z" }, + { url = "https://files.pythonhosted.org/packages/93/cf/5630b5e4be7d2e6bee8efe83865c925221103cf0221303b104ce134b01e2/lxml-6.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c08e5c694306507275f2290073350c4f32e383db15213b2c69e7ff39c1193840", size = 4623866, upload-time = "2026-05-18T19:18:30.669Z" }, + { url = "https://files.pythonhosted.org/packages/d2/51/3904907c063451cf8d4a5c9fe0cad95fa1f4ec57f4e3884fa0731bd7a305/lxml-6.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:74a9717fd0d82effef5c2854f0d917231d5324b5a3eb7275c43ac9fa32f97a14", size = 4950022, upload-time = "2026-05-18T19:19:31.958Z" }, + { url = "https://files.pythonhosted.org/packages/94/cd/9c7611a51c37a2830928405817cc5d56a97f64fab83cc3f628748b135749/lxml-6.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efe0374196335f93b53269acd811b944f2e6bdc88e8894f214bd636455484909", size = 5086695, upload-time = "2026-05-18T19:19:34.764Z" }, + { url = "https://files.pythonhosted.org/packages/da/d6/24e3b5906abb0b674ff2ae195bc3ce59708df2bcd17cf17703b2d7dd643a/lxml-6.1.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac931cdc9442c1763b8a8f6cd62c0c938737eafc5be75eff88df55fc73bc0d00", size = 5031642, upload-time = "2026-05-18T19:19:37.771Z" }, + { url = "https://files.pythonhosted.org/packages/2d/db/6ec54f99019838bff54785c51da07f189eb4676861c5f2730962b0d8d665/lxml-6.1.1-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:aee395f5d0927f947758b4ec119fd5fc8ec71f07a1c5c52077b30b04c0fa6955", size = 5647338, upload-time = "2026-05-18T19:19:40.553Z" }, + { url = "https://files.pythonhosted.org/packages/42/3d/ef4dcfffd22d27a61805d8ed9f7fb888495bc6aa88648fa07c1eaa5586b6/lxml-6.1.1-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9395002973c827b3ed67db77e6ec09f092919a587022174554096a269378fb13", size = 5239528, upload-time = "2026-05-18T19:19:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/62/bb/37fb3f0dff146bdcfa78eec47879273820b2a0bf350ec236ce14bd0b1c26/lxml-6.1.1-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:73bc2086f141224ebddb7fc5c6a36ca58b31b94b561e1dfe8e073e3270fad1e7", size = 5350730, upload-time = "2026-05-18T19:19:46.307Z" }, + { url = "https://files.pythonhosted.org/packages/90/42/43253f168388df4fae1f38c01df36ddb9bee39e2048167b54cdcbae85ea3/lxml-6.1.1-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:3779def59032b81e44a5f70096ef6bf2082f8d901937dca354474ba09782e245", size = 4697530, upload-time = "2026-05-18T19:19:49.889Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a8/c5a8504f81bbdfc8e7094c2c850cdb4ed6777fc4d5ddd9e5ab819f3b0d54/lxml-6.1.1-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:86c89b9d55ebf820ad7c90bc533410f0d098054f293351f10603c0c46ff598f5", size = 5250670, upload-time = "2026-05-18T19:19:53.199Z" }, + { url = "https://files.pythonhosted.org/packages/77/b7/c7e76ab18744d75e21f320ebf9ff9d1ceae2b54dd431ea5a64caf26c9672/lxml-6.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19607c6bbff2a44cf3fe8250abccd20942d3462473e0a721d01d379ed017e462", size = 5084485, upload-time = "2026-05-18T19:19:08.422Z" }, + { url = "https://files.pythonhosted.org/packages/31/31/b35c53f8ef7b7c31cacd23d3638652fff7bcd1deb6eedb709ab43b685908/lxml-6.1.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c6ed5141a5c7507cf3ee76bd363b0d6f801e3321adc35b5d825a23115faa5465", size = 4737635, upload-time = "2026-05-18T19:19:12.321Z" }, + { url = "https://files.pythonhosted.org/packages/d9/06/31f23c813a7fe8e0cb1b175e915b08c9bf4e86d225b210feadbdbe519667/lxml-6.1.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:62aeb7e85b5d60320b9d77eef2e773994e2c0ce10121b277e0a19804e1654a5a", size = 5670681, upload-time = "2026-05-18T19:19:15.001Z" }, + { url = "https://files.pythonhosted.org/packages/1a/bc/ce619bccc89b1fd9ad8a8e1330ee3f3beff9f2ff95b712d7bbcdd6e22fc3/lxml-6.1.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b1b963fd8f5caa68e99dfae060d54de1fe9cba899b8718b44a00cdca53c3e590", size = 5238229, upload-time = "2026-05-18T19:19:18.131Z" }, + { url = "https://files.pythonhosted.org/packages/2f/5d/b329acbbedc0b619ebc2be6cf7ee9ed07e80892c88d4dfd612c33805789a/lxml-6.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:63876be28efefa04a1df615b46770e82042cce445cfdce55160522f57b231ccb", size = 5264191, upload-time = "2026-05-18T19:19:21.118Z" }, + { url = "https://files.pythonhosted.org/packages/d6/85/be36fb1425b30db3c3f9df75fe86343ebffb79e6320bd7f588e25bfeac39/lxml-6.1.1-cp314-cp314-win32.whl", hash = "sha256:7f7a92e8583f06b1fd49d01158143b8461cfcd135dcb10ec807270a3051bd603", size = 3657202, upload-time = "2026-05-18T19:17:39.509Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ce/3cf9a827342269f54d405a6202397de63f07c69cbd6ce7d183a3f0cba1e9/lxml-6.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:b2d444f2e66624d68e9c6b211e28a76e22fff5fcabcfff4deac18b529b7d4137", size = 4064497, upload-time = "2026-05-18T19:18:14.662Z" }, + { url = "https://files.pythonhosted.org/packages/d9/3e/1a957bde8f0760039e627f94699f82caa782c9d838d86c3d28245ee67212/lxml-6.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:3fd9728a2735fda14f4e8235830c86b539e9661e849665bf926d3f867943b4bf", size = 3741991, upload-time = "2026-05-19T19:22:59.111Z" }, + { url = "https://files.pythonhosted.org/packages/78/b2/00ed55b3a2efa4658fb795c38d1090ec9b3e8a6c3683d4441fa517f09c3b/lxml-6.1.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:787b2496d0dbe8cd180984e8d29e3a6f76e7ea34db781cb3bd55e4ba1ef8b4ee", size = 8827545, upload-time = "2026-05-18T19:18:41.193Z" }, + { url = "https://files.pythonhosted.org/packages/c0/73/74573db19baa618d5f266f2407898b087ff6927115b00b71e5fc1b700847/lxml-6.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2c8daa471358dc2d6fcf02165e80ec68f77871a286df95bc5cc3816153b0fd2c", size = 4735736, upload-time = "2026-05-18T19:18:46.761Z" }, + { url = "https://files.pythonhosted.org/packages/16/02/6f7061f4f95f51e545d48e87647c54791d204a4e881be4156e7a26ba5338/lxml-6.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:acd7d70b64c0aae0c7922cca83d288a16f5f6da523637697872253415269baef", size = 4970291, upload-time = "2026-05-18T19:19:56.215Z" }, + { url = "https://files.pythonhosted.org/packages/b0/02/55fc057d8283427dea7d6edb102e7a840239c77a64a983d92f62a304c0e9/lxml-6.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4f0dd2f01f9f8a89f565d000e03abcf0a13d692a346c8d22f628d49af098777a", size = 5102822, upload-time = "2026-05-18T19:19:59.223Z" }, + { url = "https://files.pythonhosted.org/packages/e4/48/8e1cf78d89d66850121d9255a2a24414c98f775da93b90cf976956c24b14/lxml-6.1.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b7e8a14c8634bf6f7a568634cb395305a6d964aeb5b7ee32248094bed3a7e2c", size = 5027923, upload-time = "2026-05-18T19:20:01.549Z" }, + { url = "https://files.pythonhosted.org/packages/ed/00/0632a0647612c8af24d26997b3b961397daa9d5b2581444805933629a4cb/lxml-6.1.1-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:86281fbdd6a8162756f8d603f37e3435bfa38043adb79c6dc6a2dfee065e7525", size = 5595843, upload-time = "2026-05-18T19:20:03.93Z" }, + { url = "https://files.pythonhosted.org/packages/bc/86/ab008a7dc360711b66858d61c80a5979a70a09f2aa2b05d9698df80b803d/lxml-6.1.1-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5d7152ec39ca7c402d8fb9bad86140a15b9503bd0c54484e3f1bbe3dd37ceca", size = 5224515, upload-time = "2026-05-18T19:20:06.381Z" }, + { url = "https://files.pythonhosted.org/packages/75/c6/2702ff375e728e34f56d9a45339a9cf7e4427e917f542225242d63a05afa/lxml-6.1.1-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:88d8cb75b9d82858497a5393e3c63cfbf03035225e4b35a49ed7ccb151e4dc0e", size = 5312511, upload-time = "2026-05-18T19:20:09.308Z" }, + { url = "https://files.pythonhosted.org/packages/b7/57/a5807c98f87a86f10ef9ffab35516df7c0f0c4b6d5d33e9f608ab9c04a31/lxml-6.1.1-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f64ec5397ea6a41fc1b4af0380d79b44a755b5531dcaccd9940fb260dca93038", size = 4639206, upload-time = "2026-05-18T19:20:11.704Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e1/8a0a2c35734812395f4da4eaf33748a7e5705bfb2a58b128da764339d5ec/lxml-6.1.1-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d34bbf07dbc7ca5970671b1512e928991fb5e9d95365636c9b2d8b4f53af405e", size = 5232404, upload-time = "2026-05-18T19:20:14.064Z" }, + { url = "https://files.pythonhosted.org/packages/c2/e2/0e6a4dd5ad84d01d99aa7bae7cfefd4a760a0e0f8176818241de17d9b6c0/lxml-6.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:17e0e18d4ad8adbd0399291bc44845b69d9dd68439a3cdebdf35ff902ec05072", size = 5083769, upload-time = "2026-05-18T19:19:23.758Z" }, + { url = "https://files.pythonhosted.org/packages/a0/7e/161f33d463f6ffc1c7679104b65086dea120080d49dde4d238f015aaee2f/lxml-6.1.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:3ab541146f1f6968c462d6c2ac495148e8cdba2f8347700b2141b6ec5a75bf52", size = 4758936, upload-time = "2026-05-18T19:19:27.256Z" }, + { url = "https://files.pythonhosted.org/packages/f1/fb/2369825e3f6ca99305bf9f7b7085fda91c8b0922a89e54d900974aa3ef85/lxml-6.1.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2a0217714657e023ef4293500f65aa20fce6164c8fd6b08fa5bd4a859fb14b9b", size = 5620296, upload-time = "2026-05-18T19:19:29.993Z" }, + { url = "https://files.pythonhosted.org/packages/30/90/d61e383146f74c5ab683947ea14dc7b82778838ab9b95ea73a23b60d0191/lxml-6.1.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:05a82eb6e1530a64f26225b55cbd178113bd0b5af1c2b625f25e5296742c26d2", size = 5228598, upload-time = "2026-05-18T19:19:33.523Z" }, + { url = "https://files.pythonhosted.org/packages/76/2d/2dafd8149e94b05bb070690efd5bb2680720681e03ff03fc57d2b70a1105/lxml-6.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9e36f163528fc50cbef305f02a5fd66d404edf7049cdaff211dbc2cba5a7013e", size = 5247845, upload-time = "2026-05-18T19:19:36.649Z" }, + { url = "https://files.pythonhosted.org/packages/ce/68/b30e913340c380ddac9580c6e6230991fc37240ec4f64704833e4f3e2769/lxml-6.1.1-cp314-cp314t-win32.whl", hash = "sha256:649dda677cf3bd6ac9ae14007ba0c824ded8ce5808b53fc7431d9140399118c1", size = 3897345, upload-time = "2026-05-18T19:17:33.562Z" }, + { url = "https://files.pythonhosted.org/packages/3c/4e/9eb2af5335545f9fbcd7af57bcf87c6025d31eaa31b14ec184a6c8675328/lxml-6.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:793033d6c5cdf33a573f910d9bea14ef8f5771820411d118da8e1182edb53d5e", size = 4393350, upload-time = "2026-05-18T19:18:10.076Z" }, + { url = "https://files.pythonhosted.org/packages/7f/2c/0f1e93c636720e8a3eb59af2bfda99d98b55891e1c53bc30c2e0e865f01b/lxml-6.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:58bb955caba94e467d2a96da17660d2d704e0675894cba21ab8a775b8621fd1c", size = 3817223, upload-time = "2026-05-19T19:22:56.823Z" }, + { url = "https://files.pythonhosted.org/packages/b5/32/86a3f0f724a3a402d4627937a7fc27b160e45e7012b4adf47f6e1e844511/lxml-6.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:31033dc34636ea6b7d5cc11b1ddbda78a14de858ba9d3e1ed4b69a3085bc521e", size = 3930127, upload-time = "2026-05-18T19:19:02.27Z" }, + { url = "https://files.pythonhosted.org/packages/40/44/d832e82af08723761556d004b1d04d281c09f9a8cecd7d3148548c9941a3/lxml-6.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3893c14c4b6ac5b2d54ba8cf03e99fe5104e592de491f19bd6b82756c09f8004", size = 4210769, upload-time = "2026-05-18T19:20:41.427Z" }, + { url = "https://files.pythonhosted.org/packages/6d/39/0dc5949f759ed7d951e0bb8c2f2d9d7aca1908d22352fa84a8afd2ea54af/lxml-6.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c07da4cebf6889f03ebac8d238f62318e29f495de0aa18a51ea14e61ae907e2e", size = 4318163, upload-time = "2026-05-18T19:20:44.702Z" }, + { url = "https://files.pythonhosted.org/packages/e6/fb/8ab3845fe046ba4cbf74536bcf6801a774b7caf4350de1c5d37f1f0a9e90/lxml-6.1.1-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6f0ce10945fab9c4c06ce14e22af9059d1a87493a9af4501a5b0b9187e21cf2", size = 4250945, upload-time = "2026-05-18T19:20:47.385Z" }, + { url = "https://files.pythonhosted.org/packages/68/1b/7553ab136894374ffae8851ec06f98f511cd8e66246e41b6be059d0a7289/lxml-6.1.1-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f8844cd288697c6425c9beba919302241e3278871dc6519515e72b04e987abcf", size = 4401664, upload-time = "2026-05-18T19:20:50.489Z" }, + { url = "https://files.pythonhosted.org/packages/db/a4/441aee36c6f6b249823d20fd91f9be9ab89d7c5a8ae542a4a4ca6d342d56/lxml-6.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:ed21202aec73cda4d55d1ce57b389aadb90ffb044e6cd1080b8347efe1b1ec84", size = 3508989, upload-time = "2026-05-18T19:18:38.158Z" }, ] [[package]] name = "lxml-html-clean" -version = "0.4.4" +version = "0.4.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "lxml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9a/a4/5c62acfacd69ff4f5db395100f5cfb9b54e7ac8c69a235e4e939fd13f021/lxml_html_clean-0.4.4.tar.gz", hash = "sha256:58f39a9d632711202ed1d6d0b9b47a904e306c85de5761543b90e3e3f736acfb", size = 23899, upload-time = "2026-02-27T09:35:52.911Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/63/195dfdde380a84df309e3bccf4384b034b745dba43426886f7ae623b4fba/lxml_html_clean-0.4.5.tar.gz", hash = "sha256:e2a4c7d5beedd17cd7b484d848a0571e54baa239a4f9df5546e3acba7f990560", size = 24142, upload-time = "2026-05-20T12:17:53.574Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/76/7ffc1d3005cf7749123bc47cb3ea343cd97b0ac2211bab40f57283577d0e/lxml_html_clean-0.4.4-py3-none-any.whl", hash = "sha256:ce2ef506614ecb85ee1c5fe0a2aa45b06a19514ec7949e9c8f34f06925cfabcb", size = 14565, upload-time = "2026-02-27T09:35:51.86Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/6e2b76a6c5dee10397db9c929f0c5066766ec1036046f0335b7ca7ca08b8/lxml_html_clean-0.4.5-py3-none-any.whl", hash = "sha256:c76fcadd1e5bfb9b8bafc2200d51e4e78eb0dad67f56881c21dfb6484c7e7746", size = 14573, upload-time = "2026-05-20T12:17:52.215Z" }, ] [[package]] @@ -3753,7 +3739,7 @@ wheels = [ [[package]] name = "marimo" -version = "0.23.5" +version = "0.23.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -3768,6 +3754,7 @@ dependencies = [ { name = "psutil" }, { name = "pygments" }, { name = "pymdown-extensions" }, + { name = "python-multipart" }, { name = "pyyaml" }, { name = "pyzmq" }, { name = "starlette" }, @@ -3775,9 +3762,9 @@ dependencies = [ { name = "uvicorn" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/45/99/13a8947b4544b1b65ed279a550b2d2a57bbcb24fb6591020542f46dca86b/marimo-0.23.5.tar.gz", hash = "sha256:4eb4c7c12ea1c3e8f25e56a9365a4ae1b0b995faa2bd614c576fa0ea3e0c4cad", size = 38417181, upload-time = "2026-05-04T18:51:26.277Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/fe/a1dec6111a660ee3bd26521c24788e7aef519a31c60cf37f767f29313454/marimo-0.23.8.tar.gz", hash = "sha256:8049df4ad263e7126e959d7d910b014e6181dffe49f540a89c3174e61a446a99", size = 38505767, upload-time = "2026-05-22T16:24:19.881Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/d1/f2300ce52c391c5e0fa86a6292e45cb419898626b2900c2c1b44a3ba7162/marimo-0.23.5-py3-none-any.whl", hash = "sha256:9cf039ba5a11a5a3035ed0a165d3f84e2d67a4046422925b77fde154062c5112", size = 38835716, upload-time = "2026-05-04T18:51:21.64Z" }, + { url = "https://files.pythonhosted.org/packages/2d/76/51b57a2e521b9110f25ec935f2774412e2f3d678b3fdea7841987244fb2c/marimo-0.23.8-py3-none-any.whl", hash = "sha256:99a4035d035fb320c8f2dcefc2213e0d64e9de13e989bc3f2a973b19dc40542a", size = 38938839, upload-time = "2026-05-22T16:24:16.102Z" }, ] [[package]] @@ -3791,14 +3778,14 @@ wheels = [ [[package]] name = "markdown-it-py" -version = "4.0.0" +version = "4.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, ] [[package]] @@ -3962,14 +3949,14 @@ wheels = [ [[package]] name = "matplotlib-inline" -version = "0.2.1" +version = "0.2.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/c0/9f7c9a46090390368a4d7bcb76bb87a4a36c421e4c0792cdb53486ffac7a/matplotlib_inline-0.2.2.tar.gz", hash = "sha256:72f3fe8fce36b70d4a5b612f899090cd0401deddc4ea90e1572b9f4bfb058c79", size = 8150, upload-time = "2026-05-08T17:33:33.49Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, + { url = "https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl", hash = "sha256:3c821cf1c209f59fb2d2d64abbf5b23b67bcb2210d663f9918dd851c6da1fcf6", size = 9534, upload-time = "2026-05-08T17:33:32.055Z" }, ] [[package]] @@ -3986,7 +3973,7 @@ wheels = [ [[package]] name = "mcp" -version = "1.27.0" +version = "1.27.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -4004,21 +3991,21 @@ dependencies = [ { name = "typing-inspection" }, { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/eb/c0cfc62075dc6e1ec1c64d352ae09ac051d9334311ed226f1f425312848a/mcp-1.27.0.tar.gz", hash = "sha256:d3dc35a7eec0d458c1da4976a48f982097ddaab87e278c5511d5a4a56e852b83", size = 607509, upload-time = "2026-04-02T14:48:08.88Z" } +sdist = { url = "https://files.pythonhosted.org/packages/38/83/d1efe7c2980d8a3afa476f4e3d42d53dd54c0ab94c27bee5d755b45c8b73/mcp-1.27.1.tar.gz", hash = "sha256:0f47e1820f8f8f941466b39749eb1d1839a04caddca2bc60e9d46e8a99914924", size = 608458, upload-time = "2026-05-08T16:50:12.601Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/46/f6b4ad632c67ef35209a66127e4bddc95759649dd595f71f13fba11bdf9a/mcp-1.27.0-py3-none-any.whl", hash = "sha256:5ce1fa81614958e267b21fb2aa34e0aea8e2c6ede60d52aba45fd47246b4d741", size = 215967, upload-time = "2026-04-02T14:48:07.24Z" }, + { url = "https://files.pythonhosted.org/packages/fd/73/42d9596facebdb533b7f0b86c1b0364ef350d1f8ba78b1052e8a58b48b65/mcp-1.27.1-py3-none-any.whl", hash = "sha256:1af3c4203b329430fde7a87b4fcb6392a041f5cb851fd68fc674016ab4e7c06f", size = 216260, upload-time = "2026-05-08T16:50:10.547Z" }, ] [[package]] name = "mdit-py-plugins" -version = "0.5.0" +version = "0.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b2/fd/a756d36c0bfba5f6e39a1cdbdbfdd448dc02692467d83816dff4592a1ebc/mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6", size = 44655, upload-time = "2025-08-11T07:25:49.083Z" } +sdist = { url = "https://files.pythonhosted.org/packages/59/fc/f8d0863f8862f25602c0404d75568e89fb6b4109804645e5cdfb1be5cf56/mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0", size = 56114, upload-time = "2026-05-13T09:03:38.91Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl", hash = "sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f", size = 57205, upload-time = "2025-08-11T07:25:47.597Z" }, + { url = "https://files.pythonhosted.org/packages/a5/69/6da5581c6a7fede7dc261bf4e67d6adca4196f176b43288b55b3db395b6e/mdit_py_plugins-0.6.1-py3-none-any.whl", hash = "sha256:214c82fb2ac524472ab6a5bcab1de80f73b50443e187f401bfd77efbc7c6481d", size = 66663, upload-time = "2026-05-13T09:03:37.76Z" }, ] [[package]] @@ -4041,11 +4028,11 @@ wheels = [ [[package]] name = "more-itertools" -version = "11.0.2" +version = "11.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/f7/139d22fef48ac78127d18e01d80cf1be40236ae489769d17f35c3d425293/more_itertools-11.0.2.tar.gz", hash = "sha256:392a9e1e362cbc106a2457d37cabf9b36e5e12efd4ebff1654630e76597df804", size = 144659, upload-time = "2026-04-09T15:01:33.297Z" } +sdist = { url = "https://files.pythonhosted.org/packages/de/1d/f4da6f02cdffe04d6362210b807146a26044c88d839208aec273bb0d9184/more_itertools-11.1.0.tar.gz", hash = "sha256:48e8f4d9e7e5878571ecf6f2b4e57634f93cd474cc8cfbd2376f2d11b396e30d", size = 145772, upload-time = "2026-05-22T14:14:29.909Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/98/6af411189d9413534c3eb691182bff1f5c6d44ed2f93f2edfe52a1bbceb8/more_itertools-11.0.2-py3-none-any.whl", hash = "sha256:6e35b35f818b01f691643c6c611bc0902f2e92b46c18fffa77ae1e7c46e912e4", size = 71939, upload-time = "2026-04-09T15:01:32.21Z" }, + { url = "https://files.pythonhosted.org/packages/e8/3d/1087453384dbde46a8c7f9356eead2c58be8a7bf156bca40243377c85715/more_itertools-11.1.0-py3-none-any.whl", hash = "sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192", size = 72226, upload-time = "2026-05-22T14:14:28.824Z" }, ] [[package]] @@ -4327,7 +4314,7 @@ wheels = [ [[package]] name = "myst-parser" -version = "5.0.0" +version = "5.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docutils" }, @@ -4337,18 +4324,18 @@ dependencies = [ { name = "pyyaml" }, { name = "sphinx" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/fa/7b45eef11b7971f0beb29d27b7bfe0d747d063aa29e170d9edd004733c8a/myst_parser-5.0.0.tar.gz", hash = "sha256:f6f231452c56e8baa662cc352c548158f6a16fcbd6e3800fc594978002b94f3a", size = 98535, upload-time = "2026-01-15T09:08:18.036Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/dc/603751677fff302f34396e206b610f556a59d7fe58b9a2145f54e96b48e8/myst_parser-5.1.0.tar.gz", hash = "sha256:ab69322dc6719dcc7f296479dbb70181b66df6ed315064f92dbc85c0e1bf2f02", size = 101182, upload-time = "2026-05-13T09:38:19.361Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/ac/686789b9145413f1a61878c407210e41bfdb097976864e0913078b24098c/myst_parser-5.0.0-py3-none-any.whl", hash = "sha256:ab31e516024918296e169139072b81592336f2fef55b8986aa31c9f04b5f7211", size = 84533, upload-time = "2026-01-15T09:08:16.788Z" }, + { url = "https://files.pythonhosted.org/packages/09/dc/f3dfb7488b770f3f67e6545085bf2abea5172e88f57b8ad25ef860ca704c/myst_parser-5.1.0-py3-none-any.whl", hash = "sha256:9c91c52b3cdb4d94a6506e4fab4e2f296c7623a0da0dcbe6de1565c3dad67a8a", size = 85817, upload-time = "2026-05-13T09:38:17.904Z" }, ] [[package]] name = "narwhals" -version = "2.20.0" +version = "2.21.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e9/f3/257adc69a71011b4c8cda321b00f02c5bf1980ae38ffd05a58d9632d4de8/narwhals-2.20.0.tar.gz", hash = "sha256:c10994975fa7dc5a68c2cffcddbd5908fc8ebb2d463c5bab085309c0ee1f551e", size = 627848, upload-time = "2026-04-20T12:11:45.427Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/a0/6198c56d42ef2f3c6ed0c42ba30dbcefdc86a91262d7d449010770ae085b/narwhals-2.21.2.tar.gz", hash = "sha256:5c5b2d0b47aef7c73ea412cfcbcd467f2f2d5be73e3c2ab19d78f4a97718790a", size = 632176, upload-time = "2026-05-16T08:49:08.314Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/69/f24d3d1c38ad69e256138b4ec2452a8c7cf66be49dc214771ae99dd4f0a0/narwhals-2.20.0-py3-none-any.whl", hash = "sha256:16e750ea5507d4ba6e8d03455b5f93a535e0405976561baea235bca5dc9f475d", size = 449373, upload-time = "2026-04-20T12:11:43.596Z" }, + { url = "https://files.pythonhosted.org/packages/1d/77/928ea2e70641ca177a11140062cc5840d421795f2e82749d408d0cce900a/narwhals-2.21.2-py3-none-any.whl", hash = "sha256:7bb57c3700486039215455b9bf2d64261915cc0fd845cc30272d631df696b251", size = 451201, upload-time = "2026-05-16T08:49:05.536Z" }, ] [[package]] @@ -4426,7 +4413,7 @@ wheels = [ [[package]] name = "nicegui" -version = "3.12.0" +version = "3.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiofiles" }, @@ -4436,6 +4423,7 @@ dependencies = [ { name = "fastapi" }, { name = "h11" }, { name = "httpx" }, + { name = "idna" }, { name = "ifaddr" }, { name = "itsdangerous" }, { name = "jinja2" }, @@ -4455,9 +4443,9 @@ dependencies = [ { name = "uvicorn", extra = ["standard"] }, { name = "watchfiles" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d3/10/48a16e8c2a2d4c1b1efdcb9c3898a50db3d016fab86a972e3fe083bce206/nicegui-3.12.0.tar.gz", hash = "sha256:f90d091a2b4855e090a9bad8d8ad10396799f7c7792a0846447a11aea396ead3", size = 19602588, upload-time = "2026-05-12T14:22:02.317Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/80/7e4bcd1d47b3ee8d0f84b2c9b3a6f6843dda62e3d2daddba904dde846ccd/nicegui-3.12.1.tar.gz", hash = "sha256:5aaf1582db22a4b57fdc2ad41603eaf73c937d3ed68e1d8ef13da30496200eec", size = 19602119, upload-time = "2026-05-21T13:42:34.794Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/3d/222879aa74d30cd7fe057df5638623b9818de535064e0c8f02117e0b69ce/nicegui-3.12.0-py3-none-any.whl", hash = "sha256:1bdc6ef423f091afddc05ea3bcf5beac1b3c3d27081b0df243aaccf03e612434", size = 20186344, upload-time = "2026-05-12T14:22:06.794Z" }, + { url = "https://files.pythonhosted.org/packages/7d/75/ee4773ff227194b0786698f4340bc0a9ee5d3b3139922d3d65894f428c14/nicegui-3.12.1-py3-none-any.whl", hash = "sha256:9902ae42b612d5ec1d1b200e5eba808d7f614921752fc5bef4a3b4568c9e9906", size = 20186422, upload-time = "2026-05-21T13:42:40.058Z" }, ] [package.optional-dependencies] @@ -4527,81 +4515,81 @@ uv = [ [[package]] name = "numpy" -version = "2.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/c6/4218570d8c8ecc9704b5157a3348e486e84ef4be0ed3e38218ab473c83d2/numpy-2.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f983334aea213c99992053ede6168500e5f086ce74fbc4acc3f2b00f5762e9db", size = 16976799, upload-time = "2026-03-29T13:18:15.438Z" }, - { url = "https://files.pythonhosted.org/packages/dd/92/b4d922c4a5f5dab9ed44e6153908a5c665b71acf183a83b93b690996e39b/numpy-2.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72944b19f2324114e9dc86a159787333b77874143efcf89a5167ef83cfee8af0", size = 14971552, upload-time = "2026-03-29T13:18:18.606Z" }, - { url = "https://files.pythonhosted.org/packages/8a/dc/df98c095978fa6ee7b9a9387d1d58cbb3d232d0e69ad169a4ce784bde4fd/numpy-2.4.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:86b6f55f5a352b48d7fbfd2dbc3d5b780b2d79f4d3c121f33eb6efb22e9a2015", size = 5476566, upload-time = "2026-03-29T13:18:21.532Z" }, - { url = "https://files.pythonhosted.org/packages/28/34/b3fdcec6e725409223dd27356bdf5a3c2cc2282e428218ecc9cb7acc9763/numpy-2.4.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:ba1f4fc670ed79f876f70082eff4f9583c15fb9a4b89d6188412de4d18ae2f40", size = 6806482, upload-time = "2026-03-29T13:18:23.634Z" }, - { url = "https://files.pythonhosted.org/packages/68/62/63417c13aa35d57bee1337c67446761dc25ea6543130cf868eace6e8157b/numpy-2.4.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a87ec22c87be071b6bdbd27920b129b94f2fc964358ce38f3822635a3e2e03d", size = 15973376, upload-time = "2026-03-29T13:18:26.677Z" }, - { url = "https://files.pythonhosted.org/packages/cf/c5/9fcb7e0e69cef59cf10c746b84f7d58b08bc66a6b7d459783c5a4f6101a6/numpy-2.4.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3775294accfdd75f32c74ae39fcba920c9a378a2fc18a12b6820aa8c1fb502", size = 16925137, upload-time = "2026-03-29T13:18:30.14Z" }, - { url = "https://files.pythonhosted.org/packages/7e/43/80020edacb3f84b9efdd1591120a4296462c23fd8db0dde1666f6ef66f13/numpy-2.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d4e437e295f18ec29bc79daf55e8a47a9113df44d66f702f02a293d93a2d6dd", size = 17329414, upload-time = "2026-03-29T13:18:33.733Z" }, - { url = "https://files.pythonhosted.org/packages/fd/06/af0658593b18a5f73532d377188b964f239eb0894e664a6c12f484472f97/numpy-2.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6aa3236c78803afbcb255045fbef97a9e25a1f6c9888357d205ddc42f4d6eba5", size = 18658397, upload-time = "2026-03-29T13:18:37.511Z" }, - { url = "https://files.pythonhosted.org/packages/e6/ce/13a09ed65f5d0ce5c7dd0669250374c6e379910f97af2c08c57b0608eee4/numpy-2.4.4-cp311-cp311-win32.whl", hash = "sha256:30caa73029a225b2d40d9fae193e008e24b2026b7ee1a867b7ee8d96ca1a448e", size = 6239499, upload-time = "2026-03-29T13:18:40.372Z" }, - { url = "https://files.pythonhosted.org/packages/bd/63/05d193dbb4b5eec1eca73822d80da98b511f8328ad4ae3ca4caf0f4db91d/numpy-2.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:6bbe4eb67390b0a0265a2c25458f6b90a409d5d069f1041e6aff1e27e3d9a79e", size = 12614257, upload-time = "2026-03-29T13:18:42.95Z" }, - { url = "https://files.pythonhosted.org/packages/87/c5/8168052f080c26fa984c413305012be54741c9d0d74abd7fbeeccae3889f/numpy-2.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:fcfe2045fd2e8f3cb0ce9d4ba6dba6333b8fa05bb8a4939c908cd43322d14c7e", size = 10486775, upload-time = "2026-03-29T13:18:45.835Z" }, - { url = "https://files.pythonhosted.org/packages/28/05/32396bec30fb2263770ee910142f49c1476d08e8ad41abf8403806b520ce/numpy-2.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15716cfef24d3a9762e3acdf87e27f58dc823d1348f765bbea6bef8c639bfa1b", size = 16689272, upload-time = "2026-03-29T13:18:49.223Z" }, - { url = "https://files.pythonhosted.org/packages/c5/f3/a983d28637bfcd763a9c7aafdb6d5c0ebf3d487d1e1459ffdb57e2f01117/numpy-2.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23cbfd4c17357c81021f21540da84ee282b9c8fba38a03b7b9d09ba6b951421e", size = 14699573, upload-time = "2026-03-29T13:18:52.629Z" }, - { url = "https://files.pythonhosted.org/packages/9b/fd/e5ecca1e78c05106d98028114f5c00d3eddb41207686b2b7de3e477b0e22/numpy-2.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b3b60bb7cba2c8c81837661c488637eee696f59a877788a396d33150c35d842", size = 5204782, upload-time = "2026-03-29T13:18:55.579Z" }, - { url = "https://files.pythonhosted.org/packages/de/2f/702a4594413c1a8632092beae8aba00f1d67947389369b3777aed783fdca/numpy-2.4.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e4a010c27ff6f210ff4c6ef34394cd61470d01014439b192ec22552ee867f2a8", size = 6552038, upload-time = "2026-03-29T13:18:57.769Z" }, - { url = "https://files.pythonhosted.org/packages/7f/37/eed308a8f56cba4d1fdf467a4fc67ef4ff4bf1c888f5fc980481890104b1/numpy-2.4.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9e75681b59ddaa5e659898085ae0eaea229d054f2ac0c7e563a62205a700121", size = 15670666, upload-time = "2026-03-29T13:19:00.341Z" }, - { url = "https://files.pythonhosted.org/packages/0a/0d/0e3ecece05b7a7e87ab9fb587855548da437a061326fff64a223b6dcb78a/numpy-2.4.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:81f4a14bee47aec54f883e0cad2d73986640c1590eb9bfaaba7ad17394481e6e", size = 16645480, upload-time = "2026-03-29T13:19:03.63Z" }, - { url = "https://files.pythonhosted.org/packages/34/49/f2312c154b82a286758ee2f1743336d50651f8b5195db18cdb63675ff649/numpy-2.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62d6b0f03b694173f9fcb1fb317f7222fd0b0b103e784c6549f5e53a27718c44", size = 17020036, upload-time = "2026-03-29T13:19:07.428Z" }, - { url = "https://files.pythonhosted.org/packages/7b/e9/736d17bd77f1b0ec4f9901aaec129c00d59f5d84d5e79bba540ef12c2330/numpy-2.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbc356aae7adf9e6336d336b9c8111d390a05df88f1805573ebb0807bd06fd1d", size = 18368643, upload-time = "2026-03-29T13:19:10.775Z" }, - { url = "https://files.pythonhosted.org/packages/63/f6/d417977c5f519b17c8a5c3bc9e8304b0908b0e21136fe43bf628a1343914/numpy-2.4.4-cp312-cp312-win32.whl", hash = "sha256:0d35aea54ad1d420c812bfa0385c71cd7cc5bcf7c65fed95fc2cd02fe8c79827", size = 5961117, upload-time = "2026-03-29T13:19:13.464Z" }, - { url = "https://files.pythonhosted.org/packages/2d/5b/e1deebf88ff431b01b7406ca3583ab2bbb90972bbe1c568732e49c844f7e/numpy-2.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:b5f0362dc928a6ecd9db58868fca5e48485205e3855957bdedea308f8672ea4a", size = 12320584, upload-time = "2026-03-29T13:19:16.155Z" }, - { url = "https://files.pythonhosted.org/packages/58/89/e4e856ac82a68c3ed64486a544977d0e7bdd18b8da75b78a577ca31c4395/numpy-2.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:846300f379b5b12cc769334464656bc882e0735d27d9726568bc932fdc49d5ec", size = 10221450, upload-time = "2026-03-29T13:19:18.994Z" }, - { url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933, upload-time = "2026-03-29T13:19:22.47Z" }, - { url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532, upload-time = "2026-03-29T13:19:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661, upload-time = "2026-03-29T13:19:28.31Z" }, - { url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539, upload-time = "2026-03-29T13:19:30.97Z" }, - { url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806, upload-time = "2026-03-29T13:19:33.887Z" }, - { url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682, upload-time = "2026-03-29T13:19:37.336Z" }, - { url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810, upload-time = "2026-03-29T13:19:40.963Z" }, - { url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394, upload-time = "2026-03-29T13:19:44.859Z" }, - { url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556, upload-time = "2026-03-29T13:19:47.661Z" }, - { url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311, upload-time = "2026-03-29T13:19:50.67Z" }, - { url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060, upload-time = "2026-03-29T13:19:54.229Z" }, - { url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302, upload-time = "2026-03-29T13:19:57.585Z" }, - { url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407, upload-time = "2026-03-29T13:20:00.601Z" }, - { url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631, upload-time = "2026-03-29T13:20:02.855Z" }, - { url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691, upload-time = "2026-03-29T13:20:06.004Z" }, - { url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241, upload-time = "2026-03-29T13:20:09.417Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767, upload-time = "2026-03-29T13:20:13.126Z" }, - { url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169, upload-time = "2026-03-29T13:20:17.096Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477, upload-time = "2026-03-29T13:20:20.195Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487, upload-time = "2026-03-29T13:20:22.946Z" }, - { url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002, upload-time = "2026-03-29T13:20:25.909Z" }, - { url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353, upload-time = "2026-03-29T13:20:29.504Z" }, - { url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914, upload-time = "2026-03-29T13:20:33.547Z" }, - { url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005, upload-time = "2026-03-29T13:20:36.45Z" }, - { url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974, upload-time = "2026-03-29T13:20:39.014Z" }, - { url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591, upload-time = "2026-03-29T13:20:42.146Z" }, - { url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700, upload-time = "2026-03-29T13:20:46.204Z" }, - { url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781, upload-time = "2026-03-29T13:20:50.242Z" }, - { url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959, upload-time = "2026-03-29T13:20:54.019Z" }, - { url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768, upload-time = "2026-03-29T13:20:56.912Z" }, - { url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181, upload-time = "2026-03-29T13:20:59.548Z" }, - { url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035, upload-time = "2026-03-29T13:21:02.524Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958, upload-time = "2026-03-29T13:21:05.671Z" }, - { url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020, upload-time = "2026-03-29T13:21:08.635Z" }, - { url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758, upload-time = "2026-03-29T13:21:10.949Z" }, - { url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948, upload-time = "2026-03-29T13:21:14.047Z" }, - { url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325, upload-time = "2026-03-29T13:21:17.561Z" }, - { url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883, upload-time = "2026-03-29T13:21:21.106Z" }, - { url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474, upload-time = "2026-03-29T13:21:24.828Z" }, - { url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500, upload-time = "2026-03-29T13:21:28.205Z" }, - { url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755, upload-time = "2026-03-29T13:21:31.107Z" }, - { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643, upload-time = "2026-03-29T13:21:34.339Z" }, - { url = "https://files.pythonhosted.org/packages/6b/33/8fae8f964a4f63ed528264ddf25d2b683d0b663e3cba26961eb838a7c1bd/numpy-2.4.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:58c8b5929fcb8287cbd6f0a3fae19c6e03a5c48402ae792962ac465224a629a4", size = 16854491, upload-time = "2026-03-29T13:21:38.03Z" }, - { url = "https://files.pythonhosted.org/packages/bc/d0/1aabee441380b981cf8cdda3ae7a46aa827d1b5a8cce84d14598bc94d6d9/numpy-2.4.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eea7ac5d2dce4189771cedb559c738a71512768210dc4e4753b107a2048b3d0e", size = 14895830, upload-time = "2026-03-29T13:21:41.509Z" }, - { url = "https://files.pythonhosted.org/packages/a5/b8/aafb0d1065416894fccf4df6b49ef22b8db045187949545bced89c034b8e/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:51fc224f7ca4d92656d5a5eb315f12eb5fe2c97a66249aa7b5f562528a3be38c", size = 5400927, upload-time = "2026-03-29T13:21:44.747Z" }, - { url = "https://files.pythonhosted.org/packages/d6/77/063baa20b08b431038c7f9ff5435540c7b7265c78cf56012a483019ca72d/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:28a650663f7314afc3e6ec620f44f333c386aad9f6fc472030865dc0ebb26ee3", size = 6715557, upload-time = "2026-03-29T13:21:47.406Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a8/379542d45a14f149444c5c4c4e7714707239ce9cc1de8c2803958889da14/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19710a9ca9992d7174e9c52f643d4272dcd1558c5f7af7f6f8190f633bd651a7", size = 15804253, upload-time = "2026-03-29T13:21:50.753Z" }, - { url = "https://files.pythonhosted.org/packages/a2/c8/f0a45426d6d21e7ea3310a15cf90c43a14d9232c31a837702dba437f3373/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b2aec6af35c113b05695ebb5749a787acd63cafc83086a05771d1e1cd1e555f", size = 16753552, upload-time = "2026-03-29T13:21:54.344Z" }, - { url = "https://files.pythonhosted.org/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119", size = 12509075, upload-time = "2026-03-29T13:21:57.644Z" }, +version = "2.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194, upload-time = "2026-05-18T23:33:13.503Z" }, + { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111, upload-time = "2026-05-18T23:33:17.795Z" }, + { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159, upload-time = "2026-05-18T23:33:20.654Z" }, + { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936, upload-time = "2026-05-18T23:33:22.987Z" }, + { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692, upload-time = "2026-05-18T23:33:26.62Z" }, + { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164, upload-time = "2026-05-18T23:33:29.955Z" }, + { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877, upload-time = "2026-05-18T23:33:34.724Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487, upload-time = "2026-05-18T23:33:38.217Z" }, + { url = "https://files.pythonhosted.org/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945, upload-time = "2026-05-18T23:33:41.331Z" }, + { url = "https://files.pythonhosted.org/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406, upload-time = "2026-05-18T23:33:44.131Z" }, + { url = "https://files.pythonhosted.org/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528, upload-time = "2026-05-18T23:33:50.725Z" }, + { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" }, + { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" }, + { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" }, + { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" }, + { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" }, + { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" }, + { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" }, + { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" }, + { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" }, + { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" }, + { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" }, + { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" }, + { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" }, + { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" }, + { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" }, + { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" }, + { url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458, upload-time = "2026-05-18T23:35:38.353Z" }, + { url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559, upload-time = "2026-05-18T23:35:42.14Z" }, + { url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716, upload-time = "2026-05-18T23:35:45.377Z" }, + { url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947, upload-time = "2026-05-18T23:35:47.926Z" }, + { url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197, upload-time = "2026-05-18T23:35:50.863Z" }, + { url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245, upload-time = "2026-05-18T23:35:54.752Z" }, + { url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587, upload-time = "2026-05-18T23:35:58.355Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226, upload-time = "2026-05-18T23:36:02.845Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196, upload-time = "2026-05-18T23:36:05.92Z" }, + { url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334, upload-time = "2026-05-18T23:36:09.107Z" }, + { url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678, upload-time = "2026-05-18T23:36:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672, upload-time = "2026-05-18T23:36:16.473Z" }, + { url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731, upload-time = "2026-05-18T23:36:19.767Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805, upload-time = "2026-05-18T23:36:22.266Z" }, + { url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496, upload-time = "2026-05-18T23:36:25.713Z" }, + { url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616, upload-time = "2026-05-18T23:36:29.652Z" }, + { url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145, upload-time = "2026-05-18T23:36:33.449Z" }, + { url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813, upload-time = "2026-05-18T23:36:37.369Z" }, + { url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982, upload-time = "2026-05-18T23:36:40.817Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908, upload-time = "2026-05-18T23:36:43.996Z" }, + { url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867, upload-time = "2026-05-18T23:36:47.114Z" }, + { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511, upload-time = "2026-05-18T23:36:50.673Z" }, + { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064, upload-time = "2026-05-18T23:36:53.879Z" }, + { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157, upload-time = "2026-05-18T23:36:57.194Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728, upload-time = "2026-05-18T23:36:59.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374, upload-time = "2026-05-18T23:37:02.674Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286, upload-time = "2026-05-18T23:37:06.327Z" }, + { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" }, ] [[package]] @@ -4671,83 +4659,82 @@ wheels = [ [[package]] name = "opentelemetry-api" -version = "1.41.1" +version = "1.42.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "importlib-metadata" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/fc/b7564cbef36601aef0d6c9bc01f7badb64be8e862c2e1c3c5c3b43b53e4f/opentelemetry_api-1.41.1.tar.gz", hash = "sha256:0ad1814d73b875f84494387dae86ce0b12c68556331ce6ce8fe789197c949621", size = 71416, upload-time = "2026-04-24T13:15:38.262Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b4/1c/125e1c936c0873796771b7f04f6c93b9f1bf5d424cea90fda94a99f61da8/opentelemetry_api-1.42.1.tar.gz", hash = "sha256:56c63bea9f77b62856be8c47600474acad853b2924b99b1687c4cb6297166716", size = 72296, upload-time = "2026-05-21T16:32:49.335Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/59/3e7118ed140f76b0982ba4321bdaed1997a0473f9720de2d10788a577033/opentelemetry_api-1.41.1-py3-none-any.whl", hash = "sha256:a22df900e75c76dc08440710e51f52f1aa6b451b429298896023e60db5b3139f", size = 69007, upload-time = "2026-04-24T13:15:15.662Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ca/9520cc1f3dfbbd03ac5903bbf55833e257bc64b1cf30fa8b0d6df374d821/opentelemetry_api-1.42.1-py3-none-any.whl", hash = "sha256:51a69edacadbc03a8950ace1c4c21099cacc538820ac2c9e36277e78cebba714", size = 61311, upload-time = "2026-05-21T16:32:28.822Z" }, ] [[package]] name = "orjson" -version = "3.11.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/1b/2024d06792d0779f9dbc51531b61c24f76c75b9f4ce05e6f3377a1814cea/orjson-3.11.8.tar.gz", hash = "sha256:96163d9cdc5a202703e9ad1b9ae757d5f0ca62f4fa0cc93d1f27b0e180cc404e", size = 5603832, upload-time = "2026-03-31T16:16:27.878Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/41/5aa7fa3b0f4dc6b47dcafc3cea909299c37e40e9972feabc8b6a74e2730d/orjson-3.11.8-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:003646067cc48b7fcab2ae0c562491c9b5d2cbd43f1e5f16d98fd118c5522d34", size = 229229, upload-time = "2026-03-31T16:14:50.424Z" }, - { url = "https://files.pythonhosted.org/packages/0a/d7/57e7f2458e0a2c41694f39fc830030a13053a84f837a5b73423dca1f0938/orjson-3.11.8-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:ed193ce51d77a3830cad399a529cd4ef029968761f43ddc549e1bc62b40d88f8", size = 128871, upload-time = "2026-03-31T16:14:51.888Z" }, - { url = "https://files.pythonhosted.org/packages/53/4a/e0fdb9430983e6c46e0299559275025075568aad5d21dd606faee3703924/orjson-3.11.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30491bc4f862aa15744b9738517454f1e46e56c972a2be87d70d727d5b2a8f8", size = 132104, upload-time = "2026-03-31T16:14:53.142Z" }, - { url = "https://files.pythonhosted.org/packages/08/4a/2025a60ff3f5c8522060cda46612d9b1efa653de66ed2908591d8d82f22d/orjson-3.11.8-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6eda5b8b6be91d3f26efb7dc6e5e68ee805bc5617f65a328587b35255f138bf4", size = 130483, upload-time = "2026-03-31T16:14:54.605Z" }, - { url = "https://files.pythonhosted.org/packages/2d/3c/b9cde05bdc7b2385c66014e0620627da638d3d04e4954416ab48c31196c5/orjson-3.11.8-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee8db7bfb6fe03581bbab54d7c4124a6dd6a7f4273a38f7267197890f094675f", size = 135481, upload-time = "2026-03-31T16:14:55.901Z" }, - { url = "https://files.pythonhosted.org/packages/ff/f2/a8238e7734de7cb589fed319857a8025d509c89dc52fdcc88f39c6d03d5a/orjson-3.11.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d8b5231de76c528a46b57010bbd83fb51e056aa0220a372fd5065e978406f1c", size = 146819, upload-time = "2026-03-31T16:14:57.548Z" }, - { url = "https://files.pythonhosted.org/packages/db/10/dbf1e2a3cafea673b1b4350e371877b759060d6018a998643b7040e5de48/orjson-3.11.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58a4a208a6fbfdb7a7327b8f201c6014f189f721fd55d047cafc4157af1bc62a", size = 132846, upload-time = "2026-03-31T16:14:58.91Z" }, - { url = "https://files.pythonhosted.org/packages/f8/fc/55e667ec9c85694038fcff00573d221b085d50777368ee3d77f38668bf3c/orjson-3.11.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f8952d6d2505c003e8f0224ff7858d341fa4e33fef82b91c4ff0ef070f2393c", size = 133580, upload-time = "2026-03-31T16:15:00.519Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a6/c08c589a9aad0cb46c4831d17de212a2b6901f9d976814321ff8e69e8785/orjson-3.11.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0022bb50f90da04b009ce32c512dc1885910daa7cb10b7b0cba4505b16db82a8", size = 142042, upload-time = "2026-03-31T16:15:01.906Z" }, - { url = "https://files.pythonhosted.org/packages/5c/cc/2f78ea241d52b717d2efc38878615fe80425bf2beb6e68c984dde257a766/orjson-3.11.8-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ff51f9d657d1afb6f410cb435792ce4e1fe427aab23d2fcd727a2876e21d4cb6", size = 423845, upload-time = "2026-03-31T16:15:03.703Z" }, - { url = "https://files.pythonhosted.org/packages/70/07/c17dcf05dd8045457538428a983bf1f1127928df5bf328cb24d2b7cddacb/orjson-3.11.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6dbe9a97bdb4d8d9d5367b52a7c32549bba70b2739c58ef74a6964a6d05ae054", size = 147729, upload-time = "2026-03-31T16:15:05.203Z" }, - { url = "https://files.pythonhosted.org/packages/90/6c/0fb6e8a24e682e0958d71711ae6f39110e4b9cd8cab1357e2a89cb8e1951/orjson-3.11.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5c370674ebabe16c6ccac33ff80c62bf8a6e59439f5e9d40c1f5ab8fd2215b7", size = 136425, upload-time = "2026-03-31T16:15:07.052Z" }, - { url = "https://files.pythonhosted.org/packages/b2/35/4d3cc3a3d616035beb51b24a09bb872942dc452cf2df0c1d11ab35046d9f/orjson-3.11.8-cp311-cp311-win32.whl", hash = "sha256:0e32f7154299f42ae66f13488963269e5eccb8d588a65bc839ed986919fc9fac", size = 131870, upload-time = "2026-03-31T16:15:08.678Z" }, - { url = "https://files.pythonhosted.org/packages/13/26/9fe70f81d16b702f8c3a775e8731b50ad91d22dacd14c7599b60a0941cd1/orjson-3.11.8-cp311-cp311-win_amd64.whl", hash = "sha256:25e0c672a2e32348d2eb33057b41e754091f2835f87222e4675b796b92264f06", size = 127440, upload-time = "2026-03-31T16:15:09.994Z" }, - { url = "https://files.pythonhosted.org/packages/e8/c6/b038339f4145efd2859c1ca53097a52c0bb9cbdd24f947ebe146da1ad067/orjson-3.11.8-cp311-cp311-win_arm64.whl", hash = "sha256:9185589c1f2a944c17e26c9925dcdbc2df061cc4a145395c57f0c51f9b5dbfcd", size = 127399, upload-time = "2026-03-31T16:15:11.412Z" }, - { url = "https://files.pythonhosted.org/packages/01/f6/8d58b32ab32d9215973a1688aebd098252ee8af1766c0e4e36e7831f0295/orjson-3.11.8-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1cd0b77e77c95758f8e1100139844e99f3ccc87e71e6fc8e1c027e55807c549f", size = 229233, upload-time = "2026-03-31T16:15:12.762Z" }, - { url = "https://files.pythonhosted.org/packages/a9/8b/2ffe35e71f6b92622e8ea4607bf33ecf7dfb51b3619dcfabfd36cbe2d0a5/orjson-3.11.8-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:6a3d159d5ffa0e3961f353c4b036540996bf8b9697ccc38261c0eac1fd3347a6", size = 128772, upload-time = "2026-03-31T16:15:14.237Z" }, - { url = "https://files.pythonhosted.org/packages/27/d2/1f8682ae50d5c6897a563cb96bc106da8c9cb5b7b6e81a52e4cc086679b9/orjson-3.11.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76070a76e9c5ae661e2d9848f216980d8d533e0f8143e6ed462807b242e3c5e8", size = 131946, upload-time = "2026-03-31T16:15:15.607Z" }, - { url = "https://files.pythonhosted.org/packages/52/4b/5500f76f0eece84226e0689cb48dcde081104c2fa6e2483d17ca13685ffb/orjson-3.11.8-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:54153d21520a71a4c82a0dbb4523e468941d549d221dc173de0f019678cf3813", size = 130368, upload-time = "2026-03-31T16:15:17.066Z" }, - { url = "https://files.pythonhosted.org/packages/da/4e/58b927e08fbe9840e6c920d9e299b051ea667463b1f39a56e668669f8508/orjson-3.11.8-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:469ac2125611b7c5741a0b3798cd9e5786cbad6345f9f400c77212be89563bec", size = 135540, upload-time = "2026-03-31T16:15:18.404Z" }, - { url = "https://files.pythonhosted.org/packages/56/7c/ba7cb871cba1bcd5cd02ee34f98d894c6cea96353ad87466e5aef2429c60/orjson-3.11.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14778ffd0f6896aa613951a7fbf4690229aa7a543cb2bfbe9f358e08aafa9546", size = 146877, upload-time = "2026-03-31T16:15:19.833Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/eb9c25fc1386696c6a342cd361c306452c75e0b55e86ad602dd4827a7fd7/orjson-3.11.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea56a955056a6d6c550cf18b3348656a9d9a4f02e2d0c02cabf3c73f1055d506", size = 132837, upload-time = "2026-03-31T16:15:21.282Z" }, - { url = "https://files.pythonhosted.org/packages/37/87/5ddeb7fc1fbd9004aeccab08426f34c81a5b4c25c7061281862b015fce2b/orjson-3.11.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53a0f57e59a530d18a142f4d4ba6dfc708dc5fdedce45e98ff06b44930a2a48f", size = 133624, upload-time = "2026-03-31T16:15:22.641Z" }, - { url = "https://files.pythonhosted.org/packages/22/09/90048793db94ee4b2fcec4ac8e5ddb077367637d6650be896b3494b79bb7/orjson-3.11.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b48e274f8824567d74e2158199e269597edf00823a1b12b63d48462bbf5123e", size = 141904, upload-time = "2026-03-31T16:15:24.435Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cf/eb284847487821a5d415e54149a6449ba9bfc5872ce63ab7be41b8ec401c/orjson-3.11.8-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3f262401086a3960586af06c054609365e98407151f5ea24a62893a40d80dbbb", size = 423742, upload-time = "2026-03-31T16:15:26.155Z" }, - { url = "https://files.pythonhosted.org/packages/44/09/e12423d327071c851c13e76936f144a96adacfc037394dec35ac3fc8d1e8/orjson-3.11.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8e8c6218b614badf8e229b697865df4301afa74b791b6c9ade01d19a9953a942", size = 147806, upload-time = "2026-03-31T16:15:27.909Z" }, - { url = "https://files.pythonhosted.org/packages/b3/6d/37c2589ba864e582ffe7611643314785c6afb1f83c701654ef05daa8fcc7/orjson-3.11.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:093d489fa039ddade2db541097dbb484999fcc65fc2b0ff9819141e2ab364f25", size = 136485, upload-time = "2026-03-31T16:15:29.749Z" }, - { url = "https://files.pythonhosted.org/packages/be/c9/135194a02ab76b04ed9a10f68624b7ebd238bbe55548878b11ff15a0f352/orjson-3.11.8-cp312-cp312-win32.whl", hash = "sha256:e0950ed1bcb9893f4293fd5c5a7ee10934fbf82c4101c70be360db23ce24b7d2", size = 131966, upload-time = "2026-03-31T16:15:31.687Z" }, - { url = "https://files.pythonhosted.org/packages/ed/9a/9796f8fbe3cf30ce9cb696748dbb535e5c87be4bf4fe2e9ca498ef1fa8cf/orjson-3.11.8-cp312-cp312-win_amd64.whl", hash = "sha256:3cf17c141617b88ced4536b2135c552490f07799f6ad565948ea07bef0dcb9a6", size = 127441, upload-time = "2026-03-31T16:15:33.333Z" }, - { url = "https://files.pythonhosted.org/packages/cc/47/5aaf54524a7a4a0dd09dd778f3fa65dd2108290615b652e23d944152bc8e/orjson-3.11.8-cp312-cp312-win_arm64.whl", hash = "sha256:48854463b0572cc87dac7d981aa72ed8bf6deedc0511853dc76b8bbd5482d36d", size = 127364, upload-time = "2026-03-31T16:15:34.748Z" }, - { url = "https://files.pythonhosted.org/packages/66/7f/95fba509bb2305fab0073558f1e8c3a2ec4b2afe58ed9fcb7d3b8beafe94/orjson-3.11.8-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:3f23426851d98478c8970da5991f84784a76682213cd50eb73a1da56b95239dc", size = 229180, upload-time = "2026-03-31T16:15:36.426Z" }, - { url = "https://files.pythonhosted.org/packages/f6/9d/b237215c743ca073697d759b5503abd2cb8a0d7b9c9e21f524bcf176ab66/orjson-3.11.8-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:ebaed4cef74a045b83e23537b52ef19a367c7e3f536751e355a2a394f8648559", size = 128754, upload-time = "2026-03-31T16:15:38.049Z" }, - { url = "https://files.pythonhosted.org/packages/42/3d/27d65b6d11e63f133781425f132807aef793ed25075fec686fc8e46dd528/orjson-3.11.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97c8f5d3b62380b70c36ffacb2a356b7c6becec86099b177f73851ba095ef623", size = 131877, upload-time = "2026-03-31T16:15:39.484Z" }, - { url = "https://files.pythonhosted.org/packages/dd/cc/faee30cd8f00421999e40ef0eba7332e3a625ce91a58200a2f52c7fef235/orjson-3.11.8-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:436c4922968a619fb7fef1ccd4b8b3a76c13b67d607073914d675026e911a65c", size = 130361, upload-time = "2026-03-31T16:15:41.274Z" }, - { url = "https://files.pythonhosted.org/packages/5c/bb/a6c55896197f97b6d4b4e7c7fd77e7235517c34f5d6ad5aadd43c54c6d7c/orjson-3.11.8-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ab359aff0436d80bfe8a23b46b5fea69f1e18aaf1760a709b4787f1318b317f", size = 135521, upload-time = "2026-03-31T16:15:42.758Z" }, - { url = "https://files.pythonhosted.org/packages/9c/7c/ca3a3525aa32ff636ebb1778e77e3587b016ab2edb1b618b36ba96f8f2c0/orjson-3.11.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f89b6d0b3a8d81e1929d3ab3d92bbc225688bd80a770c49432543928fe09ac55", size = 146862, upload-time = "2026-03-31T16:15:44.341Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0c/18a9d7f18b5edd37344d1fd5be17e94dc652c67826ab749c6e5948a78112/orjson-3.11.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c009e7a2ca9ad0ed1376ce20dd692146a5d9fe4310848904b6b4fee5c5c137", size = 132847, upload-time = "2026-03-31T16:15:46.368Z" }, - { url = "https://files.pythonhosted.org/packages/23/91/7e722f352ad67ca573cee44de2a58fb810d0f4eb4e33276c6a557979fd8a/orjson-3.11.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b895b781b3e395c067129d8551655642dfe9437273211d5404e87ac752b53", size = 133637, upload-time = "2026-03-31T16:15:48.123Z" }, - { url = "https://files.pythonhosted.org/packages/af/04/32845ce13ac5bd1046ddb02ac9432ba856cc35f6d74dde95864fe0ad5523/orjson-3.11.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:88006eda83858a9fdf73985ce3804e885c2befb2f506c9a3723cdeb5a2880e3e", size = 141906, upload-time = "2026-03-31T16:15:49.626Z" }, - { url = "https://files.pythonhosted.org/packages/02/5e/c551387ddf2d7106d9039369862245c85738b828844d13b99ccb8d61fd06/orjson-3.11.8-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:55120759e61309af7fcf9e961c6f6af3dde5921cdb3ee863ef63fd9db126cae6", size = 423722, upload-time = "2026-03-31T16:15:51.176Z" }, - { url = "https://files.pythonhosted.org/packages/00/a3/ecfe62434096f8a794d4976728cb59bcfc4a643977f21c2040545d37eb4c/orjson-3.11.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:98bdc6cb889d19bed01de46e67574a2eab61f5cc6b768ed50e8ac68e9d6ffab6", size = 147801, upload-time = "2026-03-31T16:15:52.939Z" }, - { url = "https://files.pythonhosted.org/packages/18/6d/0dce10b9f6643fdc59d99333871a38fa5a769d8e2fc34a18e5d2bfdee900/orjson-3.11.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:708c95f925a43ab9f34625e45dcdadf09ec8a6e7b664a938f2f8d5650f6c090b", size = 136460, upload-time = "2026-03-31T16:15:54.431Z" }, - { url = "https://files.pythonhosted.org/packages/01/d6/6dde4f31842d87099238f1f07b459d24edc1a774d20687187443ab044191/orjson-3.11.8-cp313-cp313-win32.whl", hash = "sha256:01c4e5a6695dc09098f2e6468a251bc4671c50922d4d745aff1a0a33a0cf5b8d", size = 131956, upload-time = "2026-03-31T16:15:56.081Z" }, - { url = "https://files.pythonhosted.org/packages/c1/f9/4e494a56e013db957fb77186b818b916d4695b8fa2aa612364974160e91b/orjson-3.11.8-cp313-cp313-win_amd64.whl", hash = "sha256:c154a35dd1330707450bb4d4e7dd1f17fa6f42267a40c1e8a1daa5e13719b4b8", size = 127410, upload-time = "2026-03-31T16:15:57.54Z" }, - { url = "https://files.pythonhosted.org/packages/57/7f/803203d00d6edb6e9e7eef421d4e1adbb5ea973e40b3533f3cfd9aeb374e/orjson-3.11.8-cp313-cp313-win_arm64.whl", hash = "sha256:4861bde57f4d253ab041e374f44023460e60e71efaa121f3c5f0ed457c3a701e", size = 127338, upload-time = "2026-03-31T16:15:59.106Z" }, - { url = "https://files.pythonhosted.org/packages/6d/35/b01910c3d6b85dc882442afe5060cbf719c7d1fc85749294beda23d17873/orjson-3.11.8-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ec795530a73c269a55130498842aaa762e4a939f6ce481a7e986eeaa790e9da4", size = 229171, upload-time = "2026-03-31T16:16:00.651Z" }, - { url = "https://files.pythonhosted.org/packages/c2/56/c9ec97bd11240abef39b9e5d99a15462809c45f677420fd148a6c5e6295e/orjson-3.11.8-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:c492a0e011c0f9066e9ceaa896fbc5b068c54d365fea5f3444b697ee01bc8625", size = 128746, upload-time = "2026-03-31T16:16:02.673Z" }, - { url = "https://files.pythonhosted.org/packages/3b/e4/66d4f30a90de45e2f0cbd9623588e8ae71eef7679dbe2ae954ed6d66a41f/orjson-3.11.8-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:883206d55b1bd5f5679ad5e6ddd3d1a5e3cac5190482927fdb8c78fb699193b5", size = 131867, upload-time = "2026-03-31T16:16:04.342Z" }, - { url = "https://files.pythonhosted.org/packages/19/30/2a645fc9286b928675e43fa2a3a16fb7b6764aa78cc719dc82141e00f30b/orjson-3.11.8-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5774c1fdcc98b2259800b683b19599c133baeb11d60033e2095fd9d4667b82db", size = 124664, upload-time = "2026-03-31T16:16:05.837Z" }, - { url = "https://files.pythonhosted.org/packages/db/44/77b9a86d84a28d52ba3316d77737f6514e17118119ade3f91b639e859029/orjson-3.11.8-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7381c83dd3d4a6347e6635950aa448f54e7b8406a27c7ecb4a37e9f1ae08b", size = 129701, upload-time = "2026-03-31T16:16:07.407Z" }, - { url = "https://files.pythonhosted.org/packages/b3/ea/eff3d9bfe47e9bc6969c9181c58d9f71237f923f9c86a2d2f490cd898c82/orjson-3.11.8-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14439063aebcb92401c11afc68ee4e407258d2752e62d748b6942dad20d2a70d", size = 141202, upload-time = "2026-03-31T16:16:09.48Z" }, - { url = "https://files.pythonhosted.org/packages/52/c8/90d4b4c60c84d62068d0cf9e4d8f0a4e05e76971d133ac0c60d818d4db20/orjson-3.11.8-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa72e71977bff96567b0f500fc5bfd2fdf915f34052c782a4c6ebbdaa97aa858", size = 127194, upload-time = "2026-03-31T16:16:11.02Z" }, - { url = "https://files.pythonhosted.org/packages/8d/c7/ea9e08d1f0ba981adffb629811148b44774d935171e7b3d780ae43c4c254/orjson-3.11.8-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7679bc2f01bb0d219758f1a5f87bb7c8a81c0a186824a393b366876b4948e14f", size = 133639, upload-time = "2026-03-31T16:16:13.434Z" }, - { url = "https://files.pythonhosted.org/packages/6c/8c/ddbbfd6ba59453c8fc7fe1d0e5983895864e264c37481b2a791db635f046/orjson-3.11.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:14f7b8fcb35ef403b42fa5ecfa4ed032332a91f3dc7368fbce4184d59e1eae0d", size = 141914, upload-time = "2026-03-31T16:16:14.955Z" }, - { url = "https://files.pythonhosted.org/packages/4e/31/dbfbefec9df060d34ef4962cd0afcb6fa7a9ec65884cb78f04a7859526c3/orjson-3.11.8-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c2bdf7b2facc80b5e34f48a2d557727d5c5c57a8a450de122ae81fa26a81c1bc", size = 423800, upload-time = "2026-03-31T16:16:16.594Z" }, - { url = "https://files.pythonhosted.org/packages/87/cf/f74e9ae9803d4ab46b163494adba636c6d7ea955af5cc23b8aaa94cfd528/orjson-3.11.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ccd7ba1b0605813a0715171d39ec4c314cb97a9c85893c2c5c0c3a3729df38bf", size = 147837, upload-time = "2026-03-31T16:16:18.585Z" }, - { url = "https://files.pythonhosted.org/packages/64/e6/9214f017b5db85e84e68602792f742e5dc5249e963503d1b356bee611e01/orjson-3.11.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbc8c9c02463fef4d3c53a9ba3336d05496ec8e1f1c53326a1e4acc11f5c600", size = 136441, upload-time = "2026-03-31T16:16:20.151Z" }, - { url = "https://files.pythonhosted.org/packages/24/dd/3590348818f58f837a75fb969b04cdf187ae197e14d60b5e5a794a38b79d/orjson-3.11.8-cp314-cp314-win32.whl", hash = "sha256:0b57f67710a8cd459e4e54eb96d5f77f3624eba0c661ba19a525807e42eccade", size = 131983, upload-time = "2026-03-31T16:16:21.823Z" }, - { url = "https://files.pythonhosted.org/packages/3f/0f/b6cb692116e05d058f31ceee819c70f097fa9167c82f67fabe7516289abc/orjson-3.11.8-cp314-cp314-win_amd64.whl", hash = "sha256:735e2262363dcbe05c35e3a8869898022af78f89dde9e256924dc02e99fe69ca", size = 127396, upload-time = "2026-03-31T16:16:23.685Z" }, - { url = "https://files.pythonhosted.org/packages/c0/d1/facb5b5051fabb0ef9d26c6544d87ef19a939a9a001198655d0d891062dd/orjson-3.11.8-cp314-cp314-win_arm64.whl", hash = "sha256:6ccdea2c213cf9f3d9490cbd5d427693c870753df41e6cb375bd79bcbafc8817", size = 127330, upload-time = "2026-03-31T16:16:25.496Z" }, +version = "3.11.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/0c/964746fcafbd16f8ff53219ad9f6b412b34f345c75f384ad434ceaadb538/orjson-3.11.9.tar.gz", hash = "sha256:4fef17e1f8722c11587a6ef18e35902450221da0028e65dbaaa543619e68e48f", size = 5599163, upload-time = "2026-05-06T15:11:08.309Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/51/3fb9e65ae76ee97bd611869a503fa3fc0a6e81dd8b737cf3003f682df7ff/orjson-3.11.9-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f01c4818b3fc9b0da8e096722a84318071eaa118df35f6ed2344da0e73a5444f", size = 228522, upload-time = "2026-05-06T15:09:35.362Z" }, + { url = "https://files.pythonhosted.org/packages/16/fa/9d54b07cb3f3b0bfd57841478e42d7a0ece4a9f49f9907eecf5a45461687/orjson-3.11.9-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:3ebca4179031ee716ed076ffadc29428e900512f6fccee8614c9983157fcf19c", size = 128463, upload-time = "2026-05-06T15:09:37.063Z" }, + { url = "https://files.pythonhosted.org/packages/88/b1/6ceafc2eefd0a553e3be77ce6c49d107e772485d9568629376171c50e634/orjson-3.11.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48ee05097750de0ff69ed5b7bbcf0732182fd57a24043dcc2a1da780a5ead3a5", size = 132306, upload-time = "2026-05-06T15:09:38.299Z" }, + { url = "https://files.pythonhosted.org/packages/ea/76/f11311285324a40aab1e3031385c50b635a7cd0734fdaf60c7e89a696f60/orjson-3.11.9-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6082706765a95a6680d812e1daf1c0cfe8adec7831b3ff3b625693f3b461b1c", size = 127988, upload-time = "2026-05-06T15:09:39.597Z" }, + { url = "https://files.pythonhosted.org/packages/9e/85/0ef63bcf1337f44031ce9b91b1919563f62a37527b3ea4368bb15a22e5d7/orjson-3.11.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:277fefe9d76ee17eb14debf399e3533d4d63b5f677a4d3719eb763536af1f4bd", size = 135188, upload-time = "2026-05-06T15:09:40.957Z" }, + { url = "https://files.pythonhosted.org/packages/05/94/b0d27090ea8a2095db3c2bd1b1c96f96f19bbb494d7fef33130e846e613d/orjson-3.11.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03db380e3780fa0015ed776a90f20e8e20bb11dde13b216ce19e5718e3dfba62", size = 145937, upload-time = "2026-05-06T15:09:42.249Z" }, + { url = "https://files.pythonhosted.org/packages/09/eb/75d50c29c05b8054013e221e598820a365c8e64065312e75e202ed880709/orjson-3.11.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33d7d766701847dc6729846362dc27895d2f2d2251264f9d10e7cb9878194877", size = 132758, upload-time = "2026-05-06T15:09:43.945Z" }, + { url = "https://files.pythonhosted.org/packages/49/bd/360686f39348aa88827cb6fbf7dc606fd41c831a35235e1abf1db8e3a9e6/orjson-3.11.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:147302878da387104b66bb4a8b0227d1d487e976ce41a8501916161072ed87b1", size = 133971, upload-time = "2026-05-06T15:09:45.239Z" }, + { url = "https://files.pythonhosted.org/packages/0e/30/3178eb16f3221aeef068b6f1f1ebe05f656ea5c6dffe9f6c917329fe17a3/orjson-3.11.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3513550321f8c8c811a7c3297b8a630e82dc08e4c10216d07703c997776236cd", size = 141685, upload-time = "2026-05-06T15:09:46.858Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f1/ff2f19ed0225f9680fafa42febca3570dd59444ebf190980738d376214c2/orjson-3.11.9-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c5d001196b89fa9cf0a4ab79766cd835b991a166e4b621ba95089edc50c429ff", size = 415167, upload-time = "2026-05-06T15:09:48.312Z" }, + { url = "https://files.pythonhosted.org/packages/9b/61/863bddf0da6e9e586765414debd54b4e58db05f560902b6d00658cb88636/orjson-3.11.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:16969c9d369c98eb084889c6e4d2d39b77c7eb38ceccf8da2a9fff62ae908980", size = 147913, upload-time = "2026-05-06T15:09:49.733Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4081492586d75b073d60c5271a8d0f05a0955cabf1e34c8473f6fcd84235/orjson-3.11.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:63e0efbc991250c0b3143488fa57d95affcabbfc63c99c48d625dd37779aafe2", size = 136959, upload-time = "2026-05-06T15:09:51.311Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bd/70b6ab193594d7abb875320c0a7c8335e846f28968c432c31042409c3c8d/orjson-3.11.9-cp311-cp311-win32.whl", hash = "sha256:14ed654580c1ed2bc217352ec82f91b047aef82951aa71c7f64e0dcb03c0e180", size = 131533, upload-time = "2026-05-06T15:09:52.637Z" }, + { url = "https://files.pythonhosted.org/packages/3f/17/1a1a228183d62d1b77e2c30d210f47dd4768b310ebe1607c63e3c0e3a71e/orjson-3.11.9-cp311-cp311-win_amd64.whl", hash = "sha256:57ea77fb70a448ce87d18fca050193202a3da5e54598f6501ca5476fb66cfe02", size = 127106, upload-time = "2026-05-06T15:09:54.204Z" }, + { url = "https://files.pythonhosted.org/packages/b8/95/285de5fa296d09681ee9c546cd4a8aeb773b701cf343dc125994f4d52953/orjson-3.11.9-cp311-cp311-win_arm64.whl", hash = "sha256:19b72ed11572a2ee51a67a903afbe5af504f84ed6f529c0fe44b0ab3fb5cc697", size = 126848, upload-time = "2026-05-06T15:09:55.551Z" }, + { url = "https://files.pythonhosted.org/packages/16/6d/11867a3ffa3a3608d84a4de51ef4dd0896d6b5cc9132fbe1daf593e677bc/orjson-3.11.9-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9ef6fe90aadef185c7b128859f40beb24720b4ecea95379fc9000931179c3a49", size = 228515, upload-time = "2026-05-06T15:09:57.265Z" }, + { url = "https://files.pythonhosted.org/packages/24/75/05912954c8b288f34fcf5cd4b9b071cb4f6e77b9961e175e56ebb258089f/orjson-3.11.9-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:e5c9b8f28e726e97d97696c826bc7bea5d71cecd63576dba92924a32c1961291", size = 128409, upload-time = "2026-05-06T15:09:59.063Z" }, + { url = "https://files.pythonhosted.org/packages/ab/86/1c3a47df3bc8191ea9ac51603bbb872a95167a364320c269f2557911f406/orjson-3.11.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a473dbb4162108b27901492546f83c76fdcea3d0eadff00ae7a07e18dcce09", size = 132106, upload-time = "2026-05-06T15:10:00.798Z" }, + { url = "https://files.pythonhosted.org/packages/d7/cf/b33b5f3e695ae7d63feef9d915c37cc3b8f465493dcd4f8e0b4c697a2366/orjson-3.11.9-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:011382e2a60fda9d46f1cdee31068cfc52ffe952b587d683ec0463002802a0f4", size = 127864, upload-time = "2026-05-06T15:10:02.15Z" }, + { url = "https://files.pythonhosted.org/packages/31/6a/6cf69385a58208024fcb8c014e2141b8ce838aba6492b589f8acfff97fab/orjson-3.11.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2d3dc759490128c5c1711a53eeaa8ee1d437fd0038ffd2b6008abf46db3f882", size = 135213, upload-time = "2026-05-06T15:10:03.515Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f8/0b1bd3e8f2efcdd376af5c8cfd79eaf13f018080c0089c80ebd724e3c7fb/orjson-3.11.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8ea516b3726d190e1b4297e6f4e7a8650347ae053868a18163b4dd3641d1fff", size = 145994, upload-time = "2026-05-06T15:10:05.083Z" }, + { url = "https://files.pythonhosted.org/packages/f3/59/dab79f61044c529d2c81aecdc589b1f833a1c8dec11ba3b1c2498a02ca7e/orjson-3.11.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380cdce7ba24989af81d0a7013d0aaec5d0e2a21734c0e2681b1bc4f141957fe", size = 132744, upload-time = "2026-05-06T15:10:06.853Z" }, + { url = "https://files.pythonhosted.org/packages/0e/a4/82b7a2fe5d8a67a59ed831b24d59a3d46ea7d207b66e1602d376541d94a6/orjson-3.11.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4fa4f0af7fa18951f7ab3fc2148e223af211bf03f59e1c6034ec3f97f21d61", size = 134014, upload-time = "2026-05-06T15:10:08.213Z" }, + { url = "https://files.pythonhosted.org/packages/50/c7/375e83a76851b73b2e39f3bcf0e5a19e2b89bad13e5bca97d0b293d27f24/orjson-3.11.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a8f5f8bc7ce7d59f08d9f99fa510c06496164a24cb5f3d34537dbd9ca30132e2", size = 141509, upload-time = "2026-05-06T15:10:09.595Z" }, + { url = "https://files.pythonhosted.org/packages/7f/7c/49d5d82a3d3097f641f094f552131f1e2723b0b8cb0fa2874ab65ecfffa6/orjson-3.11.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:4d7fde5501b944f83b3e665e1b31343ff6e154b15560a16b7130ea1e594a4206", size = 415127, upload-time = "2026-05-06T15:10:11.049Z" }, + { url = "https://files.pythonhosted.org/packages/3a/dc/7446c538590d55f455647e5f3c61fc33f7108714e7afcffa6a2a033f8350/orjson-3.11.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cde1a448023ba7d5bb4c01c5afb48894380b5e4956e0627266526587ef4e535f", size = 148025, upload-time = "2026-05-06T15:10:12.842Z" }, + { url = "https://files.pythonhosted.org/packages/df/e5/4d2d8af06f788329b4f78f8cc3679bb395392fcaa1e4d8d3c33e85308fa4/orjson-3.11.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:71e63adb0e1f1ed5d9e168f50a91ceb93ae6420731d222dc7da5c69409aa47aa", size = 136943, upload-time = "2026-05-06T15:10:14.405Z" }, + { url = "https://files.pythonhosted.org/packages/06/69/850264ccf6d80f6b174620d30a87f65c9b1490aba33fe6b62798e618cad3/orjson-3.11.9-cp312-cp312-win32.whl", hash = "sha256:2d057a602cdd19a0ad680417527c45b6961a095081c0f46fe0e03e304aac6470", size = 131606, upload-time = "2026-05-06T15:10:15.791Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d5/973a43fc9c55e20f2051e9830997649f669be0cb3ca52192087c0143f118/orjson-3.11.9-cp312-cp312-win_amd64.whl", hash = "sha256:59e403b1cc5a676da8eaf31f6254801b7341b3e29efa85f92b48d272637e77be", size = 127101, upload-time = "2026-05-06T15:10:17.129Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ae/495470f0e4a18f73fa10b7f6b84b464ec4cc5291c4e0c7c2a6c400bef006/orjson-3.11.9-cp312-cp312-win_arm64.whl", hash = "sha256:9af678d6488357948f1f84c6cd1c1d397c014e1ae2f98ae082a44eb48f602624", size = 126736, upload-time = "2026-05-06T15:10:18.645Z" }, + { url = "https://files.pythonhosted.org/packages/32/33/93fcc25907235c344ae73122f8a4e01d2d393ef062b4af7d2e2487a32c37/orjson-3.11.9-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4bab1b2d6141fe7b32ae71dac905666ece4f94936efbfb13d55bb7739a3a6021", size = 228458, upload-time = "2026-05-06T15:10:20.079Z" }, + { url = "https://files.pythonhosted.org/packages/8f/27/b1e6dadb3c080313c03fdd8067b85e6a0460c7d8d6a1c3984ef77b904e4d/orjson-3.11.9-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:844417969855fc7a41be124aafe83dc424592a7f77cd4501900c67307122b92c", size = 128368, upload-time = "2026-05-06T15:10:21.549Z" }, + { url = "https://files.pythonhosted.org/packages/21/0f/c9ede0bf052f6b4051e64a7d4fa91b725cccf8321a6a786e86eb03519f00/orjson-3.11.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffe02797b5e9f3a9d8292ddcd289b474ad13e81ad83cd1891a240811f1d2cb81", size = 132070, upload-time = "2026-05-06T15:10:23.371Z" }, + { url = "https://files.pythonhosted.org/packages/fd/26/d398e28048dc18205bbe812f2c88cb9b40313db2470778e25964796458fe/orjson-3.11.9-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e4eed3b200023042814d2fc8a5d2e880f13b52e1ed2485e83da4f3962f7dc1a", size = 127892, upload-time = "2026-05-06T15:10:24.714Z" }, + { url = "https://files.pythonhosted.org/packages/66/60/52b0054c4c700d5aa7fc5b7ca96917400d8f061307778578e67a10e25852/orjson-3.11.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aff7da9952a5ad1cef8e68017724d96c7b9a66e99e91d6252e1b133d67a7b10", size = 135217, upload-time = "2026-05-06T15:10:26.084Z" }, + { url = "https://files.pythonhosted.org/packages/d5/97/1e3dc2b2a28b7b2528f403d2fc1d79ec5f39af3bc143ab65d3ec26426385/orjson-3.11.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d4e98d6f3b8afed8bc8cd9718ec0cdf46661826beefb53fe8eafb37f2bf0362", size = 145980, upload-time = "2026-05-06T15:10:28.062Z" }, + { url = "https://files.pythonhosted.org/packages/fc/39/31fbfe7850f2de32dee7e7e5c09f26d403ab01e440ac96001c6b01ad3c99/orjson-3.11.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a81d52442a7c99b3662333235b3adf96a1715864658b35bb797212be7bddb97", size = 132738, upload-time = "2026-05-06T15:10:29.727Z" }, + { url = "https://files.pythonhosted.org/packages/a1/08/dca0082dd2a194acb93e5457e73455388e2e2ca464a2672449a9ddbb679d/orjson-3.11.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e39364e726a8fff737309aff059ff67d8a8c8d5b677be7bb49a8b3e84b7e218", size = 134033, upload-time = "2026-05-06T15:10:31.152Z" }, + { url = "https://files.pythonhosted.org/packages/11/d4/5bdb0626801230139987385554c5d4c42255218ac906525bf4347f22cd95/orjson-3.11.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4fd66214623f1b17501df9f0543bef0b833979ab5b6ded1e1d123222866aa8c9", size = 141492, upload-time = "2026-05-06T15:10:32.641Z" }, + { url = "https://files.pythonhosted.org/packages/fa/88/a21fb53b3ede6703aede6dce4710ed4111e5b201cfa6bbff5e544f9d47d7/orjson-3.11.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8ecc30f10465fa1e0ce13fd01d9e22c316e5053a719a8d915d4545a09a5ff677", size = 415087, upload-time = "2026-05-06T15:10:34.438Z" }, + { url = "https://files.pythonhosted.org/packages/3d/57/1b30daf70f0d8180e9a73cefbfbdd99e4bf19eb020466502b01fba7e0e50/orjson-3.11.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:97db4c94a7db398a5bd636273324f0b3fd58b350bbbac8bb380ceb825a9b40f4", size = 148031, upload-time = "2026-05-06T15:10:36.358Z" }, + { url = "https://files.pythonhosted.org/packages/04/83/45fbb6d962e260807f99441db9613cee868ceda4baceda59b3720a563f97/orjson-3.11.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f78cf8fec5bd627f4082b8dfeac7871b43d7f3274904492a43dab39f18a19a0", size = 136915, upload-time = "2026-05-06T15:10:38.013Z" }, + { url = "https://files.pythonhosted.org/packages/5f/cc/2d10025f9056d376e4127ec05a5808b218d46f035fdc08178a5411b34250/orjson-3.11.9-cp313-cp313-win32.whl", hash = "sha256:d4087e5c0209a0a8efe4de3303c234b9c44d1174161dcd851e8eea07c7560b32", size = 131613, upload-time = "2026-05-06T15:10:39.569Z" }, + { url = "https://files.pythonhosted.org/packages/67/bd/2775ff28bfe883b9aa1ff348300542eb2ef1ee18d8ae0e3a49846817a865/orjson-3.11.9-cp313-cp313-win_amd64.whl", hash = "sha256:051b102c93b4f634e89f3866b07b9a9a98915ada541f4ec30f177067b2694979", size = 127086, upload-time = "2026-05-06T15:10:41.262Z" }, + { url = "https://files.pythonhosted.org/packages/91/2b/d26799e580939e32a7da9a39531bc9e58e15ca32ffaa6a8cb3e9bb0d22cd/orjson-3.11.9-cp313-cp313-win_arm64.whl", hash = "sha256:cce9127885941bd28f080cecf1f1d288336b7e0d812c345b08be88b572796254", size = 126696, upload-time = "2026-05-06T15:10:42.651Z" }, + { url = "https://files.pythonhosted.org/packages/8e/eb/5da01e356015aee6ecfa1187ced87aef51364e306f5e695dd52719bf0e78/orjson-3.11.9-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b6ef1979adc4bc243523f1a2ba91418030a8e29b0a99cbe7e0e2d6807d4dce6e", size = 228465, upload-time = "2026-05-06T15:10:44.097Z" }, + { url = "https://files.pythonhosted.org/packages/64/62/3e0e0c14c957133bcd855395c62b55ed4e3b0af23ffea11b032cb1dcbdb1/orjson-3.11.9-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:f36b7f32c7c0db4a719f1fc5824db4a9c6f8bd1a354debb91faf26ebf3a4c71e", size = 128364, upload-time = "2026-05-06T15:10:45.839Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5a/07d8aa117211a8ed7630bda80c8c0b14d04e0f8dcf99bcf49656e4a710eb/orjson-3.11.9-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08f4d8ebb44925c794e535b2bebc507cebf32209df81de22ae285fb0d8d66de0", size = 132063, upload-time = "2026-05-06T15:10:47.267Z" }, + { url = "https://files.pythonhosted.org/packages/d6/ec/4acaf21483e18aa945be74a474c74b434f284b549f275a0a39b9f98956e9/orjson-3.11.9-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6cc7923789694fd58f001cbcac7e47abc13af4d560ebbfcf3b41a8b1a0748124", size = 122356, upload-time = "2026-05-06T15:10:48.765Z" }, + { url = "https://files.pythonhosted.org/packages/13/d8/5f0555e7638801323b7a75850f92e7dfa891bc84fe27a1ba4449170d1200/orjson-3.11.9-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea5c46eb2d3af39e806b986f4b09d5c2706a1f5afde3cbf7544ce6616127173c", size = 129592, upload-time = "2026-05-06T15:10:50.13Z" }, + { url = "https://files.pythonhosted.org/packages/b6/30/ed9860412a3603ceb3c5955bfd72d28b9d0e7ba6ed81add14f83d7114236/orjson-3.11.9-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f5d89a2ed90731df3be64bab0aa44f78bff39fdc9d71c291f4a8023aa46425b7", size = 140491, upload-time = "2026-05-06T15:10:51.582Z" }, + { url = "https://files.pythonhosted.org/packages/d0/17/adc514dea7ac7c505527febf884934b815d34f0c7b8693c1a8b39c5c4a57/orjson-3.11.9-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25e4aed0312d292c09f61af25bba34e0b2c88546041472b09088c39a4d828af1", size = 127309, upload-time = "2026-05-06T15:10:53.329Z" }, + { url = "https://files.pythonhosted.org/packages/76/3e/c0b690253f0b82d86e99949af13533363acfb5432ecb5d53dd5b3bce9c34/orjson-3.11.9-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaea64f3f467d22e70eeed68bdccb3bc4f83f650446c4a03c59f2cba28a108db", size = 134030, upload-time = "2026-05-06T15:10:54.988Z" }, + { url = "https://files.pythonhosted.org/packages/c1/7a/bc82a0bb25e9faaf92dc4d9ef002732efc09737706af83e346788641d4a7/orjson-3.11.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a028425d1b440c5d92a6be1e1a020739dfe67ea87d96c6dbe828c1b30041728b", size = 141482, upload-time = "2026-05-06T15:10:56.663Z" }, + { url = "https://files.pythonhosted.org/packages/01/55/e69188b939f77d5d32a9833745ace31ea5ccae3ab613a1ec185d3cd2c4fb/orjson-3.11.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b192c6cf397e4455b11523c5cf2b18ed084c1bbd61b6c0926344d2129481972", size = 415178, upload-time = "2026-05-06T15:10:58.446Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1a/b8a5a7ac527e80b9cb11d51e3f6689b709279183264b9ec5c7bc680bb8b5/orjson-3.11.9-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea407d4ccf5891d667d045fecae97a7a1e5e87b3b97f97ae1803c2e741130be0", size = 148089, upload-time = "2026-05-06T15:11:00.441Z" }, + { url = "https://files.pythonhosted.org/packages/97/4e/00503f64204bf859b37213a63927028f30fb6268cd8677fb0a5ad48155e1/orjson-3.11.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f63aaf97afd9f6dec5b1a68e1b8da12bfccb4cb9a9a65c3e0b6c847849e7586", size = 136921, upload-time = "2026-05-06T15:11:02.176Z" }, + { url = "https://files.pythonhosted.org/packages/0d/ba/a23b82a0a8d0ed7bed4e5f5035aae751cad4ff6a1e8d2ecd14d8860f5929/orjson-3.11.9-cp314-cp314-win32.whl", hash = "sha256:e30ab17845bb9fa54ccf67fa4f9f5282652d54faa6d17452f47d0f369d038673", size = 131638, upload-time = "2026-05-06T15:11:03.696Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c3/0c6798456bade745c75c452342dabacce5798196483e77e643be1f53877d/orjson-3.11.9-cp314-cp314-win_amd64.whl", hash = "sha256:32ef5f4283a3be81913947d19608eacb7c6608026851123790cd9cc8982af34b", size = 127078, upload-time = "2026-05-06T15:11:05.123Z" }, + { url = "https://files.pythonhosted.org/packages/16/21/5a3f1e8913103b703a436a5664238e5b965ec392b555fe68943ea3691e6b/orjson-3.11.9-cp314-cp314-win_arm64.whl", hash = "sha256:eebdbdeef0094e4f5aefa20dcd4eb2368ab5e7a3b4edea27f1e7b2892e009cf9", size = 126687, upload-time = "2026-05-06T15:11:06.602Z" }, ] [[package]] @@ -4791,62 +4778,62 @@ wheels = [ [[package]] name = "pandas" -version = "3.0.2" +version = "3.0.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, { name = "python-dateutil" }, { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/da/99/b342345300f13440fe9fe385c3c481e2d9a595ee3bab4d3219247ac94e9a/pandas-3.0.2.tar.gz", hash = "sha256:f4753e73e34c8d83221ba58f232433fca2748be8b18dbca02d242ed153945043", size = 4645855, upload-time = "2026-03-31T06:48:30.816Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/97/35/6411db530c618e0e0005187e35aa02ce60ae4c4c4d206964a2f978217c27/pandas-3.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a727a73cbdba2f7458dc82449e2315899d5140b449015d822f515749a46cbbe0", size = 10326926, upload-time = "2026-03-31T06:46:08.29Z" }, - { url = "https://files.pythonhosted.org/packages/c4/d3/b7da1d5d7dbdc5ef52ed7debd2b484313b832982266905315dad5a0bf0b1/pandas-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbbd4aa20ca51e63b53bbde6a0fa4254b1aaabb74d2f542df7a7959feb1d760c", size = 9926987, upload-time = "2026-03-31T06:46:11.724Z" }, - { url = "https://files.pythonhosted.org/packages/52/77/9b1c2d6070b5dbe239a7bc889e21bfa58720793fb902d1e070695d87c6d0/pandas-3.0.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:339dda302bd8369dedeae979cb750e484d549b563c3f54f3922cb8ff4978c5eb", size = 10757067, upload-time = "2026-03-31T06:46:14.903Z" }, - { url = "https://files.pythonhosted.org/packages/20/17/ec40d981705654853726e7ac9aea9ddbb4a5d9cf54d8472222f4f3de06c2/pandas-3.0.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:61c2fd96d72b983a9891b2598f286befd4ad262161a609c92dc1652544b46b76", size = 11258787, upload-time = "2026-03-31T06:46:17.683Z" }, - { url = "https://files.pythonhosted.org/packages/90/e3/3f1126d43d3702ca8773871a81c9f15122a1f412342cc56284ffda5b1f70/pandas-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c934008c733b8bbea273ea308b73b3156f0181e5b72960790b09c18a2794fe1e", size = 11771616, upload-time = "2026-03-31T06:46:20.532Z" }, - { url = "https://files.pythonhosted.org/packages/2e/cf/0f4e268e1f5062e44a6bda9f925806721cd4c95c2b808a4c82ebe914f96b/pandas-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:60a80bb4feacbef5e1447a3f82c33209c8b7e07f28d805cfd1fb951e5cb443aa", size = 12337623, upload-time = "2026-03-31T06:46:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/44/a0/97a6339859d4acb2536efb24feb6708e82f7d33b2ed7e036f2983fcced82/pandas-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:ed72cb3f45190874eb579c64fa92d9df74e98fd63e2be7f62bce5ace0ade61df", size = 9897372, upload-time = "2026-03-31T06:46:26.703Z" }, - { url = "https://files.pythonhosted.org/packages/8f/eb/781516b808a99ddf288143cec46b342b3016c3414d137da1fdc3290d8860/pandas-3.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:f12b1a9e332c01e09510586f8ca9b108fd631fd656af82e452d7315ef6df5f9f", size = 9154922, upload-time = "2026-03-31T06:46:30.284Z" }, - { url = "https://files.pythonhosted.org/packages/f3/b0/c20bd4d6d3f736e6bd6b55794e9cd0a617b858eaad27c8f410ea05d953b7/pandas-3.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:232a70ebb568c0c4d2db4584f338c1577d81e3af63292208d615907b698a0f18", size = 10347921, upload-time = "2026-03-31T06:46:33.36Z" }, - { url = "https://files.pythonhosted.org/packages/35/d0/4831af68ce30cc2d03c697bea8450e3225a835ef497d0d70f31b8cdde965/pandas-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:970762605cff1ca0d3f71ed4f3a769ea8f85fc8e6348f6e110b8fea7e6eb5a14", size = 9888127, upload-time = "2026-03-31T06:46:36.253Z" }, - { url = "https://files.pythonhosted.org/packages/61/a9/16ea9346e1fc4a96e2896242d9bc674764fb9049b0044c0132502f7a771e/pandas-3.0.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aff4e6f4d722e0652707d7bcb190c445fe58428500c6d16005b02401764b1b3d", size = 10399577, upload-time = "2026-03-31T06:46:39.224Z" }, - { url = "https://files.pythonhosted.org/packages/c4/a8/3a61a721472959ab0ce865ef05d10b0d6bfe27ce8801c99f33d4fa996e65/pandas-3.0.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef8b27695c3d3dc78403c9a7d5e59a62d5464a7e1123b4e0042763f7104dc74f", size = 10880030, upload-time = "2026-03-31T06:46:42.412Z" }, - { url = "https://files.pythonhosted.org/packages/da/65/7225c0ea4d6ce9cb2160a7fb7f39804871049f016e74782e5dade4d14109/pandas-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f8d68083e49e16b84734eb1a4dcae4259a75c90fb6e2251ab9a00b61120c06ab", size = 11409468, upload-time = "2026-03-31T06:46:45.2Z" }, - { url = "https://files.pythonhosted.org/packages/fa/5b/46e7c76032639f2132359b5cf4c785dd8cf9aea5ea64699eac752f02b9db/pandas-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:32cc41f310ebd4a296d93515fcac312216adfedb1894e879303987b8f1e2b97d", size = 11936381, upload-time = "2026-03-31T06:46:48.293Z" }, - { url = "https://files.pythonhosted.org/packages/7b/8b/721a9cff6fa6a91b162eb51019c6243b82b3226c71bb6c8ef4a9bd65cbc6/pandas-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:a4785e1d6547d8427c5208b748ae2efb64659a21bd82bf440d4262d02bfa02a4", size = 9744993, upload-time = "2026-03-31T06:46:51.488Z" }, - { url = "https://files.pythonhosted.org/packages/d5/18/7f0bd34ae27b28159aa80f2a6799f47fda34f7fb938a76e20c7b7fe3b200/pandas-3.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:08504503f7101300107ecdc8df73658e4347586db5cfdadabc1592e9d7e7a0fd", size = 9056118, upload-time = "2026-03-31T06:46:54.548Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ca/3e639a1ea6fcd0617ca4e8ca45f62a74de33a56ae6cd552735470b22c8d3/pandas-3.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5918ba197c951dec132b0c5929a00c0bf05d5942f590d3c10a807f6e15a57d3", size = 10321105, upload-time = "2026-03-31T06:46:57.327Z" }, - { url = "https://files.pythonhosted.org/packages/0b/77/dbc82ff2fb0e63c6564356682bf201edff0ba16c98630d21a1fb312a8182/pandas-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d606a041c89c0a474a4702d532ab7e73a14fe35c8d427b972a625c8e46373668", size = 9864088, upload-time = "2026-03-31T06:46:59.935Z" }, - { url = "https://files.pythonhosted.org/packages/5c/2b/341f1b04bbca2e17e13cd3f08c215b70ef2c60c5356ef1e8c6857449edc7/pandas-3.0.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:710246ba0616e86891b58ab95f2495143bb2bc83ab6b06747c74216f583a6ac9", size = 10369066, upload-time = "2026-03-31T06:47:02.792Z" }, - { url = "https://files.pythonhosted.org/packages/12/c5/cbb1ffefb20a93d3f0e1fdcda699fb84976210d411b008f97f48bf6ce27e/pandas-3.0.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d3cfe227c725b1f3dff4278b43d8c784656a42a9325b63af6b1492a8232209e", size = 10876780, upload-time = "2026-03-31T06:47:06.205Z" }, - { url = "https://files.pythonhosted.org/packages/98/fe/2249ae5e0a69bd0ddf17353d0a5d26611d70970111f5b3600cdc8be883e7/pandas-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c3b723df9087a9a9a840e263ebd9f88b64a12075d1bf2ea401a5a42f254f084d", size = 11375181, upload-time = "2026-03-31T06:47:09.383Z" }, - { url = "https://files.pythonhosted.org/packages/de/64/77a38b09e70b6464883b8d7584ab543e748e42c1b5d337a2ee088e0df741/pandas-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3096110bf9eac0070b7208465f2740e2d8a670d5cb6530b5bb884eca495fd39", size = 11928899, upload-time = "2026-03-31T06:47:12.686Z" }, - { url = "https://files.pythonhosted.org/packages/5e/52/42855bf626868413f761addd574acc6195880ae247a5346477a4361c3acb/pandas-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:07a10f5c36512eead51bc578eb3354ad17578b22c013d89a796ab5eee90cd991", size = 9746574, upload-time = "2026-03-31T06:47:15.64Z" }, - { url = "https://files.pythonhosted.org/packages/88/39/21304ae06a25e8bf9fc820d69b29b2c495b2ae580d1e143146c309941760/pandas-3.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:5fdbfa05931071aba28b408e59226186b01eb5e92bea2ab78b65863ca3228d84", size = 9047156, upload-time = "2026-03-31T06:47:18.595Z" }, - { url = "https://files.pythonhosted.org/packages/72/20/7defa8b27d4f330a903bb68eea33be07d839c5ea6bdda54174efcec0e1d2/pandas-3.0.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:dbc20dea3b9e27d0e66d74c42b2d0c1bed9c2ffe92adea33633e3bedeb5ac235", size = 10756238, upload-time = "2026-03-31T06:47:22.012Z" }, - { url = "https://files.pythonhosted.org/packages/e9/95/49433c14862c636afc0e9b2db83ff16b3ad92959364e52b2955e44c8e94c/pandas-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b75c347eff42497452116ce05ef461822d97ce5b9ff8df6edacb8076092c855d", size = 10408520, upload-time = "2026-03-31T06:47:25.197Z" }, - { url = "https://files.pythonhosted.org/packages/3b/f8/462ad2b5881d6b8ec8e5f7ed2ea1893faa02290d13870a1600fe72ad8efc/pandas-3.0.2-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1478075142e83a5571782ad007fb201ed074bdeac7ebcc8890c71442e96adf7", size = 10324154, upload-time = "2026-03-31T06:47:28.097Z" }, - { url = "https://files.pythonhosted.org/packages/0a/65/d1e69b649cbcddda23ad6e4c40ef935340f6f652a006e5cbc3555ac8adb3/pandas-3.0.2-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5880314e69e763d4c8b27937090de570f1fb8d027059a7ada3f7f8e98bdcb677", size = 10714449, upload-time = "2026-03-31T06:47:30.85Z" }, - { url = "https://files.pythonhosted.org/packages/47/a4/85b59bc65b8190ea3689882db6cdf32a5003c0ccd5a586c30fdcc3ffc4fc/pandas-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b5329e26898896f06035241a626d7c335daa479b9bbc82be7c2742d048e41172", size = 11338475, upload-time = "2026-03-31T06:47:34.026Z" }, - { url = "https://files.pythonhosted.org/packages/1e/c4/bc6966c6e38e5d9478b935272d124d80a589511ed1612a5d21d36f664c68/pandas-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:81526c4afd31971f8b62671442a4b2b51e0aa9acc3819c9f0f12a28b6fcf85f1", size = 11786568, upload-time = "2026-03-31T06:47:36.941Z" }, - { url = "https://files.pythonhosted.org/packages/e8/74/09298ca9740beed1d3504e073d67e128aa07e5ca5ca2824b0c674c0b8676/pandas-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:7cadd7e9a44ec13b621aec60f9150e744cfc7a3dd32924a7e2f45edff31823b0", size = 10488652, upload-time = "2026-03-31T06:47:40.612Z" }, - { url = "https://files.pythonhosted.org/packages/bb/40/c6ea527147c73b24fc15c891c3fcffe9c019793119c5742b8784a062c7db/pandas-3.0.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:db0dbfd2a6cdf3770aa60464d50333d8f3d9165b2f2671bcc299b72de5a6677b", size = 10326084, upload-time = "2026-03-31T06:47:43.834Z" }, - { url = "https://files.pythonhosted.org/packages/95/25/bdb9326c3b5455f8d4d3549fce7abcf967259de146fe2cf7a82368141948/pandas-3.0.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0555c5882688a39317179ab4a0ed41d3ebc8812ab14c69364bbee8fb7a3f6288", size = 9914146, upload-time = "2026-03-31T06:47:46.67Z" }, - { url = "https://files.pythonhosted.org/packages/8d/77/3a227ff3337aa376c60d288e1d61c5d097131d0ac71f954d90a8f369e422/pandas-3.0.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:01f31a546acd5574ef77fe199bc90b55527c225c20ccda6601cf6b0fd5ed597c", size = 10444081, upload-time = "2026-03-31T06:47:49.681Z" }, - { url = "https://files.pythonhosted.org/packages/15/88/3cdd54fa279341afa10acf8d2b503556b1375245dccc9315659f795dd2e9/pandas-3.0.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:deeca1b5a931fdf0c2212c8a659ade6d3b1edc21f0914ce71ef24456ca7a6535", size = 10897535, upload-time = "2026-03-31T06:47:53.033Z" }, - { url = "https://files.pythonhosted.org/packages/06/9d/98cc7a7624f7932e40f434299260e2917b090a579d75937cb8a57b9d2de3/pandas-3.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0f48afd9bb13300ffb5a3316973324c787054ba6665cda0da3fbd67f451995db", size = 11446992, upload-time = "2026-03-31T06:47:56.193Z" }, - { url = "https://files.pythonhosted.org/packages/9a/cd/19ff605cc3760e80602e6826ddef2824d8e7050ed80f2e11c4b079741dc3/pandas-3.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6c4d8458b97a35717b62469a4ea0e85abd5ed8687277f5ccfc67f8a5126f8c53", size = 11968257, upload-time = "2026-03-31T06:47:59.137Z" }, - { url = "https://files.pythonhosted.org/packages/db/60/aba6a38de456e7341285102bede27514795c1eaa353bc0e7638b6b785356/pandas-3.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:b35d14bb5d8285d9494fe93815a9e9307c0876e10f1e8e89ac5b88f728ec8dcf", size = 9865893, upload-time = "2026-03-31T06:48:02.038Z" }, - { url = "https://files.pythonhosted.org/packages/08/71/e5ec979dd2e8a093dacb8864598c0ff59a0cee0bbcdc0bfec16a51684d4f/pandas-3.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:63d141b56ef686f7f0d714cfb8de4e320475b86bf4b620aa0b7da89af8cbdbbb", size = 9188644, upload-time = "2026-03-31T06:48:05.045Z" }, - { url = "https://files.pythonhosted.org/packages/f1/6c/7b45d85db19cae1eb524f2418ceaa9d85965dcf7b764ed151386b7c540f0/pandas-3.0.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:140f0cffb1fa2524e874dde5b477d9defe10780d8e9e220d259b2c0874c89d9d", size = 10776246, upload-time = "2026-03-31T06:48:07.789Z" }, - { url = "https://files.pythonhosted.org/packages/a8/3e/7b00648b086c106e81766f25322b48aa8dfa95b55e621dbdf2fdd413a117/pandas-3.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae37e833ff4fed0ba352f6bdd8b73ba3ab3256a85e54edfd1ab51ae40cca0af8", size = 10424801, upload-time = "2026-03-31T06:48:10.897Z" }, - { url = "https://files.pythonhosted.org/packages/da/6e/558dd09a71b53b4008e7fc8a98ec6d447e9bfb63cdaeea10e5eb9b2dabe8/pandas-3.0.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d888a5c678a419a5bb41a2a93818e8ed9fd3172246555c0b37b7cc27027effd", size = 10345643, upload-time = "2026-03-31T06:48:13.7Z" }, - { url = "https://files.pythonhosted.org/packages/be/e3/921c93b4d9a280409451dc8d07b062b503bbec0531d2627e73a756e99a82/pandas-3.0.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b444dc64c079e84df91baa8bf613d58405645461cabca929d9178f2cd392398d", size = 10743641, upload-time = "2026-03-31T06:48:16.659Z" }, - { url = "https://files.pythonhosted.org/packages/56/ca/fd17286f24fa3b4d067965d8d5d7e14fe557dd4f979a0b068ac0deaf8228/pandas-3.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4544c7a54920de8eeacaa1466a6b7268ecfbc9bc64ab4dbb89c6bbe94d5e0660", size = 11361993, upload-time = "2026-03-31T06:48:19.475Z" }, - { url = "https://files.pythonhosted.org/packages/e4/a5/2f6ed612056819de445a433ca1f2821ac3dab7f150d569a59e9cc105de1d/pandas-3.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:734be7551687c00fbd760dc0522ed974f82ad230d4a10f54bf51b80d44a08702", size = 11815274, upload-time = "2026-03-31T06:48:22.695Z" }, - { url = "https://files.pythonhosted.org/packages/00/2f/b622683e99ec3ce00b0854bac9e80868592c5b051733f2cf3a868e5fea26/pandas-3.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:57a07209bebcbcf768d2d13c9b78b852f9a15978dac41b9e6421a81ad4cdd276", size = 10888530, upload-time = "2026-03-31T06:48:25.806Z" }, - { url = "https://files.pythonhosted.org/packages/cb/2b/f8434233fab2bd66a02ec014febe4e5adced20e2693e0e90a07d118ed30e/pandas-3.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:5371b72c2d4d415d08765f32d689217a43227484e81b2305b52076e328f6f482", size = 9455341, upload-time = "2026-03-31T06:48:28.418Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/16/b5c76b838fd9bf6ce84d3a53346b8874ec05c5f0040d75ef2c320100cd2a/pandas-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:455f6f8139d4282188f526868dbc3c828470e88a3d9d59a891bd46a455f21b98", size = 10338495, upload-time = "2026-05-11T18:52:11.558Z" }, + { url = "https://files.pythonhosted.org/packages/5a/b0/a4ffc4ae74d2d822200dcc46898987d8eb6032d1e2b219cae39da6f5cbcc/pandas-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4e15135e2ee5df1063313e2425ceef8ac0f4ae775893815b0923651b806a5639", size = 9938250, upload-time = "2026-05-11T18:52:17.005Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b2/3323601a52caee42c019e370090ca4544b241437240ca04f786cce82b0cf/pandas-3.0.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:05f1f1752b8533ea03f7f39a9c15b1a058d067bb48f4748948e7a8691e0510f2", size = 10770558, upload-time = "2026-05-11T18:52:19.865Z" }, + { url = "https://files.pythonhosted.org/packages/32/f1/bbecd2f867b97abebe0f9b53d750f862251b40337e061b36676ded3d920f/pandas-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a1e45c80cceb3b4a21bc5939d52e8cbd8d9b7305309219d59e9754d9ce09e27", size = 11274611, upload-time = "2026-05-11T18:52:22.622Z" }, + { url = "https://files.pythonhosted.org/packages/7f/4f/eafabf2d5fae5adf143b4d18d3706c5efdc368a7c4eb1ee8a3eddabbd0f6/pandas-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:14da8316da4d0c5a77618425996bfb1248ca87fc2c1486e6fde4652bd18b5824", size = 11784670, upload-time = "2026-05-11T18:52:25.4Z" }, + { url = "https://files.pythonhosted.org/packages/49/44/1eb20389301b57b19cc099a1c2f662501f72f08a65f912d05822613c1532/pandas-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a55066a0505dae0ba2b50a46637db34b46f9094c65c5d4800794ef6335010938", size = 12353708, upload-time = "2026-05-11T18:52:28.139Z" }, + { url = "https://files.pythonhosted.org/packages/eb/62/c321f13b5ba1819fc8dca456c7fce578da2dcfecff1abbf0eaddf8406c0f/pandas-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6674ab18ad8c57802867264b00e15e7bb904700cdd9046e3b2fa1fce237439ea", size = 9907609, upload-time = "2026-05-11T18:52:30.982Z" }, + { url = "https://files.pythonhosted.org/packages/53/85/1b7f563ebc6357c27233a02a96b589bcce1fa9c6eb89fb4f0e56421d277e/pandas-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:5cc09a68b3120e0f54870dede8287a7bb1fa463907e4fcec1ea77cab6179bf7a", size = 9165596, upload-time = "2026-05-11T18:52:33.334Z" }, + { url = "https://files.pythonhosted.org/packages/24/f1/392f8c5bfc16f66a0d2d41561c01627c228fe7ed2a0d056ef11315042570/pandas-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fed2ff7fd9779120e388e285fc029bd5cf9490cdd2e4166a9ee22c0e49a9ab09", size = 10357846, upload-time = "2026-05-11T18:52:36.143Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3d/b16412745651e855f357e5e66930248688378853a6e2698a214e331fba1f/pandas-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b168fc218fd80a6cbdbdbc1a97ddc7889ed057d7eb45f50d866ceab5f39904c4", size = 9899550, upload-time = "2026-05-11T18:52:38.976Z" }, + { url = "https://files.pythonhosted.org/packages/31/a8/fa2535168fffcedf67f4f6de28d2dd903a747ca7c8ea6989451aaeb3a92f/pandas-3.0.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0383c72c75cdcca61a9e116e611143902dbfd08bff356829c2f6d1cf40a9ca8c", size = 10412965, upload-time = "2026-05-11T18:52:41.915Z" }, + { url = "https://files.pythonhosted.org/packages/65/b6/09b01cdbc15224e2850365192d17b7bdebb8bdbd8780ed221fcdf0d9a515/pandas-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6dc0b3fd2169c9157deed50b4d519553a3655c8c6a96027136d654592be973a9", size = 10894600, upload-time = "2026-05-11T18:52:45.02Z" }, + { url = "https://files.pythonhosted.org/packages/c9/a4/2eb28f2fccb4ced4a2c79ab2a5dee9ade1ebf44922ebad6fea158c9f95d4/pandas-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e65d5407dc0b394f509699650e4a2ec01c0514f21850f453fa60f3be79a5dbf", size = 11422824, upload-time = "2026-05-11T18:52:48.058Z" }, + { url = "https://files.pythonhosted.org/packages/f8/45/830bb57f533a4604b355e07edcb8ea18cf88b5f94e5fca92f27052d7c597/pandas-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8894dc474d648fe7b6ff0ca9b0bd73950d19952bc1a6534540762c5d79d305c", size = 11950889, upload-time = "2026-05-11T18:52:50.905Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c5/fc1b368f303087d20e8c9bf3d6ceb186263cfac0ade735cd938538bea839/pandas-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c7be265b62cef88e253a941e4698604973736dcfe242fdb5198f0f7bc473cdcc", size = 9755463, upload-time = "2026-05-11T18:52:53.386Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/fda8f9705b1b09c6ebe14bfc0fa0e4ec8584d54ea673628f157ff55131af/pandas-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:557409bc4178e70ee8d9ddb494798e51ebf6ea59330f6be22c51bab2a7db6c49", size = 9066158, upload-time = "2026-05-11T18:52:56.038Z" }, + { url = "https://files.pythonhosted.org/packages/c5/90/62d8302883c44308c477e222c3daf7c813a34c8e96985882fbd53d964352/pandas-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:67b3b64c11910cfa29f4e94a14d3bff9ee693b6fc76055e7cad549cee0aec5fa", size = 10331071, upload-time = "2026-05-11T18:52:58.838Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ae/6a6493c783a101f165e4356953ba3c74d6f77f0042fa7d753da9dfbb640c/pandas-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:39436b377d56d2a2e52d0395bdbee171f01068e99af5250509aceeb929f765c7", size = 9875690, upload-time = "2026-05-11T18:53:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/62/7c/5df8e9f56c69a2769fbe9382a5ef8f2658c007e376434e1e2cbb57ad895f/pandas-3.0.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4be06d68f9ddcfc645b87534911da79a8fbffc7573c80e0edcf42a5020624d8", size = 10381634, upload-time = "2026-05-11T18:53:04.393Z" }, + { url = "https://files.pythonhosted.org/packages/99/68/1237369725aa617bb358263d535803e3053fdbc593513ec5ed9c9896b5b6/pandas-3.0.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a4eeb6830daf35a71cc09649bd823e2b542dac246cdee9614c6e4bd65028cd6a", size = 10891243, upload-time = "2026-05-11T18:53:07.643Z" }, + { url = "https://files.pythonhosted.org/packages/25/93/77d108e8af7222b4a503ebde0e30215b1c2e4f8e53a526431890f22d5586/pandas-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1928e07221f82db493cd4af1e23c1bfca524a19a4699887975bff68f49a72bfb", size = 11388659, upload-time = "2026-05-11T18:53:10.634Z" }, + { url = "https://files.pythonhosted.org/packages/d0/bd/eff5b4399f332ac386c853f6cd2bd3fa2ca0061b9f36ecd9c4d7c4265649/pandas-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51b1fe551acb77dac643c6fda86084d8d446c10fe64b06a9cc29c4cc8540e7f2", size = 11942880, upload-time = "2026-05-11T18:53:13.536Z" }, + { url = "https://files.pythonhosted.org/packages/2c/20/559ace4200982c3887d0b86bfd0d856a2143ef8ddab63cc07934951a964c/pandas-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:a82d532a3351d435432cd913edbccaf8b8e01d4dd0e5ced5a8d2e8ecd94c7e44", size = 9757091, upload-time = "2026-05-11T18:53:16.306Z" }, + { url = "https://files.pythonhosted.org/packages/3a/66/69055a09fe200f29f922a3eeec4804611900b95f52d932ece3393c3c0c19/pandas-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:275c14e0fce14a2ec20eee474aecd305478ea3c1e6f6a9d8fe219a165542717e", size = 9057282, upload-time = "2026-05-11T18:53:18.768Z" }, + { url = "https://files.pythonhosted.org/packages/57/0e/efe801b0e6811e8e650cd21b7f2608e30f08a7067e2bf6e8752b0d56ee3c/pandas-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:46997386d528eb40376ecd6b033cf4a8a1e5282580f68f43de875b78cba2199d", size = 10767016, upload-time = "2026-05-11T18:53:21.227Z" }, + { url = "https://files.pythonhosted.org/packages/ea/dc/eb55135a1d5f0f0519f28da1f609a206d2cad1f9c35c32d51e38dd7261ae/pandas-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261e308dfb22448384b7580cf719d2f998fe2966c92893c3e77d14008af1f066", size = 10420210, upload-time = "2026-05-11T18:53:23.982Z" }, + { url = "https://files.pythonhosted.org/packages/c6/3e/b1d5d955ce33ffecb407465a60bc32769d74fcf68224b7ae67ae11d4dea4/pandas-3.0.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd1a5d1def6a46002e964510bdc67c368aa0951df5d1d9f8365336f5a1f490cd", size = 10336126, upload-time = "2026-05-11T18:53:26.731Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/a01261711ab60a22d71b862f0de20e4c504bf80457270ad8cb42110f6abc/pandas-3.0.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d72828c20c6d6e83e1e22a6a3b47b326b71664112fa9705dcbccfd7a39b62085", size = 10728051, upload-time = "2026-05-11T18:53:29.125Z" }, + { url = "https://files.pythonhosted.org/packages/e9/21/ea191195e587b18cf682e97f433f81b2d0fbe341380e80a3e0d6e4403c8e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d26cbe1fcfc12e8fd900e2454163e466b2d3af84f7c75481df7683ffc073d870", size = 11350796, upload-time = "2026-05-11T18:53:32.056Z" }, + { url = "https://files.pythonhosted.org/packages/64/69/f0eaaf54939f0e8c6768fd06be9af2cef9b36048b96dfb9e1b2c685a807e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e91cec1879ada0624fc3dc9953c5cbd60208e59c0db28f540c5d6d47502422f", size = 11799741, upload-time = "2026-05-11T18:53:34.985Z" }, + { url = "https://files.pythonhosted.org/packages/45/a4/865e0e510cae5fc2194de4db28be638952de942571ba9125934fd9c01d47/pandas-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:08d789b41f87e0905880e293cedf6197ce71fe67cc081358b1e148a491b9bd13", size = 10499958, upload-time = "2026-05-11T18:53:37.857Z" }, + { url = "https://files.pythonhosted.org/packages/86/54/effdcc3c0ff7a08037889200e148ebe94c16c4f653be078c7b3675955df1/pandas-3.0.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3650109c0f22879df8bd6179ab9ee3d7f1d1d4e7e0094a3f0032d9f51e2e64ac", size = 10336065, upload-time = "2026-05-11T18:53:41.099Z" }, + { url = "https://files.pythonhosted.org/packages/68/10/bf2d6738d72748b961a3751ab89522d58c54efc36a8e1a12161216cd45cf/pandas-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bab900348131a7db1f69a7309ef141fd5680f1487094193bcbbb61791573bf8f", size = 9926101, upload-time = "2026-05-11T18:53:43.515Z" }, + { url = "https://files.pythonhosted.org/packages/ae/e9/e35cf11c8a136e757b956f5f0efdcaa50aecde85ea055f1898dfc68262f3/pandas-3.0.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba7e08b9ac1d54569cd1e256e3668975ed624d6826f7b68df0342b012007bddb", size = 10457553, upload-time = "2026-05-11T18:53:46.394Z" }, + { url = "https://files.pythonhosted.org/packages/58/3b/1cdec6772bdbaf7b25dab360c59f03cadf05492dd724c6540af905389b07/pandas-3.0.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d71c63ae4ebdbf70209742096f1fc46a83a0613c99d4b23766cced9ff8cd62a", size = 10914065, upload-time = "2026-05-11T18:53:49.134Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c2/1ef644445fcd72e3627bceec77e3560636f87ddce4ed841afe76b83b5bf9/pandas-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e3a2ec42c98ffa2565a67e08e218d06d72576d758d90facb7c00805194d8f360", size = 11459188, upload-time = "2026-05-11T18:53:52.527Z" }, + { url = "https://files.pythonhosted.org/packages/7e/49/4d8d4f42cbc9c4adc7a1870f269c02cbd6cd40d059622c06fb298addcbad/pandas-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:335f62418ed562cfc3c49e9e196375c28b729dcef8543abf4f9438e381bf3c76", size = 11982966, upload-time = "2026-05-11T18:53:55.043Z" }, + { url = "https://files.pythonhosted.org/packages/38/55/792619469bab9882d8bbd5865d45a72f6478762d04a9af4bf0d08c503e95/pandas-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:3c20a521bbb85902f79f7270c80a59e1b5452d96d170c034f207181870f97ac5", size = 9876755, upload-time = "2026-05-11T18:53:58.067Z" }, + { url = "https://files.pythonhosted.org/packages/2a/af/33c469653b0ba03b50c3a98192d4c07f0c75c66b263ceb097fce0ee97d31/pandas-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:a2d2dff8a04f3917b55ab3910c32990f8ddf7eceba114947838cefa976a68977", size = 9198658, upload-time = "2026-05-11T18:54:00.733Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fa/b8c257bd76b8bd060c3a9151c1fca05e9b9c5e3af5d0f549c0356f6d143d/pandas-3.0.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0d589105b3c14645af1738ff279b2995102d8f7a03b0a66dc8d95550eb513e04", size = 10787242, upload-time = "2026-05-11T18:54:03.564Z" }, + { url = "https://files.pythonhosted.org/packages/54/eb/f19206ffb0bf1919002969aa448b4702c6594845156a6f8050674855aac3/pandas-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:13fc1e853d9e04743d11ba75a985ccbc2a317fe07d8af61e445a6fd24dacd6a6", size = 10436369, upload-time = "2026-05-11T18:54:06.311Z" }, + { url = "https://files.pythonhosted.org/packages/fd/24/c7c39fb4fe22b71a0c2d78bf0c585c600092d85f94f086d2b3b2f6ca27e2/pandas-3.0.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:819959dab7bbd0049c15623fbac4e29a191b9528160a61fb1032242d8ced2d9c", size = 10358306, upload-time = "2026-05-11T18:54:09.085Z" }, + { url = "https://files.pythonhosted.org/packages/16/ec/dd2a9eb7fa1204df88c0864164e35b228ac581062ac612ba0a67fd812e4c/pandas-3.0.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:60ae316d3fd75d1858d450d0db0103ea2be3e7d4a95ec2f064f7e2ae63f7b028", size = 10758394, upload-time = "2026-05-11T18:54:11.956Z" }, + { url = "https://files.pythonhosted.org/packages/95/6e/00c61ea8e85b4f6d8d35e11852a1a4998fc7fafc91c6a602d1cc9c972d64/pandas-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd3a518890b400d32f9023722dc9a9a5c969f00b415419a3c06c043f09bb5d7d", size = 11375717, upload-time = "2026-05-11T18:54:14.539Z" }, + { url = "https://files.pythonhosted.org/packages/31/89/8fc1c268969fac43688d65fd92e67df24bd128d53cb4d2eee534cd307399/pandas-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c39be2d709d01fa972a0cabc522389fceca4f3969332ba25a7d6c5802cf976a", size = 11828897, upload-time = "2026-05-11T18:54:17.146Z" }, + { url = "https://files.pythonhosted.org/packages/56/3b/e7d20dea247a3e6dc0bd8a6953854afbedc03951def4e7371e05e7263e25/pandas-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4db8c527972a821cf5286b40ccc57642a39bc62e62022b42f99f8a67fca8c3a1", size = 10900855, upload-time = "2026-05-11T18:54:19.72Z" }, + { url = "https://files.pythonhosted.org/packages/0f/54/68a0978d1ef8502b8492099beaa6e7a0c1b32e3b5d4f677f5810cb08711c/pandas-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b2c95f8bfc1ee412bf482605d7bfd30c12d1d26bd59fdd91efeef1d4718decb1", size = 9466464, upload-time = "2026-05-11T18:54:22.754Z" }, ] [[package]] @@ -4869,11 +4856,11 @@ wheels = [ [[package]] name = "pathable" -version = "0.5.0" +version = "0.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/55/b748445cb4ea6b125626f15379be7c96d1035d4fa3e8fee362fa92298abf/pathable-0.5.0.tar.gz", hash = "sha256:d81938348a1cacb525e7c75166270644782c0fb9c8cecc16be033e71427e0ef1", size = 16655, upload-time = "2026-02-20T08:47:00.748Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/f3/5a20387de9bcd0607871bfc2198ee0e15836da7baa4592ccd7f24c27c986/pathable-0.6.0.tar.gz", hash = "sha256:6404b8b82aef5ff0fd478934137128b99b12212ba35afdde5525ca4f8388ea58", size = 18970, upload-time = "2026-05-19T18:15:11.911Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/52/96/5a770e5c461462575474468e5af931cff9de036e7c2b4fea23c1c58d2cbe/pathable-0.5.0-py3-none-any.whl", hash = "sha256:646e3d09491a6351a0c82632a09c02cdf70a252e73196b36d8a15ba0a114f0a6", size = 16867, upload-time = "2026-02-20T08:46:59.536Z" }, + { url = "https://files.pythonhosted.org/packages/a2/e8/6d75ffd9784bce2e93d1ae4415649427e39a53bb172d4672b2b59c6f0a7b/pathable-0.6.0-py3-none-any.whl", hash = "sha256:82c4ca6c98c502ad12e0d4e9779b6210afee93c38990988c8c5d1b49bdcdf566", size = 18983, upload-time = "2026-05-19T18:15:10.728Z" }, ] [[package]] @@ -5068,11 +5055,11 @@ wheels = [ [[package]] name = "platformdirs" -version = "4.9.6" +version = "4.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", size = 31224, upload-time = "2026-05-28T03:32:53.587Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, + { url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743, upload-time = "2026-05-28T03:32:52.175Z" }, ] [[package]] @@ -5153,128 +5140,140 @@ wheels = [ [[package]] name = "propcache" -version = "0.4.1" +version = "0.5.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" }, - { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" }, - { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" }, - { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" }, - { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" }, - { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" }, - { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" }, - { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" }, - { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" }, - { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" }, - { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" }, - { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" }, - { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" }, - { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" }, - { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, - { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, - { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, - { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, - { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, - { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, - { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, - { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, - { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, - { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, - { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, - { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, - { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, - { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, - { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, - { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, - { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, - { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, - { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, - { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, - { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, - { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, - { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, - { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, - { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, - { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, - { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, - { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, - { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, - { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, - { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, - { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, - { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, - { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, - { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, - { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, - { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, - { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, - { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, - { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, - { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, - { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, - { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, - { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, - { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, - { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, - { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, - { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, - { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, - { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, - { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, - { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, - { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, - { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, - { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, - { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208, upload-time = "2026-05-08T21:02:12.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/f1/8a8cc1c2c7e7934ab77e0163414f736fadbc0f5e8dd9673b952355ac175b/propcache-0.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74b70780220e2dd89175ca24b81b68b67c83db499ae611e7f2313cb329801c78", size = 90744, upload-time = "2026-05-08T20:59:45.799Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f4/651b1225e976bd1a2ba5cfba0c29d096581c2636b437e3a9a7ab6276270a/propcache-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4840ab0ae0216d952f4b53dc6d0b992bfc2bedbfe360bdd9b548bc184c08959", size = 52033, upload-time = "2026-05-08T20:59:47.408Z" }, + { url = "https://files.pythonhosted.org/packages/15/a8/8ede85d6aa1f79fc7dc2f8fd2c8d65920b8272c3892903c8a1affde48cfb/propcache-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6844ba6364fb12f403928a82cfd295ab103a2b315c77c747b2dbe4a41894ea7", size = 52754, upload-time = "2026-05-08T20:59:49.202Z" }, + { url = "https://files.pythonhosted.org/packages/7d/fe/b3551b41bbc2f5b5bb088fc6920567cd43101253e68fbaa261339eb96fe1/propcache-0.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2293949b855ce597f2826452d17c2d545fb5622379c4ea6fdf525e9b8e8a2511", size = 57573, upload-time = "2026-05-08T20:59:50.778Z" }, + { url = "https://files.pythonhosted.org/packages/83/27/ab851ebd1b7172e3e161f5f8d39e315d54a91bea246f01f4d872d3376aef/propcache-0.5.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0fd59b5af35f74da48d905dcbad55449ba13be91823cb05a9bd590bbf5b61660", size = 60645, upload-time = "2026-05-08T20:59:52.227Z" }, + { url = "https://files.pythonhosted.org/packages/95/7d/466b3d18022e9897cbda9c735c493c5bd747d7a4c6f5ea1480b4cec434b6/propcache-0.5.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29f9309a2e42b0d273be006fdb4be2d6c39a47f6f57d8fb1cf9f81481df81b66", size = 61563, upload-time = "2026-05-08T20:59:53.866Z" }, + { url = "https://files.pythonhosted.org/packages/27/1b/16ab7f2cf2041da2f60d156ba64c2484eadf9168075b4ff43c3ef60045af/propcache-0.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5aaa2b923c1944ac8febd6609cb373540a5563e7cbcb0fd770f75dace2eb817b", size = 58888, upload-time = "2026-05-08T20:59:55.457Z" }, + { url = "https://files.pythonhosted.org/packages/0a/67/bb777ffd907633563bf35fd859c4ce97b0512c32f4633cf5d1eb7c33512b/propcache-0.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66ea454f095ddf5b6b14f56c064c0941c4788be11e18d2464cf643bf7203ff67", size = 59253, upload-time = "2026-05-08T20:59:57.075Z" }, + { url = "https://files.pythonhosted.org/packages/b9/42/64f8d90b73fd9cdc1499b48057ff6d9cd2a98a25734c9bb62ecf07e87061/propcache-0.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:95f1e3f4760d404b13c9976c0229b2b49a3c8e2c62a9ce92efdd2b11ada75e3f", size = 57558, upload-time = "2026-05-08T20:59:58.602Z" }, + { url = "https://files.pythonhosted.org/packages/eb/02/dba5bc03c9041f2092ea55a449caf5dfe68352c6654511b29ba0654ddb69/propcache-0.5.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:85341b12b9d55bad0bded24cac341bb34289469e03a11f3f583ea1cc1db0326c", size = 55007, upload-time = "2026-05-08T20:59:59.837Z" }, + { url = "https://files.pythonhosted.org/packages/14/c0/43f649c7aa2a77a3b100d84e9dea3a483120ecb608bfe36ce49eaff517fe/propcache-0.5.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:26a4dca084132874e639895c3135dfad5eb20bae209f62d1aeb31b03e601c3c0", size = 60355, upload-time = "2026-05-08T21:00:01.144Z" }, + { url = "https://files.pythonhosted.org/packages/83/c0/435dafd27f1cb4a495381dae60e25883ccfe4020bb72818e8184c1678092/propcache-0.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3b199b9b2b3d6a7edf3183ba8a9a137a22b97f7df525feb5ae1eccf026d2a9c6", size = 59057, upload-time = "2026-05-08T21:00:02.401Z" }, + { url = "https://files.pythonhosted.org/packages/53/ae/6e292df9135d659944e96cb3389258e4a663e5b2b5f6c217ef0ddc8d2f73/propcache-0.5.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e59bc9e66329185b93dab73f210f1a37f81cb40f321501db8017c9aea15dba27", size = 61938, upload-time = "2026-05-08T21:00:03.638Z" }, + { url = "https://files.pythonhosted.org/packages/0b/42/314ebc50d8159055411fd6b0bda322ff510e4b1f7d2e4927940ad0f6af20/propcache-0.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:552ffadf6ad409844bc5919c42a0a83d88314cedddaea0e41e80a8b8fffe881f", size = 59731, upload-time = "2026-05-08T21:00:04.881Z" }, + { url = "https://files.pythonhosted.org/packages/b8/9b/2da6dee38871c3c8772fabc2758325a5c9077d6d18c597737dc04dd884cd/propcache-0.5.2-cp311-cp311-win32.whl", hash = "sha256:cd416c1de191973c52ff1a12a57446bfc7642797b282d7caf2162d7d1b8aa9a0", size = 38966, upload-time = "2026-05-08T21:00:06.511Z" }, + { url = "https://files.pythonhosted.org/packages/42/4e/f17363fb58c0afe05b067361cb6d86ed2d29de6506779a27547c4d183075/propcache-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:44e488ef40dbb452700b2b1f8188934121f6648f52c295055662d2191959ff82", size = 42135, upload-time = "2026-05-08T21:00:08.088Z" }, + { url = "https://files.pythonhosted.org/packages/c6/eb/6af6685077d22e8b33358d3c548e3282706a0b3cd85044ffba4e5dd08e3b/propcache-0.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:54adaa85a22078d1e306304a40984dc5be99d599bf3dc0a24dc98f7daeab89ab", size = 38381, upload-time = "2026-05-08T21:00:09.692Z" }, + { url = "https://files.pythonhosted.org/packages/4a/cb/e27bc2b2737a0bb49962b275efa051e8f1c35a936df7d5139b6b658b7dc9/propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba", size = 95887, upload-time = "2026-05-08T21:00:11.277Z" }, + { url = "https://files.pythonhosted.org/packages/e6/13/b8ae04c59392f8d11c6cd9fb4011d1dc7c86b81225c770280300e259ffe1/propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a", size = 54654, upload-time = "2026-05-08T21:00:12.604Z" }, + { url = "https://files.pythonhosted.org/packages/2c/7d/49777a3e20b55863d4794384a38acd460c04157b0a00f8602b0d508b8431/propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf", size = 55190, upload-time = "2026-05-08T21:00:13.935Z" }, + { url = "https://files.pythonhosted.org/packages/44/c7/085d0cd63062e84044e3f05797749c3f8e3938ff3aeb0eb2f69d43fafc91/propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144", size = 59995, upload-time = "2026-05-08T21:00:15.526Z" }, + { url = "https://files.pythonhosted.org/packages/9c/42/32cf8e3009e92b2645cf1e944f701e8ea4e924dffde1ee26db860bcbf7e4/propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9", size = 63422, upload-time = "2026-05-08T21:00:16.824Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1b/f112433f99fc979431b87a39ef169e3f8df070d99a72792c56d6937ac48b/propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42", size = 64342, upload-time = "2026-05-08T21:00:18.362Z" }, + { url = "https://files.pythonhosted.org/packages/14/15/5574111ae50dd6e879456888c0eadd4c5a869959775854e18e18a6b345f3/propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476", size = 61639, upload-time = "2026-05-08T21:00:19.692Z" }, + { url = "https://files.pythonhosted.org/packages/cc/da/4d775080b1490c0ae604acda868bd71aabe3a89ed16f2aa4339eb8a283e7/propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba", size = 61588, upload-time = "2026-05-08T21:00:21.155Z" }, + { url = "https://files.pythonhosted.org/packages/04/ac/f076982cbe2195ee9cf32de5a1e46951d9fb399fc207f390562dd0fd8fb2/propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a", size = 60029, upload-time = "2026-05-08T21:00:22.713Z" }, + { url = "https://files.pythonhosted.org/packages/70/60/189be62e0dd898dce3b331e1b8c7a543cd3a405ac0c81fe8ee8a9d5d77e1/propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64", size = 56774, upload-time = "2026-05-08T21:00:24.001Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/93377b9c7939c1ffae98f878dee955efadfd638078bc86dbc21f9d52f651/propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913", size = 63532, upload-time = "2026-05-08T21:00:25.545Z" }, + { url = "https://files.pythonhosted.org/packages/14/f9/590ef6cfb9b8028d516d287812ece32bb0bc5f11fbb9c8bf6b2e6313fec8/propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1", size = 61592, upload-time = "2026-05-08T21:00:27.186Z" }, + { url = "https://files.pythonhosted.org/packages/b4/5e/70958b3034c297a630bba2f17ca7abc2d5f39a803ad7e370ab79d1ecd022/propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33", size = 64788, upload-time = "2026-05-08T21:00:28.8Z" }, + { url = "https://files.pythonhosted.org/packages/12/fd/77fe5936d8c3086ca9048f7f415f122ed82e53884a9ec193646b42deef06/propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a", size = 62514, upload-time = "2026-05-08T21:00:30.098Z" }, + { url = "https://files.pythonhosted.org/packages/cf/74/66bd798b5b3be70aa1b391f5cc9d6a0a5532d7fd3b19ec0b213e72e6ad9d/propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031", size = 39018, upload-time = "2026-05-08T21:00:31.622Z" }, + { url = "https://files.pythonhosted.org/packages/61/7c/5c0d34aa3024694d6dcb9271cdbdd08c4e47c1c0ad95ec7e7bc74cdea145/propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42", size = 42322, upload-time = "2026-05-08T21:00:32.918Z" }, + { url = "https://files.pythonhosted.org/packages/4d/91/875812f1a3feb20ceba818ef39fbe4d92f1081e04ac815c822496d0d038b/propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84", size = 38172, upload-time = "2026-05-08T21:00:35.124Z" }, + { url = "https://files.pythonhosted.org/packages/c5/09/f049e45385503fe67db75a6b6186a7b9f0c3930366dc960522c312a825b1/propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a", size = 94457, upload-time = "2026-05-08T21:00:36.355Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/83d1d05655baf63113731bd5a1008435e14f8d1e5a06cbe4ec5b23ad7a31/propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117", size = 53835, upload-time = "2026-05-08T21:00:38.072Z" }, + { url = "https://files.pythonhosted.org/packages/a9/12/a6ba6482bb5ea3260c000c9b20881c95fa11c6b30173715668259f844ed7/propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098", size = 54545, upload-time = "2026-05-08T21:00:39.319Z" }, + { url = "https://files.pythonhosted.org/packages/a9/19/7fa086f5764c59ec8a8e157cd93aa8497acc00aba9dcdec56bfffb32602d/propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4", size = 59886, upload-time = "2026-05-08T21:00:40.621Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e4/5d7663dc8235956c8f5281698a3af1d351d8820341ddd890f59d9a9127f2/propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e", size = 63261, upload-time = "2026-05-08T21:00:41.775Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/15a03adee24d6350da4292caeac44c34c033d2afe5e87eb370f38854560f/propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7", size = 64184, upload-time = "2026-05-08T21:00:43.018Z" }, + { url = "https://files.pythonhosted.org/packages/8b/c6/979176efdaa3d239e36d503d5af63a0a773b36662ed8f52e5b6a6d9fd40e/propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d", size = 61534, upload-time = "2026-05-08T21:00:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/c8/22/63e8cd1bae4c2d2be6493b6b7d10566ddafad88137cfbc99964a1119853c/propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a", size = 61500, upload-time = "2026-05-08T21:00:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/60/5a/28e5d9acbac1cc9ccb67045e8c1b943aa8d79fdf39c93bd73cacd68008ea/propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2", size = 59994, upload-time = "2026-05-08T21:00:47.093Z" }, + { url = "https://files.pythonhosted.org/packages/f3/40/db650677f554a95b9c01a7c9d93d629e93a15562f5deb4573c9ee136fed2/propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa", size = 56884, upload-time = "2026-05-08T21:00:48.376Z" }, + { url = "https://files.pythonhosted.org/packages/80/45/70b39b89516ff8b96bf732fa6fded8cef20f293cb1508690101c3c07ec51/propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853", size = 63464, upload-time = "2026-05-08T21:00:49.954Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e2/fa59d3a89eac5534293124af4f1d0d0ada091ce4a0ab4610ce03fd2bdd8d/propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a", size = 61588, upload-time = "2026-05-08T21:00:51.281Z" }, + { url = "https://files.pythonhosted.org/packages/0b/97/efb547a55c4bc7381cfb202d6a2239ac621045277bc1ea5dfd3a7f0516c0/propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704", size = 64667, upload-time = "2026-05-08T21:00:52.602Z" }, + { url = "https://files.pythonhosted.org/packages/92/56/f5c7d9b4b7595d5127da38974d791b2153f3d1eae6c674af3583ace92ad3/propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4", size = 62463, upload-time = "2026-05-08T21:00:54.303Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3b/484a3a65fc9f9f60c41dcd17b428bace5389544e2c680994534a20755066/propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d", size = 38621, upload-time = "2026-05-08T21:00:55.808Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fd/3f0f10dba4dabad3bf53102be007abf55481067952bde0fdddff439e7c61/propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757", size = 41649, upload-time = "2026-05-08T21:00:57.061Z" }, + { url = "https://files.pythonhosted.org/packages/90/ec/6ce619cc32bb500a482f811f9cd509368b4e58e638d13f2c68f370d6b475/propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f", size = 37636, upload-time = "2026-05-08T21:00:58.646Z" }, + { url = "https://files.pythonhosted.org/packages/1b/82/c1d268bbbf2ef981c5bf0fbbe746db617c66e3bcefe431a1aa8943fbe23a/propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d", size = 98872, upload-time = "2026-05-08T21:00:59.889Z" }, + { url = "https://files.pythonhosted.org/packages/f4/d4/52c871e73e864e6b34c0e2d58ac1ec5ccd149497ddc7ad2137ae98323a35/propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa", size = 56257, upload-time = "2026-05-08T21:01:01.195Z" }, + { url = "https://files.pythonhosted.org/packages/67/f0/9b90ca2a210b3d09bcfcd96ecd0f55545c091535abce2a45de2775cfd357/propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94", size = 56696, upload-time = "2026-05-08T21:01:02.941Z" }, + { url = "https://files.pythonhosted.org/packages/9d/0e/6e9d4ba07c8e56e21ddec1e75f12148142b21ca83a51871babce095334f4/propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164", size = 62378, upload-time = "2026-05-08T21:01:04.475Z" }, + { url = "https://files.pythonhosted.org/packages/65/19/c10badaa463dde8a27ce884f8ee2ec37e6035b7c9f5ff0c8f74f06f08dac/propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f", size = 65283, upload-time = "2026-05-08T21:01:05.959Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b6/93bea99ca80e19cef6512a8580e5b7857bbe09422d9daa7fd4ef5723306c/propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c", size = 66616, upload-time = "2026-05-08T21:01:07.228Z" }, + { url = "https://files.pythonhosted.org/packages/83/e4/5c7462e50625f051f37fb38b8224f7639f667184bbd34424ec83819bb1b7/propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc", size = 63773, upload-time = "2026-05-08T21:01:08.514Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b6/99238894047b13c823be25027e736626cd414a52a5e30d2c3347c2733529/propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f", size = 63664, upload-time = "2026-05-08T21:01:09.874Z" }, + { url = "https://files.pythonhosted.org/packages/85/1e/a3a1a63116a2b8edb415a8bb9a6f0c34bd03830b1e18e8ce2904e1dc1cf4/propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb", size = 62643, upload-time = "2026-05-08T21:01:11.132Z" }, + { url = "https://files.pythonhosted.org/packages/e4/03/893cf147de2fc6543c5eaa07ad833170e7e2a2385725bbebe8c0503723bb/propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751", size = 59595, upload-time = "2026-05-08T21:01:12.387Z" }, + { url = "https://files.pythonhosted.org/packages/86/3b/04c1a2e12c57766568ba75ba72b3bf2042818d4c1425fab6fc07155c7cff/propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836", size = 65711, upload-time = "2026-05-08T21:01:13.676Z" }, + { url = "https://files.pythonhosted.org/packages/1c/34/80f8d0099f8d6bacc4de1624c85672681c8cd1149ca2da0e38fd120b817f/propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f", size = 64247, upload-time = "2026-05-08T21:01:14.936Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1a/8b08f3a5f1037e9e370c55883ceeeee0f6dd0416fb2d2d67b8bfc91f2a79/propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55", size = 67102, upload-time = "2026-05-08T21:01:16.281Z" }, + { url = "https://files.pythonhosted.org/packages/34/68/8bdb7bb7756d76e005490649d10e4a8369e610c74d619f71e1aedf889e9c/propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568", size = 64964, upload-time = "2026-05-08T21:01:17.57Z" }, + { url = "https://files.pythonhosted.org/packages/0a/aa/50fb0b5d3968b61a510926ff8b8465f1d6e976b3ab74496d7a4b9fc42515/propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191", size = 42546, upload-time = "2026-05-08T21:01:18.946Z" }, + { url = "https://files.pythonhosted.org/packages/ae/4c/0ddbae64321bd4a95bcbfc19307238016b5b1fee645c84626c8d539e5b74/propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7", size = 46330, upload-time = "2026-05-08T21:01:20.162Z" }, + { url = "https://files.pythonhosted.org/packages/00/d9/9cddc8efb78d8af264c5ec9f6d10b62f57c515feda8d321595f56010fb23/propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96", size = 40521, upload-time = "2026-05-08T21:01:21.399Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ea/23ee535d90ce8bcc465a3028eb3cc0ce3bd1005f4bb27710b30587de798d/propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999", size = 94662, upload-time = "2026-05-08T21:01:22.683Z" }, + { url = "https://files.pythonhosted.org/packages/b5/06/c5a52f419b5d8972f8d46a7577476090d8e3263ff589ce40b5ca4968d5be/propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e", size = 53928, upload-time = "2026-05-08T21:01:23.986Z" }, + { url = "https://files.pythonhosted.org/packages/63/b1/4260d67d6bd85e58a66b72d54ce15d5de789b6f3870cc6bedf8ff9667401/propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539", size = 54650, upload-time = "2026-05-08T21:01:25.305Z" }, + { url = "https://files.pythonhosted.org/packages/70/06/2f46c318e3307cd7a6a7481def374ce838c0fe20084b39dd54b0879d0e99/propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e", size = 59912, upload-time = "2026-05-08T21:01:26.545Z" }, + { url = "https://files.pythonhosted.org/packages/4c/29/fe1aebec2ce57ab985a9c382bded1124431f85078113aa222c5d278430d4/propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979", size = 63300, upload-time = "2026-05-08T21:01:27.937Z" }, + { url = "https://files.pythonhosted.org/packages/b4/18/2334b26768b6c82be8c69e83671b767d5ef426aa09b0cba6c2ea47816774/propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80", size = 64208, upload-time = "2026-05-08T21:01:29.484Z" }, + { url = "https://files.pythonhosted.org/packages/2b/76/7f1bfd6afff4c5e38e36a3c6d68eb5f4b7311ea80baf693db78d95b603c4/propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825", size = 61633, upload-time = "2026-05-08T21:01:31.068Z" }, + { url = "https://files.pythonhosted.org/packages/c4/46/b3ff8aba2b4953a3e50de2cf72f1b5748b8eca93b15f3dc2c84339084c09/propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39", size = 61724, upload-time = "2026-05-08T21:01:32.374Z" }, + { url = "https://files.pythonhosted.org/packages/c5/01/814cfcafbcff954f94c01cf30e097ddc88a076b5440fbcf4570753437d40/propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4", size = 60069, upload-time = "2026-05-08T21:01:33.67Z" }, + { url = "https://files.pythonhosted.org/packages/da/68/5c6f7622d510cc666a300687e06fd060c1a43361c0c9b20d284f06d8096a/propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5", size = 57099, upload-time = "2026-05-08T21:01:34.915Z" }, + { url = "https://files.pythonhosted.org/packages/55/27/9cb0b4c679124085327957d42521c99dba04c88c90c3e55a6f0b633ebccc/propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702", size = 63391, upload-time = "2026-05-08T21:01:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/f0/9d/7258aaa5bdf60fc6f27591eef6fe52768cb0beda7140be477c8b12c9794a/propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3", size = 61626, upload-time = "2026-05-08T21:01:37.545Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0d/41c602003e8a9b16fe1e7eadf62c7bfba9d5474370b24200bf48b315f45f/propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5", size = 64781, upload-time = "2026-05-08T21:01:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f3/38e66b1856e9bd079deea015bc4a55f7767c0e4db2f7dcf69e7e680ba4ce/propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4", size = 62570, upload-time = "2026-05-08T21:01:40.415Z" }, + { url = "https://files.pythonhosted.org/packages/95/ca/bbfe9b910ce57dde8bb4876b4520fc02a4e89497c10de26be936758a3aaa/propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0", size = 39436, upload-time = "2026-05-08T21:01:41.654Z" }, + { url = "https://files.pythonhosted.org/packages/61/d2/45c9defbaa1ea297035d9d4cce9e8f80daafbf19319c6007f157c6256ea9/propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c", size = 42373, upload-time = "2026-05-08T21:01:43.041Z" }, + { url = "https://files.pythonhosted.org/packages/44/68/9ea5103f41d5217d7d6ec24db90018e23aebec070c3f9a6e54d12b841fd8/propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0", size = 38554, upload-time = "2026-05-08T21:01:44.336Z" }, + { url = "https://files.pythonhosted.org/packages/8a/81/fadf555f42d3b762eea8a53950b0489fdc0aa9da5f8ed9e10ce0a4e01b48/propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb", size = 99395, upload-time = "2026-05-08T21:01:45.883Z" }, + { url = "https://files.pythonhosted.org/packages/f5/c9/c61e134a686949cf7971af3a390148b1156f7be81c73bc0cd12c873e2d48/propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078", size = 56653, upload-time = "2026-05-08T21:01:47.307Z" }, + { url = "https://files.pythonhosted.org/packages/cb/73/daf935ea7048ddd7ec8eec5345b4a40b619d2d178b3c0a0900796bc3c794/propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa", size = 56914, upload-time = "2026-05-08T21:01:48.573Z" }, + { url = "https://files.pythonhosted.org/packages/79/9f/aba959b435ea18617edd7cf0a7ad0b9c574b8fc7e3d2cd55fb59cb255d33/propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917", size = 62567, upload-time = "2026-05-08T21:01:49.903Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a1/859942de9a791ff42f6141736f5b37749b8f53e65edfa49638c67dd67e6a/propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe", size = 65542, upload-time = "2026-05-08T21:01:51.204Z" }, + { url = "https://files.pythonhosted.org/packages/b5/61/315bc0fd6c0fc7f80a528b8afd209e5fc4a875ea79571b91b8f50f442907/propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03", size = 66845, upload-time = "2026-05-08T21:01:52.539Z" }, + { url = "https://files.pythonhosted.org/packages/47/f7/9f8122e3132e8e354ac41975ef8f1099be7d5a16bc7ae562734e993665c0/propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335", size = 63985, upload-time = "2026-05-08T21:01:53.847Z" }, + { url = "https://files.pythonhosted.org/packages/c8/54/c317819ec157cbf6f35df9df9657a6f82daf34d5faf15948b2f639c2192e/propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285", size = 63999, upload-time = "2026-05-08T21:01:55.179Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/387e3f7dfce0a9233df41fb888aa1c30222cb4bbbf09537c02dd9bd85fe2/propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837", size = 62779, upload-time = "2026-05-08T21:01:57.489Z" }, + { url = "https://files.pythonhosted.org/packages/a1/9c/596784cb5824ed61ee960d3f8655a3f0993e107c6e98ab6c818b7fb92ccb/propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8", size = 59796, upload-time = "2026-05-08T21:01:58.736Z" }, + { url = "https://files.pythonhosted.org/packages/c2/3d/1a6cfa1726a48542c1e8784a0761421476a5b68e09b7f36bf95eb954aaba/propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366", size = 66023, upload-time = "2026-05-08T21:02:00.228Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0e/05fd6990369477076e4e280bcb970de760fddf0161a46e988bc95f7940ec/propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56", size = 64448, upload-time = "2026-05-08T21:02:01.888Z" }, + { url = "https://files.pythonhosted.org/packages/cd/86/5f8da315a4309c62c10c0b2516b17492d5d3bbe1bb862b96604db67e2a37/propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d", size = 67329, upload-time = "2026-05-08T21:02:03.484Z" }, + { url = "https://files.pythonhosted.org/packages/da/d3/3368efe79ab21f0cdf86ef49895811c9cc933131d4cde1f28a624e22e712/propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2", size = 65172, upload-time = "2026-05-08T21:02:04.745Z" }, + { url = "https://files.pythonhosted.org/packages/d5/07/127e8b0bacfb325396196f9d976a22453049b89b9b2b08477cc3145faa44/propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821", size = 43813, upload-time = "2026-05-08T21:02:06.025Z" }, + { url = "https://files.pythonhosted.org/packages/88/fb/46dad6c0ae49ed230ab1b16c890c2b6314e2403e6c412976f4a72d64a527/propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370", size = 47764, upload-time = "2026-05-08T21:02:07.353Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c4/a47d0a63aa309d10d59ede6e9d4cff03a344a79d1f0f4cd0cd74997b53e0/propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6", size = 41140, upload-time = "2026-05-08T21:02:09.065Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, ] [[package]] name = "proto-plus" -version = "1.27.2" +version = "1.28.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/81/0d/94dfe80193e79d55258345901acd2917523d56e8381bc4dee7fd38e3868a/proto_plus-1.27.2.tar.gz", hash = "sha256:b2adde53adadf75737c44d3dcb0104fde65250dfc83ad59168b4aa3e574b6a24", size = 57204, upload-time = "2026-03-26T22:18:57.174Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/56/e647b0c675392d2da368da7b6f158f7368b18542fd6f7d7400a2f39de000/proto_plus-1.28.0.tar.gz", hash = "sha256:38e5696342835b08fc116f30a25665b29531cda9d5d5643e9b81fc312385abd9", size = 57221, upload-time = "2026-05-07T08:04:50.811Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/f3/1fba73eeffafc998a25d59703b63f8be4fe8a5cb12eaff7386a0ba0f7125/proto_plus-1.27.2-py3-none-any.whl", hash = "sha256:6432f75893d3b9e70b9c412f1d2f03f65b11fb164b793d14ae2ca01821d22718", size = 50450, upload-time = "2026-03-26T22:13:42.927Z" }, + { url = "https://files.pythonhosted.org/packages/7c/20/b122d4626976acb81132036d2ad1bb35a1a8775fceb837ec30964622516a/proto_plus-1.28.0-py3-none-any.whl", hash = "sha256:a630604310899e73c59ec302e5765c058d412b2f090b9c79c8822589f14955b8", size = 50410, upload-time = "2026-05-07T08:03:31.962Z" }, ] [[package]] name = "protobuf" -version = "7.34.1" +version = "7.35.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/6b/a0e95cad1ad7cc3f2c6821fcab91671bd5b78bd42afb357bb4765f29bc41/protobuf-7.34.1.tar.gz", hash = "sha256:9ce42245e704cc5027be797c1db1eb93184d44d1cdd71811fb2d9b25ad541280", size = 454708, upload-time = "2026-03-20T17:34:47.036Z" } +sdist = { url = "https://files.pythonhosted.org/packages/60/fd/5b1491d9e4b586d621c54f4c36b888714164b6875f8d6afa3f9072906a51/protobuf-7.35.0.tar.gz", hash = "sha256:a2efd84605f41e559f1881b0912b44099d0a2ac9bf46b3474823f10fb393b0e6", size = 458677, upload-time = "2026-05-19T23:02:29.197Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/11/3325d41e6ee15bf1125654301211247b042563bcc898784351252549a8ad/protobuf-7.34.1-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8b2cc79c4d8f62b293ad9b11ec3aebce9af481fa73e64556969f7345ebf9fc7", size = 429247, upload-time = "2026-03-20T17:34:37.024Z" }, - { url = "https://files.pythonhosted.org/packages/eb/9d/aa69df2724ff63efa6f72307b483ce0827f4347cc6d6df24b59e26659fef/protobuf-7.34.1-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:5185e0e948d07abe94bb76ec9b8416b604cfe5da6f871d67aad30cbf24c3110b", size = 325753, upload-time = "2026-03-20T17:34:38.751Z" }, - { url = "https://files.pythonhosted.org/packages/92/e8/d174c91fd48e50101943f042b09af9029064810b734e4160bbe282fa1caa/protobuf-7.34.1-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:403b093a6e28a960372b44e5eb081775c9b056e816a8029c61231743d63f881a", size = 340198, upload-time = "2026-03-20T17:34:39.871Z" }, - { url = "https://files.pythonhosted.org/packages/53/1b/3b431694a4dc6d37b9f653f0c64b0a0d9ec074ee810710c0c3da21d67ba7/protobuf-7.34.1-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:8ff40ce8cd688f7265326b38d5a1bed9bfdf5e6723d49961432f83e21d5713e4", size = 324267, upload-time = "2026-03-20T17:34:41.1Z" }, - { url = "https://files.pythonhosted.org/packages/85/29/64de04a0ac142fb685fd09999bc3d337943fb386f3a0ec57f92fd8203f97/protobuf-7.34.1-cp310-abi3-win32.whl", hash = "sha256:34b84ce27680df7cca9f231043ada0daa55d0c44a2ddfaa58ec1d0d89d8bf60a", size = 426628, upload-time = "2026-03-20T17:34:42.536Z" }, - { url = "https://files.pythonhosted.org/packages/4d/87/cb5e585192a22b8bd457df5a2c16a75ea0db9674c3a0a39fc9347d84e075/protobuf-7.34.1-cp310-abi3-win_amd64.whl", hash = "sha256:e97b55646e6ce5cbb0954a8c28cd39a5869b59090dfaa7df4598a7fba869468c", size = 437901, upload-time = "2026-03-20T17:34:44.112Z" }, - { url = "https://files.pythonhosted.org/packages/88/95/608f665226bca68b736b79e457fded9a2a38c4f4379a4a7614303d9db3bc/protobuf-7.34.1-py3-none-any.whl", hash = "sha256:bb3812cd53aefea2b028ef42bd780f5b96407247f20c6ef7c679807e9d188f11", size = 170715, upload-time = "2026-03-20T17:34:45.384Z" }, + { url = "https://files.pythonhosted.org/packages/83/ee/93d06e358a4aa32280b00e722d3ea0a1f25fc3cc5778d80581c9cca2c10e/protobuf-7.35.0-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:66be6c513931c794fa92c080ffee41671390da3d79da219cf9c0c0907f035dda", size = 433225, upload-time = "2026-05-19T23:02:19.884Z" }, + { url = "https://files.pythonhosted.org/packages/8b/39/1c76c2da93f3c507e958e0aecee2391cc44d4625de6c728bbc555195b5a8/protobuf-7.35.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:fcbe42a4ac09d3ec9c987ddfcd956afd0b15f1ff613bd8371bde9405ffd5c8e5", size = 328847, upload-time = "2026-05-19T23:02:22.3Z" }, + { url = "https://files.pythonhosted.org/packages/91/1a/39f7ce90a238c1a987a4d81ec26379e02ca0aff367de68e4a1fa474215b9/protobuf-7.35.0-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:4cbf5cc286130e06a6c9bbefac442431173906dfcc979712183d4adcc01b37ee", size = 344030, upload-time = "2026-05-19T23:02:23.591Z" }, + { url = "https://files.pythonhosted.org/packages/70/5b/6baf9008817964454055ff3fe65f1de0b5f1e26c80c82f7fb108b7cd4ea3/protobuf-7.35.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:6c0f98f10c8a05ea30f8993dfef2de093d27b490fdae78bb60c8343795d55011", size = 327130, upload-time = "2026-05-19T23:02:24.637Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e5/e46adb0badc388bfb84877a5f9f026aff63f60e611016cf64dbe77e05446/protobuf-7.35.0-cp310-abi3-win32.whl", hash = "sha256:4c4617b83ade0e279d1d2bfe04025a1adb87f9ed657de038620dc0ff959357f6", size = 428946, upload-time = "2026-05-19T23:02:25.741Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ab/547fbd9e16d879dd13c167478f8ae0a83a428008ca07a5e06acdc23ad473/protobuf-7.35.0-cp310-abi3-win_amd64.whl", hash = "sha256:f05bcadf9a2a6b8dda047007075135fb7d08c73d9177aabc067e1be46881a201", size = 439996, upload-time = "2026-05-19T23:02:26.808Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ef/50433d346c56657a70d27f156c7b349ac59a068b01de4eb796e747eecc43/protobuf-7.35.0-py3-none-any.whl", hash = "sha256:c13f325cf242bad135c350629eeb5d54b24228eb472fb3e2e9ebbd4c5dc20ca0", size = 171659, upload-time = "2026-05-19T23:02:27.842Z" }, ] [[package]] @@ -5361,15 +5360,15 @@ wheels = [ [[package]] name = "py-key-value-aio" -version = "0.4.4" +version = "0.4.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "beartype" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/3c/0397c072a38d4bc580994b42e0c90c5f44f679303489e4376289534735e5/py_key_value_aio-0.4.4.tar.gz", hash = "sha256:e3012e6243ed7cc09bb05457bd4d03b1ba5c2b1ca8700096b3927db79ffbbe55", size = 92300, upload-time = "2026-02-16T21:21:43.245Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/e2/d689d922894a7ecde73b6daeaf9b13dab5aae06fe6aaaf7514722644d382/py_key_value_aio-0.4.5.tar.gz", hash = "sha256:c6563a2c6abe5da5e20f4f9e875c2a9b425a2244a54fadbf46cf140a9eea45d7", size = 107547, upload-time = "2026-05-27T16:37:08.107Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/69/f1b537ee70b7def42d63124a539ed3026a11a3ffc3086947a1ca6e861868/py_key_value_aio-0.4.4-py3-none-any.whl", hash = "sha256:18e17564ecae61b987f909fc2cd41ee2012c84b4b1dcb8c055cf8b4bc1bf3f5d", size = 152291, upload-time = "2026-02-16T21:21:44.241Z" }, + { url = "https://files.pythonhosted.org/packages/f6/95/b8ba862968712caa12a19666175334fa979e1f198b896a430adb3bacfe87/py_key_value_aio-0.4.5-py3-none-any.whl", hash = "sha256:ab862adbcb8c72547d1c57821f22cbbb71ab86509039c96f36e914e0336c8dd7", size = 170005, upload-time = "2026-05-27T16:37:06.629Z" }, ] [package.optional-dependencies] @@ -5478,7 +5477,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.13.3" +version = "2.13.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -5486,9 +5485,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d9/e4/40d09941a2cebcb20609b86a559817d5b9291c49dd6f8c87e5feffbe703a/pydantic-2.13.3.tar.gz", hash = "sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d", size = 844068, upload-time = "2026-04-20T14:46:43.632Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/0a/fd7d723f8f8153418fb40cf9c940e82004fce7e987026b08a68a36dd3fe7/pydantic-2.13.3-py3-none-any.whl", hash = "sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927", size = 471981, upload-time = "2026-04-20T14:46:41.402Z" }, + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, ] [package.optional-dependencies] @@ -5498,104 +5497,104 @@ email = [ [[package]] name = "pydantic-core" -version = "2.46.3" +version = "2.46.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/ef/f7abb56c49382a246fd2ce9c799691e3c3e7175ec74b14d99e798bcddb1a/pydantic_core-2.46.3.tar.gz", hash = "sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c", size = 471412, upload-time = "2026-04-20T14:40:56.672Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/a2/1ba90a83e85a3f94c796b184f3efde9c72f2830dcda493eea8d59ba78e6d/pydantic_core-2.46.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5", size = 2106740, upload-time = "2026-04-20T14:41:20.932Z" }, - { url = "https://files.pythonhosted.org/packages/b6/f6/99ae893c89a0b9d3daec9f95487aa676709aa83f67643b3f0abaf4ab628a/pydantic_core-2.46.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c", size = 1948293, upload-time = "2026-04-20T14:43:42.115Z" }, - { url = "https://files.pythonhosted.org/packages/3e/b8/2e8e636dc9e3f16c2e16bf0849e24be82c5ee82c603c65fc0326666328fc/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e", size = 1973222, upload-time = "2026-04-20T14:41:57.841Z" }, - { url = "https://files.pythonhosted.org/packages/34/36/0e730beec4d83c5306f417afbd82ff237d9a21e83c5edf675f31ed84c1fe/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287", size = 2053852, upload-time = "2026-04-20T14:40:43.077Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f0/3071131f47e39136a17814576e0fada9168569f7f8c0e6ac4d1ede6a4958/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe", size = 2221134, upload-time = "2026-04-20T14:43:03.349Z" }, - { url = "https://files.pythonhosted.org/packages/2f/a9/a2dc023eec5aa4b02a467874bad32e2446957d2adcab14e107eab502e978/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050", size = 2279785, upload-time = "2026-04-20T14:41:19.285Z" }, - { url = "https://files.pythonhosted.org/packages/0a/44/93f489d16fb63fbd41c670441536541f6e8cfa1e5a69f40bc9c5d30d8c90/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2", size = 2089404, upload-time = "2026-04-20T14:43:10.108Z" }, - { url = "https://files.pythonhosted.org/packages/2a/78/8692e3aa72b2d004f7a5d937f1dfdc8552ba26caf0bec75f342c40f00dec/pydantic_core-2.46.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa", size = 2114898, upload-time = "2026-04-20T14:44:51.475Z" }, - { url = "https://files.pythonhosted.org/packages/6a/62/e83133f2e7832532060175cebf1f13748f4c7e7e7165cdd1f611f174494b/pydantic_core-2.46.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c", size = 2157856, upload-time = "2026-04-20T14:43:46.64Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ec/6a500e3ad7718ee50583fae79c8651f5d37e3abce1fa9ae177ae65842c53/pydantic_core-2.46.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf", size = 2180168, upload-time = "2026-04-20T14:42:00.302Z" }, - { url = "https://files.pythonhosted.org/packages/d8/53/8267811054b1aa7fc1dc7ded93812372ef79a839f5e23558136a6afbfde1/pydantic_core-2.46.3-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b", size = 2322885, upload-time = "2026-04-20T14:41:05.253Z" }, - { url = "https://files.pythonhosted.org/packages/c8/c1/1c0acdb3aa0856ddc4ecc55214578f896f2de16f400cf51627eb3c26c1c4/pydantic_core-2.46.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e", size = 2360328, upload-time = "2026-04-20T14:41:43.991Z" }, - { url = "https://files.pythonhosted.org/packages/f0/d0/ef39cd0f4a926814f360e71c1adeab48ad214d9727e4deb48eedfb5bce1a/pydantic_core-2.46.3-cp311-cp311-win32.whl", hash = "sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb", size = 1979464, upload-time = "2026-04-20T14:43:12.215Z" }, - { url = "https://files.pythonhosted.org/packages/18/9c/f41951b0d858e343f1cf09398b2a7b3014013799744f2c4a8ad6a3eec4f2/pydantic_core-2.46.3-cp311-cp311-win_amd64.whl", hash = "sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346", size = 2070837, upload-time = "2026-04-20T14:41:47.707Z" }, - { url = "https://files.pythonhosted.org/packages/9f/1e/264a17cd582f6ed50950d4d03dd5fefd84e570e238afe1cb3e25cf238769/pydantic_core-2.46.3-cp311-cp311-win_arm64.whl", hash = "sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6", size = 2053647, upload-time = "2026-04-20T14:42:27.535Z" }, - { url = "https://files.pythonhosted.org/packages/4b/cb/5b47425556ecc1f3fe18ed2a0083188aa46e1dd812b06e406475b3a5d536/pydantic_core-2.46.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67", size = 2101946, upload-time = "2026-04-20T14:40:52.581Z" }, - { url = "https://files.pythonhosted.org/packages/a1/4f/2fb62c2267cae99b815bbf4a7b9283812c88ca3153ef29f7707200f1d4e5/pydantic_core-2.46.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089", size = 1951612, upload-time = "2026-04-20T14:42:42.996Z" }, - { url = "https://files.pythonhosted.org/packages/50/6e/b7348fd30d6556d132cddd5bd79f37f96f2601fe0608afac4f5fb01ec0b3/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0", size = 1977027, upload-time = "2026-04-20T14:42:02.001Z" }, - { url = "https://files.pythonhosted.org/packages/82/11/31d60ee2b45540d3fb0b29302a393dbc01cd771c473f5b5147bcd353e593/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789", size = 2063008, upload-time = "2026-04-20T14:44:17.952Z" }, - { url = "https://files.pythonhosted.org/packages/8a/db/3a9d1957181b59258f44a2300ab0f0be9d1e12d662a4f57bb31250455c52/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d", size = 2233082, upload-time = "2026-04-20T14:40:57.934Z" }, - { url = "https://files.pythonhosted.org/packages/9c/e1/3277c38792aeb5cfb18c2f0c5785a221d9ff4e149abbe1184d53d5f72273/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c", size = 2304615, upload-time = "2026-04-20T14:42:12.584Z" }, - { url = "https://files.pythonhosted.org/packages/5e/d5/e3d9717c9eba10855325650afd2a9cba8e607321697f18953af9d562da2f/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395", size = 2094380, upload-time = "2026-04-20T14:43:05.522Z" }, - { url = "https://files.pythonhosted.org/packages/a1/20/abac35dedcbfd66c6f0b03e4e3564511771d6c9b7ede10a362d03e110d9b/pydantic_core-2.46.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396", size = 2135429, upload-time = "2026-04-20T14:41:55.549Z" }, - { url = "https://files.pythonhosted.org/packages/6c/a5/41bfd1df69afad71b5cf0535055bccc73022715ad362edbc124bc1e021d7/pydantic_core-2.46.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d", size = 2174582, upload-time = "2026-04-20T14:41:45.96Z" }, - { url = "https://files.pythonhosted.org/packages/79/65/38d86ea056b29b2b10734eb23329b7a7672ca604df4f2b6e9c02d4ee22fe/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca", size = 2187533, upload-time = "2026-04-20T14:40:55.367Z" }, - { url = "https://files.pythonhosted.org/packages/b6/55/a1129141678a2026badc539ad1dee0a71d06f54c2f06a4bd68c030ac781b/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976", size = 2332985, upload-time = "2026-04-20T14:44:13.05Z" }, - { url = "https://files.pythonhosted.org/packages/d7/60/cb26f4077719f709e54819f4e8e1d43f4091f94e285eb6bd21e1190a7b7c/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b", size = 2373670, upload-time = "2026-04-20T14:41:53.421Z" }, - { url = "https://files.pythonhosted.org/packages/6b/7e/c3f21882bdf1d8d086876f81b5e296206c69c6082551d776895de7801fa0/pydantic_core-2.46.3-cp312-cp312-win32.whl", hash = "sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4", size = 1966722, upload-time = "2026-04-20T14:44:30.588Z" }, - { url = "https://files.pythonhosted.org/packages/57/be/6b5e757b859013ebfbd7adba02f23b428f37c86dcbf78b5bb0b4ffd36e99/pydantic_core-2.46.3-cp312-cp312-win_amd64.whl", hash = "sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1", size = 2072970, upload-time = "2026-04-20T14:42:54.248Z" }, - { url = "https://files.pythonhosted.org/packages/bf/f8/a989b21cc75e9a32d24192ef700eea606521221a89faa40c919ce884f2b1/pydantic_core-2.46.3-cp312-cp312-win_arm64.whl", hash = "sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72", size = 2035963, upload-time = "2026-04-20T14:44:20.4Z" }, - { url = "https://files.pythonhosted.org/packages/9b/3c/9b5e8eb9821936d065439c3b0fb1490ffa64163bfe7e1595985a47896073/pydantic_core-2.46.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37", size = 2102109, upload-time = "2026-04-20T14:41:24.219Z" }, - { url = "https://files.pythonhosted.org/packages/91/97/1c41d1f5a19f241d8069f1e249853bcce378cdb76eec8ab636d7bc426280/pydantic_core-2.46.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f", size = 1951820, upload-time = "2026-04-20T14:42:14.236Z" }, - { url = "https://files.pythonhosted.org/packages/30/b4/d03a7ae14571bc2b6b3c7b122441154720619afe9a336fa3a95434df5e2f/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8", size = 1977785, upload-time = "2026-04-20T14:42:31.648Z" }, - { url = "https://files.pythonhosted.org/packages/ae/0c/4086f808834b59e3c8f1aa26df8f4b6d998cdcf354a143d18ef41529d1fe/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad", size = 2062761, upload-time = "2026-04-20T14:40:37.093Z" }, - { url = "https://files.pythonhosted.org/packages/fa/71/a649be5a5064c2df0db06e0a512c2281134ed2fcc981f52a657936a7527c/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c", size = 2232989, upload-time = "2026-04-20T14:42:59.254Z" }, - { url = "https://files.pythonhosted.org/packages/a2/84/7756e75763e810b3a710f4724441d1ecc5883b94aacb07ca71c5fb5cfb69/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f", size = 2303975, upload-time = "2026-04-20T14:41:32.287Z" }, - { url = "https://files.pythonhosted.org/packages/6c/35/68a762e0c1e31f35fa0dac733cbd9f5b118042853698de9509c8e5bf128b/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35", size = 2095325, upload-time = "2026-04-20T14:42:47.685Z" }, - { url = "https://files.pythonhosted.org/packages/77/bf/1bf8c9a8e91836c926eae5e3e51dce009bf495a60ca56060689d3df3f340/pydantic_core-2.46.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687", size = 2133368, upload-time = "2026-04-20T14:41:22.766Z" }, - { url = "https://files.pythonhosted.org/packages/e5/50/87d818d6bab915984995157ceb2380f5aac4e563dddbed6b56f0ed057aba/pydantic_core-2.46.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3", size = 2173908, upload-time = "2026-04-20T14:42:52.044Z" }, - { url = "https://files.pythonhosted.org/packages/91/88/a311fb306d0bd6185db41fa14ae888fb81d0baf648a761ae760d30819d33/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022", size = 2186422, upload-time = "2026-04-20T14:43:29.55Z" }, - { url = "https://files.pythonhosted.org/packages/8f/79/28fd0d81508525ab2054fef7c77a638c8b5b0afcbbaeee493cf7c3fef7e1/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23", size = 2332709, upload-time = "2026-04-20T14:42:16.134Z" }, - { url = "https://files.pythonhosted.org/packages/b3/21/795bf5fe5c0f379308b8ef19c50dedab2e7711dbc8d0c2acf08f1c7daa05/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7", size = 2372428, upload-time = "2026-04-20T14:41:10.974Z" }, - { url = "https://files.pythonhosted.org/packages/45/b3/ed14c659cbe7605e3ef063077680a64680aec81eb1a04763a05190d49b7f/pydantic_core-2.46.3-cp313-cp313-win32.whl", hash = "sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13", size = 1965601, upload-time = "2026-04-20T14:41:42.128Z" }, - { url = "https://files.pythonhosted.org/packages/ef/bb/adb70d9a762ddd002d723fbf1bd492244d37da41e3af7b74ad212609027e/pydantic_core-2.46.3-cp313-cp313-win_amd64.whl", hash = "sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0", size = 2071517, upload-time = "2026-04-20T14:43:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/52/eb/66faefabebfe68bd7788339c9c9127231e680b11906368c67ce112fdb47f/pydantic_core-2.46.3-cp313-cp313-win_arm64.whl", hash = "sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec", size = 2035802, upload-time = "2026-04-20T14:43:38.507Z" }, - { url = "https://files.pythonhosted.org/packages/7f/db/a7bcb4940183fda36022cd18ba8dd12f2dff40740ec7b58ce7457befa416/pydantic_core-2.46.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b", size = 2097614, upload-time = "2026-04-20T14:44:38.374Z" }, - { url = "https://files.pythonhosted.org/packages/24/35/e4066358a22e3e99519db370494c7528f5a2aa1367370e80e27e20283543/pydantic_core-2.46.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018", size = 1951896, upload-time = "2026-04-20T14:40:53.996Z" }, - { url = "https://files.pythonhosted.org/packages/87/92/37cf4049d1636996e4b888c05a501f40a43ff218983a551d57f9d5e14f0d/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34", size = 1979314, upload-time = "2026-04-20T14:41:49.446Z" }, - { url = "https://files.pythonhosted.org/packages/d8/36/9ff4d676dfbdfb2d591cf43f3d90ded01e15b1404fd101180ed2d62a2fd3/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7", size = 2056133, upload-time = "2026-04-20T14:42:23.574Z" }, - { url = "https://files.pythonhosted.org/packages/bc/f0/405b442a4d7ba855b06eec8b2bf9c617d43b8432d099dfdc7bf999293495/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2", size = 2228726, upload-time = "2026-04-20T14:44:22.816Z" }, - { url = "https://files.pythonhosted.org/packages/e7/f8/65cd92dd5a0bd89ba277a98ecbfaf6fc36bbd3300973c7a4b826d6ab1391/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba", size = 2301214, upload-time = "2026-04-20T14:44:48.792Z" }, - { url = "https://files.pythonhosted.org/packages/fd/86/ef96a4c6e79e7a2d0410826a68fbc0eccc0fd44aa733be199d5fcac3bb87/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f", size = 2099927, upload-time = "2026-04-20T14:41:40.196Z" }, - { url = "https://files.pythonhosted.org/packages/6d/53/269caf30e0096e0a8a8f929d1982a27b3879872cca2d917d17c2f9fdf4fe/pydantic_core-2.46.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22", size = 2128789, upload-time = "2026-04-20T14:41:15.868Z" }, - { url = "https://files.pythonhosted.org/packages/00/b0/1a6d9b6a587e118482910c244a1c5acf4d192604174132efd12bf0ac486f/pydantic_core-2.46.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f", size = 2173815, upload-time = "2026-04-20T14:44:25.152Z" }, - { url = "https://files.pythonhosted.org/packages/87/56/e7e00d4041a7e62b5a40815590114db3b535bf3ca0bf4dca9f16cef25246/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127", size = 2181608, upload-time = "2026-04-20T14:41:28.933Z" }, - { url = "https://files.pythonhosted.org/packages/e8/22/4bd23c3d41f7c185d60808a1de83c76cf5aeabf792f6c636a55c3b1ec7f9/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c", size = 2326968, upload-time = "2026-04-20T14:42:03.962Z" }, - { url = "https://files.pythonhosted.org/packages/24/ac/66cd45129e3915e5ade3b292cb3bc7fd537f58f8f8dbdaba6170f7cabb74/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1", size = 2369842, upload-time = "2026-04-20T14:41:35.52Z" }, - { url = "https://files.pythonhosted.org/packages/a2/51/dd4248abb84113615473aa20d5545b7c4cd73c8644003b5259686f93996c/pydantic_core-2.46.3-cp314-cp314-win32.whl", hash = "sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505", size = 1959661, upload-time = "2026-04-20T14:41:00.042Z" }, - { url = "https://files.pythonhosted.org/packages/20/eb/59980e5f1ae54a3b86372bd9f0fa373ea2d402e8cdcd3459334430f91e91/pydantic_core-2.46.3-cp314-cp314-win_amd64.whl", hash = "sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e", size = 2071686, upload-time = "2026-04-20T14:43:16.471Z" }, - { url = "https://files.pythonhosted.org/packages/8c/db/1cf77e5247047dfee34bc01fa9bca134854f528c8eb053e144298893d370/pydantic_core-2.46.3-cp314-cp314-win_arm64.whl", hash = "sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df", size = 2026907, upload-time = "2026-04-20T14:43:31.732Z" }, - { url = "https://files.pythonhosted.org/packages/57/c0/b3df9f6a543276eadba0a48487b082ca1f201745329d97dbfa287034a230/pydantic_core-2.46.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf", size = 2095047, upload-time = "2026-04-20T14:42:37.982Z" }, - { url = "https://files.pythonhosted.org/packages/66/57/886a938073b97556c168fd99e1a7305bb363cd30a6d2c76086bf0587b32a/pydantic_core-2.46.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee", size = 1934329, upload-time = "2026-04-20T14:43:49.655Z" }, - { url = "https://files.pythonhosted.org/packages/0b/7c/b42eaa5c34b13b07ecb51da21761297a9b8eb43044c864a035999998f328/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a", size = 1974847, upload-time = "2026-04-20T14:42:10.737Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9b/92b42db6543e7de4f99ae977101a2967b63122d4b6cf7773812da2d7d5b5/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c", size = 2041742, upload-time = "2026-04-20T14:40:44.262Z" }, - { url = "https://files.pythonhosted.org/packages/0f/19/46fbe1efabb5aa2834b43b9454e70f9a83ad9c338c1291e48bdc4fecf167/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1", size = 2236235, upload-time = "2026-04-20T14:41:27.307Z" }, - { url = "https://files.pythonhosted.org/packages/77/da/b3f95bc009ad60ec53120f5d16c6faa8cabdbe8a20d83849a1f2b8728148/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64", size = 2282633, upload-time = "2026-04-20T14:44:33.271Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6e/401336117722e28f32fb8220df676769d28ebdf08f2f4469646d404c43a3/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb", size = 2109679, upload-time = "2026-04-20T14:44:41.065Z" }, - { url = "https://files.pythonhosted.org/packages/fc/53/b289f9bc8756a32fe718c46f55afaeaf8d489ee18d1a1e7be1db73f42cc4/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6", size = 2108342, upload-time = "2026-04-20T14:42:50.144Z" }, - { url = "https://files.pythonhosted.org/packages/10/5b/8292fc7c1f9111f1b2b7c1b0dcf1179edcd014fc3ea4517499f50b829d71/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c", size = 2157208, upload-time = "2026-04-20T14:42:08.133Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9e/f80044e9ec07580f057a89fc131f78dda7a58751ddf52bbe05eaf31db50f/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47", size = 2167237, upload-time = "2026-04-20T14:42:25.412Z" }, - { url = "https://files.pythonhosted.org/packages/f8/84/6781a1b037f3b96be9227edbd1101f6d3946746056231bf4ac48cdff1a8d/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab", size = 2312540, upload-time = "2026-04-20T14:40:40.313Z" }, - { url = "https://files.pythonhosted.org/packages/3e/db/19c0839feeb728e7df03255581f198dfdf1c2aeb1e174a8420b63c5252e5/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba", size = 2369556, upload-time = "2026-04-20T14:41:09.427Z" }, - { url = "https://files.pythonhosted.org/packages/e0/15/3228774cb7cd45f5f721ddf1b2242747f4eb834d0c491f0c02d606f09fed/pydantic_core-2.46.3-cp314-cp314t-win32.whl", hash = "sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56", size = 1949756, upload-time = "2026-04-20T14:41:25.717Z" }, - { url = "https://files.pythonhosted.org/packages/b8/2a/c79cf53fd91e5a87e30d481809f52f9a60dd221e39de66455cf04deaad37/pydantic_core-2.46.3-cp314-cp314t-win_amd64.whl", hash = "sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8", size = 2051305, upload-time = "2026-04-20T14:43:18.627Z" }, - { url = "https://files.pythonhosted.org/packages/0b/db/d8182a7f1d9343a032265aae186eb063fe26ca4c40f256b21e8da4498e89/pydantic_core-2.46.3-cp314-cp314t-win_arm64.whl", hash = "sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374", size = 2026310, upload-time = "2026-04-20T14:41:01.778Z" }, - { url = "https://files.pythonhosted.org/packages/66/7f/03dbad45cd3aa9083fbc93c210ae8b005af67e4136a14186950a747c6874/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46", size = 2105683, upload-time = "2026-04-20T14:42:19.779Z" }, - { url = "https://files.pythonhosted.org/packages/26/22/4dc186ac8ea6b257e9855031f51b62a9637beac4d68ac06bee02f046f836/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874", size = 1940052, upload-time = "2026-04-20T14:43:59.274Z" }, - { url = "https://files.pythonhosted.org/packages/0d/ca/d376391a5aff1f2e8188960d7873543608130a870961c2b6b5236627c116/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76", size = 1988172, upload-time = "2026-04-20T14:41:17.469Z" }, - { url = "https://files.pythonhosted.org/packages/0e/6b/523b9f85c23788755d6ab949329de692a2e3a584bc6beb67fef5e035aa9d/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531", size = 2128596, upload-time = "2026-04-20T14:40:41.707Z" }, - { url = "https://files.pythonhosted.org/packages/34/42/f426db557e8ab2791bc7562052299944a118655496fbff99914e564c0a94/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803", size = 2091877, upload-time = "2026-04-20T14:43:27.091Z" }, - { url = "https://files.pythonhosted.org/packages/5c/4f/86a832a9d14df58e663bfdf4627dc00d3317c2bd583c4fb23390b0f04b8e/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3", size = 1932428, upload-time = "2026-04-20T14:40:45.781Z" }, - { url = "https://files.pythonhosted.org/packages/11/1a/fe857968954d93fb78e0d4b6df5c988c74c4aaa67181c60be7cfe327c0ca/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5", size = 1997550, upload-time = "2026-04-20T14:44:02.425Z" }, - { url = "https://files.pythonhosted.org/packages/17/eb/9d89ad2d9b0ba8cd65393d434471621b98912abb10fbe1df08e480ba57b5/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4", size = 2137657, upload-time = "2026-04-20T14:42:45.149Z" }, - { url = "https://files.pythonhosted.org/packages/1f/da/99d40830684f81dec901cac521b5b91c095394cc1084b9433393cde1c2df/pydantic_core-2.46.3-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25", size = 2107973, upload-time = "2026-04-20T14:42:06.175Z" }, - { url = "https://files.pythonhosted.org/packages/99/a5/87024121818d75bbb2a98ddbaf638e40e7a18b5e0f5492c9ca4b1b316107/pydantic_core-2.46.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3", size = 1947191, upload-time = "2026-04-20T14:43:14.319Z" }, - { url = "https://files.pythonhosted.org/packages/60/62/0c1acfe10945b83a6a59d19fbaa92f48825381509e5701b855c08f13db76/pydantic_core-2.46.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536", size = 2123791, upload-time = "2026-04-20T14:43:22.766Z" }, - { url = "https://files.pythonhosted.org/packages/75/3e/3b2393b4c8f44285561dc30b00cf307a56a2eff7c483a824db3b8221ca51/pydantic_core-2.46.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1", size = 2153197, upload-time = "2026-04-20T14:44:27.932Z" }, - { url = "https://files.pythonhosted.org/packages/ba/75/5af02fb35505051eee727c061f2881c555ab4f8ddb2d42da715a42c9731b/pydantic_core-2.46.3-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c", size = 2181073, upload-time = "2026-04-20T14:43:20.729Z" }, - { url = "https://files.pythonhosted.org/packages/10/92/7e0e1bd9ca3c68305db037560ca2876f89b2647deb2f8b6319005de37505/pydantic_core-2.46.3-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85", size = 2315886, upload-time = "2026-04-20T14:44:04.826Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d8/101655f27eaf3e44558ead736b2795d12500598beed4683f279396fa186e/pydantic_core-2.46.3-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8", size = 2360528, upload-time = "2026-04-20T14:40:47.431Z" }, - { url = "https://files.pythonhosted.org/packages/07/0f/1c34a74c8d07136f0d729ffe5e1fdab04fbdaa7684f61a92f92511a84a15/pydantic_core-2.46.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff", size = 2184144, upload-time = "2026-04-20T14:42:57Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" }, + { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" }, + { url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" }, + { url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" }, + { url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" }, + { url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" }, + { url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" }, + { url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" }, + { url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" }, + { url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" }, + { url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" }, + { url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, + { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, + { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, + { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, + { url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" }, + { url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" }, + { url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, + { url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" }, + { url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" }, + { url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" }, + { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" }, ] [[package]] @@ -5613,16 +5612,16 @@ wheels = [ [[package]] name = "pydantic-settings" -version = "2.14.0" +version = "2.14.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/98/c8345dccdc31de4228c039a98f6467a941e39558da41c1744fbe29fa5666/pydantic_settings-2.14.0.tar.gz", hash = "sha256:24285fd4b0e0c06507dd9fdfd331ee23794305352aaec8fc4eb92d4047aeb67d", size = 235709, upload-time = "2026-04-20T13:37:40.293Z" } +sdist = { url = "https://files.pythonhosted.org/packages/07/60/1d1e59c9c90d54591469ada7d268251f71c24bdb765f1a8a832cee8c6653/pydantic_settings-2.14.1.tar.gz", hash = "sha256:e874d3bec7e787b0c9958277956ed9b4dd5de6a80e162188fdaff7c5e26fd5fa", size = 235551, upload-time = "2026-05-08T13:40:06.542Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/dd/bebff3040138f00ae8a102d426b27349b9a49acc310fcae7f92112d867e3/pydantic_settings-2.14.0-py3-none-any.whl", hash = "sha256:fc8d5d692eb7092e43c8647c1c35a3ecd00e040fcf02ed86f4cb5458ca62182e", size = 60940, upload-time = "2026-04-20T13:37:38.586Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8d/f1af3832f5e6eb13ba94ee809e72b8ecb5eef226d27ee0bef7d963d943c7/pydantic_settings-2.14.1-py3-none-any.whl", hash = "sha256:6e3c7edfd8277687cdc598f56e5cff0e9bfff0910a3749deaa8d4401c3a2b9de", size = 60964, upload-time = "2026-05-08T13:40:04.958Z" }, ] [[package]] @@ -5712,11 +5711,11 @@ wheels = [ [[package]] name = "pyjwt" -version = "2.12.1" +version = "2.13.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515, upload-time = "2026-05-21T19:54:36.618Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, + { url = "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274, upload-time = "2026-05-21T19:54:35.362Z" }, ] [package.optional-dependencies] @@ -5911,15 +5910,15 @@ wheels = [ [[package]] name = "pytest-asyncio" -version = "1.3.0" +version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, + { url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" }, ] [[package]] @@ -6042,16 +6041,16 @@ wheels = [ [[package]] name = "pytest-regressions" -version = "2.10.0" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, { name = "pytest-datadir" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/15/d7/6d7525320538d59c1763ebb9f9fdde957966fea607236b2c905ded6f8c98/pytest_regressions-2.10.0.tar.gz", hash = "sha256:5239d29ffe5760acb4a37d95d575383473a2e62c55ede2e89cff735d3bbd2ac9", size = 115513, upload-time = "2026-02-10T13:37:08.21Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/0f/1d6b8cc851596be52c401af53fd0d844e72921b0b11866b88995673cd320/pytest_regressions-2.11.0.tar.gz", hash = "sha256:d4a86092f979eb25d2403c51b2b61039781c4c7c7a364a56b9fa838872a650f7", size = 117474, upload-time = "2026-05-25T12:09:01.796Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/f0/32b0a304563e42693049e31be097427f05451aa42c04e3819b4a5c0afe78/pytest_regressions-2.10.0-py3-none-any.whl", hash = "sha256:e40b98fd1e26435bf694fbd497ac74f4580cbda3b794562faab3dcea2300c0eb", size = 25087, upload-time = "2026-02-10T13:37:06.661Z" }, + { url = "https://files.pythonhosted.org/packages/97/61/fe772eda66bb8e2ee72da2937382aa4f803ca1e41092ca8d59953e96262c/pytest_regressions-2.11.0-py3-none-any.whl", hash = "sha256:fcb2bbcfb2fda256624d434c88cb1e4003667b7e078ee4084887cf839bbdf831", size = 25556, upload-time = "2026-05-25T12:09:00.292Z" }, ] [[package]] @@ -6086,14 +6085,14 @@ wheels = [ [[package]] name = "pytest-subprocess" -version = "1.5.4" +version = "1.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/8d/963aa75dafbd8e601e5d8f8ba586f64792a18257a7d6eb74ce346ce1aa64/pytest_subprocess-1.5.4.tar.gz", hash = "sha256:889e6618e3b2f8425deec15d0e9c2d846aa22ba352ab5764b633b3ab0d612ae8", size = 44908, upload-time = "2026-03-21T07:24:51.85Z" } +sdist = { url = "https://files.pythonhosted.org/packages/76/7a/0d5855132e11de2a96da26e596560757ebbbf8190cfe36cbf85d7423f384/pytest_subprocess-1.6.0.tar.gz", hash = "sha256:b2d746eb1b768a6f9087e5c7c91f87fb9d40c7fdc777550dc00397af428a0654", size = 47910, upload-time = "2026-05-10T08:22:54.207Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/e1/52aa41c22cc353ec4c05ba20c1c9683f6c693cf2ee1264c2bbc9918e12bd/pytest_subprocess-1.5.4-py3-none-any.whl", hash = "sha256:8cf7c1245433afcda31b5bdf47bc386d573cee4787a6edafdd35cf04535e6bf6", size = 22521, upload-time = "2026-03-21T07:24:50.207Z" }, + { url = "https://files.pythonhosted.org/packages/c5/4f/ebe38bf128380f6a8a9b0fbbbe24cbf83915bb2f934717be65cadf55b6fa/pytest_subprocess-1.6.0-py3-none-any.whl", hash = "sha256:00037100f30429c8546adc81f357fddb5213eb036fe3bfb47b7b6befc965e5b2", size = 23803, upload-time = "2026-05-10T08:22:52.52Z" }, ] [[package]] @@ -6164,15 +6163,15 @@ wheels = [ [[package]] name = "python-discovery" -version = "1.3.0" +version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/e0/cc5a8653e9a24f6cf84768f05064aa8ed5a83dcefd5e2a043db14a1c5f44/python_discovery-1.3.0.tar.gz", hash = "sha256:d098f1e86be5d45fe4d14bf1029294aabbd332f4321179dec85e76cddce834b0", size = 63925, upload-time = "2026-05-05T14:38:39.769Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/12/38c1a0b1e64806780c9563e3fc9f6e472251839662587cfbe9bfaf2ae10a/python_discovery-1.4.0.tar.gz", hash = "sha256:eb8bc7daad3c226c147e45bb4e970a1feb1bf4048ee178e6db59e197b8010ce3", size = 68455, upload-time = "2026-05-28T01:15:37.639Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/d4/24d543ab8b8158b7f5a97113c831205f5c900c92c8762b1e7f44b7ea0405/python_discovery-1.3.0-py3-none-any.whl", hash = "sha256:441d9ced3dfce36e113beb35ca302c71c7ef06f3c0f9c227a0b9bb3bd49b9e9f", size = 33124, upload-time = "2026-05-05T14:38:38.539Z" }, + { url = "https://files.pythonhosted.org/packages/c8/8d/3d316429f65029532bb1e28ff77b797d86b5ac3915bb44ca4e19aa283d43/python_discovery-1.4.0-py3-none-any.whl", hash = "sha256:26ed78d703e234879a66244c7d4114563fb13ec5cd30a2d1357e5fb4850782da", size = 33217, upload-time = "2026-05-28T01:15:36.573Z" }, ] [[package]] @@ -6186,14 +6185,14 @@ wheels = [ [[package]] name = "python-engineio" -version = "4.13.1" +version = "4.13.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "simple-websocket" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/34/12/bdef9dbeedbe2cdeba2a2056ad27b1fb081557d34b69a97f574843462cae/python_engineio-4.13.1.tar.gz", hash = "sha256:0a853fcef52f5b345425d8c2b921ac85023a04dfcf75d7b74696c61e940fd066", size = 92348, upload-time = "2026-02-06T23:38:06.12Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/6d/4384c2723adad93a3d6de4297e6d9c8b93be7f778a407f34f6ee0b2bea3e/python_engineio-4.13.2.tar.gz", hash = "sha256:a7732e99cfb7db6ed1aee31f18d7f73bbae086a92f31dee019bc646155d9684e", size = 79639, upload-time = "2026-05-21T21:45:07.578Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl", hash = "sha256:f32ad10589859c11053ad7d9bb3c9695cdf862113bfb0d20bc4d890198287399", size = 59847, upload-time = "2026-02-06T23:38:04.861Z" }, + { url = "https://files.pythonhosted.org/packages/b7/28/180bfc5c95e83d40cb2abce512684ccad44e4819ec899fc36cb404a19061/python_engineio-4.13.2-py3-none-any.whl", hash = "sha256:8c101cd170e400dc4e970cd523325cde22df8fc25140953f379327055d701a6b", size = 59993, upload-time = "2026-05-21T21:45:06.162Z" }, ] [[package]] @@ -6207,24 +6206,24 @@ wheels = [ [[package]] name = "python-multipart" -version = "0.0.27" +version = "0.0.29" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/9b/f23807317a113dc36e74e75eb265a02dd1a4d9082abc3c1064acd22997c4/python_multipart-0.0.27.tar.gz", hash = "sha256:9870a6a8c5a20a5bf4f07c017bd1489006ff8836cff097b6933355ee2b49b602", size = 44043, upload-time = "2026-04-27T10:51:26.649Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/fe/70bd71a6738b09a0bdf6480ca6436b167469ca4578b2a0efbe390b4b0e70/python_multipart-0.0.29.tar.gz", hash = "sha256:643e93849196645e2dbdd81a0f8829a23123ad7f797a84a364c6fb3563f18904", size = 45678, upload-time = "2026-05-17T17:29:47.654Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/78/4126abcbdbd3c559d43e0db7f7b9173fc6befe45d39a2856cc0b8ec2a5a6/python_multipart-0.0.27-py3-none-any.whl", hash = "sha256:6fccfad17a27334bd0193681b369f476eda3409f17381a2d65aa7df3f7275645", size = 29254, upload-time = "2026-04-27T10:51:24.997Z" }, + { url = "https://files.pythonhosted.org/packages/8f/cb/769cfc37177252872a45a71f3fbdde9d51b471a3f3c14bfe95dde3407386/python_multipart-0.0.29-py3-none-any.whl", hash = "sha256:2ddcc971cef266225f54f552d8fa10bcfbb1f14446caec199060daac59ff2d69", size = 29640, upload-time = "2026-05-17T17:29:45.69Z" }, ] [[package]] name = "python-socketio" -version = "5.16.1" +version = "5.16.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "bidict" }, { name = "python-engineio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/59/81/cf8284f45e32efa18d3848ed82cdd4dcc1b657b082458fbe01ad3e1f2f8d/python_socketio-5.16.1.tar.gz", hash = "sha256:f863f98eacce81ceea2e742f6388e10ca3cdd0764be21d30d5196470edf5ea89", size = 128508, upload-time = "2026-02-06T23:42:07Z" } +sdist = { url = "https://files.pythonhosted.org/packages/07/dd/6fd4112b941f7d39b8171b6ba17902609bd8fa2059c3812a3c29dade13e7/python_socketio-5.16.2.tar.gz", hash = "sha256:ad88c228d921646efa436c0a0df217e364ef30ec072df4041484e54d49c15989", size = 128011, upload-time = "2026-05-21T22:03:44.418Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl", hash = "sha256:a3eb1702e92aa2f2b5d3ba00261b61f062cce51f1cfb6900bf3ab4d1934d2d35", size = 82054, upload-time = "2026-02-06T23:42:05.772Z" }, + { url = "https://files.pythonhosted.org/packages/72/dc/0decaf5da92a7a969374474025787102d811d42aed1d32191fa338620e15/python_socketio-5.16.2-py3-none-any.whl", hash = "sha256:bef2da3374fd533aed4297f57b4f6512b52aa51604cb0da2165f401291c5ca20", size = 82137, upload-time = "2026-05-21T22:03:42.616Z" }, ] [package.optional-dependencies] @@ -6234,14 +6233,15 @@ asyncio-client = [ [[package]] name = "pythonnet" -version = "3.0.5" +version = "3.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "clr-loader", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9a/d6/1afd75edd932306ae9bd2c2d961d603dc2b52fcec51b04afea464f1f6646/pythonnet-3.0.5.tar.gz", hash = "sha256:48e43ca463941b3608b32b4e236db92d8d40db4c58a75ace902985f76dac21cf", size = 239212, upload-time = "2024-12-13T08:30:44.393Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/57/da1992e44663b71365c6e842c8d7fa453d4ec45fb99a68cfee5b7e944d3c/pythonnet-3.1.0.tar.gz", hash = "sha256:7b34c382905d10a371509ffafd64cae0416305c28817738a9cd138336f4e9991", size = 250599, upload-time = "2026-05-23T20:30:21.578Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/f1/bfb6811df4745f92f14c47a29e50e89a36b1533130fcc56452d4660bd2d6/pythonnet-3.0.5-py3-none-any.whl", hash = "sha256:f6702d694d5d5b163c9f3f5cc34e0bed8d6857150237fae411fefb883a656d20", size = 297506, upload-time = "2024-12-13T08:30:40.661Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4b/52414f442624d2589f5374a48c08d5ae94f24bea67fc13a20a752884e5b7/pythonnet-3.1.0-cp310.cp311.cp312.cp313.cp314-none-any.whl", hash = "sha256:698dd88edc198819ad63b624a6ebe76208c7b46e4fe13626f65e484f0358d6ba", size = 217578, upload-time = "2026-05-23T20:30:19.527Z" }, + { url = "https://files.pythonhosted.org/packages/db/67/031124fdcb937c266a3265118525bbf6dc13b8c79786d6a7290aecb6e7bb/pythonnet-3.1.0-cp310.cp311.cp312.cp313.cp314-none-win32.win_amd64.whl", hash = "sha256:7bdd4de03df3547a48122a3989265c8b31d5be0d19dadffa009eec7df8085e0b", size = 1644898, upload-time = "2026-05-23T20:30:16.213Z" }, ] [[package]] @@ -6476,7 +6476,7 @@ wheels = [ [[package]] name = "requests" -version = "2.33.1" +version = "2.34.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -6484,9 +6484,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, ] [[package]] @@ -6582,29 +6582,29 @@ wheels = [ [[package]] name = "rich-rst" -version = "1.3.2" +version = "2.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "docutils" }, + { name = "pygments" }, { name = "rich" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } +sdist = { url = "https://files.pythonhosted.org/packages/57/56/3191bae66b08ccc637ea8120426068bcb361cc323c96404c310886937067/rich_rst-2.0.1.tar.gz", hash = "sha256:cbe236ed0901d1ec8427cc6a50bf0a34353ba28ad014dc24def68bfe7f3b9e68", size = 300570, upload-time = "2026-05-16T00:47:57.362Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, + { url = "https://files.pythonhosted.org/packages/a0/3d/55c17d3ebdf3cd81356002afe5bef9bb8af631db2819785b6eac845b925b/rich_rst-2.0.1-py3-none-any.whl", hash = "sha256:7ee15f345ce25fa02b582c272a6cdbaf0c21243e38061cea273cff659bf3ef61", size = 272922, upload-time = "2026-05-16T00:47:55.508Z" }, ] [[package]] name = "rich-toolkit" -version = "0.19.7" +version = "0.19.10" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/ba/dae9e3096651042754da419a4042bc1c75e07d615f9b15066d738838e4df/rich_toolkit-0.19.7.tar.gz", hash = "sha256:133c0915872da91d4c25d85342d5ec1dfacc69b63448af1a08a0d4b4f23ef46e", size = 195877, upload-time = "2026-02-24T16:06:20.555Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/02/32217f3657ae91a0ea7cf1d74ade78f44352f830d00c468f753ddb3d4980/rich_toolkit-0.19.10.tar.gz", hash = "sha256:dc2e8c515ef9fbb4894e62bd41a2d2960dd7c2f505b5084894604d5ccfee3f09", size = 198167, upload-time = "2026-05-21T10:11:42.397Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/3c/c923619f6d2f5fafcc96fec0aaf9550a46cd5b6481f06e0c6b66a2a4fed0/rich_toolkit-0.19.7-py3-none-any.whl", hash = "sha256:0288e9203728c47c5a4eb60fd2f0692d9df7455a65901ab6f898437a2ba5989d", size = 32963, upload-time = "2026-02-24T16:06:22.066Z" }, + { url = "https://files.pythonhosted.org/packages/35/84/a005adcb4d1e6846ba3d62768090c3b943e3f6d8dc5c47af64f33584c4a7/rich_toolkit-0.19.10-py3-none-any.whl", hash = "sha256:93a41f67a09aefe90379f1729495c2fee9ccbcc8cfda48e2ca2ae54a995e32b1", size = 33907, upload-time = "2026-05-21T10:11:43.578Z" }, ] [[package]] @@ -6851,99 +6851,48 @@ wheels = [ [[package]] name = "ruamel-yaml" -version = "0.18.16" +version = "0.19.1" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ruamel-yaml-clib", marker = "python_full_version < '3.14' and platform_python_implementation == 'CPython'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9f/c7/ee630b29e04a672ecfc9b63227c87fd7a37eb67c1bf30fe95376437f897c/ruamel.yaml-0.18.16.tar.gz", hash = "sha256:a6e587512f3c998b2225d68aa1f35111c29fad14aed561a26e73fab729ec5e5a", size = 147269, upload-time = "2025-10-22T17:54:02.346Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/3b/ebda527b56beb90cb7652cb1c7e4f91f48649fbcd8d2eb2fb6e77cd3329b/ruamel_yaml-0.19.1.tar.gz", hash = "sha256:53eb66cd27849eff968ebf8f0bf61f46cdac2da1d1f3576dd4ccee9b25c31993", size = 142709, upload-time = "2026-01-02T16:50:31.84Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/73/bb1bc2529f852e7bf64a2dec885e89ff9f5cc7bbf6c9340eed30ff2c69c5/ruamel.yaml-0.18.16-py3-none-any.whl", hash = "sha256:048f26d64245bae57a4f9ef6feb5b552a386830ef7a826f235ffb804c59efbba", size = 119858, upload-time = "2025-10-22T17:53:59.012Z" }, -] - -[[package]] -name = "ruamel-yaml-clib" -version = "0.2.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/97/60fda20e2fb54b83a61ae14648b0817c8f5d84a3821e40bfbdae1437026a/ruamel_yaml_clib-0.2.15.tar.gz", hash = "sha256:46e4cc8c43ef6a94885f72512094e482114a8a706d3c555a34ed4b0d20200600", size = 225794, upload-time = "2025-11-16T16:12:59.761Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/80/8ce7b9af532aa94dd83360f01ce4716264db73de6bc8efd22c32341f6658/ruamel_yaml_clib-0.2.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c583229f336682b7212a43d2fa32c30e643d3076178fb9f7a6a14dde85a2d8bd", size = 147998, upload-time = "2025-11-16T16:13:13.241Z" }, - { url = "https://files.pythonhosted.org/packages/53/09/de9d3f6b6701ced5f276d082ad0f980edf08ca67114523d1b9264cd5e2e0/ruamel_yaml_clib-0.2.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56ea19c157ed8c74b6be51b5fa1c3aff6e289a041575f0556f66e5fb848bb137", size = 132743, upload-time = "2025-11-16T16:13:14.265Z" }, - { url = "https://files.pythonhosted.org/packages/0e/f7/73a9b517571e214fe5c246698ff3ed232f1ef863c8ae1667486625ec688a/ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5fea0932358e18293407feb921d4f4457db837b67ec1837f87074667449f9401", size = 731459, upload-time = "2025-11-16T20:22:44.338Z" }, - { url = "https://files.pythonhosted.org/packages/9b/a2/0dc0013169800f1c331a6f55b1282c1f4492a6d32660a0cf7b89e6684919/ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef71831bd61fbdb7aa0399d5c4da06bea37107ab5c79ff884cc07f2450910262", size = 749289, upload-time = "2025-11-16T16:13:15.633Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ed/3fb20a1a96b8dc645d88c4072df481fe06e0289e4d528ebbdcc044ebc8b3/ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:617d35dc765715fa86f8c3ccdae1e4229055832c452d4ec20856136acc75053f", size = 777630, upload-time = "2025-11-16T16:13:16.898Z" }, - { url = "https://files.pythonhosted.org/packages/60/50/6842f4628bc98b7aa4733ab2378346e1441e150935ad3b9f3c3c429d9408/ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b45498cc81a4724a2d42273d6cfc243c0547ad7c6b87b4f774cb7bcc131c98d", size = 744368, upload-time = "2025-11-16T16:13:18.117Z" }, - { url = "https://files.pythonhosted.org/packages/d3/b0/128ae8e19a7d794c2e36130a72b3bb650ce1dd13fb7def6cf10656437dcf/ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:def5663361f6771b18646620fca12968aae730132e104688766cf8a3b1d65922", size = 745233, upload-time = "2025-11-16T20:22:45.833Z" }, - { url = "https://files.pythonhosted.org/packages/75/05/91130633602d6ba7ce3e07f8fc865b40d2a09efd4751c740df89eed5caf9/ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:014181cdec565c8745b7cbc4de3bf2cc8ced05183d986e6d1200168e5bb59490", size = 770963, upload-time = "2025-11-16T16:13:19.344Z" }, - { url = "https://files.pythonhosted.org/packages/fd/4b/fd4542e7f33d7d1bc64cc9ac9ba574ce8cf145569d21f5f20133336cdc8c/ruamel_yaml_clib-0.2.15-cp311-cp311-win32.whl", hash = "sha256:d290eda8f6ada19e1771b54e5706b8f9807e6bb08e873900d5ba114ced13e02c", size = 102640, upload-time = "2025-11-16T16:13:20.498Z" }, - { url = "https://files.pythonhosted.org/packages/bb/eb/00ff6032c19c7537371e3119287999570867a0eafb0154fccc80e74bf57a/ruamel_yaml_clib-0.2.15-cp311-cp311-win_amd64.whl", hash = "sha256:bdc06ad71173b915167702f55d0f3f027fc61abd975bd308a0968c02db4a4c3e", size = 121996, upload-time = "2025-11-16T16:13:21.855Z" }, - { url = "https://files.pythonhosted.org/packages/72/4b/5fde11a0722d676e469d3d6f78c6a17591b9c7e0072ca359801c4bd17eee/ruamel_yaml_clib-0.2.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cb15a2e2a90c8475df45c0949793af1ff413acfb0a716b8b94e488ea95ce7cff", size = 149088, upload-time = "2025-11-16T16:13:22.836Z" }, - { url = "https://files.pythonhosted.org/packages/85/82/4d08ac65ecf0ef3b046421985e66301a242804eb9a62c93ca3437dc94ee0/ruamel_yaml_clib-0.2.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:64da03cbe93c1e91af133f5bec37fd24d0d4ba2418eaf970d7166b0a26a148a2", size = 134553, upload-time = "2025-11-16T16:13:24.151Z" }, - { url = "https://files.pythonhosted.org/packages/b9/cb/22366d68b280e281a932403b76da7a988108287adff2bfa5ce881200107a/ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f6d3655e95a80325b84c4e14c080b2470fe4f33b6846f288379ce36154993fb1", size = 737468, upload-time = "2025-11-16T20:22:47.335Z" }, - { url = "https://files.pythonhosted.org/packages/71/73/81230babf8c9e33770d43ed9056f603f6f5f9665aea4177a2c30ae48e3f3/ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71845d377c7a47afc6592aacfea738cc8a7e876d586dfba814501d8c53c1ba60", size = 753349, upload-time = "2025-11-16T16:13:26.269Z" }, - { url = "https://files.pythonhosted.org/packages/61/62/150c841f24cda9e30f588ef396ed83f64cfdc13b92d2f925bb96df337ba9/ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e5499db1ccbc7f4b41f0565e4f799d863ea720e01d3e99fa0b7b5fcd7802c9", size = 788211, upload-time = "2025-11-16T16:13:27.441Z" }, - { url = "https://files.pythonhosted.org/packages/30/93/e79bd9cbecc3267499d9ead919bd61f7ddf55d793fb5ef2b1d7d92444f35/ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4b293a37dc97e2b1e8a1aec62792d1e52027087c8eea4fc7b5abd2bdafdd6642", size = 743203, upload-time = "2025-11-16T16:13:28.671Z" }, - { url = "https://files.pythonhosted.org/packages/8d/06/1eb640065c3a27ce92d76157f8efddb184bd484ed2639b712396a20d6dce/ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:512571ad41bba04eac7268fe33f7f4742210ca26a81fe0c75357fa682636c690", size = 747292, upload-time = "2025-11-16T20:22:48.584Z" }, - { url = "https://files.pythonhosted.org/packages/a5/21/ee353e882350beab65fcc47a91b6bdc512cace4358ee327af2962892ff16/ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5e9f630c73a490b758bf14d859a39f375e6999aea5ddd2e2e9da89b9953486a", size = 771624, upload-time = "2025-11-16T16:13:29.853Z" }, - { url = "https://files.pythonhosted.org/packages/57/34/cc1b94057aa867c963ecf9ea92ac59198ec2ee3a8d22a126af0b4d4be712/ruamel_yaml_clib-0.2.15-cp312-cp312-win32.whl", hash = "sha256:f4421ab780c37210a07d138e56dd4b51f8642187cdfb433eb687fe8c11de0144", size = 100342, upload-time = "2025-11-16T16:13:31.067Z" }, - { url = "https://files.pythonhosted.org/packages/b3/e5/8925a4208f131b218f9a7e459c0d6fcac8324ae35da269cb437894576366/ruamel_yaml_clib-0.2.15-cp312-cp312-win_amd64.whl", hash = "sha256:2b216904750889133d9222b7b873c199d48ecbb12912aca78970f84a5aa1a4bc", size = 119013, upload-time = "2025-11-16T16:13:32.164Z" }, - { url = "https://files.pythonhosted.org/packages/17/5e/2f970ce4c573dc30c2f95825f2691c96d55560268ddc67603dc6ea2dd08e/ruamel_yaml_clib-0.2.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4dcec721fddbb62e60c2801ba08c87010bd6b700054a09998c4d09c08147b8fb", size = 147450, upload-time = "2025-11-16T16:13:33.542Z" }, - { url = "https://files.pythonhosted.org/packages/d6/03/a1baa5b94f71383913f21b96172fb3a2eb5576a4637729adbf7cd9f797f8/ruamel_yaml_clib-0.2.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:65f48245279f9bb301d1276f9679b82e4c080a1ae25e679f682ac62446fac471", size = 133139, upload-time = "2025-11-16T16:13:34.587Z" }, - { url = "https://files.pythonhosted.org/packages/dc/19/40d676802390f85784235a05788fd28940923382e3f8b943d25febbb98b7/ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:46895c17ead5e22bea5e576f1db7e41cb273e8d062c04a6a49013d9f60996c25", size = 731474, upload-time = "2025-11-16T20:22:49.934Z" }, - { url = "https://files.pythonhosted.org/packages/ce/bb/6ef5abfa43b48dd55c30d53e997f8f978722f02add61efba31380d73e42e/ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3eb199178b08956e5be6288ee0b05b2fb0b5c1f309725ad25d9c6ea7e27f962a", size = 748047, upload-time = "2025-11-16T16:13:35.633Z" }, - { url = "https://files.pythonhosted.org/packages/ff/5d/e4f84c9c448613e12bd62e90b23aa127ea4c46b697f3d760acc32cb94f25/ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d1032919280ebc04a80e4fb1e93f7a738129857eaec9448310e638c8bccefcf", size = 782129, upload-time = "2025-11-16T16:13:36.781Z" }, - { url = "https://files.pythonhosted.org/packages/de/4b/e98086e88f76c00c88a6bcf15eae27a1454f661a9eb72b111e6bbb69024d/ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab0df0648d86a7ecbd9c632e8f8d6b21bb21b5fc9d9e095c796cacf32a728d2d", size = 736848, upload-time = "2025-11-16T16:13:37.952Z" }, - { url = "https://files.pythonhosted.org/packages/0c/5c/5964fcd1fd9acc53b7a3a5d9a05ea4f95ead9495d980003a557deb9769c7/ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:331fb180858dd8534f0e61aa243b944f25e73a4dae9962bd44c46d1761126bbf", size = 741630, upload-time = "2025-11-16T20:22:51.718Z" }, - { url = "https://files.pythonhosted.org/packages/07/1e/99660f5a30fceb58494598e7d15df883a07292346ef5696f0c0ae5dee8c6/ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fd4c928ddf6bce586285daa6d90680b9c291cfd045fc40aad34e445d57b1bf51", size = 766619, upload-time = "2025-11-16T16:13:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/36/2f/fa0344a9327b58b54970e56a27b32416ffbcfe4dcc0700605516708579b2/ruamel_yaml_clib-0.2.15-cp313-cp313-win32.whl", hash = "sha256:bf0846d629e160223805db9fe8cc7aec16aaa11a07310c50c8c7164efa440aec", size = 100171, upload-time = "2025-11-16T16:13:40.456Z" }, - { url = "https://files.pythonhosted.org/packages/06/c4/c124fbcef0684fcf3c9b72374c2a8c35c94464d8694c50f37eef27f5a145/ruamel_yaml_clib-0.2.15-cp313-cp313-win_amd64.whl", hash = "sha256:45702dfbea1420ba3450bb3dd9a80b33f0badd57539c6aac09f42584303e0db6", size = 118845, upload-time = "2025-11-16T16:13:41.481Z" }, - { url = "https://files.pythonhosted.org/packages/3e/bd/ab8459c8bb759c14a146990bf07f632c1cbec0910d4853feeee4be2ab8bb/ruamel_yaml_clib-0.2.15-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:753faf20b3a5906faf1fc50e4ddb8c074cb9b251e00b14c18b28492f933ac8ef", size = 147248, upload-time = "2025-11-16T16:13:42.872Z" }, - { url = "https://files.pythonhosted.org/packages/69/f2/c4cec0a30f1955510fde498aac451d2e52b24afdbcb00204d3a951b772c3/ruamel_yaml_clib-0.2.15-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:480894aee0b29752560a9de46c0e5f84a82602f2bc5c6cde8db9a345319acfdf", size = 133764, upload-time = "2025-11-16T16:13:43.932Z" }, - { url = "https://files.pythonhosted.org/packages/82/c7/2480d062281385a2ea4f7cc9476712446e0c548cd74090bff92b4b49e898/ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d3b58ab2454b4747442ac76fab66739c72b1e2bb9bd173d7694b9f9dbc9c000", size = 730537, upload-time = "2025-11-16T20:22:52.918Z" }, - { url = "https://files.pythonhosted.org/packages/75/08/e365ee305367559f57ba6179d836ecc3d31c7d3fdff2a40ebf6c32823a1f/ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bfd309b316228acecfa30670c3887dcedf9b7a44ea39e2101e75d2654522acd4", size = 746944, upload-time = "2025-11-16T16:13:45.338Z" }, - { url = "https://files.pythonhosted.org/packages/a1/5c/8b56b08db91e569d0a4fbfa3e492ed2026081bdd7e892f63ba1c88a2f548/ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2812ff359ec1f30129b62372e5f22a52936fac13d5d21e70373dbca5d64bb97c", size = 778249, upload-time = "2025-11-16T16:13:46.871Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1d/70dbda370bd0e1a92942754c873bd28f513da6198127d1736fa98bb2a16f/ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7e74ea87307303ba91073b63e67f2c667e93f05a8c63079ee5b7a5c8d0d7b043", size = 737140, upload-time = "2025-11-16T16:13:48.349Z" }, - { url = "https://files.pythonhosted.org/packages/5b/87/822d95874216922e1120afb9d3fafa795a18fdd0c444f5c4c382f6dac761/ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:713cd68af9dfbe0bb588e144a61aad8dcc00ef92a82d2e87183ca662d242f524", size = 741070, upload-time = "2025-11-16T20:22:54.151Z" }, - { url = "https://files.pythonhosted.org/packages/b9/17/4e01a602693b572149f92c983c1f25bd608df02c3f5cf50fd1f94e124a59/ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:542d77b72786a35563f97069b9379ce762944e67055bea293480f7734b2c7e5e", size = 765882, upload-time = "2025-11-16T16:13:49.526Z" }, - { url = "https://files.pythonhosted.org/packages/9f/17/7999399081d39ebb79e807314de6b611e1d1374458924eb2a489c01fc5ad/ruamel_yaml_clib-0.2.15-cp314-cp314-win32.whl", hash = "sha256:424ead8cef3939d690c4b5c85ef5b52155a231ff8b252961b6516ed7cf05f6aa", size = 102567, upload-time = "2025-11-16T16:13:50.78Z" }, - { url = "https://files.pythonhosted.org/packages/d2/67/be582a7370fdc9e6846c5be4888a530dcadd055eef5b932e0e85c33c7d73/ruamel_yaml_clib-0.2.15-cp314-cp314-win_amd64.whl", hash = "sha256:ac9b8d5fa4bb7fd2917ab5027f60d4234345fd366fe39aa711d5dca090aa1467", size = 122847, upload-time = "2025-11-16T16:13:51.807Z" }, + { url = "https://files.pythonhosted.org/packages/b8/0c/51f6841f1d84f404f92463fc2b1ba0da357ca1e3db6b7fbda26956c3b82a/ruamel_yaml-0.19.1-py3-none-any.whl", hash = "sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93", size = 118102, upload-time = "2026-01-02T16:50:29.201Z" }, ] [[package]] name = "ruff" -version = "0.15.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/99/43/3291f1cc9106f4c63bdce7a8d0df5047fe8422a75b091c16b5e9355e0b11/ruff-0.15.12.tar.gz", hash = "sha256:ecea26adb26b4232c0c2ca19ccbc0083a68344180bba2a600605538ce51a40a6", size = 4643852, upload-time = "2026-04-24T18:17:14.305Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/6e/e78ffb61d4686f3d96ba3df2c801161843746dcbcbb17a1e927d4829312b/ruff-0.15.12-py3-none-linux_armv6l.whl", hash = "sha256:f86f176e188e94d6bdbc09f09bfd9dc729059ad93d0e7390b5a73efe19f8861c", size = 10640713, upload-time = "2026-04-24T18:17:22.841Z" }, - { url = "https://files.pythonhosted.org/packages/ae/08/a317bc231fb9e7b93e4ef3089501e51922ff88d6936ce5cf870c4fe55419/ruff-0.15.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e3bcd123364c3770b8e1b7baaf343cc99a35f197c5c6e8af79015c666c423a6c", size = 11069267, upload-time = "2026-04-24T18:17:30.105Z" }, - { url = "https://files.pythonhosted.org/packages/aa/a4/f828e9718d3dce1f5f11c39c4f65afd32783c8b2aebb2e3d259e492c47bd/ruff-0.15.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fe87510d000220aa1ed530d4448a7c696a0cae1213e5ec30e5874287b66557b5", size = 10397182, upload-time = "2026-04-24T18:17:07.177Z" }, - { url = "https://files.pythonhosted.org/packages/71/e0/3310fc6d1b5e1fdea22bf3b1b807c7e187b581021b0d7d4514cccdb5fb71/ruff-0.15.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84a1630093121375a3e2a95b4a6dc7b59e2b4ee76216e32d81aae550a832d002", size = 10758012, upload-time = "2026-04-24T18:16:55.759Z" }, - { url = "https://files.pythonhosted.org/packages/11/c1/a606911aee04c324ddaa883ae418f3569792fd3c4a10c50e0dd0a2311e1e/ruff-0.15.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fb129f40f114f089ebe0ca56c0d251cf2061b17651d464bb6478dc01e69f11f5", size = 10447479, upload-time = "2026-04-24T18:16:51.677Z" }, - { url = "https://files.pythonhosted.org/packages/9d/68/4201e8444f0894f21ab4aeeaee68aa4f10b51613514a20d80bd628d57e88/ruff-0.15.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0c862b172d695db7598426b8af465e7e9ac00a3ea2a3630ee67eb82e366aaa6", size = 11234040, upload-time = "2026-04-24T18:17:16.529Z" }, - { url = "https://files.pythonhosted.org/packages/34/ff/8a6d6cf4ccc23fd67060874e832c18919d1557a0611ebef03fdb01fff11e/ruff-0.15.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33", size = 12087377, upload-time = "2026-04-24T18:17:04.944Z" }, - { url = "https://files.pythonhosted.org/packages/85/f6/c669cf73f5152f623d34e69866a46d5e6185816b19fcd5b6dd8a2d299922/ruff-0.15.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e77c7e51c07fe396826d5969a5b846d9cd4c402535835fb6e21ce8b28fef847", size = 11367784, upload-time = "2026-04-24T18:17:25.409Z" }, - { url = "https://files.pythonhosted.org/packages/e8/39/c61d193b8a1daaa8977f7dea9e8d8ba866e02ea7b65d32f6861693aa4c12/ruff-0.15.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b2f4f2f3b1026b5fb449b467d9264bf22067b600f7b6f41fc5958909f449d0", size = 11344088, upload-time = "2026-04-24T18:17:12.258Z" }, - { url = "https://files.pythonhosted.org/packages/c2/8d/49afab3645e31e12c590acb6d3b5b69d7aab5b81926dbaf7461f9441f37a/ruff-0.15.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9ba3b8f1afd7e2e43d8943e55f249e13f9682fde09711644a6e7290eb4f3e339", size = 11271770, upload-time = "2026-04-24T18:17:02.457Z" }, - { url = "https://files.pythonhosted.org/packages/46/06/33f41fe94403e2b755481cdfb9b7ef3e4e0ed031c4581124658d935d52b4/ruff-0.15.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e852ba9fdc890655e1d78f2df1499efbe0e54126bd405362154a75e2bde159c5", size = 10719355, upload-time = "2026-04-24T18:17:27.648Z" }, - { url = "https://files.pythonhosted.org/packages/0d/59/18aa4e014debbf559670e4048e39260a85c7fcee84acfd761ac01e7b8d35/ruff-0.15.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dd8aed930da53780d22fc70bdf84452c843cf64f8cb4eb38984319c24c5cd5fd", size = 10462758, upload-time = "2026-04-24T18:17:32.347Z" }, - { url = "https://files.pythonhosted.org/packages/25/e7/cc9f16fd0f3b5fddcbd7ec3d6ae30c8f3fde1047f32a4093a98d633c6570/ruff-0.15.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b", size = 10953498, upload-time = "2026-04-24T18:17:20.674Z" }, - { url = "https://files.pythonhosted.org/packages/72/7a/a9ba7f98c7a575978698f4230c5e8cc54bbc761af34f560818f933dafa0c/ruff-0.15.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9cae0f92bd5700d1213188b31cd3bdd2b315361296d10b96b8e2337d3d11f53e", size = 11447765, upload-time = "2026-04-24T18:17:09.755Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f9/0ae446942c846b8266059ad8a30702a35afae55f5cdc54c5adf8d7afdc27/ruff-0.15.12-py3-none-win32.whl", hash = "sha256:d0185894e038d7043ba8fd6aee7499ece6462dc0ea9f1e260c7451807c714c20", size = 10657277, upload-time = "2026-04-24T18:17:18.591Z" }, - { url = "https://files.pythonhosted.org/packages/33/f1/9614e03e1cdcbf9437570b5400ced8a720b5db22b28d8e0f1bda429f660d/ruff-0.15.12-py3-none-win_amd64.whl", hash = "sha256:c87a162d61ab3adca47c03f7f717c68672edec7d1b5499e652331780fe74950d", size = 11837758, upload-time = "2026-04-24T18:17:00.113Z" }, - { url = "https://files.pythonhosted.org/packages/c0/98/6beb4b351e472e5f4c4613f7c35a5290b8be2497e183825310c4c3a3984b/ruff-0.15.12-py3-none-win_arm64.whl", hash = "sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f", size = 11120821, upload-time = "2026-04-24T18:16:57.979Z" }, +version = "0.15.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/8a/8bce2894573e9dae6ff4d77fe34ad727d79b9e6238ad288c5638990d90f6/ruff-0.15.14.tar.gz", hash = "sha256:48e866b165be4a9bdbf310f7d3c9a07edef2fe8cd63ffeb4e00bb590506ebf9f", size = 4700910, upload-time = "2026-05-21T14:34:55.177Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/c8/74a92c6ff9fcfb4f1f947126d3ebee8389276e161ecc85de5bda7cda51bd/ruff-0.15.14-py3-none-linux_armv6l.whl", hash = "sha256:8dd2db9416e487c8d4b01fa7056bb02c4d05969d4f8d17a08c229c2f4ff3c108", size = 10739177, upload-time = "2026-05-21T14:34:37.332Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/254a35c20acc38a7223c9d2d594af12e794432464f2cdeb52af1dc4a892d/ruff-0.15.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:be4ff55af755bd71a00ab3dc6bd7ffc467bd76e0df6881e286c2e3d23e8fb43b", size = 11144969, upload-time = "2026-05-21T14:34:43.978Z" }, + { url = "https://files.pythonhosted.org/packages/56/9e/d13e40f83b8d0a94430e6778ce1d94a43b38cf2efe63278bdd2b4c65abbf/ruff-0.15.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:48d5909d7d06276ce7dde6d32bfa4b0d4cb2651145cd8ee4b440722cbc77832f", size = 10478207, upload-time = "2026-05-21T14:34:48.378Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f1/b15a7839fa4f332f8acec78e20564f26bb2d866e3d21710b877fd0263000/ruff-0.15.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca8cbfa94c4f90984a67561978602746d4cd27103568f745fa90eee3f0d4107d", size = 10818459, upload-time = "2026-05-21T14:34:22.318Z" }, + { url = "https://files.pythonhosted.org/packages/45/33/53d651177f84f94b400a0e27f8824eeada3dddc9d5ee8aeb048f4352a520/ruff-0.15.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a6bbc0333f1ab053423bcbf6226477d266ca7cec7738c4c8e3f55647803f3c4", size = 10541800, upload-time = "2026-05-21T14:34:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/b8/a6/868f87e0bf9786ed24b5d0d0ad8676b8a94fd1912f42cddf9cfc7857818a/ruff-0.15.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a24a4f7605d7003a6674d4387651effd939dead3fddd0f36561eb77a9a2e542", size = 11342149, upload-time = "2026-05-21T14:34:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/a7/8b/38cd5c19faffdcc05a408d2b78edccc69492ab9720eadb49ea15ef80d768/ruff-0.15.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:049b5326e53ed80978f2fc041a280603f69dd6b0c95464342a2bb4572d9d9e2f", size = 12212563, upload-time = "2026-05-21T14:34:28.579Z" }, + { url = "https://files.pythonhosted.org/packages/3e/4d/a3c5b874a556d5731e3e657aaf04311bb76f0a5c3ec220ed43051be6b64b/ruff-0.15.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4ed42e6696c8dfa5f06728e6441993901f548eb92d73bc472cb5a38d1395fbf", size = 11493299, upload-time = "2026-05-21T14:34:41.836Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c0/56472c251d09858a53e51efbd485b09e1995d8731668b76d52e5dd6ee0f1/ruff-0.15.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:715c543cf450c4888251f91c52f1942a800541d9bddd7ac060aa4e6b77ae7cba", size = 11455931, upload-time = "2026-05-21T14:34:57.276Z" }, + { url = "https://files.pythonhosted.org/packages/2c/4a/e2e7b4d8dbf233d4eace59c75bc3435fa6d8bd3bae82d351d4e4300c0fd1/ruff-0.15.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:72ebab6013ec887d439d8b7593737a0a4ffb06d45d209d4e4bf2e92813082d3f", size = 11400794, upload-time = "2026-05-21T14:34:39.773Z" }, + { url = "https://files.pythonhosted.org/packages/97/c7/83c0539fe34c3e09136204d1e75d6052492364e0b3cb05e9465423f567d7/ruff-0.15.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:49072d36abdbe97a8dd7f480afe9c675699c0c495d4c84076e2c1203c4550581", size = 10804759, upload-time = "2026-05-21T14:34:31.045Z" }, + { url = "https://files.pythonhosted.org/packages/86/a6/18f2bfc095a2ab4a78745644e428205532ce6653a5d0fa8501572891534d/ruff-0.15.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:958522aee105068640c2c2ceae08f413ae44d922f52a1374ac13d6a96032fc93", size = 10539517, upload-time = "2026-05-21T14:34:53.064Z" }, + { url = "https://files.pythonhosted.org/packages/54/3a/5a8b3b69c654d4e4bf1d246ac5b49cbcdac6eaab6905925f8915f31e3b80/ruff-0.15.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f3707da619a143a2e8830e2abab8224478d69ace2d28cb6c20543ae97c36bf61", size = 11065169, upload-time = "2026-05-21T14:34:24.484Z" }, + { url = "https://files.pythonhosted.org/packages/ed/c5/8864e4e7925b836ea354b31d57641ec03830564e281a8b6f061f8c3e0ec1/ruff-0.15.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:bb01d645694e3ec0102105d07ef2d53703970407d59c04e59d3ba0b7a1d53553", size = 11560214, upload-time = "2026-05-21T14:34:50.975Z" }, + { url = "https://files.pythonhosted.org/packages/36/38/012bf76752e1f89ed50b77b99532d90f3a3e287bc7918e1fc0948ac866ac/ruff-0.15.14-py3-none-win32.whl", hash = "sha256:6d0c1ad2a0ab718d39b6d8fd2217981ce4d625cd96a720095f798fb47d8b13e6", size = 10805548, upload-time = "2026-05-21T14:34:33.453Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b7/4ea2c170f10ad760fff2a5250beb18897719dc8b52b53a24cddbb9dd3f19/ruff-0.15.14-py3-none-win_amd64.whl", hash = "sha256:802342981e056db3851a7836e5b070f8f15f67d4a685ae2a6160939d364b2902", size = 11939523, upload-time = "2026-05-21T14:34:18.077Z" }, + { url = "https://files.pythonhosted.org/packages/62/d5/bc97ff895ec35cf3925d4bd60f3b39d822f377a446906ec9bcc87405e59b/ruff-0.15.14-py3-none-win_arm64.whl", hash = "sha256:ff47b90a9ef6a40c9e2f3b479c1fb78531adf055b94c1eba0a7ba04b31951826", size = 11208607, upload-time = "2026-05-21T14:34:26.525Z" }, ] [[package]] name = "s3transfer" -version = "0.17.0" +version = "0.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9b/ec/7c692cde9125b77e84b307354d4fb705f98b8ccad59a036d5957ca75bfc3/s3transfer-0.17.0.tar.gz", hash = "sha256:9edeb6d1c3c2f89d6050348548834ad8289610d886e5bf7b7207728bd43ce33a", size = 155337, upload-time = "2026-04-29T22:07:36.33Z" } +sdist = { url = "https://files.pythonhosted.org/packages/11/b3/bcdc2f58fa92592db511beda154c2c08d28f21f6c4637f06a42a24b10c21/s3transfer-0.17.1.tar.gz", hash = "sha256:042dd5e3b1b512355e35a23f0223e426b7042e80b97830ea2680ddce327fc45e", size = 159439, upload-time = "2026-05-26T19:45:01.714Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/72/c6c32d2b657fa3dad1de340254e14390b1e334ce38268b7ad51abda3c8c2/s3transfer-0.17.0-py3-none-any.whl", hash = "sha256:ce3801712acf4ad3e89fb9990df97b4972e93f4b3b0004d214be5bce12814c20", size = 86811, upload-time = "2026-04-29T22:07:34.966Z" }, + { url = "https://files.pythonhosted.org/packages/85/dd/904873250a6554fbae40cddbf9198e3cc37a2f1319d5e1a5ce82fe269c17/s3transfer-0.17.1-py3-none-any.whl", hash = "sha256:5b9827d1044159bbb01b86ef8902760ea39281927f5de31de75e1d657177bf4c", size = 88264, upload-time = "2026-05-26T19:45:00.452Z" }, ] [[package]] @@ -6966,7 +6915,7 @@ wheels = [ [[package]] name = "scalene" -version = "2.2.1" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cloudpickle" }, @@ -6978,19 +6927,19 @@ dependencies = [ { name = "pyyaml" }, { name = "rich" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/fc/ed550649058e9ad4bc262039c2c146c92e4d5d0b9d305c7d33d709c151f5/scalene-2.2.1.tar.gz", hash = "sha256:642f809e34d84cae5712bbe8cca76450af81f2dc4c02762b714d1710160dd791", size = 9483401, upload-time = "2026-03-22T14:49:36.825Z" } +sdist = { url = "https://files.pythonhosted.org/packages/91/96/e2ea55e8d1d981fb2d0fe76c38c1d7ab7b91c2229176bbeed771b9da0f1f/scalene-2.3.0.tar.gz", hash = "sha256:57212ceeeeeef9891c344275592413f4b90fb407ecdec75b5b164f0eef2e6261", size = 9457857, upload-time = "2026-05-12T18:45:43.894Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/16/79de96af863a9df1ac9a3b30fc0c9bd51fe4754c137207b2ab3a1f7ebe2f/scalene-2.2.1-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:87be9856bd8b13d87e023ab5e96feae36fd6611912a5433e996e5c34288fe2a4", size = 1225210, upload-time = "2026-03-22T14:49:35.4Z" }, - { url = "https://files.pythonhosted.org/packages/4c/fa/9118db832a7b8bf4eacf20273c5d9e4fe2554bda103712df35273ef193ad/scalene-2.2.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ff0507f7f176703cf4d3f51b4778e9282910699d8de717040cd4ac070aff97b", size = 1526990, upload-time = "2026-03-22T14:42:40.539Z" }, - { url = "https://files.pythonhosted.org/packages/95/3a/10e260f5465d77191affc6ef27c04a59451a47ef7d947957c85dc12ca346/scalene-2.2.1-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:e9625d2bf3ab1fef827f836e38134d3f56fc319513ac7632e45c36fd6c00d872", size = 1224979, upload-time = "2026-03-22T14:44:08.652Z" }, - { url = "https://files.pythonhosted.org/packages/1f/b4/6765587712d20353f0d96951bee397e3e6303bc00f376d2561982b081efc/scalene-2.2.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eb22ef9c1bb8fb3df95365b6263a57e94eb81e777f550af1c513d7f32a50721", size = 1527444, upload-time = "2026-03-22T14:42:33.537Z" }, - { url = "https://files.pythonhosted.org/packages/60/a6/7c5b47a3ae695ab7110919b98d60f98c0ca1e6001f3a2e2a9c18bee5c711/scalene-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:de06f01ec97cae6939e5ff3a31e6ab0d96cfe3cf8a2146bea536a998ddac434c", size = 1158170, upload-time = "2026-03-22T14:44:52.863Z" }, - { url = "https://files.pythonhosted.org/packages/85/f0/5697e94cf16a82999e6c5d456b457fd037ce406399b75070dafbe10173ea/scalene-2.2.1-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:6f94c608d7186ce3d6311bf5b7270c2ba69350f96141c8e913e5fcd272d00ef2", size = 1229689, upload-time = "2026-03-22T14:49:47.403Z" }, - { url = "https://files.pythonhosted.org/packages/ec/43/8e9bb41288713df00a8b1d0c9eabe00272cf4a93370992cbbfb26ef660f7/scalene-2.2.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6a72a3a55c3672779bcbd0d8278d9557ff99ef23d8d1b99b9cdcef3138c25b9", size = 1537230, upload-time = "2026-03-22T14:42:36.301Z" }, - { url = "https://files.pythonhosted.org/packages/a8/86/08024979ec47aed7fa26c8c5c644539c84c935cf06657c34f745a31defb7/scalene-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:6adf5db81cc83b644fe0d60640b0b3097cfa4ff36911bc1e9f1833023930c99a", size = 1159794, upload-time = "2026-03-22T14:44:46.863Z" }, - { url = "https://files.pythonhosted.org/packages/4b/38/6ee3bbd6eb983bf6f25427b4110b2f2d34359e78fbb02b79b4e9c11ca565/scalene-2.2.1-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:7e13ceaaea670c385825b9b21b58596f86f4d0b6e2a0927eefa9dcbf1444380b", size = 1229918, upload-time = "2026-03-22T14:49:37.607Z" }, - { url = "https://files.pythonhosted.org/packages/94/1a/0e7a49db2440c31e66479c93567cc4e4f0198dbe413699a2de9d2095a8e3/scalene-2.2.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:edb511df0097d3091b4967c70023698306e5fa2c75e9532216904b882e3bd02e", size = 1537041, upload-time = "2026-03-22T14:42:40.067Z" }, - { url = "https://files.pythonhosted.org/packages/c8/13/a11f6b081c7235a66da71c5230adf5a7a87ef92a6cd98c659494ec20d22e/scalene-2.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:8af3834504d3a4f4ebaa489bb9266563be818972c83a826337936b6809c145e7", size = 1165782, upload-time = "2026-03-22T14:45:13.804Z" }, + { url = "https://files.pythonhosted.org/packages/00/e3/634743a62ff8cbcefea0a7b4e04d1ef78bf3ea79ed20153befed36b6aae3/scalene-2.3.0-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:d63903b03316b54f2487d9ea6db91e639b5f748ff37af88c80df4e0068e2a7f6", size = 1128921, upload-time = "2026-05-12T18:45:41.939Z" }, + { url = "https://files.pythonhosted.org/packages/a6/8e/f40b8682862c47b76362bb8523646c7b3db127ff26f51c9c98cd65f3b50a/scalene-2.3.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6952d0a7c23add84a5f9f4c5fa375e44d6e5635aeb7a4198396be30aa168a772", size = 1483511, upload-time = "2026-05-12T18:46:01.879Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f0/165e82e24e77a6e006ea3ba12f4302cc5d1229b7777d9fa30fe7b3750d43/scalene-2.3.0-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:cf614b958c30b8d795f99796eb0d24370d6638353a3a1a89de3301d9181cf8c4", size = 1134026, upload-time = "2026-05-12T18:45:48.37Z" }, + { url = "https://files.pythonhosted.org/packages/67/b8/2c3b8505948510281faa2c0d3cd089741764048ab2f4e64b58e3284865d0/scalene-2.3.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b8a6d26abf75226a64a9af4d9f3b74cd8c3cd9f67639d1c0950a3dfe517dc9f", size = 1495417, upload-time = "2026-05-12T18:45:59.364Z" }, + { url = "https://files.pythonhosted.org/packages/a4/5b/e096effad2eb5817aa47d405a000291b8d312f4a458fb59a50edf96631ef/scalene-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:31ccce9167a237acdfe70b2735aafb022c00fc7eb21c64724b974fbf5ea58214", size = 1046214, upload-time = "2026-05-12T18:49:41.178Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c0/b35cfe08c8b2babfd37f8b8cdc3480269dc337485e9bd55209d9b9d000c3/scalene-2.3.0-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:68ccf616be1bd957802fdc88675810352c714d26bfdbcf25445e68711822e510", size = 1134020, upload-time = "2026-05-12T18:45:49.5Z" }, + { url = "https://files.pythonhosted.org/packages/72/3f/420c84e218324edec59ccfcda32c82ef8eb6778ce6ebb0f703bb87f6f555/scalene-2.3.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:deca1214b8f2b3b1d2c770651d7817364c2e9d41c17e0fd37537707ae8482121", size = 1495613, upload-time = "2026-05-12T18:46:01.544Z" }, + { url = "https://files.pythonhosted.org/packages/f2/82/f7505155102e5a87e8ddeb869d6f69a5ab86c26a7eeff3b7be7c999412f7/scalene-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:5af3eeccf109712d0bb14c4d2782314835c30de3aa7286c787f25441caadf248", size = 1046204, upload-time = "2026-05-12T18:50:24.805Z" }, + { url = "https://files.pythonhosted.org/packages/64/59/53e513a6cd926e1b222491aa7105891f7b0b5f0fcfe713c8046769746358/scalene-2.3.0-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:b8400014d4af47410f6ba5250e27f19f1addf0f534cef7013b1a7827170f5ea8", size = 1134328, upload-time = "2026-05-12T18:46:29.283Z" }, + { url = "https://files.pythonhosted.org/packages/0a/ad/99f345fb1030fb1eca7d9bc8bc77d52a642bb81dbd6efb7d599d490b2c9e/scalene-2.3.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2c6acc3bb76e32dfbfc7a0fe75986cde8eb8e98cee2515aea541631d440bc465", size = 1495627, upload-time = "2026-05-12T18:46:01.128Z" }, + { url = "https://files.pythonhosted.org/packages/1d/35/2c8ba0472df79511e3a903100d95348c6d3113d2e2fdca89e2ca5141f265/scalene-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:dec3398f6166bbe65494241506745ed088395c6298d31394461bf7061729edef", size = 1054162, upload-time = "2026-05-12T18:49:17.465Z" }, ] [[package]] @@ -7008,7 +6957,7 @@ wheels = [ [[package]] name = "selenium" -version = "4.43.0" +version = "4.44.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -7018,9 +6967,9 @@ dependencies = [ { name = "urllib3", extra = ["socks"] }, { name = "websocket-client" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/09/6a/fe950b498a3c570ab538ad1c2b60f18863eecf077a865eea4459f3fa78a9/selenium-4.43.0.tar.gz", hash = "sha256:bada5c08a989f812728a4b5bea884d8e91894e939a441cc3a025201ce718581e", size = 967747, upload-time = "2026-04-10T06:47:03.149Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/4a/6d0a4f4a07e2a91511a51398203ee82bf6ce644a448aaa35c59b44aa9531/selenium-4.44.0.tar.gz", hash = "sha256:b03a831fcfcab9d912b4682f60718c48a04560d6c62f7496c16b7498c9a4427e", size = 993133, upload-time = "2026-05-12T22:48:19.246Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/c7/0c55fbb0275fc368676ea50514ce7d7839d799a8b3ff8425f380186c7626/selenium-4.43.0-py3-none-any.whl", hash = "sha256:4f97639055dcfa9eadf8ccf549ba7b0e49c655d4e2bde19b9a44e916b754e769", size = 9573091, upload-time = "2026-04-10T06:47:01.134Z" }, + { url = "https://files.pythonhosted.org/packages/1f/bc/885047e975e996cb317db31c4551caa915aafc6befea990f082c7233adc2/selenium-4.44.0-py3-none-any.whl", hash = "sha256:d01ea3e5ecad8149460a765f7cf5177194c21dcc0173093fc05427c289b1bf24", size = 9654291, upload-time = "2026-05-12T22:48:16.836Z" }, ] [[package]] @@ -7043,15 +6992,15 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.59.0" +version = "2.61.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/65/e0/9bf5e5fc7442b10880f3ec0eff0ef4208b84a099606f343ec4f5445227fb/sentry_sdk-2.59.0.tar.gz", hash = "sha256:cd265808ef8bf3f3edf69b527c0a0b2b6b1322762679e55b8987db2e9584aec1", size = 447331, upload-time = "2026-05-04T12:19:06.538Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/4d/3c66e6045bd2071256b6b6fdcb0cc02b86ce54b2acc2ceac79af8e0efbb5/sentry_sdk-2.61.0.tar.gz", hash = "sha256:1ca9b4bb777eb5be67004edab7eb894f21c6301f1d05ed64966719ad5d1764ce", size = 458510, upload-time = "2026-05-28T09:40:28.917Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/00/b8cc413748fb6383d1582e7cda51314f99743351c462a92dc690d5b5853b/sentry_sdk-2.59.0-py2.py3-none-any.whl", hash = "sha256:abcf65ee9a9d9cdebf9ad369782408ecca9c1c792686ef06ba34f5ab233527fe", size = 468432, upload-time = "2026-05-04T12:19:04.741Z" }, + { url = "https://files.pythonhosted.org/packages/21/5a/9794736d5802689c1a48862e6afe6b7f3e86cc37c15d4a84bc0143877dc1/sentry_sdk-2.61.0-py3-none-any.whl", hash = "sha256:ec4d30273909cb1d198e03208b16ee70e2bc5d90a16fd9f1fb2fc6a72e1f03dc", size = 483111, upload-time = "2026-05-28T09:40:27.027Z" }, ] [[package]] @@ -7154,14 +7103,14 @@ wheels = [ [[package]] name = "smart-open" -version = "7.6.0" +version = "7.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/33/7a00ac9b4a63afb4279b99a766f6cbe56c443526dcbf5db97b219e21fde9/smart_open-7.6.0.tar.gz", hash = "sha256:44717f46b5ff276fac03b88e5d13d1c416f064f3b7b081381b0fa8889004bd7e", size = 54548, upload-time = "2026-04-13T09:48:04.347Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/65/3ada667d32675399001bf022ad3d9f3989b57101351ebc71d6fbe2384634/smart_open-7.6.1.tar.gz", hash = "sha256:4347996e7ba21db7cd1e059632e0b30395407e4f6c660d2ddffc8f2a9ae5f990", size = 54754, upload-time = "2026-05-09T06:23:37.06Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/bc/2761410d0541e975f384bc89f062d716bf119499dd097eb1af33dcd3b1c0/smart_open-7.6.0-py3-none-any.whl", hash = "sha256:2a78f454610a826aa688065b54b4a0a9b12a5599fa61d5190e9bac2df5e5f53f", size = 64591, upload-time = "2026-04-13T09:48:02.687Z" }, + { url = "https://files.pythonhosted.org/packages/14/78/0f68b93564b8c6b6987a0696c582ba2591a381ab2f733a501909e949f241/smart_open-7.6.1-py3-none-any.whl", hash = "sha256:b4de6aebef023aca91cc9fb372052e1343ba3f152de215bd22391a663e3ddd21", size = 64845, upload-time = "2026-05-09T06:23:35.386Z" }, ] [package.optional-dependencies] @@ -7180,11 +7129,11 @@ wheels = [ [[package]] name = "snowballstemmer" -version = "3.0.1" +version = "3.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } +sdist = { url = "https://files.pythonhosted.org/packages/63/ee/67eef9600338e245ad7838230969a34c823ddbdbccc5e1fc43cd75b55bc9/snowballstemmer-3.1.0.tar.gz", hash = "sha256:fd9e34526b23340cd23ffea6c9f9760974ecc2c2ac9e1d81401443ccdb2a801f", size = 122523, upload-time = "2026-05-24T19:04:19.691Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/49/83/ddbf4533c62dd32667ef1238952abef155f3d3391f5be69a352ad1638a42/snowballstemmer-3.1.0-py3-none-any.whl", hash = "sha256:17e6d1da216aa07db6dad37139ea70cf13c4b2e9a096f6e64a9648fc657d3154", size = 104550, upload-time = "2026-05-24T19:04:18.026Z" }, ] [[package]] @@ -7198,11 +7147,11 @@ wheels = [ [[package]] name = "soupsieve" -version = "2.8.3" +version = "2.8.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/ae/2d9c981590ed9999a0d91755b47fc74f74de286b0f5cee14c9269041e6c4/soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349", size = 118627, upload-time = "2026-01-20T04:27:02.457Z" } +sdist = { url = "https://files.pythonhosted.org/packages/47/2c/0a5f6f8ee0d5589e48c7640213ed5175d52cf540a06725b628cc1a45d6ce/soupsieve-2.8.4.tar.gz", hash = "sha256:e121fd02e975c695e4e9e8774a5ee35d74714b59307868dcc5319ad2d9e3328e", size = 121110, upload-time = "2026-05-24T13:55:57.154Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016, upload-time = "2026-01-20T04:27:01.012Z" }, + { url = "https://files.pythonhosted.org/packages/5e/f5/0c41cb68dcae6b7de4fac4188a3a9589e21fb31df21ea3a2e888db95e6c9/soupsieve-2.8.4-py3-none-any.whl", hash = "sha256:e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65", size = 37304, upload-time = "2026-05-24T13:55:55.406Z" }, ] [[package]] @@ -7367,21 +7316,21 @@ sdist = { url = "https://files.pythonhosted.org/packages/c2/07/4d6cb9dbe1b06be5c [[package]] name = "sphinx-tabs" -version = "3.4.5" +version = "3.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docutils" }, { name = "pygments" }, { name = "sphinx" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/27/32/ab475e252dc2b704e82a91141fa404cdd8901a5cf34958fd22afacebfccd/sphinx-tabs-3.4.5.tar.gz", hash = "sha256:ba9d0c1e3e37aaadd4b5678449eb08176770e0fc227e769b6ce747df3ceea531", size = 16070, upload-time = "2024-01-21T12:13:39.392Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/30/ca5b0de830f369968d8e3483dd45a8908fd10169c05cd9837f0bd075982e/sphinx_tabs-3.5.0.tar.gz", hash = "sha256:91dba1187e4c35fd37380a56ac228bbd54c6c649b2351829f3bf033718277537", size = 17006, upload-time = "2026-03-03T23:00:30.404Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/9f/4ac7dbb9f23a2ff5a10903a4f9e9f43e0ff051f63a313e989c962526e305/sphinx_tabs-3.4.5-py3-none-any.whl", hash = "sha256:92cc9473e2ecf1828ca3f6617d0efc0aa8acb06b08c56ba29d1413f2f0f6cf09", size = 9904, upload-time = "2024-01-21T12:13:37.67Z" }, + { url = "https://files.pythonhosted.org/packages/2e/45/6adc5efeb19fd5fed4027e520b5c668ce58236a2b271ade5533c4c116276/sphinx_tabs-3.5.0-py3-none-any.whl", hash = "sha256:154be49de4d5c8249ea08c5d9bf88ca8f9c31e00a178305a93cbc33e000339e5", size = 9871, upload-time = "2026-03-03T23:00:28.89Z" }, ] [[package]] name = "sphinx-toolbox" -version = "4.1.2" +version = "4.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "apeye" }, @@ -7403,9 +7352,9 @@ dependencies = [ { name = "tabulate" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/16/769454374719569f6165149a16d06b8da27cda8330fe5f932c2c65a77a04/sphinx_toolbox-4.1.2.tar.gz", hash = "sha256:c30a4f86c4c29e97adb0eb9337d35f5093cb96a44f49caffcf7d5bc58a88b781", size = 114911, upload-time = "2026-01-14T17:33:02.156Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/fe/45526091eb845625e5feed9685152682b6bb6b792f193533bc091c9a9277/sphinx_toolbox-4.2.0.tar.gz", hash = "sha256:f4dac92fc70e09e9af15d6b8ff003bde1f2aa3573118910f81cf486ced68d4ea", size = 116034, upload-time = "2026-05-21T20:20:01.105Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/38/6763a5a981770a21a18b66d3593a8bbb801884d9828c126866efdd01d41a/sphinx_toolbox-4.1.2-py3-none-any.whl", hash = "sha256:0438f8342ba1c6c0d6e47207f4eac167adba61742e8c2b1dc9624ff955b7bc89", size = 196812, upload-time = "2026-01-14T17:33:00.51Z" }, + { url = "https://files.pythonhosted.org/packages/c5/c2/055e2fa4329d6514befbdc01f646a78e32645be1033d56713c3e1c3be2c9/sphinx_toolbox-4.2.0-py3-none-any.whl", hash = "sha256:09f7bef48ee8936ecd890a298b3e1908fda11fe9e6a21fc623495ac34e42c962", size = 198012, upload-time = "2026-05-21T20:19:59.071Z" }, ] [[package]] @@ -7502,15 +7451,15 @@ wheels = [ [[package]] name = "sse-starlette" -version = "3.4.1" +version = "3.4.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "starlette" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/9a/f35932a8c0eb6b2287b66fa65a0321df8c84e4e355a659c1841a37c39fdb/sse_starlette-3.4.1.tar.gz", hash = "sha256:f780bebcf6c8997fe514e3bd8e8c648d8284976b391c8bed0bcb1f611632b555", size = 35127, upload-time = "2026-04-26T13:32:32.292Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/2b/58abc2d1fd397e7dde08e947e05c884d8ef2f78d5e2588c17a12d42d6994/sse_starlette-3.4.4.tar.gz", hash = "sha256:07e0fa0460138baf25cdd5fb28683472c3995dc1642225191b3832d62526bcb0", size = 31819, upload-time = "2026-05-12T17:37:17.019Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/07/45c21ed03d708c477367305726b89919b020a3a2a01f72aaf5ad941caf35/sse_starlette-3.4.1-py3-none-any.whl", hash = "sha256:6b43cf21f1d574d582a6e1b0cfbde1c94dc86a32a701a7168c99c4475c6bd1d0", size = 16487, upload-time = "2026-04-26T13:32:30.819Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/805710444ea8cc75fbf70b920ed431a560c4bf9c57f7d5a3117213189399/sse_starlette-3.4.4-py3-none-any.whl", hash = "sha256:3f4dd50d8aed2771a091f3a83000323fc3844541c16b4fe585ae2420cc6df973", size = 16514, upload-time = "2026-05-12T17:37:15.601Z" }, ] [[package]] @@ -7551,30 +7500,31 @@ wheels = [ [[package]] name = "swagger-plugin-for-sphinx" -version = "7.0.0" +version = "7.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docutils" }, { name = "jinja2" }, + { name = "pyyaml" }, { name = "sphinx" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/14/36a4b2172fd6496c646d58dcb11c2877e09cc38e6d206eaa822dad0d0eab/swagger_plugin_for_sphinx-7.0.0.tar.gz", hash = "sha256:86a18b9ec1da2b78a80772fbd5d10549bf393b072f07da9edb5d1eaac02b8d44", size = 106931, upload-time = "2026-02-20T09:10:17.859Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/58/535946469ebabad714c3e724273f6f8a7f39c273a8e8c9ccbc958d082ef3/swagger_plugin_for_sphinx-7.2.0.tar.gz", hash = "sha256:5dfe5b6639020da9ede92532458637db457f6e8babb476fa8386345da635de4b", size = 121566, upload-time = "2026-05-28T08:49:06.416Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/2d/534436c46a992b8f31bb56e591422519e218b970db1d596ff96142776613/swagger_plugin_for_sphinx-7.0.0-py3-none-any.whl", hash = "sha256:e5207f385f5061bf05eb37355a3a83794bab6e41c5404f5cf4905b90efc9684f", size = 11170, upload-time = "2026-02-20T09:10:15.479Z" }, + { url = "https://files.pythonhosted.org/packages/92/be/f559ad0703804e591c2b294bccba26734c0578ad477bc96db647270871e3/swagger_plugin_for_sphinx-7.2.0-py3-none-any.whl", hash = "sha256:4dd6741ddd68e28a6f35b6fbcdb9e0a7f601b45d709c0a8a270a18a88b833ca2", size = 13660, upload-time = "2026-05-28T08:49:05.199Z" }, ] [[package]] name = "tabledata" -version = "1.3.4" +version = "1.3.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dataproperty" }, { name = "typepy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b2/35/171c8977162f1163368406deddde4c59673b62bd0cb2f34948a02effb075/tabledata-1.3.4.tar.gz", hash = "sha256:e9649cab129d718f3bff4150083b77f8a78c30f6634a30caf692b10fdc60cb97", size = 25074, upload-time = "2024-12-31T14:12:31.198Z" } +sdist = { url = "https://files.pythonhosted.org/packages/75/65/2f54f0dedd775dde48e300023d20e13ad329a51e33dcadb6d47b4dc95768/tabledata-1.3.5.tar.gz", hash = "sha256:98c64d0ad6b520846b41000fb3f5b2f42fa7ca2675c2c669e5ccab6b93082a36", size = 25396, upload-time = "2026-05-11T12:03:26.367Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/64/fa4160151976ee4b2cf0c1217a99443ffaeb991956feddfeac9eee9952f8/tabledata-1.3.4-py3-none-any.whl", hash = "sha256:1f56e433bfdeb89f4487abfa48c4603a3b07c5d3a3c7e05ff73dd018c24bd0d4", size = 11820, upload-time = "2024-12-31T14:12:28.584Z" }, + { url = "https://files.pythonhosted.org/packages/c7/86/37fa0e1437089f08b8b1b8c8ad93f6b57e9427753f002914299323300a9e/tabledata-1.3.5-py3-none-any.whl", hash = "sha256:a1e57afc4767b51bef551114c0df31f205d712dbb75e3caf9be7834a79f23136", size = 11919, upload-time = "2026-05-11T12:03:24.907Z" }, ] [[package]] @@ -7695,28 +7645,28 @@ wheels = [ [[package]] name = "tomlkit" -version = "0.14.0" +version = "0.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/af/14b24e41977adb296d6bd1fb59402cf7d60ce364f90c890bd2ec65c43b5a/tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064", size = 187167, upload-time = "2026-01-13T01:14:53.304Z" } +sdist = { url = "https://files.pythonhosted.org/packages/51/db/03eaf4331631ef6b27d6e3c9b68c54dc6f0d63d87201fed600cc409307fd/tomlkit-0.15.0.tar.gz", hash = "sha256:7d1a9ecba3086638211b13814ea79c90dd54dd11993564376f3aa92271f5c7a3", size = 161875, upload-time = "2026-05-10T07:38:22.245Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", size = 39310, upload-time = "2026-01-13T01:14:51.965Z" }, + { url = "https://files.pythonhosted.org/packages/6a/43/8bd850ee71a191bf072e31302c73a66be413fecdd98fdcd111ecbcce13ca/tomlkit-0.15.0-py3-none-any.whl", hash = "sha256:4dbc8f0fc024412b57ced8757ac7461305126a648ff8c2c807fcb8e133a78738", size = 41328, upload-time = "2026-05-10T07:38:23.517Z" }, ] [[package]] name = "tornado" -version = "6.5.5" +version = "6.5.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/f1/3173dfa4a18db4a9b03e5d55325559dab51ee653763bb8745a75af491286/tornado-6.5.5.tar.gz", hash = "sha256:192b8f3ea91bd7f1f50c06955416ed76c6b72f96779b962f07f911b91e8d30e9", size = 516006, upload-time = "2026-03-10T21:31:02.067Z" } +sdist = { url = "https://files.pythonhosted.org/packages/50/57/6d7303a77ae439d9189108f76c0c4fd89ee5e2cc8387bffb55232565c4ed/tornado-6.5.6.tar.gz", hash = "sha256:9a365179fe8ff6b8766f602c0f67c185d778193e9bdd828b19f0b6ed7764177d", size = 518139, upload-time = "2026-05-27T15:35:54.646Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:487dc9cc380e29f58c7ab88f9e27cdeef04b2140862e5076a66fb6bb68bb1bfa", size = 445983, upload-time = "2026-03-10T21:30:44.28Z" }, - { url = "https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:65a7f1d46d4bb41df1ac99f5fcb685fb25c7e61613742d5108b010975a9a6521", size = 444246, upload-time = "2026-03-10T21:30:46.571Z" }, - { url = "https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e74c92e8e65086b338fd56333fb9a68b9f6f2fe7ad532645a290a464bcf46be5", size = 447229, upload-time = "2026-03-10T21:30:48.273Z" }, - { url = "https://files.pythonhosted.org/packages/34/01/74e034a30ef59afb4097ef8659515e96a39d910b712a89af76f5e4e1f93c/tornado-6.5.5-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:435319e9e340276428bbdb4e7fa732c2d399386d1de5686cb331ec8eee754f07", size = 448192, upload-time = "2026-03-10T21:30:51.22Z" }, - { url = "https://files.pythonhosted.org/packages/be/00/fe9e02c5a96429fce1a1d15a517f5d8444f9c412e0bb9eadfbe3b0fc55bf/tornado-6.5.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3f54aa540bdbfee7b9eb268ead60e7d199de5021facd276819c193c0fb28ea4e", size = 448039, upload-time = "2026-03-10T21:30:53.52Z" }, - { url = "https://files.pythonhosted.org/packages/82/9e/656ee4cec0398b1d18d0f1eb6372c41c6b889722641d84948351ae19556d/tornado-6.5.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:36abed1754faeb80fbd6e64db2758091e1320f6bba74a4cf8c09cd18ccce8aca", size = 447445, upload-time = "2026-03-10T21:30:55.541Z" }, - { url = "https://files.pythonhosted.org/packages/5a/76/4921c00511f88af86a33de770d64141170f1cfd9c00311aea689949e274e/tornado-6.5.5-cp39-abi3-win32.whl", hash = "sha256:dd3eafaaeec1c7f2f8fdcd5f964e8907ad788fe8a5a32c4426fbbdda621223b7", size = 448582, upload-time = "2026-03-10T21:30:57.142Z" }, - { url = "https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl", hash = "sha256:6443a794ba961a9f619b1ae926a2e900ac20c34483eea67be4ed8f1e58d3ef7b", size = 448990, upload-time = "2026-03-10T21:30:58.857Z" }, - { url = "https://files.pythonhosted.org/packages/b7/c8/876602cbc96469911f0939f703453c1157b0c826ecb05bdd32e023397d4e/tornado-6.5.5-cp39-abi3-win_arm64.whl", hash = "sha256:2c9a876e094109333f888539ddb2de4361743e5d21eece20688e3e351e4990a6", size = 448016, upload-time = "2026-03-10T21:31:00.43Z" }, + { url = "https://files.pythonhosted.org/packages/1b/0d/b4f481e18c5a51864e6d12b9a05ecf72919696680b747c958c3fc1f4fbae/tornado-6.5.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:65fcfaafb079435c2c19dc9e07c0f1cf0fa9051759ed0a7d0a3ba7ea7f64919c", size = 447737, upload-time = "2026-05-27T15:35:38.122Z" }, + { url = "https://files.pythonhosted.org/packages/9e/9c/5430c39fcab1144d35860f457b15e9c08b4bc7ac86764354204e983d6183/tornado-6.5.6-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:38bc01b4acacded2de63ae78023548e41ebe6fbed3ec05a796d7ae3ad893887e", size = 445899, upload-time = "2026-05-27T15:35:40.519Z" }, + { url = "https://files.pythonhosted.org/packages/8b/79/fa7e14a2f939c807a8d30619b4eb604eab219601b78792516ebe22d40cf9/tornado-6.5.6-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b942e6a137fda31ff54bf8e6e2c8d1c37f1f50583f3ed53fb840b53b9601d104", size = 448964, upload-time = "2026-05-27T15:35:42.106Z" }, + { url = "https://files.pythonhosted.org/packages/a7/71/bd67d5f5199f937dafe03a49a37989f60f600ff6fef34c79412a829d97bd/tornado-6.5.6-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8666946e70171b8c3f1fc9b7876fac492e84822c4c7f3746f4e8f8bc9ac92a79", size = 449935, upload-time = "2026-05-27T15:35:43.906Z" }, + { url = "https://files.pythonhosted.org/packages/cc/a4/c24388c9cf5b3c3a513b56a158af9f23092c9a2810d789e294310797df21/tornado-6.5.6-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1c34cfab7ad6d104f052f55de06d39bbafc5885cfeb4da688803308dbcfa90b7", size = 449767, upload-time = "2026-05-27T15:35:45.793Z" }, + { url = "https://files.pythonhosted.org/packages/a5/eb/6a07ad550c3f7b37244bd0becdf293ec3d3e961783d8b720a97df50de1b2/tornado-6.5.6-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:385f35e4e22fb52551dfcda4cdc8c30c61c2c001aef5ddad99cdfe116952efd3", size = 449174, upload-time = "2026-05-27T15:35:47.485Z" }, + { url = "https://files.pythonhosted.org/packages/bb/84/3469e098dccdb6763130e06aacd786bb4363fca7b590a55c101ddf34ed30/tornado-6.5.6-cp39-abi3-win32.whl", hash = "sha256:db475f1b67b2809b10bb16264829087724ca8d24fe4ed47f7b8675cae453ef86", size = 450230, upload-time = "2026-05-27T15:35:49.322Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3c/273a04e0b9dd9016f1685cca0c1c8795a71ac88a34a8c889a0b443483226/tornado-6.5.6-cp39-abi3-win_amd64.whl", hash = "sha256:6739bf1e8eb09230f1280ddbd3236f0309db70f2c551a8dbc40f62babdf82f79", size = 450667, upload-time = "2026-05-27T15:35:51.194Z" }, + { url = "https://files.pythonhosted.org/packages/02/98/0cffe22a224f60c5fb1e3aa0b76f9da2e1ca78b0e9545e3d077c68ce60a7/tornado-6.5.6-cp39-abi3-win_arm64.whl", hash = "sha256:2543597b24a695d72338a9a77818362d72387c03ae173f1f169eadc5c91466ac", size = 449690, upload-time = "2026-05-27T15:35:52.902Z" }, ] [[package]] @@ -7733,11 +7683,11 @@ wheels = [ [[package]] name = "traitlets" -version = "5.14.3" +version = "5.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/22/40f55b26baeab80c2d7b3f1db0682f8954e4617fee7d90ce634022ef05c6/traitlets-5.15.0.tar.gz", hash = "sha256:4fead733f81cf1c4c938e06f8ca4633896833c9d89eff878159457f4d4392971", size = 163197, upload-time = "2026-05-06T08:05:58.016Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, + { url = "https://files.pythonhosted.org/packages/da/98/a9937a969d018a23badfea0b381f66783649d48e0ea6c41923265c3cbeb3/traitlets-5.15.0-py3-none-any.whl", hash = "sha256:fb36a18867a6803deab09f3c5e0fa81bb7b26a5c9e82501c9933f759166eff40", size = 85877, upload-time = "2026-05-06T08:05:55.853Z" }, ] [[package]] @@ -7801,38 +7751,38 @@ datetime = [ [[package]] name = "typer" -version = "0.25.1" +version = "0.26.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, - { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "rich" }, { name = "shellingham" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e4/51/9aed62104cea109b820bbd6c14245af756112017d309da813ef107d42e7e/typer-0.25.1.tar.gz", hash = "sha256:9616eb8853a09ffeabab1698952f33c6f29ffdbceb4eaeecf571880e8d7664cc", size = 122276, upload-time = "2026-04-30T19:32:16.964Z" } +sdist = { url = "https://files.pythonhosted.org/packages/67/a5/756f2e6bc81a7dd79aa3c625dd01b74cabc4516628cace2caaec09ca6ff2/typer-0.26.2.tar.gz", hash = "sha256:9b4f19e08fcc9427a822d1ef467b1fe76737a2f65c7926bdeba2337d73569b68", size = 198991, upload-time = "2026-05-27T10:41:39.166Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/f9/2b3ff4e56e5fa7debfaf9eb135d0da96f3e9a1d5b27222223c7296336e5f/typer-0.25.1-py3-none-any.whl", hash = "sha256:75caa44ed46a03fb2dab8808753ffacdbfea88495e74c85a28c5eefcf5f39c89", size = 58409, upload-time = "2026-04-30T19:32:18.271Z" }, + { url = "https://files.pythonhosted.org/packages/8b/a5/6ffd702beda8798b2b82ff70805ed4a66d963557e43a5d1823ab456251a4/typer-0.26.2-py3-none-any.whl", hash = "sha256:39beff72ffbb31978a5b545f677d57edb97c6f980f433b38556deb0af25f094d", size = 123123, upload-time = "2026-05-27T10:41:40.504Z" }, ] [[package]] name = "types-pyyaml" -version = "6.0.12.20260408" +version = "6.0.12.20260518" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/74/73/b759b1e413c31034cc01ecdfb96b38115d0ab4db55a752a3929f0cd449fd/types_pyyaml-6.0.12.20260408.tar.gz", hash = "sha256:92a73f2b8d7f39ef392a38131f76b970f8c66e4c42b3125ae872b7c93b556307", size = 17735, upload-time = "2026-04-08T04:30:50.974Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b8/83/4a1afc3fbfcf5b8d46fc390cd95ed6b0dc9010a265f4e9f46314efffa37a/types_pyyaml-6.0.12.20260518.tar.gz", hash = "sha256:d917f83fb38462550338c1297faedd860b3ec83912b96b1e3d73255f7473e466", size = 17850, upload-time = "2026-05-18T06:01:58.675Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/f0/c391068b86abb708882c6d75a08cd7d25b2c7227dab527b3a3685a3c635b/types_pyyaml-6.0.12.20260408-py3-none-any.whl", hash = "sha256:fbc42037d12159d9c801ebfcc79ebd28335a7c13b08a4cfbc6916df78fee9384", size = 20339, upload-time = "2026-04-08T04:30:50.113Z" }, + { url = "https://files.pythonhosted.org/packages/06/a2/c01db32be2ae7d6a1689972f3c492b149ee4e164b12fdfd9f64b50888215/types_pyyaml-6.0.12.20260518-py3-none-any.whl", hash = "sha256:d2150f75a231c9fe9c7463bd29487d93e60bac90400287351384bc2284eba7cd", size = 20312, upload-time = "2026-05-18T06:01:57.368Z" }, ] [[package]] name = "types-requests" -version = "2.33.0.20260503" +version = "2.33.0.20260518" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/b8/57e94268c0d82ac3eaa2fc35aa8ca7bbc2542f726b67dcf90b0b00a3b14d/types_requests-2.33.0.20260503.tar.gz", hash = "sha256:9721b2d9dbee7131f2fb39f20f0ebb1999c18cef4b512c9a7932f3722de7c5f4", size = 23931, upload-time = "2026-05-03T05:20:08.882Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/01/c5a19253fe1ac159159ddf9a3a07cec8bb5e486ec4d9002ad2821da0e5d2/types_requests-2.33.0.20260518.tar.gz", hash = "sha256:df7bd3bfe0ca8402dfb841e7d9be714bb5578203283d66d7dc4ef69343449a5e", size = 24752, upload-time = "2026-05-18T06:07:37.966Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/82/959113a6351f3ca046cd0a8cd2cee071d7ea47473560557a01eeae9a6fe2/types_requests-2.33.0.20260503-py3-none-any.whl", hash = "sha256:02aaa7e3577a13471715bb1bddb693cc985ea514f754b503bf033e6a09a3e528", size = 20736, upload-time = "2026-05-03T05:20:07.858Z" }, + { url = "https://files.pythonhosted.org/packages/1c/bc/b139710a3b6018f7fb2b9508b35c8af564e61bf2bf4fa619d088f3e16f85/types_requests-2.33.0.20260518-py3-none-any.whl", hash = "sha256:626d697d1adaaff76e2044dc8c5c051d8f21abc157bdfe204a75558076fe0bf0", size = 21391, upload-time = "2026-05-18T06:07:37.044Z" }, ] [[package]] @@ -7876,11 +7826,11 @@ wheels = [ [[package]] name = "uncalled-for" -version = "0.3.1" +version = "0.3.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e1/68/35c1d87e608940badbcfeb630347aa0509897284684f61fab6423d02b253/uncalled_for-0.3.1.tar.gz", hash = "sha256:5e412ac6708f04b56bef5867b5dcf6690ebce4eb7316058d9c50787492bb4bca", size = 49693, upload-time = "2026-04-07T13:05:06.462Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/82/345cc927f7fbdae6065e7768759932fcc827fc20b29b45dfbafa2f1f7da4/uncalled_for-0.3.2.tar.gz", hash = "sha256:89f5dbcd71e2b8f47c030b1fa302e6cce2ec795d1ac565eeb6525c5fe55cb8a2", size = 50032, upload-time = "2026-05-06T13:38:25.204Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/e1/7ec67882ad8fc9f86384bef6421fa252c9cbe5744f8df6ce77afc9eca1f5/uncalled_for-0.3.1-py3-none-any.whl", hash = "sha256:074cdc92da8356278f93d0ded6f2a66dd883dbecaf9bc89437646ee2289cc200", size = 11361, upload-time = "2026-04-07T13:05:05.341Z" }, + { url = "https://files.pythonhosted.org/packages/3b/25/2c87754f3a9e692315f7b811244090e68f362979fc8886b3fbd2985a1d8c/uncalled_for-0.3.2-py3-none-any.whl", hash = "sha256:0ff60b142c7d1f8070bde9d42afaa70aedc77dcc10998c227687e9c15713418e", size = 11444, upload-time = "2026-05-06T13:38:24.025Z" }, ] [[package]] @@ -7920,41 +7870,41 @@ socks = [ [[package]] name = "uv" -version = "0.11.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/87/05605a6c303736a4db98d25efcf4466b99d0c416a567ab0b4f6cf5f74bb2/uv-0.11.10.tar.gz", hash = "sha256:9b3a2ce56c4a22065462473acee0b729140370650974de4e781a5273f49371fa", size = 4112520, upload-time = "2026-05-05T20:22:04.903Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/26/a1659dfd05d4206a2e5ae8ce2ba215762901f9424f2e5b5a317ea84c84b4/uv-0.11.10-py3-none-linux_armv6l.whl", hash = "sha256:136dfcc1d1e95749880ffc07deca7ff9f88bce0f81df5cc437238950483747ed", size = 23553483, upload-time = "2026-05-05T20:21:51.565Z" }, - { url = "https://files.pythonhosted.org/packages/70/7f/22152a46b2ea9bcd9532ea3efa8a6a28488fe162f1bf3cc1f55c003efad8/uv-0.11.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7f61d75508075576ebfd8c17dc96e63276a5d8f7f2c5a969e8fe529d9685816b", size = 23014921, upload-time = "2026-05-05T20:22:06.655Z" }, - { url = "https://files.pythonhosted.org/packages/ed/9f/846c8eb8b5ddd4e1dd6193ef8f1450d83d869bb3aac8b0249d3a5a709fa8/uv-0.11.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:adaae647da800b6ffe8a3d5a9c098704228dd001834f7e113107e5fd02b56df6", size = 21662611, upload-time = "2026-05-05T20:21:38.226Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5e/f750dcde6598ed5afeb267e5122a5328feba0bd8745f8a6130f9dc9500a5/uv-0.11.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:12ace874453eb3a80af4684f72719751127443be75d1422e4a068a5758142f1b", size = 23309698, upload-time = "2026-05-05T20:22:00.023Z" }, - { url = "https://files.pythonhosted.org/packages/53/27/6960b4855cab4297dd4aa0ccabf0b950e046c04be0b3171de383025db562/uv-0.11.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:1f6e5149f89b66458f1f7674c452c8bb02c3a7c0ef3a2b1852cebe9ac044fbd0", size = 23066106, upload-time = "2026-05-05T20:22:02.647Z" }, - { url = "https://files.pythonhosted.org/packages/fa/5e/3b64a98cd7b5f0f3d2ba2c7fadb20a9ca06f1369aace5bff62e22344d78f/uv-0.11.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dc5e49567513edd729e41a2ab57d2add3e5770b25b9c946b3ac80ea79d86cad5", size = 23095741, upload-time = "2026-05-05T20:22:12.112Z" }, - { url = "https://files.pythonhosted.org/packages/ab/27/b4e071b10dc934e39af15bee5ba7fc0e72ab29fa53beb068b84617cba902/uv-0.11.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58eeaf2ed00beebe4d6a277bd7373ed8d0da651e98b6b481c127463123486f99", size = 24572575, upload-time = "2026-05-05T20:21:46.317Z" }, - { url = "https://files.pythonhosted.org/packages/8a/e2/58f3ef1a18aebd2becd93b5036750b01647dc425818f77011a5ddd01606c/uv-0.11.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:82c19288fbb1b5ad37565a8e242a7ee282478f8e67c0cc33a552d32a695af338", size = 25440079, upload-time = "2026-05-05T20:22:09.465Z" }, - { url = "https://files.pythonhosted.org/packages/3d/fc/4e382b58cad8fa201ec020dd0c647f867af3088a9a6450920b8fa48ed0ed/uv-0.11.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98bfe0ab36eb59f66f2d79fff05da4ce5f19629f62c8590a2f9446eaffdcb15b", size = 24554498, upload-time = "2026-05-05T20:21:35.505Z" }, - { url = "https://files.pythonhosted.org/packages/08/20/f0577dc32606c7c7db73d9df116683ea4aeedcc1f57eebda55ad36134016/uv-0.11.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a16c1fced229b82403411378182e08a86f814a9f15a281e558cf2f0d02fc4c99", size = 24671197, upload-time = "2026-05-05T20:22:15.444Z" }, - { url = "https://files.pythonhosted.org/packages/3a/e4/45ad4e0f95e178a7124095e3b2ab08edf5ca2977497d1c32ed101814c42a/uv-0.11.10-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:700ef6a0504ea98592b7bebd1196ad2f4c84be834c913e3cbd4f1411a814b40b", size = 23368345, upload-time = "2026-05-05T20:21:31.72Z" }, - { url = "https://files.pythonhosted.org/packages/34/26/03510e70a4458ea62a9193e82330bc1c10b3d378f27de62eb29fd7ae3e71/uv-0.11.10-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:0100bac6d5b1d7f133cf9def4b6ee51cef9098e04ff0b0ecb5ea19921b37c9af", size = 24080176, upload-time = "2026-05-05T20:21:40.726Z" }, - { url = "https://files.pythonhosted.org/packages/4f/3c/48ae3bcd6157c38110924a1462b7a41b6b3bb54a5174c57a950b3bcbd401/uv-0.11.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:deca9575ac2e8584e64250020352dcba0a2ed458ec949321e54d3d67a20a9c2c", size = 24148746, upload-time = "2026-05-05T20:21:57.182Z" }, - { url = "https://files.pythonhosted.org/packages/ec/0c/2f5c5993cf5ca587ce4ddf2c6a532575d4031d5f209ca35ce15606f830c6/uv-0.11.10-py3-none-musllinux_1_1_i686.whl", hash = "sha256:e51b9239056bbf7e4b4bd5ceaee51d0632ba79cca23889ea7235c1bbea5aeb23", size = 23847885, upload-time = "2026-05-05T20:22:18.127Z" }, - { url = "https://files.pythonhosted.org/packages/6a/8b/589231473d479b5e98b04b5cf64de4f1fe17f26e593a9a3e75e81aa96aad/uv-0.11.10-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:6d3892056347d3e2b7734df2fb194568599cc4b1bce36a99ade75c8458e4d1d0", size = 24858408, upload-time = "2026-05-05T20:21:54.185Z" }, - { url = "https://files.pythonhosted.org/packages/22/f9/c29a05960df10c7770c88fd784a09b830cb0703244e8844dbea7ecacf472/uv-0.11.10-py3-none-win32.whl", hash = "sha256:5d383301c876e02afb90b53d8cce36ce4aa3dcc9289625a93dd8925446157dc2", size = 22569423, upload-time = "2026-05-05T20:21:43.434Z" }, - { url = "https://files.pythonhosted.org/packages/12/e0/41fbd5d50201801b68f024f70ee66c82bfa0515aed9523dcb579a1bc01e9/uv-0.11.10-py3-none-win_amd64.whl", hash = "sha256:97172796ae0cdc9a83f429f24860029d2707cef65c213d0dd7e9a9284ad20b7b", size = 25182765, upload-time = "2026-05-05T20:22:21.077Z" }, - { url = "https://files.pythonhosted.org/packages/ef/f2/e0c8b4127b7473f5c76f77eee35c660b4df819a887eb50ae785740f88e65/uv-0.11.10-py3-none-win_arm64.whl", hash = "sha256:cb13c696fccd6e44e25bcf5d3b8b1661d963ae4ef8fbdead4cef17fdbb580aa7", size = 23551920, upload-time = "2026-05-05T20:21:48.854Z" }, +version = "0.11.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/99/025154611a4bd97a23851574c15d73bb71ada09d35f092d6972f9ac87f70/uv-0.11.16.tar.gz", hash = "sha256:4b435fcb0af8f34833dcc1903a8a223856437efd0d515c2160a2871def221238", size = 4177038, upload-time = "2026-05-21T22:10:01.009Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/e3/8b8cfc802bc476c67e31a39725538193265cf3a19585b4a60c232659f919/uv-0.11.16-py3-none-linux_armv6l.whl", hash = "sha256:c9e9d9cb73ee8cd2ad696dbf1bc3232abaac363270557684b6b85a2bdb8eb276", size = 23508087, upload-time = "2026-05-21T22:10:06.227Z" }, + { url = "https://files.pythonhosted.org/packages/45/78/d5ca91c636ac88e902b6b3ff31ad32d2d02663232d844aff871467a323d2/uv-0.11.16-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:01172238a75e42a5a55d12555cd9ec98bee24249f3645b98a4b32eb5f1ff5e43", size = 23028989, upload-time = "2026-05-21T22:09:50.127Z" }, + { url = "https://files.pythonhosted.org/packages/c7/26/c84580dfec5a87c36fb1218eac17c5194fa3e58e2a9232cf085d69eb6bed/uv-0.11.16-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c75f9b5bac49b97131973910c220feac60fe47b10a333941b237ff0ae4b36721", size = 21572023, upload-time = "2026-05-21T22:09:58.703Z" }, + { url = "https://files.pythonhosted.org/packages/84/68/ba2bdc64fea96ef8c9796a991f244541b65bb9d31c661b322cc724857a4e/uv-0.11.16-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:a801484f4507b6c2133e557350f3143b61b8f8b61dddb01ff7b84a74cdfab1fb", size = 23289936, upload-time = "2026-05-21T22:10:15.423Z" }, + { url = "https://files.pythonhosted.org/packages/c9/81/74922f693d5804a77d009338ca8dc709eff871fb60d9f2c263dede8d77d1/uv-0.11.16-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:eb538069e768b042cf870be700a210518ce628e36d99d9a83b85acaf484d7f6a", size = 23020906, upload-time = "2026-05-21T22:10:24.242Z" }, + { url = "https://files.pythonhosted.org/packages/60/81/cda8886f5df4dd28854a9b97bcc3ee6a7d1b5b5b23aaaccfbf1ed3e5e2bf/uv-0.11.16-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7cdb23457a4d1bc76bf1016638ea1d1ada0e8e032f656168e933d4d17c47e72", size = 23004220, upload-time = "2026-05-21T22:10:32.847Z" }, + { url = "https://files.pythonhosted.org/packages/98/7c/65837e07de23f0a40ab860bc6601f7c022d4bcf4b97ca79b6c35a2e72e65/uv-0.11.16-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:451327388d59ac3041cbda474296f3ceeafac5b1f645476198e7b95f504fcfd5", size = 24319651, upload-time = "2026-05-21T22:10:21.492Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/9d364542bf118433b60ed71422e47d2c8c470aca7d3aef0df9449a5f726a/uv-0.11.16-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7992b8276149b3ffaf35ce9434702d3e16bae6ec393e99df209b870a7e19eb0", size = 25359517, upload-time = "2026-05-21T22:09:46.519Z" }, + { url = "https://files.pythonhosted.org/packages/99/b4/650896e8cff5a3289cee860c41fd9876da83ca628c5871f9a61d5fc75c72/uv-0.11.16-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83a8db9b3314d900e7a240105afce43f806c9e04c59ea10a40bdbdca84c6d0c5", size = 24563421, upload-time = "2026-05-21T22:10:35.82Z" }, + { url = "https://files.pythonhosted.org/packages/b1/7d/184711a8c02466e1486d57efdc9394ce09cbf43ee2c5794da70bd25db3fb/uv-0.11.16-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b10086165189c39c53142a0e2f34e0b8889ef681886f589ed17be45a1a774c7", size = 24676607, upload-time = "2026-05-21T22:10:39.784Z" }, + { url = "https://files.pythonhosted.org/packages/ee/3f/5b338df6505f77f73c20eae38cb29f57d14dba56dac835386e3dc6e2a5d6/uv-0.11.16-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:cfe1f06fb8f135a735a961065d5ee90f99cccf41749fb1f964edb5b3c3dae19b", size = 23401615, upload-time = "2026-05-21T22:10:30.124Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f9/54bbcbc77443dc76468f09a49cc9f4f92ca49b4159a011c6010d223de4ea/uv-0.11.16-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:2454f80d8b548fb2e246151578809b14ad4395b3f357d738bae1af11918e91af", size = 24104468, upload-time = "2026-05-21T22:09:53.323Z" }, + { url = "https://files.pythonhosted.org/packages/3e/0a/b5f105514fddea5110fe3947cd18a9f199ff93dbad78e5e5a08e1b5d0ea2/uv-0.11.16-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:4249d57a563165d368050680deeb722f9c0053a0dbf3244b11cca3e6d85a3c7d", size = 24164861, upload-time = "2026-05-21T22:10:09.458Z" }, + { url = "https://files.pythonhosted.org/packages/f6/01/15d4ca2be7257862b077a9077ac31ce81c419f35ef7994e76356a317716b/uv-0.11.16-py3-none-musllinux_1_1_i686.whl", hash = "sha256:374c30126483ce95675c5de49e54c2454ddedb01c17b8321417fe4eb9da83406", size = 23644919, upload-time = "2026-05-21T22:10:03.129Z" }, + { url = "https://files.pythonhosted.org/packages/49/bf/9de3e262e6ff93aec2e0a4c238857293fd2c616dd79f25bb440f126bf32c/uv-0.11.16-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:746edfc9d1d8cd03dd58739989f634d3580648048d09f81a9c68da74c4eb9d62", size = 24973746, upload-time = "2026-05-21T22:10:18.413Z" }, + { url = "https://files.pythonhosted.org/packages/f6/7d/f4126dce104f1b5d0b451ce3ca41c4db69b963c2e78c3465fcda6440de31/uv-0.11.16-py3-none-win32.whl", hash = "sha256:50299b20aab2d28c05ff27d781ce2af3f5af2102bc304dc07a4ad54b05e2af8a", size = 22400991, upload-time = "2026-05-21T22:10:27.119Z" }, + { url = "https://files.pythonhosted.org/packages/8f/38/99627cb995a03389b227ce4b12b08e770565d0aa7850cd0420973194a638/uv-0.11.16-py3-none-win_amd64.whl", hash = "sha256:e901aafa5007beffafe57bfa44e5e248d99fb5d97036a3718fd65cf9723c5cd3", size = 25067163, upload-time = "2026-05-21T22:10:12.317Z" }, + { url = "https://files.pythonhosted.org/packages/b6/68/3ed1c0bdfb4bec501e5cde73419b4f39c8a125ef905a85fc0f239f19eb9b/uv-0.11.16-py3-none-win_arm64.whl", hash = "sha256:d777cb29661cdfa7f90dae77406c85fb5b729bf8bc13941dc237958a1ea1ba00", size = 23502015, upload-time = "2026-05-21T22:09:56.014Z" }, ] [[package]] name = "uvicorn" -version = "0.46.0" +version = "0.48.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1f/93/041fca8274050e40e6791f267d82e0e2e27dd165627bd640d3e0e378d877/uvicorn-0.46.0.tar.gz", hash = "sha256:fb9da0926999cc6cb22dc7cd71a94a632f078e6ae47ff683c5c420750fb7413d", size = 88758, upload-time = "2026-04-23T07:16:00.151Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/bf/f6544ba992ddb9a6077343a576f9844f7f8f06ab819aefd00206e9255f18/uvicorn-0.48.0.tar.gz", hash = "sha256:a5504207195d08c2511bf9125ede5ac4a4b71725d519e758d01dcf0bc2d31c37", size = 91074, upload-time = "2026-05-24T12:08:41.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/a3/5b1562db76a5a488274b2332a97199b32d0442aca0ed193697fd47786316/uvicorn-0.46.0-py3-none-any.whl", hash = "sha256:bbebbcbed972d162afca128605223022bedd345b7bc7855ce66deb31487a9048", size = 70926, upload-time = "2026-04-23T07:15:58.355Z" }, + { url = "https://files.pythonhosted.org/packages/01/be/72532be3da7acc5fdfbccdb95215cd04f995a0886532a5b423f929cda4cc/uvicorn-0.48.0-py3-none-any.whl", hash = "sha256:48097851328b87ec36117d3d575234519eb58c2b22d79666e9bbc6c49a761dad", size = 71410, upload-time = "2026-05-24T12:08:40.258Z" }, ] [package.optional-dependencies] @@ -8008,7 +7958,7 @@ wheels = [ [[package]] name = "virtualenv" -version = "21.3.1" +version = "21.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, @@ -8016,9 +7966,9 @@ dependencies = [ { name = "platformdirs" }, { name = "python-discovery" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ec/0d/915c02c94d207b85580eb09bffab54438a709e7288524094fe781da526c2/virtualenv-21.3.1.tar.gz", hash = "sha256:c2305bc1fddeec40699b8370d13f8d431b0701f00ce895061ce493aeded4426b", size = 7613791, upload-time = "2026-05-05T01:34:31.402Z" } +sdist = { url = "https://files.pythonhosted.org/packages/95/f0/b47ecf438211a25a97f8f0e4b23c22bc2496ebfea18dd6ec16210f09cc36/virtualenv-21.4.1.tar.gz", hash = "sha256:2ca543c713b72840ceffd94e9bdedfbd09a661defa1f7f69e5429ad4059442e2", size = 7613344, upload-time = "2026-05-28T04:12:49.905Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/4f/f71e641e504111a5a74e3a20bc52d01bd86788b22699dd3fee1c63253cf6/virtualenv-21.3.1-py3-none-any.whl", hash = "sha256:d1a71cf58f2f9228fff23a1f6ec15d39785c6b32e03658d104974247145edd35", size = 7594539, upload-time = "2026-05-05T01:34:28.98Z" }, + { url = "https://files.pythonhosted.org/packages/ff/dc/ac4f3a987a87e1a18556896f257c4e15c95ed157b7975347ec6b313b75ce/virtualenv-21.4.1-py3-none-any.whl", hash = "sha256:caf4ff72d1b4039057f41d8e8466e859513d67c0400d9c6b62c02c9d1ebc3e12", size = 7594078, upload-time = "2026-05-28T04:12:47.686Z" }, ] [[package]] @@ -8050,89 +8000,95 @@ wheels = [ [[package]] name = "watchfiles" -version = "1.1.1" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/f8/2c5f479fb531ce2f0564eda479faecf253d886b1ab3630a39b7bf7362d46/watchfiles-1.1.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f57b396167a2565a4e8b5e56a5a1c537571733992b226f4f1197d79e94cf0ae5", size = 406529, upload-time = "2025-10-14T15:04:32.899Z" }, - { url = "https://files.pythonhosted.org/packages/fe/cd/f515660b1f32f65df671ddf6f85bfaca621aee177712874dc30a97397977/watchfiles-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:421e29339983e1bebc281fab40d812742268ad057db4aee8c4d2bce0af43b741", size = 394384, upload-time = "2025-10-14T15:04:33.761Z" }, - { url = "https://files.pythonhosted.org/packages/7b/c3/28b7dc99733eab43fca2d10f55c86e03bd6ab11ca31b802abac26b23d161/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e43d39a741e972bab5d8100b5cdacf69db64e34eb19b6e9af162bccf63c5cc6", size = 448789, upload-time = "2025-10-14T15:04:34.679Z" }, - { url = "https://files.pythonhosted.org/packages/4a/24/33e71113b320030011c8e4316ccca04194bf0cbbaeee207f00cbc7d6b9f5/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f537afb3276d12814082a2e9b242bdcf416c2e8fd9f799a737990a1dbe906e5b", size = 460521, upload-time = "2025-10-14T15:04:35.963Z" }, - { url = "https://files.pythonhosted.org/packages/f4/c3/3c9a55f255aa57b91579ae9e98c88704955fa9dac3e5614fb378291155df/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2cd9e04277e756a2e2d2543d65d1e2166d6fd4c9b183f8808634fda23f17b14", size = 488722, upload-time = "2025-10-14T15:04:37.091Z" }, - { url = "https://files.pythonhosted.org/packages/49/36/506447b73eb46c120169dc1717fe2eff07c234bb3232a7200b5f5bd816e9/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3f58818dc0b07f7d9aa7fe9eb1037aecb9700e63e1f6acfed13e9fef648f5d", size = 596088, upload-time = "2025-10-14T15:04:38.39Z" }, - { url = "https://files.pythonhosted.org/packages/82/ab/5f39e752a9838ec4d52e9b87c1e80f1ee3ccdbe92e183c15b6577ab9de16/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb9f66367023ae783551042d31b1d7fd422e8289eedd91f26754a66f44d5cff", size = 472923, upload-time = "2025-10-14T15:04:39.666Z" }, - { url = "https://files.pythonhosted.org/packages/af/b9/a419292f05e302dea372fa7e6fda5178a92998411f8581b9830d28fb9edb/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606", size = 456080, upload-time = "2025-10-14T15:04:40.643Z" }, - { url = "https://files.pythonhosted.org/packages/b0/c3/d5932fd62bde1a30c36e10c409dc5d54506726f08cb3e1d8d0ba5e2bc8db/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5fac835b4ab3c6487b5dbad78c4b3724e26bcc468e886f8ba8cc4306f68f6701", size = 629432, upload-time = "2025-10-14T15:04:41.789Z" }, - { url = "https://files.pythonhosted.org/packages/f7/77/16bddd9779fafb795f1a94319dc965209c5641db5bf1edbbccace6d1b3c0/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:399600947b170270e80134ac854e21b3ccdefa11a9529a3decc1327088180f10", size = 623046, upload-time = "2025-10-14T15:04:42.718Z" }, - { url = "https://files.pythonhosted.org/packages/46/ef/f2ecb9a0f342b4bfad13a2787155c6ee7ce792140eac63a34676a2feeef2/watchfiles-1.1.1-cp311-cp311-win32.whl", hash = "sha256:de6da501c883f58ad50db3a32ad397b09ad29865b5f26f64c24d3e3281685849", size = 271473, upload-time = "2025-10-14T15:04:43.624Z" }, - { url = "https://files.pythonhosted.org/packages/94/bc/f42d71125f19731ea435c3948cad148d31a64fccde3867e5ba4edee901f9/watchfiles-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:35c53bd62a0b885bf653ebf6b700d1bf05debb78ad9292cf2a942b23513dc4c4", size = 287598, upload-time = "2025-10-14T15:04:44.516Z" }, - { url = "https://files.pythonhosted.org/packages/57/c9/a30f897351f95bbbfb6abcadafbaca711ce1162f4db95fc908c98a9165f3/watchfiles-1.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:57ca5281a8b5e27593cb7d82c2ac927ad88a96ed406aa446f6344e4328208e9e", size = 277210, upload-time = "2025-10-14T15:04:45.883Z" }, - { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" }, - { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" }, - { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" }, - { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" }, - { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" }, - { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" }, - { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" }, - { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" }, - { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" }, - { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" }, - { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, - { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, - { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, - { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, - { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, - { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, - { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, - { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, - { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, - { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, - { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, - { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, - { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, - { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, - { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, - { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, - { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, - { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, - { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, - { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, - { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, - { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, - { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, - { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, - { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, - { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, - { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, - { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, - { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, - { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, - { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, - { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, - { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, - { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, - { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, - { url = "https://files.pythonhosted.org/packages/d3/8e/e500f8b0b77be4ff753ac94dc06b33d8f0d839377fee1b78e8c8d8f031bf/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db476ab59b6765134de1d4fe96a1a9c96ddf091683599be0f26147ea1b2e4b88", size = 408250, upload-time = "2025-10-14T15:06:10.264Z" }, - { url = "https://files.pythonhosted.org/packages/bd/95/615e72cd27b85b61eec764a5ca51bd94d40b5adea5ff47567d9ebc4d275a/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89eef07eee5e9d1fda06e38822ad167a044153457e6fd997f8a858ab7564a336", size = 396117, upload-time = "2025-10-14T15:06:11.28Z" }, - { url = "https://files.pythonhosted.org/packages/c9/81/e7fe958ce8a7fb5c73cc9fb07f5aeaf755e6aa72498c57d760af760c91f8/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce19e06cbda693e9e7686358af9cd6f5d61312ab8b00488bc36f5aabbaf77e24", size = 450493, upload-time = "2025-10-14T15:06:12.321Z" }, - { url = "https://files.pythonhosted.org/packages/6e/d4/ed38dd3b1767193de971e694aa544356e63353c33a85d948166b5ff58b9e/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49", size = 457546, upload-time = "2025-10-14T15:06:13.372Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/cd/41/5e1a4bb12aac5f1493fa1bdc11154eca3b258ca4eba65d39c473fe19d8e9/watchfiles-1.2.0.tar.gz", hash = "sha256:c995fba777f1ea992f090f9236e9284cf7a5d1a0130dd5a3d82c598cacd76838", size = 108252, upload-time = "2026-05-18T04:32:04.251Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/3d/8024c801df84d1587740d0359e7fdd80afeae3d159011f3d5376dd82f18e/watchfiles-1.2.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:704fd259e332e01f9b9c178f4bce9e49027e5587cc2600eeeaf8e76e1c846201", size = 400242, upload-time = "2026-05-18T04:31:19.014Z" }, + { url = "https://files.pythonhosted.org/packages/87/5b/f4dfd45323e949984a3a7f9dc31d1cbb049921e7d98253488dda72ccdaa9/watchfiles-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6543cf55d170003296d185c0af981f3e1311564907e1f4e08671fc7693a890a5", size = 394562, upload-time = "2026-05-18T04:30:08.46Z" }, + { url = "https://files.pythonhosted.org/packages/98/d8/19483ef075d601c409bce8bcbb5c0f81a10876fff870400568f08ce484a1/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89d8c2394a065ca86f5d2910ff263ae67c127e1376ccc4f9fc35c71db879f80a", size = 456611, upload-time = "2026-05-18T04:30:45.723Z" }, + { url = "https://files.pythonhosted.org/packages/b1/6a/cc81fbe7ee42f2f22e661a6e12def7807e01b14b2f39e0ff83fd373fd307/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:772b80df316480d894a0e3165fdd19cf77f5d17f9a787f94029465ad0e3529d1", size = 461379, upload-time = "2026-05-18T04:31:29.292Z" }, + { url = "https://files.pythonhosted.org/packages/b1/57/7e669002082c0a0f4fb5113bb70125f7110124b846b0a11bc5ae8e90eac1/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d158cd89df6053823533e06fb1d73c549133bff5f0396170c0e53d9559340717", size = 493556, upload-time = "2026-05-18T04:30:05.44Z" }, + { url = "https://files.pythonhosted.org/packages/45/7d/f60a2b19807b21fe8281f3a8da4f59eef0d5f96825ac4680ba2d4f2ebf91/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d516b3283a758e087841aedb8031549fb41ced08f3db10aa6d2bf32dc042525b", size = 575255, upload-time = "2026-05-18T04:30:40.568Z" }, + { url = "https://files.pythonhosted.org/packages/bd/49/77f5b5e6efbcd57482f74948ebb1b97e5c0046d6b61475042d830c84b3ff/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:53b2290c92e0506d102cd448fbc610d87079553f86caa39d67440856a8b8bba5", size = 467052, upload-time = "2026-05-18T04:31:17.942Z" }, + { url = "https://files.pythonhosted.org/packages/ee/5a/73e2959af1b97fd5d556f9a8bdba017be23ceeef731869d5eaa0a753d5a3/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a711b51aec4370d0dcda5b6c09463206f133a5759341d7744b953a7b62e1100e", size = 456858, upload-time = "2026-05-18T04:30:30.182Z" }, + { url = "https://files.pythonhosted.org/packages/50/57/1bc8c27fad7e6c19bddee15d276dbb6ab72480ec01c127afff1673aee417/watchfiles-1.2.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:e2ca07fa7d89195ec0865d3d285666286740bfa83d83e5cee204043a31ecc165", size = 467579, upload-time = "2026-05-18T04:32:15.897Z" }, + { url = "https://files.pythonhosted.org/packages/09/6c/3c2e44edba3553c5e3c3b8c8a2a6dee6b9e12ae2cf4bd2378bebf9dc3038/watchfiles-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e0618518f282c4ebff60f5e5b1247b6d91bb8b9f4476947563a1e74acc66f3c6", size = 633253, upload-time = "2026-05-18T04:31:37.123Z" }, + { url = "https://files.pythonhosted.org/packages/30/c2/d8c84a882ab39bbefcc4915ab3e91830b7a7e990c5570b0b69075aba3faf/watchfiles-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0d191c054d0715c3c95c99df9b8dbf6fd096d8c1e021e8f212e1bd8bc444ccb5", size = 660713, upload-time = "2026-05-18T04:31:24.62Z" }, + { url = "https://files.pythonhosted.org/packages/a9/07/f97736a5fc605364fe67b25e9fa4a6965dfd4840d50c406ada507e9d735f/watchfiles-1.2.0-cp311-cp311-win32.whl", hash = "sha256:9342472aff9b093c5acd4f6d8f70ae0937964ab56542502bcf5579782da69ae8", size = 277222, upload-time = "2026-05-18T04:31:21.131Z" }, + { url = "https://files.pythonhosted.org/packages/cf/99/2b04981977fc2608afd60360d928c6aecf6b950292ca221d98f4005f6694/watchfiles-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:dbd6c97045dad81227c8d040173da044c1de08de64a5ea8b555da4aee1d5fa22", size = 290274, upload-time = "2026-05-18T04:31:45.966Z" }, + { url = "https://files.pythonhosted.org/packages/3c/74/f7f58a7075ee9cf612b0cfcddb78b8cd8234f0742d6f0075cf0da2dde1c6/watchfiles-1.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:57a2d9fa4fb4c2ecae57b13dfff2c7ab53e21a2ba674fe9f05506680fcdcc0d7", size = 283460, upload-time = "2026-05-18T04:31:39.126Z" }, + { url = "https://files.pythonhosted.org/packages/b8/2f/e42c992d2afda3108ea1c02acecc991b9f31d05c14adc2a7cee9ee211fc4/watchfiles-1.2.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:bc13eb17538be00c874699dc0abe4ee2bc8d50bb1166a6b9e175ef3fd7eb8f26", size = 400115, upload-time = "2026-05-18T04:32:02.06Z" }, + { url = "https://files.pythonhosted.org/packages/5f/8f/6af2ea19065c91d8b0ea3516fdfc8c0d349f407e8e9fbf4e5a17360de8ad/watchfiles-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d95ddc1eb6914154253d239089900813f6a767e174b8e6a50e7fdacb7e4236c", size = 393659, upload-time = "2026-05-18T04:30:50.951Z" }, + { url = "https://files.pythonhosted.org/packages/13/01/b32a967c56fb3e3e5be3db52c3d3b87fa4513aa367d8ed1ad96d42952e5f/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f70d8b291ef6e88d19b1f297a6905ddb978888d9272b0d05e6f53309856bcfc", size = 453207, upload-time = "2026-05-18T04:31:04.231Z" }, + { url = "https://files.pythonhosted.org/packages/04/98/97557a812180338cb1abd32e1cffcc4588f59b5f23e0cb006b2ba95ba64a/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56d8641cf834c2836922899105bd3ce3d0dfc69291d52edf0b4d0436829b34c0", size = 459273, upload-time = "2026-05-18T04:31:50.377Z" }, + { url = "https://files.pythonhosted.org/packages/e8/a8/b4b08dcb7653b8087c6586f7ce649505900e866bbcfe40dc9587af02e686/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2581a94056e55d7d0a31a823ea92bf73749c489ca2285bfdc0fbe6b2bb49d50c", size = 489927, upload-time = "2026-05-18T04:31:42.485Z" }, + { url = "https://files.pythonhosted.org/packages/50/94/3dceea03545d2e5ddfd839f0ddd5e1cecbf1697b5a428d5ba11cef6af95d/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41bc1199f7523b3f82843c88cbb979180c949caef0342cf90968f178e5d49b01", size = 570476, upload-time = "2026-05-18T04:31:03.071Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f2/d39a5450c3532092b91f81d274360e613c2371bc874a89c7a1a3c5e8d138/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7571e4464cb6e434958f867f7f730b8ab0b75e3f8e5eac0499168486ab3c33a8", size = 465650, upload-time = "2026-05-18T04:30:12.701Z" }, + { url = "https://files.pythonhosted.org/packages/22/24/ed72f68cbc1333ca9b9f2200aa048bb6658ae41709bc1caad4310f4bdffd/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e53a384f76b631c3ae5334ce6a52f0baa3a911eb94a4eac7f160079868b716d5", size = 456398, upload-time = "2026-05-18T04:30:13.784Z" }, + { url = "https://files.pythonhosted.org/packages/0d/64/982ef4a4e5bab5b6e5b6becc8cd5e732f6130a78b855f0abec6439a9a135/watchfiles-1.2.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:d20029a60a71a052a24c4db7673bc4de39ab89adbaccbfb5d67987c5d73f424d", size = 465140, upload-time = "2026-05-18T04:31:52.111Z" }, + { url = "https://files.pythonhosted.org/packages/a0/0c/95282abf4ed680b6096010bcfc30c5fa7a041fc5aa5a2ad17a2cc6c75bba/watchfiles-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2cb93af48550faf1cea04c303107c8b75833de7013e57ce27d3b8d21d8d0f58c", size = 630259, upload-time = "2026-05-18T04:31:25.676Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/607c1de1530c4bdcf2cf1d1ecc2505ddba5d96bd43ba9f2b0e79876f850f/watchfiles-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2995c176de7692b86a2e4c58d9ec718f753150a979cb4a754e2b4ffa38e70906", size = 659859, upload-time = "2026-05-18T04:30:24.333Z" }, + { url = "https://files.pythonhosted.org/packages/fa/08/d9e2e0f9e8e6791d33aefc694ad7eefa7f901f63caff84a81ded38692f9c/watchfiles-1.2.0-cp312-cp312-win32.whl", hash = "sha256:7a2cffd17d27d2ecbb310c2b1d8174f222a5495b1a721894afa88ec11e25b898", size = 275480, upload-time = "2026-05-18T04:30:31.307Z" }, + { url = "https://files.pythonhosted.org/packages/1c/e6/9d42569c0102645cc8cea5d8c7d8a1e9d4ada2cb7f05f75e554b8aa2202a/watchfiles-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:f155b3a1b2a5fc89cdc70d47ee5d54e3b75e88efa34982028a35daef9ba00379", size = 288718, upload-time = "2026-05-18T04:32:10.745Z" }, + { url = "https://files.pythonhosted.org/packages/0a/26/88e0dc6ee3898169d7fa22bb6a69cabf2502d2ee25cb8c876d1262d204f8/watchfiles-1.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:8fa585ede612ee9f9e91b18bebf9ba11b9ae29a4e3a0d0cf6fca3e382133f0d5", size = 281026, upload-time = "2026-05-18T04:30:22.23Z" }, + { url = "https://files.pythonhosted.org/packages/d1/4d/70a7feced9f87e2ff26dba42667290f41694fc64646c67261fbb8cab5d5c/watchfiles-1.2.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:01ea8d66f0693b9b60a6541c8d10263091ca9a9060d242f3c1f3143f9aad2c98", size = 399730, upload-time = "2026-05-18T04:31:38.162Z" }, + { url = "https://files.pythonhosted.org/packages/31/3a/0da302f2307aee316922806ebd5726c542cbd787c938271cf14a074c7daf/watchfiles-1.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ba0480b9a74af058f43b337e937a451e109295c420916d68ad24e3dc02f5e44", size = 392842, upload-time = "2026-05-18T04:30:27.051Z" }, + { url = "https://files.pythonhosted.org/packages/db/ef/d5bdb705c224dbc256aa0c1ec47bf4e61ec52558f2afb44a71a1fe4d7015/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f34e26a19f91f710c08e0183429f0d1d15df734e6bc78c31e77b9ea9c433658", size = 452989, upload-time = "2026-05-18T04:31:11.945Z" }, + { url = "https://files.pythonhosted.org/packages/71/29/5495f2c1661949ef7a35e4d71111d129cfe7606414a26887a919d0a55406/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4e77f6a55f858504069abd35d336a637555c09bca453dde1ee1e5ada8a6a1fb", size = 458978, upload-time = "2026-05-18T04:30:52.606Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8c/7f9c07c433811c2fffd93e13fdfb7135de9aab5f2ae41be08960fa0047dc/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0cb4d80e212f116474a545c21c912b445f16bb0cef9e6a73a498164223e14e2f", size = 490248, upload-time = "2026-05-18T04:31:36.003Z" }, + { url = "https://files.pythonhosted.org/packages/3c/11/d93632febc52fbc21be90231bb7c17fd5387f46c9076fd40a5f9c2ae6910/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b974946a10af379d425e2eef5b62f5c6ebeaccf91d45eaad6f5b27ecd4f91aa0", size = 571847, upload-time = "2026-05-18T04:31:10.862Z" }, + { url = "https://files.pythonhosted.org/packages/55/b4/383173e73aabb07ad1d9c7aa859d95437ac46a6d6a1e11005facda0c9d19/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86bc13c25a8d1fcd70b51d0ce7c9b65e90de5666fcbfd3e34957cc73ee19aeb5", size = 465974, upload-time = "2026-05-18T04:30:17.006Z" }, + { url = "https://files.pythonhosted.org/packages/a7/6c/89b1a230a78f57c52dd8893adb1f92f94411721b6ec12596c56d98c74356/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca148d73dea36c9763aaa351e4d7a51780ec1584217c45276f4fe8239c768b71", size = 454782, upload-time = "2026-05-18T04:30:35.656Z" }, + { url = "https://files.pythonhosted.org/packages/24/62/1732118367cfff0a9fce3bf62ff4bfded09ef5df21d9d446b858b3f70a96/watchfiles-1.2.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:c525543d91961c6955b2636b308569e84a1d1c5f5f2932041ab9ef46422f43e3", size = 465182, upload-time = "2026-05-18T04:30:20.846Z" }, + { url = "https://files.pythonhosted.org/packages/28/96/716f7e5f51339bf22963f3345f9f27d7f3b30e2eadc597e257c881dd3c53/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a204794696ffb8f9b10fba6f7cb5216d42f3b2b71860ccac6b6e42f5f10973b0", size = 629841, upload-time = "2026-05-18T04:31:05.397Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fe/c40783950fd771ccf66ab3ec2722d188a9af1c7f96c6e811f36e40c6e03f/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:10d86db20695afe7997ac9e1717637d6714a8d0220458c33f3d2061f54cec427", size = 658028, upload-time = "2026-05-18T04:31:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/71/72/4508db1856d1d87fcbb3b63f4839bab1b5682cb0e8d224d122263c09654a/watchfiles-1.2.0-cp313-cp313-win32.whl", hash = "sha256:eb283ee99e21ad6443c8cdb06ac5b34b1308c329cbdf03fa02b445363714c799", size = 275183, upload-time = "2026-05-18T04:30:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/f9/36/14b76ca57652e5cc5fd1c11f32a261292c08a0d19a00351013c2549cbfb2/watchfiles-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a0f27f01bee51861392bb6b7c4fdb290b27d1eb194e9e28788d68102a0e898d9", size = 288059, upload-time = "2026-05-18T04:32:07.937Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8d/0a85e395398d8d20fadfe5c5d32c726eee17a519e78fb356f2cf7531bffe/watchfiles-1.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:3651aa7058595e9cfb75d35dd5ada2bf9f48a5b8a0f3562821d3e210c507e077", size = 280186, upload-time = "2026-05-18T04:31:54.484Z" }, + { url = "https://files.pythonhosted.org/packages/37/68/36db056f1fdcc5f07302f56e631774d6835bcd6fa3ace402304621d5f9e5/watchfiles-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:faea288b6f0ab1902ef08f4ca6de005dccf856c4e0c4f21b8c5fce02d90a1b08", size = 399031, upload-time = "2026-05-18T04:30:44.576Z" }, + { url = "https://files.pythonhosted.org/packages/c1/64/01a9d6f66a82a5c101ce939274106cc72759d62427e153f01edd2b9f87c2/watchfiles-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01859b11fd9fbca670f4d5da00fbac282cfea9bd67a2125d8b2833a3b5617ea9", size = 391205, upload-time = "2026-05-18T04:30:25.413Z" }, + { url = "https://files.pythonhosted.org/packages/84/2c/0a44fe058cb4bb7b8ede6b6670698bbb7c0400740e378d00022189b7b31d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fff610d7bb2256a317bb1e96f0d7862c7aa8076733ee5df0fd41bbe76a24a4f4", size = 451892, upload-time = "2026-05-18T04:32:14.005Z" }, + { url = "https://files.pythonhosted.org/packages/67/a1/351e0d56cd35e6488b5c8b4fb11a809a5bc923e8fe8fed9faf8920be0c89/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b141a4891c995a039cd89e9a49e62df1dc8a559a5d1a6e4c7106d16c12777a55", size = 458867, upload-time = "2026-05-18T04:31:22.279Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7d/9d09605187f1b838998624049fcf8bf47b73c1a3b76901fcac1782f62277/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22943b7770483f6ea0721c6b11d022947a98eb0acae14694de034f4d0d38925", size = 490217, upload-time = "2026-05-18T04:31:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/60/5d/a17a16eccb182f04188cd308ec24b1a71a9b5c4e7098269cf35d9fa56d02/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bc6195825b7dcd217968bb1f801a60fd4c16e8eeab5bedc7fe917d7d5995ab4", size = 571458, upload-time = "2026-05-18T04:32:11.875Z" }, + { url = "https://files.pythonhosted.org/packages/d3/3d/4dd457062083ab1938e5dfd45032eb425cee2ac817287ca8ff4356183e5d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4a4b147f5dca2a5d325a06a832fb43f345751adfbc63204aec30e0d9ca965a2", size = 464707, upload-time = "2026-05-18T04:30:43.492Z" }, + { url = "https://files.pythonhosted.org/packages/c6/71/ea8c57b128f5383de74d0c7d2d9c57ad7c9a65a930c451bd25d524b295b7/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4543579a9bdb0c9560039b4ffddbdb39545707659fbc430ce4c10f3f68d557f9", size = 454663, upload-time = "2026-05-18T04:30:16.061Z" }, + { url = "https://files.pythonhosted.org/packages/53/fd/2e812bf938406d7db351f0703ddd3fc6c061cf30d96153a77bc79a943a44/watchfiles-1.2.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:20aa0e708b920bde876a4aa82dc7dd6ebea228a63a67cda6632c2fc87b787efa", size = 463537, upload-time = "2026-05-18T04:31:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/86/56/d17a7f1dd1bc3035f1072694a551301272f1739c2d8e319c927cb9e29b38/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:d413349d565dab74297f2a63e84a097936be69bf8f3b3801f27f380e32040f44", size = 629194, upload-time = "2026-05-18T04:31:14.141Z" }, + { url = "https://files.pythonhosted.org/packages/be/06/f1ff66bf5cae50aa4062779a0ecd0bbaf15e466195719074078947d9a17d/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f28b2725eb8cce327b9b3ab02415c853011dc55c95832fe90de6bc56f5315f72", size = 656194, upload-time = "2026-05-18T04:31:47.14Z" }, + { url = "https://files.pythonhosted.org/packages/e7/54/a9c7ea9a82a4ac65e7004c0a03920b5cdd2f9c3b678757d9cd425aa51d53/watchfiles-1.2.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:b8c8358484d5fa12ef34f05b7f4168eaf1932f408725ff6d023c33ec17bd79d4", size = 400205, upload-time = "2026-05-18T04:32:05.153Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5d/c9ab3534374a4a67450696905d6ef16a04405448b8dc52bd752ae50423d4/watchfiles-1.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f04b092229ad2c50126dd3c922c8822e51e605993764a33058d4a791ab42281", size = 392508, upload-time = "2026-05-18T04:30:54.849Z" }, + { url = "https://files.pythonhosted.org/packages/26/ca/1ad30103535cf0cecd7b993e8d50edc5351b1820e38f2d22e3df58962feb/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a7ce236284f002a156f70add88efe5c70879cccbb658be0822c54b1306fc09d", size = 452448, upload-time = "2026-05-18T04:30:53.727Z" }, + { url = "https://files.pythonhosted.org/packages/37/a1/ceee2cdf2afbd715fa07758d39c9859513eae411b23196f7fd039e5feedd/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b9909cc2b48468b575eefa944919e1fe8a36c5849d5c7c168f80a8c1db69398e", size = 459605, upload-time = "2026-05-18T04:30:23.312Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f6/421e30fd1cb3907a84ed92ab3f1983e37ba2dca015e9a894a048418417a2/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a37faaed405c67e28e6be45a1fa4f206ef5a2860f27c237db9fa30704c38242", size = 490757, upload-time = "2026-05-18T04:30:47.358Z" }, + { url = "https://files.pythonhosted.org/packages/41/b0/55ed1b97ed08be7bba6f9a541cac15f2a858e1d74d2b07b6da70a82aab00/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9649193aa27bd9ff2e80ff29bfaa93085496c7a3a377592823cc58b77ee88add", size = 568672, upload-time = "2026-05-18T04:30:38.915Z" }, + { url = "https://files.pythonhosted.org/packages/d1/cf/d8ae8a80dd7bafab395ea7681c10237311bbf34d37704a8c744e7cf31fc7/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e4ff8e37f99cf1da89e255e07c9c4b37c214038c4283707bdec308cb1b0ea1f", size = 464197, upload-time = "2026-05-18T04:30:09.914Z" }, + { url = "https://files.pythonhosted.org/packages/7c/8a/3076c496ca8dafe0e8cd03fcebdfc47be4b1174b4e5b24ff6e396e6b3af2/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:054dc20fd2e3132b4c3883b4a00d72fd6e1f56fdaf89fccd12e8057d74cd74d7", size = 453181, upload-time = "2026-05-18T04:30:14.829Z" }, + { url = "https://files.pythonhosted.org/packages/e5/10/9745e17c98e7b8a86454df0a3c7b5686bd650383f1e9f26e4ebcbd6cc0c0/watchfiles-1.2.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e140ed30ebde76796b686e67c182cff10ea2fbab186fafd1560f74bb5a473a6e", size = 465109, upload-time = "2026-05-18T04:30:28.123Z" }, + { url = "https://files.pythonhosted.org/packages/8f/95/8ef4a95481d3e0cb52d62a06fa6e972e81424be2d9698b91a2fecca9904c/watchfiles-1.2.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:bb7e52ecf68ba46d22df23467b87cffeb2146908aa523ebfe803019618cfda06", size = 630653, upload-time = "2026-05-18T04:31:49.304Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e4/3b3bf36b0f829b50c6ebcb8d031583863c59f923d6a6af3d485e470d0fac/watchfiles-1.2.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:23282a321c8baf9b3a3c4afff673f9fe65eb7fdc2338d765ccad9d3d1916a5ba", size = 657838, upload-time = "2026-05-18T04:31:06.497Z" }, + { url = "https://files.pythonhosted.org/packages/21/b1/6cbbb50c1f3002ab568777d44aa21206dfb8807a840990c4037523b51812/watchfiles-1.2.0-cp314-cp314-win32.whl", hash = "sha256:c0db965c5f79aa49fe672d297cf1febc5ad149b658594944f49a54a2b96270a7", size = 275108, upload-time = "2026-05-18T04:30:06.891Z" }, + { url = "https://files.pythonhosted.org/packages/92/45/190ce6db8dcb4536682cf75d3889ff1a27182a58cb519d343cb6d9ea63d8/watchfiles-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:71283b39fd17e5408eb123bd37aeecfd9d54c81fc184421943208aadb879d103", size = 288441, upload-time = "2026-05-18T04:32:12.901Z" }, + { url = "https://files.pythonhosted.org/packages/74/0d/3eae1c2313ab08378431d907c3f8095ecca00f3eda33111cf4f0f2591799/watchfiles-1.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:c5c19526f4e54a00f2666a6c0e9e40d582c09e865055ea7378bf0009aab857b3", size = 280684, upload-time = "2026-05-18T04:31:26.902Z" }, + { url = "https://files.pythonhosted.org/packages/b1/75/fb64e6c25d6b5ca636d03df34ffb1c6e9873303e76d27967e045f8df088f/watchfiles-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d73a585accffa5ae39c17264c36ec3166d2fad7000c780f5ef83b2722afb9dd2", size = 398857, upload-time = "2026-05-18T04:32:17.108Z" }, + { url = "https://files.pythonhosted.org/packages/73/4e/9f7adf01754cbf81843722ccfec169d8f26c69778281a302855cecd2ee08/watchfiles-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae99b14c5f21e026e0e9d96f40e07d8570ebee6cafd9d8fc318354606daa7a28", size = 392413, upload-time = "2026-05-18T04:31:07.911Z" }, + { url = "https://files.pythonhosted.org/packages/47/c8/bec626bcc2d69f44b9acb24ce7d60ed7b16b73628eea747fcbd169d8edda/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4429f3b105524a10b72c3a819b091c495d2811d419c1e1e8df773a5a5974f831", size = 452409, upload-time = "2026-05-18T04:31:20.142Z" }, + { url = "https://files.pythonhosted.org/packages/00/b7/b6362068e81e7c556d155a34c35d40ac3ef42d747b06d7f6e5bf58e359c2/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43d818978d06062d9b22c4fab2ebe44cf5213d42dc8e62bda8c2760cfa2eeb33", size = 458827, upload-time = "2026-05-18T04:32:06.219Z" }, + { url = "https://files.pythonhosted.org/packages/67/f8/9a813fa42afb1e0b4625e75f0479826644d3ee8dc287e093799bc01f390c/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9f732dc58b2dbe69e464ccf8fff7a03b0dd0be439da4c0720d3558527d3d6b4", size = 490104, upload-time = "2026-05-18T04:31:56.034Z" }, + { url = "https://files.pythonhosted.org/packages/2f/bf/27dfb6094ca4c9aad21298b5525b6c53cb36121ee454331d05161e58d130/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f200104103feb097de4cab8fe4f5dd18a2026934c7dea98c55a2f5fd6d5a33b", size = 571360, upload-time = "2026-05-18T04:31:57.133Z" }, + { url = "https://files.pythonhosted.org/packages/fb/39/44a096d67270ea93df91d33877dbe91fbda3aa4f8ec2edf799d93eda8736/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63ac26eefbf4af1741247d6fb68b11c49a25b2f7413fbd318a83a12aaa9cf666", size = 464644, upload-time = "2026-05-18T04:30:57.33Z" }, + { url = "https://files.pythonhosted.org/packages/0e/80/c7472203bad6268e3ef1ad260739704847898938ad7ea8b63a5131f46b50/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c4997d4e4a55f0d02b6cde327322daf3a0400e5df6c6b15948994bf72497925", size = 454771, upload-time = "2026-05-18T04:30:48.736Z" }, + { url = "https://files.pythonhosted.org/packages/51/cf/3b10b268b4b7f0fc26e9debb5eef1998b515887840f444cd3ec80c688755/watchfiles-1.2.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:4c887eba18b7945ac73067a8b4a66f21cd46c2539b2bc68588f7be6c7eb6d26b", size = 463494, upload-time = "2026-05-18T04:31:33.826Z" }, + { url = "https://files.pythonhosted.org/packages/3d/3e/a4302545cd589262a0dc7d140e86f7688eba3f9c72776c27f7e23b8864c4/watchfiles-1.2.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:3416ff151bb6b5a8d8d11664974fbef4d9305b9b2957839ab5a270468fd8df30", size = 629383, upload-time = "2026-05-18T04:31:15.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/99/d5649df0a9a410d45b7c882304d0b790903ac9b6e8f2cfd12114e0c6b9f2/watchfiles-1.2.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:0e831a271c035d89789cffc386b6aa1375f39f1cd25eb7ca0997e4970d152fc5", size = 656093, upload-time = "2026-05-18T04:31:58.707Z" }, + { url = "https://files.pythonhosted.org/packages/23/f4/7513ef1e85fc4c6331b59479d6d72661fc391fbe543678052ac72c8b6c19/watchfiles-1.2.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4674d49eb94706dfe666c069fc0a1b646ffcf920473492e209f6d5f60d3f0cc2", size = 403050, upload-time = "2026-05-18T04:30:36.753Z" }, + { url = "https://files.pythonhosted.org/packages/27/0b/a54103cfd732bb703c7a749222011a0483ef3705948dae3b203158601119/watchfiles-1.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:094b9b70103d4e963499bdea001ee3c2697b144cd9ae6218a62c0f89ec9e31db", size = 396629, upload-time = "2026-05-18T04:32:03.268Z" }, + { url = "https://files.pythonhosted.org/packages/5e/2c/73f31a3b893886206c3f54d73e8ad8dee58cdb2f69ad2622e0a8a9e07f4e/watchfiles-1.2.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0ef001f8c25ad0fa9529f914c1600647ecd0f542d11c19b7894768c67b6acb7", size = 457318, upload-time = "2026-05-18T04:31:01.932Z" }, + { url = "https://files.pythonhosted.org/packages/e9/f9/45d021e4a5cc7b9dd567f7cbb06d3b75f751a690063fb6cc7ec60f4e46b7/watchfiles-1.2.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a88fc94e647bc4eec523f1caa540258eb71d14278b9daf72fa1e2658a98df0f0", size = 457771, upload-time = "2026-05-18T04:30:56.331Z" }, ] [[package]] @@ -8262,84 +8218,85 @@ wheels = [ [[package]] name = "wrapt" -version = "2.1.2" +version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/64/925f213fdcbb9baeb1530449ac71a4d57fc361c053d06bf78d0c5c7cd80c/wrapt-2.1.2.tar.gz", hash = "sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e", size = 81678, upload-time = "2026-03-06T02:53:25.134Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/81/60c4471fce95afa5922ca09b88a25f03c93343f759aae0f31fb4412a85c7/wrapt-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb", size = 60666, upload-time = "2026-03-06T02:52:58.934Z" }, - { url = "https://files.pythonhosted.org/packages/6b/be/80e80e39e7cb90b006a0eaf11c73ac3a62bbfb3068469aec15cc0bc795de/wrapt-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d", size = 61601, upload-time = "2026-03-06T02:53:00.487Z" }, - { url = "https://files.pythonhosted.org/packages/b0/be/d7c88cd9293c859fc74b232abdc65a229bb953997995d6912fc85af18323/wrapt-2.1.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894", size = 114057, upload-time = "2026-03-06T02:52:44.08Z" }, - { url = "https://files.pythonhosted.org/packages/ea/25/36c04602831a4d685d45a93b3abea61eca7fe35dab6c842d6f5d570ef94a/wrapt-2.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842", size = 116099, upload-time = "2026-03-06T02:54:56.74Z" }, - { url = "https://files.pythonhosted.org/packages/5c/4e/98a6eb417ef551dc277bec1253d5246b25003cf36fdf3913b65cb7657a56/wrapt-2.1.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8", size = 112457, upload-time = "2026-03-06T02:53:52.842Z" }, - { url = "https://files.pythonhosted.org/packages/cb/a6/a6f7186a5297cad8ec53fd7578533b28f795fdf5372368c74bd7e6e9841c/wrapt-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6", size = 115351, upload-time = "2026-03-06T02:53:32.684Z" }, - { url = "https://files.pythonhosted.org/packages/97/6f/06e66189e721dbebd5cf20e138acc4d1150288ce118462f2fcbff92d38db/wrapt-2.1.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9", size = 111748, upload-time = "2026-03-06T02:53:08.455Z" }, - { url = "https://files.pythonhosted.org/packages/ef/43/4808b86f499a51370fbdbdfa6cb91e9b9169e762716456471b619fca7a70/wrapt-2.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15", size = 113783, upload-time = "2026-03-06T02:53:02.02Z" }, - { url = "https://files.pythonhosted.org/packages/91/2c/a3f28b8fa7ac2cefa01cfcaca3471f9b0460608d012b693998cd61ef43df/wrapt-2.1.2-cp311-cp311-win32.whl", hash = "sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b", size = 57977, upload-time = "2026-03-06T02:53:27.844Z" }, - { url = "https://files.pythonhosted.org/packages/3f/c3/2b1c7bd07a27b1db885a2fab469b707bdd35bddf30a113b4917a7e2139d2/wrapt-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1", size = 60336, upload-time = "2026-03-06T02:54:28.104Z" }, - { url = "https://files.pythonhosted.org/packages/ec/5c/76ece7b401b088daa6503d6264dd80f9a727df3e6042802de9a223084ea2/wrapt-2.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a", size = 58756, upload-time = "2026-03-06T02:53:16.319Z" }, - { url = "https://files.pythonhosted.org/packages/4c/b6/1db817582c49c7fcbb7df6809d0f515af29d7c2fbf57eb44c36e98fb1492/wrapt-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9", size = 61255, upload-time = "2026-03-06T02:52:45.663Z" }, - { url = "https://files.pythonhosted.org/packages/a2/16/9b02a6b99c09227c93cd4b73acc3678114154ec38da53043c0ddc1fba0dc/wrapt-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748", size = 61848, upload-time = "2026-03-06T02:53:48.728Z" }, - { url = "https://files.pythonhosted.org/packages/af/aa/ead46a88f9ec3a432a4832dfedb84092fc35af2d0ba40cd04aea3889f247/wrapt-2.1.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e", size = 121433, upload-time = "2026-03-06T02:54:40.328Z" }, - { url = "https://files.pythonhosted.org/packages/3a/9f/742c7c7cdf58b59085a1ee4b6c37b013f66ac33673a7ef4aaed5e992bc33/wrapt-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8", size = 123013, upload-time = "2026-03-06T02:53:26.58Z" }, - { url = "https://files.pythonhosted.org/packages/e8/44/2c3dd45d53236b7ed7c646fcf212251dc19e48e599debd3926b52310fafb/wrapt-2.1.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c", size = 117326, upload-time = "2026-03-06T02:53:11.547Z" }, - { url = "https://files.pythonhosted.org/packages/74/e2/b17d66abc26bd96f89dec0ecd0ef03da4a1286e6ff793839ec431b9fae57/wrapt-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c", size = 121444, upload-time = "2026-03-06T02:54:09.5Z" }, - { url = "https://files.pythonhosted.org/packages/3c/62/e2977843fdf9f03daf1586a0ff49060b1b2fc7ff85a7ea82b6217c1ae36e/wrapt-2.1.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1", size = 116237, upload-time = "2026-03-06T02:54:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/88/dd/27fc67914e68d740bce512f11734aec08696e6b17641fef8867c00c949fc/wrapt-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2", size = 120563, upload-time = "2026-03-06T02:53:20.412Z" }, - { url = "https://files.pythonhosted.org/packages/ec/9f/b750b3692ed2ef4705cb305bd68858e73010492b80e43d2a4faa5573cbe7/wrapt-2.1.2-cp312-cp312-win32.whl", hash = "sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0", size = 58198, upload-time = "2026-03-06T02:53:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/8e/b2/feecfe29f28483d888d76a48f03c4c4d8afea944dbee2b0cd3380f9df032/wrapt-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63", size = 60441, upload-time = "2026-03-06T02:52:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/44/e1/e328f605d6e208547ea9fd120804fcdec68536ac748987a68c47c606eea8/wrapt-2.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf", size = 58836, upload-time = "2026-03-06T02:53:22.053Z" }, - { url = "https://files.pythonhosted.org/packages/4c/7a/d936840735c828b38d26a854e85d5338894cda544cb7a85a9d5b8b9c4df7/wrapt-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b", size = 61259, upload-time = "2026-03-06T02:53:41.922Z" }, - { url = "https://files.pythonhosted.org/packages/5e/88/9a9b9a90ac8ca11c2fdb6a286cb3a1fc7dd774c00ed70929a6434f6bc634/wrapt-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e", size = 61851, upload-time = "2026-03-06T02:52:48.672Z" }, - { url = "https://files.pythonhosted.org/packages/03/a9/5b7d6a16fd6533fed2756900fc8fc923f678179aea62ada6d65c92718c00/wrapt-2.1.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb", size = 121446, upload-time = "2026-03-06T02:54:14.013Z" }, - { url = "https://files.pythonhosted.org/packages/45/bb/34c443690c847835cfe9f892be78c533d4f32366ad2888972c094a897e39/wrapt-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca", size = 123056, upload-time = "2026-03-06T02:54:10.829Z" }, - { url = "https://files.pythonhosted.org/packages/93/b9/ff205f391cb708f67f41ea148545f2b53ff543a7ac293b30d178af4d2271/wrapt-2.1.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267", size = 117359, upload-time = "2026-03-06T02:53:03.623Z" }, - { url = "https://files.pythonhosted.org/packages/1f/3d/1ea04d7747825119c3c9a5e0874a40b33594ada92e5649347c457d982805/wrapt-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f", size = 121479, upload-time = "2026-03-06T02:53:45.844Z" }, - { url = "https://files.pythonhosted.org/packages/78/cc/ee3a011920c7a023b25e8df26f306b2484a531ab84ca5c96260a73de76c0/wrapt-2.1.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8", size = 116271, upload-time = "2026-03-06T02:54:46.356Z" }, - { url = "https://files.pythonhosted.org/packages/98/fd/e5ff7ded41b76d802cf1191288473e850d24ba2e39a6ec540f21ae3b57cb/wrapt-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413", size = 120573, upload-time = "2026-03-06T02:52:50.163Z" }, - { url = "https://files.pythonhosted.org/packages/47/c5/242cae3b5b080cd09bacef0591691ba1879739050cc7c801ff35c8886b66/wrapt-2.1.2-cp313-cp313-win32.whl", hash = "sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6", size = 58205, upload-time = "2026-03-06T02:53:47.494Z" }, - { url = "https://files.pythonhosted.org/packages/12/69/c358c61e7a50f290958809b3c61ebe8b3838ea3e070d7aac9814f95a0528/wrapt-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1", size = 60452, upload-time = "2026-03-06T02:53:30.038Z" }, - { url = "https://files.pythonhosted.org/packages/8e/66/c8a6fcfe321295fd8c0ab1bd685b5a01462a9b3aa2f597254462fc2bc975/wrapt-2.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf", size = 58842, upload-time = "2026-03-06T02:52:52.114Z" }, - { url = "https://files.pythonhosted.org/packages/da/55/9c7052c349106e0b3f17ae8db4b23a691a963c334de7f9dbd60f8f74a831/wrapt-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b", size = 63075, upload-time = "2026-03-06T02:53:19.108Z" }, - { url = "https://files.pythonhosted.org/packages/09/a8/ce7b4006f7218248dd71b7b2b732d0710845a0e49213b18faef64811ffef/wrapt-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18", size = 63719, upload-time = "2026-03-06T02:54:33.452Z" }, - { url = "https://files.pythonhosted.org/packages/e4/e5/2ca472e80b9e2b7a17f106bb8f9df1db11e62101652ce210f66935c6af67/wrapt-2.1.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d", size = 152643, upload-time = "2026-03-06T02:52:42.721Z" }, - { url = "https://files.pythonhosted.org/packages/36/42/30f0f2cefca9d9cbf6835f544d825064570203c3e70aa873d8ae12e23791/wrapt-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015", size = 158805, upload-time = "2026-03-06T02:54:25.441Z" }, - { url = "https://files.pythonhosted.org/packages/bb/67/d08672f801f604889dcf58f1a0b424fe3808860ede9e03affc1876b295af/wrapt-2.1.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92", size = 145990, upload-time = "2026-03-06T02:53:57.456Z" }, - { url = "https://files.pythonhosted.org/packages/68/a7/fd371b02e73babec1de6ade596e8cd9691051058cfdadbfd62a5898f3295/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf", size = 155670, upload-time = "2026-03-06T02:54:55.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/2d/9fe0095dfdb621009f40117dcebf41d7396c2c22dca6eac779f4c007b86c/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67", size = 144357, upload-time = "2026-03-06T02:54:24.092Z" }, - { url = "https://files.pythonhosted.org/packages/0e/b6/ec7b4a254abbe4cde9fa15c5d2cca4518f6b07d0f1b77d4ee9655e30280e/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a", size = 150269, upload-time = "2026-03-06T02:53:31.268Z" }, - { url = "https://files.pythonhosted.org/packages/6e/6b/2fabe8ebf148f4ee3c782aae86a795cc68ffe7d432ef550f234025ce0cfa/wrapt-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd", size = 59894, upload-time = "2026-03-06T02:54:15.391Z" }, - { url = "https://files.pythonhosted.org/packages/ca/fb/9ba66fc2dedc936de5f8073c0217b5d4484e966d87723415cc8262c5d9c2/wrapt-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f", size = 63197, upload-time = "2026-03-06T02:54:41.943Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1c/012d7423c95d0e337117723eb8ecf73c622ce15a97847e84cf3f8f26cd7e/wrapt-2.1.2-cp313-cp313t-win_arm64.whl", hash = "sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679", size = 60363, upload-time = "2026-03-06T02:54:48.093Z" }, - { url = "https://files.pythonhosted.org/packages/39/25/e7ea0b417db02bb796182a5316398a75792cd9a22528783d868755e1f669/wrapt-2.1.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9", size = 61418, upload-time = "2026-03-06T02:53:55.706Z" }, - { url = "https://files.pythonhosted.org/packages/ec/0f/fa539e2f6a770249907757eaeb9a5ff4deb41c026f8466c1c6d799088a9b/wrapt-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9", size = 61914, upload-time = "2026-03-06T02:52:53.37Z" }, - { url = "https://files.pythonhosted.org/packages/53/37/02af1867f5b1441aaeda9c82deed061b7cd1372572ddcd717f6df90b5e93/wrapt-2.1.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e", size = 120417, upload-time = "2026-03-06T02:54:30.74Z" }, - { url = "https://files.pythonhosted.org/packages/c3/b7/0138a6238c8ba7476c77cf786a807f871672b37f37a422970342308276e7/wrapt-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c", size = 122797, upload-time = "2026-03-06T02:54:51.539Z" }, - { url = "https://files.pythonhosted.org/packages/e1/ad/819ae558036d6a15b7ed290d5b14e209ca795dd4da9c58e50c067d5927b0/wrapt-2.1.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a", size = 117350, upload-time = "2026-03-06T02:54:37.651Z" }, - { url = "https://files.pythonhosted.org/packages/8b/2d/afc18dc57a4600a6e594f77a9ae09db54f55ba455440a54886694a84c71b/wrapt-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90", size = 121223, upload-time = "2026-03-06T02:54:35.221Z" }, - { url = "https://files.pythonhosted.org/packages/b9/5b/5ec189b22205697bc56eb3b62aed87a1e0423e9c8285d0781c7a83170d15/wrapt-2.1.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586", size = 116287, upload-time = "2026-03-06T02:54:19.654Z" }, - { url = "https://files.pythonhosted.org/packages/f7/2d/f84939a7c9b5e6cdd8a8d0f6a26cabf36a0f7e468b967720e8b0cd2bdf69/wrapt-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19", size = 119593, upload-time = "2026-03-06T02:54:16.697Z" }, - { url = "https://files.pythonhosted.org/packages/0b/fe/ccd22a1263159c4ac811ab9374c061bcb4a702773f6e06e38de5f81a1bdc/wrapt-2.1.2-cp314-cp314-win32.whl", hash = "sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508", size = 58631, upload-time = "2026-03-06T02:53:06.498Z" }, - { url = "https://files.pythonhosted.org/packages/65/0a/6bd83be7bff2e7efaac7b4ac9748da9d75a34634bbbbc8ad077d527146df/wrapt-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04", size = 60875, upload-time = "2026-03-06T02:53:50.252Z" }, - { url = "https://files.pythonhosted.org/packages/6c/c0/0b3056397fe02ff80e5a5d72d627c11eb885d1ca78e71b1a5c1e8c7d45de/wrapt-2.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575", size = 59164, upload-time = "2026-03-06T02:53:59.128Z" }, - { url = "https://files.pythonhosted.org/packages/71/ed/5d89c798741993b2371396eb9d4634f009ff1ad8a6c78d366fe2883ea7a6/wrapt-2.1.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb", size = 63163, upload-time = "2026-03-06T02:52:54.873Z" }, - { url = "https://files.pythonhosted.org/packages/c6/8c/05d277d182bf36b0a13d6bd393ed1dec3468a25b59d01fba2dd70fe4d6ae/wrapt-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22", size = 63723, upload-time = "2026-03-06T02:52:56.374Z" }, - { url = "https://files.pythonhosted.org/packages/f4/27/6c51ec1eff4413c57e72d6106bb8dec6f0c7cdba6503d78f0fa98767bcc9/wrapt-2.1.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596", size = 152652, upload-time = "2026-03-06T02:53:23.79Z" }, - { url = "https://files.pythonhosted.org/packages/db/4c/d7dd662d6963fc7335bfe29d512b02b71cdfa23eeca7ab3ac74a67505deb/wrapt-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044", size = 158807, upload-time = "2026-03-06T02:53:35.742Z" }, - { url = "https://files.pythonhosted.org/packages/b4/4d/1e5eea1a78d539d346765727422976676615814029522c76b87a95f6bcdd/wrapt-2.1.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b", size = 146061, upload-time = "2026-03-06T02:52:57.574Z" }, - { url = "https://files.pythonhosted.org/packages/89/bc/62cabea7695cd12a288023251eeefdcb8465056ddaab6227cb78a2de005b/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf", size = 155667, upload-time = "2026-03-06T02:53:39.422Z" }, - { url = "https://files.pythonhosted.org/packages/e9/99/6f2888cd68588f24df3a76572c69c2de28287acb9e1972bf0c83ce97dbc1/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2", size = 144392, upload-time = "2026-03-06T02:54:22.41Z" }, - { url = "https://files.pythonhosted.org/packages/40/51/1dfc783a6c57971614c48e361a82ca3b6da9055879952587bc99fe1a7171/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3", size = 150296, upload-time = "2026-03-06T02:54:07.848Z" }, - { url = "https://files.pythonhosted.org/packages/6c/38/cbb8b933a0201076c1f64fc42883b0023002bdc14a4964219154e6ff3350/wrapt-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7", size = 60539, upload-time = "2026-03-06T02:54:00.594Z" }, - { url = "https://files.pythonhosted.org/packages/82/dd/e5176e4b241c9f528402cebb238a36785a628179d7d8b71091154b3e4c9e/wrapt-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5", size = 63969, upload-time = "2026-03-06T02:54:39Z" }, - { url = "https://files.pythonhosted.org/packages/5c/99/79f17046cf67e4a95b9987ea129632ba8bcec0bc81f3fb3d19bdb0bd60cd/wrapt-2.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00", size = 60554, upload-time = "2026-03-06T02:53:14.132Z" }, - { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/2d/9f/06263fcd8ad6c405f05a3905fd7a84dd3176eb5ad46e44bccc0cd16348bb/wrapt-2.2.1.tar.gz", hash = "sha256:6744f504375775d7609c82c8d3d94af1c9a6f05586984536905908ba905277b9", size = 127620, upload-time = "2026-05-22T14:49:43.056Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/ac/4370bde262c0e633e6c4f0e56d55095710024cf9a5cecc20c59a10de483c/wrapt-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd57607acc85678925940bd5df0385ff8332083a32fa8d7a43f8767f4997263c", size = 80321, upload-time = "2026-05-22T14:47:43.996Z" }, + { url = "https://files.pythonhosted.org/packages/eb/79/b8ff3a61e71babf58a8cf4c0d63358e8bad383e15bf7f35e62d2f6b6e4a4/wrapt-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ae574d65c9fa8e86f64f6a7c2668f9fcd507b183e0e577619f504b883cb0a6c", size = 81216, upload-time = "2026-05-22T14:47:45.243Z" }, + { url = "https://files.pythonhosted.org/packages/6e/fd/c0cac1f77c9c4f6fe58a920ca632ce379bb8be928720e11e8d73de28a5e9/wrapt-2.2.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9a04c28c10ba7fd12842b109d2edb0678872a2fe65277ca4ff06a0d61edee245", size = 159208, upload-time = "2026-05-22T14:47:47.176Z" }, + { url = "https://files.pythonhosted.org/packages/d9/4f/744132a7b2fbefa6b81118ec5942eca5fc2e9a129f9055a0c5e46885a549/wrapt-2.2.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3e2f02472a1cbbf3884b365714a810b5947134a95ad6952b554cb8cce9d492b0", size = 160322, upload-time = "2026-05-22T14:47:49.04Z" }, + { url = "https://files.pythonhosted.org/packages/d6/95/b7cd9a22a06cf93e6482904ee6afc956248983553593fd1009296d1b3b31/wrapt-2.2.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac2745950b2bff80219c15ebf2fa9d8427eba7e249739f97e55c9d169e47e9e1", size = 153243, upload-time = "2026-05-22T14:47:50.386Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4a/eb79423192015f46f0db2872e7e04a3dde8d359b83411e8959e7c9287eaa/wrapt-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:67a97e5b6c457f0cd3cfc19ebb2d84463e60c3ece754cc831e4281a3ca29bb18", size = 159231, upload-time = "2026-05-22T14:47:51.753Z" }, + { url = "https://files.pythonhosted.org/packages/ec/dc/435015b58ce33c6fc4104158fa91ddb0e809ab03a5751fb7465d1d461456/wrapt-2.2.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:c803a3d331796255af51ba2c79ed0ac8275865b516c09e61f248d1e7aff31ce9", size = 152351, upload-time = "2026-05-22T14:47:53.214Z" }, + { url = "https://files.pythonhosted.org/packages/77/ac/5d203f98df8fd136b95c5227139aea02d34505e18baf812d0c005df61963/wrapt-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9b984d1eb252145d6302c1dbd5e87fc6d404d45531447c84eadec04bf1fcb027", size = 158347, upload-time = "2026-05-22T14:47:54.982Z" }, + { url = "https://files.pythonhosted.org/packages/52/2f/a92427dbdc74e54c1674abbed27e61b2cb5e7a94441b8c1270c70671d928/wrapt-2.2.1-cp311-cp311-win32.whl", hash = "sha256:8a983a603a18c8708f024f7f6991b2e66159219abbf894634c5056243c55f3cd", size = 77562, upload-time = "2026-05-22T14:47:56.275Z" }, + { url = "https://files.pythonhosted.org/packages/c8/56/987b9c13b3e1c1a3c6de71284076f996b79caec90e75a87c044a40c23db9/wrapt-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:9c210a6994b21aa9b29e81c8d11560e8fdab54c117e9cff37870d0a27bde1343", size = 80616, upload-time = "2026-05-22T14:47:57.854Z" }, + { url = "https://files.pythonhosted.org/packages/7e/25/d01f560888d99d94a959c85533de349ce68d71ace3f2591d6ea8f632cfed/wrapt-2.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:401229e9d63ca09f9b8891ecf83798d26c11bbb445d11ed9f1836b6d4585b38a", size = 79025, upload-time = "2026-05-22T14:47:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/89/0c/bfae7b9401583b6d05938cd16dedc43857d96da2f8a3d50d78cc515bf6ff/wrapt-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ffad790d9d11d8ecf9f17c4bb671a5b4089e4d8b575c46c5129597f41f836b0", size = 81021, upload-time = "2026-05-22T14:48:00.313Z" }, + { url = "https://files.pythonhosted.org/packages/26/58/80f6a6599f933f4caecc1cb3ee88a04faf81e8b9bddbd6109c688dd63e0f/wrapt-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:628f5220c7a904d5fc78f7075c8d7871433eb6d035c94728a22fdf85f193d2a8", size = 81692, upload-time = "2026-05-22T14:48:01.49Z" }, + { url = "https://files.pythonhosted.org/packages/17/93/fb357cc7847c58a8ae790be718903afa81a28d23e642c843dc4129e8a0b2/wrapt-2.2.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:61acce4257a9883669703c525447c5b4c392edf0f987ae77ec32668440158f0e", size = 169364, upload-time = "2026-05-22T14:48:02.791Z" }, + { url = "https://files.pythonhosted.org/packages/aa/0b/76b601ee309a8bd556af0eecb184394c20b3c49aa9c8e085aa1ffacc2568/wrapt-2.2.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727ab4244622cd6ad2390f322642090c877d2e83a608d2653a7643ae5368d926", size = 171079, upload-time = "2026-05-22T14:48:04.22Z" }, + { url = "https://files.pythonhosted.org/packages/cd/87/ee3f32d5658e3e26d3e0e457922b47a36dd3bfbdfee7f97bb3e802344a66/wrapt-2.2.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03df9ebed4c73ab93fa8c07e3d41d818dfca1852b15731a3de59457b27814624", size = 160205, upload-time = "2026-05-22T14:48:05.553Z" }, + { url = "https://files.pythonhosted.org/packages/b1/d0/ae2fd64277a67f5d7bffcf2d05eea1e476263fb2a072baf0b0129ab85984/wrapt-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0d9ff006f420b2ec8296aa56ade43ea7da3e997e85769f0aafc5e0661aacb710", size = 168922, upload-time = "2026-05-22T14:48:07.132Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f3/2d541a060c5bbafb9400bca4917e4d78bfd1f239f404782c86831a8f6b29/wrapt-2.2.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:844c858fc3bb7eacc0ba8efa904935d16aac6a4470948ad1e7e55c9f5a2a665f", size = 158388, upload-time = "2026-05-22T14:48:08.629Z" }, + { url = "https://files.pythonhosted.org/packages/1d/68/8d92c8800c57e93cb116ae9e9d6cbafc34fade5ee9f9107b6f203fb4dc35/wrapt-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87bacdaf225117a342a20d9c03438d701c02112f6e3f351ce9b7f32354f14797", size = 167682, upload-time = "2026-05-22T14:48:10.042Z" }, + { url = "https://files.pythonhosted.org/packages/30/72/83ea3790ea352439442349388e29ff07b76e0686265f9088bbb505d1608d/wrapt-2.2.1-cp312-cp312-win32.whl", hash = "sha256:2f8c90c8afde51969487be4e1343ae049b268854877d415c2510baf833775052", size = 77857, upload-time = "2026-05-22T14:48:11.782Z" }, + { url = "https://files.pythonhosted.org/packages/ef/cb/99450668dd3502d62a54a1c8aa56e44f34cb8c1261b381cfe2e7926c3b75/wrapt-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ce32763ac31ce94fe9aada947e479b1975012bff166da409b4b9e4e376cf7e5", size = 80825, upload-time = "2026-05-22T14:48:13.046Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3a/87512881be64e743f9ee4c66f4cbe8e884974bef2a5989af71f999653ac7/wrapt-2.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d1b4d0e0c2119587a31f5c029abd547e0c81d93b89d394566fe1588659eb579", size = 79087, upload-time = "2026-05-22T14:48:14.323Z" }, + { url = "https://files.pythonhosted.org/packages/88/d1/a1b08f8f4fac8cbb156fa51cf64ee2c7f7f74f9875ba3cf70b3c58368694/wrapt-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d2beb1c7cab10603aecdc42f8edd6ff013f9a32e4543474e38e6b77ce9975aeb", size = 80831, upload-time = "2026-05-22T14:48:15.598Z" }, + { url = "https://files.pythonhosted.org/packages/54/ce/57890814991446a845e09b3445ce8b694f27eb0577004f2c2a36a9772ed4/wrapt-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0cb7e4dd71f4c32e5e84843cd3c4cd65dda034314004bbe1d7f99af2426ab80", size = 81375, upload-time = "2026-05-22T14:48:17.071Z" }, + { url = "https://files.pythonhosted.org/packages/38/65/08d7a6c76ac4493bdb668205ee9c1de1bd5daca61717c3e9aa49b4c01499/wrapt-2.2.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95821352042722cd9f1108874579a47989d0a7e12a37d87d2fc4af20fd99ab8a", size = 167417, upload-time = "2026-05-22T14:48:18.303Z" }, + { url = "https://files.pythonhosted.org/packages/62/ce/f1ccbee7a1bfe5cdc6b3da6bab4b45713d628b9294da32a39f563d648140/wrapt-2.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:abd621552ede77c4c69be7fac44ba911225b0c812b6ba604e5964cf98085b474", size = 166948, upload-time = "2026-05-22T14:48:19.768Z" }, + { url = "https://files.pythonhosted.org/packages/86/2a/f85d48d1cd4869aee6704028d257d740a47c1c467b457ce396b4b5b55d07/wrapt-2.2.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e3677c7146ce694874941ba82b57092cc4875445aadf29d72807351023105143", size = 158148, upload-time = "2026-05-22T14:48:21.96Z" }, + { url = "https://files.pythonhosted.org/packages/fe/5c/93939ad11d4a12358ab1aab219a2ef5efa5612e0db6b9fc65af8af1a891b/wrapt-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9a5934eaea872e17936b5f45501eba5ab0bce9a74122e172b663d7c28c459c4a", size = 165905, upload-time = "2026-05-22T14:48:23.373Z" }, + { url = "https://files.pythonhosted.org/packages/e0/22/b8c2aa89862ff58605934d7abf4b70e6a5a1c33df96656f49035ccdf1c8a/wrapt-2.2.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f5b9daf6b629fce418e0cc3dd0436eac045188fa35deadb7a7f3941d5b8203f9", size = 156712, upload-time = "2026-05-22T14:48:24.767Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/bf00a7b02239c12bb02ddcc3c0b971bfcc36e578c5a44f1ccfef5b458545/wrapt-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f53ac9f3ef573326d009ed809beff4efcac6451931c2b8132586da4b9e53ff31", size = 166560, upload-time = "2026-05-22T14:48:26.83Z" }, + { url = "https://files.pythonhosted.org/packages/fe/93/6390ca9c5b787683cef588d04f57c8d41b9a2323b5597a65f18638c90ef2/wrapt-2.2.1-cp313-cp313-win32.whl", hash = "sha256:1ffa9cfd4bdb581539951b14ae661ff20ed0c3599b3e911a131ee0ec5ac11337", size = 77817, upload-time = "2026-05-22T14:48:28.221Z" }, + { url = "https://files.pythonhosted.org/packages/97/73/ce10f0e71c0cfaa1a65faadb8efd4852028b3bb9ba28932b8889df769d38/wrapt-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:368eac1e20fd0bb03dd3cc42bf9887154c3861b60989389ccb5fac032617d215", size = 80736, upload-time = "2026-05-22T14:48:30.139Z" }, + { url = "https://files.pythonhosted.org/packages/c7/4c/89f4a6818fafbbd840330e4fa3873073e1bfc166133a64cac7f8fde7a5e3/wrapt-2.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:c754dafdf5aaf0b401b644a90a30046929a0dd1a536e0ff0ec959a59155d9c7f", size = 79099, upload-time = "2026-05-22T14:48:31.405Z" }, + { url = "https://files.pythonhosted.org/packages/bf/f2/9a8741c46f8c208ac0a45b25ba170bcb4fb72a2781d5fb97dbd7b6be73cb/wrapt-2.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ed928d0fda15fc0adc8d13305c8b3c0f2fba5b0669950c9e6d019d9162a3b3e8", size = 82802, upload-time = "2026-05-22T14:48:33.307Z" }, + { url = "https://files.pythonhosted.org/packages/9c/0d/e9c855716a3705eef1416456bdf062b60620726fdc59428ff670fc3c60dc/wrapt-2.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fafb4e739e43544d12cb4abd1605fd4683b6ca6a9ad682b7fd8f4d21973eafa8", size = 83329, upload-time = "2026-05-22T14:48:34.593Z" }, + { url = "https://files.pythonhosted.org/packages/3b/d6/a88f1c13112b7831adac75cea65d8310e0d696d570c8961844c90a57b865/wrapt-2.2.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:74d6a0c31472fe5d814917266b9f46495d7c61ed890af08b468acea92fb89a8d", size = 202937, upload-time = "2026-05-22T14:48:35.859Z" }, + { url = "https://files.pythonhosted.org/packages/42/65/e29d54aef06a4d898a5b8a25589a0b3769bde454f922fad8f6f89fbfb650/wrapt-2.2.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab5be648d5a0b86b7438864f8df3c705a65cef35a2fd3e5561e3e203167e0f27", size = 209997, upload-time = "2026-05-22T14:48:38.153Z" }, + { url = "https://files.pythonhosted.org/packages/2a/91/e4454263516cf0e12640912fbca9a83654e424f0a6ddb79f5cd7ce14bf33/wrapt-2.2.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9d8f204c8e3a8bf9ece17e0a83d137fd807440977f8a5e762d59306795011440", size = 194856, upload-time = "2026-05-22T14:48:39.69Z" }, + { url = "https://files.pythonhosted.org/packages/de/d0/fe0ee202286afdf4a7f77dd29f195703145764d572aec209c5086e57d924/wrapt-2.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d047f6498c973874ba08ac3f97c69a2c4b2211c8de6f4c205f75cb1c9522596e", size = 205654, upload-time = "2026-05-22T14:48:43.456Z" }, + { url = "https://files.pythonhosted.org/packages/23/b6/87d860dfc6460c246af70b1fd5c8b76df77571b42a493459423ded94fd7d/wrapt-2.2.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:7a4fdb9326aab4a5a477a1640e5ad786a8495901009d7e7b038371edd23a9d2b", size = 192206, upload-time = "2026-05-22T14:48:44.858Z" }, + { url = "https://files.pythonhosted.org/packages/df/46/3eea8cde077d985f239a38c0257087b8064fd9ee9b1a99e282d2c86da4ef/wrapt-2.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c8cc5094b08abeae52da9c73c8a32003623be691a5193df2f4e3eac3d557c394", size = 198428, upload-time = "2026-05-22T14:48:46.319Z" }, + { url = "https://files.pythonhosted.org/packages/18/dc/b927ee9c7fc67adc3a5658f246a0d275425eb840ba36e7b702e70f18bde8/wrapt-2.2.1-cp313-cp313t-win32.whl", hash = "sha256:9907a4402ab6db12b7077a0ea5d7a4d028ecb22c8eee2b53527080d347cd1562", size = 79448, upload-time = "2026-05-22T14:48:47.901Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b3/fd30b473fe498c70e6b9a5f328b8d3fbaf1b8c3c481465f59724bba8eb70/wrapt-2.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:5590d63f5243251641cf543009b4c9314a79d0598fdb8a8e4cfc918494536c53", size = 83021, upload-time = "2026-05-22T14:48:49.201Z" }, + { url = "https://files.pythonhosted.org/packages/ee/f3/96c39153a8737a6e9aa85adef254ac4195bea3f2d24efc60472ccc3c9e2e/wrapt-2.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:c318a64b53d97b841d7b5e637517e50a27be64bc695128422953d4b21710954e", size = 80295, upload-time = "2026-05-22T14:48:50.479Z" }, + { url = "https://files.pythonhosted.org/packages/0a/a3/11d7f34ebbf3231bc907a3e6d5ee051b14d034c1bc7b65a97d5cc00516df/wrapt-2.2.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6f56a647e4eaf5f0ca40330fb070f566bdf9f7b0db89a1af20d71c28dcd7a0ab", size = 80879, upload-time = "2026-05-22T14:48:51.802Z" }, + { url = "https://files.pythonhosted.org/packages/13/3c/b74cfd984cef560b900fb1a727af20352d89e1f06bf2e1114dd3f00f5f5a/wrapt-2.2.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:64b7deeda4b70408e382328d8bbe52a256fe9bc63ae3db86d804608367e5422c", size = 81462, upload-time = "2026-05-22T14:48:53.18Z" }, + { url = "https://files.pythonhosted.org/packages/15/a3/7c8f704b8dc07dfe0a5d01c2edbfd88317aa8e5e3fa7c743eb7a085ae767/wrapt-2.2.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b9cf53ba90717db2e292401de290776c498d4bbfb0d4a559ca2895db8b9dcb5c", size = 167251, upload-time = "2026-05-22T14:48:54.562Z" }, + { url = "https://files.pythonhosted.org/packages/80/85/a34d1888d97247da6c2ff6118c3a721c73ed8cc4dd198c00208bb73b6f80/wrapt-2.2.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf3638274ab9d9b724c9baa0b4c04e132cd6faefb78b4dd3dd1a02a4bdaad41e", size = 166316, upload-time = "2026-05-22T14:48:56.065Z" }, + { url = "https://files.pythonhosted.org/packages/e9/d7/72ffaeb01eebc704afe3fb99e840480f4bda45f0fa66e3381b6a39251c8f/wrapt-2.2.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aed9658797d0b45d6c49adcfc6b41f66e6f2d0c6de3ec79e16cf4b1855df240f", size = 157952, upload-time = "2026-05-22T14:48:57.924Z" }, + { url = "https://files.pythonhosted.org/packages/24/5b/36f5d6b024e4edfdd90b140742d11ebcf7836daf5c9daf326c55c24db412/wrapt-2.2.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1d676ee388bc42a04d56dd7deb5605244dac2e35cc2fadbb43c9fa25bbd93508", size = 166130, upload-time = "2026-05-22T14:48:59.384Z" }, + { url = "https://files.pythonhosted.org/packages/81/06/9296d9e97bfdef5483dfcc859d57b095b257144b2bc5300ab521e06f4bc7/wrapt-2.2.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e395f7bc31851ef9b612050368cb446e9bc14cd7454b025018980349caf25ae5", size = 156604, upload-time = "2026-05-22T14:49:00.921Z" }, + { url = "https://files.pythonhosted.org/packages/53/37/16953929ed6776175720e58fc966e779926d8d71e2c7b2273230590ca71f/wrapt-2.2.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f1845c2a8cc1180ccccfa45785dd06f562730d19ef75be180334254012b6283", size = 166007, upload-time = "2026-05-22T14:49:02.332Z" }, + { url = "https://files.pythonhosted.org/packages/b9/73/20ee58c0612dae7c31131a7095345812ed2c7b389019e175f68cde34e5b4/wrapt-2.2.1-cp314-cp314-win32.whl", hash = "sha256:436addbc4bb4fc0a88c702577f51195d7d73683a7f3e0e5b253d8404d7847243", size = 78327, upload-time = "2026-05-22T14:49:03.722Z" }, + { url = "https://files.pythonhosted.org/packages/22/b3/ef7c3295d02e0448a71c639a36a057f46d524d057c9486291a7a3039e65c/wrapt-2.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:50972a1d974ea07725a7f6b1cec5f8759008afd030a0024843ebe7d52de47f2b", size = 81144, upload-time = "2026-05-22T14:49:05.093Z" }, + { url = "https://files.pythonhosted.org/packages/ac/dc/7bdf336953f99f4ceb0a584bb8870e42c8f26f93ea10c87834dad62f1668/wrapt-2.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:1c9934ea5d92957e3cd0adbc0845539dccfd62710ebe16195a8c66c53954db36", size = 79569, upload-time = "2026-05-22T14:49:06.413Z" }, + { url = "https://files.pythonhosted.org/packages/6a/6d/6dfae80150ff1919c356d1dd528f049bcdfaae29b4d284bc957e022caef4/wrapt-2.2.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17de18fc12cea55b8a9587314cb830573e37fb33b247a7515696350863714188", size = 82892, upload-time = "2026-05-22T14:49:07.925Z" }, + { url = "https://files.pythonhosted.org/packages/82/7b/4e34766a7d7804ffce9e71befe47e9b3225dc350c49c94493c4ab39fd3a5/wrapt-2.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9dec1aca52dddde7df94818310fa2fe79739c8f385b2014c4cb1035f5508199", size = 83333, upload-time = "2026-05-22T14:49:09.257Z" }, + { url = "https://files.pythonhosted.org/packages/9d/57/0b34db3e8de44ccfece62d7b337abd1631dd810f5adc5f3db571727836b5/wrapt-2.2.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:69f2e9244542cb34dd59c7f073445b9e54ad9f3fce8d93606c368a1b499fc413", size = 202899, upload-time = "2026-05-22T14:49:10.572Z" }, + { url = "https://files.pythonhosted.org/packages/e5/45/ac0c459f154b99d92789a6cba7ca727185b83513b986f8ec7fe2aacddcbf/wrapt-2.2.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d83966dc7f4f45e8b97b5933685ac2e6e67fc0e19246ea314bceb9a8970c956", size = 209986, upload-time = "2026-05-22T14:49:12.229Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e4/77e37ff33ad018fa81ade52c25fa327b80b56f81d734279a63614fcb4cbc/wrapt-2.2.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:78b0aa6bfb7be8deed0ab23e7aa028cc5210c29bc2d32a04d52b50e517a7307e", size = 194893, upload-time = "2026-05-22T14:49:14.139Z" }, + { url = "https://files.pythonhosted.org/packages/dd/9d/7ea651d1ab032fc5fa222fbec91d0f8a1397f6ae04ebb93fa7219aa921d7/wrapt-2.2.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:05d5cb74d1b232ec8cfa130a8f900708699ff2491d97b8f85a4cdc5996294b85", size = 205636, upload-time = "2026-05-22T14:49:15.714Z" }, + { url = "https://files.pythonhosted.org/packages/09/af/8e88031a701275b9085c54e64bc88c0b1cd55c77eadd400691c371cd76c4/wrapt-2.2.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f6518b94edb9150452e9aba08027d4cc293433753ec1fbefb4629a21cbc74181", size = 192267, upload-time = "2026-05-22T14:49:17.283Z" }, + { url = "https://files.pythonhosted.org/packages/bf/a8/e657ca876b06710194f243d81c4b0896ade646e244bdbec2d87c8c56a8bd/wrapt-2.2.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ed55af48b3eb28f43228ca2306788892bcb629eb2b5c4876e2a3659872c2f17a", size = 198378, upload-time = "2026-05-22T14:49:18.785Z" }, + { url = "https://files.pythonhosted.org/packages/c8/59/822efe4ea722a3961331bfa35b7d90937790d2c20f0616de1997ccc3aebd/wrapt-2.2.1-cp314-cp314t-win32.whl", hash = "sha256:2e08688ab16525897da6589d56d0aebaf417bbe91c2d8e3b96203b1efa596e85", size = 80226, upload-time = "2026-05-22T14:49:20.264Z" }, + { url = "https://files.pythonhosted.org/packages/ab/31/2a7dc5f6abb2fca0b6e1610e120419f603650aceb4f1d3ac4cae0354e162/wrapt-2.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:fd0135d34387f5fd087d9be368ea77ea89cf2451dc1cd1c622d35021bcb3ab50", size = 83835, upload-time = "2026-05-22T14:49:21.634Z" }, + { url = "https://files.pythonhosted.org/packages/9f/c0/782b86e28d1ceebeb74cccea12d2cd3d2ba0bd68e3dec20b1bc5873f6127/wrapt-2.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:f70db64e8266d7c45d3b735f2e08eeb434b5e03da9a479ae42b2e2e486a21a00", size = 80722, upload-time = "2026-05-22T14:49:23.59Z" }, + { url = "https://files.pythonhosted.org/packages/53/46/29ac9daf11a86c22a8c38cd9236c62928ccae83f7ceb06bd3b0467cf9d05/wrapt-2.2.1-py3-none-any.whl", hash = "sha256:3aafea2975caef8ca49400640dde02cc7426e798f24870ed01f490bc3cffd32f", size = 61000, upload-time = "2026-05-22T14:49:41.593Z" }, ] [[package]] name = "wsidicom" -version = "0.29.0" +version = "0.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "cachetools" }, { name = "dicomweb-client" }, { name = "fsspec" }, { name = "marshmallow" }, @@ -8348,9 +8305,9 @@ dependencies = [ { name = "pydicom" }, { name = "universal-pathlib" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e8/d3/bcae589b3941ffd4fffc450dd7c2ae4546c9699bb86deedae9eec22e50af/wsidicom-0.29.0.tar.gz", hash = "sha256:851d8f8dc31098646cc06ec5b049a33aabedcaf2aa8b1fdb6eaa9fde5f06c8e1", size = 303235, upload-time = "2025-12-09T12:23:09.509Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/2b/ffcf96e36b51df266de08bacb591fd1d8cce09a2cfab6b92f4537847e770/wsidicom-0.30.0.tar.gz", hash = "sha256:011836eae7faffdae55b24ff657079650d1ee77461831c1b06ae1a2ba3962974", size = 312595, upload-time = "2026-05-22T10:19:04.717Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/bd/a533b05e43952816c1e61f86ed53a392a13b19cda74e8e40d623681ecef8/wsidicom-0.29.0-py3-none-any.whl", hash = "sha256:590e025c03630c2156d81af007b2bc175068efff0f7007008500dfb50c58ee1f", size = 242986, upload-time = "2025-12-09T12:23:07.786Z" }, + { url = "https://files.pythonhosted.org/packages/72/58/a208543ab261e1e3c00734e8fce12b7fbfb6bad53de00364003f55367741/wsidicom-0.30.0-py3-none-any.whl", hash = "sha256:f6154d0bddf2656b55f287235dd75968d2da532297a08b54d44b55d6c57c04e3", size = 243307, upload-time = "2026-05-22T10:19:02.856Z" }, ] [[package]] @@ -8367,131 +8324,108 @@ wheels = [ [[package]] name = "yarl" -version = "1.23.0" +version = "1.24.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "multidict" }, { name = "propcache" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/aa/60da938b8f0997ba3a911263c40d82b6f645a67902a490b46f3355e10fae/yarl-1.23.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b35d13d549077713e4414f927cdc388d62e543987c572baee613bf82f11a4b99", size = 123641, upload-time = "2026-03-01T22:04:42.841Z" }, - { url = "https://files.pythonhosted.org/packages/24/84/e237607faf4e099dbb8a4f511cfd5efcb5f75918baad200ff7380635631b/yarl-1.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbb0fef01f0c6b38cb0f39b1f78fc90b807e0e3c86a7ff3ce74ad77ce5c7880c", size = 86248, upload-time = "2026-03-01T22:04:44.757Z" }, - { url = "https://files.pythonhosted.org/packages/b2/0d/71ceabc14c146ba8ee3804ca7b3d42b1664c8440439de5214d366fec7d3a/yarl-1.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc52310451fc7c629e13c4e061cbe2dd01684d91f2f8ee2821b083c58bd72432", size = 85988, upload-time = "2026-03-01T22:04:46.365Z" }, - { url = "https://files.pythonhosted.org/packages/8c/6c/4a90d59c572e46b270ca132aca66954f1175abd691f74c1ef4c6711828e2/yarl-1.23.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2c6b50c7b0464165472b56b42d4c76a7b864597007d9c085e8b63e185cf4a7a", size = 100566, upload-time = "2026-03-01T22:04:47.639Z" }, - { url = "https://files.pythonhosted.org/packages/49/fb/c438fb5108047e629f6282a371e6e91cf3f97ee087c4fb748a1f32ceef55/yarl-1.23.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aafe5dcfda86c8af00386d7781d4c2181b5011b7be3f2add5e99899ea925df05", size = 92079, upload-time = "2026-03-01T22:04:48.925Z" }, - { url = "https://files.pythonhosted.org/packages/d9/13/d269aa1aed3e4f50a5a103f96327210cc5fa5dd2d50882778f13c7a14606/yarl-1.23.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ee33b875f0b390564c1fb7bc528abf18c8ee6073b201c6ae8524aca778e2d83", size = 108741, upload-time = "2026-03-01T22:04:50.838Z" }, - { url = "https://files.pythonhosted.org/packages/85/fb/115b16f22c37ea4437d323e472945bea97301c8ec6089868fa560abab590/yarl-1.23.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4c41e021bc6d7affb3364dc1e1e5fa9582b470f283748784bd6ea0558f87f42c", size = 108099, upload-time = "2026-03-01T22:04:52.499Z" }, - { url = "https://files.pythonhosted.org/packages/9a/64/c53487d9f4968045b8afa51aed7ca44f58b2589e772f32745f3744476c82/yarl-1.23.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99c8a9ed30f4164bc4c14b37a90208836cbf50d4ce2a57c71d0f52c7fb4f7598", size = 102678, upload-time = "2026-03-01T22:04:55.176Z" }, - { url = "https://files.pythonhosted.org/packages/85/59/cd98e556fbb2bf8fab29c1a722f67ad45c5f3447cac798ab85620d1e70af/yarl-1.23.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2af5c81a1f124609d5f33507082fc3f739959d4719b56877ab1ee7e7b3d602b", size = 100803, upload-time = "2026-03-01T22:04:56.588Z" }, - { url = "https://files.pythonhosted.org/packages/9e/c0/b39770b56d4a9f0bb5f77e2f1763cd2d75cc2f6c0131e3b4c360348fcd65/yarl-1.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6b41389c19b07c760c7e427a3462e8ab83c4bb087d127f0e854c706ce1b9215c", size = 100163, upload-time = "2026-03-01T22:04:58.492Z" }, - { url = "https://files.pythonhosted.org/packages/e7/64/6980f99ab00e1f0ff67cb84766c93d595b067eed07439cfccfc8fb28c1a6/yarl-1.23.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:1dc702e42d0684f42d6519c8d581e49c96cefaaab16691f03566d30658ee8788", size = 93859, upload-time = "2026-03-01T22:05:00.268Z" }, - { url = "https://files.pythonhosted.org/packages/38/69/912e6c5e146793e5d4b5fe39ff5b00f4d22463dfd5a162bec565ac757673/yarl-1.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0e40111274f340d32ebcc0a5668d54d2b552a6cca84c9475859d364b380e3222", size = 108202, upload-time = "2026-03-01T22:05:02.273Z" }, - { url = "https://files.pythonhosted.org/packages/59/97/35ca6767524687ad64e5f5c31ad54bc76d585585a9fcb40f649e7e82ffed/yarl-1.23.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:4764a6a7588561a9aef92f65bda2c4fb58fe7c675c0883862e6df97559de0bfb", size = 99866, upload-time = "2026-03-01T22:05:03.597Z" }, - { url = "https://files.pythonhosted.org/packages/d3/1c/1a3387ee6d73589f6f2a220ae06f2984f6c20b40c734989b0a44f5987308/yarl-1.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:03214408cfa590df47728b84c679ae4ef00be2428e11630277be0727eba2d7cc", size = 107852, upload-time = "2026-03-01T22:05:04.986Z" }, - { url = "https://files.pythonhosted.org/packages/a4/b8/35c0750fcd5a3f781058bfd954515dd4b1eab45e218cbb85cf11132215f1/yarl-1.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:170e26584b060879e29fac213e4228ef063f39128723807a312e5c7fec28eff2", size = 102919, upload-time = "2026-03-01T22:05:06.397Z" }, - { url = "https://files.pythonhosted.org/packages/e5/1c/9a1979aec4a81896d597bcb2177827f2dbee3f5b7cc48b2d0dadb644b41d/yarl-1.23.0-cp311-cp311-win32.whl", hash = "sha256:51430653db848d258336cfa0244427b17d12db63d42603a55f0d4546f50f25b5", size = 82602, upload-time = "2026-03-01T22:05:08.444Z" }, - { url = "https://files.pythonhosted.org/packages/93/22/b85eca6fa2ad9491af48c973e4c8cf6b103a73dbb271fe3346949449fca0/yarl-1.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf49a3ae946a87083ef3a34c8f677ae4243f5b824bfc4c69672e72b3d6719d46", size = 87461, upload-time = "2026-03-01T22:05:10.145Z" }, - { url = "https://files.pythonhosted.org/packages/93/95/07e3553fe6f113e6864a20bdc53a78113cda3b9ced8784ee52a52c9f80d8/yarl-1.23.0-cp311-cp311-win_arm64.whl", hash = "sha256:b39cb32a6582750b6cc77bfb3c49c0f8760dc18dc96ec9fb55fbb0f04e08b928", size = 82336, upload-time = "2026-03-01T22:05:11.554Z" }, - { url = "https://files.pythonhosted.org/packages/88/8a/94615bc31022f711add374097ad4144d569e95ff3c38d39215d07ac153a0/yarl-1.23.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860", size = 124737, upload-time = "2026-03-01T22:05:12.897Z" }, - { url = "https://files.pythonhosted.org/packages/e3/6f/c6554045d59d64052698add01226bc867b52fe4a12373415d7991fdca95d/yarl-1.23.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069", size = 87029, upload-time = "2026-03-01T22:05:14.376Z" }, - { url = "https://files.pythonhosted.org/packages/19/2a/725ecc166d53438bc88f76822ed4b1e3b10756e790bafd7b523fe97c322d/yarl-1.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25", size = 86310, upload-time = "2026-03-01T22:05:15.71Z" }, - { url = "https://files.pythonhosted.org/packages/99/30/58260ed98e6ff7f90ba84442c1ddd758c9170d70327394a6227b310cd60f/yarl-1.23.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8", size = 97587, upload-time = "2026-03-01T22:05:17.384Z" }, - { url = "https://files.pythonhosted.org/packages/76/0a/8b08aac08b50682e65759f7f8dde98ae8168f72487e7357a5d684c581ef9/yarl-1.23.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072", size = 92528, upload-time = "2026-03-01T22:05:18.804Z" }, - { url = "https://files.pythonhosted.org/packages/52/07/0b7179101fe5f8385ec6c6bb5d0cb9f76bd9fb4a769591ab6fb5cdbfc69a/yarl-1.23.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8", size = 105339, upload-time = "2026-03-01T22:05:20.235Z" }, - { url = "https://files.pythonhosted.org/packages/d3/8a/36d82869ab5ec829ca8574dfcb92b51286fcfb1e9c7a73659616362dc880/yarl-1.23.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7", size = 105061, upload-time = "2026-03-01T22:05:22.268Z" }, - { url = "https://files.pythonhosted.org/packages/66/3e/868e5c3364b6cee19ff3e1a122194fa4ce51def02c61023970442162859e/yarl-1.23.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51", size = 100132, upload-time = "2026-03-01T22:05:23.638Z" }, - { url = "https://files.pythonhosted.org/packages/cf/26/9c89acf82f08a52cb52d6d39454f8d18af15f9d386a23795389d1d423823/yarl-1.23.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67", size = 99289, upload-time = "2026-03-01T22:05:25.749Z" }, - { url = "https://files.pythonhosted.org/packages/6f/54/5b0db00d2cb056922356104468019c0a132e89c8d3ab67d8ede9f4483d2a/yarl-1.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7", size = 96950, upload-time = "2026-03-01T22:05:27.318Z" }, - { url = "https://files.pythonhosted.org/packages/f6/40/10fa93811fd439341fad7e0718a86aca0de9548023bbb403668d6555acab/yarl-1.23.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d", size = 93960, upload-time = "2026-03-01T22:05:28.738Z" }, - { url = "https://files.pythonhosted.org/packages/bc/d2/8ae2e6cd77d0805f4526e30ec43b6f9a3dfc542d401ac4990d178e4bf0cf/yarl-1.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760", size = 104703, upload-time = "2026-03-01T22:05:30.438Z" }, - { url = "https://files.pythonhosted.org/packages/2f/0c/b3ceacf82c3fe21183ce35fa2acf5320af003d52bc1fcf5915077681142e/yarl-1.23.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2", size = 98325, upload-time = "2026-03-01T22:05:31.835Z" }, - { url = "https://files.pythonhosted.org/packages/9d/e0/12900edd28bdab91a69bd2554b85ad7b151f64e8b521fe16f9ad2f56477a/yarl-1.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86", size = 105067, upload-time = "2026-03-01T22:05:33.358Z" }, - { url = "https://files.pythonhosted.org/packages/15/61/74bb1182cf79c9bbe4eb6b1f14a57a22d7a0be5e9cedf8e2d5c2086474c3/yarl-1.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34", size = 100285, upload-time = "2026-03-01T22:05:35.4Z" }, - { url = "https://files.pythonhosted.org/packages/69/7f/cd5ef733f2550de6241bd8bd8c3febc78158b9d75f197d9c7baa113436af/yarl-1.23.0-cp312-cp312-win32.whl", hash = "sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d", size = 82359, upload-time = "2026-03-01T22:05:36.811Z" }, - { url = "https://files.pythonhosted.org/packages/f5/be/25216a49daeeb7af2bec0db22d5e7df08ed1d7c9f65d78b14f3b74fd72fc/yarl-1.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e", size = 87674, upload-time = "2026-03-01T22:05:38.171Z" }, - { url = "https://files.pythonhosted.org/packages/d2/35/aeab955d6c425b227d5b7247eafb24f2653fedc32f95373a001af5dfeb9e/yarl-1.23.0-cp312-cp312-win_arm64.whl", hash = "sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9", size = 81879, upload-time = "2026-03-01T22:05:40.006Z" }, - { url = "https://files.pythonhosted.org/packages/9a/4b/a0a6e5d0ee8a2f3a373ddef8a4097d74ac901ac363eea1440464ccbe0898/yarl-1.23.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e", size = 123796, upload-time = "2026-03-01T22:05:41.412Z" }, - { url = "https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5", size = 86547, upload-time = "2026-03-01T22:05:42.841Z" }, - { url = "https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b", size = 85854, upload-time = "2026-03-01T22:05:44.85Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f4/4e30b250927ffdab4db70da08b9b8d2194d7c7b400167b8fbeca1e4701ca/yarl-1.23.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035", size = 98351, upload-time = "2026-03-01T22:05:46.836Z" }, - { url = "https://files.pythonhosted.org/packages/86/fc/4118c5671ea948208bdb1492d8b76bdf1453d3e73df051f939f563e7dcc5/yarl-1.23.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5", size = 92711, upload-time = "2026-03-01T22:05:48.316Z" }, - { url = "https://files.pythonhosted.org/packages/56/11/1ed91d42bd9e73c13dc9e7eb0dd92298d75e7ac4dd7f046ad0c472e231cd/yarl-1.23.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735", size = 106014, upload-time = "2026-03-01T22:05:50.028Z" }, - { url = "https://files.pythonhosted.org/packages/ce/c9/74e44e056a23fbc33aca71779ef450ca648a5bc472bdad7a82339918f818/yarl-1.23.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401", size = 105557, upload-time = "2026-03-01T22:05:51.416Z" }, - { url = "https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4", size = 101559, upload-time = "2026-03-01T22:05:52.872Z" }, - { url = "https://files.pythonhosted.org/packages/72/59/c5b8d94b14e3d3c2a9c20cb100119fd534ab5a14b93673ab4cc4a4141ea5/yarl-1.23.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f", size = 100502, upload-time = "2026-03-01T22:05:54.954Z" }, - { url = "https://files.pythonhosted.org/packages/77/4f/96976cb54cbfc5c9fd73ed4c51804f92f209481d1fb190981c0f8a07a1d7/yarl-1.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a", size = 98027, upload-time = "2026-03-01T22:05:56.409Z" }, - { url = "https://files.pythonhosted.org/packages/63/6e/904c4f476471afdbad6b7e5b70362fb5810e35cd7466529a97322b6f5556/yarl-1.23.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2", size = 95369, upload-time = "2026-03-01T22:05:58.141Z" }, - { url = "https://files.pythonhosted.org/packages/9d/40/acfcdb3b5f9d68ef499e39e04d25e141fe90661f9d54114556cf83be8353/yarl-1.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f", size = 105565, upload-time = "2026-03-01T22:06:00.286Z" }, - { url = "https://files.pythonhosted.org/packages/5e/c6/31e28f3a6ba2869c43d124f37ea5260cac9c9281df803c354b31f4dd1f3c/yarl-1.23.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b", size = 99813, upload-time = "2026-03-01T22:06:01.712Z" }, - { url = "https://files.pythonhosted.org/packages/08/1f/6f65f59e72d54aa467119b63fc0b0b1762eff0232db1f4720cd89e2f4a17/yarl-1.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a", size = 105632, upload-time = "2026-03-01T22:06:03.188Z" }, - { url = "https://files.pythonhosted.org/packages/a3/c4/18b178a69935f9e7a338127d5b77d868fdc0f0e49becd286d51b3a18c61d/yarl-1.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543", size = 101895, upload-time = "2026-03-01T22:06:04.651Z" }, - { url = "https://files.pythonhosted.org/packages/8f/54/f5b870b5505663911dba950a8e4776a0dbd51c9c54c0ae88e823e4b874a0/yarl-1.23.0-cp313-cp313-win32.whl", hash = "sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957", size = 82356, upload-time = "2026-03-01T22:06:06.04Z" }, - { url = "https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3", size = 87515, upload-time = "2026-03-01T22:06:08.107Z" }, - { url = "https://files.pythonhosted.org/packages/00/fd/7e1c66efad35e1649114fa13f17485f62881ad58edeeb7f49f8c5e748bf9/yarl-1.23.0-cp313-cp313-win_arm64.whl", hash = "sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3", size = 81785, upload-time = "2026-03-01T22:06:10.181Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fc/119dd07004f17ea43bb91e3ece6587759edd7519d6b086d16bfbd3319982/yarl-1.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa", size = 130719, upload-time = "2026-03-01T22:06:11.708Z" }, - { url = "https://files.pythonhosted.org/packages/e6/0d/9f2348502fbb3af409e8f47730282cd6bc80dec6630c1e06374d882d6eb2/yarl-1.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120", size = 89690, upload-time = "2026-03-01T22:06:13.429Z" }, - { url = "https://files.pythonhosted.org/packages/50/93/e88f3c80971b42cfc83f50a51b9d165a1dbf154b97005f2994a79f212a07/yarl-1.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59", size = 89851, upload-time = "2026-03-01T22:06:15.53Z" }, - { url = "https://files.pythonhosted.org/packages/1c/07/61c9dd8ba8f86473263b4036f70fb594c09e99c0d9737a799dfd8bc85651/yarl-1.23.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512", size = 95874, upload-time = "2026-03-01T22:06:17.553Z" }, - { url = "https://files.pythonhosted.org/packages/9e/e9/f9ff8ceefba599eac6abddcfb0b3bee9b9e636e96dbf54342a8577252379/yarl-1.23.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4", size = 88710, upload-time = "2026-03-01T22:06:19.004Z" }, - { url = "https://files.pythonhosted.org/packages/eb/78/0231bfcc5d4c8eec220bc2f9ef82cb4566192ea867a7c5b4148f44f6cbcd/yarl-1.23.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1", size = 101033, upload-time = "2026-03-01T22:06:21.203Z" }, - { url = "https://files.pythonhosted.org/packages/cd/9b/30ea5239a61786f18fd25797151a17fbb3be176977187a48d541b5447dd4/yarl-1.23.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea", size = 100817, upload-time = "2026-03-01T22:06:22.738Z" }, - { url = "https://files.pythonhosted.org/packages/62/e2/a4980481071791bc83bce2b7a1a1f7adcabfa366007518b4b845e92eeee3/yarl-1.23.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9", size = 97482, upload-time = "2026-03-01T22:06:24.21Z" }, - { url = "https://files.pythonhosted.org/packages/e5/1e/304a00cf5f6100414c4b5a01fc7ff9ee724b62158a08df2f8170dfc72a2d/yarl-1.23.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123", size = 95949, upload-time = "2026-03-01T22:06:25.697Z" }, - { url = "https://files.pythonhosted.org/packages/68/03/093f4055ed4cae649ac53bca3d180bd37102e9e11d048588e9ab0c0108d0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24", size = 95839, upload-time = "2026-03-01T22:06:27.309Z" }, - { url = "https://files.pythonhosted.org/packages/b9/28/4c75ebb108f322aa8f917ae10a8ffa4f07cae10a8a627b64e578617df6a0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de", size = 90696, upload-time = "2026-03-01T22:06:29.048Z" }, - { url = "https://files.pythonhosted.org/packages/23/9c/42c2e2dd91c1a570402f51bdf066bfdb1241c2240ba001967bad778e77b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b", size = 100865, upload-time = "2026-03-01T22:06:30.525Z" }, - { url = "https://files.pythonhosted.org/packages/74/05/1bcd60a8a0a914d462c305137246b6f9d167628d73568505fce3f1cb2e65/yarl-1.23.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6", size = 96234, upload-time = "2026-03-01T22:06:32.692Z" }, - { url = "https://files.pythonhosted.org/packages/90/b2/f52381aac396d6778ce516b7bc149c79e65bfc068b5de2857ab69eeea3b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6", size = 100295, upload-time = "2026-03-01T22:06:34.268Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/638bae5bbf1113a659b2435d8895474598afe38b4a837103764f603aba56/yarl-1.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5", size = 97784, upload-time = "2026-03-01T22:06:35.864Z" }, - { url = "https://files.pythonhosted.org/packages/80/25/a3892b46182c586c202629fc2159aa13975d3741d52ebd7347fd501d48d5/yarl-1.23.0-cp313-cp313t-win32.whl", hash = "sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595", size = 88313, upload-time = "2026-03-01T22:06:37.39Z" }, - { url = "https://files.pythonhosted.org/packages/43/68/8c5b36aa5178900b37387937bc2c2fe0e9505537f713495472dcf6f6fccc/yarl-1.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090", size = 94932, upload-time = "2026-03-01T22:06:39.579Z" }, - { url = "https://files.pythonhosted.org/packages/c6/cc/d79ba8292f51f81f4dc533a8ccfb9fc6992cabf0998ed3245de7589dc07c/yarl-1.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144", size = 84786, upload-time = "2026-03-01T22:06:41.988Z" }, - { url = "https://files.pythonhosted.org/packages/90/98/b85a038d65d1b92c3903ab89444f48d3cee490a883477b716d7a24b1a78c/yarl-1.23.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", size = 124455, upload-time = "2026-03-01T22:06:43.615Z" }, - { url = "https://files.pythonhosted.org/packages/39/54/bc2b45559f86543d163b6e294417a107bb87557609007c007ad889afec18/yarl-1.23.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", size = 86752, upload-time = "2026-03-01T22:06:45.425Z" }, - { url = "https://files.pythonhosted.org/packages/24/f9/e8242b68362bffe6fb536c8db5076861466fc780f0f1b479fc4ffbebb128/yarl-1.23.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", size = 86291, upload-time = "2026-03-01T22:06:46.974Z" }, - { url = "https://files.pythonhosted.org/packages/ea/d8/d1cb2378c81dd729e98c716582b1ccb08357e8488e4c24714658cc6630e8/yarl-1.23.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", size = 99026, upload-time = "2026-03-01T22:06:48.459Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ff/7196790538f31debe3341283b5b0707e7feb947620fc5e8236ef28d44f72/yarl-1.23.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", size = 92355, upload-time = "2026-03-01T22:06:50.306Z" }, - { url = "https://files.pythonhosted.org/packages/c1/56/25d58c3eddde825890a5fe6aa1866228377354a3c39262235234ab5f616b/yarl-1.23.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", size = 106417, upload-time = "2026-03-01T22:06:52.1Z" }, - { url = "https://files.pythonhosted.org/packages/51/8a/882c0e7bc8277eb895b31bce0138f51a1ba551fc2e1ec6753ffc1e7c1377/yarl-1.23.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", size = 106422, upload-time = "2026-03-01T22:06:54.424Z" }, - { url = "https://files.pythonhosted.org/packages/42/2b/fef67d616931055bf3d6764885990a3ac647d68734a2d6a9e1d13de437a2/yarl-1.23.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", size = 101915, upload-time = "2026-03-01T22:06:55.895Z" }, - { url = "https://files.pythonhosted.org/packages/18/6a/530e16aebce27c5937920f3431c628a29a4b6b430fab3fd1c117b26ff3f6/yarl-1.23.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", size = 100690, upload-time = "2026-03-01T22:06:58.21Z" }, - { url = "https://files.pythonhosted.org/packages/88/08/93749219179a45e27b036e03260fda05190b911de8e18225c294ac95bbc9/yarl-1.23.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", size = 98750, upload-time = "2026-03-01T22:06:59.794Z" }, - { url = "https://files.pythonhosted.org/packages/d9/cf/ea424a004969f5d81a362110a6ac1496d79efdc6d50c2c4b2e3ea0fc2519/yarl-1.23.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", size = 94685, upload-time = "2026-03-01T22:07:01.375Z" }, - { url = "https://files.pythonhosted.org/packages/e2/b7/14341481fe568e2b0408bcf1484c652accafe06a0ade9387b5d3fd9df446/yarl-1.23.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", size = 106009, upload-time = "2026-03-01T22:07:03.151Z" }, - { url = "https://files.pythonhosted.org/packages/0a/e6/5c744a9b54f4e8007ad35bce96fbc9218338e84812d36f3390cea616881a/yarl-1.23.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", size = 100033, upload-time = "2026-03-01T22:07:04.701Z" }, - { url = "https://files.pythonhosted.org/packages/0c/23/e3bfc188d0b400f025bc49d99793d02c9abe15752138dcc27e4eaf0c4a9e/yarl-1.23.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", size = 106483, upload-time = "2026-03-01T22:07:06.231Z" }, - { url = "https://files.pythonhosted.org/packages/72/42/f0505f949a90b3f8b7a363d6cbdf398f6e6c58946d85c6d3a3bc70595b26/yarl-1.23.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", size = 102175, upload-time = "2026-03-01T22:07:08.4Z" }, - { url = "https://files.pythonhosted.org/packages/aa/65/b39290f1d892a9dd671d1c722014ca062a9c35d60885d57e5375db0404b5/yarl-1.23.0-cp314-cp314-win32.whl", hash = "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", size = 83871, upload-time = "2026-03-01T22:07:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/a9/5b/9b92f54c784c26e2a422e55a8d2607ab15b7ea3349e28359282f84f01d43/yarl-1.23.0-cp314-cp314-win_amd64.whl", hash = "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", size = 89093, upload-time = "2026-03-01T22:07:11.501Z" }, - { url = "https://files.pythonhosted.org/packages/e0/7d/8a84dc9381fd4412d5e7ff04926f9865f6372b4c2fd91e10092e65d29eb8/yarl-1.23.0-cp314-cp314-win_arm64.whl", hash = "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", size = 83384, upload-time = "2026-03-01T22:07:13.069Z" }, - { url = "https://files.pythonhosted.org/packages/dd/8d/d2fad34b1c08aa161b74394183daa7d800141aaaee207317e82c790b418d/yarl-1.23.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", size = 131019, upload-time = "2026-03-01T22:07:14.903Z" }, - { url = "https://files.pythonhosted.org/packages/19/ff/33009a39d3ccf4b94d7d7880dfe17fb5816c5a4fe0096d9b56abceea9ac7/yarl-1.23.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", size = 89894, upload-time = "2026-03-01T22:07:17.372Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f1/dab7ac5e7306fb79c0190766a3c00b4cb8d09a1f390ded68c85a5934faf5/yarl-1.23.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", size = 89979, upload-time = "2026-03-01T22:07:19.361Z" }, - { url = "https://files.pythonhosted.org/packages/aa/b1/08e95f3caee1fad6e65017b9f26c1d79877b502622d60e517de01e72f95d/yarl-1.23.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", size = 95943, upload-time = "2026-03-01T22:07:21.266Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cc/6409f9018864a6aa186c61175b977131f373f1988e198e031236916e87e4/yarl-1.23.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", size = 88786, upload-time = "2026-03-01T22:07:23.129Z" }, - { url = "https://files.pythonhosted.org/packages/76/40/cc22d1d7714b717fde2006fad2ced5efe5580606cb059ae42117542122f3/yarl-1.23.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", size = 101307, upload-time = "2026-03-01T22:07:24.689Z" }, - { url = "https://files.pythonhosted.org/packages/8f/0d/476c38e85ddb4c6ec6b20b815bdd779aa386a013f3d8b85516feee55c8dc/yarl-1.23.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", size = 100904, upload-time = "2026-03-01T22:07:26.287Z" }, - { url = "https://files.pythonhosted.org/packages/72/32/0abe4a76d59adf2081dcb0397168553ece4616ada1c54d1c49d8936c74f8/yarl-1.23.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", size = 97728, upload-time = "2026-03-01T22:07:27.906Z" }, - { url = "https://files.pythonhosted.org/packages/b7/35/7b30f4810fba112f60f5a43237545867504e15b1c7647a785fbaf588fac2/yarl-1.23.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", size = 95964, upload-time = "2026-03-01T22:07:30.198Z" }, - { url = "https://files.pythonhosted.org/packages/2d/86/ed7a73ab85ef00e8bb70b0cb5421d8a2a625b81a333941a469a6f4022828/yarl-1.23.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", size = 95882, upload-time = "2026-03-01T22:07:32.132Z" }, - { url = "https://files.pythonhosted.org/packages/19/90/d56967f61a29d8498efb7afb651e0b2b422a1e9b47b0ab5f4e40a19b699b/yarl-1.23.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", size = 90797, upload-time = "2026-03-01T22:07:34.404Z" }, - { url = "https://files.pythonhosted.org/packages/72/00/8b8f76909259f56647adb1011d7ed8b321bcf97e464515c65016a47ecdf0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", size = 101023, upload-time = "2026-03-01T22:07:35.953Z" }, - { url = "https://files.pythonhosted.org/packages/ac/e2/cab11b126fb7d440281b7df8e9ddbe4851e70a4dde47a202b6642586b8d9/yarl-1.23.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", size = 96227, upload-time = "2026-03-01T22:07:37.594Z" }, - { url = "https://files.pythonhosted.org/packages/c2/9b/2c893e16bfc50e6b2edf76c1a9eb6cb0c744346197e74c65e99ad8d634d0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", size = 100302, upload-time = "2026-03-01T22:07:39.334Z" }, - { url = "https://files.pythonhosted.org/packages/28/ec/5498c4e3a6d5f1003beb23405671c2eb9cdbf3067d1c80f15eeafe301010/yarl-1.23.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", size = 98202, upload-time = "2026-03-01T22:07:41.717Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c3/cd737e2d45e70717907f83e146f6949f20cc23cd4bf7b2688727763aa458/yarl-1.23.0-cp314-cp314t-win32.whl", hash = "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", size = 90558, upload-time = "2026-03-01T22:07:43.433Z" }, - { url = "https://files.pythonhosted.org/packages/e1/19/3774d162f6732d1cfb0b47b4140a942a35ca82bb19b6db1f80e9e7bdc8f8/yarl-1.23.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", size = 97610, upload-time = "2026-03-01T22:07:45.773Z" }, - { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" }, - { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/79/12/1e8f37460ea0f7eb59c221fdaf0ed75e7ac43e97f8093b9c6f411df50a78/yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8", size = 210798, upload-time = "2026-05-19T21:31:05.599Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/c5/1ce244152ff2839645e7cae92f90e7bafcb2c52bea7ff586ac714f14f5df/yarl-1.24.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:36348bebb147b83818b9d7e673ea4debc75970afc6ffdc7e3975ad05ce5a58c1", size = 128971, upload-time = "2026-05-19T21:28:20.543Z" }, + { url = "https://files.pythonhosted.org/packages/87/5a/00f36967203ed89cb3acd2c8ed526cc3fed9418eb70ce128160a911c8499/yarl-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a97e42c8a2233f2f279ecadd9e4a037bcb5d813b78435e8eedd4db5a9e9708c", size = 91507, upload-time = "2026-05-19T21:28:22.556Z" }, + { url = "https://files.pythonhosted.org/packages/31/d0/1fb0c1cd27288f39f6974da4318c32768d72c9890984541fdf1e2e32a51d/yarl-1.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8d027d56f1035e339d1001ac33eceab5b2ec8e42e449787bb75e289fb9a5cd1d", size = 91343, upload-time = "2026-05-19T21:28:24.092Z" }, + { url = "https://files.pythonhosted.org/packages/03/ce/d4a646508bed2f8dec6435b40166fe9308dd191262033d3f307b2bbcaecd/yarl-1.24.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a6377060e7927187a42b7eb202090cbe2b34933a4eeaf90e3bd9e33432e5cae", size = 105704, upload-time = "2026-05-19T21:28:25.872Z" }, + { url = "https://files.pythonhosted.org/packages/4b/07/b3278e82d8bc41485bcf6d856cd0433262593de615b1d3dc43bd3f5bead4/yarl-1.24.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:17076578bce0049a5ce57d14ad1bded391b68a3b213e9b81b0097b090244999a", size = 97281, upload-time = "2026-05-19T21:28:27.352Z" }, + { url = "https://files.pythonhosted.org/packages/17/5b/4cee6e7c92e487bebe7afc797da0aa54a248ab4e776a68fe369ec29665a5/yarl-1.24.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:50713f1d4d6be6375bb178bb43d140ee1acb8abe589cd723320b7925a275be1e", size = 114020, upload-time = "2026-05-19T21:28:29.458Z" }, + { url = "https://files.pythonhosted.org/packages/5c/82/111076571545a7d4f9cca3fbd5c6f40615af58642be09f12328f48022468/yarl-1.24.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:34263e2fa8fb5bb63a0d97706cda38edbad62fddb58c7f12d6acbc092812aa50", size = 111450, upload-time = "2026-05-19T21:28:31.262Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ec/08f671f69a444d704aeecebf92af659b67b97a869942411d0a578b08c334/yarl-1.24.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49016d82f032b1bd1e10b01078a7d29ae71bf468eeae0ea22df8bab691e60003", size = 106384, upload-time = "2026-05-19T21:28:32.856Z" }, + { url = "https://files.pythonhosted.org/packages/e5/86/ce41e7a7a199340b2330d52b60f25c4074b6636dd0e60b1a80d31a9db042/yarl-1.24.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3f6d2c216318f8f32038ca3f72501ba08536f0fd18a36e858836b121b2deed9f", size = 106153, upload-time = "2026-05-19T21:28:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5d/31be8a729531ab3e55ac3e7e5c800be8c89ea98947f418b2f6ea259fb6ee/yarl-1.24.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:08d3a33218e0c64393e7610284e770409a9c31c429b078bcb24096ed0a783b8f", size = 105322, upload-time = "2026-05-19T21:28:36.642Z" }, + { url = "https://files.pythonhosted.org/packages/47/9b/b57afb22b386ae87ac9940f09878b98d8c333f89113e6fc96fcf4ca9eb64/yarl-1.24.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5d699376c4ca3cba49bbfae3a05b5b70ded572937171ce1e0b8d87118e2ba294", size = 99057, upload-time = "2026-05-19T21:28:38.386Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4f/06348c27c8389256c313e8a57d796808fc0264c915dd5e7cfd3c0e314dc7/yarl-1.24.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a1cab588b4fa14bea2e55ebea27478adfb05372f47573738e1acc4a36c0b05d2", size = 113502, upload-time = "2026-05-19T21:28:40.091Z" }, + { url = "https://files.pythonhosted.org/packages/5f/1c/284f307b298e4a17b7943b07d9d7ecc4151537f8d137ba51f3bb6c31ca20/yarl-1.24.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ec87ccc31bd21db7ad009d8572c127c1000f268517618a4cc09adba3c2a7f21c", size = 105253, upload-time = "2026-05-19T21:28:41.987Z" }, + { url = "https://files.pythonhosted.org/packages/c8/bf/0de123bec8619e45c80cbded9085f61b5b4a9eddb8abe6d25d28ee1ec866/yarl-1.24.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d1dd47a22843b212baa8d74f37796815d43bd046b42a0f41e9da433386c3136b", size = 111345, upload-time = "2026-05-19T21:28:43.93Z" }, + { url = "https://files.pythonhosted.org/packages/90/af/0248eb065e51129d2a9b2436cd1b5c772c19a6b04e5b6a186955671e3319/yarl-1.24.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7b54b9c67c2b06bd7b9a77253d242124b9c95d2c02def5a1144001ee547dd9d5", size = 106558, upload-time = "2026-05-19T21:28:45.806Z" }, + { url = "https://files.pythonhosted.org/packages/21/3c/f960d7a65ef97d8ba9b424fb5128796a4bc710fc6df2ddbbd7dfdc3bbd20/yarl-1.24.2-cp311-cp311-win_amd64.whl", hash = "sha256:f8fdbcff8b2c7c9284e60c196f693588598ddcee31e11c18e14949ce44519d45", size = 92808, upload-time = "2026-05-19T21:28:48.465Z" }, + { url = "https://files.pythonhosted.org/packages/03/1a/49fb03750e4de4d2284cd5b885a383133c34eef45bd59631b2bb8b7e81e8/yarl-1.24.2-cp311-cp311-win_arm64.whl", hash = "sha256:b32c37a7a337e90822c45797bf3d79d60875cfcccd3ecc80e9f453d87026c122", size = 87610, upload-time = "2026-05-19T21:28:50.07Z" }, + { url = "https://files.pythonhosted.org/packages/f0/da/866bcb01076ba49d2b42b309867bed3826421f1c479655eb7a607b44f20b/yarl-1.24.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b975866c184564c827e0877380f0dae57dcca7e52782128381b72feff6dfceb8", size = 129957, upload-time = "2026-05-19T21:28:51.695Z" }, + { url = "https://files.pythonhosted.org/packages/bf/1d/fcefb70922ea2268a8971d8e5874d9a8218644200fb8465f1dcad55e6851/yarl-1.24.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3b075301a2836a0e297b1b658cb6d6135df535d62efefdd60366bd589c2c82f2", size = 92164, upload-time = "2026-05-19T21:28:53.242Z" }, + { url = "https://files.pythonhosted.org/packages/29/b6/170e2b8d4e3bc30e6bfdcca53556537f5bf595e938632dfcb059311f3ff6/yarl-1.24.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ae44649b00947634ab0dab2a374a638f52923a6e67083f2c156cd5cbd1a881d", size = 91688, upload-time = "2026-05-19T21:28:54.865Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a5/c9f655d5553ea0b99fdac9d6a99ad3f9b3e73b8e5758bb46f58c9831f74c/yarl-1.24.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507cc19f0b45454e2d6dcd62ff7d062b9f77a2812404e62dbdaec05b50faa035", size = 102902, upload-time = "2026-05-19T21:28:56.963Z" }, + { url = "https://files.pythonhosted.org/packages/5d/bc/6b9664d815d79af4ee553337f9d606c56bbf269186ada9172de45f1b5f60/yarl-1.24.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4c17bad5a530912d2111825d3f05e89bab2dd376aaa8cbc77e449e6db63e576", size = 97931, upload-time = "2026-05-19T21:28:58.56Z" }, + { url = "https://files.pythonhosted.org/packages/98/ec/32ba48acae30fecd60928f5791188b80a9d6ee3840507ffda29fecd37b71/yarl-1.24.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5f0cbb112838a4a293985b6ed73948a547dadcc1ba6d2089938e7abdedceef8", size = 111030, upload-time = "2026-05-19T21:29:00.148Z" }, + { url = "https://files.pythonhosted.org/packages/82/5a/6f4cd081e5f4934d2ae3a8ef4abe3afacc010d26f0035ee91b35cd7d7c37/yarl-1.24.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ec8356b8a6afcf81fc7aeeef13b1ff7a49dec00f313394bbb9e83830d32ccd7", size = 110392, upload-time = "2026-05-19T21:29:02.155Z" }, + { url = "https://files.pythonhosted.org/packages/7a/da/323a01c349bd5fb01bb6652e314d9bb218cee630a736bdb810ad50e4013f/yarl-1.24.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e7ebcdef69dec6c6451e616f32b622a6d4a2e92b445c992f7c8e5274a6bbc4c", size = 105612, upload-time = "2026-05-19T21:29:04.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/80/264ab684f181e1a876389374519ff05d10248725535ae2ac4e8ac4e563d6/yarl-1.24.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:47a55d6cf6db2f401017a9e96e5288844e5051911fb4e0c8311a3980f5e59a7d", size = 104487, upload-time = "2026-05-19T21:29:06.491Z" }, + { url = "https://files.pythonhosted.org/packages/41/07/efabe5df87e96d7ad5959760b888344be48cd6884db127b407c6b5503adc/yarl-1.24.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3065657c80a2321225e804048597ad55658a7e76b32d6f5ee4074d04c50401db", size = 102333, upload-time = "2026-05-19T21:29:08.267Z" }, + { url = "https://files.pythonhosted.org/packages/44/0c/bcf7c42603e1009295f586d8890f2ba032c8b53310e815adf0a202c73d9f/yarl-1.24.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cb84b80d88e19ede158619b80813968713d8d008b0e2497a576e6a0557d50712", size = 99025, upload-time = "2026-05-19T21:29:10.682Z" }, + { url = "https://files.pythonhosted.org/packages/4f/82/84482ab1a57a0f21a08afe6a7004c61d741f8f2ecc3b05c321577c612164/yarl-1.24.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:990de4f680b1c217e77ff0d6aa0029f9eb79889c11fb3e9a3942c7eba29c1996", size = 110507, upload-time = "2026-05-19T21:29:12.954Z" }, + { url = "https://files.pythonhosted.org/packages/c4/8d/a546ba1dfe1b0f290e05fef145cd07614c0f15df1a707195e512d1e39d1d/yarl-1.24.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:abb8ec0323b80161e3802da3150ef660b41d0e9be2048b76a363d93eee992c2b", size = 103719, upload-time = "2026-05-19T21:29:14.893Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b6/267f2a09213138473adfce6b8a6e17791d7fee70bd4d9003218e4dec58b0/yarl-1.24.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e7977781f83638a4c73e0f88425563d70173e0dfd90ac006a45c65036293ee3c", size = 110438, upload-time = "2026-05-19T21:29:16.485Z" }, + { url = "https://files.pythonhosted.org/packages/48/2d/1c8d89c7c5f9cad9fb2902445d94e2ab1d7aa35de029afbb8ae95c42d00f/yarl-1.24.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e30dd55825dc554ec5b66a94953b8eda8745926514c5089dfcacecb9c99b5bd1", size = 105719, upload-time = "2026-05-19T21:29:18.367Z" }, + { url = "https://files.pythonhosted.org/packages/a7/25/722e3b93bd687009afb2d59a35e13d30ddd8f80571445bb0c4e4ce26ec66/yarl-1.24.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dafe10c12ddd4d120d528c4b5599c953bd7b12845347d507b95451195bb6cad", size = 92901, upload-time = "2026-05-19T21:29:20.014Z" }, + { url = "https://files.pythonhosted.org/packages/39/47/4486ccfb674c04854a1ef8aa77868b6a6f765feaf69633409d7ca4f02cb8/yarl-1.24.2-cp312-cp312-win_arm64.whl", hash = "sha256:044a09d8401fcf8681977faef6d286b8ade1e2d2e9dceda175d1cfa5ca496f30", size = 87229, upload-time = "2026-05-19T21:29:22.1Z" }, + { url = "https://files.pythonhosted.org/packages/82/62/fcf0ce677f17e5c471c06311dd25964be38a4c586993632910d2e75278bc/yarl-1.24.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:491ac9141decf49ee8030199e1ee251cdff0e131f25678817ff6aa5f837a3536", size = 128978, upload-time = "2026-05-19T21:29:23.83Z" }, + { url = "https://files.pythonhosted.org/packages/d3/58/8e63299bb71ed61a834121d9d3fe6c9fcf2a6a5d09754ff4f20f2d20baf5/yarl-1.24.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e89418f65eda18f99030386305bd44d7d504e328a7945db1ead514fbe03a0607", size = 91733, upload-time = "2026-05-19T21:29:25.375Z" }, + { url = "https://files.pythonhosted.org/packages/c1/24/16748d5dab6daec8b0ed81ccec639a1cded0f18dcc62a4f696b4fe366c37/yarl-1.24.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cdfcce633b4a4bb8281913c57fcafd4b5933fbc19111a5e3930bbd299d6102f1", size = 91113, upload-time = "2026-05-19T21:29:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/1b/66/b63fff7b71211e866624b21432d5943cbb633eb0c2872d9ee3070648f22c/yarl-1.24.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:863297ddede92ee49024e9a9b11ecb59f310ca85b60d8537f56bed9bbb5b1986", size = 103899, upload-time = "2026-05-19T21:29:28.842Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ac/ba1974b8533909636f7733fe86cf677e3619527c3c2fa913e0ea89c48757/yarl-1.24.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:374423f70754a2c96942ede36a29d37dc6b0cb8f92f8d009ddf3ed78d3da5488", size = 97862, upload-time = "2026-05-19T21:29:31.086Z" }, + { url = "https://files.pythonhosted.org/packages/1b/a5/123ac993b5c2ba6f554a140305620cb8f150fa543711bbc49be3ec0a65a4/yarl-1.24.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33a29b5d00ccbf3219bb3e351d7875739c19481e030779f48cc46a7a71681a9b", size = 111060, upload-time = "2026-05-19T21:29:32.657Z" }, + { url = "https://files.pythonhosted.org/packages/23/37/c472d3af3509688392134a88a825276770a187f1daa4de3f6dc0a327a751/yarl-1.24.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a9532c57211730c515341af11fef6e9b61d157487272a096d0c04da445642592", size = 110613, upload-time = "2026-05-19T21:29:34.379Z" }, + { url = "https://files.pythonhosted.org/packages/df/88/09c28dad91e662ccfaa1b78f1c57badde74fc9d0b23e74aef644750ecd73/yarl-1.24.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91e72cf093fd833483a97ee648e0c053c7c629f51ff4a0e7edd84f806b0c5617", size = 107012, upload-time = "2026-05-19T21:29:36.216Z" }, + { url = "https://files.pythonhosted.org/packages/07/ab/9d4f69d571a94f4d112fa7e2e007200f5a54d319f58c82ac7b7baa61f5c6/yarl-1.24.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b3177bc0a768ef3bacceb4f272632990b7bea352f1b2f1eee9d6d6ff16516f92", size = 105887, upload-time = "2026-05-19T21:29:38.746Z" }, + { url = "https://files.pythonhosted.org/packages/8e/9a/000b2b66c0d772a499fc531d21dab92dfeb73b640a12eed6ba89f49bb2d0/yarl-1.24.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e196952aacaf3b232e265ff02980b64d483dc0972bd49bcb061171ff22ac203a", size = 103620, upload-time = "2026-05-19T21:29:40.368Z" }, + { url = "https://files.pythonhosted.org/packages/41/7c/7c1050f73450fbdaa3f0c72017059f00ce5e13366692f3dba25275a1083d/yarl-1.24.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:204e7a61ce99919c0de1bf904ab5d7aa188a129ea8f690a8f76cfb6e2844dc44", size = 100599, upload-time = "2026-05-19T21:29:42.66Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b1/29e5756b3926705f5f6089bd5b9f50a56eaac550da6e260bf713ead44d04/yarl-1.24.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b156914620f0b9d78dc1adb3751141daee561cfec796088abb89ed49d220f1a", size = 110604, upload-time = "2026-05-19T21:29:44.632Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4b/8415bc96e9b150cde942fbac9a8182985e58f40ce5c54c34ed015407d3ee/yarl-1.24.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8372a2b976cf70654b2be6619ab6068acabb35f724c0fda7b277fbf53d66a5cf", size = 105161, upload-time = "2026-05-19T21:29:46.755Z" }, + { url = "https://files.pythonhosted.org/packages/8b/d4/cde059abfa229553b7298a2eadde2752e723d50aeedaef86ce59da2718ee/yarl-1.24.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f9a1e9b622ca284143aab5d885848686dcd85453bb1ca9abcdb7503e64dc0056", size = 110619, upload-time = "2026-05-19T21:29:48.972Z" }, + { url = "https://files.pythonhosted.org/packages/e7/2c/d6a6c9a61549f7b6c7e6dc6937d195bcf069582b47b7200dcd0e7b256acf/yarl-1.24.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:810e19b685c8c3c5862f6a38160a1f4e4c0916c9390024ec347b6157a45a0992", size = 107362, upload-time = "2026-05-19T21:29:51Z" }, + { url = "https://files.pythonhosted.org/packages/92/dd/3ae5fe417e9d1c353a548553326eb9935e76b6b727161563b424cc296df3/yarl-1.24.2-cp313-cp313-win_amd64.whl", hash = "sha256:7d37fb7c38f2b6edab0f845c4f85148d4c44204f52bc127021bd2bc9fdbf1656", size = 92667, upload-time = "2026-05-19T21:29:52.743Z" }, + { url = "https://files.pythonhosted.org/packages/10/cc/a7beb239f78f27fca1b053c8e8595e4179c02e62249b4687ec218c370c50/yarl-1.24.2-cp313-cp313-win_arm64.whl", hash = "sha256:1e831894be7c2954240e49791fa4b50c05a0dc881de2552cfe3ffd8631c7f461", size = 87069, upload-time = "2026-05-19T21:29:54.442Z" }, + { url = "https://files.pythonhosted.org/packages/40/0e/e08087695fc12789263821c5dc0f8dc52b5b17efd0887cacf419f8a43ba3/yarl-1.24.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:f9312b3c02d9b3d23840f67952913c9c8721d7f1b7db305289faefa878f364c2", size = 129670, upload-time = "2026-05-19T21:29:56.631Z" }, + { url = "https://files.pythonhosted.org/packages/3a/98/ab4b5ed1b1b5cd973c8a3eb994c3a6aefb6ce6d399e21bb5f0316c33815c/yarl-1.24.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a4f4d6cd615823bfc7fb7e9b5987c3f41666371d870d51058f77e2680fbe9630", size = 91916, upload-time = "2026-05-19T21:29:58.645Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b1/5297bb6a7df4782f7605bffc43b31f5044070935fbbcaa6c705a07e6ac65/yarl-1.24.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0c3063e5c0a8e8e62fae6c2596fa01da1561e4cd1da6fec5789f5cf99a8aefd8", size = 91625, upload-time = "2026-05-19T21:30:00.412Z" }, + { url = "https://files.pythonhosted.org/packages/02/a7/45baabfff76829264e623b185cff0c340d7e11bf3e1cd9ea37e7d17934bd/yarl-1.24.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fecd17873a096036c1c87ab3486f1aef7f269ada7f23f7f856f93b1cc7744f14", size = 104574, upload-time = "2026-05-19T21:30:02.544Z" }, + { url = "https://files.pythonhosted.org/packages/f3/40/3a5ab144d3d650ca37d4f4b57e56169be8af3ca34c448793e064b30baaed/yarl-1.24.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a46d1ab4ba4d32e6dc80daf8a28ce0bd83d08df52fbc32f3e288663427734535", size = 97534, upload-time = "2026-05-19T21:30:04.319Z" }, + { url = "https://files.pythonhosted.org/packages/9c/b5/5658fef3681fb5776b4513b052bec750009f47b3a592251c705d75375798/yarl-1.24.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73e68edf6dfd5f73f9ca127d84e2a6f9213c65bdffb736bda19524c0564fcd14", size = 111481, upload-time = "2026-05-19T21:30:05.988Z" }, + { url = "https://files.pythonhosted.org/packages/4c/06/fdcd7dde037f00866dce123ed4ba23dba94beb56fc4cf561668d27be37f2/yarl-1.24.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a296ca617f2d25fbceafb962b88750d627e5984e75732c712154d058ae8d79a3", size = 111529, upload-time = "2026-05-19T21:30:07.738Z" }, + { url = "https://files.pythonhosted.org/packages/c2/53/d81269aaafccea0d33396c03035de997b743f11e648e6e27a0df99c72980/yarl-1.24.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51b2cf5ec89a8b8470177641ed62a3ba22d74e1e898e06ad53aa77972487208", size = 107338, upload-time = "2026-05-19T21:30:09.713Z" }, + { url = "https://files.pythonhosted.org/packages/ae/04/23049463f729bd899df203a7960505a75333edd499cda8aa1d5a82b64df5/yarl-1.24.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:310fc687f7b2044ec54e372c8cbe923bb88f5c37bded0d3079e5791c2fc3cf50", size = 106147, upload-time = "2026-05-19T21:30:11.365Z" }, + { url = "https://files.pythonhosted.org/packages/14/18/04a4b5830b43ed5e4c5015b40e9f6241ad91487d71611061b4e111d6ac80/yarl-1.24.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:297a2fe352ecf858b30a98f87948746ec16f001d279f84aebdbd3bd965e2f1bd", size = 104272, upload-time = "2026-05-19T21:30:12.978Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f7/8cffdf319aee7a7c1dbd07b61d91c3e3fda460c7a93b5f93e445f3806c4c/yarl-1.24.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2a263e76b97bc42bdcd7c5f4953dec1f7cd62a1112fa7f869e57255229390d67", size = 99962, upload-time = "2026-05-19T21:30:15.001Z" }, + { url = "https://files.pythonhosted.org/packages/d7/39/b3cce3b7dbef64ac700ad4cea156a207d01bede0f507587616c364b5468e/yarl-1.24.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:822519b64cf0b474f1a0aaef1dc621438ea46bb77c94df97a5b4d213a7d8a8b1", size = 111063, upload-time = "2026-05-19T21:30:16.683Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ea/100818505e7ebf165c7242ff17fdf7d9fee79e27234aeca871c1082920d7/yarl-1.24.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b6067060d9dc594899ba83e6db6c48c68d1e494a6dab158156ed86977ca7bcb1", size = 105438, upload-time = "2026-05-19T21:30:18.769Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d2/e075a0b32aa6625087de9e653087df0759fed5de4a435fef594181102a77/yarl-1.24.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0063adad533e57171b79db3943b229d40dfafeeee579767f96541f106bac5f1b", size = 111458, upload-time = "2026-05-19T21:30:21.024Z" }, + { url = "https://files.pythonhosted.org/packages/e6/5c/ceea7ba98b65c8eb8d947fdc52f9bedfcd43c6a57c9e3c90c17be8f324a3/yarl-1.24.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ee8e3fb34513e8dc082b586ef4910c98335d43a6fab688cd44d4851bacfce3e8", size = 107589, upload-time = "2026-05-19T21:30:23.412Z" }, + { url = "https://files.pythonhosted.org/packages/fa/d9/5582d57e2b2db9b85eb6663a22efdd78e08805f3f5389566e9fcad254d1b/yarl-1.24.2-cp314-cp314-win_amd64.whl", hash = "sha256:afb00d7fd8e0f285ca29a44cc50df2d622ff2f7a6d933fa641577b5f9d5f3db0", size = 94424, upload-time = "2026-05-19T21:30:25.425Z" }, + { url = "https://files.pythonhosted.org/packages/92/10/7dc07a0e22806a9280f42a57361395506e800c64e22737cd7b0886feab42/yarl-1.24.2-cp314-cp314-win_arm64.whl", hash = "sha256:68cf6eacd6028ef1142bc4b48376b81566385ca6f9e7dde3b0fa91be08ffcb57", size = 88690, upload-time = "2026-05-19T21:30:27.623Z" }, + { url = "https://files.pythonhosted.org/packages/9e/13/d5b8e2c8667db955bcb3de233f18798fefe7edf1d7429c2c9d4f9c401114/yarl-1.24.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:221ce1dd921ac4f603957f17d7c18c5cc0797fbb52f156941f92e04605d1d67b", size = 136248, upload-time = "2026-05-19T21:30:29.297Z" }, + { url = "https://files.pythonhosted.org/packages/de/46/a4a97c05c9c9b8fd266bb2a0df12992c7fbd02391eb9640583411b6dab32/yarl-1.24.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5f3224db28173a00d7afacdee07045cc4673dfab2b15492c7ae10deddbece761", size = 95084, upload-time = "2026-05-19T21:30:31.031Z" }, + { url = "https://files.pythonhosted.org/packages/95/b2/845cf2074a015e6fe0d0808cf1a2d9e868386c4220d657ebd8302b199043/yarl-1.24.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c557165320d6244ebe3a02431b2a201a20080e02f41f0cfa0ccc47a183765da8", size = 95272, upload-time = "2026-05-19T21:30:33.062Z" }, + { url = "https://files.pythonhosted.org/packages/fe/16/e69d4aa244aef45235ddfebc0e04036a6829842bc5a6a795aedc6c998d23/yarl-1.24.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:904065e6e85b1fa54d0d87438bd58c14c0bad97aad654ad1077fd9d87e8478ed", size = 101497, upload-time = "2026-05-19T21:30:34.842Z" }, + { url = "https://files.pythonhosted.org/packages/15/94/c07107715d621076863ee88b3ddf183fa5e9d4aba5769623c9979828410a/yarl-1.24.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cec2a38d70edc10e0e856ceda886af5327a017ccbde8e1de1bd44d300357543", size = 94002, upload-time = "2026-05-19T21:30:37.724Z" }, + { url = "https://files.pythonhosted.org/packages/a9/35/fc1bbdd895b5e4010b8fdd037f7ed3aa289d3863e08231b30231ca9a0815/yarl-1.24.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e7484b9361ed222ee1ca5b4337aa4cbdcc4618ce5aff57d9ef1582fd95893fc0", size = 106524, upload-time = "2026-05-19T21:30:40.196Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f2/32b66d0a4ba47c296cf86d03e2c67bff58399fe6d6d84d5205c04c66cc6d/yarl-1.24.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:84f9670b89f34db07f81e53aee83e0b938a3412329d51c8f922488be7fcc4024", size = 106165, upload-time = "2026-05-19T21:30:41.888Z" }, + { url = "https://files.pythonhosted.org/packages/95/47/37cb5ff50c5e825d4d38e81bb04d1b7e96bf960f7ab89f9850b162f3f114/yarl-1.24.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:abb2759733d63a28b4956500a5dd57140f26486c92b2caedfb964ab7d9b79dbf", size = 103010, upload-time = "2026-05-19T21:30:43.985Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d2/4597912315096f7bb359e46e13bf8b60994fcbb2db29b804c0902ef4eff5/yarl-1.24.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:081c2bf54efe03774d0311172bc04fedf9ca01e644d4cd8c805688e527209bdc", size = 101128, upload-time = "2026-05-19T21:30:46.291Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d5/c8e86e120521e646013d02a8e3b8884392e28494be8f392366e50d208efc/yarl-1.24.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:86746bef442aa479107fe28132e1277237f9c24c2f00b0b0cf22b3ee0904f2bb", size = 101382, upload-time = "2026-05-19T21:30:48.085Z" }, + { url = "https://files.pythonhosted.org/packages/fa/98/70b229236118f89dbeb739b76f10225bbf53b5497725502594c9a01d699a/yarl-1.24.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:2d07d21d0bc4b17558e8de0b02fbfdf1e347d3bb3699edd00bb92e7c57925420", size = 95964, upload-time = "2026-05-19T21:30:49.785Z" }, + { url = "https://files.pythonhosted.org/packages/87/f8/56c386981e3c8648d279fdef2397ffec577e8320fd5649745e34d54faeb7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4fb1ac3fc5fecd8ae7453ea237e4d22b49befa70266dfe1629924245c21a0c7f", size = 106204, upload-time = "2026-05-19T21:30:51.862Z" }, + { url = "https://files.pythonhosted.org/packages/1a/1e/765afe97811ca35933e2a7de70ac57b1997ea2e4ee895719ee7a231fb7e5/yarl-1.24.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4da31a5512ed1729ca8d8aacde3f7faeb8843cde3165d6bcf7f88f74f17bb8aa", size = 101510, upload-time = "2026-05-19T21:30:53.62Z" }, + { url = "https://files.pythonhosted.org/packages/ee/78/393913f4b9039e1edd09ae8a9bbb9d539be909a8abf6d8a2084585bed4b7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:533ded4dceb5f1f3da7906244f4e82cf46cfd40d84c69a1faf5ac506aa65ecbe", size = 105584, upload-time = "2026-05-19T21:30:55.962Z" }, + { url = "https://files.pythonhosted.org/packages/78/87/deb17b7049bbe74ea11a713b86f8f27800cc1c8648b0b797243ebb4830ba/yarl-1.24.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7b3a85525f6e7eeabcfdd372862b21ee1915db1b498a04e8bf0e389b607ff0bd", size = 103410, upload-time = "2026-05-19T21:30:57.962Z" }, + { url = "https://files.pythonhosted.org/packages/8f/be/f9f7594e23b5b93affff0318e4593c1920331bcaefda326cabcad94296a1/yarl-1.24.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a7624b1ca46ca5d7b864ef0d2f8efe3091454085ee1855b4e992314529972215", size = 102980, upload-time = "2026-05-19T21:30:59.735Z" }, + { url = "https://files.pythonhosted.org/packages/65/a4/ba80dccd3593ff1f01051a818694d07b58cb8232677ee9a22a5a1f93a9fc/yarl-1.24.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e434a45ce2e7a947f951fc5a8944c8cc080b7e59f9c50ae80fd39107cf88126d", size = 91219, upload-time = "2026-05-19T21:31:01.934Z" }, + { url = "https://files.pythonhosted.org/packages/fd/4d/4b880086bd0d3e034d25647be1d830afc3e3f610e98c4ab3490af6b1b6d5/yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9", size = 53576, upload-time = "2026-05-19T21:31:03.909Z" }, ] [[package]] name = "zipp" -version = "3.23.1" +version = "4.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/21/093488dfc7cc8964ded15ab726fad40f25fd3d788fd741cc1c5a17d78ee8/zipp-3.23.1.tar.gz", hash = "sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110", size = 25965, upload-time = "2026-04-13T23:21:46.6Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/d8/eab98a517c14134c0b2eb4e2387bc5f457334293ec5d2dd3857ec2966802/zipp-4.1.0.tar.gz", hash = "sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602", size = 26214, upload-time = "2026-05-18T20:08:57.967Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/8a/0861bec20485572fbddf3dfba2910e38fe249796cb73ecdeb74e07eeb8d3/zipp-3.23.1-py3-none-any.whl", hash = "sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc", size = 10378, upload-time = "2026-04-13T23:21:45.386Z" }, + { url = "https://files.pythonhosted.org/packages/3a/13/547360d81e6d88d58492968ffda9f9542854f11310ee556fef14260cc886/zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f", size = 10238, upload-time = "2026-05-18T20:08:57.045Z" }, ]