diff --git a/.github/workflows/auto-pr-description.yml b/.github/workflows/auto-pr-description.yml index cf85642..0d9cb8a 100644 --- a/.github/workflows/auto-pr-description.yml +++ b/.github/workflows/auto-pr-description.yml @@ -8,6 +8,7 @@ on: - reopened branches: - release + - main jobs: force-release-pr: @@ -15,7 +16,12 @@ jobs: permissions: contents: read pull-requests: write - if: github.event.pull_request.head.ref == 'main' + if: >- + github.event_name == 'pull_request' && + !contains(github.event.pull_request.labels.*.name, 'dependencies') && ( + (github.event.pull_request.base.ref == 'release' && github.event.pull_request.head.ref == 'main') || + (github.event.pull_request.base.ref == 'main' && github.event.pull_request.head.ref != 'release') + ) steps: - uses: actions/checkout@v6 @@ -27,7 +33,7 @@ jobs: pull_request_url: ${{ github.event.pull_request.url }} api_token: ${{ secrets.GITHUB_TOKEN }} - - name: Force overwrite PR title and body + - name: Update PR description and title uses: actions/github-script@v9 env: PR_BODY: ${{ steps.gen.outputs.pull_request_description }} @@ -36,10 +42,22 @@ jobs: script: | const now = new Date().toISOString().slice(0,16).replace('T',' '); const body = process.env.PR_BODY; - await github.rest.pulls.update({ + const base = context.payload.pull_request.base.ref; + const head = context.payload.pull_request.head.ref; + const update = { owner: context.repo.owner, repo: context.repo.repo, pull_number: context.payload.pull_request.number, - title: `Release PR for ${now}`, - body: body - }); + body, + }; + + // Branch roles come from `.releaserc.cjs`: + // - base `release`: stable releases + // - base `main`: prereleases ("rc") + if (base === 'release') { + update.title = `Release! PR for ${now}`; + } else if (base === 'main') { + update.title = `PR for ${now}`; + } + + await github.rest.pulls.update(update); diff --git a/.github/workflows/lint_and_test.yaml b/.github/workflows/lint_and_test.yaml index b3ad82e..59b3862 100644 --- a/.github/workflows/lint_and_test.yaml +++ b/.github/workflows/lint_and_test.yaml @@ -15,7 +15,7 @@ jobs: contents: read strategy: matrix: - python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.11", "3.12", "3.13", "3.14"] steps: - name: Checkout uses: actions/checkout@v6 diff --git a/.github/workflows/release-stable.yaml b/.github/workflows/release-stable.yaml deleted file mode 100644 index b7d9769..0000000 --- a/.github/workflows/release-stable.yaml +++ /dev/null @@ -1,66 +0,0 @@ ---- -name: Release (stable) - -"on": - push: - branches: - - release - -jobs: - release: - name: "Release" - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - concurrency: - group: semantic-release - cancel-in-progress: false - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - token: ${{ secrets.SEMANTIC_RELEASE_TOKEN || secrets.GITHUB_TOKEN }} - - - name: Semantic release - run: | - docker run --rm \ - --user 1001 \ - -v ${{ github.workspace }}:/workspace \ - -w /workspace \ - -e GITHUB_TOKEN=${{ secrets.SEMANTIC_RELEASE_TOKEN || secrets.GITHUB_TOKEN }} \ - -e CI=true \ - ghcr.io/disafronov/semantic-release:latest - - - name: Sync release to main - if: success() - run: | - git config user.name "Release Bot" - git config user.email "noreply@github.com" - - # Fetch latest state after semantic-release pushed commits - # This ensures we get all commits that semantic-release created - git fetch origin - - # Check if main is already up to date with release - if git diff --quiet origin/main origin/release; then - echo "main is already up to date with release" - exit 0 - fi - - # Check if main is ancestor of release (can fast-forward) - if git merge-base --is-ancestor origin/main origin/release; then - echo "Fast-forwarding main to release" - git checkout -B main origin/main - git merge --ff-only origin/release - git push origin main - else - echo "Rebasing main onto release (force-with-lease required)" - git checkout -B main origin/main - git rebase origin/release - # Use --force-with-lease for safety: only push if remote hasn't changed - git push --force-with-lease origin main - fi - env: - GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/semantic.yaml b/.github/workflows/semantic.yaml index 9db05b9..e77df7e 100644 --- a/.github/workflows/semantic.yaml +++ b/.github/workflows/semantic.yaml @@ -5,6 +5,7 @@ name: Semantic pull_request: branches: - main + - release push: branches: - main diff --git a/.markdownlintignore b/.markdownlintignore new file mode 100644 index 0000000..1b763b1 --- /dev/null +++ b/.markdownlintignore @@ -0,0 +1 @@ +CHANGELOG.md diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4e06d7b..af006e5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,6 +30,12 @@ repos: args: [--allow-multiple-documents] - id: check-toml stages: [pre-commit] + - id: check-case-conflict + stages: [pre-commit] + - id: name-tests-test + stages: [pre-commit] + args: [--pytest-test-first] + exclude: ^(.*/)?tests/helpers\.py$ - repo: https://github.com/igorshubovych/markdownlint-cli rev: v0.46.0 @@ -46,13 +52,6 @@ repos: - repo: local hooks: - - id: make-format - name: make format (pre-commit) - stages: [pre-commit] - language: system - entry: make format - pass_filenames: false - - id: make-lint name: make lint (pre-commit) stages: [pre-commit] diff --git a/.releaserc.cjs b/.releaserc.cjs index 0d46f84..3b0351a 100644 --- a/.releaserc.cjs +++ b/.releaserc.cjs @@ -10,6 +10,7 @@ module.exports = { ["@semantic-release/commit-analyzer", { "preset": "conventionalcommits", "releaseRules": [ + { "breaking": true, "release": "major" }, { "type": "feat", "release": "minor" }, { "type": "fix", "release": "patch" }, { "type": "perf", "release": "patch" }, diff --git a/CHANGELOG.md b/CHANGELOG.md index 390a780..34a638a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +## [1.0.0-rc.1](https://github.com/disafronov/python-logging-objects-with-schema/compare/v0.4.2-rc.2...v1.0.0-rc.1) (2026-05-05) + +### ⚠ BREAKING CHANGES + +* first stable release, no longer considered experimental + +Signed-off-by: Dmitrii Safronov + +### Features + +* promote to stable 1.0.0 ([1fdd2ec](https://github.com/disafronov/python-logging-objects-with-schema/commit/1fdd2ec40dad460c0bddcf65c6961ef5357c878a)) + +## [0.4.2-rc.2](https://github.com/disafronov/python-logging-objects-with-schema/compare/v0.4.2-rc.1...v0.4.2-rc.2) (2026-05-05) + +### Bug Fixes + +* enforce non-empty strings for type and source fields ([21ba61d](https://github.com/disafronov/python-logging-objects-with-schema/commit/21ba61dfc7ca32de848c85f42ad39450f4bbb3cc)) + +## [0.4.2-rc.1](https://github.com/disafronov/python-logging-objects-with-schema/compare/v0.4.1...v0.4.2-rc.1) (2026-05-05) + ## [0.4.1](https://github.com/disafronov/python-logging-objects-with-schema/compare/v0.4.0...v0.4.1) (2026-04-28) ## [0.4.1-rc.1](https://github.com/disafronov/python-logging-objects-with-schema/compare/v0.4.0...v0.4.1-rc.1) (2026-04-28) diff --git a/Makefile b/Makefile index e905583..5ff3661 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,6 @@ +# Python version is pinned via `.python-version` (used by uv and CI). +PYTHON_VERSION := $(shell tr -d '[:space:]' < .python-version) + # Variables PYTEST_CMD = uv run python -m pytest -v COVERAGE_OPTS = --cov --cov-report=term-missing --cov-report=html @@ -15,7 +18,8 @@ help: ## Show this help message # Development install: ## Install dependencies @echo "Installing dependencies..." - uv sync + uv python install $(PYTHON_VERSION) + uv sync --python $(PYTHON_VERSION) @echo "Installing pre-commit hooks..." uv run pre-commit install @@ -33,16 +37,12 @@ dead-code: ## Check for dead code using vulture uv run vulture # Testing -test: ## Run tests - @echo "Running tests..." - $(PYTEST_CMD) - -test-coverage: ## Run tests with coverage +test: ## Run tests with coverage @echo "Running tests with coverage..." $(PYTEST_CMD) $(COVERAGE_OPTS) # Combined operations -all: format lint test dead-code ## Run format, lint, test, and dead-code check +all: lint test dead-code ## Run lint, test, and dead-code check @echo "All checks completed successfully!" # Maintenance diff --git a/README.md b/README.md index 0059da1..f402bd7 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,9 @@ systems) can rely on a consistent log structure. problem. - Any mismatch between runtime values and the declared types (wrong types, `None` values, disallowed list elements) is also treated as a data error. + Type checks use `type()`, not `isinstance()`: `bool` is **not** accepted + where `int` is declared, and `int` is **not** accepted where `float` is + declared. There are no implicit type conversions. - All validation problems are aggregated and logged as a single ERROR message **after** the log record has been emitted, ensuring 100% compatibility with standard logger behavior (no exceptions are raised). @@ -145,7 +148,9 @@ directory until it finds the file or reaches the filesystem root. **Important**: If there are any problems with the schema (missing file, broken JSON, invalid structure, etc.), the application is terminated after logging schema problems to stderr. Schema validation happens when the first logger -instance is created. +instance is created. Termination uses `os._exit(1)` — bypassing `atexit` +handlers and `finally` blocks — to prevent any cleanup code from running +against a broken logger that was never fully registered. **Note**: The schema is compiled once per process and cached. Schema changes require an application restart to take effect. The library is thread-safe. diff --git a/pyproject.toml b/pyproject.toml index 780f963..0c8558d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,10 +4,10 @@ build-backend = "uv_build" [project] name = "logging-objects-with-schema" -version = "0.4.1" +version = "1.0.0rc1" description = "Proxy logging wrapper that validates extra fields against a JSON schema." readme = "README.md" -requires-python = ">=3.10" +requires-python = ">=3.11" dependencies = [ ] license = "Apache-2.0" license-files = [ "LICENSE" ] @@ -20,7 +20,6 @@ keywords = [ ] classifiers = [ "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", @@ -102,6 +101,7 @@ source = [ "src" ] branch = true [tool.coverage.report] +fail_under = 100 exclude_lines = [ "def __repr__", "if self\\.debug", diff --git a/src/logging_objects_with_schema/__init__.py b/src/logging_objects_with_schema/__init__.py index d81e922..465f270 100644 --- a/src/logging_objects_with_schema/__init__.py +++ b/src/logging_objects_with_schema/__init__.py @@ -5,8 +5,6 @@ according to an application-defined JSON schema. """ -from __future__ import annotations - from .schema_logger import SchemaLogger __all__ = [ diff --git a/src/logging_objects_with_schema/errors.py b/src/logging_objects_with_schema/errors.py index b91624e..e08411b 100644 --- a/src/logging_objects_with_schema/errors.py +++ b/src/logging_objects_with_schema/errors.py @@ -1,8 +1,8 @@ """Custom exception types used by logging_objects_with_schema.""" -from __future__ import annotations - +import json from dataclasses import dataclass +from typing import Any @dataclass @@ -60,15 +60,12 @@ class _DataProblem: - Redundant fields (fields not defined in the schema) Attributes: - message: JSON string containing structured error information. The message - is always a valid JSON object with the following structure: - ``{"field": "...", "error": "...", "value": "..."}`` - All values are serialized via ``repr()`` for safety and consistency. - Examples: - - ``{"field": "'user_id'", "error": "'has type str, expected int'", "value": "'abc-123'"}`` - - ``{"field": "'request_id'", "error": "'is None'", "value": "None"}`` - - ``{"field": "'tags'", "error": "'is a list but contains elements with types dict; expected all elements to be of type str'", "value": "[{'key': 'color'}]"}`` # noqa: E501 - - ``{"field": "'unknown_field'", "error": "'is not defined in schema'", "value": "'some_value'"}`` + data: Dict containing structured error information with keys + ``field``, ``error``, and ``value`` (all via ``repr()``). """ - message: str + data: dict[str, Any] + + @property + def message(self) -> str: + return json.dumps(self.data) diff --git a/src/logging_objects_with_schema/schema_applier.py b/src/logging_objects_with_schema/schema_applier.py index 74ff80b..487dbd2 100644 --- a/src/logging_objects_with_schema/schema_applier.py +++ b/src/logging_objects_with_schema/schema_applier.py @@ -4,10 +4,6 @@ to user-provided extra fields, used by SchemaLogger. """ -from __future__ import annotations - -import json -from collections import defaultdict from collections.abc import Mapping, MutableMapping from typing import Any @@ -15,14 +11,11 @@ from .schema_loader import _CompiledSchema, _SchemaLeaf -def _create_validation_error_json(field: str, error: str, value: Any) -> str: - """Create JSON string for a single validation error. +def _create_validation_error_dict(field: str, error: str, value: Any) -> dict[str, Any]: + """Create a dict for a single validation error. - All values are wrapped in repr() before JSON serialization. This ensures: - - Any value type can be safely serialized (even non-JSON-serializable types) - - The error message always contains a valid Python representation of the value - - Security: prevents issues with special characters or control sequences - - Consistency: all error messages have the same format regardless of value type + All values are wrapped in repr() to ensure any type is representable + and special characters cannot cause issues downstream. Args: field: Field name that caused the validation error. @@ -30,15 +23,13 @@ def _create_validation_error_json(field: str, error: str, value: Any) -> str: value: Invalid value that caused the error. Returns: - JSON string with field, error, and value (all via repr() for safety). + Dict with field, error, and value (all via repr() for safety). """ - return json.dumps( - { - "field": repr(field), - "error": repr(error), - "value": repr(value), - } - ) + return { + "field": repr(field), + "error": repr(error), + "value": repr(value), + } def _validate_list_value( @@ -62,16 +53,12 @@ def _validate_list_value( """ if item_expected_type is None: error_msg = "is a list but has no item type configured" - return _DataProblem(_create_validation_error_json(source, error_msg, value)) + return _DataProblem(_create_validation_error_dict(source, error_msg, value)) if len(value) == 0: - # Empty lists are always valid return None - # Collect unique type names of items that don't match the expected type. - # We use a set comprehension to get unique type names (not the types themselves) - # for the error message. This gives a clear, readable error message showing - # which types were found (e.g., "int, str") vs what was expected. + # __name__ gives "int, str" in error messages instead of "". invalid_item_types = { type(item).__name__ for item in value if type(item) is not item_expected_type } @@ -83,7 +70,7 @@ def _validate_list_value( f"expected all elements to be of type " f"{item_expected_type.__name__}" ) - return _DataProblem(_create_validation_error_json(source, error_msg, value)) + return _DataProblem(_create_validation_error_dict(source, error_msg, value)) return None @@ -104,22 +91,14 @@ def _set_nested_value( value: The value to set at the target location. """ current = target - # Navigate through intermediate dictionaries, creating them as needed. - # We iterate through all keys except the last one (path[:-1]) to build - # the nested structure. for key in path[:-1]: child = current.get(key) - # If the key doesn't exist or exists but is not a dict, create a new dict. - # This overwrites any non-dict value that might have been there (which - # shouldn't happen in normal operation, but we handle it defensively). - # We use isinstance() instead of checking for None because we need to - # ensure the value is actually a dict, not just that the key exists. + # isinstance catches both missing keys and non-dict values at the same key. if not isinstance(child, dict): child = {} current[key] = child current = child - # Set the final value at the last key in the path current[path[-1]] = value @@ -142,24 +121,18 @@ def _validate_and_apply_leaf( extra: The target dictionary to write the value to if validation passes. problems: List to append validation problems to. """ - # Use strict type checking (type() is) instead of isinstance() to - # prevent bool values from passing validation for int types (since - # bool is a subclass of int). This ensures that the actual - # runtime type matches the schema type exactly. + # type() is, not isinstance(): bool subclasses int and must not pass int validation. if type(value) is not leaf.expected_type: error_msg = ( f"has type {type(value).__name__}, " f"expected {leaf.expected_type.__name__}" ) problems.append( - _DataProblem(_create_validation_error_json(source, error_msg, value)) + _DataProblem(_create_validation_error_dict(source, error_msg, value)) ) return - # For lists, validate that all elements strictly match the declared - # item_expected_type (homogeneous primitive list). - # Note: isinstance() check is needed for type narrowing (mypy), even though - # type(value) is list is already guaranteed by the check above. + # isinstance() for mypy narrowing; type(value) is list is already guaranteed above. if leaf.expected_type is list and isinstance(value, list): list_problem = _validate_list_value(value, source, leaf.item_expected_type) if list_problem is not None: @@ -197,8 +170,12 @@ def _strip_empty(node: Any) -> Any: The cleaned structure with empty dicts and None values removed. """ if isinstance(node, dict): - cleaned = {k: _strip_empty(v) for k, v in node.items()} - return {k: v for k, v in cleaned.items() if v != {} and v is not None} + result: dict[str, Any] = {} + for k, v in node.items(): + v = _strip_empty(v) + if v != {} and v is not None: + result[k] = v + return result return node @@ -274,63 +251,31 @@ def _apply_schema_internal( extra: dict[str, Any] = {} problems: list[_DataProblem] = [] - # Group leaves by source field name. This is necessary because a single source - # can be referenced by multiple leaves (allowing the same value to appear in - # different locations in the output structure). Grouping allows us to process - # all leaves for a given source together, which is more efficient and allows - # us to validate the value once per source (e.g., checking for None) rather - # than once per leaf. - source_to_leaves: dict[str, list[_SchemaLeaf]] = defaultdict(list) - for leaf in compiled.leaves: - source_to_leaves[leaf.source].append(leaf) - - used_sources = set(source_to_leaves.keys()) - - # Process each source that appears in the schema. If a source is missing from - # extra_values, we silently skip it (this is normal - not all sources need to - # be present in every log call). We only validate and apply sources that are - # actually provided. - for source, leaves in source_to_leaves.items(): + for source, leaves in compiled.source_to_leaves.items(): if source not in extra_values: - # Source not provided - this is normal, not an error. Skip it. continue value = extra_values[source] - # Check for None values explicitly. None is never allowed for any type, - # so we check it once per source (not once per leaf) before attempting - # type-specific validation. This avoids redundant checks when a source - # is used by multiple leaves. if value is None: - error_msg = "is None" problems.append( - _DataProblem(_create_validation_error_json(source, error_msg, None)) + _DataProblem(_create_validation_error_dict(source, "is None", None)) ) continue - # Validate the value against each leaf that references this source. - # Each leaf validates independently, so a value might pass validation - # for some leaves (where type matches) but fail for others (where type - # doesn't match). The value is written only to locations where validation - # succeeds. for leaf in leaves: _validate_and_apply_leaf(leaf, value, source, extra, problems) - # Report redundant fields: any keys in extra_values that are not referenced - # by any schema leaf. These are fields that the user provided but which are - # not defined in the schema, so they cannot be included in the log output. - # Optimization: if schema is empty (no used_sources), all fields are redundant, - # so we can skip the membership check for each key. redundant_keys = ( extra_values.keys() - if not used_sources - else (key for key in extra_values.keys() if key not in used_sources) + if not compiled.known_sources + else (key for key in extra_values.keys() if key not in compiled.known_sources) ) for key in redundant_keys: error_msg = "is not defined in schema" problems.append( _DataProblem( - _create_validation_error_json(key, error_msg, extra_values[key]) + _create_validation_error_dict(key, error_msg, extra_values[key]) ) ) diff --git a/src/logging_objects_with_schema/schema_loader.py b/src/logging_objects_with_schema/schema_loader.py index f51757b..7fb7061 100644 --- a/src/logging_objects_with_schema/schema_loader.py +++ b/src/logging_objects_with_schema/schema_loader.py @@ -6,15 +6,13 @@ emitted and which Python types they must have. """ -from __future__ import annotations - import functools import json import logging import os import threading from collections.abc import Iterable, Mapping, MutableMapping -from dataclasses import dataclass +from dataclasses import dataclass, field from pathlib import Path from typing import Any, Literal @@ -55,9 +53,32 @@ class _CompiledSchema: This class is part of the internal implementation and is not considered a public API. Its signature and behaviour may change between releases without preserving backward compatibility. + + Attributes: + leaves: Flat list of all schema leaves. This is the only constructor + argument; all other attributes are derived from it in + ``__post_init__``. + source_to_leaves: Maps each source field name to the list of leaves + that read from it. Populated by ``__post_init__``. + known_sources: Frozenset of all source field names appearing in the + schema. Used for O(1) redundant-field detection during validation. + Populated by ``__post_init__``. """ leaves: list[_SchemaLeaf] + source_to_leaves: dict[str, list[_SchemaLeaf]] = field( + default_factory=dict, init=False, repr=False, compare=False + ) + known_sources: frozenset[str] = field( + default_factory=frozenset, init=False, repr=False, compare=False + ) + + def __post_init__(self) -> None: + source_map: dict[str, list[_SchemaLeaf]] = {} + for leaf in self.leaves: + source_map.setdefault(leaf.source, []).append(leaf) + self.source_to_leaves = source_map + self.known_sources = frozenset(source_map) @property def is_empty(self) -> bool: @@ -103,7 +124,7 @@ def _create_empty_compiled_schema_with_problems( # 2. Compiled schema cache (_SCHEMA_CACHE): Caches the compiled schema and # validation problems for a given schema file path. This avoids re-parsing # and re-compiling the schema JSON on every logger creation. The cache key -# is the absolute schema file path from the path cache. +# is a tuple of (absolute schema file path, frozenset of forbidden_keys). # # These caches work together: path cache finds the file location, compiled # cache stores the result of compiling that file. Both are thread-safe and @@ -126,9 +147,10 @@ def _create_empty_compiled_schema_with_problems( # was not found. _cached_cwd: Path | None = None # RLock for thread-safe access to path cache variables. -# RLock (not Lock) is needed because helper functions (_check_cached_found_file_path, -# _check_cached_missing_file_path) are called from _get_schema_path() which already -# holds the lock, and these helpers also need to acquire the lock. +# RLock (not Lock) allows the same thread to acquire the lock multiple times without +# deadlocking, which guards against any future refactoring where a helper might +# independently acquire the lock. Currently, helpers are always called while the +# caller already holds the lock and do not re-acquire it themselves. _path_cache_lock = threading.RLock() @@ -147,15 +169,11 @@ def _find_schema_file() -> Path | None: while True: schema_path = current / _SCHEMA_FILE_NAME if schema_path.exists(): - # Use resolve() to get an absolute path and resolve any symbolic links. - # This ensures we have a canonical path that can be used as a cache key - # and for consistent error reporting. + # resolve() canonicalizes symlinks so the path is stable as a cache key. return schema_path.resolve() - # Move to parent directory parent = current.parent if parent == current: - # Reached filesystem root, stop searching break current = parent @@ -187,18 +205,13 @@ def _check_cached_found_file_path() -> Path | None: if _resolved_schema_path is None: return None - # Check if this is actually a found file cache (not missing file cache) - # by checking _cached_cwd. We don't need 'global' here since we only read it. + # _cached_cwd being non-None means we cached a missing-file path, not a found one. if _cached_cwd is not None: - # This path was cached as missing file, not found file return None - # Schema file was found, absolute path doesn't depend on CWD - # Return it if it still exists if _resolved_schema_path.exists(): return _resolved_schema_path - # Cached file no longer exists; invalidate cache so caller will re-search _resolved_schema_path = None return None @@ -219,16 +232,12 @@ def _check_cached_missing_file_path() -> Path | None: if _resolved_schema_path is None or _cached_cwd is None: return None - # Cached path is based on CWD when file was not found, - # check if CWD changed current_cwd = _get_current_working_directory() if current_cwd != _cached_cwd: - # CWD changed, invalidate cache and re-search from new CWD _resolved_schema_path = None _cached_cwd = None return None - # CWD unchanged, return cached path return _resolved_schema_path @@ -284,27 +293,22 @@ def _get_schema_path() -> Path: Absolute path where the schema file is located or expected to be. """ with _path_cache_lock: - # Check cached path for missing file first (CWD-dependent) - # This must be checked before found file cache, because - # _check_cached_found_file_path would invalidate the cache if file - # doesn't exist, even for missing file cache. + # Missing-file cache checked first: _check_cached_found_file_path would + # invalidate it on existence check failure if we called it first. if _cached_cwd is not None: cached_path = _check_cached_missing_file_path() if cached_path is not None: return cached_path - # Check cached path for found file (CWD-independent) cached_path = _check_cached_found_file_path() if cached_path is not None: return cached_path - # Search for schema file found_path = _find_schema_file() if found_path is not None: return _cache_and_return_found_path(found_path) - # Schema file not found, return absolute path in current working directory - # (this path may not exist, but allows caller to report proper error) + # Return expected path even if missing — callers use it in error messages. return _cache_and_return_missing_path() @@ -333,18 +337,14 @@ def _load_raw_schema(schema_path: Path) -> tuple[dict[str, Any], Path]: Tuple of (schema data, schema file path). """ if not schema_path.exists(): - # Let the caller decide how to report this. raise FileNotFoundError(f"Schema file not found: {schema_path}") try: with schema_path.open("r", encoding="utf-8") as f: data = json.load(f) except OSError as exc: - # Normalise I/O errors when reading the schema file (e.g., permission - # denied, file not found) to ValueError so that _compile_schema_internal() - # can report them as _SchemaProblem instances instead of leaking raw - # OSError to callers. Note: System-level OSError (e.g., from os.getcwd()) - # is not caught here and propagates directly. + # Converted to ValueError so _compile_schema_internal treats it as a + # _SchemaProblem. System-level OSError (e.g. os.getcwd()) propagates uncaught. raise ValueError( f"Failed to read schema file {schema_path}: {exc}", ) from exc @@ -352,8 +352,7 @@ def _load_raw_schema(schema_path: Path) -> tuple[dict[str, Any], Path]: raise ValueError(f"Failed to parse JSON schema: {exc}") from exc if not isinstance(data, dict): - # Normalise non-object top-level schemas into a ValueError so that the - # caller can report it as a _SchemaProblem while keeping type safety. + # ValueError lets the caller treat a non-object top level the same as bad JSON. raise ValueError("Top-level schema must be a JSON object") return data, schema_path @@ -427,27 +426,19 @@ def _determine_node_type_and_validate( source_value = value_dict.get("source") item_type_value = value_dict.get("item_type") - # Check if node has leaf properties - # Leaf properties are: type (required, string), source (required, string), - # item_type (optional, string). If type/source/item_type are objects, - # they are children, not properties + # type/source/item_type as objects means children, not leaf properties. has_leaf_properties = ( isinstance(type_value, str) or isinstance(source_value, str) or isinstance(item_type_value, str) ) - # Check if node has children (any field that is an object/Mapping) - # Children can have ANY names, including type, source, item_type - this is - # valid for inner nodes. If type, source, or item_type are objects, - # they count as children + # Any field whose value is an object is a child, even if named "type" or "source". has_children = any( isinstance(field_value, Mapping) for field_value in value_dict.values() ) - # Validate node structure if has_leaf_properties and has_children: - # Node cannot have both properties and children problems.append( _SchemaProblem( f"Invalid node at {_format_path(path, key)}: " @@ -458,7 +449,6 @@ def _determine_node_type_and_validate( return (None, False) if not has_leaf_properties and not has_children: - # Node must be either a leaf or have children problems.append( _SchemaProblem( f"Invalid node at {_format_path(path, key)}: " @@ -468,49 +458,12 @@ def _determine_node_type_and_validate( ) return (None, False) - # Node is valid - determine type if has_leaf_properties: return ("leaf", True) else: # has_children return ("inner", True) -def _is_leaf_node(value_dict: dict[str, Any]) -> bool: - """Check if a schema node is a leaf node. - - A leaf node is identified by having at least one of 'type' or 'source' fields - that is a string. If 'type' or 'source' are themselves objects (not strings), - they are child nodes, not leaf properties. - - Inner nodes have either no 'type'/'source' fields, or have these fields as - objects (child nodes) rather than strings. - - We use `.get()` with `is not None` check instead of `in` operator because: - - A field might be present but have a None value (which indicates an error) - - We need to distinguish between "field not present" (inner node) and - "field present but None" (invalid leaf node that will be caught later) - - Args: - value_dict: Dictionary containing node data. - - Returns: - True if the node is a leaf, False if it's an inner node. - """ - type_value = value_dict.get("type") - source_value = value_dict.get("source") - - # If either field is an object (Mapping), this is an inner node, not a leaf - if isinstance(type_value, Mapping) or isinstance(source_value, Mapping): - return False - - # Check if at least one field is present and is a string - if isinstance(type_value, str) or isinstance(source_value, str): - return True - - # Neither field is present as a string - this is an inner node - return False - - def _validate_and_create_leaf( value_dict: dict[str, Any], path: tuple[str, ...], @@ -531,17 +484,14 @@ def _validate_and_create_leaf( leaf_type = value_dict.get("type") leaf_source = value_dict.get("source") - # This is supposed to be a leaf - validate required fields first. - # Type must be a string (not None, not empty, and not other types like bool/int) type_invalid = _is_empty_or_none(leaf_type) or not isinstance(leaf_type, str) - # Source must be a string (not None, not empty, and not other types like bool/int) source_invalid = _is_empty_or_none(leaf_source) or not isinstance(leaf_source, str) if type_invalid: problems.append( _SchemaProblem( f"Incomplete leaf at {_format_path(path, key)}: " - f"type cannot be None or empty", + f"type must be a non-empty string", ), ) @@ -549,19 +499,14 @@ def _validate_and_create_leaf( problems.append( _SchemaProblem( f"Incomplete leaf at {_format_path(path, key)}: " - f"source cannot be None or empty", + f"source must be a non-empty string", ), ) if type_invalid or source_invalid: return None - # Note: Check for children is done in _determine_node_type_and_validate() - # before this function is called, so we don't need to check here. - - # Convert to string before lookup to handle cases where the JSON parser - # might return non-string types (though this shouldn't happen with valid JSON). - # This ensures type safety and consistent behavior. + # Children check is upstream in _determine_node_type_and_validate. expected_type = _TYPE_MAP.get(str(leaf_type)) if expected_type is None: problems.append( @@ -572,12 +517,8 @@ def _validate_and_create_leaf( return None item_expected_type: type | None = None - # For list-typed leaves we require an explicit, primitive item_type - # to ensure element homogeneity (e.g. list[str], list[int]). if expected_type is list: item_type_name = value_dict.get("item_type") - # Item type must be a string (not None, not empty, and not other types - # like bool/int) item_type_invalid = _is_empty_or_none(item_type_name) or not isinstance( item_type_name, str ) @@ -585,14 +526,12 @@ def _validate_and_create_leaf( problems.append( _SchemaProblem( f"Incomplete leaf at {_format_path(path, key)}: " - f"item_type is required for list type and " - f"cannot be None or empty", + f"item_type must be a non-empty string", ), ) return None item_expected_type = _TYPE_MAP.get(str(item_type_name)) - # Item type must be a primitive (str, int, float, bool), not list if item_expected_type is None or item_expected_type is list: problems.append( _SchemaProblem( @@ -605,9 +544,6 @@ def _validate_and_create_leaf( return _SchemaLeaf( path=path + (key,), - # Convert to string to ensure type consistency. Even though source should - # be a string from JSON, this guards against unexpected types and ensures - # the _SchemaLeaf always has a string source. source=str(leaf_source), expected_type=expected_type, item_expected_type=item_expected_type, @@ -646,10 +582,7 @@ def _compile_schema_tree( Yields: _SchemaLeaf objects found in the tree. """ - # Check for excessive nesting depth (DoS protection: prevent deeply nested - # schemas that could cause stack overflow or excessive memory usage). - # This check happens before processing the node to avoid unnecessary work - # and to provide clear error messages about the problematic path. + # DoS guard: deeply nested schemas could overflow the call stack. if len(path) > MAX_SCHEMA_DEPTH: problems.append( _SchemaProblem( @@ -668,30 +601,21 @@ def _compile_schema_tree( ) continue - # Create a mutable copy of the value dict. This is necessary because - # we may need to modify it during processing, and the original value - # might be a read-only Mapping (e.g., from JSON parsing). + # dict() because the original Mapping may be read-only. value_dict = dict(value) - # Determine node type and validate structure - # (checks for mixed nodes, empty nodes, etc.) node_type, is_valid = _determine_node_type_and_validate( value_dict, path, key, problems ) if not is_valid: - # Node has validation errors, skip processing but continue with other nodes continue if node_type == "leaf": - # Process as leaf node leaf = _validate_and_create_leaf(value_dict, path, key, problems) if leaf is not None: yield leaf else: # node_type == "inner" - # Process as inner node - recurse into children - # Note: node_type can only be "leaf" or "inner" when is_valid is True. - # If is_valid is False, we already continue above. for child_leaf in _compile_schema_tree(value_dict, path + (key,), problems): yield child_leaf @@ -728,9 +652,7 @@ def _get_builtin_logrecord_attributes() -> set[str]: >>> "ServicePayload" in forbidden False """ - # Create a minimal LogRecord instance to inspect its attributes - # This is necessary because LogRecord attributes are not defined as - # class attributes but are set in __init__ + # LogRecord attributes are set in __init__, not on the class — must instantiate. record = logging.LogRecord( name="", level=0, @@ -741,10 +663,6 @@ def _get_builtin_logrecord_attributes() -> set[str]: exc_info=None, ) - # Use dir() to get all attributes, then filter out: - # - Private attributes (starting with _) - # - Callable attributes (methods) - # This leaves only data fields that represent actual LogRecord attributes forbidden = set() for attr_name in dir(record): if attr_name.startswith("_"): @@ -772,13 +690,8 @@ def _check_root_conflicts( Note: None and empty set() are semantically equivalent - both mean "no additional forbidden keys" and produce the same result. """ - # Builtin keys always present, cannot be replaced forbidden_root_keys = _get_builtin_logrecord_attributes() - # Merge with additional forbidden keys if provided. - # We use `if forbidden_keys:` instead of `if forbidden_keys is not None:` because - # both None and empty set() semantically mean "no additional forbidden keys". - # This check treats them equivalently: None is falsy, and empty set() is also - # falsy, so both cases skip the merge operation, which is the correct behavior. + # if forbidden_keys: treats None and empty set() equally — both mean no additions. if forbidden_keys: forbidden_root_keys = forbidden_root_keys | forbidden_keys @@ -848,31 +761,15 @@ def _compile_schema_internal( Tuple of (_CompiledSchema, list[_SchemaProblem]). """ schema_path = _get_schema_path() - # Create cache key that includes forbidden keys. - # We use `frozenset(forbidden_keys or ())` instead of checking for None explicitly - # because both None and empty set() should produce the same cache key (frozenset()). - # This is semantically correct: both None and set() mean "no additional forbidden - # keys", so they should share the same cached compilation result. The `or ()` - # operator converts None to empty tuple, which frozenset() then converts to empty - # frozenset, and empty set() also becomes empty frozenset, ensuring cache key - # consistency. + # frozenset(forbidden_keys or ()) treats None and set() as the same cache key. cache_key = (schema_path, frozenset(forbidden_keys or ())) - # Fast-path: First check (with lock for thread-safety) if we have already attempted - # to compile schema for this path and forbidden keys set. This provides thread-safe - # cache access in the common case when the schema is already cached. + # Fast path: check cache under lock before doing expensive compilation. with _cache_lock: cached = _SCHEMA_CACHE.get(cache_key) if cached is not None: return cached - # Schema not in cache. We need to compile it. However, between the check above - # and now, another thread might have started (or even finished) compiling the - # same schema. We use double-checked locking to handle this race condition: - # after doing the expensive work (loading/compiling), we check the cache again - # while holding the lock. If another thread already compiled it, we use that - # result instead of storing our own (which might be different if the file changed). - problems: list[_SchemaProblem] = [] try: @@ -880,9 +777,7 @@ def _compile_schema_internal( except (FileNotFoundError, ValueError) as exc: problems.append(_SchemaProblem(str(exc))) result = _create_empty_compiled_schema_with_problems(problems) - # Double-checked locking: Check cache again while holding lock. Another - # thread might have compiled the schema (or handled the same error) while - # we were processing the exception. If so, use the cached result. + # DCL: another thread may have stored a result while we handled the error. with _cache_lock: cached = _SCHEMA_CACHE.get(cache_key) if cached is not None: @@ -890,13 +785,8 @@ def _compile_schema_internal( _SCHEMA_CACHE[cache_key] = result return result - # Check root key conflicts before compiling the tree. This allows us to - # catch conflicts early and report them as schema problems. We do this - # before tree compilation to avoid unnecessary work if there are conflicts. _check_root_conflicts(raw_schema, problems, forbidden_keys) - # Compile the schema tree into leaves. Each root key becomes a separate - # tree that we compile recursively. Problems are collected as we go. leaves: list[_SchemaLeaf] = [] for key, value in raw_schema.items(): if not isinstance(value, Mapping): @@ -905,20 +795,14 @@ def _compile_schema_internal( ) continue - # Create a mutable copy of the value dict for recursive compilation. - # This ensures we can safely process nested structures without modifying - # the original schema dictionary. + # dict() because the original Mapping may be read-only. for leaf in _compile_schema_tree(dict(value), (key,), problems): leaves.append(leaf) compiled = _CompiledSchema(leaves=leaves) result = (compiled, problems) - # Double-checked locking: Check cache again while holding lock. Another thread - # might have compiled the schema while we were doing the expensive compilation - # work (parsing JSON, validating, building leaves). If so, use the cached result - # instead of overwriting it with our own (which might be different if the file - # was modified between reads). + # DCL: another thread may have stored a result while we compiled. with _cache_lock: cached = _SCHEMA_CACHE.get(cache_key) if cached is not None: diff --git a/src/logging_objects_with_schema/schema_logger.py b/src/logging_objects_with_schema/schema_logger.py index 8adba12..3b4fbf8 100644 --- a/src/logging_objects_with_schema/schema_logger.py +++ b/src/logging_objects_with_schema/schema_logger.py @@ -7,9 +7,6 @@ logger behavior. """ -from __future__ import annotations - -import inspect import json import logging import os @@ -21,11 +18,6 @@ from .schema_applier import _apply_schema_internal from .schema_loader import _compile_schema_internal, _CompiledSchema -# Python 3.11+ has improved findCaller() implementation with proper stacklevel support. -# For Python < 3.11, we use inspect.stack() as a fallback due to known issues with -# findCaller() and stacklevel parameter. -_USE_FINDCALLER = sys.version_info >= (3, 11) - def _log_schema_problems_and_exit(problems: list[_SchemaProblem]) -> None: """Log schema problems to stderr and terminate the application. @@ -39,13 +31,12 @@ def _log_schema_problems_and_exit(problems: list[_SchemaProblem]) -> None: Args: problems: List of schema problems to log. """ - # Format error message with details of all problems - # (same format as data problems) problem_messages = [problem.message for problem in problems] error_msg = f"Schema has problems: {'; '.join(problem_messages)}\n" sys.stderr.write(error_msg) sys.stderr.flush() - # Use os._exit() for immediate termination without cleanup handlers + # os._exit skips atexit/finally — prevents cleanup code from touching a + # broken logger that was never fully registered. os._exit(1) @@ -94,35 +85,20 @@ def __init__( Note: None and empty set() are semantically equivalent - both mean "no additional forbidden keys" and produce the same result. """ - # Validate schema before creating the logger instance to avoid - # registering a broken logger in the logging manager cache. - # Schema is compiled and cached first, then problems are checked. + # Compile before super().__init__() to avoid registering a broken logger + # in logging's manager cache. try: compiled, problems = _compile_schema_internal(forbidden_keys) except (OSError, ValueError, RuntimeError) as exc: - # Convert system-level exceptions to _SchemaProblem so they can be - # handled the same way as schema validation problems. - # - OSError: system-level file system issues (e.g., os.getcwd() failures - # when the current working directory is inaccessible or deleted). - # Note: OSError that occurs when reading the schema file (e.g., permission - # denied, I/O errors) is converted to _SchemaProblem in _load_raw_schema() - # and does not reach this exception handler. - # - ValueError: path resolution issues (e.g., invalid path characters, - # malformed paths during schema file discovery) - # - RuntimeError: threading issues (e.g., lock acquisition problems) - # Note: JSON parsing and schema structure validation errors are - # converted to _SchemaProblem instances and do not raise ValueError here. - # Note: System exceptions (KeyboardInterrupt, SystemExit) are not - # caught, which is the correct behavior. + # OSError: os.getcwd() failed (inaccessible CWD). File-level OSError is + # already converted to _SchemaProblem inside _load_raw_schema and won't + # reach here. ValueError: path resolution. RuntimeError: lock failure. problems = [_SchemaProblem(f"Schema compilation failed: {exc}")] compiled = _CompiledSchema(leaves=[]) if problems: - # Schema is invalid; log problems and terminate without creating - # the logger instance. _log_schema_problems_and_exit(problems) - # Schema is valid; create the logger instance. super().__init__(name, level) self._schema: _CompiledSchema = compiled @@ -158,12 +134,7 @@ def _log( extra or {}, ) - # Emit the main log record first, even if there are validation problems. - # This ensures 100% compatibility with standard logger behavior: the user's - # log message is always emitted, and validation errors are reported separately - # as additional ERROR messages. This approach guarantees that the application - # continues to work normally even when validation problems occur (no exceptions - # are raised, no log records are lost). + # Emit user's message first — validation errors follow as separate ERRORs. super()._log( level, msg, @@ -171,97 +142,21 @@ def _log( exc_info=exc_info, extra=structured_extra, stack_info=stack_info, - # Increment stacklevel to account for this override frame so that - # caller information points to user code instead of SchemaLogger._log. - stacklevel=stacklevel + 1, + stacklevel=stacklevel + 1, # +1 skips this override frame ) - # If there were validation problems, log them as separate ERROR messages - # after the main log record has been emitted. This ensures the main message - # is always logged first, and validation errors are clearly separated. if data_problems: - # Get caller information for the error log record so that validation - # errors point to the same location in user code as the original log call. - # Python 3.11+ has improved findCaller() with proper stacklevel support, - # so we use it as the primary method for better performance. - # For Python < 3.11, we fall back to inspect.stack() due to known issues - # with findCaller() and stacklevel parameter. - if _USE_FINDCALLER: - # Use findCaller() for Python 3.11+ (more efficient) - fn, lno, func, sinfo = self.findCaller( - stack_info=False, stacklevel=stacklevel + 1 - ) - else: - # Fallback to inspect.stack() for Python < 3.11 - # The stack looks like: - # - Frame 0: this function (_log) - # - Frame 1: logger.info() wrapper - # - Frame 2: actual caller (test_function) - # We need to skip frame 0 (this function) and frame 1 - # (logger.info wrapper), so we use stacklevel + 1 to get to - # the actual caller. - stack = inspect.stack() - frame_idx = ( - stacklevel + 1 - ) # Skip this function (0) + logger.info wrapper (1) - if frame_idx < len(stack): - frame = stack[frame_idx] - fn = frame.filename - lno = frame.lineno - func = frame.function - sinfo = None - else: - # Fallback to findCaller if stack is shorter than expected - fn, lno, func, sinfo = self.findCaller( - stack_info=False, stacklevel=stacklevel + 1 - ) - # Format error message as JSON for machine processing. - # Each _DataProblem.message is already a JSON string (created by - # _create_validation_error_json) with structure: - # {"field": "...", "error": "...", "value": "..."} - # We parse them back to dicts and combine into a single JSON object - # with all validation errors. The final structure is: - # {"validation_errors": [{"field": "...", "error": "...", - # "value": "..."}, ...]} - # This allows consumers to parse the error message as structured data - # and programmatically extract field names, error types, and values. - validation_errors = [] - for problem in data_problems: - try: - # Parse the JSON string back to a dict so we can combine - # all errors into a single JSON object. Each error object - # maintains the same structure: field, error, value - # (all via repr()). - error_obj = json.loads(problem.message) - validation_errors.append(error_obj) - except (json.JSONDecodeError, TypeError) as exc: - # Defensive handling: if problem.message is not valid JSON, - # create a fallback error object. This should never happen in - # normal operation since problem.message is always created via - # _create_validation_error_json, but protects against unexpected - # data corruption or manual _DataProblem creation. The fallback - # preserves the same structure (field, error, value) for - # consistency. - validation_errors.append( - { - "field": repr("unknown"), - "error": repr(f"Failed to parse validation error: {exc}"), - "value": repr(problem.message), - } - ) + # stack_info=False: stack trace already attached to the main record above. + fn, lno, func, sinfo = self.findCaller( + stack_info=False, stacklevel=stacklevel + 1 + ) + validation_errors = [problem.data for problem in data_problems] try: - # Combine all validation errors into a single JSON object. - # The resulting string will be used as the log message for the - # ERROR record, allowing structured parsing by log consumers. error_msg = json.dumps({"validation_errors": validation_errors}) except (TypeError, ValueError) as exc: - # Defensive handling: if serialization fails, create a fallback - # error message. This should never happen in normal operation since - # validation_errors contains only dicts with primitive values (all - # values are already serialized via repr()), but protects against - # unexpected data corruption or edge cases in JSON serialization. - # The fallback ensures we always have a valid JSON error message. + # Should never happen: repr() in _create_validation_error_dict + # guarantees all values are JSON-serializable strings. error_msg = json.dumps( { "validation_errors": [ diff --git a/tests/conftest.py b/tests/conftest.py index f11696f..75ba783 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,5 @@ """Shared fixtures for tests.""" -from __future__ import annotations - import pytest from logging_objects_with_schema import schema_loader @@ -9,11 +7,15 @@ @pytest.fixture(autouse=True) def clear_schema_cache() -> None: - """Clear schema path cache before each test to ensure test isolation.""" - with schema_loader._path_cache_lock: - schema_loader._resolved_schema_path = None - schema_loader._cached_cwd = None + """Clear all schema caches before each test to ensure test isolation.""" + + def _clear() -> None: + with schema_loader._path_cache_lock: + schema_loader._resolved_schema_path = None + schema_loader._cached_cwd = None + with schema_loader._cache_lock: + schema_loader._SCHEMA_CACHE.clear() + + _clear() yield - with schema_loader._path_cache_lock: - schema_loader._resolved_schema_path = None - schema_loader._cached_cwd = None + _clear() diff --git a/tests/helpers.py b/tests/helpers.py index d09090b..84ad28a 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -1,7 +1,5 @@ """Helper functions for tests.""" -from __future__ import annotations - import json from pathlib import Path diff --git a/tests/private/test_errors.py b/tests/private/test_errors.py index c000f8f..cc85b50 100644 --- a/tests/private/test_errors.py +++ b/tests/private/test_errors.py @@ -1,7 +1,5 @@ """Tests for errors module private classes.""" -from __future__ import annotations - import json from logging_objects_with_schema.errors import _DataProblem, _SchemaProblem @@ -41,18 +39,17 @@ def test_schema_problem_inequality() -> None: def test_data_problem_creation() -> None: - """_DataProblem should be created with message attribute.""" - message = '{"field": "test", "error": "test error", "value": "test value"}' - problem = _DataProblem(message) - assert problem.message == message + """_DataProblem should be created with data attribute.""" + data = {"field": "test", "error": "test error", "value": "test value"} + problem = _DataProblem(data) + assert problem.data == data def test_data_problem_message_is_valid_json() -> None: - """_DataProblem.message should be a valid JSON string.""" - message = '{"field": "test_field", "error": "test error", "value": "test value"}' - problem = _DataProblem(message) + """_DataProblem.message should return a valid JSON string.""" + data = {"field": "test_field", "error": "test error", "value": "test value"} + problem = _DataProblem(data) - # Should not raise exception parsed = json.loads(problem.message) assert isinstance(parsed, dict) assert "field" in parsed @@ -62,8 +59,8 @@ def test_data_problem_message_is_valid_json() -> None: def test_data_problem_message_structure() -> None: """_DataProblem.message should have correct JSON structure.""" - message = '{"field": "user_id", "error": "expected int", "value": "abc"}' - problem = _DataProblem(message) + data = {"field": "user_id", "error": "expected int", "value": "abc"} + problem = _DataProblem(data) parsed = json.loads(problem.message) assert parsed["field"] == "user_id" @@ -72,33 +69,30 @@ def test_data_problem_message_structure() -> None: def test_data_problem_equality() -> None: - """_DataProblem instances with same message should be equal.""" - message = '{"field": "test", "error": "error", "value": "value"}' - problem1 = _DataProblem(message) - problem2 = _DataProblem(message) + """_DataProblem instances with same data should be equal.""" + data = {"field": "test", "error": "error", "value": "value"} + problem1 = _DataProblem(data) + problem2 = _DataProblem(data) assert problem1 == problem2 def test_data_problem_inequality() -> None: - """_DataProblem instances with different messages should not be equal.""" - message1 = '{"field": "field1", "error": "error1", "value": "value1"}' - message2 = '{"field": "field2", "error": "error2", "value": "value2"}' - problem1 = _DataProblem(message1) - problem2 = _DataProblem(message2) + """_DataProblem instances with different data should not be equal.""" + data1 = {"field": "field1", "error": "error1", "value": "value1"} + data2 = {"field": "field2", "error": "error2", "value": "value2"} + problem1 = _DataProblem(data1) + problem2 = _DataProblem(data2) assert problem1 != problem2 def test_data_problem_with_repr_values() -> None: """_DataProblem.message can contain repr() formatted values.""" - # This is how _create_validation_error_json creates messages - message = json.dumps( - { - "field": repr("user_id"), - "error": repr("expected int"), - "value": repr("abc-123"), - } - ) - problem = _DataProblem(message) + data = { + "field": repr("user_id"), + "error": repr("expected int"), + "value": repr("abc-123"), + } + problem = _DataProblem(data) parsed = json.loads(problem.message) assert parsed["field"] == "'user_id'" @@ -108,14 +102,12 @@ def test_data_problem_with_repr_values() -> None: def test_data_problem_with_none_value() -> None: """_DataProblem.message can contain None value.""" - message = json.dumps( - { - "field": repr("user_id"), - "error": repr("is None"), - "value": repr(None), - } - ) - problem = _DataProblem(message) + data = { + "field": repr("user_id"), + "error": repr("is None"), + "value": repr(None), + } + problem = _DataProblem(data) parsed = json.loads(problem.message) assert parsed["value"] == "None" @@ -124,16 +116,13 @@ def test_data_problem_with_none_value() -> None: def test_data_problem_with_complex_value() -> None: """_DataProblem.message can contain complex repr() formatted values.""" complex_value = {"nested": {"key": "value"}} - message = json.dumps( - { - "field": repr("tags"), - "error": repr("invalid type"), - "value": repr(complex_value), - } - ) - problem = _DataProblem(message) + data = { + "field": repr("tags"), + "error": repr("invalid type"), + "value": repr(complex_value), + } + problem = _DataProblem(data) parsed = json.loads(problem.message) - # Value should be the repr() string representation assert isinstance(parsed["value"], str) assert "nested" in parsed["value"] diff --git a/tests/private/test_schema_applier.py b/tests/private/test_schema_applier.py index 6d46a76..0304570 100644 --- a/tests/private/test_schema_applier.py +++ b/tests/private/test_schema_applier.py @@ -4,16 +4,12 @@ functionality directly, without going through SchemaLogger. """ -from __future__ import annotations - -import json - from logging_objects_with_schema.errors import _DataProblem from logging_objects_with_schema.schema_applier import ( _apply_schema_internal as apply_schema_internal, ) from logging_objects_with_schema.schema_applier import ( - _create_validation_error_json as create_validation_error_json, + _create_validation_error_dict as create_validation_error_dict, ) from logging_objects_with_schema.schema_applier import ( _set_nested_value as set_nested_value, @@ -829,143 +825,124 @@ def test_validate_and_apply_leaf_list_without_item_type() -> None: assert extra == {} -# Tests for _create_validation_error_json helper function +# Tests for _create_validation_error_dict helper function -def test_create_validation_error_json_with_string() -> None: - """_create_validation_error_json should create valid JSON with string value.""" - result = create_validation_error_json("user_id", "expected int", "abc-123") +def test_create_validation_error_dict_with_string() -> None: + """_create_validation_error_dict should create dict with repr'd string value.""" + result = create_validation_error_dict("user_id", "expected int", "abc-123") - parsed = json.loads(result) - assert parsed["field"] == "'user_id'" - assert parsed["error"] == "'expected int'" - assert parsed["value"] == "'abc-123'" + assert result["field"] == "'user_id'" + assert result["error"] == "'expected int'" + assert result["value"] == "'abc-123'" -def test_create_validation_error_json_with_int() -> None: - """_create_validation_error_json should use repr() for int values.""" - result = create_validation_error_json("count", "expected str", 42) +def test_create_validation_error_dict_with_int() -> None: + """_create_validation_error_dict should use repr() for int values.""" + result = create_validation_error_dict("count", "expected str", 42) - parsed = json.loads(result) - assert parsed["field"] == "'count'" - assert parsed["error"] == "'expected str'" - assert parsed["value"] == "42" + assert result["field"] == "'count'" + assert result["error"] == "'expected str'" + assert result["value"] == "42" -def test_create_validation_error_json_with_float() -> None: - """_create_validation_error_json should use repr() for float values.""" - result = create_validation_error_json("price", "expected int", 3.14) +def test_create_validation_error_dict_with_float() -> None: + """_create_validation_error_dict should use repr() for float values.""" + result = create_validation_error_dict("price", "expected int", 3.14) - parsed = json.loads(result) - assert parsed["field"] == "'price'" - assert parsed["error"] == "'expected int'" - assert parsed["value"] == "3.14" + assert result["field"] == "'price'" + assert result["error"] == "'expected int'" + assert result["value"] == "3.14" -def test_create_validation_error_json_with_bool() -> None: - """_create_validation_error_json should use repr() for bool values.""" - result = create_validation_error_json("is_active", "expected int", True) +def test_create_validation_error_dict_with_bool() -> None: + """_create_validation_error_dict should use repr() for bool values.""" + result = create_validation_error_dict("is_active", "expected int", True) - parsed = json.loads(result) - assert parsed["field"] == "'is_active'" - assert parsed["error"] == "'expected int'" - assert parsed["value"] == "True" + assert result["field"] == "'is_active'" + assert result["error"] == "'expected int'" + assert result["value"] == "True" -def test_create_validation_error_json_with_none() -> None: - """_create_validation_error_json should use repr() for None value.""" - result = create_validation_error_json("user_id", "is None", None) +def test_create_validation_error_dict_with_none() -> None: + """_create_validation_error_dict should use repr() for None value.""" + result = create_validation_error_dict("user_id", "is None", None) - parsed = json.loads(result) - assert parsed["field"] == "'user_id'" - assert parsed["error"] == "'is None'" - assert parsed["value"] == "None" + assert result["field"] == "'user_id'" + assert result["error"] == "'is None'" + assert result["value"] == "None" -def test_create_validation_error_json_with_dict() -> None: - """_create_validation_error_json should use repr() for dict values.""" +def test_create_validation_error_dict_with_dict() -> None: + """_create_validation_error_dict should use repr() for dict values.""" dict_value = {"key": "value", "nested": {"inner": 42}} - result = create_validation_error_json("tags", "invalid type", dict_value) + result = create_validation_error_dict("tags", "invalid type", dict_value) - parsed = json.loads(result) - assert parsed["field"] == "'tags'" - assert parsed["error"] == "'invalid type'" - # Value should be the repr() string representation - assert isinstance(parsed["value"], str) - assert "key" in parsed["value"] - assert "value" in parsed["value"] + assert result["field"] == "'tags'" + assert result["error"] == "'invalid type'" + assert isinstance(result["value"], str) + assert "key" in result["value"] + assert "value" in result["value"] -def test_create_validation_error_json_with_list() -> None: - """_create_validation_error_json should use repr() for list values.""" +def test_create_validation_error_dict_with_list() -> None: + """_create_validation_error_dict should use repr() for list values.""" list_value = [1, 2, "three", {"key": "value"}] - result = create_validation_error_json("items", "invalid type", list_value) - - parsed = json.loads(result) - assert parsed["field"] == "'items'" - assert parsed["error"] == "'invalid type'" - # Value should be the repr() string representation - assert isinstance(parsed["value"], str) - assert "1" in parsed["value"] - assert "2" in parsed["value"] - - -def test_create_validation_error_json_returns_valid_json() -> None: - """_create_validation_error_json should return valid JSON string.""" - result = create_validation_error_json("field", "error", "value") - - # Should not raise exception - parsed = json.loads(result) - assert isinstance(parsed, dict) - assert "field" in parsed - assert "error" in parsed - assert "value" in parsed - - -def test_create_validation_error_json_all_values_wrapped_in_repr() -> None: - """_create_validation_error_json should wrap all values in repr().""" - result = create_validation_error_json("test_field", "test error", "test value") - - parsed = json.loads(result) - # All values should be strings (wrapped in repr()) - assert isinstance(parsed["field"], str) - assert isinstance(parsed["error"], str) - assert isinstance(parsed["value"], str) - # Field and error should have quotes (repr() of strings) - assert parsed["field"].startswith("'") - assert parsed["field"].endswith("'") - assert parsed["error"].startswith("'") - assert parsed["error"].endswith("'") - - -def test_create_validation_error_json_with_special_characters() -> None: - """_create_validation_error_json should handle special characters via repr().""" + result = create_validation_error_dict("items", "invalid type", list_value) + + assert result["field"] == "'items'" + assert result["error"] == "'invalid type'" + assert isinstance(result["value"], str) + assert "1" in result["value"] + assert "2" in result["value"] + + +def test_create_validation_error_dict_returns_dict() -> None: + """_create_validation_error_dict should return a dict.""" + result = create_validation_error_dict("field", "error", "value") + + assert isinstance(result, dict) + assert "field" in result + assert "error" in result + assert "value" in result + + +def test_create_validation_error_dict_all_values_wrapped_in_repr() -> None: + """_create_validation_error_dict should wrap all values in repr().""" + result = create_validation_error_dict("test_field", "test error", "test value") + + assert isinstance(result["field"], str) + assert isinstance(result["error"], str) + assert isinstance(result["value"], str) + assert result["field"].startswith("'") + assert result["field"].endswith("'") + assert result["error"].startswith("'") + assert result["error"].endswith("'") + + +def test_create_validation_error_dict_with_special_characters() -> None: + """_create_validation_error_dict should handle special characters via repr().""" special_value = "value with\nnewline\tand\ttab" - result = create_validation_error_json("field", "error", special_value) + result = create_validation_error_dict("field", "error", special_value) - parsed = json.loads(result) - # repr() should escape special characters - assert "\\n" in parsed["value"] or "\n" in parsed["value"] - assert "\\t" in parsed["value"] or "\t" in parsed["value"] + assert "\\n" in result["value"] or "\n" in result["value"] + assert "\\t" in result["value"] or "\t" in result["value"] -def test_create_validation_error_json_with_unicode() -> None: - """_create_validation_error_json should handle unicode characters via repr().""" +def test_create_validation_error_dict_with_unicode() -> None: + """_create_validation_error_dict should handle unicode characters via repr().""" unicode_value = "тест 测试 🚀" - result = create_validation_error_json("field", "error", unicode_value) + result = create_validation_error_dict("field", "error", unicode_value) - parsed = json.loads(result) - # Should be valid JSON with unicode preserved assert ( - "тест" in parsed["value"] or "\\u0442\\u0435\\u0441\\u0442" in parsed["value"] + "тест" in result["value"] or "\\u0442\\u0435\\u0441\\u0442" in result["value"] ) -def test_create_validation_error_json_with_empty_string() -> None: - """_create_validation_error_json should handle empty string value.""" - result = create_validation_error_json("field", "error", "") +def test_create_validation_error_dict_with_empty_string() -> None: + """_create_validation_error_dict should handle empty string value.""" + result = create_validation_error_dict("field", "error", "") - parsed = json.loads(result) - assert parsed["field"] == "'field'" - assert parsed["error"] == "'error'" - assert parsed["value"] == "''" + assert result["field"] == "'field'" + assert result["error"] == "'error'" + assert result["value"] == "''" diff --git a/tests/private/test_schema_loader.py b/tests/private/test_schema_loader.py index ad73f49..5d6169b 100644 --- a/tests/private/test_schema_loader.py +++ b/tests/private/test_schema_loader.py @@ -4,8 +4,6 @@ expected contract rather than exhaustively cover edge cases. """ -from __future__ import annotations - from pathlib import Path from typing import Any @@ -57,7 +55,6 @@ from logging_objects_with_schema.schema_loader import ( _is_empty_or_none as is_empty_or_none, ) -from logging_objects_with_schema.schema_loader import _is_leaf_node as is_leaf_node from logging_objects_with_schema.schema_loader import ( _load_raw_schema as load_raw_schema, ) @@ -313,7 +310,7 @@ def test_incomplete_leaf_missing_type( compiled, problems = compile_schema_internal() assert isinstance(compiled, _CompiledSchema) assert compiled.is_empty - assert any("type cannot be None or empty" in p.message for p in problems) + assert any("type must be a non-empty string" in p.message for p in problems) def test_incomplete_leaf_missing_source( @@ -335,7 +332,7 @@ def test_incomplete_leaf_missing_source( compiled, problems = compile_schema_internal() assert isinstance(compiled, _CompiledSchema) assert compiled.is_empty - assert any("source cannot be None or empty" in p.message for p in problems) + assert any("source must be a non-empty string" in p.message for p in problems) def test_incomplete_leaf_empty_source( @@ -357,7 +354,7 @@ def test_incomplete_leaf_empty_source( compiled, problems = compile_schema_internal() assert isinstance(compiled, _CompiledSchema) assert compiled.is_empty - assert any("source cannot be None or empty" in p.message for p in problems) + assert any("source must be a non-empty string" in p.message for p in problems) def test_incomplete_leaf_empty_type( @@ -379,7 +376,7 @@ def test_incomplete_leaf_empty_type( compiled, problems = compile_schema_internal() assert isinstance(compiled, _CompiledSchema) assert compiled.is_empty - assert any("type cannot be None or empty" in p.message for p in problems) + assert any("type must be a non-empty string" in p.message for p in problems) def test_multiple_root_keys_with_valid_leaves( @@ -669,7 +666,7 @@ def test_validate_and_create_leaf_missing_type() -> None: assert leaf is None assert len(problems) == 1 - assert "type cannot be None or empty" in problems[0].message + assert "type must be a non-empty string" in problems[0].message def test_validate_and_create_leaf_missing_source() -> None: @@ -683,7 +680,7 @@ def test_validate_and_create_leaf_missing_source() -> None: assert leaf is None assert len(problems) == 1 - assert "source cannot be None or empty" in problems[0].message + assert "source must be a non-empty string" in problems[0].message def test_validate_and_create_leaf_empty_type() -> None: @@ -697,7 +694,7 @@ def test_validate_and_create_leaf_empty_type() -> None: assert leaf is None assert len(problems) == 1 - assert "type cannot be None or empty" in problems[0].message + assert "type must be a non-empty string" in problems[0].message def test_validate_and_create_leaf_whitespace_type() -> None: @@ -711,7 +708,7 @@ def test_validate_and_create_leaf_whitespace_type() -> None: assert leaf is None assert len(problems) == 1 - assert "type cannot be None or empty" in problems[0].message + assert "type must be a non-empty string" in problems[0].message def test_validate_and_create_leaf_empty_source() -> None: @@ -725,7 +722,7 @@ def test_validate_and_create_leaf_empty_source() -> None: assert leaf is None assert len(problems) == 1 - assert "source cannot be None or empty" in problems[0].message + assert "source must be a non-empty string" in problems[0].message def test_validate_and_create_leaf_whitespace_source() -> None: @@ -739,7 +736,7 @@ def test_validate_and_create_leaf_whitespace_source() -> None: assert leaf is None assert len(problems) == 1 - assert "source cannot be None or empty" in problems[0].message + assert "source must be a non-empty string" in problems[0].message def test_validate_and_create_leaf_unknown_type() -> None: @@ -766,7 +763,7 @@ def test_validate_and_create_leaf_list_missing_item_type() -> None: assert leaf is None assert len(problems) == 1 - assert "item_type is required for list type" in problems[0].message + assert "item_type must be a non-empty string" in problems[0].message def test_validate_and_create_leaf_list_empty_item_type() -> None: @@ -778,7 +775,7 @@ def test_validate_and_create_leaf_list_empty_item_type() -> None: assert leaf is None assert len(problems) == 1 - assert "item_type is required for list type" in problems[0].message + assert "item_type must be a non-empty string" in problems[0].message def test_validate_and_create_leaf_list_invalid_item_type() -> None: @@ -1036,6 +1033,19 @@ def test_check_cached_found_file_path_returns_none_when_no_cache() -> None: assert result is None +def test_check_cached_found_file_path_returns_none_when_cached_as_missing( + tmp_path: Path, +) -> None: + """check_cached_found_file_path returns None for missing file cache.""" + + with schema_loader._path_cache_lock: + schema_loader._resolved_schema_path = tmp_path / _SCHEMA_FILE_NAME + schema_loader._cached_cwd = tmp_path + + result = check_cached_found_file_path() + assert result is None + + def test_check_cached_missing_file_path_returns_path_when_cwd_unchanged( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, @@ -1449,103 +1459,6 @@ def test_check_root_conflicts_empty_schema() -> None: assert problems == [] -def test_is_leaf_node_with_type() -> None: - """_is_leaf_node should return True for node with type field.""" - value_dict = {"type": "str", "source": "request_id"} - assert is_leaf_node(value_dict) is True - - -def test_is_leaf_node_with_source() -> None: - """_is_leaf_node should return True for node with source field.""" - value_dict = {"source": "request_id"} - assert is_leaf_node(value_dict) is True - - -def test_is_leaf_node_with_both_fields() -> None: - """_is_leaf_node should return True for node with both type and source.""" - value_dict = {"type": "int", "source": "user_id"} - assert is_leaf_node(value_dict) is True - - -def test_is_leaf_node_without_fields() -> None: - """_is_leaf_node should return False for inner node without type or source.""" - value_dict = {"nested": {}} - assert is_leaf_node(value_dict) is False - - -def test_is_leaf_node_with_empty_dict() -> None: - """_is_leaf_node should return False for empty dictionary.""" - value_dict: dict[str, Any] = {} - assert is_leaf_node(value_dict) is False - - -def test_is_leaf_node_with_type_none() -> None: - """_is_leaf_node should return True when type is None but source is present.""" - value_dict = {"type": None, "source": "request_id"} - # Note: get() returns None, so "type" is None, but "source" is not None - # So this should still return True because source is present - assert is_leaf_node(value_dict) is True - - -def test_is_leaf_node_with_source_none() -> None: - """_is_leaf_node should return True when source is None but type is present.""" - value_dict = {"type": "str", "source": None} - # Note: get() returns None, so "source" is None, but "type" is not None - # So this should still return True because type is present - assert is_leaf_node(value_dict) is True - - -def test_is_leaf_node_with_both_none() -> None: - """_is_leaf_node should return False when both type and source are None.""" - value_dict = {"type": None, "source": None} - assert is_leaf_node(value_dict) is False - - -def test_is_leaf_node_with_type_as_object() -> None: - """_is_leaf_node should return False when type is an object (child node).""" - value_dict = { - "type": {"type": "str", "source": "some.type"}, - "other": "value", - } - assert is_leaf_node(value_dict) is False - - -def test_is_leaf_node_with_source_as_object() -> None: - """_is_leaf_node should return False when source is an object (child node).""" - value_dict = { - "source": {"type": "str", "source": "some.source"}, - "other": "value", - } - assert is_leaf_node(value_dict) is False - - -def test_is_leaf_node_with_both_as_objects() -> None: - """_is_leaf_node should return False when both type and source are objects.""" - value_dict = { - "type": {"type": "str", "source": "some.type"}, - "source": {"type": "str", "source": "some.source"}, - } - assert is_leaf_node(value_dict) is False - - -def test_is_leaf_node_with_type_as_object_source_as_string() -> None: - """_is_leaf_node should return False when type is object (even if source is string).""" # noqa: E501 - value_dict = { - "type": {"type": "str", "source": "some.type"}, - "source": "request_id", - } - assert is_leaf_node(value_dict) is False - - -def test_is_leaf_node_with_source_as_object_type_as_string() -> None: - """_is_leaf_node should return False when source is object (even if type is string).""" # noqa: E501 - value_dict = { - "type": "str", - "source": {"type": "str", "source": "some.source"}, - } - assert is_leaf_node(value_dict) is False - - def test_leaf_node_with_child_nodes_produces_problem( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, @@ -1919,7 +1832,7 @@ def test_node_with_only_item_type_as_string_produces_problem( assert isinstance(compiled, _CompiledSchema) assert compiled.is_empty # Should have problems because type and source are required - assert any("type cannot be None or empty" in p.message for p in problems) + assert any("type must be a non-empty string" in p.message for p in problems) def test_node_with_type_as_number_produces_problem( @@ -1946,7 +1859,7 @@ def test_node_with_type_as_number_produces_problem( assert isinstance(compiled, _CompiledSchema) assert compiled.is_empty # Should have problem because type must be a string, not a number - assert any("type cannot be None or empty" in p.message for p in problems) + assert any("type must be a non-empty string" in p.message for p in problems) def test_node_with_type_as_boolean_produces_problem( @@ -1973,7 +1886,7 @@ def test_node_with_type_as_boolean_produces_problem( assert isinstance(compiled, _CompiledSchema) assert compiled.is_empty # Should have problem because type must be a string, not boolean - assert any("type cannot be None or empty" in p.message for p in problems) + assert any("type must be a non-empty string" in p.message for p in problems) def test_node_with_source_as_boolean_produces_problem( @@ -2000,7 +1913,7 @@ def test_node_with_source_as_boolean_produces_problem( assert isinstance(compiled, _CompiledSchema) assert compiled.is_empty # Should have problem because source must be a string, not boolean - assert any("source cannot be None or empty" in p.message for p in problems) + assert any("source must be a non-empty string" in p.message for p in problems) def test_node_with_source_as_number_produces_problem( @@ -2027,7 +1940,7 @@ def test_node_with_source_as_number_produces_problem( assert isinstance(compiled, _CompiledSchema) assert compiled.is_empty # Should have problem because source must be a string, not number - assert any("source cannot be None or empty" in p.message for p in problems) + assert any("source must be a non-empty string" in p.message for p in problems) def test_node_with_type_and_source_as_numbers_produces_problem( @@ -2108,7 +2021,7 @@ def test_node_with_item_type_as_number_produces_problem( assert isinstance(compiled, _CompiledSchema) assert compiled.is_empty # Should have problem because item_type must be a string, not a number - assert any("item_type is required for list type" in p.message for p in problems) + assert any("item_type must be a non-empty string" in p.message for p in problems) def test_node_with_item_type_as_boolean_produces_problem( @@ -2139,7 +2052,7 @@ def test_node_with_item_type_as_boolean_produces_problem( assert isinstance(compiled, _CompiledSchema) assert compiled.is_empty # Should have problem because item_type must be a string, not a boolean - assert any("item_type is required for list type" in p.message for p in problems) + assert any("item_type must be a non-empty string" in p.message for p in problems) def test_node_with_item_type_as_object_is_determined_as_inner( @@ -2200,7 +2113,7 @@ def test_node_with_type_as_list_produces_problem( assert isinstance(compiled, _CompiledSchema) assert compiled.is_empty # Should have problem because type must be a string, not a list - assert any("type cannot be None or empty" in p.message for p in problems) + assert any("type must be a non-empty string" in p.message for p in problems) def test_node_with_source_as_list_produces_problem( @@ -2230,4 +2143,4 @@ def test_node_with_source_as_list_produces_problem( assert isinstance(compiled, _CompiledSchema) assert compiled.is_empty # Should have problem because source must be a string, not a list - assert any("source cannot be None or empty" in p.message for p in problems) + assert any("source must be a non-empty string" in p.message for p in problems) diff --git a/tests/private/test_schema_logger.py b/tests/private/test_schema_logger.py index e350de8..f189285 100644 --- a/tests/private/test_schema_logger.py +++ b/tests/private/test_schema_logger.py @@ -387,51 +387,6 @@ def test_function() -> None: assert "test_schema_logger.py" in output -def test_schema_logger_log_handles_json_loads_exception( - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, -) -> None: - """SchemaLogger._log should handle json.loads exception gracefully.""" - monkeypatch.chdir(tmp_path) - _write_schema( - tmp_path, - { - "ServicePayload": { - "RequestID": {"type": "str", "source": "request_id"}, - }, - }, - ) - - logger = SchemaLogger("test_logger") - stream = StringIO() - handler = logging.StreamHandler(stream) - handler.setFormatter(logging.Formatter("%(message)s")) - logger.addHandler(handler) - logger.setLevel(logging.INFO) - - # Create a mock _DataProblem with invalid JSON - from logging_objects_with_schema.errors import _DataProblem - from logging_objects_with_schema.schema_applier import _apply_schema_internal - - original_apply = _apply_schema_internal - - def mock_apply(*args, **kwargs): - result, problems = original_apply(*args, **kwargs) - # Add a problem with invalid JSON - problems.append(_DataProblem("invalid json {")) - return result, problems - - with patch( - "logging_objects_with_schema.schema_logger._apply_schema_internal", mock_apply - ): - logger._log(logging.INFO, "test", (), extra={"request_id": "abc"}) - - output = stream.getvalue() - # Should handle the exception and log a fallback error - assert "test" in output - assert "validation_errors" in output - - def test_schema_logger_log_handles_json_dumps_exception( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, @@ -557,88 +512,6 @@ def test_schema_logger_log_uses_findcaller_for_python_311_plus( assert mock_findcaller.called -def test_schema_logger_log_uses_inspect_stack_fallback_for_old_python( - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, -) -> None: - """SchemaLogger._log should use inspect.stack() fallback for Python < 3.11.""" - import sys - - if sys.version_info >= (3, 11): - pytest.skip("Test only for Python < 3.11") - - monkeypatch.chdir(tmp_path) - _write_schema( - tmp_path, - { - "ServicePayload": { - "RequestID": {"type": "str", "source": "request_id"}, - }, - }, - ) - - logger = SchemaLogger("test_logger") - stream = StringIO() - handler = logging.StreamHandler(stream) - handler.setFormatter(logging.Formatter("%(message)s")) - logger.addHandler(handler) - logger.setLevel(logging.INFO) - - with patch("inspect.stack") as mock_stack: - # Mock stack to return a frame - mock_frame = MagicMock() - mock_frame.filename = "test.py" - mock_frame.lineno = 42 - mock_frame.function = "test_func" - mock_stack.return_value = [None, None, mock_frame] - - logger._log(logging.INFO, "test", (), extra={"request_id": 123}) - - # inspect.stack should be called for validation error logging - assert mock_stack.called - - -def test_schema_logger_log_fallback_to_findcaller_when_stack_too_short( - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, -) -> None: - """SchemaLogger._log should fallback to findCaller when stack is too short.""" - import sys - - if sys.version_info >= (3, 11): - pytest.skip("Test only for Python < 3.11") - - monkeypatch.chdir(tmp_path) - _write_schema( - tmp_path, - { - "ServicePayload": { - "RequestID": {"type": "str", "source": "request_id"}, - }, - }, - ) - - logger = SchemaLogger("test_logger") - stream = StringIO() - handler = logging.StreamHandler(stream) - handler.setFormatter(logging.Formatter("%(message)s")) - logger.addHandler(handler) - logger.setLevel(logging.INFO) - - with ( - patch("inspect.stack") as mock_stack, - patch.object(logger, "findCaller") as mock_findcaller, - ): - # Mock stack to return a short stack (less than expected) - mock_stack.return_value = [None] # Too short - mock_findcaller.return_value = ("test.py", 42, "test_func", None) - - logger._log(logging.INFO, "test", (), extra={"request_id": 123}, stacklevel=10) - - # findCaller should be called as fallback - assert mock_findcaller.called - - def test_schema_logger_log_makerecord_called_with_correct_params( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, diff --git a/tests/test_data_validation.py b/tests/test_data_validation.py index c6e2e46..d20d96a 100644 --- a/tests/test_data_validation.py +++ b/tests/test_data_validation.py @@ -242,67 +242,6 @@ def test_logger_with_empty_schema( assert "another" in output -def test_logger_handles_invalid_json_in_data_problem_message( - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, -) -> None: - """Logger should handle DataProblem with invalid JSON in message gracefully. - - This tests the defensive code path when _DataProblem.message is not valid JSON - (should never happen in normal operation, but protects against data corruption). - """ - - monkeypatch.chdir(tmp_path) - _write_schema( - tmp_path, - { - "ServicePayload": { - "UserID": {"type": "int", "source": "user_id"}, - }, - }, - ) - - from logging_objects_with_schema.errors import _DataProblem - from logging_objects_with_schema.schema_applier import _apply_schema_internal - - # Create a logger and patch _apply_schema_internal to return a DataProblem - # with invalid JSON in the message - logger = SchemaLogger("test") - original_apply = _apply_schema_internal - - def mock_apply_schema(schema, extra): - # Call original to get normal result, but replace one problem with invalid JSON - structured_extra, problems = original_apply(schema, extra) - if problems: - # Replace first problem with one that has invalid JSON - invalid_problem = _DataProblem("not valid json {") - problems = [invalid_problem] + problems[1:] - return structured_extra, problems - - stream = StringIO() - handler = logging.StreamHandler(stream) - handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s")) - logger.addHandler(handler) - logger.setLevel(logging.INFO) - - with monkeypatch.context() as m: - m.setattr( - "logging_objects_with_schema.schema_logger._apply_schema_internal", - mock_apply_schema, - ) - # Log with invalid type to trigger validation error - logger.info("msg", extra={"user_id": "not-an-int"}) - - output = stream.getvalue() - # Main message should be logged - assert "msg" in output - # Error should be logged with fallback message - assert "ERROR" in output - assert "validation_errors" in output - # Should contain fallback error message - assert "Failed to parse validation error" in output or "unknown" in output - - def test_logger_handles_json_serialization_error( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, @@ -334,16 +273,16 @@ def test_logger_handles_json_serialization_error( logger.addHandler(handler) logger.setLevel(logging.INFO) - # Mock json.dumps to fail on the second call (when serializing validation errors) + # Mock json.dumps to fail on the first call (when serializing validation errors) original_dumps = json.dumps call_count = 0 def mock_dumps(*args, **kwargs): nonlocal call_count call_count += 1 - # First call is from _create_validation_error_json (should succeed) - # Second call is from _log when combining errors (should fail) - if call_count == 2: + # First call is from _log when combining errors (should fail) + # Second call is the fallback serialization (should succeed) + if call_count == 1: raise TypeError("Serialization failed") return original_dumps(*args, **kwargs) @@ -410,107 +349,3 @@ def emit(self, record: logging.LogRecord) -> None: # Error should be written to stderr because handler failed stderr_output = stderr_capture.getvalue() assert "Error in logging handler" in stderr_output - - -def test_logger_uses_inspect_stack_fallback_for_old_python( - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, -) -> None: - """Logger should use inspect.stack() fallback for Python < 3.11. - - This tests the fallback code path when _USE_FINDCALLER is False - (simulating Python < 3.11 behavior). - """ - - monkeypatch.chdir(tmp_path) - _write_schema( - tmp_path, - { - "ServicePayload": { - "UserID": {"type": "int", "source": "user_id"}, - }, - }, - ) - - from unittest.mock import patch - - stream = StringIO() - handler = logging.StreamHandler(stream) - handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s")) - logger = SchemaLogger("test") - logger.addHandler(handler) - logger.setLevel(logging.INFO) - - # Mock _USE_FINDCALLER to be False to trigger inspect.stack() path - with patch( - "logging_objects_with_schema.schema_logger._USE_FINDCALLER", - False, - ): - # Log with invalid type to trigger validation error - def test_function() -> None: - logger.info("msg", extra={"user_id": "not-an-int"}) - - test_function() - - output = stream.getvalue() - # Main message should be logged - assert "msg" in output - # Error should be logged - assert "ERROR" in output - assert "validation_errors" in output - - -def test_logger_uses_findcaller_fallback_when_stack_too_short( - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, -) -> None: - """Logger should use findCaller() fallback when inspect.stack() is too short. - - This tests the fallback within fallback: when _USE_FINDCALLER is False - but inspect.stack() returns a stack that's shorter than expected. - """ - - monkeypatch.chdir(tmp_path) - _write_schema( - tmp_path, - { - "ServicePayload": { - "UserID": {"type": "int", "source": "user_id"}, - }, - }, - ) - - from unittest.mock import patch - - stream = StringIO() - handler = logging.StreamHandler(stream) - handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s")) - logger = SchemaLogger("test") - logger.addHandler(handler) - logger.setLevel(logging.INFO) - - # Mock _USE_FINDCALLER to be False and inspect.stack() to return short stack - def mock_stack(): - # Return a very short stack (shorter than expected) - return [ - type( - "Frame", (), {"filename": "test.py", "lineno": 1, "function": "test"} - )() - ] - - with ( - patch( - "logging_objects_with_schema.schema_logger._USE_FINDCALLER", - False, - ), - patch("inspect.stack", side_effect=mock_stack), - ): - # Log with invalid type to trigger validation error - logger.info("msg", extra={"user_id": "not-an-int"}) - - output = stream.getvalue() - # Main message should be logged - assert "msg" in output - # Error should be logged (fallback to findCaller should work) - assert "ERROR" in output - assert "validation_errors" in output diff --git a/tests/test_integration.py b/tests/test_integration.py index 1d07a6e..1933359 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -5,8 +5,6 @@ handling during initialization (schema problems, file system errors, etc.). """ -from __future__ import annotations - import logging from io import StringIO from pathlib import Path diff --git a/tests/test_logging_compatibility.py b/tests/test_logging_compatibility.py index 02793d0..556e0be 100644 --- a/tests/test_logging_compatibility.py +++ b/tests/test_logging_compatibility.py @@ -1,7 +1,5 @@ """Tests for SchemaLogger behaviour mimicking logging.Logger.""" -from __future__ import annotations - import logging from io import StringIO from pathlib import Path diff --git a/tests/test_mypy_support.py b/tests/test_mypy_support.py index d342088..b5f36d1 100644 --- a/tests/test_mypy_support.py +++ b/tests/test_mypy_support.py @@ -1,7 +1,5 @@ """Tests for mypy type checking support.""" -from __future__ import annotations - import subprocess import sys from pathlib import Path diff --git a/tests/test_thread_safety.py b/tests/test_thread_safety.py index 2bca573..8e6281f 100644 --- a/tests/test_thread_safety.py +++ b/tests/test_thread_safety.py @@ -1,7 +1,5 @@ """Tests for thread safety of schema caching and logger creation.""" -from __future__ import annotations - import logging import threading from pathlib import Path diff --git a/uv.lock b/uv.lock index f440212..471c115 100644 --- a/uv.lock +++ b/uv.lock @@ -1,10 +1,14 @@ version = 1 revision = 3 -requires-python = ">=3.10" +requires-python = ">=3.11" +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version < '3.15'" +] [[package]] name = "bandit" -version = "1.9.2" +version = "1.9.4" [package.source] registry = "https://pypi.org/simple" @@ -23,16 +27,16 @@ version = "1.9.2" name = "stevedore" [package.sdist] - url = "https://files.pythonhosted.org/packages/cf/72/f704a97aac430aeb704fa16435dfa24fbeaf087d46724d0965eb1f756a2c/bandit-1.9.2.tar.gz" - hash = "sha256:32410415cd93bf9c8b91972159d5cf1e7f063a9146d70345641cd3877de348ce" - size = 4_241_659 - upload-time = "2025-11-23T21:36:18.722Z" + url = "https://files.pythonhosted.org/packages/aa/c3/0cb80dfe0f3076e5da7e4c5ad8e57bac6ac357ff4a6406205501cade4965/bandit-1.9.4.tar.gz" + hash = "sha256:b589e5de2afe70bd4d53fa0c1da6199f4085af666fde00e8a034f152a52cd628" + size = 4_242_677 + upload-time = "2026-02-25T06:44:15.503Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/55/1a/5b0320642cca53a473e79c7d273071b5a9a8578f9e370b74da5daa2768d7/bandit-1.9.2-py3-none-any.whl" - hash = "sha256:bda8d68610fc33a6e10b7a8f1d61d92c8f6c004051d5e946406be1fb1b16a868" - size = 134_377 - upload-time = "2025-11-23T21:36:17.39Z" + url = "https://files.pythonhosted.org/packages/05/a4/a26d5b25671d27e03afb5401a0be5899d94ff8fab6a698b1ac5be3ec29ef/bandit-1.9.4-py3-none-any.whl" + hash = "sha256:f89ffa663767f5a0585ea075f01020207e966a9c0f2b9ef56a57c7963a3f6f8e" + size = 134_741 + upload-time = "2026-02-25T06:44:13.694Z" [[package]] name = "black" @@ -59,50 +63,12 @@ version = "26.3.1" [[package.dependencies]] name = "pytokens" - [[package.dependencies]] - name = "tomli" - marker = "python_full_version < '3.11'" - - [[package.dependencies]] - name = "typing-extensions" - marker = "python_full_version < '3.11'" - [package.sdist] url = "https://files.pythonhosted.org/packages/e1/c5/61175d618685d42b005847464b8fb4743a67b1b8fdb75e50e5a96c31a27a/black-26.3.1.tar.gz" hash = "sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07" size = 666_155 upload-time = "2026-03-12T03:36:03.593Z" - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/32/a8/11170031095655d36ebc6664fe0897866f6023892396900eec0e8fdc4299/black-26.3.1-cp310-cp310-macosx_10_9_x86_64.whl" - hash = "sha256:86a8b5035fce64f5dcd1b794cf8ec4d31fe458cf6ce3986a30deb434df82a1d2" - size = 1_866_562 - upload-time = "2026-03-12T03:39:58.639Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/69/ce/9e7548d719c3248c6c2abfd555d11169457cbd584d98d179111338423790/black-26.3.1-cp310-cp310-macosx_11_0_arm64.whl" - hash = "sha256:5602bdb96d52d2d0672f24f6ffe5218795736dd34807fd0fd55ccd6bf206168b" - size = 1_703_623 - upload-time = "2026-03-12T03:40:00.347Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/7f/0a/8d17d1a9c06f88d3d030d0b1d4373c1551146e252afe4547ed601c0e697f/black-26.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:6c54a4a82e291a1fee5137371ab488866b7c86a3305af4026bdd4dc78642e1ac" - size = 1_768_388 - upload-time = "2026-03-12T03:40:01.765Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/52/79/c1ee726e221c863cde5164f925bacf183dfdf0397d4e3f94889439b947b4/black-26.3.1-cp310-cp310-win_amd64.whl" - hash = "sha256:6e131579c243c98f35bce64a7e08e87fb2d610544754675d4a0e73a070a5aa3a" - size = 1_412_969 - upload-time = "2026-03-12T03:40:03.252Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/73/a5/15c01d613f5756f68ed8f6d4ec0a1e24b82b18889fa71affd3d1f7fad058/black-26.3.1-cp310-cp310-win_arm64.whl" - hash = "sha256:5ed0ca58586c8d9a487352a96b15272b7fa55d139fc8496b519e78023a8dab0a" - size = 1_220_345 - upload-time = "2026-03-12T03:40:04.892Z" - [[package.wheels]] url = "https://files.pythonhosted.org/packages/17/57/5f11c92861f9c92eb9dddf515530bc2d06db843e44bdcf1c83c1427824bc/black-26.3.1-cp311-cp311-macosx_10_9_x86_64.whl" hash = "sha256:28ef38aee69e4b12fda8dba75e21f9b4f979b490c8ac0baa7cb505369ac9e1ff" @@ -250,7 +216,7 @@ version = "3.5.0" [[package]] name = "click" -version = "8.3.1" +version = "8.3.3" [package.source] registry = "https://pypi.org/simple" @@ -260,16 +226,16 @@ version = "8.3.1" marker = "sys_platform == 'win32'" [package.sdist] - url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz" - hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a" - size = 295_065 - upload-time = "2025-11-15T20:45:42.706Z" + url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz" + hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2" + size = 328_061 + upload-time = "2026-04-22T15:11:27.506Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl" - hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6" - size = 108_274 - upload-time = "2025-11-15T20:45:41.139Z" + url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl" + hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613" + size = 110_502 + upload-time = "2026-04-22T15:11:25.044Z" [[package]] name = "colorama" @@ -292,562 +258,562 @@ version = "0.4.6" [[package]] name = "coverage" -version = "7.12.0" +version = "7.13.5" [package.source] registry = "https://pypi.org/simple" [package.sdist] - url = "https://files.pythonhosted.org/packages/89/26/4a96807b193b011588099c3b5c89fbb05294e5b90e71018e065465f34eb6/coverage-7.12.0.tar.gz" - hash = "sha256:fc11e0a4e372cb5f282f16ef90d4a585034050ccda536451901abfb19a57f40c" - size = 819_341 - upload-time = "2025-11-18T13:34:20.766Z" + url = "https://files.pythonhosted.org/packages/9d/e0/70553e3000e345daff267cec284ce4cbf3fc141b6da229ac52775b5428f1/coverage-7.13.5.tar.gz" + hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179" + size = 915_967 + upload-time = "2026-03-17T10:33:18.341Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/26/4a/0dc3de1c172d35abe512332cfdcc43211b6ebce629e4cc42e6cd25ed8f4d/coverage-7.12.0-cp310-cp310-macosx_10_9_x86_64.whl" - hash = "sha256:32b75c2ba3f324ee37af3ccee5b30458038c50b349ad9b88cee85096132a575b" - size = 217_409 - upload-time = "2025-11-18T13:31:53.122Z" + url = "https://files.pythonhosted.org/packages/4b/37/d24c8f8220ff07b839b2c043ea4903a33b0f455abe673ae3c03bbdb7f212/coverage-7.13.5-cp311-cp311-macosx_10_9_x86_64.whl" + hash = "sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d" + size = 219_381 + upload-time = "2026-03-17T10:30:14.68Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/01/c3/086198b98db0109ad4f84241e8e9ea7e5fb2db8c8ffb787162d40c26cc76/coverage-7.12.0-cp310-cp310-macosx_11_0_arm64.whl" - hash = "sha256:cb2a1b6ab9fe833714a483a915de350abc624a37149649297624c8d57add089c" - size = 217_927 - upload-time = "2025-11-18T13:31:54.458Z" + url = "https://files.pythonhosted.org/packages/35/8b/cd129b0ca4afe886a6ce9d183c44d8301acbd4ef248622e7c49a23145605/coverage-7.13.5-cp311-cp311-macosx_11_0_arm64.whl" + hash = "sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587" + size = 219_880 + upload-time = "2026-03-17T10:30:16.231Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/5d/5f/34614dbf5ce0420828fc6c6f915126a0fcb01e25d16cf141bf5361e6aea6/coverage-7.12.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" - hash = "sha256:5734b5d913c3755e72f70bf6cc37a0518d4f4745cde760c5d8e12005e62f9832" - size = 244_678 - upload-time = "2025-11-18T13:31:55.805Z" + url = "https://files.pythonhosted.org/packages/55/2f/e0e5b237bffdb5d6c530ce87cc1d413a5b7d7dfd60fb067ad6d254c35c76/coverage-7.13.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" + hash = "sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642" + size = 250_303 + upload-time = "2026-03-17T10:30:17.748Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/55/7b/6b26fb32e8e4a6989ac1d40c4e132b14556131493b1d06bc0f2be169c357/coverage-7.12.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" - hash = "sha256:b527a08cdf15753279b7afb2339a12073620b761d79b81cbe2cdebdb43d90daa" - size = 246_507 - upload-time = "2025-11-18T13:31:57.05Z" + url = "https://files.pythonhosted.org/packages/92/be/b1afb692be85b947f3401375851484496134c5554e67e822c35f28bf2fbc/coverage-7.13.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" + hash = "sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b" + size = 252_218 + upload-time = "2026-03-17T10:30:19.804Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/06/42/7d70e6603d3260199b90fb48b537ca29ac183d524a65cc31366b2e905fad/coverage-7.12.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:9bb44c889fb68004e94cab71f6a021ec83eac9aeabdbb5a5a88821ec46e1da73" - size = 248_366 - upload-time = "2025-11-18T13:31:58.362Z" + url = "https://files.pythonhosted.org/packages/da/69/2f47bb6fa1b8d1e3e5d0c4be8ccb4313c63d742476a619418f85740d597b/coverage-7.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + hash = "sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686" + size = 254_326 + upload-time = "2026-03-17T10:30:21.321Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/2d/4a/d86b837923878424c72458c5b25e899a3c5ca73e663082a915f5b3c4d749/coverage-7.12.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" - hash = "sha256:4b59b501455535e2e5dde5881739897967b272ba25988c89145c12d772810ccb" - size = 245_366 - upload-time = "2025-11-18T13:31:59.572Z" + url = "https://files.pythonhosted.org/packages/d5/d0/79db81da58965bd29dabc8f4ad2a2af70611a57cba9d1ec006f072f30a54/coverage-7.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + hash = "sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743" + size = 256_267 + upload-time = "2026-03-17T10:30:23.094Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/e6/c2/2adec557e0aa9721875f06ced19730fdb7fc58e31b02b5aa56f2ebe4944d/coverage-7.12.0-cp310-cp310-musllinux_1_2_aarch64.whl" - hash = "sha256:d8842f17095b9868a05837b7b1b73495293091bed870e099521ada176aa3e00e" - size = 246_408 - upload-time = "2025-11-18T13:32:00.784Z" + url = "https://files.pythonhosted.org/packages/e5/32/d0d7cc8168f91ddab44c0ce4806b969df5f5fdfdbb568eaca2dbc2a04936/coverage-7.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + hash = "sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75" + size = 250_430 + upload-time = "2026-03-17T10:30:25.311Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/5a/4b/8bd1f1148260df11c618e535fdccd1e5aaf646e55b50759006a4f41d8a26/coverage-7.12.0-cp310-cp310-musllinux_1_2_i686.whl" - hash = "sha256:c5a6f20bf48b8866095c6820641e7ffbe23f2ac84a2efc218d91235e404c7777" - size = 244_416 - upload-time = "2025-11-18T13:32:01.963Z" + url = "https://files.pythonhosted.org/packages/4d/06/a055311d891ddbe231cd69fdd20ea4be6e3603ffebddf8704b8ca8e10a3c/coverage-7.13.5-cp311-cp311-musllinux_1_2_aarch64.whl" + hash = "sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209" + size = 252_017 + upload-time = "2026-03-17T10:30:27.284Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/0e/13/3a248dd6a83df90414c54a4e121fd081fb20602ca43955fbe1d60e2312a9/coverage-7.12.0-cp310-cp310-musllinux_1_2_riscv64.whl" - hash = "sha256:5f3738279524e988d9da2893f307c2093815c623f8d05a8f79e3eff3a7a9e553" - size = 244_681 - upload-time = "2025-11-18T13:32:03.408Z" + url = "https://files.pythonhosted.org/packages/d6/f6/d0fd2d21e29a657b5f77a2fe7082e1568158340dceb941954f776dce1b7b/coverage-7.13.5-cp311-cp311-musllinux_1_2_i686.whl" + hash = "sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a" + size = 250_080 + upload-time = "2026-03-17T10:30:29.481Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/76/30/aa833827465a5e8c938935f5d91ba055f70516941078a703740aaf1aa41f/coverage-7.12.0-cp310-cp310-musllinux_1_2_x86_64.whl" - hash = "sha256:e0d68c1f7eabbc8abe582d11fa393ea483caf4f44b0af86881174769f185c94d" - size = 245_300 - upload-time = "2025-11-18T13:32:04.686Z" + url = "https://files.pythonhosted.org/packages/4e/ab/0d7fb2efc2e9a5eb7ddcc6e722f834a69b454b7e6e5888c3a8567ecffb31/coverage-7.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl" + hash = "sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e" + size = 253_843 + upload-time = "2026-03-17T10:30:31.301Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/38/24/f85b3843af1370fb3739fa7571819b71243daa311289b31214fe3e8c9d68/coverage-7.12.0-cp310-cp310-win32.whl" - hash = "sha256:7670d860e18b1e3ee5930b17a7d55ae6287ec6e55d9799982aa103a2cc1fa2ef" - size = 220_008 - upload-time = "2025-11-18T13:32:05.806Z" + url = "https://files.pythonhosted.org/packages/ba/6f/7467b917bbf5408610178f62a49c0ed4377bb16c1657f689cc61470da8ce/coverage-7.13.5-cp311-cp311-musllinux_1_2_riscv64.whl" + hash = "sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd" + size = 249_802 + upload-time = "2026-03-17T10:30:33.358Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/3a/a2/c7da5b9566f7164db9eefa133d17761ecb2c2fde9385d754e5b5c80f710d/coverage-7.12.0-cp310-cp310-win_amd64.whl" - hash = "sha256:f999813dddeb2a56aab5841e687b68169da0d3f6fc78ccf50952fa2463746022" - size = 220_943 - upload-time = "2025-11-18T13:32:07.166Z" + url = "https://files.pythonhosted.org/packages/75/2c/1172fb689df92135f5bfbbd69fc83017a76d24ea2e2f3a1154007e2fb9f8/coverage-7.13.5-cp311-cp311-musllinux_1_2_x86_64.whl" + hash = "sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8" + size = 250_707 + upload-time = "2026-03-17T10:30:35.2Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/5a/0c/0dfe7f0487477d96432e4815537263363fb6dd7289743a796e8e51eabdf2/coverage-7.12.0-cp311-cp311-macosx_10_9_x86_64.whl" - hash = "sha256:aa124a3683d2af98bd9d9c2bfa7a5076ca7e5ab09fdb96b81fa7d89376ae928f" - size = 217_535 - upload-time = "2025-11-18T13:32:08.812Z" + url = "https://files.pythonhosted.org/packages/67/21/9ac389377380a07884e3b48ba7a620fcd9dbfaf1d40565facdc6b36ec9ef/coverage-7.13.5-cp311-cp311-win32.whl" + hash = "sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf" + size = 221_880 + upload-time = "2026-03-17T10:30:36.775Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/9b/f5/f9a4a053a5bbff023d3bec259faac8f11a1e5a6479c2ccf586f910d8dac7/coverage-7.12.0-cp311-cp311-macosx_11_0_arm64.whl" - hash = "sha256:d93fbf446c31c0140208dcd07c5d882029832e8ed7891a39d6d44bd65f2316c3" - size = 218_044 - upload-time = "2025-11-18T13:32:10.329Z" + url = "https://files.pythonhosted.org/packages/af/7f/4cd8a92531253f9d7c1bbecd9fa1b472907fb54446ca768c59b531248dc5/coverage-7.13.5-cp311-cp311-win_amd64.whl" + hash = "sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9" + size = 222_816 + upload-time = "2026-03-17T10:30:38.891Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/95/c5/84fc3697c1fa10cd8571919bf9693f693b7373278daaf3b73e328d502bc8/coverage-7.12.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" - hash = "sha256:52ca620260bd8cd6027317bdd8b8ba929be1d741764ee765b42c4d79a408601e" - size = 248_440 - upload-time = "2025-11-18T13:32:12.536Z" + url = "https://files.pythonhosted.org/packages/12/a6/1d3f6155fb0010ca68eba7fe48ca6c9da7385058b77a95848710ecf189b1/coverage-7.13.5-cp311-cp311-win_arm64.whl" + hash = "sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028" + size = 221_483 + upload-time = "2026-03-17T10:30:40.463Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/f4/36/2d93fbf6a04670f3874aed397d5a5371948a076e3249244a9e84fb0e02d6/coverage-7.12.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" - hash = "sha256:f3433ffd541380f3a0e423cff0f4926d55b0cc8c1d160fdc3be24a4c03aa65f7" - size = 250_361 - upload-time = "2025-11-18T13:32:13.852Z" + url = "https://files.pythonhosted.org/packages/a0/c3/a396306ba7db865bf96fc1fb3b7fd29bcbf3d829df642e77b13555163cd6/coverage-7.13.5-cp312-cp312-macosx_10_13_x86_64.whl" + hash = "sha256:460cf0114c5016fa841214ff5564aa4864f11948da9440bc97e21ad1f4ba1e01" + size = 219_554 + upload-time = "2026-03-17T10:30:42.208Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/5d/49/66dc65cc456a6bfc41ea3d0758c4afeaa4068a2b2931bf83be6894cf1058/coverage-7.12.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:f7bbb321d4adc9f65e402c677cd1c8e4c2d0105d3ce285b51b4d87f1d5db5245" - size = 252_472 - upload-time = "2025-11-18T13:32:15.068Z" + url = "https://files.pythonhosted.org/packages/a6/16/a68a19e5384e93f811dccc51034b1fd0b865841c390e3c931dcc4699e035/coverage-7.13.5-cp312-cp312-macosx_11_0_arm64.whl" + hash = "sha256:0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422" + size = 219_908 + upload-time = "2026-03-17T10:30:43.906Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/35/1f/ebb8a18dffd406db9fcd4b3ae42254aedcaf612470e8712f12041325930f/coverage-7.12.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" - hash = "sha256:22a7aade354a72dff3b59c577bfd18d6945c61f97393bc5fb7bd293a4237024b" - size = 248_592 - upload-time = "2025-11-18T13:32:16.328Z" + url = "https://files.pythonhosted.org/packages/29/72/20b917c6793af3a5ceb7fb9c50033f3ec7865f2911a1416b34a7cfa0813b/coverage-7.13.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" + hash = "sha256:6e3370441f4513c6252bf042b9c36d22491142385049243253c7e48398a15a9f" + size = 251_419 + upload-time = "2026-03-17T10:30:45.545Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/da/a8/67f213c06e5ea3b3d4980df7dc344d7fea88240b5fe878a5dcbdfe0e2315/coverage-7.12.0-cp311-cp311-musllinux_1_2_aarch64.whl" - hash = "sha256:3ff651dcd36d2fea66877cd4a82de478004c59b849945446acb5baf9379a1b64" - size = 250_167 - upload-time = "2025-11-18T13:32:17.687Z" + url = "https://files.pythonhosted.org/packages/8c/49/cd14b789536ac6a4778c453c6a2338bc0a2fb60c5a5a41b4008328b9acc1/coverage-7.13.5-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" + hash = "sha256:03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5" + size = 254_159 + upload-time = "2026-03-17T10:30:47.204Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/f0/00/e52aef68154164ea40cc8389c120c314c747fe63a04b013a5782e989b77f/coverage-7.12.0-cp311-cp311-musllinux_1_2_i686.whl" - hash = "sha256:31b8b2e38391a56e3cea39d22a23faaa7c3fc911751756ef6d2621d2a9daf742" - size = 248_238 - upload-time = "2025-11-18T13:32:19.2Z" + url = "https://files.pythonhosted.org/packages/9d/00/7b0edcfe64e2ed4c0340dac14a52ad0f4c9bd0b8b5e531af7d55b703db7c/coverage-7.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + hash = "sha256:3f4818d065964db3c1c66dc0fbdac5ac692ecbc875555e13374fdbe7eedb4376" + size = 255_270 + upload-time = "2026-03-17T10:30:48.812Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/1f/a4/4d88750bcf9d6d66f77865e5a05a20e14db44074c25fd22519777cb69025/coverage-7.12.0-cp311-cp311-musllinux_1_2_riscv64.whl" - hash = "sha256:297bc2da28440f5ae51c845a47c8175a4db0553a53827886e4fb25c66633000c" - size = 247_964 - upload-time = "2025-11-18T13:32:21.027Z" + url = "https://files.pythonhosted.org/packages/93/89/7ffc4ba0f5d0a55c1e84ea7cee39c9fc06af7b170513d83fbf3bbefce280/coverage-7.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + hash = "sha256:012d5319e66e9d5a218834642d6c35d265515a62f01157a45bcc036ecf947256" + size = 257_538 + upload-time = "2026-03-17T10:30:50.77Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/a7/6b/b74693158899d5b47b0bf6238d2c6722e20ba749f86b74454fac0696bb00/coverage-7.12.0-cp311-cp311-musllinux_1_2_x86_64.whl" - hash = "sha256:6ff7651cc01a246908eac162a6a86fc0dbab6de1ad165dfb9a1e2ec660b44984" - size = 248_862 - upload-time = "2025-11-18T13:32:22.304Z" + url = "https://files.pythonhosted.org/packages/81/bd/73ddf85f93f7e6fa83e77ccecb6162d9415c79007b4bc124008a4995e4a7/coverage-7.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + hash = "sha256:8dd02af98971bdb956363e4827d34425cb3df19ee550ef92855b0acb9c7ce51c" + size = 251_821 + upload-time = "2026-03-17T10:30:52.5Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/18/de/6af6730227ce0e8ade307b1cc4a08e7f51b419a78d02083a86c04ccceb29/coverage-7.12.0-cp311-cp311-win32.whl" - hash = "sha256:313672140638b6ddb2c6455ddeda41c6a0b208298034544cfca138978c6baed6" - size = 220_033 - upload-time = "2025-11-18T13:32:23.714Z" + url = "https://files.pythonhosted.org/packages/a0/81/278aff4e8dec4926a0bcb9486320752811f543a3ce5b602cc7a29978d073/coverage-7.13.5-cp312-cp312-musllinux_1_2_aarch64.whl" + hash = "sha256:f08fd75c50a760c7eb068ae823777268daaf16a80b918fa58eea888f8e3919f5" + size = 253_191 + upload-time = "2026-03-17T10:30:54.543Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/e2/a1/e7f63021a7c4fe20994359fcdeae43cbef4a4d0ca36a5a1639feeea5d9e1/coverage-7.12.0-cp311-cp311-win_amd64.whl" - hash = "sha256:a1783ed5bd0d5938d4435014626568dc7f93e3cb99bc59188cc18857c47aa3c4" - size = 220_966 - upload-time = "2025-11-18T13:32:25.599Z" + url = "https://files.pythonhosted.org/packages/70/ee/fe1621488e2e0a58d7e94c4800f0d96f79671553488d401a612bebae324b/coverage-7.13.5-cp312-cp312-musllinux_1_2_i686.whl" + hash = "sha256:843ea8643cf967d1ac7e8ecd4bb00c99135adf4816c0c0593fdcc47b597fcf09" + size = 251_337 + upload-time = "2026-03-17T10:30:56.663Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/77/e8/deae26453f37c20c3aa0c4433a1e32cdc169bf415cce223a693117aa3ddd/coverage-7.12.0-cp311-cp311-win_arm64.whl" - hash = "sha256:4648158fd8dd9381b5847622df1c90ff314efbfc1df4550092ab6013c238a5fc" - size = 219_637 - upload-time = "2025-11-18T13:32:27.265Z" + url = "https://files.pythonhosted.org/packages/37/a6/f79fb37aa104b562207cc23cb5711ab6793608e246cae1e93f26b2236ed9/coverage-7.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl" + hash = "sha256:9d44d7aa963820b1b971dbecd90bfe5fe8f81cff79787eb6cca15750bd2f79b9" + size = 255_404 + upload-time = "2026-03-17T10:30:58.427Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/02/bf/638c0427c0f0d47638242e2438127f3c8ee3cfc06c7fdeb16778ed47f836/coverage-7.12.0-cp312-cp312-macosx_10_13_x86_64.whl" - hash = "sha256:29644c928772c78512b48e14156b81255000dcfd4817574ff69def189bcb3647" - size = 217_704 - upload-time = "2025-11-18T13:32:28.906Z" + url = "https://files.pythonhosted.org/packages/75/f0/ed15262a58ec81ce457ceb717b7f78752a1713556b19081b76e90896e8d4/coverage-7.13.5-cp312-cp312-musllinux_1_2_riscv64.whl" + hash = "sha256:7132bed4bd7b836200c591410ae7d97bf7ae8be6fc87d160b2bd881df929e7bf" + size = 250_903 + upload-time = "2026-03-17T10:31:00.093Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/08/e1/706fae6692a66c2d6b871a608bbde0da6281903fa0e9f53a39ed441da36a/coverage-7.12.0-cp312-cp312-macosx_11_0_arm64.whl" - hash = "sha256:8638cbb002eaa5d7c8d04da667813ce1067080b9a91099801a0053086e52b736" - size = 218_064 - upload-time = "2025-11-18T13:32:30.161Z" + url = "https://files.pythonhosted.org/packages/0f/e9/9129958f20e7e9d4d56d51d42ccf708d15cac355ff4ac6e736e97a9393d2/coverage-7.13.5-cp312-cp312-musllinux_1_2_x86_64.whl" + hash = "sha256:a698e363641b98843c517817db75373c83254781426e94ada3197cabbc2c919c" + size = 252_780 + upload-time = "2026-03-17T10:31:01.916Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/a9/8b/eb0231d0540f8af3ffda39720ff43cb91926489d01524e68f60e961366e4/coverage-7.12.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" - hash = "sha256:083631eeff5eb9992c923e14b810a179798bb598e6a0dd60586819fc23be6e60" - size = 249_560 - upload-time = "2025-11-18T13:32:31.835Z" + url = "https://files.pythonhosted.org/packages/a4/d7/0ad9b15812d81272db94379fe4c6df8fd17781cc7671fdfa30c76ba5ff7b/coverage-7.13.5-cp312-cp312-win32.whl" + hash = "sha256:bdba0a6b8812e8c7df002d908a9a2ea3c36e92611b5708633c50869e6d922fdf" + size = 222_093 + upload-time = "2026-03-17T10:31:03.642Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/e9/a1/67fb52af642e974d159b5b379e4d4c59d0ebe1288677fbd04bbffe665a82/coverage-7.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" - hash = "sha256:99d5415c73ca12d558e07776bd957c4222c687b9f1d26fa0e1b57e3598bdcde8" - size = 252_318 - upload-time = "2025-11-18T13:32:33.178Z" + url = "https://files.pythonhosted.org/packages/29/3d/821a9a5799fac2556bcf0bd37a70d1d11fa9e49784b6d22e92e8b2f85f18/coverage-7.13.5-cp312-cp312-win_amd64.whl" + hash = "sha256:d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810" + size = 222_900 + upload-time = "2026-03-17T10:31:05.651Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/41/e5/38228f31b2c7665ebf9bdfdddd7a184d56450755c7e43ac721c11a4b8dab/coverage-7.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:e949ebf60c717c3df63adb4a1a366c096c8d7fd8472608cd09359e1bd48ef59f" - size = 253_403 - upload-time = "2025-11-18T13:32:34.45Z" + url = "https://files.pythonhosted.org/packages/d4/fa/2238c2ad08e35cf4f020ea721f717e09ec3152aea75d191a7faf3ef009a8/coverage-7.13.5-cp312-cp312-win_arm64.whl" + hash = "sha256:bf69236a9a81bdca3bff53796237aab096cdbf8d78a66ad61e992d9dac7eb2de" + size = 221_515 + upload-time = "2026-03-17T10:31:07.293Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/ec/4b/df78e4c8188f9960684267c5a4897836f3f0f20a20c51606ee778a1d9749/coverage-7.12.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" - hash = "sha256:6d907ddccbca819afa2cd014bc69983b146cca2735a0b1e6259b2a6c10be1e70" - size = 249_984 - upload-time = "2025-11-18T13:32:35.747Z" + url = "https://files.pythonhosted.org/packages/74/8c/74fedc9663dcf168b0a059d4ea756ecae4da77a489048f94b5f512a8d0b3/coverage-7.13.5-cp313-cp313-macosx_10_13_x86_64.whl" + hash = "sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1" + size = 219_576 + upload-time = "2026-03-17T10:31:09.045Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/ba/51/bb163933d195a345c6f63eab9e55743413d064c291b6220df754075c2769/coverage-7.12.0-cp312-cp312-musllinux_1_2_aarch64.whl" - hash = "sha256:b1518ecbad4e6173f4c6e6c4a46e49555ea5679bf3feda5edb1b935c7c44e8a0" - size = 251_339 - upload-time = "2025-11-18T13:32:37.352Z" + url = "https://files.pythonhosted.org/packages/0c/c9/44fb661c55062f0818a6ffd2685c67aa30816200d5f2817543717d4b92eb/coverage-7.13.5-cp313-cp313-macosx_11_0_arm64.whl" + hash = "sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3" + size = 219_942 + upload-time = "2026-03-17T10:31:10.708Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/15/40/c9b29cdb8412c837cdcbc2cfa054547dd83affe6cbbd4ce4fdb92b6ba7d1/coverage-7.12.0-cp312-cp312-musllinux_1_2_i686.whl" - hash = "sha256:51777647a749abdf6f6fd8c7cffab12de68ab93aab15efc72fbbb83036c2a068" - size = 249_489 - upload-time = "2025-11-18T13:32:39.212Z" + url = "https://files.pythonhosted.org/packages/5f/13/93419671cee82b780bab7ea96b67c8ef448f5f295f36bf5031154ec9a790/coverage-7.13.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" + hash = "sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26" + size = 250_935 + upload-time = "2026-03-17T10:31:12.392Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/c8/da/b3131e20ba07a0de4437a50ef3b47840dfabf9293675b0cd5c2c7f66dd61/coverage-7.12.0-cp312-cp312-musllinux_1_2_riscv64.whl" - hash = "sha256:42435d46d6461a3b305cdfcad7cdd3248787771f53fe18305548cba474e6523b" - size = 249_070 - upload-time = "2025-11-18T13:32:40.598Z" + url = "https://files.pythonhosted.org/packages/ac/68/1666e3a4462f8202d836920114fa7a5ee9275d1fa45366d336c551a162dd/coverage-7.13.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" + hash = "sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3" + size = 253_541 + upload-time = "2026-03-17T10:31:14.247Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/70/81/b653329b5f6302c08d683ceff6785bc60a34be9ae92a5c7b63ee7ee7acec/coverage-7.12.0-cp312-cp312-musllinux_1_2_x86_64.whl" - hash = "sha256:5bcead88c8423e1855e64b8057d0544e33e4080b95b240c2a355334bb7ced937" - size = 250_929 - upload-time = "2025-11-18T13:32:42.915Z" + url = "https://files.pythonhosted.org/packages/4e/5e/3ee3b835647be646dcf3c65a7c6c18f87c27326a858f72ab22c12730773d/coverage-7.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + hash = "sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b" + size = 254_780 + upload-time = "2026-03-17T10:31:16.193Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/a3/00/250ac3bca9f252a5fb1338b5ad01331ebb7b40223f72bef5b1b2cb03aa64/coverage-7.12.0-cp312-cp312-win32.whl" - hash = "sha256:dcbb630ab034e86d2a0f79aefd2be07e583202f41e037602d438c80044957baa" - size = 220_241 - upload-time = "2025-11-18T13:32:44.665Z" + url = "https://files.pythonhosted.org/packages/44/b3/cb5bd1a04cfcc49ede6cd8409d80bee17661167686741e041abc7ee1b9a9/coverage-7.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + hash = "sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a" + size = 256_912 + upload-time = "2026-03-17T10:31:17.89Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/64/1c/77e79e76d37ce83302f6c21980b45e09f8aa4551965213a10e62d71ce0ab/coverage-7.12.0-cp312-cp312-win_amd64.whl" - hash = "sha256:2fd8354ed5d69775ac42986a691fbf68b4084278710cee9d7c3eaa0c28fa982a" - size = 221_051 - upload-time = "2025-11-18T13:32:46.008Z" + url = "https://files.pythonhosted.org/packages/1b/66/c1dceb7b9714473800b075f5c8a84f4588f887a90eb8645282031676e242/coverage-7.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + hash = "sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969" + size = 251_165 + upload-time = "2026-03-17T10:31:19.605Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/31/f5/641b8a25baae564f9e52cac0e2667b123de961985709a004e287ee7663cc/coverage-7.12.0-cp312-cp312-win_arm64.whl" - hash = "sha256:737c3814903be30695b2de20d22bcc5428fdae305c61ba44cdc8b3252984c49c" - size = 219_692 - upload-time = "2025-11-18T13:32:47.372Z" + url = "https://files.pythonhosted.org/packages/b7/62/5502b73b97aa2e53ea22a39cf8649ff44827bef76d90bf638777daa27a9d/coverage-7.13.5-cp313-cp313-musllinux_1_2_aarch64.whl" + hash = "sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161" + size = 252_908 + upload-time = "2026-03-17T10:31:21.312Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/b8/14/771700b4048774e48d2c54ed0c674273702713c9ee7acdfede40c2666747/coverage-7.12.0-cp313-cp313-macosx_10_13_x86_64.whl" - hash = "sha256:47324fffca8d8eae7e185b5bb20c14645f23350f870c1649003618ea91a78941" - size = 217_725 - upload-time = "2025-11-18T13:32:49.22Z" + url = "https://files.pythonhosted.org/packages/7d/37/7792c2d69854397ca77a55c4646e5897c467928b0e27f2d235d83b5d08c6/coverage-7.13.5-cp313-cp313-musllinux_1_2_i686.whl" + hash = "sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15" + size = 250_873 + upload-time = "2026-03-17T10:31:23.565Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/17/a7/3aa4144d3bcb719bf67b22d2d51c2d577bf801498c13cb08f64173e80497/coverage-7.12.0-cp313-cp313-macosx_11_0_arm64.whl" - hash = "sha256:ccf3b2ede91decd2fb53ec73c1f949c3e034129d1e0b07798ff1d02ea0c8fa4a" - size = 218_098 - upload-time = "2025-11-18T13:32:50.78Z" + url = "https://files.pythonhosted.org/packages/a3/23/bc866fb6163be52a8a9e5d708ba0d3b1283c12158cefca0a8bbb6e247a43/coverage-7.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl" + hash = "sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1" + size = 255_030 + upload-time = "2026-03-17T10:31:25.58Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/fc/9c/b846bbc774ff81091a12a10203e70562c91ae71badda00c5ae5b613527b1/coverage-7.12.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" - hash = "sha256:b365adc70a6936c6b0582dc38746b33b2454148c02349345412c6e743efb646d" - size = 249_093 - upload-time = "2025-11-18T13:32:52.554Z" + url = "https://files.pythonhosted.org/packages/7d/8b/ef67e1c222ef49860701d346b8bbb70881bef283bd5f6cbba68a39a086c7/coverage-7.13.5-cp313-cp313-musllinux_1_2_riscv64.whl" + hash = "sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6" + size = 250_694 + upload-time = "2026-03-17T10:31:27.316Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/76/b6/67d7c0e1f400b32c883e9342de4a8c2ae7c1a0b57c5de87622b7262e2309/coverage-7.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" - hash = "sha256:bc13baf85cd8a4cfcf4a35c7bc9d795837ad809775f782f697bf630b7e200211" - size = 251_686 - upload-time = "2025-11-18T13:32:54.862Z" + url = "https://files.pythonhosted.org/packages/46/0d/866d1f74f0acddbb906db212e096dee77a8e2158ca5e6bb44729f9d93298/coverage-7.13.5-cp313-cp313-musllinux_1_2_x86_64.whl" + hash = "sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17" + size = 252_469 + upload-time = "2026-03-17T10:31:29.472Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/cc/75/b095bd4b39d49c3be4bffbb3135fea18a99a431c52dd7513637c0762fecb/coverage-7.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:099d11698385d572ceafb3288a5b80fe1fc58bf665b3f9d362389de488361d3d" - size = 252_930 - upload-time = "2025-11-18T13:32:56.417Z" + url = "https://files.pythonhosted.org/packages/7a/f5/be742fec31118f02ce42b21c6af187ad6a344fed546b56ca60caacc6a9a0/coverage-7.13.5-cp313-cp313-win32.whl" + hash = "sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85" + size = 222_112 + upload-time = "2026-03-17T10:31:31.526Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/6e/f3/466f63015c7c80550bead3093aacabf5380c1220a2a93c35d374cae8f762/coverage-7.12.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" - hash = "sha256:473dc45d69694069adb7680c405fb1e81f60b2aff42c81e2f2c3feaf544d878c" - size = 249_296 - upload-time = "2025-11-18T13:32:58.074Z" + url = "https://files.pythonhosted.org/packages/66/40/7732d648ab9d069a46e686043241f01206348e2bbf128daea85be4d6414b/coverage-7.13.5-cp313-cp313-win_amd64.whl" + hash = "sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b" + size = 222_923 + upload-time = "2026-03-17T10:31:33.633Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/27/86/eba2209bf2b7e28c68698fc13437519a295b2d228ba9e0ec91673e09fa92/coverage-7.12.0-cp313-cp313-musllinux_1_2_aarch64.whl" - hash = "sha256:583f9adbefd278e9de33c33d6846aa8f5d164fa49b47144180a0e037f0688bb9" - size = 251_068 - upload-time = "2025-11-18T13:32:59.646Z" + url = "https://files.pythonhosted.org/packages/48/af/fea819c12a095781f6ccd504890aaddaf88b8fab263c4940e82c7b770124/coverage-7.13.5-cp313-cp313-win_arm64.whl" + hash = "sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664" + size = 221_540 + upload-time = "2026-03-17T10:31:35.445Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/ec/55/ca8ae7dbba962a3351f18940b359b94c6bafdd7757945fdc79ec9e452dc7/coverage-7.12.0-cp313-cp313-musllinux_1_2_i686.whl" - hash = "sha256:b2089cc445f2dc0af6f801f0d1355c025b76c24481935303cf1af28f636688f0" - size = 249_034 - upload-time = "2025-11-18T13:33:01.481Z" + url = "https://files.pythonhosted.org/packages/23/d2/17879af479df7fbbd44bd528a31692a48f6b25055d16482fdf5cdb633805/coverage-7.13.5-cp313-cp313t-macosx_10_13_x86_64.whl" + hash = "sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d" + size = 220_262 + upload-time = "2026-03-17T10:31:37.184Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/7a/d7/39136149325cad92d420b023b5fd900dabdd1c3a0d1d5f148ef4a8cedef5/coverage-7.12.0-cp313-cp313-musllinux_1_2_riscv64.whl" - hash = "sha256:950411f1eb5d579999c5f66c62a40961f126fc71e5e14419f004471957b51508" - size = 248_853 - upload-time = "2025-11-18T13:33:02.935Z" + url = "https://files.pythonhosted.org/packages/5b/4c/d20e554f988c8f91d6a02c5118f9abbbf73a8768a3048cb4962230d5743f/coverage-7.13.5-cp313-cp313t-macosx_11_0_arm64.whl" + hash = "sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0" + size = 220_617 + upload-time = "2026-03-17T10:31:39.245Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/fe/b6/76e1add8b87ef60e00643b0b7f8f7bb73d4bf5249a3be19ebefc5793dd25/coverage-7.12.0-cp313-cp313-musllinux_1_2_x86_64.whl" - hash = "sha256:b1aab7302a87bafebfe76b12af681b56ff446dc6f32ed178ff9c092ca776e6bc" - size = 250_619 - upload-time = "2025-11-18T13:33:04.336Z" + url = "https://files.pythonhosted.org/packages/29/9c/f9f5277b95184f764b24e7231e166dfdb5780a46d408a2ac665969416d61/coverage-7.13.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" + hash = "sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806" + size = 261_912 + upload-time = "2026-03-17T10:31:41.324Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/95/87/924c6dc64f9203f7a3c1832a6a0eee5a8335dbe5f1bdadcc278d6f1b4d74/coverage-7.12.0-cp313-cp313-win32.whl" - hash = "sha256:d7e0d0303c13b54db495eb636bc2465b2fb8475d4c8bcec8fe4b5ca454dfbae8" - size = 220_261 - upload-time = "2025-11-18T13:33:06.493Z" + url = "https://files.pythonhosted.org/packages/d5/f6/7f1ab39393eeb50cfe4747ae8ef0e4fc564b989225aa1152e13a180d74f8/coverage-7.13.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" + hash = "sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3" + size = 263_987 + upload-time = "2026-03-17T10:31:43.724Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/91/77/dd4aff9af16ff776bf355a24d87eeb48fc6acde54c907cc1ea89b14a8804/coverage-7.12.0-cp313-cp313-win_amd64.whl" - hash = "sha256:ce61969812d6a98a981d147d9ac583a36ac7db7766f2e64a9d4d059c2fe29d07" - size = 221_072 - upload-time = "2025-11-18T13:33:07.926Z" + url = "https://files.pythonhosted.org/packages/a0/d7/62c084fb489ed9c6fbdf57e006752e7c516ea46fd690e5ed8b8617c7d52e/coverage-7.13.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + hash = "sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9" + size = 266_416 + upload-time = "2026-03-17T10:31:45.769Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/70/49/5c9dc46205fef31b1b226a6e16513193715290584317fd4df91cdaf28b22/coverage-7.12.0-cp313-cp313-win_arm64.whl" - hash = "sha256:bcec6f47e4cb8a4c2dc91ce507f6eefc6a1b10f58df32cdc61dff65455031dfc" - size = 219_702 - upload-time = "2025-11-18T13:33:09.631Z" + url = "https://files.pythonhosted.org/packages/a9/f6/df63d8660e1a0bff6125947afda112a0502736f470d62ca68b288ea762d8/coverage-7.13.5-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + hash = "sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd" + size = 267_558 + upload-time = "2026-03-17T10:31:48.293Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/9b/62/f87922641c7198667994dd472a91e1d9b829c95d6c29529ceb52132436ad/coverage-7.12.0-cp313-cp313t-macosx_10_13_x86_64.whl" - hash = "sha256:459443346509476170d553035e4a3eed7b860f4fe5242f02de1010501956ce87" - size = 218_420 - upload-time = "2025-11-18T13:33:11.153Z" + url = "https://files.pythonhosted.org/packages/5b/02/353ca81d36779bd108f6d384425f7139ac3c58c750dcfaafe5d0bee6436b/coverage-7.13.5-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + hash = "sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606" + size = 261_163 + upload-time = "2026-03-17T10:31:50.125Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/85/dd/1cc13b2395ef15dbb27d7370a2509b4aee77890a464fb35d72d428f84871/coverage-7.12.0-cp313-cp313t-macosx_11_0_arm64.whl" - hash = "sha256:04a79245ab2b7a61688958f7a855275997134bc84f4a03bc240cf64ff132abf6" - size = 218_773 - upload-time = "2025-11-18T13:33:12.569Z" + url = "https://files.pythonhosted.org/packages/2c/16/2e79106d5749bcaf3aee6d309123548e3276517cd7851faa8da213bc61bf/coverage-7.13.5-cp313-cp313t-musllinux_1_2_aarch64.whl" + hash = "sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e" + size = 263_981 + upload-time = "2026-03-17T10:31:51.961Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/74/40/35773cc4bb1e9d4658d4fb669eb4195b3151bef3bbd6f866aba5cd5dac82/coverage-7.12.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" - hash = "sha256:09a86acaaa8455f13d6a99221d9654df249b33937b4e212b4e5a822065f12aa7" - size = 260_078 - upload-time = "2025-11-18T13:33:14.037Z" + url = "https://files.pythonhosted.org/packages/29/c7/c29e0c59ffa6942030ae6f50b88ae49988e7e8da06de7ecdbf49c6d4feae/coverage-7.13.5-cp313-cp313t-musllinux_1_2_i686.whl" + hash = "sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0" + size = 261_604 + upload-time = "2026-03-17T10:31:53.872Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/ec/ee/231bb1a6ffc2905e396557585ebc6bdc559e7c66708376d245a1f1d330fc/coverage-7.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" - hash = "sha256:907e0df1b71ba77463687a74149c6122c3f6aac56c2510a5d906b2f368208560" - size = 262_144 - upload-time = "2025-11-18T13:33:15.601Z" + url = "https://files.pythonhosted.org/packages/40/48/097cdc3db342f34006a308ab41c3a7c11c3f0d84750d340f45d88a782e00/coverage-7.13.5-cp313-cp313t-musllinux_1_2_ppc64le.whl" + hash = "sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87" + size = 265_321 + upload-time = "2026-03-17T10:31:55.997Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/28/be/32f4aa9f3bf0b56f3971001b56508352c7753915345d45fab4296a986f01/coverage-7.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:9b57e2d0ddd5f0582bae5437c04ee71c46cd908e7bc5d4d0391f9a41e812dd12" - size = 264_574 - upload-time = "2025-11-18T13:33:17.354Z" + url = "https://files.pythonhosted.org/packages/bb/1f/4994af354689e14fd03a75f8ec85a9a68d94e0188bbdab3fc1516b55e512/coverage-7.13.5-cp313-cp313t-musllinux_1_2_riscv64.whl" + hash = "sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479" + size = 260_502 + upload-time = "2026-03-17T10:31:58.308Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/68/7c/00489fcbc2245d13ab12189b977e0cf06ff3351cb98bc6beba8bd68c5902/coverage-7.12.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" - hash = "sha256:58c1c6aa677f3a1411fe6fb28ec3a942e4f665df036a3608816e0847fad23296" - size = 259_298 - upload-time = "2025-11-18T13:33:18.958Z" + url = "https://files.pythonhosted.org/packages/22/c6/9bb9ef55903e628033560885f5c31aa227e46878118b63ab15dc7ba87797/coverage-7.13.5-cp313-cp313t-musllinux_1_2_x86_64.whl" + hash = "sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2" + size = 262_688 + upload-time = "2026-03-17T10:32:00.141Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/96/b4/f0760d65d56c3bea95b449e02570d4abd2549dc784bf39a2d4721a2d8ceb/coverage-7.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl" - hash = "sha256:4c589361263ab2953e3c4cd2a94db94c4ad4a8e572776ecfbad2389c626e4507" - size = 262_150 - upload-time = "2025-11-18T13:33:20.644Z" + url = "https://files.pythonhosted.org/packages/14/4f/f5df9007e50b15e53e01edea486814783a7f019893733d9e4d6caad75557/coverage-7.13.5-cp313-cp313t-win32.whl" + hash = "sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a" + size = 222_788 + upload-time = "2026-03-17T10:32:02.246Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/c5/71/9a9314df00f9326d78c1e5a910f520d599205907432d90d1c1b7a97aa4b1/coverage-7.12.0-cp313-cp313t-musllinux_1_2_i686.whl" - hash = "sha256:91b810a163ccad2e43b1faa11d70d3cf4b6f3d83f9fd5f2df82a32d47b648e0d" - size = 259_763 - upload-time = "2025-11-18T13:33:22.189Z" + url = "https://files.pythonhosted.org/packages/e1/98/aa7fccaa97d0f3192bec013c4e6fd6d294a6ed44b640e6bb61f479e00ed5/coverage-7.13.5-cp313-cp313t-win_amd64.whl" + hash = "sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819" + size = 223_851 + upload-time = "2026-03-17T10:32:04.416Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/10/34/01a0aceed13fbdf925876b9a15d50862eb8845454301fe3cdd1df08b2182/coverage-7.12.0-cp313-cp313t-musllinux_1_2_riscv64.whl" - hash = "sha256:40c867af715f22592e0d0fb533a33a71ec9e0f73a6945f722a0c85c8c1cbe3a2" - size = 258_653 - upload-time = "2025-11-18T13:33:24.239Z" + url = "https://files.pythonhosted.org/packages/3d/8b/e5c469f7352651e5f013198e9e21f97510b23de957dd06a84071683b4b60/coverage-7.13.5-cp313-cp313t-win_arm64.whl" + hash = "sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911" + size = 222_104 + upload-time = "2026-03-17T10:32:06.65Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/8d/04/81d8fd64928acf1574bbb0181f66901c6c1c6279c8ccf5f84259d2c68ae9/coverage-7.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl" - hash = "sha256:68b0d0a2d84f333de875666259dadf28cc67858bc8fd8b3f1eae84d3c2bec455" - size = 260_856 - upload-time = "2025-11-18T13:33:26.365Z" + url = "https://files.pythonhosted.org/packages/8e/77/39703f0d1d4b478bfd30191d3c14f53caf596fac00efb3f8f6ee23646439/coverage-7.13.5-cp314-cp314-macosx_10_15_x86_64.whl" + hash = "sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f" + size = 219_621 + upload-time = "2026-03-17T10:32:08.589Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/f2/76/fa2a37bfaeaf1f766a2d2360a25a5297d4fb567098112f6517475eee120b/coverage-7.12.0-cp313-cp313t-win32.whl" - hash = "sha256:73f9e7fbd51a221818fd11b7090eaa835a353ddd59c236c57b2199486b116c6d" - size = 220_936 - upload-time = "2025-11-18T13:33:28.165Z" + url = "https://files.pythonhosted.org/packages/e2/3e/51dff36d99ae14639a133d9b164d63e628532e2974d8b1edb99dd1ebc733/coverage-7.13.5-cp314-cp314-macosx_11_0_arm64.whl" + hash = "sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e" + size = 219_953 + upload-time = "2026-03-17T10:32:10.507Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/f9/52/60f64d932d555102611c366afb0eb434b34266b1d9266fc2fe18ab641c47/coverage-7.12.0-cp313-cp313t-win_amd64.whl" - hash = "sha256:24cff9d1f5743f67db7ba46ff284018a6e9aeb649b67aa1e70c396aa1b7cb23c" - size = 222_001 - upload-time = "2025-11-18T13:33:29.656Z" + url = "https://files.pythonhosted.org/packages/6a/6c/1f1917b01eb647c2f2adc9962bd66c79eb978951cab61bdc1acab3290c07/coverage-7.13.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" + hash = "sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a" + size = 250_992 + upload-time = "2026-03-17T10:32:12.41Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/77/df/c303164154a5a3aea7472bf323b7c857fed93b26618ed9fc5c2955566bb0/coverage-7.12.0-cp313-cp313t-win_arm64.whl" - hash = "sha256:c87395744f5c77c866d0f5a43d97cc39e17c7f1cb0115e54a2fe67ca75c5d14d" - size = 220_273 - upload-time = "2025-11-18T13:33:31.415Z" + url = "https://files.pythonhosted.org/packages/22/e5/06b1f88f42a5a99df42ce61208bdec3bddb3d261412874280a19796fc09c/coverage-7.13.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" + hash = "sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510" + size = 253_503 + upload-time = "2026-03-17T10:32:14.449Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/bf/2e/fc12db0883478d6e12bbd62d481210f0c8daf036102aa11434a0c5755825/coverage-7.12.0-cp314-cp314-macosx_10_15_x86_64.whl" - hash = "sha256:a1c59b7dc169809a88b21a936eccf71c3895a78f5592051b1af8f4d59c2b4f92" - size = 217_777 - upload-time = "2025-11-18T13:33:32.86Z" + url = "https://files.pythonhosted.org/packages/80/28/2a148a51e5907e504fa7b85490277734e6771d8844ebcc48764a15e28155/coverage-7.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + hash = "sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247" + size = 254_852 + upload-time = "2026-03-17T10:32:16.56Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/1f/c1/ce3e525d223350c6ec16b9be8a057623f54226ef7f4c2fee361ebb6a02b8/coverage-7.12.0-cp314-cp314-macosx_11_0_arm64.whl" - hash = "sha256:8787b0f982e020adb732b9f051f3e49dd5054cebbc3f3432061278512a2b1360" - size = 218_100 - upload-time = "2025-11-18T13:33:34.532Z" + url = "https://files.pythonhosted.org/packages/61/77/50e8d3d85cc0b7ebe09f30f151d670e302c7ff4a1bf6243f71dd8b0981fa/coverage-7.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + hash = "sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6" + size = 257_161 + upload-time = "2026-03-17T10:32:19.004Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/15/87/113757441504aee3808cb422990ed7c8bcc2d53a6779c66c5adef0942939/coverage-7.12.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" - hash = "sha256:5ea5a9f7dc8877455b13dd1effd3202e0bca72f6f3ab09f9036b1bcf728f69ac" - size = 249_151 - upload-time = "2025-11-18T13:33:36.135Z" + url = "https://files.pythonhosted.org/packages/3b/c4/b5fd1d4b7bf8d0e75d997afd3925c59ba629fc8616f1b3aae7605132e256/coverage-7.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + hash = "sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0" + size = 251_021 + upload-time = "2026-03-17T10:32:21.344Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/d9/1d/9529d9bd44049b6b05bb319c03a3a7e4b0a8a802d28fa348ad407e10706d/coverage-7.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" - hash = "sha256:fdba9f15849534594f60b47c9a30bc70409b54947319a7c4fd0e8e3d8d2f355d" - size = 251_667 - upload-time = "2025-11-18T13:33:37.996Z" + url = "https://files.pythonhosted.org/packages/f8/66/6ea21f910e92d69ef0b1c3346ea5922a51bad4446c9126db2ae96ee24c4c/coverage-7.13.5-cp314-cp314-musllinux_1_2_aarch64.whl" + hash = "sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882" + size = 252_858 + upload-time = "2026-03-17T10:32:23.506Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/11/bb/567e751c41e9c03dc29d3ce74b8c89a1e3396313e34f255a2a2e8b9ebb56/coverage-7.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:a00594770eb715854fb1c57e0dea08cce6720cfbc531accdb9850d7c7770396c" - size = 253_003 - upload-time = "2025-11-18T13:33:39.553Z" + url = "https://files.pythonhosted.org/packages/9e/ea/879c83cb5d61aa2a35fb80e72715e92672daef8191b84911a643f533840c/coverage-7.13.5-cp314-cp314-musllinux_1_2_i686.whl" + hash = "sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740" + size = 250_823 + upload-time = "2026-03-17T10:32:25.516Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/e4/b3/c2cce2d8526a02fb9e9ca14a263ca6fc074449b33a6afa4892838c903528/coverage-7.12.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" - hash = "sha256:5560c7e0d82b42eb1951e4f68f071f8017c824ebfd5a6ebe42c60ac16c6c2434" - size = 249_185 - upload-time = "2025-11-18T13:33:42.086Z" + url = "https://files.pythonhosted.org/packages/8a/fb/616d95d3adb88b9803b275580bdeee8bd1b69a886d057652521f83d7322f/coverage-7.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl" + hash = "sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16" + size = 255_099 + upload-time = "2026-03-17T10:32:27.944Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/0e/a7/967f93bb66e82c9113c66a8d0b65ecf72fc865adfba5a145f50c7af7e58d/coverage-7.12.0-cp314-cp314-musllinux_1_2_aarch64.whl" - hash = "sha256:d6c2e26b481c9159c2773a37947a9718cfdc58893029cdfb177531793e375cfc" - size = 251_025 - upload-time = "2025-11-18T13:33:43.634Z" + url = "https://files.pythonhosted.org/packages/1c/93/25e6917c90ec1c9a56b0b26f6cad6408e5f13bb6b35d484a0d75c9cf000d/coverage-7.13.5-cp314-cp314-musllinux_1_2_riscv64.whl" + hash = "sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0" + size = 250_638 + upload-time = "2026-03-17T10:32:29.914Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/b9/b2/f2f6f56337bc1af465d5b2dc1ee7ee2141b8b9272f3bf6213fcbc309a836/coverage-7.12.0-cp314-cp314-musllinux_1_2_i686.whl" - hash = "sha256:6e1a8c066dabcde56d5d9fed6a66bc19a2883a3fe051f0c397a41fc42aedd4cc" - size = 248_979 - upload-time = "2025-11-18T13:33:46.04Z" + url = "https://files.pythonhosted.org/packages/fc/7b/dc1776b0464145a929deed214aef9fb1493f159b59ff3c7eeeedf91eddd0/coverage-7.13.5-cp314-cp314-musllinux_1_2_x86_64.whl" + hash = "sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0" + size = 252_295 + upload-time = "2026-03-17T10:32:31.981Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/f4/7a/bf4209f45a4aec09d10a01a57313a46c0e0e8f4c55ff2965467d41a92036/coverage-7.12.0-cp314-cp314-musllinux_1_2_riscv64.whl" - hash = "sha256:f7ba9da4726e446d8dd8aae5a6cd872511184a5d861de80a86ef970b5dacce3e" - size = 248_800 - upload-time = "2025-11-18T13:33:47.546Z" + url = "https://files.pythonhosted.org/packages/ea/fb/99cbbc56a26e07762a2740713f3c8f9f3f3106e3a3dd8cc4474954bccd34/coverage-7.13.5-cp314-cp314-win32.whl" + hash = "sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc" + size = 222_360 + upload-time = "2026-03-17T10:32:34.233Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/b8/b7/1e01b8696fb0521810f60c5bbebf699100d6754183e6cc0679bf2ed76531/coverage-7.12.0-cp314-cp314-musllinux_1_2_x86_64.whl" - hash = "sha256:e0f483ab4f749039894abaf80c2f9e7ed77bbf3c737517fb88c8e8e305896a17" - size = 250_460 - upload-time = "2025-11-18T13:33:49.537Z" + url = "https://files.pythonhosted.org/packages/8d/b7/4758d4f73fb536347cc5e4ad63662f9d60ba9118cb6785e9616b2ce5d7fa/coverage-7.13.5-cp314-cp314-win_amd64.whl" + hash = "sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633" + size = 223_174 + upload-time = "2026-03-17T10:32:36.369Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/71/ae/84324fb9cb46c024760e706353d9b771a81b398d117d8c1fe010391c186f/coverage-7.12.0-cp314-cp314-win32.whl" - hash = "sha256:76336c19a9ef4a94b2f8dc79f8ac2da3f193f625bb5d6f51a328cd19bfc19933" - size = 220_533 - upload-time = "2025-11-18T13:33:51.16Z" + url = "https://files.pythonhosted.org/packages/2c/f2/24d84e1dfe70f8ac9fdf30d338239860d0d1d5da0bda528959d0ebc9da28/coverage-7.13.5-cp314-cp314-win_arm64.whl" + hash = "sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8" + size = 221_739 + upload-time = "2026-03-17T10:32:38.736Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/e2/71/1033629deb8460a8f97f83e6ac4ca3b93952e2b6f826056684df8275e015/coverage-7.12.0-cp314-cp314-win_amd64.whl" - hash = "sha256:7c1059b600aec6ef090721f8f633f60ed70afaffe8ecab85b59df748f24b31fe" - size = 221_348 - upload-time = "2025-11-18T13:33:52.776Z" + url = "https://files.pythonhosted.org/packages/60/5b/4a168591057b3668c2428bff25dd3ebc21b629d666d90bcdfa0217940e84/coverage-7.13.5-cp314-cp314t-macosx_10_15_x86_64.whl" + hash = "sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b" + size = 220_351 + upload-time = "2026-03-17T10:32:41.196Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/0a/5f/ac8107a902f623b0c251abdb749be282dc2ab61854a8a4fcf49e276fce2f/coverage-7.12.0-cp314-cp314-win_arm64.whl" - hash = "sha256:172cf3a34bfef42611963e2b661302a8931f44df31629e5b1050567d6b90287d" - size = 219_922 - upload-time = "2025-11-18T13:33:54.316Z" + url = "https://files.pythonhosted.org/packages/f5/21/1fd5c4dbfe4a58b6b99649125635df46decdfd4a784c3cd6d410d303e370/coverage-7.13.5-cp314-cp314t-macosx_11_0_arm64.whl" + hash = "sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c" + size = 220_612 + upload-time = "2026-03-17T10:32:43.204Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/79/6e/f27af2d4da367f16077d21ef6fe796c874408219fa6dd3f3efe7751bd910/coverage-7.12.0-cp314-cp314t-macosx_10_15_x86_64.whl" - hash = "sha256:aa7d48520a32cb21c7a9b31f81799e8eaec7239db36c3b670be0fa2403828d1d" - size = 218_511 - upload-time = "2025-11-18T13:33:56.343Z" + url = "https://files.pythonhosted.org/packages/d6/fe/2a924b3055a5e7e4512655a9d4609781b0d62334fa0140c3e742926834e2/coverage-7.13.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" + hash = "sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9" + size = 261_985 + upload-time = "2026-03-17T10:32:45.514Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/67/dd/65fd874aa460c30da78f9d259400d8e6a4ef457d61ab052fd248f0050558/coverage-7.12.0-cp314-cp314t-macosx_11_0_arm64.whl" - hash = "sha256:90d58ac63bc85e0fb919f14d09d6caa63f35a5512a2205284b7816cafd21bb03" - size = 218_771 - upload-time = "2025-11-18T13:33:57.966Z" + url = "https://files.pythonhosted.org/packages/d7/0d/c8928f2bd518c45990fe1a2ab8db42e914ef9b726c975facc4282578c3eb/coverage-7.13.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" + hash = "sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29" + size = 264_107 + upload-time = "2026-03-17T10:32:47.971Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/55/e0/7c6b71d327d8068cb79c05f8f45bf1b6145f7a0de23bbebe63578fe5240a/coverage-7.12.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" - hash = "sha256:ca8ecfa283764fdda3eae1bdb6afe58bf78c2c3ec2b2edcb05a671f0bba7b3f9" - size = 260_151 - upload-time = "2025-11-18T13:33:59.597Z" + url = "https://files.pythonhosted.org/packages/ef/ae/4ae35bbd9a0af9d820362751f0766582833c211224b38665c0f8de3d487f/coverage-7.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + hash = "sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607" + size = 266_513 + upload-time = "2026-03-17T10:32:50.1Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/49/ce/4697457d58285b7200de6b46d606ea71066c6e674571a946a6ea908fb588/coverage-7.12.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" - hash = "sha256:874fe69a0785d96bd066059cd4368022cebbec1a8958f224f0016979183916e6" - size = 262_257 - upload-time = "2025-11-18T13:34:01.166Z" + url = "https://files.pythonhosted.org/packages/9c/20/d326174c55af36f74eac6ae781612d9492f060ce8244b570bb9d50d9d609/coverage-7.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + hash = "sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90" + size = 267_650 + upload-time = "2026-03-17T10:32:52.391Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/2f/33/acbc6e447aee4ceba88c15528dbe04a35fb4d67b59d393d2e0d6f1e242c1/coverage-7.12.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:5b3c889c0b8b283a24d721a9eabc8ccafcfc3aebf167e4cd0d0e23bf8ec4e339" - size = 264_671 - upload-time = "2025-11-18T13:34:02.795Z" + url = "https://files.pythonhosted.org/packages/7a/5e/31484d62cbd0eabd3412e30d74386ece4a0837d4f6c3040a653878bfc019/coverage-7.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + hash = "sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3" + size = 261_089 + upload-time = "2026-03-17T10:32:54.544Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/87/ec/e2822a795c1ed44d569980097be839c5e734d4c0c1119ef8e0a073496a30/coverage-7.12.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" - hash = "sha256:8bb5b894b3ec09dcd6d3743229dc7f2c42ef7787dc40596ae04c0edda487371e" - size = 259_231 - upload-time = "2025-11-18T13:34:04.397Z" + url = "https://files.pythonhosted.org/packages/e9/d8/49a72d6de146eebb0b7e48cc0f4bc2c0dd858e3d4790ab2b39a2872b62bd/coverage-7.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl" + hash = "sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab" + size = 263_982 + upload-time = "2026-03-17T10:32:56.803Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/72/c5/a7ec5395bb4a49c9b7ad97e63f0c92f6bf4a9e006b1393555a02dae75f16/coverage-7.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl" - hash = "sha256:79a44421cd5fba96aa57b5e3b5a4d3274c449d4c622e8f76882d76635501fd13" - size = 262_137 - upload-time = "2025-11-18T13:34:06.068Z" + url = "https://files.pythonhosted.org/packages/06/3b/0351f1bd566e6e4dd39e978efe7958bde1d32f879e85589de147654f57bb/coverage-7.13.5-cp314-cp314t-musllinux_1_2_i686.whl" + hash = "sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562" + size = 261_579 + upload-time = "2026-03-17T10:32:59.466Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/67/0c/02c08858b764129f4ecb8e316684272972e60777ae986f3865b10940bdd6/coverage-7.12.0-cp314-cp314t-musllinux_1_2_i686.whl" - hash = "sha256:33baadc0efd5c7294f436a632566ccc1f72c867f82833eb59820ee37dc811c6f" - size = 259_745 - upload-time = "2025-11-18T13:34:08.04Z" + url = "https://files.pythonhosted.org/packages/5d/ce/796a2a2f4017f554d7810f5c573449b35b1e46788424a548d4d19201b222/coverage-7.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl" + hash = "sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2" + size = 265_316 + upload-time = "2026-03-17T10:33:01.847Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/5a/04/4fd32b7084505f3829a8fe45c1a74a7a728cb251aaadbe3bec04abcef06d/coverage-7.12.0-cp314-cp314t-musllinux_1_2_riscv64.whl" - hash = "sha256:c406a71f544800ef7e9e0000af706b88465f3573ae8b8de37e5f96c59f689ad1" - size = 258_570 - upload-time = "2025-11-18T13:34:09.676Z" + url = "https://files.pythonhosted.org/packages/3d/16/d5ae91455541d1a78bc90abf495be600588aff8f6db5c8b0dae739fa39c9/coverage-7.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl" + hash = "sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea" + size = 260_427 + upload-time = "2026-03-17T10:33:03.945Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/48/35/2365e37c90df4f5342c4fa202223744119fe31264ee2924f09f074ea9b6d/coverage-7.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl" - hash = "sha256:e71bba6a40883b00c6d571599b4627f50c360b3d0d02bfc658168936be74027b" - size = 260_899 - upload-time = "2025-11-18T13:34:11.259Z" + url = "https://files.pythonhosted.org/packages/48/11/07f413dba62db21fb3fad5d0de013a50e073cc4e2dc4306e770360f6dfc8/coverage-7.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl" + hash = "sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a" + size = 262_745 + upload-time = "2026-03-17T10:33:06.285Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/05/56/26ab0464ca733fa325e8e71455c58c1c374ce30f7c04cebb88eabb037b18/coverage-7.12.0-cp314-cp314t-win32.whl" - hash = "sha256:9157a5e233c40ce6613dead4c131a006adfda70e557b6856b97aceed01b0e27a" - size = 221_313 - upload-time = "2025-11-18T13:34:12.863Z" + url = "https://files.pythonhosted.org/packages/91/15/d792371332eb4663115becf4bad47e047d16234b1aff687b1b18c58d60ae/coverage-7.13.5-cp314-cp314t-win32.whl" + hash = "sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215" + size = 223_146 + upload-time = "2026-03-17T10:33:08.756Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/da/1c/017a3e1113ed34d998b27d2c6dba08a9e7cb97d362f0ec988fcd873dcf81/coverage-7.12.0-cp314-cp314t-win_amd64.whl" - hash = "sha256:e84da3a0fd233aeec797b981c51af1cabac74f9bd67be42458365b30d11b5291" - size = 222_423 - upload-time = "2025-11-18T13:34:15.14Z" + url = "https://files.pythonhosted.org/packages/db/51/37221f59a111dca5e85be7dbf09696323b5b9f13ff65e0641d535ed06ea8/coverage-7.13.5-cp314-cp314t-win_amd64.whl" + hash = "sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43" + size = 224_254 + upload-time = "2026-03-17T10:33:11.174Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/4c/36/bcc504fdd5169301b52568802bb1b9cdde2e27a01d39fbb3b4b508ab7c2c/coverage-7.12.0-cp314-cp314t-win_arm64.whl" - hash = "sha256:01d24af36fedda51c2b1aca56e4330a3710f83b02a5ff3743a6b015ffa7c9384" - size = 220_459 - upload-time = "2025-11-18T13:34:17.222Z" + url = "https://files.pythonhosted.org/packages/54/83/6acacc889de8987441aa7d5adfbdbf33d288dad28704a67e574f1df9bcbb/coverage-7.13.5-cp314-cp314t-win_arm64.whl" + hash = "sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45" + size = 222_276 + upload-time = "2026-03-17T10:33:13.466Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/ce/a3/43b749004e3c09452e39bb56347a008f0a0668aad37324a99b5c8ca91d9e/coverage-7.12.0-py3-none-any.whl" - hash = "sha256:159d50c0b12e060b15ed3d39f87ed43d4f7f7ad40b8a534f4dd331adbb51104a" - size = 209_503 - upload-time = "2025-11-18T13:34:18.892Z" + url = "https://files.pythonhosted.org/packages/9e/ee/a4cf96b8ce1e566ed238f0659ac2d3f007ed1d14b181bcb684e19561a69a/coverage-7.13.5-py3-none-any.whl" + hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61" + size = 211_346 + upload-time = "2026-03-17T10:33:15.691Z" [[package.optional-dependencies.toml]] name = "tomli" @@ -872,47 +838,24 @@ version = "0.4.0" size = 469_047 upload-time = "2025-07-17T16:51:58.613Z" -[[package]] -name = "exceptiongroup" -version = "1.3.1" - - [package.source] - registry = "https://pypi.org/simple" - - [[package.dependencies]] - name = "typing-extensions" - marker = "python_full_version < '3.13'" - - [package.sdist] - url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz" - hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219" - size = 30_371 - upload-time = "2025-11-21T23:01:54.787Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl" - hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598" - size = 16_740 - upload-time = "2025-11-21T23:01:53.443Z" - [[package]] name = "filelock" -version = "3.20.3" +version = "3.29.0" [package.source] registry = "https://pypi.org/simple" [package.sdist] - url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz" - hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1" - size = 19_485 - upload-time = "2026-01-09T17:55:05.421Z" + url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz" + hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90" + size = 57_571 + upload-time = "2026-04-19T15:39:10.068Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl" - hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1" - size = 16_701 - upload-time = "2026-01-09T17:55:04.334Z" + url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl" + hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258" + size = 39_812 + upload-time = "2026-04-19T15:39:08.752Z" [[package]] name = "flake8" @@ -952,10 +895,6 @@ version = "1.2.4" [[package.dependencies]] name = "flake8" - [[package.dependencies]] - name = "tomli" - marker = "python_full_version < '3.11'" - [[package.wheels]] url = "https://files.pythonhosted.org/packages/85/6a/cdee9ff7f2b7c6ddc219fd95b7c70c0a3d9f0367a506e9793eedfc72e337/flake8_pyproject-1.2.4-py3-none-any.whl" hash = "sha256:ea34c057f9a9329c76d98723bb2bb498cc6ba8ff9872c4d19932d48c91249a77" @@ -964,22 +903,22 @@ version = "1.2.4" [[package]] name = "identify" -version = "2.6.15" +version = "2.6.19" [package.source] registry = "https://pypi.org/simple" [package.sdist] - url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz" - hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf" - size = 99_311 - upload-time = "2025-10-02T17:43:40.631Z" + url = "https://files.pythonhosted.org/packages/52/63/51723b5f116cc04b061cb6f5a561790abf249d25931d515cd375e063e0f4/identify-2.6.19.tar.gz" + hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842" + size = 99_567 + upload-time = "2026-04-17T18:39:50.265Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl" - hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757" - size = 99_183 - upload-time = "2025-10-02T17:43:39.137Z" + url = "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl" + hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a" + size = 99_397 + upload-time = "2026-04-17T18:39:49.221Z" [[package]] name = "iniconfig" @@ -1002,429 +941,429 @@ version = "2.3.0" [[package]] name = "isort" -version = "7.0.0" +version = "8.0.1" [package.source] registry = "https://pypi.org/simple" [package.sdist] - url = "https://files.pythonhosted.org/packages/63/53/4f3c058e3bace40282876f9b553343376ee687f3c35a525dc79dbd450f88/isort-7.0.0.tar.gz" - hash = "sha256:5513527951aadb3ac4292a41a16cbc50dd1642432f5e8c20057d414bdafb4187" - size = 805_049 - upload-time = "2025-10-11T13:30:59.107Z" + url = "https://files.pythonhosted.org/packages/ef/7c/ec4ab396d31b3b395e2e999c8f46dec78c5e29209fac49d1f4dace04041d/isort-8.0.1.tar.gz" + hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d" + size = 769_592 + upload-time = "2026-02-28T10:08:20.685Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/7f/ed/e3705d6d02b4f7aea715a353c8ce193efd0b5db13e204df895d38734c244/isort-7.0.0-py3-none-any.whl" - hash = "sha256:1bcabac8bc3c36c7fb7b98a76c8abb18e0f841a3ba81decac7691008592499c1" - size = 94_672 - upload-time = "2025-10-11T13:30:57.665Z" + url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl" + hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75" + size = 89_733 + upload-time = "2026-02-28T10:08:19.466Z" [[package]] name = "librt" -version = "0.6.3" +version = "0.10.0" [package.source] registry = "https://pypi.org/simple" [package.sdist] - url = "https://files.pythonhosted.org/packages/37/c3/cdff3c10e2e608490dc0a310ccf11ba777b3943ad4fcead2a2ade98c21e1/librt-0.6.3.tar.gz" - hash = "sha256:c724a884e642aa2bbad52bb0203ea40406ad742368a5f90da1b220e970384aae" - size = 54_209 - upload-time = "2025-11-29T14:01:56.058Z" + url = "https://files.pythonhosted.org/packages/39/cb/c1945e506893b5b8577fb45a60c80e3ffe4a82092a04a6f29b0b951d9a24/librt-0.10.0.tar.gz" + hash = "sha256:1aba1e8aa4e3307a7be68a74149545fde7451964dc0235a8bec5704a17bdda42" + size = 191_799 + upload-time = "2026-05-05T16:31:23.535Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/a6/84/859df8db21dedab2538ddfbe1d486dda3eb66a98c6ad7ba754a99e25e45e/librt-0.6.3-cp310-cp310-macosx_10_9_x86_64.whl" - hash = "sha256:45660d26569cc22ed30adf583389d8a0d1b468f8b5e518fcf9bfe2cd298f9dd1" - size = 27_294 - upload-time = "2025-11-29T14:00:35.053Z" + 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 = 76_045 + upload-time = "2026-05-05T16:29:18.731Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/f7/01/ec3971cf9c4f827f17de6729bdfdbf01a67493147334f4ef8fac68936e3a/librt-0.6.3-cp310-cp310-macosx_11_0_arm64.whl" - hash = "sha256:54f3b2177fb892d47f8016f1087d21654b44f7fc4cf6571c1c6b3ea531ab0fcf" - size = 27_635 - upload-time = "2025-11-29T14:00:36.496Z" + url = "https://files.pythonhosted.org/packages/a6/31/bfe32355d4b369aef3d7aa442df663bb5558c2ffa2de286cb2956346bc24/librt-0.10.0-cp311-cp311-macosx_11_0_arm64.whl" + hash = "sha256:6b7e42d1b3e300d20bfc87e72ffd62f0a92a2cb3c35f7bf90df90c9d2a49f74c" + size = 79_466 + upload-time = "2026-05-05T16:29:20.052Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/b4/f9/3efe201df84dd26388d2e0afa4c4dc668c8e406a3da7b7319152faf835a1/librt-0.6.3-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" - hash = "sha256:c5b31bed2c2f2fa1fcb4815b75f931121ae210dc89a3d607fb1725f5907f1437" - size = 81_768 - upload-time = "2025-11-29T14:00:37.451Z" + 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 = 242_283 + upload-time = "2026-05-05T16:29:21.596Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/0a/13/f63e60bc219b17f3d8f3d13423cd4972e597b0321c51cac7bfbdd5e1f7b9/librt-0.6.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:8f8ed5053ef9fb08d34f1fd80ff093ccbd1f67f147633a84cf4a7d9b09c0f089" - size = 85_884 - upload-time = "2025-11-29T14:00:38.433Z" + 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 = 230_735 + upload-time = "2026-05-05T16:29:23.335Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/c2/42/0068f14f39a79d1ce8a19d4988dd07371df1d0a7d3395fbdc8a25b1c9437/librt-0.6.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:3f0e4bd9bcb0ee34fa3dbedb05570da50b285f49e52c07a241da967840432513" - size = 85_830 - upload-time = "2025-11-29T14:00:39.418Z" + 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 = 256_606 + upload-time = "2026-05-05T16:29:24.91Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/14/1c/87f5af3a9e6564f09e50c72f82fc3057fd42d1facc8b510a707d0438c4ad/librt-0.6.3-cp310-cp310-musllinux_1_2_aarch64.whl" - hash = "sha256:d8f89c8d20dfa648a3f0a56861946eb00e5b00d6b00eea14bc5532b2fcfa8ef1" - size = 88_086 - upload-time = "2025-11-29T14:00:40.555Z" + 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 = 249_739 + upload-time = "2026-05-05T16:29:26.648Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/05/e5/22153b98b88a913b5b3f266f12e57df50a2a6960b3f8fcb825b1a0cfe40a/librt-0.6.3-cp310-cp310-musllinux_1_2_i686.whl" - hash = "sha256:ecc2c526547eacd20cb9fbba19a5268611dbc70c346499656d6cf30fae328977" - size = 86_470 - upload-time = "2025-11-29T14:00:41.827Z" + url = "https://files.pythonhosted.org/packages/b2/f5/dc5b7eb294656ad23d4ff4cf8514208d54fe1026b909d726a0dc026689c9/librt-0.10.0-cp311-cp311-musllinux_1_2_aarch64.whl" + hash = "sha256:c3e33747c068e86a9007c20fdb777eb5ba8d3d19136d7812f88e69a713041b6f" + size = 261_414 + upload-time = "2026-05-05T16:29:28.702Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/18/3c/ea1edb587799b1edcc22444e0630fa422e32d7aaa5bfb5115b948acc2d1c/librt-0.6.3-cp310-cp310-musllinux_1_2_x86_64.whl" - hash = "sha256:fbedeb9b48614d662822ee514567d2d49a8012037fc7b4cd63f282642c2f4b7d" - size = 89_079 - upload-time = "2025-11-29T14:00:42.882Z" + url = "https://files.pythonhosted.org/packages/58/e4/990ed8d12c7f114ac8f8ccd47f7d9bd9704ef61acfcb1df4a05047da7710/librt-0.10.0-cp311-cp311-musllinux_1_2_i686.whl" + hash = "sha256:d509c745bf7e77d1107cf05e6abb249dc03fad13eb39f2286a49deedaeb2bcd7" + size = 256_614 + upload-time = "2026-05-05T16:29:30.357Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/73/ad/50bb4ae6b07c9f3ab19653e0830a210533b30eb9a18d515efb5a2b9d0c7c/librt-0.6.3-cp310-cp310-win32.whl" - hash = "sha256:0765b0fe0927d189ee14b087cd595ae636bef04992e03fe6dfdaa383866c8a46" - size = 19_820 - upload-time = "2025-11-29T14:00:44.211Z" + url = "https://files.pythonhosted.org/packages/60/eb/52d2726c7fb22818507dc3cc166c8f36dd4a4b68a7be67f12006ac8777c1/librt-0.10.0-cp311-cp311-musllinux_1_2_riscv64.whl" + hash = "sha256:786ad5a15e99d0e0e74f3adbeecc198a5ac58f340be07e984723d1e0074838de" + size = 255_144 + upload-time = "2026-05-05T16:29:32.106Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/7a/12/7426ee78f3b1dbe11a90619d54cb241ca924ca3c0ff9ade3992178e9b440/librt-0.6.3-cp310-cp310-win_amd64.whl" - hash = "sha256:8c659f9fb8a2f16dc4131b803fa0144c1dadcb3ab24bb7914d01a6da58ae2457" - size = 21_332 - upload-time = "2025-11-29T14:00:45.427Z" + 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 = 279_121 + upload-time = "2026-05-05T16:29:33.688Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/8b/80/bc60fd16fe24910bf5974fb914778a2e8540cef55385ab2cb04a0dfe42c4/librt-0.6.3-cp311-cp311-macosx_10_9_x86_64.whl" - hash = "sha256:61348cc488b18d1b1ff9f3e5fcd5ac43ed22d3e13e862489d2267c2337285c08" - size = 27_285 - upload-time = "2025-11-29T14:00:46.626Z" + url = "https://files.pythonhosted.org/packages/fd/df/7c2b838dfc89a1762dd156d8b0c39848a7a2845d725a50be5a6e021fb8ba/librt-0.10.0-cp311-cp311-win32.whl" + hash = "sha256:75ecdc3f5a90065aa2af2e574706c5495adc392520762dcf10b1aa716f0b8090" + size = 62_593 + upload-time = "2026-05-05T16:29:35.152Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/88/3c/26335536ed9ba097c79cffcee148393592e55758fe76d99015af3e47a6d0/librt-0.6.3-cp311-cp311-macosx_11_0_arm64.whl" - hash = "sha256:64645b757d617ad5f98c08e07620bc488d4bced9ced91c6279cec418f16056fa" - size = 27_629 - upload-time = "2025-11-29T14:00:47.863Z" + url = "https://files.pythonhosted.org/packages/91/19/22ff572981049a9d436a083dbea1572d0f5dc068b7353637d2dd9977c8f1/librt-0.10.0-cp311-cp311-win_amd64.whl" + hash = "sha256:b6f6084884131d8a52cb9d7095ff2aa52c1e786d9fdaefab1fb4515415e9e083" + size = 70_914 + upload-time = "2026-05-05T16:29:36.407Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/af/fd/2dcedeacfedee5d2eda23e7a49c1c12ce6221b5d58a13555f053203faafc/librt-0.6.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" - hash = "sha256:26b8026393920320bb9a811b691d73c5981385d537ffc5b6e22e53f7b65d4122" - size = 82_039 - upload-time = "2025-11-29T14:00:49.131Z" + url = "https://files.pythonhosted.org/packages/12/22/1697cc64f4a5c7e9bce55e99c6d234a346beaedaefcd1e2ca90dd285f98c/librt-0.10.0-cp311-cp311-win_arm64.whl" + hash = "sha256:0140bd62151160047e89b2730cb6f8506cdac5127baa1afb9231e4dd3fe7f681" + size = 61_176 + upload-time = "2026-05-05T16:29:37.62Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/48/ff/6aa11914b83b0dc2d489f7636942a8e3322650d0dba840db9a1b455f3caa/librt-0.6.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:d998b432ed9ffccc49b820e913c8f327a82026349e9c34fa3690116f6b70770f" - size = 86_560 - upload-time = "2025-11-29T14:00:50.403Z" + 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 = 77_327 + upload-time = "2026-05-05T16:29:38.919Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/76/a1/d25af61958c2c7eb978164aeba0350719f615179ba3f428b682b9a5fdace/librt-0.6.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:e18875e17ef69ba7dfa9623f2f95f3eda6f70b536079ee6d5763ecdfe6cc9040" - size = 86_494 - upload-time = "2025-11-29T14:00:51.383Z" + url = "https://files.pythonhosted.org/packages/e9/3d/8233cbee8e99e6a8992f02bfc2dec8d787509566a511d1fde2574ee7473f/librt-0.10.0-cp312-cp312-macosx_11_0_arm64.whl" + hash = "sha256:950b79b11762531bdf45a9df909d2f9a2a8445c70c88665c01d14c8511a27dc5" + size = 79_971 + upload-time = "2026-05-05T16:29:40.96Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/7d/4b/40e75d3b258c801908e64b39788f9491635f9554f8717430a491385bd6f2/librt-0.6.3-cp311-cp311-musllinux_1_2_aarch64.whl" - hash = "sha256:a218f85081fc3f70cddaed694323a1ad7db5ca028c379c214e3a7c11c0850523" - size = 88_914 - upload-time = "2025-11-29T14:00:52.688Z" + 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 = 246_559 + upload-time = "2026-05-05T16:29:42.701Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/97/6d/0070c81aba8a169224301c75fb5fb6c3c25ca67e6ced086584fc130d5a67/librt-0.6.3-cp311-cp311-musllinux_1_2_i686.whl" - hash = "sha256:1ef42ff4edd369e84433ce9b188a64df0837f4f69e3d34d3b34d4955c599d03f" - size = 86_944 - upload-time = "2025-11-29T14:00:53.768Z" + 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 = 235_216 + upload-time = "2026-05-05T16:29:44.193Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/a6/94/809f38887941b7726692e0b5a083dbdc87dbb8cf893e3b286550c5f0b129/librt-0.6.3-cp311-cp311-musllinux_1_2_x86_64.whl" - hash = "sha256:0e0f2b79993fec23a685b3e8107ba5f8675eeae286675a216da0b09574fa1e47" - size = 89_852 - upload-time = "2025-11-29T14:00:54.71Z" + 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 = 263_108 + upload-time = "2026-05-05T16:29:46.365Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/58/a3/b0e5b1cda675b91f1111d8ba941da455d8bfaa22f4d2d8963ba96ccb5b12/librt-0.6.3-cp311-cp311-win32.whl" - hash = "sha256:fd98cacf4e0fabcd4005c452cb8a31750258a85cab9a59fb3559e8078da408d7" - size = 19_948 - upload-time = "2025-11-29T14:00:55.989Z" + 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 = 255_280 + upload-time = "2026-05-05T16:29:48.087Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/cc/73/70011c2b37e3be3ece3affd3abc8ebe5cda482b03fd6b3397906321a901e/librt-0.6.3-cp311-cp311-win_amd64.whl" - hash = "sha256:e17b5b42c8045867ca9d1f54af00cc2275198d38de18545edaa7833d7e9e4ac8" - size = 21_406 - upload-time = "2025-11-29T14:00:56.874Z" + url = "https://files.pythonhosted.org/packages/ed/c5/ba301d571d9e05844e2435b73aba30bee77bb75ce155c9affcfd2173dd03/librt-0.10.0-cp312-cp312-musllinux_1_2_aarch64.whl" + hash = "sha256:bc8771c9fcf0ea894ca41fdc2abd83572c2fbda221f232d86e718614e57ff513" + size = 268_829 + upload-time = "2026-05-05T16:29:49.628Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/91/ee/119aa759290af6ca0729edf513ca390c1afbeae60f3ecae9b9d56f25a8a9/librt-0.6.3-cp311-cp311-win_arm64.whl" - hash = "sha256:87597e3d57ec0120a3e1d857a708f80c02c42ea6b00227c728efbc860f067c45" - size = 20_875 - upload-time = "2025-11-29T14:00:57.752Z" + url = "https://files.pythonhosted.org/packages/8b/60/af70e135bc1f1fe15dd3894b1e4bbefc7ecdf911749a925a39eb86ceb2a1/librt-0.10.0-cp312-cp312-musllinux_1_2_i686.whl" + hash = "sha256:70805dbc5257892ac572f86290a61e3c8d90224ecce1a8b2d1f7ed51965417f4" + size = 262_051 + upload-time = "2026-05-05T16:29:51.244Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/b4/2c/b59249c566f98fe90e178baf59e83f628d6c38fb8bc78319301fccda0b5e/librt-0.6.3-cp312-cp312-macosx_10_13_x86_64.whl" - hash = "sha256:74418f718083009108dc9a42c21bf2e4802d49638a1249e13677585fcc9ca176" - size = 27_841 - upload-time = "2025-11-29T14:00:58.925Z" + url = "https://files.pythonhosted.org/packages/83/c2/c8236eb8b421bac5a172ba208f965abaa89805da2a3fa112bdf1764caf8f/librt-0.10.0-cp312-cp312-musllinux_1_2_riscv64.whl" + hash = "sha256:d3b4f300f7bcba6e2ff73fb8bef1898479e9772bfa2682998c636391633ec826" + size = 264_347 + upload-time = "2026-05-05T16:29:53.013Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/40/e8/9db01cafcd1a2872b76114c858f81cc29ce7ad606bc102020d6dabf470fb/librt-0.6.3-cp312-cp312-macosx_11_0_arm64.whl" - hash = "sha256:514f3f363d1ebc423357d36222c37e5c8e6674b6eae8d7195ac9a64903722057" - size = 27_844 - upload-time = "2025-11-29T14:01:00.2Z" + 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 = 286_482 + upload-time = "2026-05-05T16:29:54.675Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/59/4d/da449d3a7d83cc853af539dee42adc37b755d7eea4ad3880bacfd84b651d/librt-0.6.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" - hash = "sha256:cf1115207a5049d1f4b7b4b72de0e52f228d6c696803d94843907111cbf80610" - size = 84_091 - upload-time = "2025-11-29T14:01:01.118Z" + url = "https://files.pythonhosted.org/packages/fb/8e/b1b959bacd323eb4360579db992513e1406d1c6ef7edb57b5511fd0666fd/librt-0.10.0-cp312-cp312-win32.whl" + hash = "sha256:6065c1a758fba1010b41401013903d3d5d2750eab425ddedd584abac31d0630e" + size = 62_955 + upload-time = "2026-05-05T16:29:56.39Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/ea/6c/f90306906fb6cc6eaf4725870f0347115de05431e1f96d35114392d31fda/librt-0.6.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:ad8ba80cdcea04bea7b78fcd4925bfbf408961e9d8397d2ee5d3ec121e20c08c" - size = 88_239 - upload-time = "2025-11-29T14:01:02.11Z" + url = "https://files.pythonhosted.org/packages/9e/4c/d4cd6e4b9fc24098e63cc85537d1b6689682aee96809c38f08072067cc2b/librt-0.10.0-cp312-cp312-win_amd64.whl" + hash = "sha256:d788ecbe208ab352dab0e105cc06057bf9a2fc7e58cabb0d751ad9e30062b9e2" + size = 71_191 + upload-time = "2026-05-05T16:29:57.682Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/e7/ae/473ce7b423cfac2cb503851a89d9d2195bf615f534d5912bf86feeebbee7/librt-0.6.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:4018904c83eab49c814e2494b4e22501a93cdb6c9f9425533fe693c3117126f9" - size = 88_815 - upload-time = "2025-11-29T14:01:03.114Z" + url = "https://files.pythonhosted.org/packages/2b/19/8641da1f63d24b92354a492f893c022d6b3a0df44e70c8eff49364613983/librt-0.10.0-cp312-cp312-win_arm64.whl" + hash = "sha256:6003d1f295bdba02656dc81308208fc060d0a51d8c0d0a6db70f7f3c57b9ba0a" + size = 61_432 + upload-time = "2026-05-05T16:29:58.971Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/c4/6d/934df738c87fb9617cabefe4891eece585a06abe6def25b4bca3b174429d/librt-0.6.3-cp312-cp312-musllinux_1_2_aarch64.whl" - hash = "sha256:8983c5c06ac9c990eac5eb97a9f03fe41dc7e9d7993df74d9e8682a1056f596c" - size = 90_598 - upload-time = "2025-11-29T14:01:04.071Z" + 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 = 77_299 + upload-time = "2026-05-05T16:30:00.209Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/72/89/eeaa124f5e0f431c2b39119550378ae817a4b1a3c93fd7122f0639336fff/librt-0.6.3-cp312-cp312-musllinux_1_2_i686.whl" - hash = "sha256:d7769c579663a6f8dbf34878969ac71befa42067ce6bf78e6370bf0d1194997c" - size = 88_603 - upload-time = "2025-11-29T14:01:05.02Z" + url = "https://files.pythonhosted.org/packages/62/24/0c7ca445a55d04be79cac19819437fd094782347fa116f6681844fa6143e/librt-0.10.0-cp313-cp313-macosx_11_0_arm64.whl" + hash = "sha256:e0ba0b131fdb336c8b9c948e397f4a7e649d0f783b529f07b647bf4961df392e" + size = 79_930 + upload-time = "2026-05-05T16:30:01.555Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/4d/ed/c60b3c1cfc27d709bc0288af428ce58543fcb5053cf3eadbc773c24257f5/librt-0.6.3-cp312-cp312-musllinux_1_2_x86_64.whl" - hash = "sha256:d3c9a07eafdc70556f8c220da4a538e715668c0c63cabcc436a026e4e89950bf" - size = 92_112 - upload-time = "2025-11-29T14:01:06.304Z" + 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 = 246_195 + upload-time = "2026-05-05T16:30:03.261Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/c1/ab/f56169be5f716ef4ab0277be70bcb1874b4effc262e655d85b505af4884d/librt-0.6.3-cp312-cp312-win32.whl" - hash = "sha256:38320386a48a15033da295df276aea93a92dfa94a862e06893f75ea1d8bbe89d" - size = 20_127 - upload-time = "2025-11-29T14:01:07.283Z" + 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 = 234_951 + upload-time = "2026-05-05T16:30:04.881Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/ff/8d/222750ce82bf95125529eaab585ac7e2829df252f3cfc05d68792fb1dd2c/librt-0.6.3-cp312-cp312-win_amd64.whl" - hash = "sha256:c0ecf4786ad0404b072196b5df774b1bb23c8aacdcacb6c10b4128bc7b00bd01" - size = 21_545 - upload-time = "2025-11-29T14:01:08.184Z" + 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 = 262_768 + upload-time = "2026-05-05T16:30:06.638Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/72/c9/f731ddcfb72f446a92a8674c6b8e1e2242773cce43a04f41549bd8b958ff/librt-0.6.3-cp312-cp312-win_arm64.whl" - hash = "sha256:9f2a6623057989ebc469cd9cc8fe436c40117a0147627568d03f84aef7854c55" - size = 20_946 - upload-time = "2025-11-29T14:01:09.384Z" + 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 = 255_075 + upload-time = "2026-05-05T16:30:08.216Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/dd/aa/3055dd440f8b8b3b7e8624539a0749dd8e1913e978993bcca9ce7e306231/librt-0.6.3-cp313-cp313-macosx_10_13_x86_64.whl" - hash = "sha256:9e716f9012148a81f02f46a04fc4c663420c6fbfeacfac0b5e128cf43b4413d3" - size = 27_874 - upload-time = "2025-11-29T14:01:10.615Z" + url = "https://files.pythonhosted.org/packages/35/1e/25bac4c7f2ca36f0e612cade186970683cf79153d96beccc3a11a9e19b97/librt-0.10.0-cp313-cp313-musllinux_1_2_aarch64.whl" + hash = "sha256:4efa7b9587503fa5b67f40593302b9c8836d211d222ff9f7cafe67be5f8f0b10" + size = 268_559 + upload-time = "2026-05-05T16:30:10.1Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/ef/93/226d7dd455eaa4c26712b5ccb2dfcca12831baa7f898c8ffd3a831e29fda/librt-0.6.3-cp313-cp313-macosx_11_0_arm64.whl" - hash = "sha256:669ff2495728009a96339c5ad2612569c6d8be4474e68f3f3ac85d7c3261f5f5" - size = 27_852 - upload-time = "2025-11-29T14:01:11.535Z" + url = "https://files.pythonhosted.org/packages/18/54/4601faab35b6632a13200faa146ca62bfd111ffbe2568be430d65c89493a/librt-0.10.0-cp313-cp313-musllinux_1_2_i686.whl" + hash = "sha256:22dc982ef59df0136df36092ccbdbb570ced8aafb33e49585739b2f1de1c13b6" + size = 261_753 + upload-time = "2026-05-05T16:30:11.912Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/4e/8b/db9d51191aef4e4cc06285250affe0bb0ad8b2ed815f7ca77951655e6f02/librt-0.6.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" - hash = "sha256:349b6873ebccfc24c9efd244e49da9f8a5c10f60f07575e248921aae2123fc42" - size = 84_264 - upload-time = "2025-11-29T14:01:12.461Z" + url = "https://files.pythonhosted.org/packages/1b/cf/39f4023509e94fade8b074666fa3292db9cb6b34ea5dcbe7af53df9fca1d/librt-0.10.0-cp313-cp313-musllinux_1_2_riscv64.whl" + hash = "sha256:6f2e5f3606253a84cea719c94a3bb1c54487b5d617d0254d46e0920d8a06be3f" + size = 264_055 + upload-time = "2026-05-05T16:30:13.465Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/8d/53/297c96bda3b5a73bdaf748f1e3ae757edd29a0a41a956b9c10379f193417/librt-0.6.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:0c74c26736008481c9f6d0adf1aedb5a52aff7361fea98276d1f965c0256ee70" - size = 88_432 - upload-time = "2025-11-29T14:01:13.405Z" + 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 = 286_190 + upload-time = "2026-05-05T16:30:15.073Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/54/3a/c005516071123278e340f22de72fa53d51e259d49215295c212da16c4dc2/librt-0.6.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:408a36ddc75e91918cb15b03460bdc8a015885025d67e68c6f78f08c3a88f522" - size = 89_014 - upload-time = "2025-11-29T14:01:14.373Z" + url = "https://files.pythonhosted.org/packages/d8/6e/5566beb94431a985abe1787af5ef86e087750172ff9d0bbf20f93e88132d/librt-0.10.0-cp313-cp313-win32.whl" + hash = "sha256:3cd34cd8254eba756660bff6c2da91278248184301054fe3e4feb073bdd49b14" + size = 62_949 + upload-time = "2026-05-05T16:30:16.503Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/8e/9b/ea715f818d926d17b94c80a12d81a79e95c44f52848e61e8ca1ff29bb9a9/librt-0.6.3-cp313-cp313-musllinux_1_2_aarch64.whl" - hash = "sha256:e61ab234624c9ffca0248a707feffe6fac2343758a36725d8eb8a6efef0f8c30" - size = 90_807 - upload-time = "2025-11-29T14:01:15.377Z" + url = "https://files.pythonhosted.org/packages/d0/c2/3ea3301d6c8dff51d39dbe8ed75db3dc92896947d4afb5eeadf821c1e67f/librt-0.10.0-cp313-cp313-win_amd64.whl" + hash = "sha256:7baac5313e2d8dce1386f97777a8d03ab28f5fe1e780b3b9ac2ee7544551fedc" + size = 71_152 + upload-time = "2026-05-05T16:30:17.766Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/f0/fc/4e2e4c87e002fa60917a8e474fd13c4bac9a759df82be3778573bb1ab954/librt-0.6.3-cp313-cp313-musllinux_1_2_i686.whl" - hash = "sha256:324462fe7e3896d592b967196512491ec60ca6e49c446fe59f40743d08c97917" - size = 88_890 - upload-time = "2025-11-29T14:01:16.633Z" + url = "https://files.pythonhosted.org/packages/3c/de/5d49cb92cadcbc77d3abc27b93fd6030ed8437487dde2eae38cab5e6704d/librt-0.10.0-cp313-cp313-win_arm64.whl" + hash = "sha256:afc5b4406c8e2515698d922a5c7823a009312835ea58196671fff40e35cb8166" + size = 61_336 + upload-time = "2026-05-05T16:30:19.021Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/70/7f/c7428734fbdfd4db3d5b9237fc3a857880b2ace66492836f6529fef25d92/librt-0.6.3-cp313-cp313-musllinux_1_2_x86_64.whl" - hash = "sha256:36b2ec8c15030002c7f688b4863e7be42820d7c62d9c6eece3db54a2400f0530" - size = 92_300 - upload-time = "2025-11-29T14:01:17.658Z" + 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 = 76_794 + upload-time = "2026-05-05T16:30:20.392Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/f9/0c/738c4824fdfe74dc0f95d5e90ef9e759d4ecf7fd5ba964d54a7703322251/librt-0.6.3-cp313-cp313-win32.whl" - hash = "sha256:25b1b60cb059471c0c0c803e07d0dfdc79e41a0a122f288b819219ed162672a3" - size = 20_159 - upload-time = "2025-11-29T14:01:18.61Z" + url = "https://files.pythonhosted.org/packages/ae/ef/bf8613febf651b90c5222ee79dea5ae58d4cc2b544df69d3033424448934/librt-0.10.0-cp314-cp314-macosx_11_0_arm64.whl" + hash = "sha256:131ade118d12bd7a0adc4e655474a553f1b76cf78385868885944d21d51e45e0" + size = 79_662 + upload-time = "2026-05-05T16:30:22.025Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/f2/95/93d0e61bc617306ecf4c54636b5cbde4947d872563565c4abdd9d07a39d3/librt-0.6.3-cp313-cp313-win_amd64.whl" - hash = "sha256:10a95ad074e2a98c9e4abc7f5b7d40e5ecbfa84c04c6ab8a70fabf59bd429b88" - size = 21_484 - upload-time = "2025-11-29T14:01:19.506Z" + 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 = 242_390 + upload-time = "2026-05-05T16:30:23.51Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/10/23/abd7ace79ab54d1dbee265f13529266f686a7ce2d21ab59a992f989009b6/librt-0.6.3-cp313-cp313-win_arm64.whl" - hash = "sha256:17000df14f552e86877d67e4ab7966912224efc9368e998c96a6974a8d609bf9" - size = 20_935 - upload-time = "2025-11-29T14:01:20.415Z" + 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 = 232_603 + upload-time = "2026-05-05T16:30:25.198Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/83/14/c06cb31152182798ed98be73f54932ab984894f5a8fccf9b73130897a938/librt-0.6.3-cp314-cp314-macosx_10_13_x86_64.whl" - hash = "sha256:8e695f25d1a425ad7a272902af8ab8c8d66c1998b177e4b5f5e7b4e215d0c88a" - size = 27_566 - upload-time = "2025-11-29T14:01:21.609Z" + 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 = 259_187 + upload-time = "2026-05-05T16:30:26.772Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/0c/b1/ce83ca7b057b06150519152f53a0b302d7c33c8692ce2f01f669b5a819d9/librt-0.6.3-cp314-cp314-macosx_11_0_arm64.whl" - hash = "sha256:3e84a4121a7ae360ca4da436548a9c1ca8ca134a5ced76c893cc5944426164bd" - size = 27_753 - upload-time = "2025-11-29T14:01:22.558Z" + 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 = 251_846 + upload-time = "2026-05-05T16:30:28.56Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/3b/ec/739a885ef0a2839b6c25f1b01c99149d2cb6a34e933ffc8c051fcd22012e/librt-0.6.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" - hash = "sha256:05f385a414de3f950886ea0aad8f109650d4b712cf9cc14cc17f5f62a9ab240b" - size = 83_178 - upload-time = "2025-11-29T14:01:23.555Z" + url = "https://files.pythonhosted.org/packages/18/ca/f4d49133dd86a6f55d79eca30bf412fa722f511a9abe67f62f57aa64e66a/librt-0.10.0-cp314-cp314-musllinux_1_2_aarch64.whl" + hash = "sha256:a77385c5a202e831149f7ad03be9e67cf80e957e52c614e83dcb822c95222eb8" + size = 264_936 + upload-time = "2026-05-05T16:30:30.491Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/db/bd/dc18bb1489d48c0911b9f4d72eae2d304ea264e215ba80f1e6ba4a9fc41d/librt-0.6.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:36a8e337461150b05ca2c7bdedb9e591dfc262c5230422cea398e89d0c746cdc" - size = 87_266 - upload-time = "2025-11-29T14:01:24.532Z" + url = "https://files.pythonhosted.org/packages/de/66/a8df2fbadc1f6c1827a096d11c40175bd526133480bd3bc88ec64a03d257/librt-0.10.0-cp314-cp314-musllinux_1_2_i686.whl" + hash = "sha256:c6a5eafa74b5655bad59886138ed68426f098a6beb8cb95a71f2cc3cd8bb33fe" + size = 258_699 + upload-time = "2026-05-05T16:30:32.002Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/94/f3/d0c5431b39eef15e48088b2d739ad84b17c2f1a22c0345c6d4c4a42b135e/librt-0.6.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:dcbe48f6a03979384f27086484dc2a14959be1613cb173458bd58f714f2c48f3" - size = 87_623 - upload-time = "2025-11-29T14:01:25.798Z" + url = "https://files.pythonhosted.org/packages/bb/73/1e3c83613fe05451bb969e27b68a573d177f08d5f63533cc29fec0989658/librt-0.10.0-cp314-cp314-musllinux_1_2_riscv64.whl" + hash = "sha256:1fc93d0439204c50ab4d1512611ce2c206f1b369b419f69c7c27c761561e3291" + size = 259_825 + upload-time = "2026-05-05T16:30:35.077Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/3b/15/9a52e90834e4bd6ee16cdbaf551cb32227cbaad27398391a189c489318bc/librt-0.6.3-cp314-cp314-musllinux_1_2_aarch64.whl" - hash = "sha256:4bca9e4c260233fba37b15c4ec2f78aa99c1a79fbf902d19dd4a763c5c3fb751" - size = 89_436 - upload-time = "2025-11-29T14:01:26.769Z" + 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 = 282_548 + upload-time = "2026-05-05T16:30:36.639Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/c3/8a/a7e78e46e8486e023c50f21758930ef4793999115229afd65de69e94c9cc/librt-0.6.3-cp314-cp314-musllinux_1_2_i686.whl" - hash = "sha256:760c25ed6ac968e24803eb5f7deb17ce026902d39865e83036bacbf5cf242aa8" - size = 87_540 - upload-time = "2025-11-29T14:01:27.756Z" + url = "https://files.pythonhosted.org/packages/fc/7d/3e89ed6ad0162561fa8bef9df3195e24263104c955713cd0237d3711fad2/librt-0.10.0-cp314-cp314-win32.whl" + hash = "sha256:2eba9d955a68c41d9f326be3da42f163ec3518b7ab20f1c826224e7bed71e0bf" + size = 58_970 + upload-time = "2026-05-05T16:30:38.183Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/49/01/93799044a1cccac31f1074b07c583e181829d240539657e7f305ae63ae2a/librt-0.6.3-cp314-cp314-musllinux_1_2_x86_64.whl" - hash = "sha256:4aa4a93a353ccff20df6e34fa855ae8fd788832c88f40a9070e3ddd3356a9f0e" - size = 90_597 - upload-time = "2025-11-29T14:01:29.35Z" + url = "https://files.pythonhosted.org/packages/76/25/579e731c94a7086a268bfa3e7a4945cd47836bebd3cbf3faeafd2e7eaef9/librt-0.10.0-cp314-cp314-win_amd64.whl" + hash = "sha256:cbfaf7f5145e9917f5d18bffa298eff6a19d74e7b8b11dabdca95785befe8dbf" + size = 67_260 + upload-time = "2026-05-05T16:30:39.804Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/a7/29/00c7f58b8f8eb1bad6529ffb6c9cdcc0890a27dac59ecda04f817ead5277/librt-0.6.3-cp314-cp314-win32.whl" - hash = "sha256:cb92741c2b4ea63c09609b064b26f7f5d9032b61ae222558c55832ec3ad0bcaf" - size = 18_955 - upload-time = "2025-11-29T14:01:30.325Z" + url = "https://files.pythonhosted.org/packages/6e/f8/235822b7ae0b2334f12ee18bcf2476d07924077a5efeea57dbe927704be2/librt-0.10.0-cp314-cp314-win_arm64.whl" + hash = "sha256:8d6d385d1969849a6b1397114df22714b6ded917bada98668e3e974dc663477e" + size = 57_156 + upload-time = "2026-05-05T16:30:41.412Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/d7/13/2739e6e197a9f751375a37908a6a5b0bff637b81338497a1bcb5817394da/librt-0.6.3-cp314-cp314-win_amd64.whl" - hash = "sha256:fdcd095b1b812d756fa5452aca93b962cf620694c0cadb192cec2bb77dcca9a2" - size = 20_263 - upload-time = "2025-11-29T14:01:31.287Z" + 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 = 79_150 + upload-time = "2026-05-05T16:30:42.761Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/e1/73/393868fc2158705ea003114a24e73bb10b03bda31e9ad7b5c5ec6575338b/librt-0.6.3-cp314-cp314-win_arm64.whl" - hash = "sha256:822ca79e28720a76a935c228d37da6579edef048a17cd98d406a2484d10eda78" - size = 19_575 - upload-time = "2025-11-29T14:01:32.229Z" + url = "https://files.pythonhosted.org/packages/6a/f5/72a944aa3bc3498169a168087eff58ca48b58bf1b704e59d091fd30739f3/librt-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl" + hash = "sha256:d64cc66005dc324c9bb1fa3fc2841f529002f6eb15966d55e46d430f56955a6a" + size = 82_304 + upload-time = "2026-05-05T16:30:44.082Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/48/6d/3c8ff3dec21bf804a205286dd63fd28dcdbe00b8dd7eb7ccf2e21a40a0b0/librt-0.6.3-cp314-cp314t-macosx_10_13_x86_64.whl" - hash = "sha256:078cd77064d1640cb7b0650871a772956066174d92c8aeda188a489b58495179" - size = 28_732 - upload-time = "2025-11-29T14:01:33.165Z" + 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 = 272_556 + upload-time = "2026-05-05T16:30:45.497Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/f4/90/e214b8b4aa34ed3d3f1040719c06c4d22472c40c5ef81a922d5af7876eb4/librt-0.6.3-cp314-cp314t-macosx_11_0_arm64.whl" - hash = "sha256:5cc22f7f5c0cc50ed69f4b15b9c51d602aabc4500b433aaa2ddd29e578f452f7" - size = 29_065 - upload-time = "2025-11-29T14:01:34.088Z" + 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 = 256_941 + upload-time = "2026-05-05T16:30:47.059Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/ab/90/ef61ed51f0a7770cc703422d907a757bbd8811ce820c333d3db2fd13542a/librt-0.6.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" - hash = "sha256:14b345eb7afb61b9fdcdfda6738946bd11b8e0f6be258666b0646af3b9bb5916" - size = 93_703 - upload-time = "2025-11-29T14:01:35.057Z" + 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 = 285_855 + upload-time = "2026-05-05T16:30:48.86Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/a8/ae/c30bb119c35962cbe9a908a71da99c168056fc3f6e9bbcbc157d0b724d89/librt-0.6.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:6d46aa46aa29b067f0b8b84f448fd9719aaf5f4c621cc279164d76a9dc9ab3e8" - size = 98_890 - upload-time = "2025-11-29T14:01:36.031Z" + 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 = 275_321 + upload-time = "2026-05-05T16:30:50.63Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/d1/96/47a4a78d252d36f072b79d592df10600d379a895c3880c8cbd2ac699f0ad/librt-0.6.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:1b51ba7d9d5d9001494769eca8c0988adce25d0a970c3ba3f2eb9df9d08036fc" - size = 98_255 - upload-time = "2025-11-29T14:01:37.058Z" + url = "https://files.pythonhosted.org/packages/d9/ea/da5918d4070362e9a4d2ee9cd34f9dc84902daad8fd4275f8504a727ff4e/librt-0.10.0-cp314-cp314t-musllinux_1_2_aarch64.whl" + hash = "sha256:d261c6a2f93335a5167887fb0223e8b98ffce20ee3fde242e8e58a37ece6d0e5" + size = 293_993 + upload-time = "2026-05-05T16:30:52.577Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/e5/28/779b5cc3cd9987683884eb5f5672e3251676bebaaae6b7da1cf366eb1da1/librt-0.6.3-cp314-cp314t-musllinux_1_2_aarch64.whl" - hash = "sha256:ced0925a18fddcff289ef54386b2fc230c5af3c83b11558571124bfc485b8c07" - size = 100_769 - upload-time = "2025-11-29T14:01:38.413Z" + url = "https://files.pythonhosted.org/packages/c9/8d/68b6086bed1fcdc314c640ea04e31e52d18052e08059fa595409d66a51a9/librt-0.10.0-cp314-cp314t-musllinux_1_2_i686.whl" + hash = "sha256:e2ffd44963f8e7f68995504d90f9881d64e94dc1d8e310039b9526108fc0c0f7" + size = 284_254 + upload-time = "2026-05-05T16:30:55.086Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/28/d7/771755e57c375cb9d25a4e106f570607fd856e2cb91b02418db1db954796/librt-0.6.3-cp314-cp314t-musllinux_1_2_i686.whl" - hash = "sha256:6bac97e51f66da2ca012adddbe9fd656b17f7368d439de30898f24b39512f40f" - size = 98_580 - upload-time = "2025-11-29T14:01:39.459Z" + url = "https://files.pythonhosted.org/packages/06/c8/b810f1d84ec34a5a7ed93d7b510ab04164d75fbdf23088d5c3fbe6b08357/librt-0.10.0-cp314-cp314t-musllinux_1_2_riscv64.whl" + hash = "sha256:5f285f6455ed495791c4d8630e5af732960adea93cac4c893d15619f2eae53e8" + size = 284_925 + upload-time = "2026-05-05T16:30:56.728Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/d0/ec/8b157eb8fbc066339a2f34b0aceb2028097d0ed6150a52e23284a311eafe/librt-0.6.3-cp314-cp314t-musllinux_1_2_x86_64.whl" - hash = "sha256:b2922a0e8fa97395553c304edc3bd36168d8eeec26b92478e292e5d4445c1ef0" - size = 101_706 - upload-time = "2025-11-29T14:01:40.474Z" + 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 = 307_830 + upload-time = "2026-05-05T16:30:58.377Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/82/a8/4aaead9a06c795a318282aebf7d3e3e578fa889ff396e1b640c3be4c7806/librt-0.6.3-cp314-cp314t-win32.whl" - hash = "sha256:f33462b19503ba68d80dac8a1354402675849259fb3ebf53b67de86421735a3a" - size = 19_465 - upload-time = "2025-11-29T14:01:41.77Z" + url = "https://files.pythonhosted.org/packages/99/3a/9c635ac3e8a00383ff689161d3eac8a30b3b2ddc711b40471e6b8983ea29/librt-0.10.0-cp314-cp314t-win32.whl" + hash = "sha256:657860fd877fba6a241ea088ef99f63ca819945d3c715265da670bad56c37ebe" + size = 60_147 + upload-time = "2026-05-05T16:31:00.293Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/3a/61/b7e6a02746c1731670c19ba07d86da90b1ae45d29e405c0b5615abf97cde/librt-0.6.3-cp314-cp314t-win_amd64.whl" - hash = "sha256:04f8ce401d4f6380cfc42af0f4e67342bf34c820dae01343f58f472dbac75dcf" - size = 21_042 - upload-time = "2025-11-29T14:01:42.865Z" + url = "https://files.pythonhosted.org/packages/dc/e8/6f65f3e565d4ac212cddddd552eacc8035ffdf941ca0ad6fe945a211d41f/librt-0.10.0-cp314-cp314t-win_amd64.whl" + hash = "sha256:56ded2d66010203a0cb5af063b609e3f079531a0e5e576d618dece859fd2e1af" + size = 68_649 + upload-time = "2026-05-05T16:31:01.778Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/0e/3d/72cc9ec90bb80b5b1a65f0bb74a0f540195837baaf3b98c7fa4a7aa9718e/librt-0.6.3-cp314-cp314t-win_arm64.whl" - hash = "sha256:afb39550205cc5e5c935762c6bf6a2bb34f7d21a68eadb25e2db7bf3593fecc0" - size = 20_246 - upload-time = "2025-11-29T14:01:44.13Z" + url = "https://files.pythonhosted.org/packages/51/78/a0705a67cacd81e5fa01a5035b3adbdfbb43a7b8d4bd27e2b282ae61baf2/librt-0.10.0-cp314-cp314t-win_arm64.whl" + hash = "sha256:1ee63f30abf18ed4830fdbaf87b2b6f4bba1e198d46085c314edde4045e56715" + size = 58_247 + upload-time = "2026-05-05T16:31:03.191Z" [[package]] name = "logging-objects-with-schema" -version = "0.4.1" +version = "1.0.0rc1" [package.source] editable = "." @@ -1561,13 +1500,14 @@ version = "0.1.2" [[package]] name = "mypy" -version = "1.19.0" +version = "1.20.2" [package.source] registry = "https://pypi.org/simple" [[package.dependencies]] name = "librt" + marker = "platform_python_implementation != 'PyPy'" [[package.dependencies]] name = "mypy-extensions" @@ -1575,204 +1515,230 @@ version = "1.19.0" [[package.dependencies]] name = "pathspec" - [[package.dependencies]] - name = "tomli" - marker = "python_full_version < '3.11'" - [[package.dependencies]] name = "typing-extensions" [package.sdist] - url = "https://files.pythonhosted.org/packages/f9/b5/b58cdc25fadd424552804bf410855d52324183112aa004f0732c5f6324cf/mypy-1.19.0.tar.gz" - hash = "sha256:f6b874ca77f733222641e5c46e4711648c4037ea13646fd0cdc814c2eaec2528" - size = 3_579_025 - upload-time = "2025-11-28T15:49:01.26Z" + url = "https://files.pythonhosted.org/packages/04/af/e3d4b3e9ec91a0ff9aabfdb38692952acf49bbb899c2e4c29acb3a6da3ae/mypy-1.20.2.tar.gz" + hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665" + size = 3_817_349 + upload-time = "2026-04-21T17:12:28.473Z" + + [[package.wheels]] + url = "https://files.pythonhosted.org/packages/1f/4d/9ebeae211caccbdaddde7ed5e31dfcf57faac66be9b11deb1dc6526c8078/mypy-1.20.2-cp311-cp311-macosx_10_9_x86_64.whl" + hash = "sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c" + size = 14_371_307 + upload-time = "2026-04-21T17:08:56.442Z" + + [[package.wheels]] + url = "https://files.pythonhosted.org/packages/95/d7/93473d34b61f04fac1aecc01368485c89c5c4af7a4b9a0cab5d77d04b63f/mypy-1.20.2-cp311-cp311-macosx_11_0_arm64.whl" + hash = "sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3" + size = 13_258_917 + upload-time = "2026-04-21T17:05:50.978Z" + + [[package.wheels]] + url = "https://files.pythonhosted.org/packages/e2/30/3dd903e8bafb7b5f7bf87fcd58f8382086dea2aa19f0a7b357f21f63071b/mypy-1.20.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + hash = "sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254" + size = 13_700_516 + upload-time = "2026-04-21T17:11:33.161Z" + + [[package.wheels]] + url = "https://files.pythonhosted.org/packages/07/05/c61a140aba4c729ac7bc99ae26fc627c78a6e08f5b9dd319244ea71a3d7e/mypy-1.20.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + hash = "sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98" + size = 14_562_889 + upload-time = "2026-04-21T17:05:27.674Z" + + [[package.wheels]] + url = "https://files.pythonhosted.org/packages/fd/87/da78243742ffa8a36d98c3010f0d829f93d5da4e6786f1a1a6f2ad616502/mypy-1.20.2-cp311-cp311-musllinux_1_2_x86_64.whl" + hash = "sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac" + size = 14_803_844 + upload-time = "2026-04-21T17:10:06.2Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/98/8f/55fb488c2b7dabd76e3f30c10f7ab0f6190c1fcbc3e97b1e588ec625bbe2/mypy-1.19.0-cp310-cp310-macosx_10_9_x86_64.whl" - hash = "sha256:6148ede033982a8c5ca1143de34c71836a09f105068aaa8b7d5edab2b053e6c8" - size = 13_093_239 - upload-time = "2025-11-28T15:45:11.342Z" + url = "https://files.pythonhosted.org/packages/37/52/10a1ddf91b40f843943a3c6db51e2df59c9e237f29d355e95eaab427461f/mypy-1.20.2-cp311-cp311-win_amd64.whl" + hash = "sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67" + size = 10_846_300 + upload-time = "2026-04-21T17:12:23.886Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/72/1b/278beea978456c56b3262266274f335c3ba5ff2c8108b3b31bec1ffa4c1d/mypy-1.19.0-cp310-cp310-macosx_11_0_arm64.whl" - hash = "sha256:a9ac09e52bb0f7fb912f5d2a783345c72441a08ef56ce3e17c1752af36340a39" - size = 12_156_128 - upload-time = "2025-11-28T15:46:02.566Z" + url = "https://files.pythonhosted.org/packages/20/02/f9a4415b664c53bd34d6709be59da303abcae986dc4ac847b402edb6fa1e/mypy-1.20.2-cp311-cp311-win_arm64.whl" + hash = "sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100" + size = 9_779_498 + upload-time = "2026-04-21T17:09:23.695Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/21/f8/e06f951902e136ff74fd7a4dc4ef9d884faeb2f8eb9c49461235714f079f/mypy-1.19.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:11f7254c15ab3f8ed68f8e8f5cbe88757848df793e31c36aaa4d4f9783fd08ab" - size = 12_753_508 - upload-time = "2025-11-28T15:44:47.538Z" + url = "https://files.pythonhosted.org/packages/71/4e/7560e4528db9e9b147e4c0f22660466bf30a0a1fe3d63d1b9d3b0fd354ee/mypy-1.20.2-cp312-cp312-macosx_10_13_x86_64.whl" + hash = "sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b" + size = 14_539_393 + upload-time = "2026-04-21T17:07:12.52Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/67/5a/d035c534ad86e09cee274d53cf0fd769c0b29ca6ed5b32e205be3c06878c/mypy-1.19.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:318ba74f75899b0e78b847d8c50821e4c9637c79d9a59680fc1259f29338cb3e" - size = 13_507_553 - upload-time = "2025-11-28T15:44:39.26Z" + url = "https://files.pythonhosted.org/packages/32/d9/34a5efed8124f5a9234f55ac6a4ced4201e2c5b81e1109c49ad23190ec8c/mypy-1.20.2-cp312-cp312-macosx_11_0_arm64.whl" + hash = "sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4" + size = 13_361_642 + upload-time = "2026-04-21T17:06:53.742Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/6a/17/c4a5498e00071ef29e483a01558b285d086825b61cf1fb2629fbdd019d94/mypy-1.19.0-cp310-cp310-musllinux_1_2_x86_64.whl" - hash = "sha256:cf7d84f497f78b682edd407f14a7b6e1a2212b433eedb054e2081380b7395aa3" - size = 13_792_898 - upload-time = "2025-11-28T15:44:31.102Z" + url = "https://files.pythonhosted.org/packages/d1/14/eb377acf78c03c92d566a1510cda8137348215b5335085ef662ab82ecd3a/mypy-1.20.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + hash = "sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6" + size = 13_740_347 + upload-time = "2026-04-21T17:12:04.73Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/67/f6/bb542422b3ee4399ae1cdc463300d2d91515ab834c6233f2fd1d52fa21e0/mypy-1.19.0-cp310-cp310-win_amd64.whl" - hash = "sha256:c3385246593ac2b97f155a0e9639be906e73534630f663747c71908dfbf26134" - size = 10_048_835 - upload-time = "2025-11-28T15:48:15.744Z" + url = "https://files.pythonhosted.org/packages/b9/94/7e4634a32b641aa1c112422eed1bbece61ee16205f674190e8b536f884de/mypy-1.20.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + hash = "sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066" + size = 14_734_042 + upload-time = "2026-04-21T17:07:43.16Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/0f/d2/010fb171ae5ac4a01cc34fbacd7544531e5ace95c35ca166dd8fd1b901d0/mypy-1.19.0-cp311-cp311-macosx_10_9_x86_64.whl" - hash = "sha256:a31e4c28e8ddb042c84c5e977e28a21195d086aaffaf08b016b78e19c9ef8106" - size = 13_010_563 - upload-time = "2025-11-28T15:48:23.975Z" + url = "https://files.pythonhosted.org/packages/7a/f3/f7e62395cb7f434541b4491a01149a4439e28ace4c0c632bbf5431e92d1f/mypy-1.20.2-cp312-cp312-musllinux_1_2_x86_64.whl" + hash = "sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102" + size = 14_964_958 + upload-time = "2026-04-21T17:11:00.665Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/41/6b/63f095c9f1ce584fdeb595d663d49e0980c735a1d2004720ccec252c5d47/mypy-1.19.0-cp311-cp311-macosx_11_0_arm64.whl" - hash = "sha256:34ec1ac66d31644f194b7c163d7f8b8434f1b49719d403a5d26c87fff7e913f7" - size = 12_077_037 - upload-time = "2025-11-28T15:47:51.582Z" + url = "https://files.pythonhosted.org/packages/3e/0d/47e3c3a0ec2a876e35aeac365df3cac7776c36bbd4ed18cc521e1b9d255b/mypy-1.20.2-cp312-cp312-win_amd64.whl" + hash = "sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9" + size = 10_911_340 + upload-time = "2026-04-21T17:10:49.179Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/d7/83/6cb93d289038d809023ec20eb0b48bbb1d80af40511fa077da78af6ff7c7/mypy-1.19.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:cb64b0ba5980466a0f3f9990d1c582bcab8db12e29815ecb57f1408d99b4bff7" - size = 12_680_255 - upload-time = "2025-11-28T15:46:57.628Z" + url = "https://files.pythonhosted.org/packages/d6/b2/6c852d72e0ea8b01f49da817fb52539993cde327e7d010e0103dc12d0dac/mypy-1.20.2-cp312-cp312-win_arm64.whl" + hash = "sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58" + size = 9_833_947 + upload-time = "2026-04-21T17:09:05.267Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/99/db/d217815705987d2cbace2edd9100926196d6f85bcb9b5af05058d6e3c8ad/mypy-1.19.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:120cffe120cca5c23c03c77f84abc0c14c5d2e03736f6c312480020082f1994b" - size = 13_421_472 - upload-time = "2025-11-28T15:47:59.655Z" + url = "https://files.pythonhosted.org/packages/5b/c4/b93812d3a192c9bcf5df405bd2f30277cd0e48106a14d1023c7f6ed6e39b/mypy-1.20.2-cp313-cp313-macosx_10_13_x86_64.whl" + hash = "sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026" + size = 14_524_670 + upload-time = "2026-04-21T17:10:30.737Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/4e/51/d2beaca7c497944b07594f3f8aad8d2f0e8fc53677059848ae5d6f4d193e/mypy-1.19.0-cp311-cp311-musllinux_1_2_x86_64.whl" - hash = "sha256:7a500ab5c444268a70565e374fc803972bfd1f09545b13418a5174e29883dab7" - size = 13_651_823 - upload-time = "2025-11-28T15:45:29.318Z" + url = "https://files.pythonhosted.org/packages/f3/47/42c122501bff18eaf1e8f457f5c017933452d8acdc52918a9f59f6812955/mypy-1.20.2-cp313-cp313-macosx_11_0_arm64.whl" + hash = "sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943" + size = 13_336_218 + upload-time = "2026-04-21T17:08:44.069Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/aa/d1/7883dcf7644db3b69490f37b51029e0870aac4a7ad34d09ceae709a3df44/mypy-1.19.0-cp311-cp311-win_amd64.whl" - hash = "sha256:c14a98bc63fd867530e8ec82f217dae29d0550c86e70debc9667fff1ec83284e" - size = 10_049_077 - upload-time = "2025-11-28T15:45:39.818Z" + url = "https://files.pythonhosted.org/packages/92/8f/75bbc92f41725fbd585fb17b440b1119b576105df1013622983e18640a93/mypy-1.20.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + hash = "sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517" + size = 13_724_906 + upload-time = "2026-04-21T17:08:01.02Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/11/7e/1afa8fb188b876abeaa14460dc4983f909aaacaa4bf5718c00b2c7e0b3d5/mypy-1.19.0-cp312-cp312-macosx_10_13_x86_64.whl" - hash = "sha256:0fb3115cb8fa7c5f887c8a8d81ccdcb94cff334684980d847e5a62e926910e1d" - size = 13_207_728 - upload-time = "2025-11-28T15:46:26.463Z" + url = "https://files.pythonhosted.org/packages/a1/32/4c49da27a606167391ff0c39aa955707a00edc500572e562f7c36c08a71f/mypy-1.20.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + hash = "sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15" + size = 14_726_046 + upload-time = "2026-04-21T17:11:22.354Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/b2/13/f103d04962bcbefb1644f5ccb235998b32c337d6c13145ea390b9da47f3e/mypy-1.19.0-cp312-cp312-macosx_11_0_arm64.whl" - hash = "sha256:f3e19e3b897562276bb331074d64c076dbdd3e79213f36eed4e592272dabd760" - size = 12_202_945 - upload-time = "2025-11-28T15:48:49.143Z" + url = "https://files.pythonhosted.org/packages/7f/fc/4e354a1bd70216359deb0c9c54847ee6b32ef78dfb09f5131ff99b494078/mypy-1.20.2-cp313-cp313-musllinux_1_2_x86_64.whl" + hash = "sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee" + size = 14_955_587 + upload-time = "2026-04-21T17:12:16.033Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/e4/93/a86a5608f74a22284a8ccea8592f6e270b61f95b8588951110ad797c2ddd/mypy-1.19.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:b9d491295825182fba01b6ffe2c6fe4e5a49dbf4e2bb4d1217b6ced3b4797bc6" - size = 12_718_673 - upload-time = "2025-11-28T15:47:37.193Z" + url = "https://files.pythonhosted.org/packages/62/b2/c0f2056e9eb8f08c62cafd9715e4584b89132bdc832fcf85d27d07b5f3e5/mypy-1.20.2-cp313-cp313-win_amd64.whl" + hash = "sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f" + size = 10_922_681 + upload-time = "2026-04-21T17:06:35.842Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/3d/58/cf08fff9ced0423b858f2a7495001fda28dc058136818ee9dffc31534ea9/mypy-1.19.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:6016c52ab209919b46169651b362068f632efcd5eb8ef9d1735f6f86da7853b2" - size = 13_608_336 - upload-time = "2025-11-28T15:48:32.625Z" + url = "https://files.pythonhosted.org/packages/e5/14/065e333721f05de8ef683d0aa804c23026bcc287446b61cac657b902ccac/mypy-1.20.2-cp313-cp313-win_arm64.whl" + hash = "sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330" + size = 9_830_560 + upload-time = "2026-04-21T17:07:51.023Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/64/ed/9c509105c5a6d4b73bb08733102a3ea62c25bc02c51bca85e3134bf912d3/mypy-1.19.0-cp312-cp312-musllinux_1_2_x86_64.whl" - hash = "sha256:f188dcf16483b3e59f9278c4ed939ec0254aa8a60e8fc100648d9ab5ee95a431" - size = 13_833_174 - upload-time = "2025-11-28T15:45:48.091Z" + url = "https://files.pythonhosted.org/packages/ae/d1/b4ec96b0ecc620a4443570c6e95c867903428cfcde4206518eafdd5880c3/mypy-1.20.2-cp314-cp314-macosx_10_15_x86_64.whl" + hash = "sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30" + size = 14_524_561 + upload-time = "2026-04-21T17:06:27.325Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/cd/71/01939b66e35c6f8cb3e6fdf0b657f0fd24de2f8ba5e523625c8e72328208/mypy-1.19.0-cp312-cp312-win_amd64.whl" - hash = "sha256:0e3c3d1e1d62e678c339e7ade72746a9e0325de42cd2cccc51616c7b2ed1a018" - size = 10_112_208 - upload-time = "2025-11-28T15:46:41.702Z" + url = "https://files.pythonhosted.org/packages/3a/63/d2c2ff4fa66bc49477d32dfa26e8a167ba803ea6a69c5efb416036909d30/mypy-1.20.2-cp314-cp314-macosx_11_0_arm64.whl" + hash = "sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924" + size = 13_363_883 + upload-time = "2026-04-21T17:11:11.239Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/cb/0d/a1357e6bb49e37ce26fcf7e3cc55679ce9f4ebee0cd8b6ee3a0e301a9210/mypy-1.19.0-cp313-cp313-macosx_10_13_x86_64.whl" - hash = "sha256:7686ed65dbabd24d20066f3115018d2dce030d8fa9db01aa9f0a59b6813e9f9e" - size = 13_191_993 - upload-time = "2025-11-28T15:47:22.336Z" + url = "https://files.pythonhosted.org/packages/2a/56/983916806bf4eddeaaa2c9230903c3669c6718552a921154e1c5182c701f/mypy-1.20.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + hash = "sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb" + size = 13_742_945 + upload-time = "2026-04-21T17:08:34.181Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/5d/75/8e5d492a879ec4490e6ba664b5154e48c46c85b5ac9785792a5ec6a4d58f/mypy-1.19.0-cp313-cp313-macosx_11_0_arm64.whl" - hash = "sha256:fd4a985b2e32f23bead72e2fb4bbe5d6aceee176be471243bd831d5b2644672d" - size = 12_174_411 - upload-time = "2025-11-28T15:44:55.492Z" + url = "https://files.pythonhosted.org/packages/19/65/0cd9285ab010ee8214c83d67c6b49417c40d86ce46f1aa109457b5a9b8d7/mypy-1.20.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + hash = "sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc" + size = 14_706_163 + upload-time = "2026-04-21T17:05:15.51Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/71/31/ad5dcee9bfe226e8eaba777e9d9d251c292650130f0450a280aec3485370/mypy-1.19.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:fc51a5b864f73a3a182584b1ac75c404396a17eced54341629d8bdcb644a5bba" - size = 12_727_751 - upload-time = "2025-11-28T15:44:14.169Z" + url = "https://files.pythonhosted.org/packages/94/97/48ff3b297cafcc94d185243a9190836fb1b01c1b0918fff64e941e973cc9/mypy-1.20.2-cp314-cp314-musllinux_1_2_x86_64.whl" + hash = "sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558" + size = 14_938_677 + upload-time = "2026-04-21T17:05:39.562Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/77/06/b6b8994ce07405f6039701f4b66e9d23f499d0b41c6dd46ec28f96d57ec3/mypy-1.19.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:37af5166f9475872034b56c5efdcf65ee25394e9e1d172907b84577120714364" - size = 13_593_323 - upload-time = "2025-11-28T15:46:34.699Z" + url = "https://files.pythonhosted.org/packages/fd/a1/1b4233d255bdd0b38a1f284feeb1c143ca508c19184964e22f8d837ec851/mypy-1.20.2-cp314-cp314-win_amd64.whl" + hash = "sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8" + size = 11_089_322 + upload-time = "2026-04-21T17:06:44.29Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/68/b1/126e274484cccdf099a8e328d4fda1c7bdb98a5e888fa6010b00e1bbf330/mypy-1.19.0-cp313-cp313-musllinux_1_2_x86_64.whl" - hash = "sha256:510c014b722308c9bd377993bcbf9a07d7e0692e5fa8fc70e639c1eb19fc6bee" - size = 13_818_032 - upload-time = "2025-11-28T15:46:18.286Z" + url = "https://files.pythonhosted.org/packages/78/c2/ce7ee2ba36aeb954ba50f18fa25d9c1188578654b97d02a66a15b6f09531/mypy-1.20.2-cp314-cp314-win_arm64.whl" + hash = "sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3" + size = 10_017_775 + upload-time = "2026-04-21T17:07:20.732Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/f8/56/53a8f70f562dfc466c766469133a8a4909f6c0012d83993143f2a9d48d2d/mypy-1.19.0-cp313-cp313-win_amd64.whl" - hash = "sha256:cabbee74f29aa9cd3b444ec2f1e4fa5a9d0d746ce7567a6a609e224429781f53" - size = 10_120_644 - upload-time = "2025-11-28T15:47:43.99Z" + url = "https://files.pythonhosted.org/packages/4e/a1/9d93a7d0b5859af0ead82b4888b46df6c8797e1bc5e1e262a08518c6d48e/mypy-1.20.2-cp314-cp314t-macosx_10_15_x86_64.whl" + hash = "sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609" + size = 15_549_002 + upload-time = "2026-04-21T17:08:23.107Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/b0/f4/7751f32f56916f7f8c229fe902cbdba3e4dd3f3ea9e8b872be97e7fc546d/mypy-1.19.0-cp314-cp314-macosx_10_15_x86_64.whl" - hash = "sha256:f2e36bed3c6d9b5f35d28b63ca4b727cb0228e480826ffc8953d1892ddc8999d" - size = 13_185_236 - upload-time = "2025-11-28T15:45:20.696Z" + url = "https://files.pythonhosted.org/packages/00/d2/09a6a10ee1bf0008f6c144d9676f2ca6a12512151b4e0ad0ff6c4fac5337/mypy-1.20.2-cp314-cp314t-macosx_11_0_arm64.whl" + hash = "sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2" + size = 14_401_942 + upload-time = "2026-04-21T17:07:31.837Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/35/31/871a9531f09e78e8d145032355890384f8a5b38c95a2c7732d226b93242e/mypy-1.19.0-cp314-cp314-macosx_11_0_arm64.whl" - hash = "sha256:a18d8abdda14035c5718acb748faec09571432811af129bf0d9e7b2d6699bf18" - size = 12_213_902 - upload-time = "2025-11-28T15:46:10.117Z" + url = "https://files.pythonhosted.org/packages/57/da/9594b75c3c019e805250bed3583bdf4443ff9e6ef08f97e39ae308cb06f2/mypy-1.20.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + hash = "sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c" + size = 15_041_649 + upload-time = "2026-04-21T17:09:34.653Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/58/b8/af221910dd40eeefa2077a59107e611550167b9994693fc5926a0b0f87c0/mypy-1.19.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:f75e60aca3723a23511948539b0d7ed514dda194bc3755eae0bfc7a6b4887aa7" - size = 12_738_600 - upload-time = "2025-11-28T15:44:22.521Z" + url = "https://files.pythonhosted.org/packages/97/77/f75a65c278e6e8eba2071f7f5a90481891053ecc39878cc444634d892abe/mypy-1.20.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + hash = "sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744" + size = 15_864_588 + upload-time = "2026-04-21T17:11:44.936Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/11/9f/c39e89a3e319c1d9c734dedec1183b2cc3aefbab066ec611619002abb932/mypy-1.19.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:8f44f2ae3c58421ee05fe609160343c25f70e3967f6e32792b5a78006a9d850f" - size = 13_592_639 - upload-time = "2025-11-28T15:48:08.55Z" + url = "https://files.pythonhosted.org/packages/d7/46/1a4e1c66e96c1a3246ddf5403d122ac9b0a8d2b7e65730b9d6533ba7a6d3/mypy-1.20.2-cp314-cp314t-musllinux_1_2_x86_64.whl" + hash = "sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6" + size = 16_093_956 + upload-time = "2026-04-21T17:10:17.683Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/97/6d/ffaf5f01f5e284d9033de1267e6c1b8f3783f2cf784465378a86122e884b/mypy-1.19.0-cp314-cp314-musllinux_1_2_x86_64.whl" - hash = "sha256:63ea6a00e4bd6822adbfc75b02ab3653a17c02c4347f5bb0cf1d5b9df3a05835" - size = 13_799_132 - upload-time = "2025-11-28T15:47:06.032Z" + url = "https://files.pythonhosted.org/packages/5a/2c/78a8851264dec38cd736ca5b8bc9380674df0dd0be7792f538916157716c/mypy-1.20.2-cp314-cp314t-win_amd64.whl" + hash = "sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec" + size = 12_568_661 + upload-time = "2026-04-21T17:11:54.473Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/fe/b0/c33921e73aaa0106224e5a34822411bea38046188eb781637f5a5b07e269/mypy-1.19.0-cp314-cp314-win_amd64.whl" - hash = "sha256:3ad925b14a0bb99821ff6f734553294aa6a3440a8cb082fe1f5b84dfb662afb1" - size = 10_269_832 - upload-time = "2025-11-28T15:47:29.392Z" + url = "https://files.pythonhosted.org/packages/83/01/cd7318aa03493322ce275a0e14f4f52b8896335e4e79d4fb8153a7ad2b77/mypy-1.20.2-cp314-cp314t-win_arm64.whl" + hash = "sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382" + size = 10_389_240 + upload-time = "2026-04-21T17:09:42.719Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/09/0e/fe228ed5aeab470c6f4eb82481837fadb642a5aa95cc8215fd2214822c10/mypy-1.19.0-py3-none-any.whl" - hash = "sha256:0c01c99d626380752e527d5ce8e69ffbba2046eb8a060db0329690849cf9b6f9" - size = 2_469_714 - upload-time = "2025-11-28T15:45:33.22Z" + url = "https://files.pythonhosted.org/packages/28/9a/f23c163e25b11074188251b0b5a0342625fc1cdb6af604757174fa9acc9b/mypy-1.20.2-py3-none-any.whl" + hash = "sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563" + size = 2_637_314 + upload-time = "2026-04-21T17:05:54.5Z" [[package]] name = "mypy-extensions" @@ -1795,41 +1761,41 @@ version = "1.1.0" [[package]] name = "nodeenv" -version = "1.9.1" +version = "1.10.0" [package.source] registry = "https://pypi.org/simple" [package.sdist] - url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz" - hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f" - size = 47_437 - upload-time = "2024-06-04T18:44:11.171Z" + url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz" + hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb" + size = 55_611 + upload-time = "2025-12-20T14:08:54.006Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl" - hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9" - size = 22_314 - upload-time = "2024-06-04T18:44:08.352Z" + url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl" + hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827" + size = 23_438 + upload-time = "2025-12-20T14:08:52.782Z" [[package]] name = "packaging" -version = "25.0" +version = "26.2" [package.source] registry = "https://pypi.org/simple" [package.sdist] - url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz" - hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" - size = 165_727 - upload-time = "2025-04-19T11:48:59.673Z" + url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz" + hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661" + size = 228_134 + upload-time = "2026-04-24T20:15:23.917Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl" - hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484" - size = 66_469 - upload-time = "2025-04-19T11:48:57.875Z" + url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl" + hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e" + size = 100_195 + upload-time = "2026-04-24T20:15:22.081Z" [[package]] name = "pathspec" @@ -1852,22 +1818,22 @@ version = "1.1.1" [[package]] name = "platformdirs" -version = "4.5.0" +version = "4.9.6" [package.source] registry = "https://pypi.org/simple" [package.sdist] - url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz" - hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312" - size = 21_632 - upload-time = "2025-10-08T17:44:48.791Z" + url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz" + hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a" + size = 29_400 + upload-time = "2026-04-09T00:04:10.812Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl" - hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3" - size = 18_651 - upload-time = "2025-10-08T17:44:47.223Z" + url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl" + hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917" + size = 21_348 + upload-time = "2026-04-09T00:04:09.463Z" [[package]] name = "pluggy" @@ -1890,7 +1856,7 @@ version = "1.6.0" [[package]] name = "pre-commit" -version = "4.5.0" +version = "4.6.0" [package.source] registry = "https://pypi.org/simple" @@ -1911,16 +1877,16 @@ version = "4.5.0" name = "virtualenv" [package.sdist] - url = "https://files.pythonhosted.org/packages/f4/9b/6a4ffb4ed980519da959e1cf3122fc6cb41211daa58dbae1c73c0e519a37/pre_commit-4.5.0.tar.gz" - hash = "sha256:dc5a065e932b19fc1d4c653c6939068fe54325af8e741e74e88db4d28a4dd66b" - size = 198_428 - upload-time = "2025-11-22T21:02:42.304Z" + url = "https://files.pythonhosted.org/packages/8e/22/2de9408ac81acbb8a7d05d4cc064a152ccf33b3d480ebe0cd292153db239/pre_commit-4.6.0.tar.gz" + hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9" + size = 198_525 + upload-time = "2026-04-21T20:31:41.613Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/5d/c4/b2d28e9d2edf4f1713eb3c29307f1a63f3d67cf09bdda29715a36a68921a/pre_commit-4.5.0-py2.py3-none-any.whl" - hash = "sha256:25e2ce09595174d9c97860a95609f9f852c0614ba602de3561e267547f2335e1" - size = 226_429 - upload-time = "2025-11-22T21:02:40.836Z" + url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl" + hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b" + size = 226_472 + upload-time = "2026-04-21T20:31:40.092Z" [[package]] name = "pycodestyle" @@ -1990,10 +1956,6 @@ version = "9.0.3" name = "colorama" marker = "sys_platform == 'win32'" - [[package.dependencies]] - name = "exceptiongroup" - marker = "python_full_version < '3.11'" - [[package.dependencies]] name = "iniconfig" @@ -2006,10 +1968,6 @@ version = "9.0.3" [[package.dependencies]] name = "pygments" - [[package.dependencies]] - name = "tomli" - marker = "python_full_version < '3.11'" - [package.sdist] url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz" hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c" @@ -2024,7 +1982,7 @@ version = "9.0.3" [[package]] name = "pytest-cov" -version = "7.0.0" +version = "7.1.0" [package.source] registry = "https://pypi.org/simple" @@ -2040,16 +1998,41 @@ version = "7.0.0" name = "pytest" [package.sdist] - url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz" - hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1" - size = 54_328 - upload-time = "2025-09-09T10:57:02.113Z" + url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz" + hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2" + size = 55_592 + upload-time = "2026-03-21T20:11:16.284Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl" - hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861" - size = 22_424 - upload-time = "2025-09-09T10:57:00.695Z" + url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl" + hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678" + size = 22_876 + upload-time = "2026-03-21T20:11:14.438Z" + +[[package]] +name = "python-discovery" +version = "1.3.0" + + [package.source] + registry = "https://pypi.org/simple" + + [[package.dependencies]] + name = "filelock" + + [[package.dependencies]] + name = "platformdirs" + + [package.sdist] + url = "https://files.pythonhosted.org/packages/ae/e0/cc5a8653e9a24f6cf84768f05064aa8ed5a83dcefd5e2a043db14a1c5f44/python_discovery-1.3.0.tar.gz" + hash = "sha256:d098f1e86be5d45fe4d14bf1029294aabbd332f4321179dec85e76cddce834b0" + size = 63_925 + upload-time = "2026-05-05T14:38:39.769Z" + + [[package.wheels]] + url = "https://files.pythonhosted.org/packages/30/d4/24d543ab8b8158b7f5a97113c831205f5c900c92c8762b1e7f44b7ea0405/python_discovery-1.3.0-py3-none-any.whl" + hash = "sha256:441d9ced3dfce36e113beb35ca302c71c7ef06f3c0f9c227a0b9bb3bd49b9e9f" + size = 33_124 + upload-time = "2026-05-05T14:38:38.539Z" [[package]] name = "pytokens" @@ -2064,36 +2047,6 @@ version = "0.4.1" size = 23_015 upload-time = "2026-01-30T01:03:45.924Z" - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/42/24/f206113e05cb8ef51b3850e7ef88f20da6f4bf932190ceb48bd3da103e10/pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl" - hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5" - size = 161_522 - upload-time = "2026-01-30T01:02:50.393Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/d4/e9/06a6bf1b90c2ed81a9c7d2544232fe5d2891d1cd480e8a1809ca354a8eb2/pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe" - size = 246_945 - upload-time = "2026-01-30T01:02:52.399Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/69/66/f6fb1007a4c3d8b682d5d65b7c1fb33257587a5f782647091e3408abe0b8/pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c" - size = 259_525 - upload-time = "2026-01-30T01:02:53.737Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/04/92/086f89b4d622a18418bac74ab5db7f68cf0c21cf7cc92de6c7b919d76c88/pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl" - hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7" - size = 262_693 - upload-time = "2026-01-30T01:02:54.871Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/b4/7b/8b31c347cf94a3f900bdde750b2e9131575a61fdb620d3d3c75832262137/pytokens-0.4.1-cp310-cp310-win_amd64.whl" - hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2" - size = 103_567 - upload-time = "2026-01-30T01:02:56.414Z" - [[package.wheels]] url = "https://files.pythonhosted.org/packages/3d/92/790ebe03f07b57e53b10884c329b9a1a308648fc083a6d4a39a10a28c8fc/pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl" hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440" @@ -2263,60 +2216,6 @@ version = "6.0.3" size = 130_960 upload-time = "2025-09-25T21:33:16.546Z" - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl" - hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b" - size = 184_227 - upload-time = "2025-09-25T21:31:46.04Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl" - hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956" - size = 174_019 - upload-time = "2025-09-25T21:31:47.706Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8" - size = 740_646 - upload-time = "2025-09-25T21:31:49.21Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" - hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198" - size = 840_793 - upload-time = "2025-09-25T21:31:50.735Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b" - size = 770_293 - upload-time = "2025-09-25T21:31:51.828Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl" - hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0" - size = 732_872 - upload-time = "2025-09-25T21:31:53.282Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl" - hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69" - size = 758_828 - upload-time = "2025-09-25T21:31:54.807Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl" - hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e" - size = 142_415 - upload-time = "2025-09-25T21:31:55.885Z" - - [[package.wheels]] - url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl" - hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c" - size = 158_561 - upload-time = "2025-09-25T21:31:57.406Z" - [[package.wheels]] url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl" hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e" @@ -2601,7 +2500,7 @@ version = "6.0.3" [[package]] name = "rich" -version = "14.2.0" +version = "15.0.0" [package.source] registry = "https://pypi.org/simple" @@ -2613,294 +2512,324 @@ version = "14.2.0" name = "pygments" [package.sdist] - url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz" - hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4" - size = 219_990 - upload-time = "2025-10-09T14:16:53.064Z" + url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz" + hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36" + size = 230_680 + upload-time = "2026-04-12T08:24:00.75Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl" - hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd" - size = 243_393 - upload-time = "2025-10-09T14:16:51.245Z" + url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl" + hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb" + size = 310_654 + upload-time = "2026-04-12T08:24:02.83Z" [[package]] name = "stevedore" -version = "5.6.0" +version = "5.7.0" [package.source] registry = "https://pypi.org/simple" [package.sdist] - url = "https://files.pythonhosted.org/packages/96/5b/496f8abebd10c3301129abba7ddafd46c71d799a70c44ab080323987c4c9/stevedore-5.6.0.tar.gz" - hash = "sha256:f22d15c6ead40c5bbfa9ca54aa7e7b4a07d59b36ae03ed12ced1a54cf0b51945" - size = 516_074 - upload-time = "2025-11-20T10:06:07.264Z" + url = "https://files.pythonhosted.org/packages/a2/6d/90764092216fa560f6587f83bb70113a8ba510ba436c6476a2b47359057c/stevedore-5.7.0.tar.gz" + hash = "sha256:31dd6fe6b3cbe921e21dcefabc9a5f1cf848cf538a1f27543721b8ca09948aa3" + size = 516_200 + upload-time = "2026-02-20T13:27:06.765Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/f4/40/8561ce06dc46fd17242c7724ab25b257a2ac1b35f4ebf551b40ce6105cfa/stevedore-5.6.0-py3-none-any.whl" - hash = "sha256:4a36dccefd7aeea0c70135526cecb7766c4c84c473b1af68db23d541b6dc1820" - size = 54_428 - upload-time = "2025-11-20T10:06:05.946Z" + url = "https://files.pythonhosted.org/packages/69/06/36d260a695f383345ab5bbc3fd447249594ae2fa8dfd19c533d5ae23f46b/stevedore-5.7.0-py3-none-any.whl" + hash = "sha256:fd25efbb32f1abb4c9e502f385f0018632baac11f9ee5d1b70f88cc5e22ad4ed" + size = 54_483 + upload-time = "2026-02-20T13:27:05.561Z" [[package]] name = "tomli" -version = "2.3.0" +version = "2.4.1" [package.source] registry = "https://pypi.org/simple" [package.sdist] - url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz" - hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549" - size = 17_392 - upload-time = "2025-10-08T22:01:47.119Z" + url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz" + hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f" + size = 17_543 + upload-time = "2026-03-25T20:22:03.828Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl" - hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45" - size = 153_236 - upload-time = "2025-10-08T22:01:00.137Z" + url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl" + hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30" + size = 154_704 + upload-time = "2026-03-25T20:21:10.473Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl" - hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba" - size = 148_084 - upload-time = "2025-10-08T22:01:01.63Z" + url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl" + hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a" + size = 149_454 + upload-time = "2026-03-25T20:21:12.036Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf" - size = 234_832 - upload-time = "2025-10-08T22:01:02.543Z" + url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076" + size = 237_561 + upload-time = "2026-03-25T20:21:13.098Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441" - size = 242_052 - upload-time = "2025-10-08T22:01:03.836Z" + url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9" + size = 243_824 + upload-time = "2026-03-25T20:21:14.569Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl" - hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845" - size = 239_555 - upload-time = "2025-10-08T22:01:04.834Z" + url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl" + hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c" + size = 242_227 + upload-time = "2026-03-25T20:21:15.712Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl" - hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c" - size = 245_128 - upload-time = "2025-10-08T22:01:05.84Z" + url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl" + hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc" + size = 247_859 + upload-time = "2026-03-25T20:21:17.001Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl" - hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456" - size = 96_445 - upload-time = "2025-10-08T22:01:06.896Z" + url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl" + hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049" + size = 97_204 + upload-time = "2026-03-25T20:21:18.079Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl" - hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be" - size = 107_165 - upload-time = "2025-10-08T22:01:08.107Z" + url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl" + hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e" + size = 108_084 + upload-time = "2026-03-25T20:21:18.978Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl" - hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac" - size = 154_891 - upload-time = "2025-10-08T22:01:09.082Z" + url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl" + hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece" + size = 95_285 + upload-time = "2026-03-25T20:21:20.309Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl" - hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22" - size = 148_796 - upload-time = "2025-10-08T22:01:10.266Z" + url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl" + hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a" + size = 155_924 + upload-time = "2026-03-25T20:21:21.626Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f" - size = 242_121 - upload-time = "2025-10-08T22:01:11.332Z" + url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl" + hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085" + size = 150_018 + upload-time = "2026-03-25T20:21:23.002Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52" - size = 250_070 - upload-time = "2025-10-08T22:01:12.498Z" + url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9" + size = 244_948 + upload-time = "2026-03-25T20:21:24.04Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl" - hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8" - size = 245_859 - upload-time = "2025-10-08T22:01:13.551Z" + url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5" + size = 253_341 + upload-time = "2026-03-25T20:21:25.177Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl" - hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6" - size = 250_296 - upload-time = "2025-10-08T22:01:14.614Z" + url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl" + hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585" + size = 248_159 + upload-time = "2026-03-25T20:21:26.364Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl" - hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876" - size = 97_124 - upload-time = "2025-10-08T22:01:15.629Z" + url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl" + hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1" + size = 253_290 + upload-time = "2026-03-25T20:21:27.46Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl" - hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878" - size = 107_698 - upload-time = "2025-10-08T22:01:16.51Z" + url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl" + hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917" + size = 98_141 + upload-time = "2026-03-25T20:21:28.492Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl" - hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b" - size = 154_819 - upload-time = "2025-10-08T22:01:17.964Z" + url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl" + hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9" + size = 108_847 + upload-time = "2026-03-25T20:21:29.386Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl" - hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae" - size = 148_766 - upload-time = "2025-10-08T22:01:18.959Z" + url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl" + hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257" + size = 95_088 + upload-time = "2026-03-25T20:21:30.677Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b" - size = 240_771 - upload-time = "2025-10-08T22:01:20.106Z" + url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl" + hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54" + size = 155_866 + upload-time = "2026-03-25T20:21:31.65Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf" - size = 248_586 - upload-time = "2025-10-08T22:01:21.164Z" + url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl" + hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a" + size = 149_887 + upload-time = "2026-03-25T20:21:33.028Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl" - hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f" - size = 244_792 - upload-time = "2025-10-08T22:01:22.417Z" + url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897" + size = 243_704 + upload-time = "2026-03-25T20:21:34.51Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl" - hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05" - size = 248_909 - upload-time = "2025-10-08T22:01:23.859Z" + url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f" + size = 251_628 + upload-time = "2026-03-25T20:21:36.012Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl" - hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606" - size = 96_946 - upload-time = "2025-10-08T22:01:24.893Z" + url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl" + hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d" + size = 247_180 + upload-time = "2026-03-25T20:21:37.136Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl" - hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999" - size = 107_705 - upload-time = "2025-10-08T22:01:26.153Z" + url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl" + hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5" + size = 251_674 + upload-time = "2026-03-25T20:21:38.298Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl" - hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e" - size = 154_244 - upload-time = "2025-10-08T22:01:27.06Z" + url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl" + hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd" + size = 97_976 + upload-time = "2026-03-25T20:21:39.316Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl" - hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3" - size = 148_637 - upload-time = "2025-10-08T22:01:28.059Z" + url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl" + hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36" + size = 108_755 + upload-time = "2026-03-25T20:21:40.248Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc" - size = 241_925 - upload-time = "2025-10-08T22:01:29.066Z" + url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl" + hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd" + size = 95_265 + upload-time = "2026-03-25T20:21:41.219Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0" - size = 249_045 - upload-time = "2025-10-08T22:01:31.98Z" + url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl" + hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf" + size = 155_726 + upload-time = "2026-03-25T20:21:42.23Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl" - hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879" - size = 245_835 - upload-time = "2025-10-08T22:01:32.989Z" + url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl" + hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac" + size = 149_859 + upload-time = "2026-03-25T20:21:43.386Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl" - hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005" - size = 253_109 - upload-time = "2025-10-08T22:01:34.052Z" + url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662" + size = 244_713 + upload-time = "2026-03-25T20:21:44.474Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl" - hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463" - size = 97_930 - upload-time = "2025-10-08T22:01:35.082Z" + url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853" + size = 252_084 + upload-time = "2026-03-25T20:21:45.62Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl" - hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8" - size = 107_964 - upload-time = "2025-10-08T22:01:36.057Z" + url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl" + hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15" + size = 247_973 + upload-time = "2026-03-25T20:21:46.937Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl" - hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77" - size = 163_065 - upload-time = "2025-10-08T22:01:37.27Z" + url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl" + hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba" + size = 256_223 + upload-time = "2026-03-25T20:21:48.467Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl" - hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf" - size = 159_088 - upload-time = "2025-10-08T22:01:38.235Z" + url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl" + hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6" + size = 98_973 + upload-time = "2026-03-25T20:21:49.526Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" - hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530" - size = 268_193 - upload-time = "2025-10-08T22:01:39.712Z" + url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl" + hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7" + size = 109_082 + upload-time = "2026-03-25T20:21:50.506Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" - hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b" - size = 275_488 - upload-time = "2025-10-08T22:01:40.773Z" + url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl" + hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232" + size = 96_490 + upload-time = "2026-03-25T20:21:51.474Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl" - hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67" - size = 272_669 - upload-time = "2025-10-08T22:01:41.824Z" + url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl" + hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4" + size = 164_263 + upload-time = "2026-03-25T20:21:52.543Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl" - hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f" - size = 279_709 - upload-time = "2025-10-08T22:01:43.177Z" + url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl" + hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c" + size = 160_736 + upload-time = "2026-03-25T20:21:53.674Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl" - hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0" - size = 107_563 - upload-time = "2025-10-08T22:01:44.233Z" + url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d" + size = 270_717 + upload-time = "2026-03-25T20:21:55.129Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl" - hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba" - size = 119_756 - upload-time = "2025-10-08T22:01:45.234Z" + url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41" + size = 278_461 + upload-time = "2026-03-25T20:21:56.228Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl" - hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b" - size = 14_408 - upload-time = "2025-10-08T22:01:46.04Z" + url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl" + hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c" + size = 274_855 + upload-time = "2026-03-25T20:21:57.653Z" + + [[package.wheels]] + url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl" + hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f" + size = 283_144 + upload-time = "2026-03-25T20:21:59.089Z" + + [[package.wheels]] + url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl" + hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8" + size = 108_683 + upload-time = "2026-03-25T20:22:00.214Z" + + [[package.wheels]] + url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl" + hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26" + size = 121_196 + upload-time = "2026-03-25T20:22:01.169Z" + + [[package.wheels]] + url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl" + hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396" + size = 100_393 + upload-time = "2026-03-25T20:22:02.137Z" + + [[package.wheels]] + url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl" + hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe" + size = 14_583 + upload-time = "2026-03-25T20:22:03.012Z" [[package]] name = "typing-extensions" @@ -2923,7 +2852,7 @@ version = "4.15.0" [[package]] name = "virtualenv" -version = "20.36.1" +version = "21.3.1" [package.source] registry = "https://pypi.org/simple" @@ -2938,40 +2867,35 @@ version = "20.36.1" name = "platformdirs" [[package.dependencies]] - name = "typing-extensions" - marker = "python_full_version < '3.11'" + name = "python-discovery" [package.sdist] - url = "https://files.pythonhosted.org/packages/aa/a3/4d310fa5f00863544e1d0f4de93bddec248499ccf97d4791bc3122c9d4f3/virtualenv-20.36.1.tar.gz" - hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba" - size = 6_032_239 - upload-time = "2026-01-09T18:21:01.296Z" + url = "https://files.pythonhosted.org/packages/ec/0d/915c02c94d207b85580eb09bffab54438a709e7288524094fe781da526c2/virtualenv-21.3.1.tar.gz" + hash = "sha256:c2305bc1fddeec40699b8370d13f8d431b0701f00ce895061ce493aeded4426b" + size = 7_613_791 + upload-time = "2026-05-05T01:34:31.402Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/6a/2a/dc2228b2888f51192c7dc766106cd475f1b768c10caaf9727659726f7391/virtualenv-20.36.1-py3-none-any.whl" - hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f" - size = 6_008_258 - upload-time = "2026-01-09T18:20:59.425Z" + url = "https://files.pythonhosted.org/packages/b1/4f/f71e641e504111a5a74e3a20bc52d01bd86788b22699dd3fee1c63253cf6/virtualenv-21.3.1-py3-none-any.whl" + hash = "sha256:d1a71cf58f2f9228fff23a1f6ec15d39785c6b32e03658d104974247145edd35" + size = 7_594_539 + upload-time = "2026-05-05T01:34:28.98Z" [[package]] name = "vulture" -version = "2.14" +version = "2.16" [package.source] registry = "https://pypi.org/simple" - [[package.dependencies]] - name = "tomli" - marker = "python_full_version < '3.11'" - [package.sdist] - url = "https://files.pythonhosted.org/packages/8e/25/925f35db758a0f9199113aaf61d703de891676b082bd7cf73ea01d6000f7/vulture-2.14.tar.gz" - hash = "sha256:cb8277902a1138deeab796ec5bef7076a6e0248ca3607a3f3dee0b6d9e9b8415" - size = 58_823 - upload-time = "2024-12-08T17:39:43.319Z" + url = "https://files.pythonhosted.org/packages/66/3e/4d08c5903b2c0c70cad583c170cc4a663fc6a61e2ad00b711fcda61358cd/vulture-2.16.tar.gz" + hash = "sha256:f8d9f6e2af03011664a3c6c240c9765b3f392917d3135fddca6d6a68d359f717" + size = 52_680 + upload-time = "2026-03-25T14:41:27.141Z" [[package.wheels]] - url = "https://files.pythonhosted.org/packages/a0/56/0cc15b8ff2613c1d5c3dc1f3f576ede1c43868c1bc2e5ccaa2d4bcd7974d/vulture-2.14-py2.py3-none-any.whl" - hash = "sha256:d9a90dba89607489548a49d557f8bac8112bd25d3cbc8aeef23e860811bd5ed9" - size = 28_915 - upload-time = "2024-12-08T17:39:40.573Z" + url = "https://files.pythonhosted.org/packages/f5/be/f935130312330614811dae2ea9df3f395f6d63889eb6c2e68c14507152ee/vulture-2.16-py3-none-any.whl" + hash = "sha256:6e0f1c312cef1c87856957e5c2ca9608834a7c794c2180477f30bf0e4cc58eee" + size = 26_993 + upload-time = "2026-03-25T14:41:26.21Z"