diff --git a/.github/workflows/pr_code_changes.yaml b/.github/workflows/pr_code_changes.yaml index 78711dbf..d64d5424 100644 --- a/.github/workflows/pr_code_changes.yaml +++ b/.github/workflows/pr_code_changes.yaml @@ -10,6 +10,7 @@ on: - .github/** - changelog.d/** - pyproject.toml + - uv.lock - src/policyengine/data/bundle/manifest.json workflow_dispatch: @@ -57,7 +58,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} @@ -68,16 +69,13 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v8.1.0 - name: Install package (no country-model extras) - # `h5py` is used transitively by policyengine.core.scoping_strategy - # but is normally supplied via the [us]/[uk] extras (through - # policyengine-core). Install it directly so the smoke import can - # exercise the wrapper without the country models, which pin - # versions that don't support 3.9/3.10 yet. - run: uv pip install --system . h5py + run: uv pip install --system . - name: Smoke-import core modules run: python -c "import policyengine; from policyengine.core import Dataset, Policy, Simulation; from policyengine.outputs import aggregate, poverty, inequality; print('import OK')" BundleVerification: name: Verify bundle metadata + # Private manifest credentials are available only to trusted repository PRs. + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository runs-on: ubuntu-latest env: POLICYENGINE_SKIP_COUNTRY_IMPORTS: "1" @@ -113,6 +111,13 @@ jobs: PY - name: Check installed package consistency run: python -m pip check + # Only the reviewed registry lock is a pull-request concern. The + # read-only credential, published-measurement and TRACE sidecar gates + # are release gates: the release workflow regenerates the sidecars and + # publishes the wheels immediately before checking them, so they cannot + # be satisfied from a pull request. They run in push.yaml instead. + - name: Check the reviewed registry lock + run: python scripts/release_lock.py - name: Verify bundle packages run: policyengine bundle verify --country us --country uk --packages-only --json Test: @@ -120,7 +125,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.13', '3.14'] + python-version: ['3.11', '3.12', '3.13', '3.14'] steps: - name: Checkout repo uses: actions/checkout@v6 diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index d7ba2b12..fa367e7b 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -43,7 +43,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.13', '3.14'] + python-version: ['3.11', '3.12', '3.13', '3.14'] steps: - name: Checkout repo uses: actions/checkout@v6 @@ -114,22 +114,39 @@ jobs: fetch-depth: 0 - name: Fetch tags run: git fetch --tags --force + - name: Install uv + uses: astral-sh/setup-uv@v8.1.0 - name: Setup Python uses: actions/setup-python@v6 with: python-version: '3.13' + - name: Install package for TRO verification and regeneration + run: uv pip install -e . h5py --system + - name: Check reviewed release inputs before versioning + env: + HUGGING_FACE_TOKEN: ${{ secrets.HUGGING_FACE_TOKEN }} + run: | + if [[ -z "${HUGGING_FACE_TOKEN:-}" ]]; then + echo "::error::Full private release verification requires HUGGING_FACE_TOKEN" + exit 1 + fi + python scripts/check_release_credentials.py + python scripts/bundle.py check --published-spm --include-tros --strict-tros + python scripts/release_lock.py - name: Build changelog run: pip install yaml-changelog towncrier && make changelog - name: Generate derived bundle metadata run: python scripts/bundle.py generate + - name: Refresh only the release version in the registry lock + run: python scripts/release_lock.py --refresh - name: Preview changelog update run: ".github/get-changelog-diff.sh" - - name: Install package for TRO regeneration - run: pip install -e . h5py - name: Regenerate bundled TRACE TROs env: HUGGING_FACE_TOKEN: ${{ secrets.HUGGING_FACE_TOKEN }} - run: python scripts/bundle.py generate --include-tros + run: | + python scripts/bundle.py generate --include-tros --strict-tros + python scripts/bundle.py check --published-spm --include-tros --strict-tros - name: Update changelog, bundle metadata, and TROs uses: EndBug/add-and-commit@v9 with: @@ -160,6 +177,17 @@ jobs: - name: Capture published version id: version run: bash .github/capture-version.sh + - name: Validate complete bundle and registry lock before publication + env: + HUGGING_FACE_TOKEN: ${{ secrets.HUGGING_FACE_TOKEN }} + run: | + if [[ -z "${HUGGING_FACE_TOKEN:-}" ]]; then + echo "::error::Full private release verification requires HUGGING_FACE_TOKEN" + exit 1 + fi + python scripts/check_release_credentials.py + python scripts/bundle.py check --published-spm --include-tros --strict-tros + python scripts/release_lock.py - name: Publish a git tag run: ".github/publish-git-tag.sh" - name: Build package @@ -200,10 +228,16 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@v6 + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: '3.13' - name: Wait for release to be visible on PyPI env: VERSION: ${{ needs.Publish.outputs.version }} run: bash .github/wait-for-pypi.sh + - name: Validate published SPM bundle before consumer updates + run: python -S scripts/bundle.py check --published-spm - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@v1 diff --git a/README.md b/README.md index 112fa503..ea392936 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ A Python package for tax-benefit microsimulation analysis. Run policy simulations, analyse distributional impacts, and visualise results across the UK and US. +Requires Python 3.11–3.14. + > **Results are estimates.** PolicyEngine simulates a large, evolving body of tax-benefit law (the US model alone encodes more than 95,000 parameters across 5,500+ variables) over survey microdata calibrated to administrative targets. Treat outputs as estimates, and validate them against the policies relevant to your analysis, the scope of the rules engine, and external or back-of-the-envelope calculations. You can inspect the certified US dataset's calibration at https://calibration-diagnostics.vercel.app/populace. @@ -26,13 +28,17 @@ print(uk.household.hbai_household_net_income) # net income us = pe.us.calculate_household( people=[{"age": 35, "employment_income": 60_000}], tax_unit={"filing_status": "SINGLE"}, - household={"state_code": "CA"}, + household={"state_code": "CA", "county_fips": "06037"}, year=2026, reform={"gov.irs.credits.ctc.amount.adult_dependent": 1000}, ) print(us.tax_unit.income_tax, us.household.household_net_income) ``` +US default outputs require observed county FIPS or an explicit national/SPM-area +choice. See the [SPM household contract](docs/households.md#spm-geography-and-measurement-selection) +for settings, provenance and the coordinated bundle this release ships. + ### Population analysis ```python diff --git a/changelog.d/canonical-certification-guards.fixed.md b/changelog.d/canonical-certification-guards.fixed.md new file mode 100644 index 00000000..08ba107f --- /dev/null +++ b/changelog.d/canonical-certification-guards.fixed.md @@ -0,0 +1 @@ +Recognise the `microcosm-data` producer name and the `evidence` artifact kind when certifying a US data release, so source-enrichment releases keep the per-artifact reachability check and pin their release-directory sidecars at paths that resolve. diff --git a/changelog.d/canonical-spm-bundle.changed.md b/changelog.d/canonical-spm-bundle.changed.md new file mode 100644 index 00000000..36bef33a --- /dev/null +++ b/changelog.d/canonical-spm-bundle.changed.md @@ -0,0 +1 @@ +Validate independently pinned SPM bundle settings, verify published calculator wheel identities, and support explicitly uncertified local-wheel development fixtures ahead of the production promotion that follows this release. diff --git a/changelog.d/canonical-spm-python.breaking.md b/changelog.d/canonical-spm-python.breaking.md new file mode 100644 index 00000000..478aa73b --- /dev/null +++ b/changelog.d/canonical-spm-python.breaking.md @@ -0,0 +1 @@ +Require Python 3.11–3.14, matching the supported runtime range of the canonical US model bundle. Install checks now cover that complete range. diff --git a/changelog.d/canonical-spm-release-validation.fixed.md b/changelog.d/canonical-spm-release-validation.fixed.md new file mode 100644 index 00000000..0eb0cbb8 --- /dev/null +++ b/changelog.d/canonical-spm-release-validation.fixed.md @@ -0,0 +1 @@ +Require complete authenticated bundle provenance and a registry-only, version-consistent lockfile before automatic release publication. diff --git a/changelog.d/canonical-spm-review.fixed.md b/changelog.d/canonical-spm-review.fixed.md new file mode 100644 index 00000000..7f84f3f4 --- /dev/null +++ b/changelog.d/canonical-spm-review.fixed.md @@ -0,0 +1,6 @@ +Preserve native entity ID alignment across input/output tables, reject malformed +group IDs, and retain partial SPM selection semantics through JSON round trips. +Validate managed SPM settings before dataset materialization, preserve +dependency-light bundle bootstrap, and enforce published measurement pins in +the release workflow. Authenticate candidate import sources and correct the +measurement serialization example. diff --git a/changelog.d/canonical-spm-wrapper.breaking.md b/changelog.d/canonical-spm-wrapper.breaking.md new file mode 100644 index 00000000..255a425e --- /dev/null +++ b/changelog.d/canonical-spm-wrapper.breaking.md @@ -0,0 +1 @@ +Use the bundle-pinned canonical SPM configuration across US household and population simulations, require explicit geography for SPM-dependent outputs, reject formula-owned SPM inputs, and retain serialisable selection and calculation provenance across results and replay. diff --git a/changelog.d/canonical-tuple-pins.changed.md b/changelog.d/canonical-tuple-pins.changed.md new file mode 100644 index 00000000..02506e46 --- /dev/null +++ b/changelog.d/canonical-tuple-pins.changed.md @@ -0,0 +1 @@ +Pin the canonical PolicyEngine tuple: policyengine-us 2.2.1, policyengine-core 3.32.5 and spm-calculator 1.0.0. diff --git a/changelog.d/certify-us-populace-us-2024-spm-20260915.changed.md b/changelog.d/certify-us-populace-us-2024-spm-20260915.changed.md new file mode 100644 index 00000000..e29691ca --- /dev/null +++ b/changelog.d/certify-us-populace-us-2024-spm-20260915.changed.md @@ -0,0 +1 @@ +Certify the US populace data release `populace-us-2024-spm-20260915` (populace_us_2024, policyengine-us 2.2.1) into the PolicyEngine bundle manifest. diff --git a/changelog.d/native-us-hdf-weights.fixed.md b/changelog.d/native-us-hdf-weights.fixed.md new file mode 100644 index 00000000..1c7484c3 --- /dev/null +++ b/changelog.d/native-us-hdf-weights.fixed.md @@ -0,0 +1 @@ +Load native US pandas HDF datasets that store only calibrated household weights by mapping missing person and entity weights through native membership IDs in memory, preserving source files and supplied weights. diff --git a/changelog.d/repository-tro-schema.fixed.md b/changelog.d/repository-tro-schema.fixed.md new file mode 100644 index 00000000..0b0354e6 --- /dev/null +++ b/changelog.d/repository-tro-schema.fixed.md @@ -0,0 +1 @@ +Accept the repository-bundle TRACE emission context and validate all generated country TROs against the packaged schema before writing bundle sidecars. diff --git a/changelog.d/spm-receipt-comment.fixed.md b/changelog.d/spm-receipt-comment.fixed.md new file mode 100644 index 00000000..6e03aeea --- /dev/null +++ b/changelog.d/spm-receipt-comment.fixed.md @@ -0,0 +1 @@ +Corrected the SPM receipt storage comment, which justified the H5 dataset by an HDF5 attribute-size limit that the supported h5py range does not have. diff --git a/changelog.d/spm-resource-geography-contract.changed.md b/changelog.d/spm-resource-geography-contract.changed.md new file mode 100644 index 00000000..9e13f1f1 --- /dev/null +++ b/changelog.d/spm-resource-geography-contract.changed.md @@ -0,0 +1 @@ +Documented that SPM geography is required only by results that use the measurement: SPM thresholds and poverty always require it, the ordinary resource outputs never do, and the SPM unit's capped housing subsidy and everything downstream of it require it for units allocated housing assistance. diff --git a/changelog.d/us-household-snapshots-country-uprating.changed.md b/changelog.d/us-household-snapshots-country-uprating.changed.md new file mode 100644 index 00000000..5de9dbf4 --- /dev/null +++ b/changelog.d/us-household-snapshots-country-uprating.changed.md @@ -0,0 +1 @@ +Regenerated three US household snapshots for two located country changes: the BLS CPI-U actuals refresh that uprates school meal reimbursement rates, and the California standard deduction whose uprating metadata was un-frozen. diff --git a/changelog.d/us-model-surface-snapshot.changed.md b/changelog.d/us-model-surface-snapshot.changed.md new file mode 100644 index 00000000..7659c631 --- /dev/null +++ b/changelog.d/us-model-surface-snapshot.changed.md @@ -0,0 +1 @@ +Regenerated the US model-version surface snapshot for the pinned country and data package. diff --git a/changelog.d/us-no-income-snapshot-snap-work-requirements.changed.md b/changelog.d/us-no-income-snapshot-snap-work-requirements.changed.md new file mode 100644 index 00000000..e18c26ab --- /dev/null +++ b/changelog.d/us-no-income-snapshot-snap-work-requirements.changed.md @@ -0,0 +1 @@ +Regenerated the US single-adult-no-income snapshot for the SNAP ABAWD work-requirement changes, and noted that omitting weekly_hours_worked_before_lsr now reads as zero hours. diff --git a/changelog.d/wrapper-repin-policyengine-us-221.changed.md b/changelog.d/wrapper-repin-policyengine-us-221.changed.md new file mode 100644 index 00000000..1c7a4f0d --- /dev/null +++ b/changelog.d/wrapper-repin-policyengine-us-221.changed.md @@ -0,0 +1 @@ +Repin the US bundle, registry lock and TRACE sidecars onto policyengine-us 2.2.1, the build-time model the certified data release records. The certified microdata is byte-identical to the superseded `populace-us-2024-spm-20260909` release. diff --git a/docs/bundles.md b/docs/bundles.md index ca26ce3a..0c33ec82 100644 --- a/docs/bundles.md +++ b/docs/bundles.md @@ -84,10 +84,10 @@ Derived artifacts are: - `src/policyengine/data/bundle/{country}.trace.tro.jsonld` - GitHub release assets exported from the bundle manifest -The US model in this bundle uses the SPM calculator 0.3.1 API. The manifest pins -`spm-calculator==0.3.1` in the US package scaffold and the generated `us`, -`models`, and `dev` extras. The model formulas and certified dataset artifacts -remain unchanged. +The US model in this bundle uses the SPM calculator 1.0.0 API. The manifest pins +`spm-calculator==1.0.0` as a US runtime dependency, and the generated `us`, +`models` and `dev` extras carry that pin. The model formulas and certified +dataset artifacts remain unchanged. Previously published versions cannot acquire a new dependency constraint. To reproduce the historical 5.3.0 US package set, include the compatible calculator diff --git a/docs/countries.md b/docs/countries.md index 26553627..4e2227a5 100644 --- a/docs/countries.md +++ b/docs/countries.md @@ -38,7 +38,9 @@ Override in any output with `income_variable=`. ## State / regional breakdown US: Populace row scoping uses `state_fips` and `congressional_district_geoid`. -`state_code` remains the human-readable input for custom households. +`state_code` remains the human-readable state input for custom households. +US resource and poverty calculations also require observed `county_fips` or an +explicit national or SPM-area choice; see [Households](households.md#spm-geography-and-measurement-selection). UK: constituency code and local authority code on every household where available. @@ -72,7 +74,7 @@ Most analysis patterns are identical — swap `pe.us` for `pe.uk`: pe.us.calculate_household( people=[{"age": 35, "employment_income": 60_000}], tax_unit={"filing_status": "SINGLE"}, - household={"state_code": "CA"}, + household={"state_code": "CA", "county_fips": "06037"}, year=2026, ) diff --git a/docs/engineering/runbooks/canonical-spm-development.md b/docs/engineering/runbooks/canonical-spm-development.md new file mode 100644 index 00000000..4e094bc5 --- /dev/null +++ b/docs/engineering/runbooks/canonical-spm-development.md @@ -0,0 +1,193 @@ +# Canonical SPM wrapper development and remaining release gates + +The canonical SPM wrapper integration is **pending promotion**. The packaged +5.3.0 manifest still pins US 1.764.6 and its existing certified Build P release. +Only `measurements.spm` has been staged: forecast content SHA-256 +`3d86d5c4c0423480e6b69b75d222ffa4a7a2639e4094df5ba2504af01be17173`, scenario +`ce_trend`, county geography, vintage `2020`. This measurement pin does not +certify the new country package or the enriched data. + +The configuration accepts exactly `forecast_content_sha256`, `scenario`, +`geography_kind`, `geography_id`, `county_vintage` and `as_of`. The hash and +scenario are mandatory in a bundle. County and national modes omit +`geography_id`; metro requires it. `as_of`, when present, is an ISO calendar +date. Unknown fields, paths, provider overrides and fallback settings fail +validation. Country runtime validation resolves scenario, year and location +against the pinned artifact when the calculation needs SPM. + +## Reproduce the local development fixture + +Use a fresh isolated environment containing the exact qualified local country, +calculator and Core wheels. Preserve all earlier qualification environments. Set +`SPM_PYTHON` to that environment's interpreter and `SPM_COUNTRY_WHEEL` and +`SPM_CALCULATOR_WHEEL` to the authenticated wheel paths. The following command +reads wheel `METADATA` and hashes the bytes; it never substitutes an invented +registry release or changes the packaged manifest. + +```bash +UV_CACHE_DIR=/tmp/wrapper-spm-uv-cache \ + uv run --python "$SPM_PYTHON" --no-sync python scripts/bundle.py \ + development-manifest \ + --country-wheel "$SPM_COUNTRY_WHEEL" \ + --calculator-wheel "$SPM_CALCULATOR_WHEEL" \ + --output /tmp/wrapper-spm-development-manifest.json +``` + +The most recently qualified development country wheel is version 1.824.7, SHA-256 +`7644819916a4f8ca2aa37a4a9d992fa834bfa66ab2d441326a9686baa5c1a688`. +The observed local calculator wheel is version 1.0.0, SHA-256 +`c49c41da5fd482e563eaea956e205a3ba6841cadd4dd32bef4c0c3dbce17ffba`. +These are local development artifacts, not published package identities. +The calculator wheel uses the canonical source commit +`fd7de4b3b670a10acd72567904ccea800252c9a5`. Core remains 3.30.1 and UK 2.90.2. + +The fixture records `development.data_certification: not_certified` and +`development.promotion_status: pending`. It removes the inherited US +`certification` and `certified_data_artifact`; inherited data metadata is +reference-only. It records the real local wheel names, versions, file URIs and +SHA-256 hashes. UK release metadata is retained unchanged. It refuses to +overwrite the packaged manifest. + +Country model initialization requires a data compatibility receipt even for +household-only tests. An explicitly enabled pytest plugin supplies the +`unverified_development_fixture` receipt and disables remote data-manifest +fetches for that test process. It does not add a compatibility claim, certify a +dataset, or install a production environment-variable override. Its existing +receipt field `certified_for_model_version` names the tested runtime; the +`unverified_development_fixture` basis explicitly denies certification. + +```bash +UV_CACHE_DIR=/tmp/wrapper-spm-uv-cache \ + uv run --python "$SPM_PYTHON" --no-sync python -m pytest \ + -p tests.fixtures.spm_development \ + --spm-development-manifest /tmp/wrapper-spm-development-manifest.json \ + tests/test_spm_selection.py tests/test_spm_model.py \ + tests/test_spm_household.py -q +``` + +The plugin requires explicit opt-in and checks installed US/Core/calculator +versions, both local wheel hashes, and every installed country/calculator +package file against those wheel bytes before country imports. It authenticates +the actual US/Core/calculator import origins and package search paths, rejects +shadowing from `PYTHONPATH` or previously loaded modules, and repeats these +checks after bootstrap. Core wheel bytes are checked by the separate four-wheel +native probe. Only focused household +and synthetic small-dataset tests belong in this command. The final acceptance +worker owns the heavy population process. Local enriched H5 experiments require +an explicit path with `allow_unmanaged=True` and remain development runs while +certification is pending. + +An explicitly labeled development script can use the same bootstrap before +importing country models. This helper lives under `tests/fixtures` and is not a +production environment override: + +```python +from tests.fixtures.spm_development import activate_spm_development_manifest + +development = activate_spm_development_manifest( + "/tmp/wrapper-spm-development-manifest.json" +) +import policyengine as pe + +assert development["development"]["promotion_status"] == "pending" +assert pe.us.model.data_certification.compatibility_basis == ( + "unverified_development_fixture" +) +``` + +Run such a script from the wrapper worktree with the isolated interpreter and +`uv run --python "$SPM_PYTHON" --no-sync`. The final acceptance worker can +then call `pe.us.managed_microsimulation(dataset=LOCAL_H5, +allow_unmanaged=True, spm={...})`, reporting the explicit development receipt. +Only that worker is authorized for the heavy population process. + +Bundle-only tests avoid the country bootstrap entirely: + +```bash +UV_CACHE_DIR=/tmp/wrapper-spm-uv-cache POLICYENGINE_SKIP_COUNTRY_IMPORTS=1 \ + uv run --python "$SPM_PYTHON" --no-sync python -m pytest --noconftest \ + tests/test_spm_bundle_tooling.py tests/test_bundle_metadata.py \ + tests/test_bundle.py -q +``` + +## Stage and later verify published measurement pins + +The operator command validates all settings before writing. Omitting +`--calculator-version` stages only configuration and preserves package pins +and data certification: + +```bash +uv run --python "$SPM_PYTHON" --no-sync python scripts/bundle.py set-spm \ + --forecast-content-sha256 3d86d5c4c0423480e6b69b75d222ffa4a7a2639e4094df5ba2504af01be17173 \ + --scenario ce_trend +uv run --python "$SPM_PYTHON" --no-sync python scripts/bundle.py check +``` + +After the calculator's actual registry release is authorized and published, +repeat `set-spm` with `--calculator-version "$PUBLISHED_CALCULATOR_VERSION"`. +The command fetches that exact version's authoritative PyPI metadata, requires +one non-yanked universal wheel on `files.pythonhosted.org`, downloads its bytes, +and checks the registry SHA-256 before writing. It adds `spm-calculator` as a +hashed `runtime_dependency` scoped to the US, adds it to US/models/dev extras, +and preserves UK requirements. The version must come from the real release; +local wheel metadata is not proof of registry publication. + +`scripts/bundle.py check --published-spm` checks the measurement pin and +published calculator dependency metadata. **It does not establish country/data +compatibility or whole-release readiness.** It rejects development fixtures and +currently exits 2 with `SPM promotion pending: no published spm-calculator pin`. +Ordinary `check` passes for the staged draft; this is metadata consistency only. + +## Exact work remaining before a production bundle + +1. Complete the coordinator's canonical numerical acceptance and source/review + gates. Deploy the separately owned protections for old bundle images before + calculator publication. Record the reviewed source and actual published + calculator wheel identity and import/content verification. +2. Authenticate the producer's clean reviewed source and build a new immutable + native source-enrichment release. The completed producer handoff records a + local candidate at `/private/tmp/microcosm-spm-native-role-final-20260909`, + whose H5 SHA-256 is + `6496cc4393d4d3c6574f76eca231de5898c803b9067645591fd5c4d3e65aee84`. + The candidate is not published or certified. The new person primitive is + `is_spm_independent_minor_role`; all prior fields, values, dtypes, membership, + county inputs and weights are preserved. This is source enrichment, not + recalibration. The inherited diagnostics remain schema 5, SHA-256 + `870449b44e86b13b25bcea1a57f0e7af37f4d4db18be815eea3acdf9fe6eb40e`; + relabeling those diagnostics as schema 6 is invalid. +3. In the producer checkout, run its real `python -m + microcosm.data.source_enrichment --certify` contract with the new candidate, + exact parent H5 and actual country/Core/wrapper compatibility wheels. That + contract verifies native loaders, registration, input precedence and wheel + source identities without a full population simulation. Run the actual + publisher's `--preflight-only` contract. The producer handoff contains the + full commands. Commit authentication and compatibility evidence are still + required; no edited compatibility ranges can replace them. +4. Publish the reviewed immutable enrichment release only after its real gates + pass. Resolve the actual country/wrapper publication sequence with the + coordinator; do not invent future versions to break that dependency. + Refresh country and wrapper locks against real registry bytes. +5. Certify the published data release through `scripts/bundle.py certify-data + --country us --data-producer populace --model-version + "$PUBLISHED_COUNTRY_VERSION" --manifest-uri "$PUBLISHED_RELEASE_MANIFEST_URI"`. + The URI must identify the actual immutable producer release. This command + updates the country pin and data metadata from real compatibility evidence. + Regenerate bundle artifacts and TRACE sidecars, inspect full-manifest hash + bindings, and run the required exact model/data tests. Staging an SPM hash + changes the manifest bytes; old sidecar bindings cannot establish provenance + for those new bytes. +6. Finish the coordinator-owned final population, simulation beta, API staging, + browser/client and production version-route acceptance. The focused local + tests above do not establish those deployment gates. + +No release, publication, deployment, full population run or data certification +was performed by this wrapper tooling work. + +The bundled TRACE sidecars are regenerated against the current manifest, so +`TestVendoredSidecarBinding` passes: both `us.trace.tro.jsonld` and +`uk.trace.tro.jsonld` bind manifest SHA-256 +`d430b8e561d76e5836a17bf506b06b9cd16560eb77f15718dd46f4e78513de85`, which is +the hash of `src/policyengine/data/bundle/manifest.json` in this tree. That +binding covers the staged measurement bytes only; it is metadata provenance, +not data certification, and the sidecars must be regenerated and reviewed again +at the real bundle promotion step against published artifacts. diff --git a/docs/getting-started.md b/docs/getting-started.md index 6dd489b1..bbd9afa5 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -4,6 +4,8 @@ title: "Getting started" ## Install +Use Python 3.11–3.14, the supported range for the certified country models. + ```bash pip install policyengine ``` @@ -26,7 +28,7 @@ import policyengine as pe result = pe.us.calculate_household( people=[{"age": 35, "employment_income": 60_000}], tax_unit={"filing_status": "SINGLE"}, - household={"state_code": "CA"}, + household={"state_code": "CA", "county_fips": "06037"}, year=2026, ) @@ -35,7 +37,7 @@ result.tax_unit.eitc result.household.household_net_income ``` -Each `.*` lookup is a regular Python scalar. The result is a typed `HouseholdResult` with entity sections (`person[i]`, `tax_unit`, `spm_unit`, `household`) populated from every variable in the country model. +Each `.*` lookup is a regular Python scalar. The result is a typed `HouseholdResult` with entity sections (`person[i]`, `tax_unit`, `spm_unit`, `household`) populated from the default output catalog. Additional outputs can be requested with `extra_variables`. US defaults include SPM resources and poverty, so provide observed county FIPS or explicitly select national measurement with `spm={"geography_kind": "national"}`; see [Households](households.md#spm-geography-and-measurement-selection). ## Apply a reform @@ -45,6 +47,7 @@ Reforms are parameter-path → value dicts: reformed = pe.us.calculate_household( people=[{"age": 35, "employment_income": 60_000}], tax_unit={"filing_status": "SINGLE"}, + household={"state_code": "CA", "county_fips": "06037"}, year=2026, reform={"gov.irs.credits.ctc.amount.adult_dependent": 1_000}, ) diff --git a/docs/households.md b/docs/households.md index bd6f8973..bac0225e 100644 --- a/docs/households.md +++ b/docs/households.md @@ -2,7 +2,7 @@ title: "Households" --- -`pe.us.calculate_household` and `pe.uk.calculate_household` compute every variable in the country model for a single household. Same keyword arguments, different entity structures. +`pe.us.calculate_household` and `pe.uk.calculate_household` compute a default set of tax, benefit and income outputs for a single household. Use `extra_variables` to request additional outputs. ## US @@ -15,7 +15,7 @@ result = pe.us.calculate_household( {"age": 5}, ], tax_unit={"filing_status": "JOINT"}, - household={"state_code": "TX"}, + household={"state_code": "TX", "county_fips": "48201"}, year=2026, ) ``` @@ -27,12 +27,91 @@ result = pe.us.calculate_household( | `people` | List of person dicts. Keys are any person-level variable on the model. | | `tax_unit` | Tax-unit inputs (e.g. `filing_status`). | | `spm_unit` | SPM-unit inputs. | -| `household` | Household inputs. `state_code` is essentially always needed. | +| `household` | Household inputs, including `state_code` and observed five-digit `county_fips` for the default SPM geography selection. | | `family` | Family-level inputs. | | `marital_unit` | Marital-unit inputs. | All adults default to one shared tax unit and household. For separate tax units (e.g. two adult roommates), construct the `Simulation` directly and set the entity-membership arrays. +### SPM geography and measurement selection + +US household results include SPM resources and poverty by default. Provide the +household's county FIPS, as above, or explicitly select national measurement: + +```python +result = pe.us.calculate_household( + people=[{"age": 40, "employment_income": 50_000}], + tax_unit={"filing_status": "SINGLE"}, + household={"state_code": "CA"}, + year=2026, + spm={"geography_kind": "national"}, +) +receipt = result.to_dict()["provenance"]["spm"] +result.write("household-result.json") # Includes the JSON-compatible receipt. +``` + +County FIPS assigns a household to the selected year's Census SPM estimation +area; it does not select a separately estimated county rent factor. National +measurement is a conscious analytical choice and is recorded in provenance. +A fixed SPM area can instead be selected with +`spm={"geography_kind": "metro", "geography_id": area_id}`, using an area ID +available in the pinned artifact for the requested year. + +The `spm` argument accepts a dictionary or `pe.us.SPMSelection`. Its complete +set of keys is: + +| Key | Meaning | +|---|---| +| `forecast_content_sha256` | Optional assertion of the bundle's independently pinned artifact content hash. A different hash is rejected. | +| `scenario` | Scenario within that artifact: `ce_trend` by default or the `zero_real` sensitivity. | +| `geography_kind` | `county` by default; `national` or `metro` require an explicit choice. | +| `geography_id` | Required only for a fixed `metro` SPM area. | +| `county_vintage` | County assignment vintage, `"2020"` by default. | +| `as_of` | Optional information-date cutoff accepted by the pinned artifact. | + +Forecast paths, provider objects and fallback policies are not settings. The +canonical artifact covers 2022–2035. Its receipt distinguishes published +national inputs from forecast components and research geography; an unsupported +year fails instead of being extrapolated by the wrapper. Scenario forecasts +are conditional research estimates, not agency forecasts or uncertainty bounds. + +State alone is insufficient for SPM and raises `SPM_GEOGRAPHY_REQUIRED`. +Unknown counties or selected areas raise `SPM_GEOGRAPHY_UNAVAILABLE`; a measured +unit with no classified adult raises `SPM_COMPOSITION_REQUIRED`. These are +calculator `SPMInputError` exceptions with `code` and `to_dict()` attributes. +Geography is checked when an SPM-dependent formula runs, and only for the units +whose result depends on the measurement. SPM measurement itself — thresholds, +the geographic factor and SPM poverty — always requires this choice. The +ordinary resource outputs — household net income, benefits, income decile and +equivalized net income, and each person's marginal tax rate — take the +household's housing assistance amount rather than the capped SPM subsidy, so +they succeed on state alone and record an empty measurement receipt whether or +not the unit is allocated assistance. The country's cap +(`spm_unit_capped_housing_subsidy`) is what consults the canonical housing +portion, and only for units allocated assistance. So for an assisted unit +without a geography the cap and everything downstream of it — +`spm_unit_benefits`, `spm_unit_net_income` and in turn +`spm_unit_oecd_equiv_net_income` and `spm_unit_income_decile` — raise +`SPM_GEOGRAPHY_REQUIRED`, while a unit allocated none has a capped subsidy of +zero by construction and consults no measurement. A genuinely independent +tax-only country-model calculation can use state alone; the wrapper's default +outputs include SPM poverty and therefore always require the choice. + +Supply observed inputs such as age, tenure, county and the source-backed +`is_spm_independent_minor_role`. Computed SPM thresholds, geographic factors, +resources, poverty outputs and `spm_measurement_adults` / +`spm_measurement_children` cannot be supplied as household inputs or axes. +Generic `is_adult`, `spm_unit_count_adults` and `spm_unit_count_children` remain +separate benefit inputs; they do not override SPM measurement composition. + +These changes require the coordinated canonical country/calculator bundle, which +the packaged production manifest now pins and certifies: `policyengine-us` +2.2.1, `policyengine-core` 3.32.5, `spm-calculator` 1.0.0, and the US data +release `populace-us-2024-spm-20260915`. Local-wheel development manifest +fixtures remain explicitly uncertified. The measurement receipt identifies a +calculation; it does not certify a population dataset or establish publication +readiness. + ## UK ```python @@ -94,7 +173,7 @@ The year determines which parameter values apply. For multi-year analysis, call ## Extra variables -The result exposes every variable in the model by default. To surface variables that aren't in the default catalog explicitly: +The result exposes a default output catalog. To request additional variables: ```python result = pe.us.calculate_household( @@ -120,7 +199,7 @@ result = pe.us.calculate_household( } ], tax_unit={"filing_status": "SINGLE"}, - household={"state_code": "CA"}, + household={"state_code": "CA", "county_fips": "06037"}, year=2026, axes=[ { diff --git a/docs/index.md b/docs/index.md index ec4b6a49..45b5eff6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -20,7 +20,7 @@ import policyengine as pe result = pe.us.calculate_household( people=[{"age": 35, "employment_income": 60_000}], tax_unit={"filing_status": "SINGLE"}, - household={"state_code": "CA"}, + household={"state_code": "CA", "county_fips": "06037"}, year=2026, ) diff --git a/docs/microsim.md b/docs/microsim.md index 0e3d73e1..91795062 100644 --- a/docs/microsim.md +++ b/docs/microsim.md @@ -4,6 +4,10 @@ title: "Microsimulation" For population-level estimates — budget cost, winners and losers, poverty impact — run a microsimulation over calibrated microdata. +For US decile analysis, `calculate_decile_impacts(dataset=..., spm=...)` passes +the selection to both constructed simulations. When supplying existing +baseline and reform simulations, set `spm` on each simulation directly. + ## Quick example ```python @@ -28,6 +32,78 @@ total_snap.result `Simulation.ensure()` loads a cached result if one exists, or runs and caches on miss. Call `Simulation.run()` explicitly if you want to bypass the cache. +### Canonical US SPM settings and provenance + +The coordinated canonical SPM integration resolves measurement settings from +the bundle. Its default uses each household's observed `county_fips` and native +SPM membership. To make an explicit national sensitivity calculation: + +```python +simulation = pe.Simulation( + dataset=dataset, + tax_benefit_model_version=pe.us.model, + spm={"geography_kind": "national", "scenario": "zero_real"}, +) +simulation.run() +settings = simulation.spm_config +receipt = simulation.spm_provenance() +serialized = simulation.model_dump_json(include={"spm", "spm_receipt"}) +``` + +`spm_config` exposes all six resolved settings; `spm_provenance()` returns a +detached JSON-compatible measurement receipt after calculation. The stored +`spm_receipt` is a Pydantic model. Before calculation, serializing a partial +selection preserves omitted options so they continue to inherit the bundle's +defaults after JSON restoration; successful calculation freezes all six settings. +Selection participates in simulation identity +and cached results. The example serializes the measurement settings and receipt; +the existing full simulation model graph contains circular model/variable +references and cannot currently be exported with an unrestricted +`model_dump_json()`. Use simulation persistence and run records for saved runs. +The same `spm=` selection is accepted by +`pe.us.managed_microsimulation`; its returned country simulation exposes +`spm_config` and `spm_provenance()` too. See the [household SPM +contract](households.md#spm-geography-and-measurement-selection) for exact keys, +the 2022–2035 artifact horizon and explicit geography errors. No provider or +forecast-file path can replace the bundle's pinned artifact. + +For a small local development dataset, explicitly opt out of managed data +selection: + +```python +simulation = pe.us.managed_microsimulation( + dataset=str(local_h5_path), + allow_unmanaged=True, + spm={"geography_kind": "county"}, +) +``` + +This does not certify the local file. The canonical population release must +preserve the prior native arrays, memberships and weights while adding the +source-backed `is_spm_independent_minor_role` input. Formula-owned measurement +counts, thresholds, geographic factors, SPM resources and poverty outputs must +not be present as input columns; the wrapper validates the input DataFrames +before passing them to the country model. Generic adult/child counts remain +separate. The native source enrichment is not recalibration, and inherited +schema-5 calibration diagnostics cannot be relabeled as schema 6. + +The wrapper's native pandas HDF loader accepts files that store only calibrated +`household_weight`. It maps missing person and group weight columns through +native membership IDs in memory, retaining supplied weight columns and the +original row order. Inputs are aligned to country populations by native entity ID, +and calculated outputs are aligned back to those IDs before attaching weights. +Independently shuffled entity tables therefore retain the correct geography and +results. Null or duplicate entity IDs are rejected before weight mapping. +Group weights are not sums of person weights. The file is +opened read-only; missing or ambiguous links raise an error instead of creating +unweighted rows. Core variable/period H5 files use the same mapping. + +The packaged 5.3.0 production manifest retains `policyengine-us==1.764.6` and +does not yet certify this integration. Local-wheel tests use an explicitly +uncertified development manifest. Registry publication, a producer-issued data +compatibility certification and final bundle promotion remain separate gates; +measurement provenance alone does not satisfy them. + ## Datasets Microdata is stored as HDF5 on Hugging Face. `ensure_datasets` downloads, caches, and uprates: diff --git a/docs/release-bundles.md b/docs/release-bundles.md index e7e2469b..63026246 100644 --- a/docs/release-bundles.md +++ b/docs/release-bundles.md @@ -12,6 +12,58 @@ This document defines the intended reproducibility boundary for `policyengine.py`. +## Preparing and checking release artifacts + +After certifying a new country/data pair, prepare its complete sidecars with +`python scripts/bundle.py generate --include-tros`. Review the generated manifest, +TRO source hashes and locations, and the resolved `uv.lock` together. Ordinary +local generation can emit limited provenance when a remote manifest is unavailable; +that output cannot pass the public release checks. + +Trusted same-repository PRs, Versioning, and Publish require: + +```bash +python scripts/check_release_credentials.py +python scripts/bundle.py check --published-spm --include-tros --strict-tros +python scripts/release_lock.py +``` + +Strict generation requires full US/UK release manifests from pinned revisions, +matching the raw hashes and URLs in the reviewed sidecars. It checks the existing +publisher compatibility contract and certified dataset hash, and requires pinned +model wheel hashes and URLs. The certified model must match the corresponding +runtime package name/version, any explicit component wheel pins, and the exact +wheel URL and SHA256 in the reviewed `uv.lock`. Missing credentials, fetch failures, missing datasets, +or changed source bytes stop the workflow. These metadata checks do not replace +native dataset certification or the model test suite. Supply a read-only Hugging +Face credential through the repository's `HUGGING_FACE_TOKEN` secret; PR verification +uses `pull_request` only for same-repository heads, never `pull_request_target`. +Each trusted verification job authenticates its actual credential with Hugging +Face's `whoami-v2` endpoint and requires access-token role `read`. Missing metadata, +write scope, and unrecognized or fine-grained permission schemas fail closed; +the check logs no token, account details, or response body. The workflow does not +change existing credential permissions. + +Versioning checks those reviewed inputs before the bump, generates the versioned +bundle metadata, and runs `python scripts/release_lock.py --refresh`. The helper +clears inherited uv resolver overrides, explicitly disables frozen mode, resolves +from PyPI without local sources, and permits only the root package version to change +in the reviewed lock. Every locked distribution must use an HTTPS artifact URL +on PyPI's `files.pythonhosted.org` host and a valid SHA256 digest; a registry source +label alone is insufficient. Any dependency graph change fails and restores the original +lock; stage such changes in a reviewed PR instead. The helper then runs the actual +`uv lock --check`. Final unpublished dependency pins must wait for registry +publication before these checks can pass. + +The bump changes the wrapper version, bundle identifiers, derived requirements and +citation fields, root lock version, and the TRO bundle-manifest hash/composition +fingerprint and wrapper-version fields. Remote data and model hashes, certification +fields, dataset identities, and fixed repository emission context remain unchanged. +TRO timestamps come from the pinned manifest, not the current clock. The changelog +uses Towncrier's release date and consumes the reviewed fragments. Run the actual +version helper, generator, strict checks, and lock refresh in a staging checkout +before merge, and compare the eventual published wheel against that receipt. + The key design decision is: - country `*-data` repos build and stage immutable data artifacts diff --git a/examples/employment_income_variation_us.py b/examples/employment_income_variation_us.py index 9ac2891f..b56c4e8a 100644 --- a/examples/employment_income_variation_us.py +++ b/examples/employment_income_variation_us.py @@ -101,7 +101,8 @@ def create_dataset_with_varied_employment_income( # Create household data household_data = { "household_id": list(range(n_households)), - "state_name": ["CA"] * n_households, # California + "state_code": ["CA"] * n_households, + "county_fips": ["06037"] * n_households, # Los Angeles County "household_weight": [1000.0] * n_households, } diff --git a/examples/household_impact_example.py b/examples/household_impact_example.py index 4b96cd96..0133dce9 100644 --- a/examples/household_impact_example.py +++ b/examples/household_impact_example.py @@ -61,11 +61,11 @@ def us_example() -> None: print("US household calculator") print("=" * 60) - # Single adult earning $50,000 in California. + # Single adult earning $50,000 in Los Angeles County, California. single = pe.us.calculate_household( people=[{"age": 35, "employment_income": 50_000}], tax_unit={"filing_status": "SINGLE"}, - household={"state_code_str": "CA"}, + household={"state_code": "CA", "county_fips": "06037"}, year=2026, ) print("\nSingle adult, $50k income (California):") @@ -73,7 +73,9 @@ def us_example() -> None: print(f" Income tax: ${single.tax_unit.income_tax:,.0f}") print(f" Payroll tax: ${single.tax_unit.employee_payroll_tax:,.0f}") - # Married couple with two kids, Texas, lower income. + # Married couple with two kids, Texas, lower income. Explicit national + # measurement is recorded in the result; state alone does not select an + # SPM area. Use an observed county_fips for local SPM measurement instead. family = pe.us.calculate_household( people=[ {"age": 35, "employment_income": 40_000}, @@ -82,8 +84,9 @@ def us_example() -> None: {"age": 5}, ], tax_unit={"filing_status": "JOINT"}, - household={"state_code_str": "TX"}, + household={"state_code": "TX"}, year=2026, + spm={"geography_kind": "national"}, ) print("\nMarried couple with 2 children, $40k income (Texas):") print(f" Net income: ${family.household.household_net_income:,.0f}") @@ -91,6 +94,10 @@ def us_example() -> None: print(f" EITC: ${family.tax_unit.eitc:,.0f}") print(f" Child tax credit: ${family.tax_unit.ctc:,.0f}") print(f" SNAP: ${family.spm_unit.snap:,.0f}") + receipt = family.to_dict()["provenance"]["spm"] + print(f" SPM geography: {receipt['geography_kind']}") + print(f" SPM scenario: {receipt['scenario']}") + print(f" SPM artifact hash: {receipt['forecast_sha256']}") def main() -> None: diff --git a/pyproject.toml b/pyproject.toml index 5d4086e7..59f34b09 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,10 +11,8 @@ authors = [ {name = "PolicyEngine", email = "hello@policyengine.org"}, ] license = {file = "LICENSE"} -requires-python = ">=3.9" +requires-python = ">=3.11,<3.15" classifiers = [ - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", @@ -42,19 +40,19 @@ graph = [ "networkx>=3.0", ] models = [ - "policyengine-core==3.30.1", - "policyengine-us==1.764.6", + "policyengine-core==3.32.5", + "policyengine-us==2.2.1", "policyengine-uk==2.90.2", - "spm-calculator==0.3.1", + "spm-calculator==1.0.0", ] uk = [ - "policyengine-core==3.30.1", + "policyengine-core==3.32.5", "policyengine-uk==2.90.2", ] us = [ - "policyengine-core==3.30.1", - "policyengine-us==1.764.6", - "spm-calculator==0.3.1", + "policyengine-core==3.32.5", + "policyengine-us==2.2.1", + "spm-calculator==1.0.0", ] dev = [ "pytest", @@ -70,10 +68,10 @@ dev = [ "towncrier>=24.8.0", "mypy>=1.11.0", "pytest-cov>=5.0.0", - "policyengine-core==3.30.1", - "policyengine-us==1.764.6", + "policyengine-core==3.32.5", + "policyengine-us==2.2.1", "policyengine-uk==2.90.2", - "spm-calculator==0.3.1", + "spm-calculator==1.0.0", ] [tool.setuptools] @@ -100,7 +98,7 @@ filterwarnings = [ [tool.ruff] line-length = 88 -target-version = "py39" +target-version = "py311" extend-exclude = ["*.ipynb"] [tool.ruff.lint] @@ -113,13 +111,12 @@ select = [ ] ignore = [ "E501", # Ignore line length errors - # The following pyupgrade rules would require Python 3.10+, but we - # support 3.9+. Re-enable these once the 3.9 floor is dropped. - "UP006", # prefer `list` over `List` — OK on 3.9 at runtime, but for - # pydantic models we use typing.List for consistency - "UP007", # prefer `X | Y` over `Union[X, Y]` — needs 3.10+ - "UP035", # `typing.List` is deprecated — same as UP006 - "UP045", # prefer `X | None` over `Optional[X]` — needs 3.10+ + # Preserve the existing annotation style across Pydantic models. + "UP006", + "UP007", + "UP035", + "UP045", + "UP042", # Preserve public Enum string representations. ] [tool.ruff.format] @@ -129,7 +126,7 @@ skip-magic-trailing-comma = false line-ending = "auto" [tool.mypy] -python_version = "3.9" +python_version = "3.11" warn_return_any = true warn_unused_configs = true ignore_missing_imports = true diff --git a/scripts/bundle.py b/scripts/bundle.py index 8c2aa2ba..4c535e21 100644 --- a/scripts/bundle.py +++ b/scripts/bundle.py @@ -74,23 +74,69 @@ def _generate(args: argparse.Namespace) -> int: result = generate(check=False) if not args.include_tros: return result - return _generate_tros() + return _generate_tros(strict=args.strict_tros) def _check(args: argparse.Namespace) -> int: - from generate_bundle_artifacts import generate + from generate_bundle_artifacts import ( + generate, + load_bundle_manifest, + validate_bundle_measurements, + ) + validate_bundle_measurements( + load_bundle_manifest(), require_published_spm=args.published_spm + ) result = generate(check=True) if not args.include_tros: return result - return result or _check_tros() + return result or _check_tros(strict=args.strict_tros) + +def _set_spm(args: argparse.Namespace) -> int: + from generate_bundle_artifacts import ( + generate, + load_bundle_manifest, + write_bundle_manifest, + ) + from spm_bundle import configure_spm, published_calculator_component + + calculator = ( + published_calculator_component(args.calculator_version) + if args.calculator_version + else None + ) + settings = { + key: getattr(args, key) + for key in ( + "forecast_content_sha256", + "scenario", + "geography_kind", + "geography_id", + "county_vintage", + "as_of", + ) + } + write_bundle_manifest(configure_spm(load_bundle_manifest(), settings, calculator)) + return generate(check=False) + + +def _development_manifest(args: argparse.Namespace) -> int: + from generate_bundle_artifacts import load_bundle_manifest + from spm_bundle import write_development_manifest + + write_development_manifest( + load_bundle_manifest(), args.country_wheel, args.calculator_wheel, args.output + ) + print(f"Wrote uncertified local development fixture {args.output}") + return 0 -def _generate_tros() -> int: + +def _generate_tros(*, strict: bool = False) -> int: os.environ.setdefault("POLICYENGINE_SKIP_COUNTRY_IMPORTS", "1") from generate_trace_tros import regenerate_all - written, regressions = regenerate_all() + written, regressions = regenerate_all(strict=strict) for path in written: print(f"wrote {path}") for country_id, tro_path, reason in regressions: @@ -106,12 +152,12 @@ def _generate_tros() -> int: return 0 -def _check_tros() -> int: +def _check_tros(*, strict: bool = False) -> int: os.environ.setdefault("POLICYENGINE_SKIP_COUNTRY_IMPORTS", "1") from generate_trace_tros import generated_tros changed = False - for path, payload in generated_tros(): + for path, payload in generated_tros(strict=strict): if path.exists() and path.read_bytes() == payload: continue print( @@ -201,6 +247,32 @@ def _parser() -> argparse.ArgumentParser: ) packages.set_defaults(func=_update_packages) + spm = subparsers.add_parser( + "set-spm", help="Stage SPM defaults and optional verified PyPI calculator pin." + ) + spm.add_argument("--forecast-content-sha256", required=True) + spm.add_argument("--scenario", required=True) + spm.add_argument( + "--geography-kind", choices=["county", "metro", "national"], default="county" + ) + spm.add_argument("--geography-id") + spm.add_argument("--county-vintage", default="2020") + spm.add_argument("--as-of") + spm.add_argument( + "--calculator-version", + help="Verify actual published wheel bytes before adding the runtime dependency.", + ) + spm.set_defaults(func=_set_spm) + + development = subparsers.add_parser( + "development-manifest", + help="Write an uncertified fixture using actual local wheel metadata.", + ) + development.add_argument("--country-wheel", required=True, type=Path) + development.add_argument("--calculator-wheel", required=True, type=Path) + development.add_argument("--output", required=True, type=Path) + development.set_defaults(func=_development_manifest) + generate = subparsers.add_parser( "generate", help="Regenerate derived bundle artifacts.", @@ -219,6 +291,11 @@ def _parser() -> argparse.ArgumentParser: "check", help="Check derived bundle metadata.", ) + check.add_argument( + "--published-spm", + action="store_true", + help="Check SPM measurement pins include a published hashed calculator; does not certify country/data compatibility or release readiness.", + ) check.add_argument( "--include-tros", action="store_true", @@ -229,12 +306,25 @@ def _parser() -> argparse.ArgumentParser: ) check.set_defaults(func=_check) + for command in (generate, check): + command.add_argument( + "--strict-tros", + action="store_true", + help="Require full authenticated manifests matching reviewed TRO source pins; requires --include-tros.", + ) + return parser def main(argv: list[str] | None = None) -> int: - args = _parser().parse_args(argv) - return args.func(args) + parser = _parser() + args = parser.parse_args(argv) + if getattr(args, "strict_tros", False) and not args.include_tros: + parser.error("--strict-tros requires --include-tros") + try: + return args.func(args) + except ValueError as exc: + parser.error(str(exc)) if __name__ == "__main__": diff --git a/scripts/check_native_spm_loading.py b/scripts/check_native_spm_loading.py new file mode 100644 index 00000000..0e96f141 --- /dev/null +++ b/scripts/check_native_spm_loading.py @@ -0,0 +1,135 @@ +"""Run the producer's four-wheel native-input qualification without publication. + +Run only after installing the four candidate wheels in an isolated environment. +The wrapper must import from its installed wheel, never a checkout PYTHONPATH. +The explicit development bootstrap does not certify data or published packages. +""" + +from __future__ import annotations + +import argparse +import hashlib +import importlib.util +import json +import sys +from pathlib import Path + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def check_derived_weights(path: Path) -> dict[str, int]: + """Check household weight projection with one additional read-only load.""" + import numpy as np + + from policyengine.tax_benefit_models.us.datasets import PolicyEngineUSDataset + + dataset = PolicyEngineUSDataset( + name="native_spm_weight_qualification", + description="Read-only mapping check; not population model acceptance", + filepath=str(path), + year=2024, + ) + import pandas as pd + + # This is an ID/array qualification, never a population poverty statistic. + person = pd.DataFrame(dataset.data.person) + household = pd.DataFrame(dataset.data.household) + lookup = household.set_index("household_id")["household_weight"] + counts = {} + for entity, frame in dataset.data.entity_data.items(): + frame = pd.DataFrame(frame) + counts[entity] = len(frame) + if entity == "household": + continue + if entity == "person": + expected = person["person_household_id"].map(lookup) + else: + link = f"person_{entity}_id" + relationships = person[[link, "person_household_id"]].drop_duplicates() + assert not relationships[link].duplicated().any() + group_households = relationships.set_index(link)["person_household_id"] + expected = frame[f"{entity}_id"].map(group_households).map(lookup) + if not np.array_equal( + frame[f"{entity}_weight"].to_numpy(), expected.to_numpy() + ): + raise ValueError( + f"Wrapper {entity} weights do not match household projection" + ) + return counts + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--candidate-h5", required=True, type=Path) + parser.add_argument("--producer-src", required=True, type=Path) + parser.add_argument("--development-manifest", required=True, type=Path) + parser.add_argument("--wheel", required=True, action="append", type=Path) + parser.add_argument("--check-derived-weights", action="store_true") + parser.add_argument("--output", type=Path) + args = parser.parse_args() + if args.output is not None and args.output.exists(): + parser.error("Output must be a new diagnostic file") + + # Import the producer read-only. This path contains Microcosm only, so + # installed-wrapper source ownership remains independently verifiable. + sys.dont_write_bytecode = True + sys.path.insert(0, str(args.producer_src.resolve(strict=True))) + helper = ( + Path(__file__).resolve().parents[1] + / "tests" + / "fixtures" + / "spm_development.py" + ) + spec = importlib.util.spec_from_file_location("spm_qualification_bootstrap", helper) + bootstrap = importlib.util.module_from_spec(spec) + spec.loader.exec_module(bootstrap) + bootstrap.activate_spm_development_manifest(args.development_manifest) + + from microcosm.data.source_enrichment import run_native_loader_compatibility + + before = sha256_file(args.candidate_h5) + try: + receipt = run_native_loader_compatibility( + args.candidate_h5, + require_wheels=True, + compatibility_wheels=tuple(args.wheel), + ) + weight_check = ( + check_derived_weights(args.candidate_h5) + if args.check_derived_weights + else None + ) + finally: + after = sha256_file(args.candidate_h5) + if after != before: + raise RuntimeError("Native candidate H5 changed during qualification") + report = { + "scope": "read_only_native_input_qualification", + "data_certification": "not_certified", + "external_package_publication": "not_attested", + "canonical_spm_model_acceptance": "not_attested", + "candidate_sha256_before": before, + "candidate_sha256_after": after, + "development_manifest": { + "path": str(args.development_manifest.resolve()), + "sha256": sha256_file(args.development_manifest), + }, + "probe_sha256": sha256_file(Path(__file__)), + "derived_weight_check": weight_check, + "producer_compatibility": receipt, + } + text = json.dumps(report, indent=2, sort_keys=True) + "\n" + if args.output is not None: + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(text) + print(text, end="") + + +if __name__ == "__main__": + main() diff --git a/scripts/check_release_credentials.py b/scripts/check_release_credentials.py new file mode 100644 index 00000000..0f4546bb --- /dev/null +++ b/scripts/check_release_credentials.py @@ -0,0 +1,51 @@ +"""Verify the trusted release job's Hugging Face credential has read-only scope.""" + +from __future__ import annotations + +import os +import sys + +import requests + + +def check_read_only_token(token: str | None) -> None: + if not token or not token.strip(): + raise ValueError( + "Full private release verification requires HUGGING_FACE_TOKEN" + ) + try: + response = requests.get( + "https://huggingface.co/api/whoami-v2", + headers={"Authorization": f"Bearer {token}"}, + timeout=30, + allow_redirects=False, + ) + if response.status_code != 200: + raise ValueError + payload = response.json() + auth = payload.get("auth", {}) + if ( + auth.get("type") != "access_token" + or auth.get("accessToken", {}).get("role") != "read" + ): + raise ValueError + except (requests.RequestException, ValueError, TypeError, AttributeError): + # Never include the response, account, token, or request exception in CI + # output. Unknown and fine-grained permission schemas fail closed. + raise ValueError( + "Release verification requires an authenticated Hugging Face access token with role read" + ) from None + + +def main() -> int: + try: + check_read_only_token(os.environ.get("HUGGING_FACE_TOKEN")) + except ValueError as exc: + print(str(exc), file=sys.stderr) + return 1 + print("Verified read-only Hugging Face release credential") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/generate_bundle_artifacts.py b/scripts/generate_bundle_artifacts.py index d99b993d..cdbfaea7 100644 --- a/scripts/generate_bundle_artifacts.py +++ b/scripts/generate_bundle_artifacts.py @@ -4,7 +4,9 @@ import argparse import copy +import importlib.util import json +import os import re import sys from pathlib import Path @@ -20,6 +22,21 @@ BUNDLE_MANIFEST = ( REPO_ROOT / "src" / "policyengine" / "data" / "bundle" / "manifest.json" ) +# Other maintenance subcommands explicitly load runtime helpers after this +# module; retain their source-checkout and country-import behavior. +os.environ.setdefault("POLICYENGINE_SKIP_COUNTRY_IMPORTS", "1") +sys.path.insert(0, str(REPO_ROOT / "src")) + +# These commands run before dependencies are installed in release/PR CI. Load +# the same stdlib-only validator used at runtime without importing policyengine. +_VALIDATION_SPEC = importlib.util.spec_from_file_location( + "_policyengine_bundle_validation", + REPO_ROOT / "src" / "policyengine" / "provenance" / "bundle_validation.py", +) +assert _VALIDATION_SPEC is not None and _VALIDATION_SPEC.loader is not None +_VALIDATION_MODULE = importlib.util.module_from_spec(_VALIDATION_SPEC) +_VALIDATION_SPEC.loader.exec_module(_VALIDATION_MODULE) +validate_bundle_measurements = _VALIDATION_MODULE.validate_bundle_measurements OPTIONAL_DEPENDENCIES_HEADER = "[project.optional-dependencies]" NEXT_SECTION_PATTERN = re.compile(r"\n\[tool\.setuptools\]", re.MULTILINE) @@ -34,7 +51,9 @@ def load_bundle_manifest(path: Path = BUNDLE_MANIFEST) -> dict[str, Any]: - return json.loads(path.read_text()) + bundle = json.loads(path.read_text()) + validate_bundle_measurements(bundle) + return bundle def write_bundle_manifest( @@ -51,6 +70,7 @@ def normalized_manifest(bundle: Mapping[str, Any]) -> dict[str, Any]: so release assets and runtime commands can consume one stable shape. """ + validate_bundle_measurements(bundle) packages = { key: { **value, diff --git a/scripts/generate_trace_tros.py b/scripts/generate_trace_tros.py index bc7d5d26..a9541b46 100644 --- a/scripts/generate_trace_tros.py +++ b/scripts/generate_trace_tros.py @@ -6,18 +6,30 @@ matches the pinned bundle. The richer data release manifest is included when available; otherwise the TRO still binds the certified dataset sha256 and URI pinned in the bundle manifest. + +Public release workflows use ``strict=True``: complete manifests must match +the hash and URI in the reviewed sidecars, and all builder inputs must already +be pinned. Preparing a newly certified bundle remains a separate local step. """ from __future__ import annotations import json import sys +from importlib.resources import files from pathlib import Path +from jsonschema import Draft202012Validator +from release_lock import PYPI, load_toml, validate_registry_artifacts + +from policyengine.provenance.certification import validate_release_manifest from policyengine.provenance.manifest import ( + CountryReleaseManifest, + DataReleaseManifest, DataReleaseManifestUnavailableError, get_data_release_manifest, get_release_manifest, + https_release_manifest_uri, ) from policyengine.provenance.trace import ( build_trace_tro_from_release_bundle, @@ -29,32 +41,166 @@ REPO_ROOT / "src" / "policyengine" / "data" / "bundle" / "manifest.json" ) BUNDLE_TRO_DIR = REPO_ROOT / "src" / "policyengine" / "data" / "bundle" +BUNDLE_LOCK = REPO_ROOT / "uv.lock" -def regenerate_all() -> tuple[list[Path], list[tuple[str, Path, str]]]: +def regenerate_all( + *, strict: bool = False +) -> tuple[list[Path], list[tuple[str, Path, str]]]: written: list[Path] = [] regressions: list[tuple[str, Path, str]] = [] - for tro_path, payload in generated_tros(): + payloads = generated_tros(strict=strict) + BUNDLE_TRO_DIR.mkdir(parents=True, exist_ok=True) + for tro_path, payload in payloads: tro_path.write_bytes(payload) written.append(tro_path) return written, regressions -def generated_tros() -> list[tuple[Path, bytes]]: +def _validate_reviewed_inputs( + country: CountryReleaseManifest, + data: DataReleaseManifest, + tro_path: Path, +) -> None: + """Authenticate full release inputs without downloading the dataset again.""" + prefix = f"{country.country_id}: strict TRACE release" + revision = country.data_package.release_manifest_revision + if not revision or revision.lower() in {"main", "master", "latest"}: + raise ValueError(f"{prefix} requires a pinned data release manifest revision") + if not country.model_package.sha256 or not country.model_package.wheel_url: + raise ValueError(f"{prefix} requires a pinned model wheel hash and URL") + if ( + country.certification is None + or country.certification.certified_for_model_version + != country.model_package.version + ): + raise ValueError(f"{prefix} requires certification for the pinned model") + validate_release_manifest( + data, country.model_package.name, country.model_package.version + ) + certified = country.certified_data_artifact + artifact = data.artifacts.get(certified.dataset) if certified else None + if ( + certified is None + or artifact is None + or not certified.sha256 + or artifact.sha256 != certified.sha256 + ): + raise ValueError( + f"{prefix} requires the certified dataset and matching hash in the full data release manifest" + ) + + # The committed TRO is the reviewed source pin. A changed data release must + # first be certified and its full sidecar prepared/reviewed before release; + # Versioning may change wrapper versions, but never substitute remote bytes. + artifact_id = "composition/1/artifact/data_release_manifest" + try: + tro = json.loads(tro_path.read_bytes())["@graph"][0] + pins = [ + item["trov:sha256"] + for item in tro["trov:hasComposition"]["trov:hasArtifact"] + if item["@id"] == artifact_id + ] + locations = [ + item["trov:hasLocation"] + for arrangement in tro["trov:hasArrangement"] + for item in arrangement["trov:hasArtifactLocation"] + if item["trov:hasArtifact"]["@id"] == artifact_id + ] + except (OSError, ValueError, KeyError, IndexError, TypeError) as exc: + raise ValueError( + f"{prefix} requires a reviewed full sidecar at {tro_path}" + ) from exc + if not data.source_sha256 or pins != [data.source_sha256]: + raise ValueError( + f"{prefix}: reviewed data release manifest hash does not match fetched bytes" + ) + if locations != [https_release_manifest_uri(country.data_package)]: + raise ValueError( + f"{prefix}: reviewed data release manifest URI does not match the bundle" + ) + + +def _validate_runtime_model(bundle: dict, country: CountryReleaseManifest) -> None: + """Connect the certification to the package and wheel installed by the lock.""" + key = f"policyengine-{country.country_id}" + component = bundle.get("packages", {}).get(key, {}) + model = country.model_package + prefix = f"{country.country_id}: strict TRACE release" + if ( + model.name != key + or component.get("name") != model.name + or component.get("version") != model.version + or any( + field in component and component[field] != getattr(model, field) + for field in ("sha256", "wheel_url") + ) + ): + raise ValueError( + f"{prefix}: runtime country model differs from the certified model" + ) + try: + locked = [ + package + for package in load_toml(BUNDLE_LOCK).get("package", []) + if package["name"] == model.name + ] + except (OSError, ValueError) as exc: + raise ValueError(f"{prefix} requires the reviewed registry lock") from exc + if ( + len(locked) != 1 + or locked[0]["version"] != model.version + or locked[0].get("source") != {"registry": PYPI} + ): + raise ValueError( + f"{prefix}: registry lock differs from the certified runtime model" + ) + validate_registry_artifacts(locked[0]) + if {(wheel["url"], wheel["hash"]) for wheel in locked[0].get("wheels", [])} != { + (model.wheel_url, f"sha256:{model.sha256}") + }: + raise ValueError( + f"{prefix}: locked runtime wheel differs from the certified model wheel" + ) + + +def generated_tros(*, strict: bool = False) -> list[tuple[Path, bytes]]: + """Prepare and schema-validate every serialized country TRO without writes.""" payloads: list[tuple[Path, bytes]] = [] bundle = json.loads(BUNDLE_MANIFEST.read_text()) - BUNDLE_TRO_DIR.mkdir(parents=True, exist_ok=True) + schema = json.loads( + files("policyengine") + .joinpath("data", "schemas", "trace_tro.schema.json") + .read_text() + ) + validator = Draft202012Validator(schema) + if strict: + expected = { + country + for country in ("us", "uk") + if f"policyengine-{country}" in bundle.get("packages", {}) + } + if not expected or expected - bundle.get("data_releases", {}).keys(): + raise ValueError( + "Strict TRACE release requires a data release manifest for every bundled country model" + ) for country_id in sorted(bundle.get("data_releases", {})): tro_path = BUNDLE_TRO_DIR / f"{country_id}.trace.tro.jsonld" country_manifest = get_release_manifest(country_id) + if strict: + _validate_runtime_model(bundle, country_manifest) try: data_release_manifest = get_data_release_manifest(country_id) except DataReleaseManifestUnavailableError as exc: + if strict: + raise data_release_manifest = None print( f"warning: {country_id}: {exc}; writing limited TRO", file=sys.stderr, ) + if strict: + _validate_reviewed_inputs(country_manifest, data_release_manifest, tro_path) tro = build_trace_tro_from_release_bundle( country_manifest, data_release_manifest, @@ -63,7 +209,9 @@ def generated_tros() -> list[tuple[Path, bytes]]: model_wheel_url=country_manifest.model_package.wheel_url, emission_context={"pe:emittedIn": "repository-bundle"}, ) - payloads.append((tro_path, serialize_trace_tro(tro))) + payload = serialize_trace_tro(tro) + validator.validate(json.loads(payload)) + payloads.append((tro_path, payload)) return payloads diff --git a/scripts/release_lock.py b/scripts/release_lock.py new file mode 100644 index 00000000..93fab4db --- /dev/null +++ b/scripts/release_lock.py @@ -0,0 +1,198 @@ +"""Check the release lock, or refresh only its post-bump root version. + +Resolve reviewed dependency changes with ordinary ``uv lock`` before the PR is +reviewed. Versioning must not introduce a new dependency graph after review. +""" + +from __future__ import annotations + +import argparse +import copy +import os +import re +import subprocess +import sys +import tomllib +from pathlib import Path +from urllib.parse import urlsplit + +REPO_ROOT = Path(__file__).resolve().parents[1] +PYPI = "https://pypi.org/simple" + + +def validate_registry_artifacts(package: dict) -> None: + """PyPI's source label alone does not constrain uv's download locations.""" + artifacts = [*package.get("wheels", [])] + if package.get("sdist"): + artifacts.append(package["sdist"]) + if not artifacts: + raise ValueError( + f"Release lock dependency {package['name']} has no registry artifacts" + ) + for artifact in artifacts: + url = urlsplit(artifact.get("url", "")) + if ( + url.scheme != "https" + or url.hostname != "files.pythonhosted.org" + or url.username is not None + or url.password is not None + or url.port not in (None, 443) + or not url.path.startswith("/packages/") + or url.query + or url.fragment + ): + raise ValueError( + f"Release lock dependency {package['name']} has a non-PyPI artifact URL" + ) + if re.fullmatch(r"sha256:[0-9a-f]{64}", artifact.get("hash", "")) is None: + raise ValueError( + f"Release lock dependency {package['name']} has an invalid artifact SHA256" + ) + + +def load_toml(path: Path) -> dict: + return tomllib.loads(path.read_text()) + + +def validate_registry_project(project: dict) -> None: + """Reject development sources in public package and resolver metadata.""" + uv = project.get("tool", {}).get("uv", {}) + if uv.get("sources") or uv.get("index") or uv.get("workspace"): + raise ValueError( + "Release metadata must use registry dependencies without uv source/index/workspace overrides" + ) + + def reject_urls(value): + if isinstance(value, str) and "@" in value: + raise ValueError( + "Release requirements must be registry dependencies, not direct URLs or local paths" + ) + if isinstance(value, list): + for item in value: + reject_urls(item) + if isinstance(value, dict): + for item in value.values(): + reject_urls(item) + + metadata = project.get("project", {}) + for value in ( + metadata.get("dependencies", []), + metadata.get("optional-dependencies", {}), + project.get("dependency-groups", {}), + project.get("build-system", {}).get("requires", []), + uv.get("constraint-dependencies", []), + uv.get("override-dependencies", []), + ): + reject_urls(value) + + +def _normalized_python_range(value: object) -> str: + """Compare the reviewed range by content, not by spelling. + + ``uv lock`` writes a canonically spaced specifier set, so an unchanged + range can differ from ``pyproject.toml`` by whitespace alone. Any real + change to a clause still fails the comparison. + """ + if not isinstance(value, str): + return "" + return ",".join(clause.strip() for clause in value.split(",")) + + +def validate_registry_lock( + project: dict, lock: dict, *, allow_previous_version: bool = False +) -> None: + """The checkout itself is the only permitted non-registry package.""" + metadata = project["project"] + if _normalized_python_range(lock.get("requires-python")) != ( + _normalized_python_range(metadata["requires-python"]) + ): + raise ValueError("Release lock Python range differs from pyproject.toml") + root_name = metadata["name"].lower().replace("_", "-") + roots = [] + for package in lock.get("package", []): + if package["name"] == root_name: + roots.append(package) + if package.get("source") != {"editable": "."}: + raise ValueError( + "Release lock root must be the current checkout; dependencies must use the registry" + ) + elif package.get("source") != {"registry": PYPI}: + raise ValueError( + f"Release lock dependency {package['name']} is not from the PyPI registry" + ) + else: + validate_registry_artifacts(package) + if len(roots) != 1: + raise ValueError("Release lock must contain exactly one root package") + if not allow_previous_version and roots[0]["version"] != metadata["version"]: + raise ValueError("Release lock root version differs from pyproject.toml") + + +def without_root_version(lock: dict, root_name: str) -> dict: + result = copy.deepcopy(lock) + for package in result["package"]: + if package["name"] == root_name: + package.pop("version") + return result + + +def resolver_environment() -> dict[str, str]: + # Do not let an inherited development index, find-links directory, project + # path, frozen mode, or resolver option change this publication boundary. + env = {key: value for key, value in os.environ.items() if not key.startswith("UV_")} + env["UV_FROZEN"] = "0" + return env + + +def check_release_lock(root: Path = REPO_ROOT, *, refresh: bool = False) -> None: + project = load_toml(root / "pyproject.toml") + validate_registry_project(project) + lock_path = root / "uv.lock" + before_bytes = lock_path.read_bytes() + before = tomllib.loads(before_bytes.decode()) + validate_registry_lock(project, before, allow_previous_version=refresh) + command = [ + "uv", + "lock", + "--no-config", + "--no-sources", + "--default-index", + PYPI, + "--python", + sys.executable, + ] + kwargs = {"cwd": root, "env": resolver_environment(), "check": True} + try: + if refresh: + subprocess.run(command, **kwargs) + after = load_toml(lock_path) + validate_registry_lock(project, after) + name = project["project"]["name"].lower().replace("_", "-") + if without_root_version(before, name) != without_root_version(after, name): + raise ValueError( + "Versioning changed the reviewed dependency graph; prepare and review the lock before release" + ) + subprocess.run([*command, "--check"], **kwargs) + except (ValueError, OSError, subprocess.CalledProcessError): + if refresh: + lock_path.write_bytes(before_bytes) + raise + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--refresh", + action="store_true", + help="Refresh only the root package version after the automatic bump", + ) + args = parser.parse_args() + try: + check_release_lock(refresh=args.refresh) + except (ValueError, OSError, subprocess.CalledProcessError) as exc: + parser.error(str(exc)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/spm_bundle.py b/scripts/spm_bundle.py new file mode 100644 index 00000000..54e6c380 --- /dev/null +++ b/scripts/spm_bundle.py @@ -0,0 +1,147 @@ +"""SPM bundle pins and explicitly uncertified local-wheel development fixtures.""" + +from __future__ import annotations + +import copy +import hashlib +import json +import re +import zipfile +from email.parser import BytesParser +from pathlib import Path +from urllib.parse import urlparse + +import requests +from generate_bundle_artifacts import normalized_manifest +from packaging.version import Version + +from policyengine.provenance.bundle_validation import validate_bundle_measurements +from policyengine.provenance.spm_configuration import SPMMeasurementConfiguration + + +def _calculator_component(version: str, sha256: str, wheel_url: str) -> dict: + return { + "name": "spm-calculator", + "import_name": "spm_calculator", + "version": version, + "role": "runtime_dependency", + "country": "us", + "sha256": sha256, + "wheel_url": wheel_url, + } + + +def published_calculator_component(version: str) -> dict: + """Read authoritative registry metadata and verify the actual wheel bytes.""" + if str(Version(version)) != version: + raise ValueError("Pass an exact normalized calculator version") + response = requests.get( + f"https://pypi.org/pypi/spm-calculator/{version}/json", timeout=60 + ) + response.raise_for_status() + metadata = response.json() + if metadata["info"]["version"] != version: + raise ValueError("Registry returned a different calculator version") + wheels = [ + entry + for entry in metadata.get("urls", []) + if entry.get("packagetype") == "bdist_wheel" + and entry.get("filename", "").endswith("-py3-none-any.whl") + and not entry.get("yanked") + ] + if len(wheels) != 1: + raise ValueError("Expected exactly one non-yanked universal calculator wheel") + wheel = wheels[0] + url = urlparse(wheel["url"]) + if url.scheme != "https" or url.hostname != "files.pythonhosted.org": + raise ValueError("Calculator wheel must be published on files.pythonhosted.org") + expected = wheel.get("digests", {}).get("sha256", "") + if not re.fullmatch(r"[0-9a-f]{64}", expected): + raise ValueError("Registry wheel metadata has no valid sha256") + download = requests.get(wheel["url"], timeout=60) + download.raise_for_status() + if hashlib.sha256(download.content).hexdigest() != expected: + raise ValueError( + "Published calculator wheel bytes do not match registry sha256" + ) + return _calculator_component(version, expected, wheel["url"]) + + +def configure_spm(bundle: dict, configuration: dict, calculator: dict | None) -> dict: + """Stage measurement defaults and, when available, a verified runtime pin.""" + updated = copy.deepcopy(bundle) + updated.setdefault("measurements", {})["spm"] = ( + SPMMeasurementConfiguration.model_validate(configuration).model_dump( + mode="json" + ) + ) + if calculator is not None: + updated["packages"]["spm-calculator"] = calculator + for extra in ("us", "models"): + entries = updated["extras"][extra] + if "spm-calculator" not in entries: + entries.append("spm-calculator") + validate_bundle_measurements(updated) + return normalized_manifest(updated) + + +def local_wheel_component(path: Path, expected_name: str) -> dict: + """Extract actual wheel metadata and hash, without registry identity claims.""" + path = path.resolve(strict=True) + with zipfile.ZipFile(path) as archive: + metadata_paths = [ + name for name in archive.namelist() if name.endswith(".dist-info/METADATA") + ] + if len(metadata_paths) != 1: + raise ValueError("Wheel must contain exactly one METADATA file") + metadata = BytesParser().parsebytes(archive.read(metadata_paths[0])) + if metadata["Name"].lower().replace("_", "-") != expected_name: + raise ValueError(f"Expected {expected_name} wheel, got {metadata['Name']}") + version = str(Version(metadata["Version"])) + return { + "name": expected_name, + "version": version, + "sha256": hashlib.sha256(path.read_bytes()).hexdigest(), + "wheel_url": path.as_uri(), + } + + +def development_manifest( + bundle: dict, country_wheel: Path, calculator_wheel: Path +) -> dict: + """Build a local test identity, stripping the inherited US certification.""" + country = local_wheel_component(country_wheel, "policyengine-us") + calculator = local_wheel_component(calculator_wheel, "spm-calculator") + updated = copy.deepcopy(bundle) + updated["packages"]["policyengine-us"].update(country) + updated["data_releases"]["us"]["model_package"] = country + updated["data_releases"]["us"].pop("certification", None) + updated["data_releases"]["us"].pop("certified_data_artifact", None) + updated["development"] = { + "purpose": "local canonical SPM wrapper tests", + "data_certification": "not_certified", + "promotion_status": "pending", + "data_release_metadata": "inherited reference only; no compatibility claim", + "local_wheels": {"policyengine-us": country, "spm-calculator": calculator}, + } + return configure_spm( + updated, + bundle["measurements"]["spm"], + _calculator_component( + calculator["version"], calculator["sha256"], calculator["wheel_url"] + ), + ) + + +def write_development_manifest( + bundle: dict, country_wheel: Path, calculator_wheel: Path, output: Path +) -> dict: + """Write an explicitly local development fixture, never the packaged manifest.""" + from generate_bundle_artifacts import BUNDLE_MANIFEST + + if output.resolve() == BUNDLE_MANIFEST.resolve(): + raise ValueError("Development fixtures cannot overwrite the packaged manifest") + manifest = development_manifest(bundle, country_wheel, calculator_wheel) + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(json.dumps(manifest, indent=2, sort_keys=True) + "\n") + return manifest diff --git a/src/policyengine/__init__.py b/src/policyengine/__init__.py index 1c4fb0ae..587cc254 100644 --- a/src/policyengine/__init__.py +++ b/src/policyengine/__init__.py @@ -10,6 +10,7 @@ result = pe.us.calculate_household( people=[{"age": 35, "employment_income": 60000}], tax_unit={"filing_status": "SINGLE"}, + household={"state_code": "NY", "county_fips": "36061"}, year=2026, reform={"gov.irs.credits.ctc.amount.adult_dependent": 1000}, ) diff --git a/src/policyengine/bundle.py b/src/policyengine/bundle.py index c4518673..2831fa59 100644 --- a/src/policyengine/bundle.py +++ b/src/policyengine/bundle.py @@ -13,7 +13,7 @@ import shutil import subprocess import venv as venv_module -from datetime import datetime, timezone +from datetime import UTC, datetime from importlib import metadata from importlib.resources import files from pathlib import Path @@ -21,6 +21,7 @@ import requests +from policyengine.provenance.bundle_validation import validate_bundle_measurements from policyengine.provenance.dataset_materialization import ( DatasetMaterializationError, MaterializedDataset, @@ -71,6 +72,10 @@ def _normalise_manifest(manifest: Mapping[str, Any]) -> dict[str, Any]: payload.setdefault("packages", {}) payload.setdefault("extras", {}) payload.setdefault("data_releases", _data_releases_from_countries(payload)) + try: + validate_bundle_measurements(payload) + except ValueError as exc: + raise BundleError(str(exc)) from exc return payload @@ -183,10 +188,12 @@ def _include_component( return False role = component.get("role") country = component.get("country") - if role in {"bundle_carrier", "runtime_dependency"}: + if role == "bundle_carrier": return True if isinstance(country, str): return country in countries + if role == "runtime_dependency": + return True return key == "policyengine" @@ -352,7 +359,7 @@ def write_receipt( ) -> Path: receipt = { "schema_version": 1, - "installed_at": datetime.now(timezone.utc).isoformat(), + "installed_at": datetime.now(UTC).isoformat(), "bundle_version": manifest["bundle_version"], "policyengine_version": manifest["policyengine_version"], "countries": list(countries), diff --git a/src/policyengine/core/run_record.py b/src/policyengine/core/run_record.py index 10118077..c0bf7956 100644 --- a/src/policyengine/core/run_record.py +++ b/src/policyengine/core/run_record.py @@ -175,6 +175,15 @@ def build_simulation_run_record_payloads( output["tables"] = _table_summaries(simulation.output_dataset) results_payload = {"output_dataset": output} + if simulation.spm_config is not None: + recorded_config = simulation.output_dataset.metadata.get("spm_config") + if recorded_config != simulation.spm_config: + raise ValueError( + "SPM settings changed since this output was calculated; run again before writing a run record" + ) + input_payload["spm"] = simulation.spm_config + results_payload["spm"] = simulation.spm_provenance() + return {"reform": reform, "input": input_payload, "results": results_payload} diff --git a/src/policyengine/core/simulation.py b/src/policyengine/core/simulation.py index d9afd5ba..f337a7f2 100644 --- a/src/policyengine/core/simulation.py +++ b/src/policyengine/core/simulation.py @@ -1,3 +1,5 @@ +import hashlib +import json import logging from datetime import datetime from typing import Any, Optional, Union @@ -10,6 +12,7 @@ from .dynamic import Dynamic from .policy import Policy from .scoping_strategy import ScopingStrategy +from .spm import SPMProvenance, SPMSelection from .tax_benefit_model_version import TaxBenefitModelVersion logger = logging.getLogger(__name__) @@ -91,6 +94,43 @@ class Simulation(BaseModel): output_dataset: Optional[Dataset] = None + spm: Optional[SPMSelection] = Field( + default=None, + description="US SPM forecast and geography selection within the release bundle.", + ) + spm_receipt: Optional[SPMProvenance] = Field( + default=None, + description="Detached SPM calculation receipt, populated by a US run or load.", + ) + + @property + def spm_config(self) -> Optional[dict[str, Any]]: + """Resolve the US measurement selection without retaining a country object.""" + if getattr(self.tax_benefit_model_version, "country_code", None) != "us": + if self.spm is not None: + raise ValueError("SPM selection is only supported by the US model") + return None + from policyengine.tax_benefit_models.us.spm import resolve_spm_selection + + return resolve_spm_selection(self.spm) + + def spm_provenance(self) -> Optional[dict[str, Any]]: + """Return a detached JSON receipt, or None before a US calculation.""" + return ( + self.spm_receipt.model_dump(mode="json") + if self.spm_receipt is not None + else None + ) + + @property + def storage_id(self) -> str: + """Include resolved SPM settings in cache and saved-result identity.""" + config = self.spm_config + if config is None: + return self.id + encoded = json.dumps(config, sort_keys=True, separators=(",", ":")).encode() + return f"{self.id}-spm-{hashlib.sha256(encoded).hexdigest()}" + @model_validator(mode="after") def _compile_dict_reforms(self) -> "Simulation": """Coerce dict ``policy`` / ``dynamic`` inputs into proper objects. @@ -128,12 +168,24 @@ def _compile_dict_reforms(self) -> "Simulation": return self def run(self): + # Reject unsupported country settings even on model paths that do not + # use US measurement. Results from an earlier run are no longer valid. + if self.spm_config is not None: + self.spm_receipt = None + self.output_dataset = None self.tax_benefit_model_version.run(self) def ensure(self): - cached_result = _cache.get(self.id) + cache_key = self.storage_id + cached_result = _cache.get(cache_key) if cached_result: self.output_dataset = cached_result.output_dataset + self.spm = cached_result.spm + self.spm_receipt = ( + cached_result.spm_receipt.model_copy(deep=True) + if cached_result.spm_receipt is not None + else None + ) return try: self.tax_benefit_model_version.load(self) @@ -149,7 +201,18 @@ def ensure(self): self.run() self.save() - _cache.add(self.id, self) + # Cache a snapshot: changing this Simulation's selection then running + # it again must not overwrite the receipt for an earlier cache key. + _cache.add( + cache_key, + self.model_copy( + update={ + "spm_receipt": self.spm_receipt.model_copy(deep=True) + if self.spm_receipt is not None + else None + } + ), + ) def save(self): """Save the simulation's output dataset.""" @@ -171,15 +234,20 @@ def write_run_record(self, directory, **kwargs): return write_simulation_run_record(self, directory, **kwargs) @property - def release_bundle(self) -> dict[str, Optional[str]]: + def release_bundle(self) -> dict[str, Any]: bundle = ( self.tax_benefit_model_version.release_bundle if self.tax_benefit_model_version is not None else {} ) - return { + result = { **bundle, "dataset_filepath": self.dataset.filepath if self.dataset is not None else None, } + if self.spm_config is not None: + recorded = getattr(self.output_dataset, "metadata", {}).get("spm_config") + result["spm_config"] = dict(recorded or self.spm_config) + result["spm"] = self.spm_provenance() + return result diff --git a/src/policyengine/core/spm.py b/src/policyengine/core/spm.py new file mode 100644 index 00000000..2d14b86e --- /dev/null +++ b/src/policyengine/core/spm.py @@ -0,0 +1,85 @@ +"""Dependency-light public models for US SPM selection and calculation receipts.""" + +from datetime import date +from typing import Any, Literal, Optional + +from pydantic import ( + BaseModel, + ConfigDict, + Field, + SerializerFunctionWrapHandler, + field_validator, + model_serializer, + model_validator, +) + + +def _selection_schema(schema: dict[str, Any]) -> None: + """Keep generated clients from replacing inherited settings with defaults.""" + for field in schema.get("properties", {}).values(): + field.pop("default", None) + + +class SPMSelection(BaseModel): + """Select from the bundle's pinned artifact; national geography is explicit. + + County mode reads the household's observed ``county_fips``. A state alone + does not identify an SPM area. These settings contain no provider or path. + Serialization preserves omitted options so they still inherit bundle defaults + after a round trip. A resolved selection explicitly contains all six fields. + """ + + model_config = ConfigDict( + frozen=True, extra="forbid", json_schema_extra=_selection_schema + ) + + forecast_content_sha256: Optional[str] = Field( + default=None, pattern=r"^[0-9a-f]{64}$" + ) + scenario: Optional[str] = Field(default=None, min_length=1, pattern=r"^\S+$") + geography_kind: Literal["county", "national", "metro"] = "county" + geography_id: Optional[str] = Field(default=None, min_length=1) + county_vintage: str = Field(default="2020", pattern=r"^[0-9]{4}$") + as_of: Optional[str] = None + + @model_serializer(mode="wrap") + def serialize_selection(self, handler: SerializerFunctionWrapHandler): + """Keep inherited defaults distinct from explicit options in nested JSON.""" + return { + name: value + for name, value in handler(self).items() + if name in self.model_fields_set + } + + @field_validator("as_of") + @classmethod + def validate_as_of(cls, value): + if value is not None: + if date.fromisoformat(value).isoformat() != value: + raise ValueError("as_of must be an ISO calendar date (YYYY-MM-DD)") + return value + + @model_validator(mode="after") + def validate_location(self): + if self.geography_kind == "metro": + if not self.geography_id or not self.geography_id.strip(): + raise ValueError("An SPM area selection requires geography_id") + elif self.geography_id is not None: + raise ValueError("Only an SPM area selection accepts geography_id") + return self + + +class SPMProvenance(BaseModel): + """Detached calculation receipt; data certification is a separate claim.""" + + model_config = ConfigDict(extra="forbid") + + forecast_id: str + forecast_sha256: str + scenario: str + geography_kind: str + runtime_versions: dict[str, Optional[str]] + years: dict[str, dict[str, Any]] + geographies: list[dict[str, Any]] + composition_method: str + storage_method: str diff --git a/src/policyengine/core/tax_benefit_model_version.py b/src/policyengine/core/tax_benefit_model_version.py index 530d75a8..d6aead49 100644 --- a/src/policyengine/core/tax_benefit_model_version.py +++ b/src/policyengine/core/tax_benefit_model_version.py @@ -1,4 +1,4 @@ -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import TYPE_CHECKING, Optional from uuid import uuid4 @@ -31,9 +31,7 @@ class TaxBenefitModelVersion(BaseModel): model: TaxBenefitModel version: str description: Optional[str] = None - created_at: Optional[datetime] = Field( - default_factory=lambda: datetime.now(timezone.utc) - ) + created_at: Optional[datetime] = Field(default_factory=lambda: datetime.now(UTC)) variables: list["Variable"] = Field(default_factory=list) parameters: list["Parameter"] = Field(default_factory=list) diff --git a/src/policyengine/data/bundle/manifest.json b/src/policyengine/data/bundle/manifest.json index 1018ce8c..26468980 100644 --- a/src/policyengine/data/bundle/manifest.json +++ b/src/policyengine/data/bundle/manifest.json @@ -111,80 +111,104 @@ "version": "policyengine-uk-data-1.56.16" }, "us": { - "build_id": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "build_id": "populace-us-2024-spm-20260915", "bundle_id": "us-5.3.1", "certification": { - "built_with_model_version": "1.764.6", + "built_with_model_version": "2.2.1", "certified_by": "policyengine.py bundle certification", - "certified_for_model_version": "1.764.6", + "certified_for_model_version": "2.2.1", "compatibility_basis": "built_with_model_package", - "data_build_id": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z" + "data_build_id": "populace-us-2024-spm-20260915" }, "certified_data_artifact": { - "build_id": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "build_id": "populace-us-2024-spm-20260915", "data_package": { - "name": "populace-data", + "name": "microcosm-data", "version": "0.1.0" }, "dataset": "populace_us_2024", - "sha256": "48b9d479fb4fd1c3537f9383ce4697d130b6f618658409d74f6233c43b994c7e", - "uri": "hf://policyengine/populace-us/populace_us_2024.h5@populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z" + "sha256": "6496cc4393d4d3c6574f76eca231de5898c803b9067645591fd5c4d3e65aee84", + "uri": "hf://policyengine/populace-us/populace_us_2024.h5@populace-us-2024-spm-20260915" }, "country_id": "us", "data_package": { - "name": "populace-data", - "release_manifest_path": "releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/release_manifest.json", - "release_manifest_revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "name": "microcosm-data", + "release_manifest_path": "releases/populace-us-2024-spm-20260915/release_manifest.json", + "release_manifest_revision": "populace-us-2024-spm-20260915", "repo_id": "policyengine/populace-us", "repo_type": "dataset", "version": "0.1.0" }, "data_producer": "populace", "datasets": { + "build_manifest": { + "path": "releases/populace-us-2024-spm-20260915/build_manifest.json", + "repo_id": "policyengine/populace-us", + "revision": "populace-us-2024-spm-20260915", + "sha256": "f3fd734867324bca7619f5f083a8115e32104bea3948d48723b345cfd39fa03b" + }, "calibration_diagnostics": { - "path": "releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/calibration_diagnostics.json", + "path": "releases/populace-us-2024-spm-20260915/calibration_diagnostics.json", "repo_id": "policyengine/populace-us", - "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "revision": "populace-us-2024-spm-20260915", "sha256": "870449b44e86b13b25bcea1a57f0e7af37f4d4db18be815eea3acdf9fe6eb40e" }, - "demographics": { - "path": "releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/demographics.json", + "parent_build_manifest": { + "path": "releases/populace-us-2024-spm-20260915/parent_build_manifest.json", + "repo_id": "policyengine/populace-us", + "revision": "populace-us-2024-spm-20260915", + "sha256": "1990e8fd37ce66f499b8e6600c5896701bb07015be132a12de64e321a2edc67e" + }, + "parent_release_manifest": { + "path": "releases/populace-us-2024-spm-20260915/parent_release_manifest.json", "repo_id": "policyengine/populace-us", - "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", - "sha256": "50c39dea345fa4e21f32272304b376979b9572ca17383dd13061b752d9a81112" + "revision": "populace-us-2024-spm-20260915", + "sha256": "dd949ba3c4c7a56aff8442c6db5a031d2c84c3da59f5d14832f468c358604506" }, "populace_us_2024": { "path": "populace_us_2024.h5", "repo_id": "policyengine/populace-us", - "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", - "sha256": "48b9d479fb4fd1c3537f9383ce4697d130b6f618658409d74f6233c43b994c7e" + "revision": "populace-us-2024-spm-20260915", + "sha256": "6496cc4393d4d3c6574f76eca231de5898c803b9067645591fd5c4d3e65aee84" }, - "populace_us_2024_calibration": { - "path": "populace_us_2024_calibration.npz", + "source_enrichment": { + "path": "releases/populace-us-2024-spm-20260915/source_enrichment.json", "repo_id": "policyengine/populace-us", - "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", - "sha256": "b6e9c0678de5e79e8ddbd7a2a2f17c357bd6b368153cea7b5119c299a280a1b5" + "revision": "populace-us-2024-spm-20260915", + "sha256": "85329907b2b9c4299855b0970b1f24ede5819d3b16c312eb7400b4203bd71203" }, - "us_source_coverage": { - "path": "releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/us_source_coverage.json", + "source_enrichment_compatibility": { + "path": "releases/populace-us-2024-spm-20260915/source_enrichment_compatibility.json", "repo_id": "policyengine/populace-us", - "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", - "sha256": "6406c8686c292015a5bc7265a42402f89935f9395a8b63510efdd4d9562e7ff5" + "revision": "populace-us-2024-spm-20260915", + "sha256": "63109acffd486453c99c476dece6b0154cc5388e5a430830a30744a2249444d1" + }, + "source_spm_independence_provenance": { + "path": "releases/populace-us-2024-spm-20260915/source_spm_independence_provenance.json", + "repo_id": "policyengine/populace-us", + "revision": "populace-us-2024-spm-20260915", + "sha256": "265e844178ed3856d68c1638820f3089e0e1e1363aa4677e33bebb37cbd5bff3" }, - "us_ssi_take_up": { - "path": "releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/us_ssi_take_up.json", + "source_spm_person_independence": { + "path": "releases/populace-us-2024-spm-20260915/source_spm_person_independence.csv", "repo_id": "policyengine/populace-us", - "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", - "sha256": "ce631a37e2fe2ed22927fa232d41150d3b6cf328b38d3e5b38018fc4eec1b44d" + "revision": "populace-us-2024-spm-20260915", + "sha256": "22b5968d90fecfeef7614583e493fe10cc16bda8b5be82e6f49a5bc2102d3ce5" + }, + "us_source_coverage": { + "path": "releases/populace-us-2024-spm-20260915/us_source_coverage.json", + "repo_id": "policyengine/populace-us", + "revision": "populace-us-2024-spm-20260915", + "sha256": "6406c8686c292015a5bc7265a42402f89935f9395a8b63510efdd4d9562e7ff5" } }, "default_dataset": "populace_us_2024", - "default_dataset_uri": "hf://policyengine/populace-us/populace_us_2024.h5@populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "default_dataset_uri": "hf://policyengine/populace-us/populace_us_2024.h5@populace-us-2024-spm-20260915", "model_package": { "name": "policyengine-us", - "sha256": "2b2c5c02c26ab212270948db1f30129728b625fc6109fc9ca0c604b714546909", - "version": "1.764.6", - "wheel_url": "https://files.pythonhosted.org/packages/e2/5a/0566666f30ad06e416f0cf25b24bdee73acd71897fb2de3c0ac43b59e3e3/policyengine_us-1.764.6-py3-none-any.whl" + "sha256": "0993a6c73fcdfbe171a796aca9d302741bd8e00ab83388ad81c15a08740318c6", + "version": "2.2.1", + "wheel_url": "https://files.pythonhosted.org/packages/5e/09/86ac3968d2a18e28fce3ba1186fc21728e9f299b96a806d6f204f1e0aaed/policyengine_us-2.2.1-py3-none-any.whl" }, "policyengine_version": "5.3.1", "region_datasets": { @@ -192,10 +216,10 @@ "path_template": "populace_us_2024.h5" } }, - "release_manifest_uri": "https://huggingface.co/datasets/policyengine/populace-us/resolve/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/release_manifest.json", + "release_manifest_uri": "https://huggingface.co/datasets/policyengine/populace-us/resolve/populace-us-2024-spm-20260915/releases/populace-us-2024-spm-20260915/release_manifest.json", "schema_version": 1, - "source_manifest_uri": "hf://dataset/policyengine/populace-us@populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/release_manifest.json", - "version": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z" + "source_manifest_uri": "hf://dataset/policyengine/populace-us@populace-us-2024-spm-20260915/releases/populace-us-2024-spm-20260915/release_manifest.json", + "version": "populace-us-2024-spm-20260915" } }, "dataset_overlays": { @@ -253,6 +277,16 @@ "spm-calculator" ] }, + "measurements": { + "spm": { + "as_of": null, + "county_vintage": "2020", + "forecast_content_sha256": "3d86d5c4c0423480e6b69b75d222ffa4a7a2639e4094df5ba2504af01be17173", + "geography_id": null, + "geography_kind": "county", + "scenario": "ce_trend" + } + }, "packages": { "policyengine": { "import_name": "policyengine", @@ -263,10 +297,10 @@ }, "policyengine-core": { "import_name": "policyengine_core", - "install_requirement": "policyengine-core==3.30.1", + "install_requirement": "policyengine-core==3.32.5", "name": "policyengine-core", "role": "runtime_dependency", - "version": "3.30.1" + "version": "3.32.5" }, "policyengine-uk": { "country": "uk", @@ -279,18 +313,20 @@ "policyengine-us": { "country": "us", "import_name": "policyengine_us", - "install_requirement": "policyengine-us==1.764.6", + "install_requirement": "policyengine-us==2.2.1", "name": "policyengine-us", "role": "country_model", - "version": "1.764.6" + "version": "2.2.1" }, "spm-calculator": { "country": "us", "import_name": "spm_calculator", - "install_requirement": "spm-calculator==0.3.1", + "install_requirement": "spm-calculator==1.0.0", "name": "spm-calculator", - "role": "country_dependency", - "version": "0.3.1" + "role": "runtime_dependency", + "sha256": "e354937a5e1a4045d4966ed594a528d8b02866fabaac9bb5672017004b627305", + "version": "1.0.0", + "wheel_url": "https://files.pythonhosted.org/packages/19/a0/c484f69a0ebf88a9b9fd0ac28176f8df66550f46714f1b0c5ec0b360824e/spm_calculator-1.0.0-py3-none-any.whl" } }, "policyengine_version": "5.3.1", diff --git a/src/policyengine/data/bundle/uk.trace.tro.jsonld b/src/policyengine/data/bundle/uk.trace.tro.jsonld index ecea2927..ac6a207f 100644 --- a/src/policyengine/data/bundle/uk.trace.tro.jsonld +++ b/src/policyengine/data/bundle/uk.trace.tro.jsonld @@ -75,7 +75,7 @@ "@type": "trov:ResearchArtifact", "schema:name": "policyengine.py bundle manifest for uk", "trov:mimeType": "application/json", - "trov:sha256": "75a9dab38c12dd00051f363532f2f45315557fecaf41d5827615bac4180de358" + "trov:sha256": "cb714acc65b9b131887e8d423b1a8a4dfd5f4c3833f5dd27ab992b2024166a60" }, { "@id": "composition/1/artifact/data_release_manifest", @@ -102,7 +102,7 @@ "trov:hasFingerprint": { "@id": "composition/1/fingerprint", "@type": "trov:CompositionFingerprint", - "trov:sha256": "0f664a8f0d9a4adaff2b7a04cac2dcb9d813770e395b6d77170a8078360e3733" + "trov:sha256": "846ccd2880c885e9704ed9b8830de5d5c07ea0f7489661f35167f40e9a7f6177" } }, "trov:hasPerformance": { diff --git a/src/policyengine/data/bundle/us.trace.tro.jsonld b/src/policyengine/data/bundle/us.trace.tro.jsonld index f579283a..07ba8c9c 100644 --- a/src/policyengine/data/bundle/us.trace.tro.jsonld +++ b/src/policyengine/data/bundle/us.trace.tro.jsonld @@ -17,7 +17,6 @@ "schema:name": "PolicyEngine", "schema:url": "https://policyengine.org" }, - "schema:dateCreated": "2026-07-28T03:56:03.415544+00:00", "schema:description": "TRACE TRO for certified runtime bundle us-5.3.1 covering the bundle manifest, the certified dataset artifact, the country model wheel, and the country data release manifest when it is available.", "schema:name": "policyengine us certified bundle TRO", "trov:createdWith": { @@ -45,7 +44,7 @@ "trov:hasArtifact": { "@id": "composition/1/artifact/data_release_manifest" }, - "trov:hasLocation": "https://huggingface.co/datasets/policyengine/populace-us/resolve/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/release_manifest.json" + "trov:hasLocation": "https://huggingface.co/datasets/policyengine/populace-us/resolve/populace-us-2024-spm-20260915/releases/populace-us-2024-spm-20260915/release_manifest.json" }, { "@id": "arrangement/1/location/dataset", @@ -53,7 +52,7 @@ "trov:hasArtifact": { "@id": "composition/1/artifact/dataset" }, - "trov:hasLocation": "https://huggingface.co/datasets/policyengine/populace-us/resolve/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/populace_us_2024.h5" + "trov:hasLocation": "https://huggingface.co/datasets/policyengine/populace-us/resolve/populace-us-2024-spm-20260915/populace_us_2024.h5" }, { "@id": "arrangement/1/location/model_wheel", @@ -61,7 +60,7 @@ "trov:hasArtifact": { "@id": "composition/1/artifact/model_wheel" }, - "trov:hasLocation": "https://files.pythonhosted.org/packages/e2/5a/0566666f30ad06e416f0cf25b24bdee73acd71897fb2de3c0ac43b59e3e3/policyengine_us-1.764.6-py3-none-any.whl" + "trov:hasLocation": "https://files.pythonhosted.org/packages/5e/09/86ac3968d2a18e28fce3ba1186fc21728e9f299b96a806d6f204f1e0aaed/policyengine_us-2.2.1-py3-none-any.whl" } ] } @@ -75,50 +74,49 @@ "@type": "trov:ResearchArtifact", "schema:name": "policyengine.py bundle manifest for us", "trov:mimeType": "application/json", - "trov:sha256": "75a9dab38c12dd00051f363532f2f45315557fecaf41d5827615bac4180de358" + "trov:sha256": "cb714acc65b9b131887e8d423b1a8a4dfd5f4c3833f5dd27ab992b2024166a60" }, { "@id": "composition/1/artifact/data_release_manifest", "@type": "trov:ResearchArtifact", - "schema:name": "populace-data release manifest 0.1.0", + "schema:name": "microcosm-data release manifest 0.1.0", "trov:mimeType": "application/json", - "trov:sha256": "dd949ba3c4c7a56aff8442c6db5a031d2c84c3da59f5d14832f468c358604506" + "trov:sha256": "d5c9e2a33d097294af60a816fafb1bdcfbbd81cdda70ae4637daceb829dedc01" }, { "@id": "composition/1/artifact/dataset", "@type": "trov:ResearchArtifact", "schema:name": "populace_us_2024", "trov:mimeType": "application/x-hdf5", - "trov:sha256": "48b9d479fb4fd1c3537f9383ce4697d130b6f618658409d74f6233c43b994c7e" + "trov:sha256": "6496cc4393d4d3c6574f76eca231de5898c803b9067645591fd5c4d3e65aee84" }, { "@id": "composition/1/artifact/model_wheel", "@type": "trov:ResearchArtifact", - "schema:name": "policyengine-us==1.764.6 wheel", + "schema:name": "policyengine-us==2.2.1 wheel", "trov:mimeType": "application/zip", - "trov:sha256": "2b2c5c02c26ab212270948db1f30129728b625fc6109fc9ca0c604b714546909" + "trov:sha256": "0993a6c73fcdfbe171a796aca9d302741bd8e00ab83388ad81c15a08740318c6" } ], "trov:hasFingerprint": { "@id": "composition/1/fingerprint", "@type": "trov:CompositionFingerprint", - "trov:sha256": "1a3131f53ec77aa7eebf243f01bff2969848e8979dc40d23d94f28dff03aa5de" + "trov:sha256": "73d73bf16a7624feb60ae4fe4cffc3caf71a6896af67c8095633b3a1d0a1f153" } }, "trov:hasPerformance": { "@id": "trp/1", "@type": "trov:TransparentResearchPerformance", - "pe:builtWithModelVersion": "1.764.6", + "pe:builtWithModelVersion": "2.2.1", "pe:certifiedBy": "policyengine.py bundle certification", - "pe:certifiedForModelVersion": "1.764.6", + "pe:certifiedForModelVersion": "2.2.1", "pe:compatibilityBasis": "built_with_model_package", - "pe:dataBuildId": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "pe:dataBuildId": "populace-us-2024-spm-20260915", "pe:emittedIn": "repository-bundle", - "rdfs:comment": "Certification of build populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z for policyengine-us 1.764.6.", + "rdfs:comment": "Certification of build populace-us-2024-spm-20260915 for policyengine-us 2.2.1.", "trov:accessedArrangement": { "@id": "arrangement/1" }, - "trov:startedAtTime": "2026-07-28T03:56:03.415544+00:00", "trov:wasConductedBy": { "@id": "trs" } diff --git a/src/policyengine/data/schemas/trace_tro.schema.json b/src/policyengine/data/schemas/trace_tro.schema.json index f7b902ca..6046caf6 100644 --- a/src/policyengine/data/schemas/trace_tro.schema.json +++ b/src/policyengine/data/schemas/trace_tro.schema.json @@ -174,7 +174,7 @@ }, "pe:emittedIn": { "type": "string", - "enum": ["local", "github-actions", "policyengine-api"] + "enum": ["local", "github-actions", "policyengine-api", "repository-bundle"] } } } diff --git a/src/policyengine/outputs/decile_impact.py b/src/policyengine/outputs/decile_impact.py index b41d5973..097d50df 100644 --- a/src/policyengine/outputs/decile_impact.py +++ b/src/policyengine/outputs/decile_impact.py @@ -9,6 +9,7 @@ from policyengine.core.dataset import Dataset from policyengine.core.dynamic import Dynamic from policyengine.core.policy import Policy +from policyengine.core.spm import SPMSelection from policyengine.core.tax_benefit_model_version import TaxBenefitModelVersion from policyengine.outputs.decile_analysis import ( _prepare_decile_analysis, @@ -151,9 +152,14 @@ def calculate_decile_impacts( quantiles: int = 10, baseline_simulation: Optional[Simulation] = None, reform_simulation: Optional[Simulation] = None, + spm: Optional[SPMSelection] = None, ) -> OutputCollection[DecileImpact]: """Calculate decile-by-decile impact of a reform. + ``spm`` selects canonical US measurement settings when constructing the + baseline/reform simulations. When supplying simulations, select SPM on + each Simulation directly. + By default, changes are measured in ``household_net_income`` and household deciles are computed from that variable using survey weights multiplied by household size. Households with negative values of the computed income @@ -174,6 +180,8 @@ def calculate_decile_impacts( raise ValueError( "baseline_simulation and reform_simulation must be provided together" ) + if baseline_simulation is not None and spm is not None: + raise ValueError("Pass SPM settings on the supplied simulations directly") if baseline_simulation is None: if dataset is None or tax_benefit_model_version is None: @@ -186,12 +194,14 @@ def calculate_decile_impacts( tax_benefit_model_version=tax_benefit_model_version, policy=baseline_policy, dynamic=dynamic, + spm=spm, ) reform_simulation = Simulation( dataset=dataset, tax_benefit_model_version=tax_benefit_model_version, policy=reform_policy, dynamic=dynamic, + spm=spm, ) assert baseline_simulation is not None diff --git a/src/policyengine/provenance/bundle_validation.py b/src/policyengine/provenance/bundle_validation.py new file mode 100644 index 00000000..51324de8 --- /dev/null +++ b/src/policyengine/provenance/bundle_validation.py @@ -0,0 +1,152 @@ +"""Validate independently pinned measurement configuration in bundle manifests.""" + +import re +from datetime import date +from typing import Any, Mapping +from urllib.parse import urlparse + +# Canonical PEP 440 spellings only: no aliases, leading zeroes, or implicit +# pre/post/dev numbers. Keep bootstrap validation independent of packaging. +_INTEGER = r"(?:0|[1-9][0-9]*)" +_NORMALIZED_VERSION = re.compile( + rf"(?:[1-9][0-9]*!)?{_INTEGER}(?:\.{_INTEGER})*" + rf"(?:(?:a|b|rc){_INTEGER})?(?:\.post{_INTEGER})?(?:\.dev{_INTEGER})?" + r"(?:\+[a-z0-9]+(?:\.[a-z0-9]+)*)?" +) + + +def _is_normalized_version(value: Any) -> bool: + if not isinstance(value, str) or not _NORMALIZED_VERSION.fullmatch(value): + return False + local = value.partition("+")[2] + return all( + not part.isdigit() or part == str(int(part)) for part in local.split(".") + ) + + +def validate_spm_configuration(configuration: Any) -> None: + """Validate JSON defaults using only the standard library. + + Both runtime loading and pre-install bundle maintenance call this function. + Keep it importable by file path without executing the wrapper package. + """ + if not isinstance(configuration, Mapping): + raise ValueError("SPM measurement configuration must be an object") + allowed = { + "forecast_content_sha256", + "scenario", + "geography_kind", + "geography_id", + "county_vintage", + "as_of", + } + if set(configuration) - allowed: + raise ValueError("SPM measurement configuration contains unknown fields") + sha256 = configuration.get("forecast_content_sha256") + if not isinstance(sha256, str) or not re.fullmatch(r"[0-9a-f]{64}", sha256): + raise ValueError("SPM forecast_content_sha256 must be a lowercase SHA256") + scenario = configuration.get("scenario") + if not isinstance(scenario, str) or not re.fullmatch(r"\S+", scenario): + raise ValueError("SPM scenario must be nonempty without whitespace") + kind = configuration.get("geography_kind", "county") + if kind not in ("county", "metro", "national"): + raise ValueError("SPM geography_kind must be county, metro, or national") + geography_id = configuration.get("geography_id") + if geography_id is not None and ( + not isinstance(geography_id, str) + or not geography_id.strip() + or geography_id != geography_id.strip() + ): + raise ValueError("SPM geography_id must be nonempty without outer whitespace") + if (kind == "metro") != (geography_id is not None): + raise ValueError("Only metro SPM selection requires geography_id") + vintage = configuration.get("county_vintage", "2020") + if not isinstance(vintage, str) or not re.fullmatch(r"[0-9]{4}", vintage): + raise ValueError("SPM county_vintage must be a four-digit year") + as_of = configuration.get("as_of") + if as_of is not None: + if not isinstance(as_of, str) or not re.fullmatch( + r"[0-9]{4}-[0-9]{2}-[0-9]{2}", as_of + ): + raise ValueError("SPM as_of must be an ISO YYYY-MM-DD date") + if date.fromisoformat(as_of).isoformat() != as_of: + raise ValueError("SPM as_of must be an ISO YYYY-MM-DD date") + + +def validate_bundle_measurements( + bundle: Mapping[str, Any], *, require_published_spm: bool = False +) -> None: + """Reject malformed SPM pins, retaining support for historical bundles. + + Drafts may stage a content pin before registry publication. The stricter + measurement-pin check requires the calculator's published wheel identity as + well. It does not establish country/data compatibility or release readiness. + """ + measurements = bundle.get("measurements", {}) + if not isinstance(measurements, Mapping): + raise ValueError("Bundle measurements must be an object") + if "spm" not in measurements: + if require_published_spm: + raise ValueError("Published SPM pin check requires measurements.spm") + return + validate_spm_configuration(measurements["spm"]) + packages = bundle.get("packages", {}) + if not isinstance(packages, Mapping): + raise ValueError("Bundle packages must be an object") + calculator = packages.get("spm-calculator") + if calculator is None: + if require_published_spm: + raise ValueError("SPM promotion pending: no published spm-calculator pin") + return + if not isinstance(calculator, Mapping): + raise ValueError("spm-calculator package pin must be an object") + role = calculator.get("role") + if calculator.get("name") != "spm-calculator" or role not in ( + "runtime_dependency", + "country_dependency", + ): + raise ValueError("spm-calculator must be an explicit runtime_dependency") + # A protective `country_dependency` pin declares a compatible version only. + # A `runtime_dependency` pin additionally carries the registry-verified + # wheel identity that the promotion gate below requires. + verified = role == "runtime_dependency" + if calculator.get("country") != "us": + raise ValueError("spm-calculator must be scoped to country us") + if calculator.get("installable", True) is not True or calculator.get("markers"): + raise ValueError("spm-calculator must be installed unconditionally for US") + sha256 = calculator.get("sha256") + if verified and ( + not isinstance(sha256, str) or not re.fullmatch(r"[0-9a-f]{64}", sha256) + ): + raise ValueError("spm-calculator requires its wheel sha256") + version = calculator.get("version") + if not _is_normalized_version(version): + raise ValueError("spm-calculator requires an exact normalized version") + requirement = calculator.get("install_requirement") + expected_requirement = f"spm-calculator=={version}" + if calculator.get("markers"): + expected_requirement += f"; {calculator['markers']}" + if requirement is not None and requirement != expected_requirement: + raise ValueError( + "spm-calculator install_requirement must match its exact version" + ) + extras = bundle.get("extras", {}) + if not isinstance(extras, Mapping): + raise ValueError("Bundle extras must be an object") + for extra in ("us", "models"): + entries = extras.get(extra, []) + if not isinstance(entries, list) or "spm-calculator" not in entries: + raise ValueError(f"spm-calculator must be included in the {extra} extra") + if require_published_spm: + if not verified: + raise ValueError("SPM promotion pending: no published spm-calculator pin") + if bundle.get("development"): + raise ValueError( + "Development fixtures are not certified production bundles" + ) + wheel_url = calculator.get("wheel_url") + if not isinstance(wheel_url, str): + raise ValueError("Published SPM pin check requires a published PyPI wheel") + url = urlparse(wheel_url) + if url.scheme != "https" or url.hostname != "files.pythonhosted.org": + raise ValueError("Published SPM pin check requires a published PyPI wheel") diff --git a/src/policyengine/provenance/certification.py b/src/policyengine/provenance/certification.py index 73e4f1db..19d08ff0 100644 --- a/src/policyengine/provenance/certification.py +++ b/src/policyengine/provenance/certification.py @@ -70,6 +70,13 @@ # check, so a claims-certified bundle stays importable without network access. BASIS_PUBLISHER_CLAIM = "legacy_compatible_model_package" POPULACE_US_SOURCE_COVERAGE_FILE = "us_source_coverage.json" +# The Populace US producer publishes under `microcosm-data` since the +# source-enrichment releases; `populace-data` is its earlier name. Both denote +# the same producer, so every Populace-specific rule below must accept either. +POPULACE_DATA_PACKAGES = frozenset({"populace-data", "microcosm-data"}) +# Artifact kinds published beside the release manifest rather than at the repo +# root, so their manifest paths are relative to the release directory. +RELEASE_SCOPED_ARTIFACT_KINDS = frozenset({"diagnostics", "evidence"}) US_STATE_CODES = ( "AL", "AK", @@ -322,7 +329,7 @@ def artifact_path_for_country_manifest(artifact, uri_parts: dict) -> str: path = artifact.path release_dir = release_manifest_dir(uri_parts) if ( - artifact.kind == "diagnostics" + artifact.kind in RELEASE_SCOPED_ARTIFACT_KINDS and artifact.repo_id == uri_parts["repo_id"] and artifact.revision == uri_parts["revision"] and not path.startswith(f"{release_dir}/") @@ -370,7 +377,7 @@ def required_supplemental_release_files( ) -> tuple[str, ...]: if ( country == "us" - and manifest.data_package.name == "populace-data" + and manifest.data_package.name in POPULACE_DATA_PACKAGES and uri_parts["repo_id"] == "policyengine/populace-us" ): return (POPULACE_US_SOURCE_COVERAGE_FILE,) @@ -384,7 +391,7 @@ def should_validate_vendored_artifacts( ) -> bool: return ( country == "us" - and manifest.data_package.name == "populace-data" + and manifest.data_package.name in POPULACE_DATA_PACKAGES and uri_parts["repo_id"] == "policyengine/populace-us" ) @@ -489,7 +496,7 @@ def build_country_manifest_payload( for name, artifact in manifest.artifacts.items(): if ( country == "us" - and manifest.data_package.name == "populace-data" + and manifest.data_package.name in POPULACE_DATA_PACKAGES and artifact.path.endswith(".h5") and ( artifact.path.startswith("states/") @@ -513,7 +520,7 @@ def build_country_manifest_payload( for region, template in sorted(raw_regions.items()): if ( country == "us" - and manifest.data_package.name == "populace-data" + and manifest.data_package.name in POPULACE_DATA_PACKAGES and region in {"state", "congressional_district"} ): continue diff --git a/src/policyengine/provenance/spm_configuration.py b/src/policyengine/provenance/spm_configuration.py new file mode 100644 index 00000000..277a4493 --- /dev/null +++ b/src/policyengine/provenance/spm_configuration.py @@ -0,0 +1,26 @@ +"""Typed operator input for SPM bundle configuration.""" + +from typing import Literal, Optional + +from pydantic import BaseModel, ConfigDict, Field, model_validator + +from policyengine.provenance.bundle_validation import validate_spm_configuration + + +class SPMMeasurementConfiguration(BaseModel): + """Canonical SPM defaults; this pin is not a data certification claim.""" + + model_config = ConfigDict(extra="forbid", strict=True) + + forecast_content_sha256: str = Field(pattern=r"^[0-9a-f]{64}$") + scenario: str = Field(min_length=1, pattern=r"^\S+$") + geography_kind: Literal["county", "metro", "national"] = "county" + geography_id: Optional[str] = None + county_vintage: str = Field(default="2020", pattern=r"^[0-9]{4}$") + as_of: Optional[str] = None + + @model_validator(mode="before") + @classmethod + def canonical_defaults(cls, value): + validate_spm_configuration(value) + return value diff --git a/src/policyengine/provenance/zenodo.py b/src/policyengine/provenance/zenodo.py index 97d7801f..8fe4c41b 100644 --- a/src/policyengine/provenance/zenodo.py +++ b/src/policyengine/provenance/zenodo.py @@ -27,7 +27,7 @@ import hashlib import os from dataclasses import dataclass, field -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import Any, Callable, Optional from .manifest import ( @@ -477,7 +477,7 @@ def mirror_release_to_zenodo( "resume it manually before retrying, to avoid orphaned drafts." ) from exc - deposited_at = datetime.now(timezone.utc).isoformat() + deposited_at = datetime.now(UTC).isoformat() mirrors = [ PreservationMirror( kind="zenodo", diff --git a/src/policyengine/tax_benefit_models/common/model_version.py b/src/policyengine/tax_benefit_models/common/model_version.py index c5e6c090..1d6982b9 100644 --- a/src/policyengine/tax_benefit_models/common/model_version.py +++ b/src/policyengine/tax_benefit_models/common/model_version.py @@ -17,6 +17,7 @@ from __future__ import annotations import datetime +import json import os import tempfile import warnings @@ -25,6 +26,7 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, ClassVar, Optional +import h5py import pandas as pd from policyengine.core import ( @@ -60,7 +62,7 @@ def output_dataset_filepath(simulation: Simulation) -> Path: parent = ( Path(input_filepath).parent if input_filepath else Path(tempfile.gettempdir()) ) - return parent / (simulation.id + ".h5") + return parent / (simulation.storage_id + ".h5") def build_runtime_dataset_provenance( @@ -353,7 +355,36 @@ def save(self, simulation: Simulation) -> None: "simulation.run() or simulation.ensure() first so there is " "something to persist." ) + serialized_spm = None + if self.country_code == "us": + from policyengine.core.spm import SPMProvenance + + receipt = SPMProvenance.model_validate(simulation.spm_provenance()) + if ( + simulation.output_dataset.metadata.get("spm_config") + != simulation.spm_config + ): + raise ValueError( + "SPM settings changed since this output was calculated; run again before saving" + ) + serialized_spm = json.dumps( + { + "config": simulation.spm_config, + "provenance": receipt.model_dump(mode="json"), + }, + sort_keys=True, + ) simulation.output_dataset.save() + if serialized_spm is not None: + # Store UTF-8 JSON in a dataset rather than an attribute: the + # payload size stays unbounded and the receipt is kept out of + # the object header, keeping the result file self-contained. + with h5py.File(simulation.output_dataset.filepath, "a") as stream: + stream.create_dataset( + "policyengine_spm", + data=serialized_spm, + dtype=h5py.string_dtype("utf-8"), + ) def load(self, simulation: Simulation) -> None: """Rehydrate the simulation's output dataset from disk. @@ -363,9 +394,26 @@ def load(self, simulation: Simulation) -> None: are not written back on ``save()``, so they're filesystem approximations, not a true round-trip of the original timestamps. """ - filepath = str( - Path(simulation.dataset.filepath).parent / (simulation.id + ".h5") - ) + filepath = str(output_dataset_filepath(simulation)) + + receipt = None + if self.country_code == "us": + from policyengine.core.spm import SPMProvenance + + with h5py.File(filepath, "r") as stream: + raw = ( + stream["policyengine_spm"].asstr()[()] + if "policyengine_spm" in stream + else None + ) + if raw is None: + raise ValueError( + "Saved US simulation has no SPM configuration or receipt" + ) + recorded = json.loads(raw) + if recorded["config"] != simulation.spm_config: + raise ValueError("Saved US simulation uses different SPM settings") + receipt = SPMProvenance.model_validate(recorded["provenance"]) simulation.output_dataset = self._dataset_class( id=simulation.id, @@ -375,6 +423,12 @@ def load(self, simulation: Simulation) -> None: year=simulation.dataset.year, is_output_dataset=True, ) + if self.country_code == "us": + from policyengine.core.spm import SPMSelection + + simulation.spm_receipt = receipt + simulation.spm = SPMSelection.model_validate(recorded["config"]) + simulation.output_dataset.metadata["spm_config"] = recorded["config"] if os.path.exists(filepath): simulation.created_at = datetime.datetime.fromtimestamp( diff --git a/src/policyengine/tax_benefit_models/common/result.py b/src/policyengine/tax_benefit_models/common/result.py index e73fa406..78b5c221 100644 --- a/src/policyengine/tax_benefit_models/common/result.py +++ b/src/policyengine/tax_benefit_models/common/result.py @@ -63,8 +63,8 @@ def to_dict(self) -> dict[str, Any]: """Return a plain ``dict[str, Any]`` copy suitable for JSON dumps.""" def _convert(value: Any) -> Any: - if isinstance(value, EntityResult): - return dict(value) + if isinstance(value, dict): + return {key: _convert(item) for key, item in value.items()} if isinstance(value, list): return [_convert(v) for v in value] return value diff --git a/src/policyengine/tax_benefit_models/us/__init__.py b/src/policyengine/tax_benefit_models/us/__init__.py index d98ba089..bfd62147 100644 --- a/src/policyengine/tax_benefit_models/us/__init__.py +++ b/src/policyengine/tax_benefit_models/us/__init__.py @@ -10,6 +10,7 @@ result = pe.us.calculate_household( people=[{"age": 35, "employment_income": 60000}], tax_unit={"filing_status": "SINGLE"}, + household={"state_code": "NY", "county_fips": "36061"}, year=2026, ) print(result.tax_unit.income_tax) @@ -18,6 +19,7 @@ reformed = pe.us.calculate_household( people=[{"age": 35, "employment_income": 60000}], tax_unit={"filing_status": "SINGLE"}, + household={"state_code": "NY", "county_fips": "36061"}, year=2026, reform={"gov.irs.credits.ctc.amount.adult_dependent": 1000}, extra_variables=["adjusted_gross_income"], @@ -52,6 +54,7 @@ managed_microsimulation, us_latest, ) + from .spm import SPMProvenance, SPMSelection model = us_latest """The pinned US ``TaxBenefitModelVersion`` for this policyengine release.""" @@ -78,6 +81,8 @@ "model", "us_latest", "calculate_household", + "SPMSelection", + "SPMProvenance", "economic_impact_analysis", "calculate_budgetary_impact", "BudgetaryImpact", diff --git a/src/policyengine/tax_benefit_models/us/datasets.py b/src/policyengine/tax_benefit_models/us/datasets.py index 7032ea61..2ff4aa47 100644 --- a/src/policyengine/tax_benefit_models/us/datasets.py +++ b/src/policyengine/tax_benefit_models/us/datasets.py @@ -106,18 +106,17 @@ def load(self) -> None: return with pd.HDFStore(filepath, mode="r") as store: - self.data = USYearData( - person=MicroDataFrame(store["person"], weights="person_weight"), - marital_unit=MicroDataFrame( - store["marital_unit"], weights="marital_unit_weight" - ), - family=MicroDataFrame(store["family"], weights="family_weight"), - spm_unit=MicroDataFrame(store["spm_unit"], weights="spm_unit_weight"), - tax_unit=MicroDataFrame(store["tax_unit"], weights="tax_unit_weight"), - household=MicroDataFrame( - store["household"], weights="household_weight" - ), - ) + data = {entity: store[entity] for entity in US_ENTITY_KEYS} + # Native producer files store calibrated household weights. Project + # missing weights through membership IDs on these in-memory copies, + # before constructing MicroDataFrames; the source HDF remains read-only. + _assign_missing_entity_weights(data) + self.data = USYearData( + **{ + entity: MicroDataFrame(frame, weights=f"{entity}_weight") + for entity, frame in data.items() + } + ) def __repr__(self) -> str: if self.data is None: @@ -201,45 +200,88 @@ def _core_h5_variable_entities() -> dict[str, str]: return {name: variable.entity.key for name, variable in system.variables.items()} +def _validate_entity_ids(data: dict[str, pd.DataFrame]) -> None: + """Reject ambiguous row identities before joining inputs or weights.""" + for entity, frame in data.items(): + entity_id = US_ENTITY_ID_COLUMNS[entity] + if entity_id not in frame: + raise ValueError(f"Missing {entity_id} for native entity alignment") + if frame[entity_id].isna().any() or frame[entity_id].duplicated().any(): + raise ValueError( + f"Native entity alignment requires unique nonmissing {entity_id} values" + ) + + def _assign_missing_entity_weights(data: dict[str, pd.DataFrame]) -> None: + """Project missing weights from calibrated households through native IDs. + + Both supported HDF layouts use this mapping. Existing weight columns are + retained; group weights are one household weight, never sums of person + weights. Missing or ambiguous links cannot silently produce unweighted rows. + """ + _validate_entity_ids(data) + missing = [ + entity for entity in US_ENTITY_KEYS if f"{entity}_weight" not in data[entity] + ] + if not missing: + return household = data["household"] if "household_id" not in household or "household_weight" not in household: - return - - household_weights = household[["household_id", "household_weight"]] - person = data["person"] + raise ValueError( + "Missing household_id or household_weight for deriving entity weights" + ) if ( - "person_weight" not in person - and "person_household_id" in person - and len(person) > 0 + household["household_id"].isna().any() + or household["household_id"].duplicated().any() ): - person.loc[:, "person_weight"] = person["person_household_id"].map( - household_weights.set_index("household_id")["household_weight"] + raise ValueError( + "Deriving entity weights requires unique nonmissing household_id values" + ) + if household["household_weight"].isna().any(): + raise ValueError( + "Cannot derive entity weights from missing household_weight values" ) - for entity, person_entity_id in US_PERSON_ENTITY_ID_COLUMNS.items(): - if entity == "household": - continue - entity_id = US_ENTITY_ID_COLUMNS[entity] - entity_weight = US_ENTITY_WEIGHT_COLUMNS[entity] - if ( - entity_weight in data[entity] - or entity_id not in data[entity] - or person_entity_id not in person - or "person_household_id" not in person - ): - continue + household_weights = household.set_index("household_id")["household_weight"] + person = data["person"] - entity_households = person[ - [person_entity_id, "person_household_id"] - ].drop_duplicates(subset=[person_entity_id]) - weight_lookup = entity_households.merge( - household_weights, - left_on="person_household_id", - right_on="household_id", - how="left", - ).set_index(person_entity_id)["household_weight"] - data[entity].loc[:, entity_weight] = data[entity][entity_id].map(weight_lookup) + def person_link(entity: str) -> str: + native = US_PERSON_ENTITY_ID_COLUMNS[entity] + alias = US_ENTITY_ID_COLUMNS[entity] + if native in person: + return native + if alias in person: + return alias + raise ValueError(f"Missing person {native} link for deriving {entity} weights") + + for entity in missing: + frame = data[entity] + weight = US_ENTITY_WEIGHT_COLUMNS[entity] + if frame.empty: + frame[weight] = pd.Series(index=frame.index, dtype=household_weights.dtype) + continue + household_link = person_link("household") + if entity == "person": + weights = person[household_link].map(household_weights) + else: + entity_id = US_ENTITY_ID_COLUMNS[entity] + if entity_id not in frame: + raise ValueError(f"Missing {entity_id} for deriving {weight}") + entity_link = person_link(entity) + relationships = person[[entity_link, household_link]].drop_duplicates() + if relationships[entity_link].duplicated().any(): + raise ValueError( + f"Cannot derive {weight}: {entity_id} spans multiple households" + ) + weight_lookup = relationships.set_index(entity_link)[household_link].map( + household_weights + ) + weights = frame[entity_id].map(weight_lookup) + if weights.isna().any(): + raise ValueError( + f"Cannot derive {weight}: missing or unknown native household/entity links" + ) + frame[weight] = weights def _load_policyengine_core_h5(path: Path, year: int) -> USYearData: @@ -298,6 +340,8 @@ def create_datasets( """ from policyengine_us import Microsimulation + from policyengine.tax_benefit_models.us.spm import resolve_spm_selection + dataset_requests: list[Optional[str]] = datasets or [None] result = {} for dataset in dataset_requests: @@ -308,7 +352,7 @@ def create_datasets( data_dir=Path(data_folder), ) dataset_stem = source.name - sim = Microsimulation(dataset=source.path) + sim = Microsimulation(dataset=source.path, spm=resolve_spm_selection()) for year in years: # Get all input variables from the simulation diff --git a/src/policyengine/tax_benefit_models/us/household.py b/src/policyengine/tax_benefit_models/us/household.py index 5122bb15..9801214b 100644 --- a/src/policyengine/tax_benefit_models/us/household.py +++ b/src/policyengine/tax_benefit_models/us/household.py @@ -15,7 +15,7 @@ {"age": 6, "is_tax_unit_dependent": True}, ], tax_unit={"filing_status": "HEAD_OF_HOUSEHOLD"}, - household={"state_code": "NY"}, + household={"state_code": "NY", "county_fips": "36061"}, year=2026, extra_variables=["adjusted_gross_income"], ) @@ -29,7 +29,7 @@ {"age": 6, "is_tax_unit_dependent": True}, ], tax_unit={"filing_status": "HEAD_OF_HOUSEHOLD"}, - household={"state_code": "NY"}, + household={"state_code": "NY", "county_fips": "36061"}, year=2026, reform={"gov.usda.snap.income.deductions.earned_income": 0}, ) @@ -52,6 +52,7 @@ from policyengine.utils.household_validation import validate_household_input from .model import us_latest +from .spm import SPMSelection, calculation_provenance, resolve_spm_selection _GROUP_ENTITIES = ("marital_unit", "family", "spm_unit", "tax_unit", "household") @@ -68,7 +69,7 @@ def _raise_unexpected_kwargs(unexpected: Mapping[str, Any]) -> None: lines.append(f" - '{name}'{hint}") lines.append( "Valid kwargs: people, marital_unit, family, spm_unit, tax_unit, " - "household, year, reform, extra_variables, axes." + "household, year, reform, extra_variables, axes, spm." ) raise TypeError("\n".join(lines)) @@ -142,6 +143,7 @@ def _group(spec: Mapping[str, Any]) -> dict[str, Any]: "reform", "extra_variables", "axes", + "spm", } ) @@ -158,6 +160,7 @@ def calculate_household( reform: Optional[Mapping[str, Any]] = None, extra_variables: Optional[list[str]] = None, axes: Optional[list[Any]] = None, + spm: Optional[SPMSelection] = None, **unexpected: Any, ) -> HouseholdResult: """Compute tax and benefit variables for a single US household. @@ -184,6 +187,27 @@ def calculate_household( shape or a flat list of axis dictionaries. Missing ``period`` values default to ``year``. When axes are present, result values are lists ordered by the axis grid instead of scalars. + spm: SPMSelection or mapping selecting a scenario and geography from + the bundle's independently pinned artifact. Geography is demanded + only by the results that actually use the measurement. SPM + measurement itself — thresholds, the geographic factor and SPM + poverty — always requires household county_fips or an explicit + metro/national selection, and so do the default outputs, which + include SPM poverty. The ordinary resource outputs take the + household's housing assistance amount rather than the capped SPM + subsidy, so they compute on state alone whether or not the unit is + allocated assistance. The country's cap + (``spm_unit_capped_housing_subsidy``) is what consults the + canonical housing portion, and only for units allocated + assistance, so for an assisted unit without a geography the cap + and everything downstream of it — ``spm_unit_benefits``, + ``spm_unit_net_income`` and in turn + ``spm_unit_oecd_equiv_net_income`` and + ``spm_unit_income_decile`` — raise ``SPM_GEOGRAPHY_REQUIRED``; + a unit allocated none has a capped subsidy of zero by + construction. + Formula-owned SPM amounts and measurement counts cannot be + supplied as inputs/axes. Returns: :class:`HouseholdResult` with dot-accessible per-entity @@ -218,6 +242,11 @@ def calculate_household( ) from policyengine_us import Simulation + from spm_calculator.policyengine_adapter import validate_policyengine_inputs + + validate_policyengine_inputs( + {"person": people, **{name: [value] for name, value in entities.items()}} + ) validate_household_input( model_version=us_latest, @@ -234,7 +263,16 @@ def calculate_household( output_columns = _default_output_columns(extra_by_entity) reform_dict = compile_reform(reform, year=year, model_version=us_latest) normalized_axes = normalize_axes(axes=axes, year=year, model_version=us_latest) + if normalized_axes is not None: + validate_policyengine_inputs( + { + "axes": [ + {axis["name"]: None} for group in normalized_axes for axis in group + ] + } + ) axes_active = normalized_axes is not None + spm_config = resolve_spm_selection(spm) simulation = Simulation( situation=_build_situation( @@ -248,6 +286,7 @@ def calculate_household( axes=normalized_axes, ), reform=reform_dict, + spm=spm_config, ) result = HouseholdResult() @@ -282,4 +321,8 @@ def calculate_household( for variable in columns } ) + result["provenance"] = { + "spm_config": dict(simulation.spm_config), + "spm": calculation_provenance(simulation), + } return result diff --git a/src/policyengine/tax_benefit_models/us/model.py b/src/policyengine/tax_benefit_models/us/model.py index c517855f..51338b3e 100644 --- a/src/policyengine/tax_benefit_models/us/model.py +++ b/src/policyengine/tax_benefit_models/us/model.py @@ -16,7 +16,13 @@ output_dataset_filepath as _output_dataset_filepath, ) -from .datasets import PolicyEngineUSDataset, USYearData +from .datasets import PolicyEngineUSDataset, USYearData, _validate_entity_ids +from .spm import ( + SPMProvenance, + SPMSelection, + calculation_provenance, + resolve_spm_selection, +) if TYPE_CHECKING: from policyengine.core.simulation import Simulation @@ -157,6 +163,12 @@ def run(self, simulation: "Simulation") -> "Simulation": if dataset.data is None: dataset.load() + # Validate every entity before constructing either the reform or its + # baseline, so no set_input can suppress a canonical SPM formula. + from spm_calculator.policyengine_adapter import validate_policyengine_inputs + + validate_policyengine_inputs(dataset.data.entity_data) + # Apply regional scoping if specified if simulation.scoping_strategy: scoped_data = simulation.scoping_strategy.apply( @@ -190,7 +202,17 @@ def run(self, simulation: "Simulation") -> "Simulation": dynamic_reform = build_reform_dict(simulation.dynamic) reform_dict = merge_reform_dicts(policy_reform, dynamic_reform) - microsim = Microsimulation(reform=reform_dict) + class InputMicrosimulation(Microsimulation): + # The caller already supplied the entire dataset. The country + # default would load another population before we build this one. + default_dataset = None + + microsim = InputMicrosimulation( + situation={}, + reform=reform_dict, + spm=simulation.spm_config, + default_input_period=dataset.year, + ) # Use ``microsim.tax_benefit_system``, not the module-level # ``system``: ``Microsimulation.__init__`` applies structural # reforms (e.g. ``gov.contrib.ctc.*``) to its per-sim system but @@ -248,22 +270,33 @@ def run(self, simulation: "Simulation") -> "Simulation": # Person entity also needs person-level group ID columns so that # downstream joins (e.g. person->tax_unit) work. person_input_df = pd.DataFrame(dataset.data.person) - for col in person_input_df.columns: - if col.startswith("person_") and col.endswith("_id"): - target_col = col.replace("person_", "") - if target_col in id_columns: - data["person"][target_col] = person_input_df[col].values + for entity in US_GROUP_ENTITIES: + target_col = f"{entity}_id" + native_col = f"person_{target_col}" + source_col = ( + native_col if native_col in person_input_df.columns else target_col + ) + if source_col in person_input_df.columns: + data["person"][target_col] = person_input_df[source_col].values # Calculate non-ID, non-weight variables from simulation. # ``resolve_entity_variables`` merges bundled defaults with # caller-supplied ``simulation.extra_variables``; unknown # entity keys or variable names raise with close-match hints. for entity, variables in self.resolve_entity_variables(simulation).items(): + # Country populations use their own ID order. Restore each native + # table's order before attaching values to its IDs and weights. + input_ids = pd.DataFrame(getattr(dataset.data, entity))[f"{entity}_id"] + output_order = pd.Index(microsim.populations[entity].ids).get_indexer( + input_ids + ) + if (output_order < 0).any(): + raise ValueError(f"Missing {entity}_id in simulation output") for var in variables: if var not in id_columns and var not in weight_columns: data[entity][var] = microsim.calculate( var, period=simulation.dataset.year, map_to=entity - ).values + ).values[output_order] data["person"] = MicroDataFrame(data["person"], weights="person_weight") data["marital_unit"] = MicroDataFrame( @@ -283,6 +316,7 @@ def run(self, simulation: "Simulation") -> "Simulation": filepath=str(_output_dataset_filepath(simulation)), year=simulation.dataset.year, is_output_dataset=True, + metadata={"spm_config": dict(microsim.spm_config)}, data=USYearData( person=data["person"], marital_unit=data["marital_unit"], @@ -292,6 +326,10 @@ def run(self, simulation: "Simulation") -> "Simulation": household=data["household"], ), ) + simulation.spm_receipt = SPMProvenance.model_validate( + calculation_provenance(microsim) + ) + simulation.spm = SPMSelection.model_validate(microsim.spm_config) def _build_simulation_from_dataset(self, microsim, dataset, system): """Build a PolicyEngine Core simulation from dataset entity IDs. @@ -304,6 +342,10 @@ def _build_simulation_from_dataset(self, microsim, dataset, system): from policyengine_core.simulations.simulation_builder import ( SimulationBuilder, ) + from spm_calculator.policyengine_adapter import validate_policyengine_inputs + + validate_policyengine_inputs(dataset.data.entity_data) + _validate_entity_ids(dataset.data.entity_data) builder = SimulationBuilder() builder.populations = system.instantiate_entities() @@ -336,6 +378,24 @@ def _build_simulation_from_dataset(self, microsim, dataset, system): else "tax_unit_id" ) + # The builder requires membership IDs and declared population IDs to + # describe the same groups. Check explicitly before its positional + # membership mapping can silently attach a person to a different group. + for entity, link in { + "household": household_id_col, + "marital_unit": marital_unit_id_col, + "family": family_id_col, + "spm_unit": spm_unit_id_col, + "tax_unit": tax_unit_id_col, + }.items(): + if link not in person_data or person_data[link].isna().any(): + raise ValueError(f"Missing nonnull person {link} membership") + entity_ids = pd.DataFrame(getattr(dataset.data, entity))[f"{entity}_id"] + if set(entity_ids) != set(person_data[link]): + raise ValueError( + f"Native {entity}_id values must match person {link} memberships" + ) + builder.declare_person_entity("person", person_data["person_id"].values) builder.declare_entity( "household", np.unique(person_data[household_id_col].values) @@ -401,7 +461,8 @@ def _build_simulation_from_dataset(self, microsim, dataset, system): ("tax_unit", dataset.data.tax_unit), ("marital_unit", dataset.data.marital_unit), ]: - df = pd.DataFrame(entity_df) + df = pd.DataFrame(entity_df).set_index(f"{entity_name}_id", drop=False) + df = df.loc[microsim.populations[entity_name].ids] for column in df.columns: if column not in id_columns and column in system.variables: microsim.set_input(column, dataset.year, df[column].values) @@ -411,6 +472,7 @@ def managed_microsimulation( *, dataset: Optional[str] = None, allow_unmanaged: bool = False, + spm: Optional[SPMSelection] = None, **kwargs, ): """Construct a country-package Microsimulation pinned to this bundle. @@ -428,12 +490,13 @@ def managed_microsimulation( "**kwargs, so policyengine.py can enforce the release bundle." ) + selection = resolve_spm_selection(spm) source = materialize_dataset( "us", dataset, allow_unmanaged=allow_unmanaged, ) - microsim = Microsimulation(dataset=source.path, **kwargs) + microsim = Microsimulation(dataset=source.path, spm=selection, **kwargs) microsim.policyengine_bundle = dict(us_latest.release_bundle) microsim.policyengine_bundle.update( build_runtime_dataset_provenance( @@ -442,6 +505,7 @@ def managed_microsimulation( source.bundle_dataset, ) ) + microsim.policyengine_bundle["spm"] = dict(microsim.spm_config) return microsim diff --git a/src/policyengine/tax_benefit_models/us/spm.py b/src/policyengine/tax_benefit_models/us/spm.py new file mode 100644 index 00000000..568fa58f --- /dev/null +++ b/src/policyengine/tax_benefit_models/us/spm.py @@ -0,0 +1,49 @@ +"""Typed SPM selection and receipts for the certified US model bundle.""" + +from policyengine.core.spm import SPMProvenance, SPMSelection + +__all__ = [ + "SPMSelection", + "SPMProvenance", + "resolve_spm_selection", + "calculation_provenance", +] + + +def resolve_spm_selection(selection=None) -> dict: + """Resolve public options against an independently pinned bundle artifact.""" + from policyengine.bundle import get_current_bundle + + configured = get_current_bundle().get("measurements", {}).get("spm") + if not isinstance(configured, dict): + raise ValueError( + "The current bundle has no certified SPM measurement configuration" + ) + defaults = SPMSelection.model_validate(configured) + if defaults.forecast_content_sha256 is None or defaults.scenario is None: + raise ValueError( + "The bundle must pin the SPM artifact hash and default scenario" + ) + chosen = SPMSelection.model_validate({} if selection is None else selection) + if chosen.forecast_content_sha256 not in (None, defaults.forecast_content_sha256): + raise ValueError("SPM selection does not match this bundle's artifact hash") + # Serialization intentionally preserves omissions in a partial selection. + # Materialize the validated bundle defaults before applying that selection, + # so the returned config freezes every setting for later replay. + values = {name: getattr(defaults, name) for name in SPMSelection.model_fields} + values.update(chosen.model_dump(exclude_unset=True)) + if ( + chosen.geography_kind != defaults.geography_kind + and "geography_kind" in chosen.model_fields_set + ): + values["geography_id"] = chosen.geography_id + # Explicit null must not remove an independently expected artifact identity. + values["forecast_content_sha256"] = defaults.forecast_content_sha256 + values["scenario"] = chosen.scenario or defaults.scenario + return SPMSelection.model_validate(values).model_dump() + + +def calculation_provenance(simulation) -> dict: + return SPMProvenance.model_validate(simulation.spm_provenance()).model_dump( + mode="json" + ) diff --git a/tests/fixtures/household_calculator_snapshots/us_married_two_kids_high_income.json b/tests/fixtures/household_calculator_snapshots/us_married_two_kids_high_income.json index e92b7541..a3c2746f 100644 --- a/tests/fixtures/household_calculator_snapshots/us_married_two_kids_high_income.json +++ b/tests/fixtures/household_calculator_snapshots/us_married_two_kids_high_income.json @@ -2,13 +2,13 @@ "family.family_id": 0.0, "family.family_weight": 0.0, "household.congressional_district_geoid": 0.0, - "household.household_benefits": 1130.96, + "household.household_benefits": 1142.24, "household.household_count_people": 4.0, "household.household_id": 0.0, "household.household_income_decile": 10.0, "household.household_market_income": 240000.0, - "household.household_net_income": 176196.89, - "household.household_tax": 64934.07, + "household.household_net_income": 176232.22, + "household.household_tax": 64910.02, "household.household_weight": 1.0, "marital_unit.marital_unit_id": 0.0, "marital_unit.marital_unit_weight": 1.0, @@ -88,15 +88,15 @@ "spm_unit.spm_unit_id": 0.0, "spm_unit.spm_unit_is_in_deep_spm_poverty": 0.0, "spm_unit.spm_unit_is_in_spm_poverty": 0.0, - "spm_unit.spm_unit_net_income": 147995.31, + "spm_unit.spm_unit_net_income": 148030.66, "spm_unit.spm_unit_weight": 1.0, "spm_unit.tanf": 0.0, "tax_unit.ctc": 4400.0, "tax_unit.eitc": 0.0, "tax_unit.employee_payroll_tax": 21480.0, - "tax_unit.household_state_income_tax": 12690.07, + "tax_unit.household_state_income_tax": 12666.02, "tax_unit.income_tax": 30740.0, - "tax_unit.state_income_tax": 12690.07, + "tax_unit.state_income_tax": 12666.02, "tax_unit.tax_unit_id": 0.0, "tax_unit.tax_unit_weight": 1.0 } diff --git a/tests/fixtures/household_calculator_snapshots/us_model_surface.json b/tests/fixtures/household_calculator_snapshots/us_model_surface.json index c5563c26..cf8749e7 100644 --- a/tests/fixtures/household_calculator_snapshots/us_model_surface.json +++ b/tests/fixtures/household_calculator_snapshots/us_model_surface.json @@ -1,11 +1,11 @@ { "country_id": "us", - "data_package_name": "populace-data", + "data_package_name": "microcosm-data", "has_employment_income": true, "has_income_tax": true, "has_region_registry": true, "model_package_name": "policyengine-us", - "num_parameters_bucketed_100s": 978, - "num_variables_bucketed_100s": 57, + "num_parameters_bucketed_100s": 1025, + "num_variables_bucketed_100s": 61, "region_registry_country": "us" } diff --git a/tests/fixtures/household_calculator_snapshots/us_single_adult_employment_income.json b/tests/fixtures/household_calculator_snapshots/us_single_adult_employment_income.json index 12373778..e3a77657 100644 --- a/tests/fixtures/household_calculator_snapshots/us_single_adult_employment_income.json +++ b/tests/fixtures/household_calculator_snapshots/us_single_adult_employment_income.json @@ -7,8 +7,8 @@ "household.household_id": 0.0, "household.household_income_decile": 10.0, "household.household_market_income": 60000.0, - "household.household_net_income": 48001.14, - "household.household_tax": 11998.86, + "household.household_net_income": 48008.9, + "household.household_tax": 11991.1, "household.household_weight": 1.0, "marital_unit.marital_unit_id": 0.0, "marital_unit.marital_unit_weight": 1.0, @@ -34,15 +34,15 @@ "spm_unit.spm_unit_id": 0.0, "spm_unit.spm_unit_is_in_deep_spm_poverty": 0.0, "spm_unit.spm_unit_is_in_spm_poverty": 0.0, - "spm_unit.spm_unit_net_income": 42025.14, + "spm_unit.spm_unit_net_income": 42032.9, "spm_unit.spm_unit_weight": 1.0, "spm_unit.tanf": 0.0, "tax_unit.ctc": 0.0, "tax_unit.eitc": 0.0, "tax_unit.employee_payroll_tax": 5370.0, - "tax_unit.household_state_income_tax": 1602.86, + "tax_unit.household_state_income_tax": 1595.1, "tax_unit.income_tax": 5020.0, - "tax_unit.state_income_tax": 1602.86, + "tax_unit.state_income_tax": 1595.1, "tax_unit.tax_unit_id": 0.0, "tax_unit.tax_unit_weight": 1.0 } diff --git a/tests/fixtures/household_calculator_snapshots/us_single_adult_no_income.json b/tests/fixtures/household_calculator_snapshots/us_single_adult_no_income.json index 0f05357a..8147e37e 100644 --- a/tests/fixtures/household_calculator_snapshots/us_single_adult_no_income.json +++ b/tests/fixtures/household_calculator_snapshots/us_single_adult_no_income.json @@ -2,12 +2,12 @@ "family.family_id": 0.0, "family.family_weight": 0.0, "household.congressional_district_geoid": 0.0, - "household.household_benefits": 3596.04, + "household.household_benefits": 298.0, "household.household_count_people": 1.0, "household.household_id": 0.0, "household.household_income_decile": 10.0, "household.household_market_income": 0.0, - "household.household_net_income": 3595.04, + "household.household_net_income": 297.0, "household.household_tax": 1.0, "household.household_weight": 1.0, "marital_unit.marital_unit_id": 0.0, @@ -30,11 +30,11 @@ "person[0].ssi": 0.0, "person[0].tax_unit_id": 0.0, "person[0].unemployment_compensation": 0.0, - "spm_unit.snap": 3596.04, + "spm_unit.snap": 298.0, "spm_unit.spm_unit_id": 0.0, "spm_unit.spm_unit_is_in_deep_spm_poverty": 1.0, "spm_unit.spm_unit_is_in_spm_poverty": 1.0, - "spm_unit.spm_unit_net_income": 3595.04, + "spm_unit.spm_unit_net_income": 297.0, "spm_unit.spm_unit_weight": 1.0, "spm_unit.tanf": 0.0, "tax_unit.ctc": 0.0, diff --git a/tests/fixtures/household_calculator_snapshots/us_single_parent_one_child.json b/tests/fixtures/household_calculator_snapshots/us_single_parent_one_child.json index 29254dbe..1160dde0 100644 --- a/tests/fixtures/household_calculator_snapshots/us_single_parent_one_child.json +++ b/tests/fixtures/household_calculator_snapshots/us_single_parent_one_child.json @@ -2,12 +2,12 @@ "family.family_id": 0.0, "family.family_weight": 0.0, "household.congressional_district_geoid": 0.0, - "household.household_benefits": 1130.96, + "household.household_benefits": 1142.24, "household.household_count_people": 2.0, "household.household_id": 0.0, "household.household_income_decile": 10.0, "household.household_market_income": 40000.0, - "household.household_net_income": 40014.58, + "household.household_net_income": 40025.86, "household.household_tax": 1116.38, "household.household_weight": 1.0, "marital_unit.marital_unit_id": 0.0, @@ -52,7 +52,7 @@ "spm_unit.spm_unit_id": 0.0, "spm_unit.spm_unit_is_in_deep_spm_poverty": 0.0, "spm_unit.spm_unit_is_in_spm_poverty": 0.0, - "spm_unit.spm_unit_net_income": 37586.66, + "spm_unit.spm_unit_net_income": 37597.94, "spm_unit.spm_unit_weight": 1.0, "spm_unit.tanf": 0.0, "tax_unit.ctc": 2200.0, diff --git a/tests/fixtures/release_tros/README.md b/tests/fixtures/release_tros/README.md new file mode 100644 index 00000000..f64ee4bb --- /dev/null +++ b/tests/fixtures/release_tros/README.md @@ -0,0 +1,23 @@ +# Historical public TRACE inputs + +These are existing published inputs used to exercise the real repository TRO +generator. They do not certify a new canonical SPM release. + +- `manifest.json` and `us.trace.tro.jsonld`: copied byte for byte from + PolicyEngine/policyengine.py commit + `3c3b4f6442f4a5adc47274734d71a6ca10103b43`, under + `src/policyengine/data/bundle/`. +- `us-release_manifest.json`: published Populace release + `populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z`, at + `https://huggingface.co/datasets/policyengine/populace-us/resolve/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/release_manifest.json`. + Its raw SHA256 is + `dd949ba3c4c7a56aff8442c6db5a031d2c84c3da59f5d14832f468c358604506`, + matching the reviewed sidecar's data-release-manifest artifact. +- `country-models.lock.toml`: the complete, unchanged `policyengine-us` and + `policyengine-uk` package blocks extracted from `uv.lock` at the same wrapper + commit. These bind the certified model names, versions, wheel URLs and hashes + to the actual reviewed installation artifacts. This excerpt is used only by + the TRO tests; the separate lock tests resolve a complete project with uv. + +The UK private data manifest is not vendored here. Release CI authenticates it +from the pinned Hugging Face revision using the repository's existing secret. diff --git a/tests/fixtures/release_tros/country-models.lock.toml b/tests/fixtures/release_tros/country-models.lock.toml new file mode 100644 index 00000000..e3936a3c --- /dev/null +++ b/tests/fixtures/release_tros/country-models.lock.toml @@ -0,0 +1,36 @@ +[[package]] +name = "policyengine-uk" +version = "2.90.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "microdf-python" }, + { name = "policyengine-core" }, + { name = "pydantic" }, + { name = "tables", version = "3.9.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "tables", version = "3.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "tables", version = "3.11.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/e1/6652fd4ff8806d81b7c846ce2baeef4b9a8457ed380bb0853419d3cf36d9/policyengine_uk-2.90.2.tar.gz", hash = "sha256:c9baeaded1bb6d6f5ca5fcd962b80a198509513737c194aa867b0ef6d13d6a09", size = 1237478, upload-time = "2026-08-13T05:30:05.961Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/da/658bff2df6094bd7bf2b0f561ff60fd41508adbd0a3af29b4553c83bc678/policyengine_uk-2.90.2-py3-none-any.whl", hash = "sha256:f100ef97e20b29b84138078f1dbc5b83f8857e6bc74b745fc6abef7ea1eeadf2", size = 2034933, upload-time = "2026-08-13T05:30:03.884Z" }, +] + +[[package]] +name = "policyengine-us" +version = "1.764.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "microdf-python" }, + { name = "pandas" }, + { name = "policyengine-core" }, + { name = "spm-calculator" }, + { name = "tables", version = "3.9.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "tables", version = "3.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "tables", version = "3.11.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/cc/9e65a9586069beab1f02102a23f7a0e6332f1a9c2d590cb9f0e68f9ac987/policyengine_us-1.764.6.tar.gz", hash = "sha256:9817d48abc5b7d690b8abe33047005e5cf40b71654a85a2534bb5e3bcf921f79", size = 11009555, upload-time = "2026-07-06T13:13:07.254Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/5a/0566666f30ad06e416f0cf25b24bdee73acd71897fb2de3c0ac43b59e3e3/policyengine_us-1.764.6-py3-none-any.whl", hash = "sha256:2b2c5c02c26ab212270948db1f30129728b625fc6109fc9ca0c604b714546909", size = 13117921, upload-time = "2026-07-06T13:13:03.476Z" }, +] + diff --git a/tests/fixtures/release_tros/manifest.json b/tests/fixtures/release_tros/manifest.json new file mode 100644 index 00000000..335d0b02 --- /dev/null +++ b/tests/fixtures/release_tros/manifest.json @@ -0,0 +1,288 @@ +{ + "bundle_version": "5.3.0", + "citation": { + "publisher": "PolicyEngine", + "title": "PolicyEngine bundle 5.3.0", + "type": "software-bundle", + "version": "5.3.0" + }, + "countries": { + "uk": { + "model_package": "policyengine-uk" + }, + "us": { + "model_package": "policyengine-us" + } + }, + "data_releases": { + "uk": { + "build_id": "policyengine-uk-data-1.56.16", + "bundle_id": "uk-5.3.0", + "certification": { + "built_with_model_git_sha": "12a1e028afeef08d8b2d74ee03fd9de3a78b2dd3", + "built_with_model_version": "2.89.2", + "certified_by": "policyengine.py bundle certification", + "certified_for_model_version": "2.90.2", + "compatibility_basis": "legacy_compatible_model_package", + "data_build_fingerprint": "sha256:6bfff835b504094c05342d2b4d770d2d94c8c1adcb15c4468931f53b21a89ea9", + "data_build_id": "policyengine-uk-data-1.56.16" + }, + "certified_data_artifact": { + "build_id": "policyengine-uk-data-1.56.16", + "data_package": { + "name": "policyengine-uk-data", + "version": "1.56.16" + }, + "dataset": "enhanced_frs_2024_25", + "sha256": "e433e532b17bd8ce76030156285816e33d44e93edabd2204adbef71d19a68712", + "uri": "hf://policyengine/policyengine-uk-data-private/enhanced_frs_2024_25.h5@1.56.16" + }, + "country_id": "uk", + "data_package": { + "name": "policyengine-uk-data", + "release_manifest_path": "releases/1.56.16/release_manifest.json", + "release_manifest_revision": "2966541fca275ab5ccb6cfb659b010b1db37ad49", + "repo_id": "policyengine/policyengine-uk-data-private", + "repo_type": "model", + "version": "1.56.16" + }, + "data_producer": "populace", + "datasets": { + "enhanced_frs_2024_25": { + "path": "enhanced_frs_2024_25.h5", + "repo_id": "policyengine/policyengine-uk-data-private", + "revision": "1.56.16", + "sha256": "e433e532b17bd8ce76030156285816e33d44e93edabd2204adbef71d19a68712" + }, + "enhanced_frs_2024_25_tiny": { + "path": "enhanced_frs_2024_25_tiny.h5", + "repo_id": "policyengine/policyengine-uk-data-private", + "revision": "1.56.16", + "sha256": "6cbbda7eef1b0b16d62e24a3099dc7959906287d659868d31aa7fc82af598871" + }, + "frs_2024_25": { + "path": "frs_2024_25.h5", + "repo_id": "policyengine/policyengine-uk-data-private", + "revision": "1.56.16", + "sha256": "d4d29901d7ea0c1f636060d4177a411a39c1977fa9fd23a61af3bb0f030769bd" + }, + "frs_2024_25_tiny": { + "path": "frs_2024_25_tiny.h5", + "repo_id": "policyengine/policyengine-uk-data-private", + "revision": "1.56.16", + "sha256": "74ca086be959b2942ecbf7755034d94dc6a817f4d9d54f2585a1c6df849d9588" + }, + "local_authority_weights": { + "path": "local_authority_weights.h5", + "repo_id": "policyengine/policyengine-uk-data-private", + "revision": "1.56.16", + "sha256": "6d634d69fb472b80cc17ffe696e028c54d7b739166bb7d9a57860f9d73537aa5" + }, + "local_geography_weights": { + "path": "local_geography_weights.csv.gz", + "repo_id": "policyengine/policyengine-uk-data-private", + "revision": "1.56.16", + "sha256": "6f623e277089d06c2c50ec3d8ab766635f695c8579d360410e3dcb4214ea9709" + }, + "parliamentary_constituency_weights": { + "path": "parliamentary_constituency_weights.h5", + "repo_id": "policyengine/policyengine-uk-data-private", + "revision": "1.56.16", + "sha256": "6870c36072a0c34d2e6ce3ebf58664b7932ca46a9e3808d387988773e63b66d5" + } + }, + "default_dataset": "enhanced_frs_2024_25", + "default_dataset_uri": "hf://policyengine/policyengine-uk-data-private/enhanced_frs_2024_25.h5@1.56.16", + "model_package": { + "name": "policyengine-uk", + "sha256": "f100ef97e20b29b84138078f1dbc5b83f8857e6bc74b745fc6abef7ea1eeadf2", + "version": "2.90.2", + "wheel_url": "https://files.pythonhosted.org/packages/ec/da/658bff2df6094bd7bf2b0f561ff60fd41508adbd0a3af29b4553c83bc678/policyengine_uk-2.90.2-py3-none-any.whl" + }, + "policyengine_version": "5.3.0", + "region_datasets": { + "national": { + "path_template": "enhanced_frs_2024_25.h5" + } + }, + "release_manifest_uri": "https://huggingface.co/policyengine/policyengine-uk-data-private/resolve/2966541fca275ab5ccb6cfb659b010b1db37ad49/releases/1.56.16/release_manifest.json", + "schema_version": 1, + "source_manifest_uri": "hf://model/policyengine/policyengine-uk-data-private@2966541fca275ab5ccb6cfb659b010b1db37ad49/releases/1.56.16/release_manifest.json", + "version": "policyengine-uk-data-1.56.16" + }, + "us": { + "build_id": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "bundle_id": "us-5.3.0", + "certification": { + "built_with_model_version": "1.764.6", + "certified_by": "policyengine.py bundle certification", + "certified_for_model_version": "1.764.6", + "compatibility_basis": "built_with_model_package", + "data_build_id": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z" + }, + "certified_data_artifact": { + "build_id": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "data_package": { + "name": "populace-data", + "version": "0.1.0" + }, + "dataset": "populace_us_2024", + "sha256": "48b9d479fb4fd1c3537f9383ce4697d130b6f618658409d74f6233c43b994c7e", + "uri": "hf://policyengine/populace-us/populace_us_2024.h5@populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z" + }, + "country_id": "us", + "data_package": { + "name": "populace-data", + "release_manifest_path": "releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/release_manifest.json", + "release_manifest_revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "repo_id": "policyengine/populace-us", + "repo_type": "dataset", + "version": "0.1.0" + }, + "data_producer": "populace", + "datasets": { + "calibration_diagnostics": { + "path": "releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/calibration_diagnostics.json", + "repo_id": "policyengine/populace-us", + "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "sha256": "870449b44e86b13b25bcea1a57f0e7af37f4d4db18be815eea3acdf9fe6eb40e" + }, + "demographics": { + "path": "releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/demographics.json", + "repo_id": "policyengine/populace-us", + "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "sha256": "50c39dea345fa4e21f32272304b376979b9572ca17383dd13061b752d9a81112" + }, + "populace_us_2024": { + "path": "populace_us_2024.h5", + "repo_id": "policyengine/populace-us", + "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "sha256": "48b9d479fb4fd1c3537f9383ce4697d130b6f618658409d74f6233c43b994c7e" + }, + "populace_us_2024_calibration": { + "path": "populace_us_2024_calibration.npz", + "repo_id": "policyengine/populace-us", + "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "sha256": "b6e9c0678de5e79e8ddbd7a2a2f17c357bd6b368153cea7b5119c299a280a1b5" + }, + "us_source_coverage": { + "path": "releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/us_source_coverage.json", + "repo_id": "policyengine/populace-us", + "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "sha256": "6406c8686c292015a5bc7265a42402f89935f9395a8b63510efdd4d9562e7ff5" + }, + "us_ssi_take_up": { + "path": "releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/us_ssi_take_up.json", + "repo_id": "policyengine/populace-us", + "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "sha256": "ce631a37e2fe2ed22927fa232d41150d3b6cf328b38d3e5b38018fc4eec1b44d" + } + }, + "default_dataset": "populace_us_2024", + "default_dataset_uri": "hf://policyengine/populace-us/populace_us_2024.h5@populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "model_package": { + "name": "policyengine-us", + "sha256": "2b2c5c02c26ab212270948db1f30129728b625fc6109fc9ca0c604b714546909", + "version": "1.764.6", + "wheel_url": "https://files.pythonhosted.org/packages/e2/5a/0566666f30ad06e416f0cf25b24bdee73acd71897fb2de3c0ac43b59e3e3/policyengine_us-1.764.6-py3-none-any.whl" + }, + "policyengine_version": "5.3.0", + "region_datasets": { + "national": { + "path_template": "populace_us_2024.h5" + } + }, + "release_manifest_uri": "https://huggingface.co/datasets/policyengine/populace-us/resolve/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/release_manifest.json", + "schema_version": 1, + "source_manifest_uri": "hf://dataset/policyengine/populace-us@populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/release_manifest.json", + "version": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z" + } + }, + "dataset_overlays": { + "uk": { + "enhanced_frs_2023_24": { + "data_package_name": "policyengine-uk-data", + "path": "enhanced_frs_2023_24.h5", + "repo_id": "policyengine/policyengine-uk-data-private", + "repo_type": "model", + "revision": "655dd07e4bb9c777b00dac044949611f1feb824f", + "sha256": "584ae33d80ca0431254610a3f8254d132da73477d31966d6446282861ecae50d" + }, + "frs_2023_24": { + "data_package_name": "policyengine-uk-data", + "path": "frs_2023_24.h5", + "repo_id": "policyengine/policyengine-uk-data-private", + "repo_type": "model", + "revision": "655dd07e4bb9c777b00dac044949611f1feb824f", + "sha256": "df26d4d7af9d164aa2d064181b39290292d2f62bb26fee6126fc095fc06da292" + }, + "populace_uk_2023": { + "data_package_name": "populace-data", + "path": "populace_uk_2023.h5", + "repo_id": "policyengine/populace-uk-private", + "repo_type": "dataset", + "revision": "populace-uk-2023-dd68c73-4aa4b14-20260619T023711Z", + "sha256": "f17306ccb2aad7ff0130be3589b560afb2e2a12a943570911cd0c77f07934833" + } + }, + "us": { + "populace_us_2024_acs_local": { + "data_package_name": "populace-data", + "path": "populace_us_2024_acs_local.h5", + "repo_id": "policyengine/populace-us", + "repo_type": "dataset", + "revision": "populace-us-2024-buildo-acs-local-77e2061-20260724T110908Z", + "sha256": "71763290ded993789af0ad818fd833a6aabb9ca7ff7d14f78315340d8617c6f4" + } + } + }, + "extras": { + "models": [ + "policyengine-core", + "policyengine-us", + "policyengine-uk" + ], + "uk": [ + "policyengine-core", + "policyengine-uk" + ], + "us": [ + "policyengine-core", + "policyengine-us" + ] + }, + "packages": { + "policyengine": { + "import_name": "policyengine", + "install_requirement": "policyengine==5.3.0", + "name": "policyengine", + "role": "bundle_carrier", + "version": "5.3.0" + }, + "policyengine-core": { + "import_name": "policyengine_core", + "install_requirement": "policyengine-core==3.30.1", + "name": "policyengine-core", + "role": "runtime_dependency", + "version": "3.30.1" + }, + "policyengine-uk": { + "country": "uk", + "import_name": "policyengine_uk", + "install_requirement": "policyengine-uk==2.90.2", + "name": "policyengine-uk", + "role": "country_model", + "version": "2.90.2" + }, + "policyengine-us": { + "country": "us", + "import_name": "policyengine_us", + "install_requirement": "policyengine-us==1.764.6", + "name": "policyengine-us", + "role": "country_model", + "version": "1.764.6" + } + }, + "policyengine_version": "5.3.0", + "schema_version": 2 +} diff --git a/tests/fixtures/release_tros/us-release_manifest.json b/tests/fixtures/release_tros/us-release_manifest.json new file mode 100644 index 00000000..3a71d3b0 --- /dev/null +++ b/tests/fixtures/release_tros/us-release_manifest.json @@ -0,0 +1,364 @@ +{ + "schema_version": 1, + "data_package": { + "name": "populace-data", + "version": "0.1.0" + }, + "default_datasets": { + "national": "populace_us_2024" + }, + "build": { + "build_id": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "built_at": "2026-07-28T03:56:03.415544+00:00", + "built_with_core_package": { + "name": "policyengine-core", + "version": "3.26.11" + }, + "built_with_model_package": { + "name": "policyengine-us", + "version": "1.764.6" + }, + "timing": { + "target_compilation_seconds": 1132.080194915994, + "calibration_seconds": 1072.4242046249856, + "elapsed_through_calibration_seconds": 2800.574625542009, + "total_build_seconds": 8161.056042167009 + }, + "ledger_artifact": { + "path_name": "consumer_facts_buildn_v9_4.jsonl", + "fact_row_count": 37405, + "facts_sha256": "b3c0835631a446eb96aa84d86f3ee962d15ca356174c7114db52974f1cacc080", + "schema_version": null, + "manifest_sha256": null + }, + "warm_start_calibration": { + "enabled": false + }, + "selection_source": { + "mode": "frozen_support", + "join_key": [ + "source_year", + "source_household_id", + "household_support_channel", + "household_support_clone_index" + ], + "source": { + "kind": "reselection", + "method": "buildm-keogh-carrier-protect-swap", + "issue": "PolicyEngine/populace#434", + "base_h5_sha256": "5d8a31622e6f4772188944385c5a6d4bd0e589f846043e6fd56634c9f6ef7d61", + "parent_selection_identities_sha256": "152baca3afeedfe93fbef49e95978b8f3804eec38c32eb8e5c3a1216a925340d", + "parent_selection": "buildi_rmloss100_selection_source (152baca3)", + "n_add": 9, + "swap_rule": "ADD the 9 keogh_distributions carrier households absent from the parent (13 carriers total in base-m; 4 already selected); REMOVE the 9 lowest-base-weight non-carrier asec-channel households (deterministic tiebreak on the join key). Channel composition preserved. Motivated by the keogh_distribution_neutralization reform-coverage smoke probe: the parent selection predates the keogh restoration and carried ~$0.1M unweighted of the $148.97M weighted source mass." + }, + "n_source": 57240, + "n_base_candidates": 337704, + "n_selected": 57240, + "n_unmapped": 0, + "n_ambiguous": 0 + }, + "default_dataset": { + "method": "dense_no_l0", + "sparse": false, + "n_candidate_households": 57240, + "n_exported_households": 57240, + "epochs": 6000, + "l2_lambda": 0.0, + "final_loss": 0.01768391632283638 + }, + "base_population_scale": { + "passed": true, + "details": { + "measure": "person_weight", + "population": 334200000.0, + "benchmark": 334200000.0, + "relative_error": 0.0, + "max_abs_relative_error": 0.25, + "calibration_mass_policy": "conserve", + "mass_repair": { + "method": "rescale_household_weights_to_census_person_population", + "applied": true, + "reason": "US fiscal refresh rescaled base household weights to the Census 2024 national person-population benchmark before mass='conserve' calibration.", + "initial_population": 62697558.345378846, + "benchmark": 334200000.0, + "factor": 5.330351114456635, + "initial_relative_error": -0.8123950977098179, + "repaired_population": 334200000.0, + "repaired_relative_error": 0.0, + "mass_change": { + "entity": "household", + "old_total": 22988559.15013668, + "new_total": 122537091.8856833, + "declared_factor": 5.330351114456635, + "reason": "US fiscal refresh rescaled base household weights to the Census 2024 national person-population benchmark before mass='conserve' calibration." + } + } + } + }, + "immigration_composition": { + "passed": true, + "details": { + "summary": { + "person_population": 334200000.0, + "ssn_card_type": { + "population": { + "CITIZEN": 308582144.34344304, + "NONE": 13090234.740726216, + "NON_CITIZEN_VALID_EAD": 4011891.027315071, + "OTHER_NON_CITIZEN": 8515729.8885157 + }, + "share": { + "CITIZEN": 0.9233457341216129, + "NONE": 0.03916886517272955, + "NON_CITIZEN_VALID_EAD": 0.012004461482091774, + "OTHER_NON_CITIZEN": 0.025480939223565827 + } + }, + "immigration_status_str": { + "population": { + "CITIZEN": 308582144.34344304, + "CUBAN_HAITIAN_ENTRANT": 518697.3384474666, + "DACA": 398079.4611381175, + "LEGAL_PERMANENT_RESIDENT": 11610844.116245188, + "UNDOCUMENTED": 13090234.740726216 + }, + "share": { + "CITIZEN": 0.9233457341216129, + "CUBAN_HAITIAN_ENTRANT": 0.0015520566680055853, + "DACA": 0.0011911414157334454, + "LEGAL_PERMANENT_RESIDENT": 0.03474220262191857, + "UNDOCUMENTED": 0.03916886517272955 + } + } + }, + "controls": { + "undocumented_workers": 8300000.0, + "undocumented_students": 408000.0, + "undocumented_population_anchor": 11000000.0, + "sources": { + "undocumented_workers": "https://www.pewresearch.org/short-reads/2024/07/22/what-we-know-about-unauthorized-immigrants-living-in-the-us/", + "undocumented_students": "https://www.higheredimmigrationportal.org/research/undocumented-students-in-higher-education-updated-march-2021/", + "undocumented_population_anchor": "https://www.pewresearch.org/short-reads/2024/07/22/what-we-know-about-unauthorized-immigrants-living-in-the-us/" + } + }, + "non_citizen_share_band": [ + 0.03, + 0.12 + ], + "undocumented_anchor_relative_band": [ + 0.5, + 1.6 + ] + } + }, + "ecps_parity": { + "passed": true, + "details": { + "reference_populated_layers": 158, + "gaps": 0, + "exempted": [ + "employer_sponsored_insurance_premiums", + "financial_assistance", + "is_unmarried_partner_of_household_head", + "is_wic_at_nutritional_risk", + "survivor_benefits", + "takes_up_dc_ptc", + "takes_up_early_head_start_if_eligible" + ], + "stale_exemptions": [], + "dormant_exemptions": [], + "reference": { + "repo_id": "policyengine/policyengine-us-data", + "repo_type": "model", + "filename": "enhanced_cps_2024.h5", + "revision": "21280dca5995e978d706740a8a4b9b7860cfd7b6", + "sha256": "0a6b961ad363a421bde99f2c8e5d8f20370bcba45fd303050537a25bdd805b14", + "vintage": "2024", + "period": "2024" + }, + "candidate_populated_layers": 172, + "known_gaps": { + "employer_sponsored_insurance_premiums": { + "reason": "SOURCE UNAVAILABILITY WITH EVIDENCE: archived commit 42ed5d45c56df80d754fbe24cce21cfeb8d05cbe datasets/cps/cps.py lines 197-271 and 1575-1581 requires NOW_OWNGRP, NOW_HIPAID, NOW_GRPFTYP, and PHIP_VAL; datasets/cps/census_cps.py lines 13-55 declares the three NOW_* fields optional. All three SHA-recorded 2022-2024 HDF inputs consumed by the hermetic --asec-h5 build contain PHIP_VAL but omit all three required NOW_* fields, so the retired owner/payment/plan-type derivation cannot execute. has_esi and NOW_GRP are not semantically valid substitutes.", + "issue": "PolicyEngine/populace#32" + }, + "financial_assistance": { + "reason": "SOURCE UNAVAILABILITY WITH EVIDENCE: archived commit 42ed5d45c56df80d754fbe24cce21cfeb8d05cbe datasets/cps/cps.py lines 1493-1496 derives person-level financial_assistance directly from FIN_VAL; datasets/cps/census_cps.py lines 39-58 and 306-359 make FIN_VAL a required person source. All three SHA-recorded 2022-2024 ASEC H5 inputs consumed by the hermetic --asec-h5 build omit FIN_VAL, FIN_YN, and I_FINVAL from their person tables. HFINVAL and FFINVAL are household/family aggregates and cannot recover the person recipient without synthetic allocation. The retired PUF-clone QRF at datasets/cps/extended_cps.py lines 140-194 and 639-745 trains financial_assistance from CPS person observations, so PUF provides no independent replacement source.", + "issue": "PolicyEngine/populace#38" + }, + "is_unmarried_partner_of_household_head": { + "reason": "SOURCE UNAVAILABILITY WITH EVIDENCE: archived commit 42ed5d45c56df80d754fbe24cce21cfeb8d05cbe datasets/cps/cps.py lines 1214-1221 derives is_unmarried_partner_of_household_head exclusively from person PERRP; datasets/cps/census_cps.py lines 306-318 declares that person source. All three SHA-recorded 2022-2024 ASEC HDF inputs consumed by the hermetic build omit PERRP. The 2022 and 2023 inputs also omit PECOHAB, A_EXPRRP, and A_FAMREL; only 2024 carries those alternatives, and its A_EXPRRP code 13 conflates partner with roommate. Populace's older-year relationship fallback uses only line, spouse, sex, and parent pointers and never identifies a partner. Using the 2024 pointer for one-third of the pool while treating the other two-thirds as false, or assigning partner status from the combined partner/roommate code, would synthesize statuses the locked sources do not report.", + "issue": "PolicyEngine/populace#38" + }, + "is_wic_at_nutritional_risk": { + "reason": "SOURCE UNAVAILABILITY WITH EVIDENCE: archived commit 42ed5d45c56df80d754fbe24cce21cfeb8d05cbe datasets/cps/cps.py lines 684-701 does not map a measured nutritional-risk assessment; it loads category priors, makes a seeded Bernoulli draw, and ORs that synthetic draw with reported WIC receipt. parameters/take_up/wic_nutritional_risk.yaml lines 1-13 gives every fixed category prior a 1980 effective key (the infant factor was historically adopted in 1991), while parameters/__init__.py lines 38-50 merely carries the latest value forward. datasets/cps/census_cps.py lines 293-365 loads only WIC receipt/value fields, and cps.py line 1558 maps WICYN solely to receives_wic. All three SHA-recorded 2022-2024 ASEC H5 inputs consumed by the hermetic build contain only SPM_WICVAL/WICYN on person and HRNUMWIC/HRWICYN on household; none contains an individual nutritional-risk assessment. The Census questionnaire gives WICYN an adult-female universe and allows receipt reporting for the respondent or on behalf of a child, so a yes does not identify the assessed person and code 0 is not-in-universe rather than a negative assessment. USDA requires a competent professional to assess and document risk in separate WIC certification records; FNS adopted an all-category 1.0 risk adjustment while producing its CY2020 estimates and applied it to the revised CY2016-2021 series. Assigning risk to a WICYN row, assigning False to nonrecipients, or recreating the retired random False outcomes would synthesize person-level assessments absent from the locked sources, while assigning True to all unknowns is the PolicyEngine-US default and carries no non-default signal.", + "issue": "PolicyEngine/populace#312" + }, + "survivor_benefits": { + "reason": "SOURCE UNAVAILABILITY WITH EVIDENCE: archived commit 42ed5d45c56df80d754fbe24cce21cfeb8d05cbe datasets/cps/cps.py line 1495 derives person-level survivor_benefits exclusively from SRVS_VAL; datasets/cps/census_cps.py lines 39-58 and 306-359 make SRVS_VAL a required person source. All three SHA-recorded 2022-2024 ASEC H5 inputs consumed by the hermetic --asec-h5 build omit SRVS_VAL from their person tables. Their FSURVAL/HSURVAL fields are only family/household aggregates: each household value exactly equals summed family values, and 657/647/610 positive families have multiple people, so the recipient cannot be recovered. The retired PUF-clone QRF at datasets/cps/extended_cps.py lines 140-194 and 639-745 trains survivor_benefits from CPS person observations, so PUF provides no independent replacement source. Restoring a nonzero person-year amount without SRVS_VAL would synthesize data.", + "issue": "PolicyEngine/populace#38" + }, + "takes_up_dc_ptc": { + "reason": "SOURCE UNAVAILABILITY WITH EVIDENCE: archived commit 42ed5d45c56df80d754fbe24cce21cfeb8d05cbe datasets/cps/cps.py lines 565-599 does not carry an observed DC property-tax-credit claim; it loads a scalar rate and assigns takes_up_dc_ptc by an independent seeded Bernoulli draw. parameters/take_up/dc_ptc.yaml lines 1-11 divides an aggregate 37,133-claim count by a 131,791,388 PolicyEngine value estimate, which is neither dimensionally a participation denominator nor mathematically 0.32; utils/randomness.py lines 5-28 confirms the output is generated from a variable-name-seeded random stream. Archived census_cps.py lines 251-262 exposes only generic state-tax liability amounts, not a Schedule H claim; archived puf.py lines 704-719 maps other_credits from federal P08000, and the locked PUF has no state geography. All three SHA-recorded 2022-2024 ASEC H5 inputs and the SHA-recorded processed PUF 1.8.0 input consumed by the hermetic build omit takes_up_dc_ptc and any DC Schedule H claim indicator. The aggregate claim count cannot identify claiming tax units, so replaying the retired draw or selecting modeled-eligible units would synthesize claimant identities absent from every locked source.", + "issue": "PolicyEngine/populace#312" + }, + "takes_up_early_head_start_if_eligible": { + "reason": "SOURCE UNAVAILABILITY WITH EVIDENCE: archived commit 42ed5d45c56df80d754fbe24cce21cfeb8d05cbe datasets/cps/cps.py lines 582-583 and 640-648 does not carry an observed Early Head Start enrollment indicator; it loads a scalar rate and assigns takes_up_early_head_start_if_eligible by an unanchored person-level draw. parameters/take_up/early_head_start.yaml lines 1-9 supplies 0.09 from a NIEER research chart, not an ACF administrative participation fact, and utils/randomness.py lines 5-28 confirms variable-name-seeded randomness. PolicyEngine-US 1.764.6 applies the input to people under age 3 or pregnant. All three SHA-recorded 2022-2024 ASEC H5 inputs and the processed PUF consumed by the hermetic build omit both Head Start take-up flags and any individual Head Start/Early Head Start enrollment indicator. The independently SHA-pinned full 2023 SIPP observes standard Head Start only through a nursery/preschool education item with no reported positives under age 3; its child-care fields cover only ages 2-7, contain no Early Head Start distinction or pregnancy branch, and have a Census-documented universe inconsistency. ACF's exact EHS cumulative-enrollment total includes every participant served during the year and turnover replacements, while its funded enrollment is capacity; neither identifies point-in-time person recipients. Assigning either aggregate to modeled-eligible infants, toddlers, or pregnant people, mapping ambiguous age-2 SIPP Head Start responses to EHS, or replaying the retired 0.09 draw would synthesize the missing person values.", + "issue": "PolicyEngine/populace#312" + } + } + } + }, + "hours_worked_signal": { + "passed": true, + "details": { + "worked_share": 0.5296885704999547, + "mean_weekly_hours_workers": 37.56500077416023, + "worked_share_band": [ + 0.35, + 0.62 + ], + "mean_weekly_hours_band": [ + 30.0, + 45.0 + ], + "unique_counts": { + "weekly_hours_worked_before_lsr": 94, + "hours_worked_last_week": 99, + "weeks_worked": 53 + } + } + }, + "snap_take_up_signal": { + "passed": true, + "details": { + "take_up_share": 0.81845299430267, + "take_up_share_band": [ + 0.7, + 0.95 + ], + "unique_count": 2, + "reported_share": 0.09658006751385996, + "reporters_not_taking_up": 0 + } + }, + "eligibility_inputs_signal": { + "passed": true, + "details": { + "disabled_share": 0.09470379050475294, + "full_time_college_student_share": 0.014372059621570291, + "parent_share": 0.30573658099536954, + "veterans_benefits_share": 0.014050680524622211, + "disabled_share_band": [ + 0.05, + 0.2 + ], + "full_time_student_share_band": [ + 0.01, + 0.08 + ], + "parent_share_band": [ + 0.12, + 0.4 + ], + "veterans_benefits_share_band": [ + 0.002, + 0.04 + ], + "unique_counts": { + "is_disabled": 2, + "is_blind": 2, + "is_full_time_college_student": 2, + "own_children_in_household": 14, + "veterans_benefits": 568 + } + } + }, + "pregnancy_signal": { + "passed": true, + "details": { + "pregnant_share": 0.008977563210398776, + "pregnant_share_band": [ + 0.002, + 0.02 + ], + "unique_count": 2 + } + }, + "snap_discretionary_exemption_signal": { + "passed": true, + "details": { + "exempt_share": 0.046463120829380554, + "exempt_share_band": [ + 0.015, + 0.09 + ], + "unique_count": 2 + } + }, + "documented_absent_inputs": { + "is_homeless": "No ASEC source: the CPS samples the housed population, so the pre-HR1 SNAP ABAWD homeless exemption cannot fire (PolicyEngine/populace#351).", + "was_in_foster_care": "No ASEC item for foster-care history, so the pre-HR1 former-foster-youth ABAWD exemption (7 CFR 273.24(c)(9)) cannot fire (PolicyEngine/populace#351).", + "is_snap_work_program_participant": "No ASEC item measures SNAP E&T or qualifying work-program participation, so compliance via program participation never fires; USDA reports E&T reaches a small minority of participants, so the always-False default understates compliance only modestly (PolicyEngine/populace#249).", + "weekly_snap_work_program_hours": "No ASEC item measures qualifying work-program hours, so combined work-plus-program hours toward the 20-hour ABAWD test omit program hours (PolicyEngine/populace#249).", + "is_snap_workfare_participant": "No ASEC item measures workfare participation under 7 CFR 273.7(m), which satisfies the ABAWD requirement regardless of hours; defaults False (PolicyEngine/populace#249)." + } + }, + "compatible_core_packages": [ + { + "name": "policyengine-core", + "specifier": "==3.26.11" + } + ], + "compatible_model_packages": [ + { + "name": "policyengine-us", + "specifier": "==1.764.6" + } + ], + "artifacts": { + "populace_us_2024": { + "kind": "microdata", + "path": "populace_us_2024.h5", + "repo_id": "policyengine/populace-us", + "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "sha256": "48b9d479fb4fd1c3537f9383ce4697d130b6f618658409d74f6233c43b994c7e" + }, + "populace_us_2024_calibration": { + "kind": "calibration", + "path": "populace_us_2024_calibration.npz", + "repo_id": "policyengine/populace-us", + "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "sha256": "b6e9c0678de5e79e8ddbd7a2a2f17c357bd6b368153cea7b5119c299a280a1b5" + }, + "calibration_diagnostics": { + "kind": "diagnostics", + "path": "calibration_diagnostics.json", + "repo_id": "policyengine/populace-us", + "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "sha256": "870449b44e86b13b25bcea1a57f0e7af37f4d4db18be815eea3acdf9fe6eb40e" + }, + "us_source_coverage": { + "kind": "diagnostics", + "path": "us_source_coverage.json", + "repo_id": "policyengine/populace-us", + "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "sha256": "6406c8686c292015a5bc7265a42402f89935f9395a8b63510efdd4d9562e7ff5" + }, + "us_ssi_take_up": { + "kind": "diagnostics", + "path": "us_ssi_take_up.json", + "repo_id": "policyengine/populace-us", + "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "sha256": "ce631a37e2fe2ed22927fa232d41150d3b6cf328b38d3e5b38018fc4eec1b44d" + }, + "demographics": { + "kind": "diagnostics", + "path": "demographics.json", + "repo_id": "policyengine/populace-us", + "revision": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "sha256": "50c39dea345fa4e21f32272304b376979b9572ca17383dd13061b752d9a81112" + } + } +} \ No newline at end of file diff --git a/tests/fixtures/release_tros/us.trace.tro.jsonld b/tests/fixtures/release_tros/us.trace.tro.jsonld new file mode 100644 index 00000000..1d21864e --- /dev/null +++ b/tests/fixtures/release_tros/us.trace.tro.jsonld @@ -0,0 +1,134 @@ +{ + "@context": [ + { + "pe": "https://policyengine.org/trace/0.1#", + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + "schema": "https://schema.org/", + "trov": "https://w3id.org/trace/trov/0.1#" + } + ], + "@graph": [ + { + "@id": "tro", + "@type": "trov:TransparentResearchObject", + "schema:creator": { + "@type": "schema:Organization", + "schema:name": "PolicyEngine", + "schema:url": "https://policyengine.org" + }, + "schema:dateCreated": "2026-07-28T03:56:03.415544+00:00", + "schema:description": "TRACE TRO for certified runtime bundle us-5.3.0 covering the bundle manifest, the certified dataset artifact, the country model wheel, and the country data release manifest when it is available.", + "schema:name": "policyengine us certified bundle TRO", + "trov:createdWith": { + "@type": "schema:SoftwareApplication", + "schema:name": "policyengine", + "schema:softwareVersion": "5.3.0" + }, + "trov:hasArrangement": [ + { + "@id": "arrangement/1", + "@type": "trov:ArtifactArrangement", + "rdfs:comment": "Certified arrangement for bundle us-5.3.0.", + "trov:hasArtifactLocation": [ + { + "@id": "arrangement/1/location/bundle_manifest", + "@type": "trov:ArtifactLocation", + "trov:hasArtifact": { + "@id": "composition/1/artifact/bundle_manifest" + }, + "trov:hasLocation": "data/bundle/manifest.json" + }, + { + "@id": "arrangement/1/location/data_release_manifest", + "@type": "trov:ArtifactLocation", + "trov:hasArtifact": { + "@id": "composition/1/artifact/data_release_manifest" + }, + "trov:hasLocation": "https://huggingface.co/datasets/policyengine/populace-us/resolve/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/releases/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/release_manifest.json" + }, + { + "@id": "arrangement/1/location/dataset", + "@type": "trov:ArtifactLocation", + "trov:hasArtifact": { + "@id": "composition/1/artifact/dataset" + }, + "trov:hasLocation": "https://huggingface.co/datasets/policyengine/populace-us/resolve/populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z/populace_us_2024.h5" + }, + { + "@id": "arrangement/1/location/model_wheel", + "@type": "trov:ArtifactLocation", + "trov:hasArtifact": { + "@id": "composition/1/artifact/model_wheel" + }, + "trov:hasLocation": "https://files.pythonhosted.org/packages/e2/5a/0566666f30ad06e416f0cf25b24bdee73acd71897fb2de3c0ac43b59e3e3/policyengine_us-1.764.6-py3-none-any.whl" + } + ] + } + ], + "trov:hasComposition": { + "@id": "composition/1", + "@type": "trov:ArtifactComposition", + "trov:hasArtifact": [ + { + "@id": "composition/1/artifact/bundle_manifest", + "@type": "trov:ResearchArtifact", + "schema:name": "policyengine.py bundle manifest for us", + "trov:mimeType": "application/json", + "trov:sha256": "57c6fe9c5c370ea2b9460120656004b1a583db06cabb6c774551effc3c125695" + }, + { + "@id": "composition/1/artifact/data_release_manifest", + "@type": "trov:ResearchArtifact", + "schema:name": "populace-data release manifest 0.1.0", + "trov:mimeType": "application/json", + "trov:sha256": "dd949ba3c4c7a56aff8442c6db5a031d2c84c3da59f5d14832f468c358604506" + }, + { + "@id": "composition/1/artifact/dataset", + "@type": "trov:ResearchArtifact", + "schema:name": "populace_us_2024", + "trov:mimeType": "application/x-hdf5", + "trov:sha256": "48b9d479fb4fd1c3537f9383ce4697d130b6f618658409d74f6233c43b994c7e" + }, + { + "@id": "composition/1/artifact/model_wheel", + "@type": "trov:ResearchArtifact", + "schema:name": "policyengine-us==1.764.6 wheel", + "trov:mimeType": "application/zip", + "trov:sha256": "2b2c5c02c26ab212270948db1f30129728b625fc6109fc9ca0c604b714546909" + } + ], + "trov:hasFingerprint": { + "@id": "composition/1/fingerprint", + "@type": "trov:CompositionFingerprint", + "trov:sha256": "c9806c4dbc8cb664ca2ed431af72f8540c08171fea5ce02d6e579f2d6866cf21" + } + }, + "trov:hasPerformance": { + "@id": "trp/1", + "@type": "trov:TransparentResearchPerformance", + "pe:builtWithModelVersion": "1.764.6", + "pe:certifiedBy": "policyengine.py bundle certification", + "pe:certifiedForModelVersion": "1.764.6", + "pe:compatibilityBasis": "built_with_model_package", + "pe:dataBuildId": "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z", + "pe:emittedIn": "repository-bundle", + "rdfs:comment": "Certification of build populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z for policyengine-us 1.764.6.", + "trov:accessedArrangement": { + "@id": "arrangement/1" + }, + "trov:startedAtTime": "2026-07-28T03:56:03.415544+00:00", + "trov:wasConductedBy": { + "@id": "trs" + } + }, + "trov:wasAssembledBy": { + "@id": "trs", + "@type": "trov:TransparentResearchSystem", + "rdfs:comment": "PolicyEngine certification workflow that pins a country model version, a country data release, and a specific dataset artifact.", + "schema:name": "PolicyEngine release pipeline" + } + } + ] +} diff --git a/tests/fixtures/spm_development.py b/tests/fixtures/spm_development.py new file mode 100644 index 00000000..6ed81414 --- /dev/null +++ b/tests/fixtures/spm_development.py @@ -0,0 +1,223 @@ +"""Explicit test-only bootstrap for unpublished country/calculator wheels. + +Load with ``-p tests.fixtures.spm_development --spm-development-manifest PATH``. +This bypass is never imported by production code or enabled by default pytest. +It supplies an UNVERIFIED receipt, not fabricated data compatibility evidence. +""" + +import copy +import hashlib +import importlib +import json +import os +import sys +import zipfile +from importlib import metadata +from pathlib import Path +from urllib.parse import unquote, urlparse + +import pytest + +_DEVELOPMENT_MANIFEST = None + + +def _verify_import_sources(packages): + """Authenticate import resolution and already loaded modules, not just metadata. + + Distribution metadata can describe the right wheel while PYTHONPATH or a + previously imported module supplies different code. Check without importing + the country packages, then repeat after the explicit bootstrap imports them. + """ + for import_name, (root, members) in packages.items(): + spec = importlib.util.find_spec(import_name) + if spec is None or spec.origin is None: + raise ValueError(f"Cannot authenticate import source for {import_name}") + expected_init = root / "__init__.py" + if Path(spec.origin).resolve() != expected_init: + raise ValueError( + f"Unexpected import source for {import_name}: {spec.origin}" + ) + if {Path(p).resolve() for p in spec.submodule_search_locations or ()} != {root}: + raise ValueError(f"Unexpected package search path for {import_name}") + for name, module in tuple(sys.modules.items()): + if name != import_name and not name.startswith(import_name + "."): + continue + if module is None: + continue + source = getattr(module, "__file__", None) + module_spec = getattr(module, "__spec__", None) + if ( + source is None + and module_spec is not None + and module_spec.origin is None + and module_spec.submodule_search_locations + ): + # Country parameter directories are legitimate namespace + # packages. Every namespace search directory must still belong + # to the authenticated installation and wheel inventory. + locations = { + Path(p).resolve() for p in module_spec.submodule_search_locations + } + if locations != { + Path(p).resolve() for p in getattr(module, "__path__", ()) + }: + raise ValueError( + f"Unexpected loaded package search path for {name}" + ) + for location in locations: + try: + prefix = location.relative_to(root).as_posix() + "/" + except ValueError as exc: + raise ValueError( + f"Unexpected loaded import source for {name}" + ) from exc + if members is not None and not any( + member.startswith(prefix) for member in members + ): + raise ValueError(f"Unexpected loaded import source for {name}") + continue + if not source or module_spec is None or module_spec.origin is None: + raise ValueError(f"Cannot authenticate loaded import source for {name}") + source = Path(source).resolve() + try: + relative = source.relative_to(root).as_posix() + except ValueError as exc: + raise ValueError(f"Unexpected loaded import source for {name}") from exc + if Path(module_spec.origin).resolve() != source or ( + members is not None and relative not in members + ): + raise ValueError(f"Unexpected loaded import source for {name}") + if hasattr(module, "__path__") and { + Path(p).resolve() for p in module.__path__ + } != {source.parent}: + raise ValueError(f"Unexpected loaded package search path for {name}") + + +def pytest_addoption(parser): + parser.addoption("--spm-development-manifest", type=Path) + + +def pytest_load_initial_conftests(early_config, parser, args): + path = early_config.known_args_namespace.spm_development_manifest + if path is None: + raise pytest.UsageError( + "The SPM development plugin requires an explicit manifest" + ) + try: + activate_spm_development_manifest(path) + except ValueError as exc: + raise pytest.UsageError(str(exc)) from exc + + +def _verify_local_wheel_installations(payload): + """Require both fixture wheel identity and installed source byte identity.""" + packages = {} + for name, import_name in ( + ("policyengine-us", "policyengine_us"), + ("spm-calculator", "spm_calculator"), + ): + component = payload["packages"][name] + if component["import_name"] != import_name: + raise ValueError(f"Unexpected import name for {name}") + uri = urlparse(component["wheel_url"]) + if uri.scheme != "file" or uri.netloc: + raise ValueError("Development fixtures require explicit local wheel files") + path = Path(unquote(uri.path)) + if hashlib.sha256(path.read_bytes()).hexdigest() != component["sha256"]: + raise ValueError(f"Local {name} wheel does not match the fixture sha256") + distribution = metadata.distribution(name) + members = set() + with zipfile.ZipFile(path) as archive: + for member in archive.namelist(): + if not member.startswith(import_name + "/") or member.endswith("/"): + continue + installed = distribution.locate_file(member) + if not installed.is_file() or installed.read_bytes() != archive.read( + member + ): + raise ValueError( + f"Installed {name} file differs from wheel: {member}" + ) + members.add(member[len(import_name) + 1 :]) + if not members: + raise ValueError(f"Local {name} wheel contains no package files") + packages[import_name] = ( + Path(distribution.locate_file(import_name)).resolve(), + members, + ) + # Core's version is pinned in the fixture; its wheel is authenticated by the + # separate four-wheel producer probe. Still reject a shadowed core import. + packages["policyengine_core"] = ( + Path( + metadata.distribution("policyengine-core").locate_file("policyengine_core") + ).resolve(), + None, + ) + _verify_import_sources(packages) + return packages + + +def activate_spm_development_manifest(path): + """Explicitly bootstrap an unpublished local runtime before country imports. + + This test helper is also available to bounded development scripts. It never + certifies the inherited data and refuses a mismatched installed wheel. + """ + global _DEVELOPMENT_MANIFEST + + if "policyengine.tax_benefit_models.us.model" in sys.modules: + raise ValueError( + "Activate the development fixture before importing country models" + ) + path = Path(path) + payload = json.loads(path.read_text()) + if payload.get("development", {}).get("data_certification") != "not_certified": + raise ValueError("The SPM plugin only accepts uncertified development fixtures") + for name in ("policyengine-us", "policyengine-core", "spm-calculator"): + if metadata.version(name) != payload["packages"][name]["version"]: + raise ValueError(f"Installed {name} does not match the development fixture") + packages = _verify_local_wheel_installations(payload) + os.environ["POLICYENGINE_SKIP_COUNTRY_IMPORTS"] = "1" + import policyengine + from policyengine import bundle + from policyengine.provenance import manifest + + _DEVELOPMENT_MANIFEST = payload + original_release = manifest.get_release_manifest + original_certify = manifest.certify_data_release_compatibility + + def development_release(country_id): + if country_id == "us": + return manifest.CountryReleaseManifest.model_validate( + copy.deepcopy(payload["data_releases"]["us"]) + ) + return original_release(country_id) + + def unavailable_data_release(country_id): + raise manifest.DataReleaseManifestUnavailableError( + "Explicit offline local-wheel development fixture; data certification pending" + ) + + def unverified_development(country_id, runtime_model_version, **kwargs): + if country_id == "us": + return manifest.DataCertification( + compatibility_basis="unverified_development_fixture", + certified_for_model_version=runtime_model_version, + ) + return original_certify(country_id, runtime_model_version, **kwargs) + + manifest.get_release_manifest = development_release + manifest.get_data_release_manifest = unavailable_data_release + manifest.certify_data_release_compatibility = unverified_development + bundle.get_current_bundle = lambda: copy.deepcopy(payload) + policyengine.us = importlib.import_module("policyengine.tax_benefit_models.us") + if importlib.util.find_spec("policyengine_uk") is not None: + policyengine.uk = importlib.import_module("policyengine.tax_benefit_models.uk") + _verify_import_sources(packages) + return copy.deepcopy(payload) + + +@pytest.fixture +def spm_development_bundle(): + """Expose the explicitly enabled, unverified fixture for test assertions.""" + return copy.deepcopy(_DEVELOPMENT_MANIFEST) diff --git a/tests/test_bundle.py b/tests/test_bundle.py index 3a9cac87..4a3f536b 100644 --- a/tests/test_bundle.py +++ b/tests/test_bundle.py @@ -64,7 +64,7 @@ def test_bundle_install_requirements_are_country_scoped(): f"policyengine=={manifest['policyengine_version']}", manifest["packages"]["policyengine-core"]["install_requirement"], manifest["packages"]["policyengine-us"]["install_requirement"], - "spm-calculator==0.3.1", + "spm-calculator==1.0.0", ] assert not any("policyengine-us-data" in req for req in us_requirements) diff --git a/tests/test_bundle_metadata.py b/tests/test_bundle_metadata.py index 1b184d1d..85dd2d01 100644 --- a/tests/test_bundle_metadata.py +++ b/tests/test_bundle_metadata.py @@ -57,7 +57,7 @@ def test_generated_us_extras_preserve_legacy_spm_compatibility(monkeypatch): assert generated == original for extra in ("us", "models", "dev"): section = generated.split(f"\n{extra} = [\n", 1)[1].split("\n]", 1)[0] - assert '"spm-calculator==0.3.1"' in section + assert '"spm-calculator==1.0.0"' in section uk_section = generated.split("\nuk = [\n", 1)[1].split("\n]", 1)[0] assert "spm-calculator" not in uk_section @@ -136,7 +136,7 @@ def test_export_release_assets_writes_bundle_assets(monkeypatch, tmp_path): in (tmp_path / f"policyengine-bundle-{version}.constraints.txt").read_text() ) assert ( - "spm-calculator==0.3.1" + "spm-calculator==1.0.0" in (tmp_path / f"policyengine-bundle-{version}.constraints.txt").read_text() ) assert ( diff --git a/tests/test_certify_data_release.py b/tests/test_certify_data_release.py index ebc8a9fe..9b25a1db 100644 --- a/tests/test_certify_data_release.py +++ b/tests/test_certify_data_release.py @@ -16,6 +16,8 @@ certify_data_release, merge_us_state_release_manifest, parse_manifest_uri, + required_supplemental_release_files, + should_validate_vendored_artifacts, validate_release_manifest, ) from policyengine.provenance.manifest import DataReleaseManifest @@ -741,3 +743,92 @@ def test__given_vendored_bundle_manifest__then_tro_sidecar_binds_it(self): assert bundle_manifest["trov:sha256"] == expected performance = tro["@graph"][0]["trov:hasPerformance"] assert performance["pe:emittedIn"] == "repository-bundle" + + +def _source_enrichment_manifest_payload() -> dict: + """A `microcosm-data` release whose sidecars use the `evidence` kind. + + This is the shape the Populace US producer publishes for source-enrichment + releases: the data package is named `microcosm-data` rather than + `populace-data`, and release-directory sidecars are labelled `evidence` + rather than `diagnostics`. + """ + payload = _release_manifest_payload() + payload["data_package"]["name"] = "microcosm-data" + payload["artifacts"]["us_source_coverage"]["kind"] = "evidence" + payload["artifacts"]["build_manifest"] = { + "kind": "evidence", + "path": "build_manifest.json", + "repo_id": "policyengine/populace-us", + "revision": TAG, + "sha256": "1" * 64, + "size_bytes": 1, + } + return payload + + +class TestSourceEnrichmentProducerRelease: + """The `microcosm-data` producer name must keep every Populace rule armed. + + A release published under the newer producer name previously fell through + every `populace-data` guard, which both skipped the per-artifact + reachability check and left release-scoped sidecars pinned at repo-root + paths that do not resolve. + """ + + def _payload(self) -> dict: + return build_country_manifest_payload( + country="us", + manifest=DataReleaseManifest.model_validate( + _source_enrichment_manifest_payload() + ), + uri_parts=parse_manifest_uri(MANIFEST_URI), + policyengine_version="9.9.9", + model_package="policyengine-us", + model_version="1.723.0", + model_wheel={"sha256": "d" * 64, "url": "https://example/wheel"}, + ) + + def test__given_evidence_kind_sidecar__then_rewrites_to_release_path(self): + datasets = self._payload()["datasets"] + + assert datasets["us_source_coverage"]["path"] == ( + f"releases/{TAG}/us_source_coverage.json" + ) + assert datasets["build_manifest"]["path"] == ( + f"releases/{TAG}/build_manifest.json" + ) + + def test__given_microdata_kind__then_keeps_repo_root_path(self): + datasets = self._payload()["datasets"] + + assert datasets["populace_us_2024"]["path"] == "populace_us_2024.h5" + assert ( + datasets["populace_us_2024_calibration"]["path"] + == "populace_us_2024_calibration.npz" + ) + + def test__given_microcosm_producer__then_validates_vendored_artifacts(self): + assert should_validate_vendored_artifacts( + "us", + DataReleaseManifest.model_validate(_source_enrichment_manifest_payload()), + parse_manifest_uri(MANIFEST_URI), + ) + + def test__given_microcosm_producer__then_requires_source_coverage_file(self): + assert required_supplemental_release_files( + "us", + DataReleaseManifest.model_validate(_source_enrichment_manifest_payload()), + parse_manifest_uri(MANIFEST_URI), + ) == ("us_source_coverage.json",) + + def test__given_microcosm_producer__then_still_omits_area_h5_artifacts(self): + datasets = self._payload()["datasets"] + + assert "states/AK" not in datasets + assert "districts/CA-01" not in datasets + + def test__given_microcosm_producer__then_still_drops_area_region_templates(self): + assert self._payload()["region_datasets"] == { + "national": {"path_template": "populace_us_2024.h5"} + } diff --git a/tests/test_dataset_runtime.py b/tests/test_dataset_runtime.py index 9d97db0e..76cab79b 100644 --- a/tests/test_dataset_runtime.py +++ b/tests/test_dataset_runtime.py @@ -9,6 +9,7 @@ MaterializedDataset, _resolve_bundle_dataset, ) +from policyengine.tax_benefit_models.us.spm import resolve_spm_selection REPO_ROOT = Path(__file__).resolve().parents[1] @@ -70,7 +71,9 @@ def test_us_create_datasets_passes_verified_bundle_source_to_country_package( allow_unmanaged=False, data_dir=Path("./data"), ) - microsimulation.assert_called_once_with(dataset="/tmp/populace_us_2024.h5") + microsimulation.assert_called_once_with( + dataset="/tmp/populace_us_2024.h5", spm=resolve_spm_selection() + ) def test_uk_create_datasets_passes_verified_bundle_source_to_country_package( diff --git a/tests/test_extra_variables.py b/tests/test_extra_variables.py index 1631566d..219c0602 100644 --- a/tests/test_extra_variables.py +++ b/tests/test_extra_variables.py @@ -48,6 +48,11 @@ def _us_fixture_dataset(tmp_path): { "household_id": [1], "state_code": ["CA"], + # The certified bundle measures SPM at county geography + # (manifest ``measurements.spm.geography_kind == "county"``), + # so a US household fixture must name its county. State alone + # does not identify an SPM area. + "county_fips": ["06037"], "household_weight": [1_000.0], } ), diff --git a/tests/test_generate_trace_tros.py b/tests/test_generate_trace_tros.py new file mode 100644 index 00000000..012b0e3d --- /dev/null +++ b/tests/test_generate_trace_tros.py @@ -0,0 +1,120 @@ +"""Repository TRO generation must validate complete payloads before any writes.""" + +import json +import sys +from importlib.resources import files +from pathlib import Path + +import pytest +from jsonschema import Draft202012Validator, ValidationError + +from policyengine.provenance.manifest import DataReleaseManifestUnavailableError + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts")) +import generate_trace_tros as generator # noqa: E402 + + +@pytest.fixture +def schema(): + return json.loads( + files("policyengine") + .joinpath("data", "schemas", "trace_tro.schema.json") + .read_text() + ) + + +@pytest.fixture +def offline_generator(monkeypatch): + """Use actual bundled US/UK pins, exercising the supported offline fallback.""" + + def unavailable(country): + raise DataReleaseManifestUnavailableError("No network in this test") + + monkeypatch.setattr(generator, "get_data_release_manifest", unavailable) + return generator + + +def test_real_generated_country_payloads_validate(offline_generator, schema): + payloads = offline_generator.generated_tros() + assert {path.name for path, _ in payloads} == { + "us.trace.tro.jsonld", + "uk.trace.tro.jsonld", + } + for _, payload in payloads: + tro = json.loads(payload) + assert ( + tro["@graph"][0]["trov:hasPerformance"]["pe:emittedIn"] + == "repository-bundle" + ) + Draft202012Validator(schema).validate(tro) + + +@pytest.mark.parametrize("country", ["us", "uk"]) +def test_packaged_repository_tros_validate(country, schema): + payload = json.loads( + files("policyengine") + .joinpath("data", "bundle", f"{country}.trace.tro.jsonld") + .read_text() + ) + Draft202012Validator(schema).validate(payload) + + +def test_unknown_emission_still_rejected(offline_generator, schema): + payload = json.loads(offline_generator.generated_tros()[0][1]) + payload["@graph"][0]["trov:hasPerformance"]["pe:emittedIn"] = "unknown-emission" + with pytest.raises(ValidationError, match="unknown-emission"): + Draft202012Validator(schema).validate(payload) + + +@pytest.mark.parametrize("existing", [False, True]) +@pytest.mark.parametrize("defect", ["emission", "composition"]) +def test_last_serialized_country_failure_preserves_all_outputs( + offline_generator, monkeypatch, tmp_path, existing, defect +): + output = tmp_path / "bundle" + before = {} + if existing: + output.mkdir() + for country in ["uk", "us"]: + path = output / f"{country}.trace.tro.jsonld" + path.write_bytes(f"preserve original {country}".encode()) + before[path.name] = path.read_bytes() + monkeypatch.setattr(offline_generator, "BUNDLE_TRO_DIR", output) + serialize = offline_generator.serialize_trace_tro + serialized = [] + + def corrupt_last(tro): + # Corrupt the serialized US output after the valid UK payload is prepared. + payload = json.loads(serialize(tro)) + serialized.append(payload) + if len(serialized) == 2: + node = payload["@graph"][0] + if defect == "emission": + node["trov:hasPerformance"]["pe:emittedIn"] = "unknown-emission" + else: + del node["trov:hasComposition"] + return json.dumps(payload).encode() + + monkeypatch.setattr(offline_generator, "serialize_trace_tro", corrupt_last) + with pytest.raises(ValidationError): + offline_generator.regenerate_all() + assert len(serialized) == 2 + if existing: + assert {p.name: p.read_bytes() for p in output.iterdir()} == before + else: + assert not output.exists() + + +def test_dry_generation_has_no_writes_then_regeneration_writes_all( + offline_generator, monkeypatch, tmp_path, schema +): + output = tmp_path / "new-bundle" + monkeypatch.setattr(offline_generator, "BUNDLE_TRO_DIR", output) + expected = dict(offline_generator.generated_tros()) + assert not output.exists() + written, regressions = offline_generator.regenerate_all() + assert set(written) == set(expected) + assert regressions == [] + for path in written: + assert path.read_bytes() == expected[path] + Draft202012Validator(schema).validate(json.loads(path.read_bytes())) diff --git a/tests/test_household_calculator_snapshot.py b/tests/test_household_calculator_snapshot.py index 987d49d8..170f7b4c 100644 --- a/tests/test_household_calculator_snapshot.py +++ b/tests/test_household_calculator_snapshot.py @@ -117,12 +117,63 @@ def test_us_household_snapshot(case_name: str) -> None: import policyengine as pe kwargs = US_CASES[case_name] - result = pe.us.calculate_household(**kwargs) + result = pe.us.calculate_household(**kwargs, spm={"geography_kind": "national"}) out: dict[str, float] = {} - _flatten("", result.to_dict(), out) + # Provenance is an additive receipt tested in test_spm_household.py; keep + # this historical snapshot focused on its existing numeric output contract. + values = result.to_dict() + values.pop("provenance", None) + _flatten("", values, out) _check_snapshot(case_name, out) +def test_snap_work_requirement_inputs_drive_the_no_income_case() -> None: + """Pin why ``us_single_adult_no_income`` reports one month of SNAP. + + A childless 35-year-old with no income is an ABAWD subject to the SNAP + time limit, and the case supplies no hours. Two country changes decide the + result: ``weekly_hours_worked_before_lsr`` now defaults to 0 rather than + 40, so an omitted-hours household fails the 20-hour test; and California's + statewide ABAWD waiver runs only through 2026-01-31. The unit is therefore + exempt in January 2026 and time-limited from February, giving one eligible + month rather than a broken annualization — the twelve-month sum is intact, + as the hours-supplied comparison here shows. + + This is the mechanism behind the snapshot's SNAP figure, so it is pinned + separately: if the hours default or the waiver modelling moves, this fails + with the reason rather than leaving an unexplained number in a fixture. + """ + pytest.importorskip("policyengine_us") + from policyengine_us import Simulation + + def situation(hours: float | None) -> dict: + person: dict = {"age": {"2026": 35}} + if hours is not None: + person["weekly_hours_worked_before_lsr"] = {"2026": hours} + return { + "people": {"you": person}, + "spm_units": {"sp": {"members": ["you"]}}, + "tax_units": {"tu": {"members": ["you"]}}, + "families": {"f": {"members": ["you"]}}, + "marital_units": {"mu": {"members": ["you"]}}, + "households": {"hh": {"members": ["you"], "state_code": {"2026": "CA"}}}, + } + + omitted = Simulation(situation=situation(None)) + assert omitted.calculate("snap", 2026)[0] == pytest.approx(298.00, abs=0.01) + # Exempt only while the California statewide waiver is in force. + assert bool(omitted.calculate("is_snap_abawd_exempt", "2026-01")[0]) is True + assert bool(omitted.calculate("is_snap_abawd_exempt", "2026-02")[0]) is False + assert omitted.calculate("snap", "2026-01")[0] == pytest.approx(298.00, abs=0.01) + assert omitted.calculate("snap", "2026-02")[0] == pytest.approx(0.0, abs=0.01) + + # Supplying hours satisfies the work requirement, and the same model then + # returns a full, correctly uprated twelve months: nine at the 298.00 + # allotment plus three at the October 2026 uprated rate. + working = Simulation(situation=situation(40)) + assert working.calculate("snap", 2026)[0] == pytest.approx(3607.57, abs=0.01) + + # UK cases ------------------------------------------------------------------- diff --git a/tests/test_household_impact.py b/tests/test_household_impact.py index 584c74f2..56133b1a 100644 --- a/tests/test_household_impact.py +++ b/tests/test_household_impact.py @@ -4,6 +4,9 @@ ``pe.uk.calculate_household`` pair returning a dot-accessible :class:`HouseholdResult`. Input validation raises on unknown variable names; extra variables are a flat list dispatched by the library. + +US regression cases explicitly select national SPM geography because these +fixtures test the calculator interface without claiming a local SPM area. """ import pytest @@ -125,6 +128,7 @@ def test__axes__then_result_values_are_axis_series(self): class TestUSCalculateHousehold: def test__single_adult__then_returns_result_with_net_income(self): result = pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 30, "is_tax_unit_head": True}], year=2026, ) @@ -134,6 +138,7 @@ def test__single_adult__then_returns_result_with_net_income(self): def test__single_adult_with_income__then_tax_unit_income_tax_positive(self): result = pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 30, "employment_income": 50000, "is_tax_unit_head": True}], tax_unit={"filing_status": "SINGLE"}, year=2026, @@ -143,6 +148,7 @@ def test__single_adult_with_income__then_tax_unit_income_tax_positive(self): def test__reform_applied_through_dict__then_numbers_change(self): baseline = pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 35, "employment_income": 60000, "is_tax_unit_head": True}], tax_unit={"filing_status": "SINGLE"}, year=2026, @@ -150,6 +156,7 @@ def test__reform_applied_through_dict__then_numbers_change(self): # Halve the standard deduction — biggest tax number a reform dict # can move for a simple wage-earner test case. reformed = pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 35, "employment_income": 60000, "is_tax_unit_head": True}], tax_unit={"filing_status": "SINGLE"}, year=2026, @@ -159,6 +166,7 @@ def test__reform_applied_through_dict__then_numbers_change(self): def test__extra_variables_flat_list__then_values_appear_on_entity(self): result = pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 35, "employment_income": 60000, "is_tax_unit_head": True}], tax_unit={"filing_status": "SINGLE"}, year=2026, @@ -169,6 +177,7 @@ def test__extra_variables_flat_list__then_values_appear_on_entity(self): def test__axes__then_result_values_are_axis_series(self): result = pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[ { "age": 35, @@ -202,6 +211,7 @@ def test__axes__then_result_values_are_axis_series(self): def test__nested_axes_shape__then_supported(self): result = pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[ { "age": 35, @@ -227,6 +237,7 @@ def test__nested_axes_shape__then_supported(self): def test__axes_nonpositive_count__then_raises_before_calculation(self): with pytest.raises(ValueError, match="'count' must be a positive integer"): pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 35, "is_tax_unit_head": True}], year=2026, axes=[ @@ -242,6 +253,7 @@ def test__axes_nonpositive_count__then_raises_before_calculation(self): def test__axes_mismatched_group_counts__then_raises_before_calculation(self): with pytest.raises(ValueError, match="must share 'count'"): pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 35, "is_tax_unit_head": True}], year=2026, axes=[ @@ -262,6 +274,7 @@ def test__axes_mismatched_group_counts__then_raises_before_calculation(self): def test__reform_compiles_effective_date_form(self): result = pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 30, "is_tax_unit_head": True}], year=2026, reform={"gov.irs.credits.ctc.amount.adult_dependent": {"2026-01-01": 1000}}, @@ -271,6 +284,7 @@ def test__reform_compiles_effective_date_form(self): def test__monthly_year_period__then_raises_before_calculation(self): with pytest.raises(ValueError, match="Monthly periods are not supported"): pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 30, "is_tax_unit_head": True}], year="2026-01", ) @@ -281,6 +295,7 @@ def test__periodized_person_input__then_raises_before_calculation(self): match=r"Periodized household inputs.*people\[0\]\.employment_income", ): pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[ { "age": 30, @@ -297,6 +312,7 @@ def test__periodized_group_input__then_raises_before_calculation(self): match=r"Periodized household inputs.*household\.state_code", ): pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 30, "is_tax_unit_head": True}], household={"state_code": {"2026-01": "CA"}}, year=2026, @@ -320,6 +336,7 @@ def test__non_annual_year__then_error_includes_received_year(self): def test__unknown_person_variable__then_raises_with_suggestion(self): with pytest.raises(ValueError, match="employment_incme"): pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 35, "employment_incme": 60000}], year=2026, ) @@ -329,6 +346,7 @@ def test__variable_on_wrong_entity__then_raises_with_entity_swap_hint(self): # point the caller at the correct entity kwarg. with pytest.raises(ValueError, match="belongs on tax_unit"): pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 35, "filing_status": "SINGLE"}], year=2026, ) @@ -340,6 +358,7 @@ def test__empty_people__then_raises(self): def test__unknown_extra_variable__then_raises(self): with pytest.raises(ValueError, match="not defined"): pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 35}], year=2026, extra_variables=["not_a_real_variable"], @@ -347,6 +366,7 @@ def test__unknown_extra_variable__then_raises(self): def test__unknown_dot_access__then_raises_with_extra_variables_hint(self): result = pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 35, "is_tax_unit_head": True}], year=2026, ) @@ -356,6 +376,7 @@ def test__unknown_dot_access__then_raises_with_extra_variables_hint(self): def test__unknown_reform_path__then_raises_with_close_match(self): with pytest.raises(ValueError, match="not defined"): pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 35, "is_tax_unit_head": True}], year=2026, reform={"gov.irs.not_a_real_parameter": 0}, @@ -364,6 +385,7 @@ def test__unknown_reform_path__then_raises_with_close_match(self): def test__unknown_axis_variable__then_raises_with_suggestion(self): with pytest.raises(ValueError, match="axis variable"): pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 35, "is_tax_unit_head": True}], year=2026, axes=[ @@ -386,6 +408,7 @@ def test__us_kwarg_on_uk__then_raises_with_uk_hint(self): def test__uk_kwarg_on_us__then_raises_with_us_hint(self): with pytest.raises(TypeError, match="UK-only"): pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 30, "is_tax_unit_head": True}], benunit={"foo": 1}, ) @@ -394,6 +417,7 @@ def test__uk_kwarg_on_us__then_raises_with_us_hint(self): class TestHouseholdResultSerialisation: def test__to_dict_produces_plain_dict_tree(self): result = pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 30, "is_tax_unit_head": True}], year=2026, ) @@ -405,6 +429,7 @@ def test__to_dict_produces_plain_dict_tree(self): def test__write_creates_json_file(self, tmp_path): result = pe.us.calculate_household( + spm={"geography_kind": "national"}, people=[{"age": 30, "is_tax_unit_head": True}], year=2026, ) diff --git a/tests/test_models.py b/tests/test_models.py index 1b1066e8..e9c3e5eb 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -135,13 +135,13 @@ def test_has_release_manifest_metadata(self): assert us_latest.release_manifest is not None assert us_latest.release_manifest.country_id == "us" assert us_latest.model_package.name == "policyengine-us" - assert us_latest.model_package.version == "1.764.6" - assert us_latest.data_package.name == "populace-data" + assert us_latest.model_package.version == "2.2.1" + assert us_latest.data_package.name == "microcosm-data" assert us_latest.data_package.version == "0.1.0" assert ( us_latest.default_dataset_uri == "hf://policyengine/populace-us/populace_us_2024.h5" - "@populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z" + "@populace-us-2024-spm-20260915" ) def test_has_hundreds_of_parameters(self): diff --git a/tests/test_release_credentials.py b/tests/test_release_credentials.py new file mode 100644 index 00000000..c0b40934 --- /dev/null +++ b/tests/test_release_credentials.py @@ -0,0 +1,88 @@ +"""Credential verification fails closed without exposing account or token data.""" + +import importlib.util +from pathlib import Path + +import pytest +import requests + +ROOT = Path(__file__).resolve().parents[1] +spec = importlib.util.spec_from_file_location( + "release_credentials", ROOT / "scripts/check_release_credentials.py" +) +credentials = importlib.util.module_from_spec(spec) +spec.loader.exec_module(credentials) + + +@pytest.mark.parametrize("role", ["read", "write", "fineGrained", "admin", "", None]) +def test_requires_authenticated_read_role(monkeypatch, capsys, role): + token = "fixture-secret-must-not-appear" + monkeypatch.setenv("HUGGING_FACE_TOKEN", token) + response = requests.Response() + response.status_code = 200 + payload = { + "name": "fixture-private-account", + "auth": {"type": "access_token", "accessToken": {"role": role}}, + } + + def whoami(url, **kwargs): + assert url == "https://huggingface.co/api/whoami-v2" + assert kwargs["headers"] == {"Authorization": f"Bearer {token}"} + assert kwargs["allow_redirects"] is False + assert kwargs["timeout"] == 30 + response.json = lambda: payload + return response + + monkeypatch.setattr(credentials.requests, "get", whoami) + assert credentials.main() == (0 if role == "read" else 1) + output = capsys.readouterr() + assert token not in output.out + output.err + assert payload["name"] not in output.out + output.err + + +@pytest.mark.parametrize( + "payload", + [ + {}, + {"auth": None}, + {"auth": {"type": "oauth", "accessToken": {"role": "read"}}}, + {"auth": {"type": "access_token"}}, + [], + ], +) +def test_missing_or_unknown_auth_metadata_is_rejected(monkeypatch, payload): + response = requests.Response() + response.status_code = 200 + response.json = lambda: payload + monkeypatch.setattr(credentials.requests, "get", lambda *a, **kw: response) + with pytest.raises(ValueError, match="role read"): + credentials.check_read_only_token("fixture-token") + + +@pytest.mark.parametrize("failure", ["timeout", "invalid-json", 301, 401, 403, 503]) +def test_fetch_failures_never_log_sensitive_response(monkeypatch, capsys, failure): + secret = "fixture-sensitive-detail" + monkeypatch.setenv("HUGGING_FACE_TOKEN", secret) + + def fetch(*args, **kwargs): + if failure == "timeout": + raise requests.Timeout(secret) + response = requests.Response() + response.status_code = failure if isinstance(failure, int) else 200 + response._content = secret.encode() + return response + + monkeypatch.setattr(credentials.requests, "get", fetch) + assert credentials.main() == 1 + output = capsys.readouterr() + assert secret not in output.out + output.err + + +@pytest.mark.parametrize("token", [None, "", " "]) +def test_absent_credentials_fail_before_network(monkeypatch, token): + def unexpected(*args, **kwargs): + pytest.fail("No request may be sent without a credential") + + monkeypatch.setattr(credentials.requests, "get", unexpected) + with pytest.raises(ValueError, match="requires HUGGING_FACE_TOKEN"): + credentials.check_read_only_token(token) diff --git a/tests/test_release_lock.py b/tests/test_release_lock.py new file mode 100644 index 00000000..5c5c68c9 --- /dev/null +++ b/tests/test_release_lock.py @@ -0,0 +1,229 @@ +"""Release versioning preserves the reviewed registry dependency graph.""" + +import copy +import importlib.util +import shutil +import subprocess +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parents[1] +spec = importlib.util.spec_from_file_location( + "release_lock", ROOT / "scripts/release_lock.py" +) +release_lock = importlib.util.module_from_spec(spec) +spec.loader.exec_module(release_lock) + + +def test_registry_lock_rejects_other_sources(): + project = { + "project": { + "name": "policyengine", + "version": "6.0.0", + "requires-python": ">=3.11", + } + } + lock = { + "requires-python": ">=3.11", + "package": [ + {"name": "policyengine", "version": "6.0.0", "source": {"editable": "."}}, + { + "name": "example", + "version": "1.0.0", + "source": {"registry": "https://pypi.org/simple"}, + "wheels": [ + { + "url": "https://files.pythonhosted.org/packages/example.whl", + "hash": "sha256:" + "a" * 64, + } + ], + }, + ], + } + release_lock.validate_registry_lock(project, lock) + for source in ( + {"directory": "../example"}, + {"git": "https://example.com/repo"}, + {"url": "file:///tmp/example.whl"}, + {"registry": "https://other.example/simple"}, + ): + changed = copy.deepcopy(lock) + changed["package"][1]["source"] = source + with pytest.raises(ValueError, match="registry"): + release_lock.validate_registry_lock(project, changed) + + +def test_registry_lock_rejects_version_and_python_drift(): + project = { + "project": { + "name": "policyengine", + "version": "6.0.0", + "requires-python": ">=3.11", + } + } + lock = { + "requires-python": ">=3.11", + "package": [ + {"name": "policyengine", "version": "5.3.0", "source": {"editable": "."}} + ], + } + with pytest.raises(ValueError, match="version"): + release_lock.validate_registry_lock(project, lock) + release_lock.validate_registry_lock(project, lock, allow_previous_version=True) + lock["requires-python"] = ">=3.9" + with pytest.raises(ValueError, match="Python"): + release_lock.validate_registry_lock(project, lock, allow_previous_version=True) + + +@pytest.mark.parametrize( + "config", + [ + {"project": {"dependencies": ["example @ file:///tmp/example.whl"]}}, + { + "project": { + "optional-dependencies": {"us": ["example @ https://example.com/x.whl"]} + } + }, + {"tool": {"uv": {"sources": {"example": {"path": "../example"}}}}}, + {"tool": {"uv": {"index": [{"url": "https://other.example/simple"}]}}}, + ], +) +def test_registry_project_rejects_development_overrides(config): + with pytest.raises(ValueError, match="registry"): + release_lock.validate_registry_project(config) + + +def test_actual_uv_refresh_updates_only_root_version(tmp_path): + if shutil.which("uv") is None: + pytest.skip("uv is required for the release workflow") + project = tmp_path / "pyproject.toml" + project.write_text( + '[project]\nname = "policyengine"\nversion = "5.3.0"\nrequires-python = ">=3.11"\n[build-system]\nrequires = ["setuptools"]\nbuild-backend = "setuptools.build_meta"\n' + ) + subprocess.run( + ["uv", "lock", "--offline", "--no-config"], + cwd=tmp_path, + check=True, + env=release_lock.resolver_environment(), + ) + before = release_lock.load_toml(tmp_path / "uv.lock") + project.write_text(project.read_text().replace('"5.3.0"', '"6.0.0"')) + release_lock.check_release_lock(tmp_path, refresh=True) + after = release_lock.load_toml(tmp_path / "uv.lock") + assert after["package"][0]["version"] == "6.0.0" + assert release_lock.without_root_version( + before, "policyengine" + ) == release_lock.without_root_version(after, "policyengine") + release_lock.check_release_lock(tmp_path) + + +def test_refresh_restores_reviewed_lock_on_dependency_drift(tmp_path, monkeypatch): + (tmp_path / "pyproject.toml").write_text( + '[project]\nname="policyengine"\nversion="6.0.0"\nrequires-python=">=3.11"\n' + ) + lock_path = tmp_path / "uv.lock" + before = b'requires-python=">=3.11"\n[[package]]\nname="policyengine"\nversion="5.3.0"\nsource={editable="."}\n' + lock_path.write_bytes(before) + + def changed(*args, **kwargs): + lock_path.write_bytes( + before.replace(b"5.3.0", b"6.0.0") + + b'[[package]]\nname="unexpected"\nversion="1"\nsource={registry="https://pypi.org/simple"}\nwheels=[{url="https://files.pythonhosted.org/packages/example.whl",hash="sha256:' + + b"a" * 64 + + b'"}]\n' + ) + + monkeypatch.setattr(release_lock.subprocess, "run", changed) + with pytest.raises(ValueError, match="dependency graph"): + release_lock.check_release_lock(tmp_path, refresh=True) + assert lock_path.read_bytes() == before + + +def test_resolver_environment_disables_inherited_overrides(monkeypatch): + monkeypatch.setenv("UV_FROZEN", "1") + monkeypatch.setenv("UV_FIND_LINKS", "/tmp/candidate-wheels") + monkeypatch.setenv("UV_INDEX", "https://other.example/simple") + env = release_lock.resolver_environment() + assert env["UV_FROZEN"] == "0" + assert "UV_FIND_LINKS" not in env + assert "UV_INDEX" not in env + + +@pytest.fixture(scope="module") +def actual_registry_project(tmp_path_factory): + root = tmp_path_factory.mktemp("registry-artifact-lock") + (root / "pyproject.toml").write_text( + '[project]\nname="policyengine"\nversion="6.0.0"\nrequires-python=">=3.11"\n' + 'dependencies=["packaging==26.2"]\n[build-system]\nrequires=["setuptools"]\n' + 'build-backend="setuptools.build_meta"\n' + ) + subprocess.run( + [ + "uv", + "lock", + "--no-config", + "--no-sources", + "--default-index", + release_lock.PYPI, + ], + cwd=root, + check=True, + env=release_lock.resolver_environment(), + ) + release_lock.check_release_lock(root) + return root + + +@pytest.mark.parametrize("kind", ["sdist", "wheel"]) +@pytest.mark.parametrize( + "url", + [ + "file:///tmp/unpublished-candidate.whl", + "https://example.com/unpublished-candidate.whl", + "http://files.pythonhosted.org/packages/candidate.whl", + "https://files.pythonhosted.org.evil.example/packages/candidate.whl", + "https://user@files.pythonhosted.org/packages/candidate.whl", + "https://files.pythonhosted.org:8443/packages/candidate.whl", + ], +) +def test_actual_uv_lock_rejects_artifact_locations_without_mutating( + actual_registry_project, tmp_path, kind, url +): + for name in ("pyproject.toml", "uv.lock"): + shutil.copyfile(actual_registry_project / name, tmp_path / name) + lock_path = tmp_path / "uv.lock" + package = next( + p + for p in release_lock.load_toml(lock_path)["package"] + if p["name"] == "packaging" + ) + artifact = package["sdist"] if kind == "sdist" else package["wheels"][0] + lock_path.write_text(lock_path.read_text().replace(artifact["url"], url)) + before = lock_path.read_bytes() + for refresh in (False, True): + with pytest.raises(ValueError, match="artifact"): + release_lock.check_release_lock(tmp_path, refresh=refresh) + assert lock_path.read_bytes() == before + + +@pytest.mark.parametrize( + "digest", ["sha256:short", "sha256:" + "g" * 64, "md5:" + "a" * 64, ""] +) +def test_actual_uv_lock_rejects_malformed_hash_without_mutating( + actual_registry_project, tmp_path, digest +): + for name in ("pyproject.toml", "uv.lock"): + shutil.copyfile(actual_registry_project / name, tmp_path / name) + lock_path = tmp_path / "uv.lock" + package = next( + p + for p in release_lock.load_toml(lock_path)["package"] + if p["name"] == "packaging" + ) + before_text = lock_path.read_text() + lock_path.write_text(before_text.replace(package["wheels"][0]["hash"], digest)) + before = lock_path.read_bytes() + with pytest.raises(ValueError, match="artifact"): + release_lock.check_release_lock(tmp_path) + assert lock_path.read_bytes() == before diff --git a/tests/test_release_manifests.py b/tests/test_release_manifests.py index 24bb05be..f1c75d17 100644 --- a/tests/test_release_manifests.py +++ b/tests/test_release_manifests.py @@ -44,10 +44,10 @@ PYPROJECT.read_text(), re.MULTILINE, ).group(1) -US_MODEL_VERSION = "1.764.6" -US_BUILT_WITH_MODEL_VERSION = "1.764.6" +US_MODEL_VERSION = "2.2.1" +US_BUILT_WITH_MODEL_VERSION = "2.2.1" US_DATA_RELEASE_VERSION = "0.1.0" -US_DATA_RELEASE_ID = "populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z" +US_DATA_RELEASE_ID = "populace-us-2024-spm-20260915" US_DATA_RELEASE_REVISION = US_DATA_RELEASE_ID US_DATA_RELEASE_PATH = f"releases/{US_DATA_RELEASE_ID}/release_manifest.json" US_DATA_ARTIFACT_REVISION = US_DATA_RELEASE_ID @@ -195,7 +195,7 @@ def test__given_us_manifest__then_has_pinned_model_and_data_packages(self): assert manifest.policyengine_version == POLICYENGINE_VERSION assert manifest.model_package.name == "policyengine-us" assert manifest.model_package.version == US_MODEL_VERSION - assert manifest.data_package.name == "populace-data" + assert manifest.data_package.name == "microcosm-data" assert manifest.data_package.version == US_DATA_RELEASE_VERSION assert manifest.data_package.repo_id == "policyengine/populace-us" assert manifest.data_package.release_manifest_path == US_DATA_RELEASE_PATH diff --git a/tests/test_release_tro_generation.py b/tests/test_release_tro_generation.py new file mode 100644 index 00000000..05f8277e --- /dev/null +++ b/tests/test_release_tro_generation.py @@ -0,0 +1,320 @@ +"""Release regeneration authenticates the reviewed, complete TRACE inputs.""" + +import argparse +import copy +import hashlib +import importlib.util +import json +import shutil +import sys +from pathlib import Path + +import pytest +import requests + +from policyengine.provenance import manifest + +ROOT = Path(__file__).resolve().parents[1] +FIXTURES = ROOT / "tests" / "fixtures" / "release_tros" +sys.path.insert(0, str(ROOT / "scripts")) +import bundle as maintenance # noqa: E402 +import generate_trace_tros as generator # noqa: E402 + + +@pytest.fixture +def release(monkeypatch, tmp_path): + package = tmp_path / "policyengine" + bundle_dir = package / "data" / "bundle" + bundle_dir.mkdir(parents=True) + for name in ("manifest.json", "us.trace.tro.jsonld"): + shutil.copyfile(FIXTURES / name, bundle_dir / name) + # Only the public US snapshot is a repository test fixture. UK is verified + # against its authenticated private release in the release preflight. + source = json.loads((bundle_dir / "manifest.json").read_bytes()) + source["data_releases"].pop("uk") + source["packages"].pop("policyengine-uk") + (bundle_dir / "manifest.json").write_text(json.dumps(source)) + monkeypatch.setattr(generator, "BUNDLE_MANIFEST", bundle_dir / "manifest.json") + monkeypatch.setattr(generator, "BUNDLE_TRO_DIR", bundle_dir) + lock_path = tmp_path / "uv.lock" + shutil.copyfile(FIXTURES / "country-models.lock.toml", lock_path) + monkeypatch.setattr(generator, "BUNDLE_LOCK", lock_path) + monkeypatch.setattr(manifest, "files", lambda _: package) + response = requests.Response() + response.status_code = 200 + response._content = (FIXTURES / "us-release_manifest.json").read_bytes() + monkeypatch.setattr(manifest.requests, "get", lambda *a, **kw: response) + manifest.get_release_manifest.cache_clear() + manifest.get_data_release_manifest.cache_clear() + yield bundle_dir, response + manifest.get_release_manifest.cache_clear() + manifest.get_data_release_manifest.cache_clear() + + +def test_strict_generation_is_complete_and_deterministic(release): + first = generator.generated_tros(strict=True) + assert first == generator.generated_tros(strict=True) + artifacts = json.loads(first[0][1])["@graph"][0]["trov:hasComposition"][ + "trov:hasArtifact" + ] + assert len(artifacts) == 4 + assert any( + item["trov:sha256"] + == hashlib.sha256( + (FIXTURES / "us-release_manifest.json").read_bytes() + ).hexdigest() + for item in artifacts + ) + + +def test_runtime_pin_update_requires_matching_certified_model(release): + from prepare_package_bundle_update import update_package_pins + + path = release[0] / "manifest.json" + original = path.read_bytes() + tro_path = release[0] / "us.trace.tro.jsonld" + reviewed = tro_path.read_bytes() + updated = update_package_pins( + json.loads(original), argparse.Namespace(core=None, us="1.824.1", uk=None) + ) + path.write_text(json.dumps(updated)) + manifest.get_release_manifest.cache_clear() + manifest.get_data_release_manifest.cache_clear() + for action in (generator.regenerate_all, maintenance._check_tros): + with pytest.raises(ValueError, match="runtime.*model"): + action(strict=True) + assert tro_path.read_bytes() == reviewed + # Restoring the actual certified runtime pin admits the same real inputs. + path.write_bytes(original) + manifest.get_release_manifest.cache_clear() + manifest.get_data_release_manifest.cache_clear() + generator.regenerate_all(strict=True) + assert maintenance._check_tros(strict=True) == 0 + + +@pytest.mark.parametrize("field", ["name", "version", "sha256", "wheel_url"]) +def test_runtime_model_identity_must_match_certification(release, field): + path = release[0] / "manifest.json" + payload = json.loads(path.read_bytes()) + payload["packages"]["policyengine-us"][field] = "different" + path.write_text(json.dumps(payload)) + with pytest.raises(ValueError, match="runtime.*model"): + generator.regenerate_all(strict=True) + + +@pytest.mark.parametrize( + "change", ["missing", "version", "url", "hash", "registry", "extra-wheel"] +) +def test_certified_model_must_match_locked_wheel(release, change): + lock_path = generator.BUNDLE_LOCK + text = lock_path.read_text() + model = manifest.get_release_manifest("us").model_package + if change == "missing": + lock_path.unlink() + elif change == "extra-wheel": + record = next( + line for line in text.splitlines() if f'url = "{model.wheel_url}"' in line + ) + lock_path.write_text( + text.replace(record, record + "\n" + record.replace("/e2/", "/aa/")) + ) + else: + old, new = { + "version": ('version = "1.764.6"', 'version = "1.824.1"'), + "url": (model.wheel_url, model.wheel_url.replace("/e2/", "/aa/")), + "hash": (model.sha256, "a" * 64), + "registry": ("https://pypi.org/simple", "https://example.com/simple"), + }[change] + assert old in text + lock_path.write_text(text.replace(old, new)) + path = release[0] / "us.trace.tro.jsonld" + before = path.read_bytes() + with pytest.raises(ValueError, match="lock|registry"): + generator.regenerate_all(strict=True) + assert path.read_bytes() == before + + +@pytest.mark.parametrize("status", [401, 403, 404, 503]) +def test_strict_fetch_failure_preserves_reviewed_tro(release, status): + bundle_dir, response = release + path = bundle_dir / "us.trace.tro.jsonld" + before = path.read_bytes() + response.status_code = status + with pytest.raises(ValueError, match="data release manifest"): + generator.regenerate_all(strict=True) + assert path.read_bytes() == before + + +def test_local_generation_retains_explicit_limited_mode(release, capsys): + release[1].status_code = 404 + assert generator.generated_tros() + assert "writing limited TRO" in capsys.readouterr().err + + +def test_strict_rejects_changed_remote_manifest_bytes(release): + # Equivalent JSON with changed bytes must not silently replace reviewed pins. + release[1]._content += b"\n" + with pytest.raises(ValueError, match="reviewed.*hash"): + generator.generated_tros(strict=True) + + +def test_strict_network_failure_never_writes_limited_output( + release, monkeypatch, capsys +): + path = release[0] / "us.trace.tro.jsonld" + before = path.read_bytes() + + def unavailable(*args, **kwargs): + raise requests.Timeout("fixture timeout") + + monkeypatch.setattr(manifest.requests, "get", unavailable) + with pytest.raises(manifest.DataReleaseManifestUnavailableError): + generator.regenerate_all(strict=True) + assert path.read_bytes() == before + assert "limited TRO" not in capsys.readouterr().err + + +def test_strict_missing_remote_dataset_never_writes_limited_output(release, capsys): + path = release[0] / "us.trace.tro.jsonld" + before = path.read_bytes() + data = json.loads(release[1].content) + del data["artifacts"][data["default_datasets"]["national"]] + release[1]._content = json.dumps(data).encode() + with pytest.raises(ValueError, match="Default dataset"): + generator.regenerate_all(strict=True) + assert path.read_bytes() == before + assert "limited TRO" not in capsys.readouterr().err + + +@pytest.mark.parametrize("remove_models", [False, True]) +def test_strict_cannot_skip_missing_country_declarations(release, remove_models): + path = release[0] / "manifest.json" + payload = json.loads(path.read_bytes()) + payload["data_releases"] = {} + if remove_models: + payload["packages"] = {} + path.write_text(json.dumps(payload)) + with pytest.raises(ValueError, match="every bundled country"): + generator.generated_tros(strict=True) + + +@pytest.mark.parametrize( + "change", ["dataset", "hash", "wheel", "certification", "revision"] +) +def test_strict_rejects_incomplete_or_drifted_certification( + release, monkeypatch, change +): + country = copy.deepcopy(manifest.get_release_manifest("us")) + if change == "dataset": + country.certified_data_artifact.dataset = "missing" + elif change == "hash": + country.certified_data_artifact.sha256 = "a" * 64 + elif change == "wheel": + country.model_package.sha256 = None + elif change == "certification": + country.certification.certified_for_model_version = "0.0.0" + else: + country.data_package.release_manifest_revision = "main" + monkeypatch.setattr(generator, "get_release_manifest", lambda _: country) + with pytest.raises(ValueError): + generator.generated_tros(strict=True) + + +@pytest.mark.parametrize("change", ["missing", "limited", "location"]) +def test_strict_requires_prepared_reviewed_sidecar(release, change): + path = release[0] / "us.trace.tro.jsonld" + if change == "missing": + path.unlink() + elif change == "limited": + release[1].status_code = 404 + path.write_bytes(generator.generated_tros()[0][1]) + release[1].status_code = 200 + else: + path.write_text(path.read_text().replace("resolve/populace-", "resolve/other-")) + with pytest.raises(ValueError, match="reviewed"): + generator.generated_tros(strict=True) + + +def test_strict_all_countries_validate_before_any_write(release, monkeypatch): + path = release[0] / "us.trace.tro.jsonld" + before = path.read_bytes() + bundle_path = release[0] / "manifest.json" + bundle = json.loads(bundle_path.read_bytes()) + bundle["data_releases"]["zz"] = bundle["data_releases"]["us"] + bundle_path.write_text(json.dumps(bundle)) + with pytest.raises(ValueError): + generator.regenerate_all(strict=True) + assert path.read_bytes() == before + + +def test_reviewed_sidecar_allows_only_version_regeneration(release): + spec = importlib.util.spec_from_file_location( + "release_bump", ROOT / ".github/bump_version.py" + ) + bump = importlib.util.module_from_spec(spec) + spec.loader.exec_module(bump) + before = generator.generated_tros(strict=True)[0][1] + bump.sync_bundle_versions(release[0] / "manifest.json", "6.0.0") + manifest.get_release_manifest.cache_clear() + manifest.get_data_release_manifest.cache_clear() + after = generator.generated_tros(strict=True)[0][1] + assert before != after + assert b'"schema:softwareVersion": "6.0.0"' in after + assert b'"pe:emittedIn": "repository-bundle"' in after + # New version bytes are allowed, but regenerate/check must agree exactly. + generator.regenerate_all(strict=True) + assert generator.generated_tros(strict=True)[0][1] == after + + +def test_strict_cli_requires_tros_and_checks_exact_bytes(release, monkeypatch): + import generate_bundle_artifacts + + with pytest.raises(SystemExit) as exc: + maintenance.main(["check", "--strict-tros"]) + assert exc.value.code == 2 + monkeypatch.setattr(generate_bundle_artifacts, "generate", lambda **kw: 0) + monkeypatch.setattr(maintenance, "REPO_ROOT", release[0].parents[2]) + assert maintenance.main(["check", "--include-tros", "--strict-tros"]) == 1 + assert maintenance.main(["generate", "--include-tros", "--strict-tros"]) == 0 + assert maintenance.main(["check", "--include-tros", "--strict-tros"]) == 0 + + +def test_release_workflows_gate_complete_inputs_and_lock(): + import yaml + + pr = yaml.safe_load((ROOT / ".github/workflows/pr_code_changes.yaml").read_text()) + push = yaml.safe_load((ROOT / ".github/workflows/push.yaml").read_text()) + assert "pull_request_target" not in pr.get("on", pr.get(True, {})) + assert ( + "github.event.pull_request.head.repo.full_name == github.repository" + in pr["jobs"]["BundleVerification"]["if"] + ) + for job in ( + push["jobs"]["Versioning"], + push["jobs"]["Publish"], + ): + commands = "\n".join(step.get("run", "") for step in job["steps"]) + assert "check --published-spm --include-tros --strict-tros" in commands + assert 'if [[ -z "${HUGGING_FACE_TOKEN:-}" ]]' in commands + assert "python scripts/release_lock.py" in commands + assert commands.index( + "python scripts/check_release_credentials.py" + ) < commands.index("check --published-spm") + # The pull-request job checks only the reviewed registry lock. The + # read-only credential, published-measurement and TRACE sidecar gates + # depend on the release workflow regenerating sidecars and publishing + # wheels first, so a pull request can never satisfy them. + pr_commands = "\n".join( + step.get("run", "") for step in pr["jobs"]["BundleVerification"]["steps"] + ) + assert "python scripts/release_lock.py" in pr_commands + assert "--published-spm" not in pr_commands + commands = "\n".join( + step.get("run", "") for step in push["jobs"]["Versioning"]["steps"] + ) + assert commands.index("check --published-spm") < commands.index("make changelog") + assert ( + commands.index("make changelog") + < commands.index("release_lock.py --refresh") + < commands.index("generate --include-tros --strict-tros") + ) diff --git a/tests/test_spm_bundle_bootstrap.py b/tests/test_spm_bundle_bootstrap.py new file mode 100644 index 00000000..bb17c402 --- /dev/null +++ b/tests/test_spm_bundle_bootstrap.py @@ -0,0 +1,125 @@ +"""Exercise the bundle commands as CI runs them before runtime installation.""" + +import json +import os +import shutil +import subprocess +import sys +from pathlib import Path + +import pytest +import yaml + +REPO_ROOT = Path(__file__).resolve().parents[1] + + +@pytest.fixture +def bootstrap_checkout(tmp_path): + paths = ( + "scripts/bundle.py", + "scripts/generate_bundle_artifacts.py", + "src/policyengine/provenance/bundle_validation.py", + "src/policyengine/data/bundle/manifest.json", + "pyproject.toml", + ) + for path in paths: + destination = tmp_path / path + destination.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(REPO_ROOT / path, destination) + return tmp_path + + +def _bootstrap(checkout, *arguments): + env = os.environ.copy() + env.pop("PYTHONPATH", None) + env.pop("PYTHONHOME", None) + return subprocess.run( + [sys.executable, "-S", *arguments], + cwd=checkout, + env=env, + capture_output=True, + text=True, + timeout=30, + check=False, + ) + + +@pytest.mark.parametrize( + "arguments", + [ + ("scripts/bundle.py", "check"), + ("scripts/bundle.py", "generate"), + ("scripts/generate_bundle_artifacts.py", "--check"), + ("scripts/generate_bundle_artifacts.py",), + ], +) +def test_bundle_commands_work_without_site_packages(bootstrap_checkout, arguments): + manifest = bootstrap_checkout / "src/policyengine/data/bundle/manifest.json" + pyproject = bootstrap_checkout / "pyproject.toml" + before = (manifest.read_bytes(), pyproject.read_bytes()) + result = _bootstrap(bootstrap_checkout, *arguments) + assert result.returncode == 0, result.stdout + result.stderr + assert (manifest.read_bytes(), pyproject.read_bytes()) == before + + +def test_dependency_free_check_keeps_published_spm_gate(bootstrap_checkout): + manifest = bootstrap_checkout / "src/policyengine/data/bundle/manifest.json" + staged = json.loads(manifest.read_text()) + staged["packages"].pop("spm-calculator", None) + manifest.write_text(json.dumps(staged)) + result = _bootstrap( + bootstrap_checkout, "scripts/bundle.py", "check", "--published-spm" + ) + assert result.returncode == 2 + assert "no published spm-calculator pin" in result.stderr + assert "ModuleNotFoundError" not in result.stderr + + +@pytest.mark.parametrize( + "change", + [ + {"forecast_content_sha256": "unverified"}, + {"scenario": ""}, + {"geography_kind": "metro", "geography_id": None}, + {"as_of": "2026-02-30"}, + {"provider": "/tmp/unsupported.json"}, + ], +) +def test_dependency_free_generation_validates_before_writing( + bootstrap_checkout, change +): + manifest = bootstrap_checkout / "src/policyengine/data/bundle/manifest.json" + invalid = json.loads(manifest.read_text()) + invalid["measurements"]["spm"].update(change) + manifest.write_text(json.dumps(invalid)) + before = manifest.read_bytes() + result = _bootstrap(bootstrap_checkout, "scripts/bundle.py", "generate") + assert result.returncode == 2 + assert "ModuleNotFoundError" not in result.stderr + assert manifest.read_bytes() == before + + +def test_release_checks_published_spm_before_publication_and_after_pypi_visibility(): + workflow = yaml.safe_load((REPO_ROOT / ".github/workflows/push.yaml").read_text()) + publish_steps = workflow["jobs"]["Publish"]["steps"] + # Publish installs the package first, so its gate is the full bundle check + # rather than the dependency-light one NotifyConsumers has to use. + prepublication_gate = next( + index + for index, step in enumerate(publish_steps) + if "bundle.py check --published-spm" in step.get("run", "") + ) + publication = next( + index + for index, step in enumerate(publish_steps) + if step.get("uses", "").startswith("pypa/gh-action-pypi-publish@") + ) + assert prepublication_gate < publication + notify = workflow["jobs"]["NotifyConsumers"] + assert "Publish" in notify["needs"] + steps = notify["steps"] + runs = [step.get("run", "") for step in steps] + wait = runs.index("bash .github/wait-for-pypi.sh") + strict_gate = runs.index("python -S scripts/bundle.py check --published-spm") + dispatch = runs.index("bash .github/dispatch-policyengine-release.sh") + assert wait < strict_gate < dispatch diff --git a/tests/test_spm_bundle_tooling.py b/tests/test_spm_bundle_tooling.py new file mode 100644 index 00000000..ea3c0097 --- /dev/null +++ b/tests/test_spm_bundle_tooling.py @@ -0,0 +1,302 @@ +"""Measurement pins are independent of data certification and registry publication.""" + +import copy +import hashlib +import sys +import zipfile +from pathlib import Path +from types import SimpleNamespace + +import pytest +from packaging.version import InvalidVersion, Version + +from policyengine import bundle +from policyengine.provenance.bundle_validation import ( + _is_normalized_version, + validate_bundle_measurements, +) +from policyengine.provenance.spm_configuration import SPMMeasurementConfiguration + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts")) +import bundle as maintenance # noqa: E402 +from generate_bundle_artifacts import ( # noqa: E402 + load_bundle_manifest, + normalized_manifest, +) +from spm_bundle import ( # noqa: E402 + configure_spm, + development_manifest, + published_calculator_component, + write_development_manifest, +) + + +@pytest.fixture +def canonical_bundle(): + # Read the packaged JSON even when an explicitly enabled development plugin + # replaces runtime get_current_bundle for the household test process. + return load_bundle_manifest() + + +@pytest.mark.parametrize( + "change", + [ + {"forecast_content_sha256": None}, + {"forecast_content_sha256": "a" * 63}, + {"forecast_content_sha256": "A" * 64}, + {"scenario": None}, + {"scenario": " "}, + {"scenario": "ce_trend "}, + {"scenario": "ce trend"}, + {"geography_kind": "state"}, + {"geography_kind": "metro", "geography_id": None}, + {"geography_kind": "metro", "geography_id": ""}, + {"geography_kind": "national", "geography_id": "31080"}, + {"county_vintage": 2020}, + {"as_of": "2026-02-30"}, + {"as_of": "20260909"}, + {"forecast_path": "/tmp/provider.json"}, + {"provider": "fallback"}, + ], +) +def test_malformed_measurement_defaults_fail_before_bundle_use( + canonical_bundle, change +): + canonical_bundle["measurements"]["spm"].update(change) + with pytest.raises(ValueError): + SPMMeasurementConfiguration.model_validate( + canonical_bundle["measurements"]["spm"] + ) + with pytest.raises(ValueError): + normalized_manifest(canonical_bundle) + with pytest.raises(bundle.BundleError): + bundle._normalise_manifest(canonical_bundle) + + +@pytest.mark.parametrize("measurements", [None, [], "spm", {"spm": None}]) +def test_measurements_require_an_object(canonical_bundle, measurements): + canonical_bundle["measurements"] = measurements + with pytest.raises(ValueError): + validate_bundle_measurements(canonical_bundle) + + +def test_historical_bundle_without_measurements_still_loads(canonical_bundle): + canonical_bundle.pop("measurements") + validate_bundle_measurements(canonical_bundle) + assert ( + bundle._normalise_manifest(canonical_bundle)["bundle_version"] + == canonical_bundle["bundle_version"] + ) + + +def test_staged_config_preserves_certification_and_fails_release_gate(canonical_bundle): + # The canonical bundle now ships the promoted runtime pin, so rebuild the + # pre-promotion staging state this guard is about. + canonical_bundle["packages"]["spm-calculator"]["role"] = "country_dependency" + before = copy.deepcopy(canonical_bundle) + updated = configure_spm( + canonical_bundle, canonical_bundle["measurements"]["spm"], None + ) + assert updated["data_releases"] == before["data_releases"] + assert updated["packages"] == before["packages"] + assert updated["packages"]["policyengine-us"]["version"] == "2.2.1" + validate_bundle_measurements(updated) + with pytest.raises(ValueError, match="promotion pending"): + validate_bundle_measurements(updated, require_published_spm=True) + + +def _calculator_pin(): + # Synthetic registry metadata is a unit fixture, not publication evidence. + return { + "name": "spm-calculator", + "version": "1.0.0", + "role": "runtime_dependency", + "country": "us", + "sha256": "a" * 64, + "wheel_url": "https://files.pythonhosted.org/unit-test/spm.whl", + } + + +def test_published_runtime_dependency_includes_us_and_preserves_uk(canonical_bundle): + updated = configure_spm( + canonical_bundle, canonical_bundle["measurements"]["spm"], _calculator_pin() + ) + assert "spm-calculator==1.0.0" in bundle.bundle_install_requirements( + updated, countries=["us"] + ) + assert "spm-calculator==1.0.0" not in bundle.bundle_install_requirements( + updated, countries=["uk"] + ) + assert updated["extras"]["uk"] == canonical_bundle["extras"]["uk"] + validate_bundle_measurements(updated, require_published_spm=True) + + +@pytest.mark.parametrize( + "change", + [ + {"sha256": None}, + {"role": "country_model"}, + {"country": "uk"}, + {"version": ">=1.0"}, + {"version": None}, + {"install_requirement": "spm-calculator>=1"}, + {"installable": False}, + {"markers": "python_version < '0'"}, + ], +) +def test_calculator_requires_scoped_runtime_identity(canonical_bundle, change): + component = {**_calculator_pin(), **change} + with pytest.raises(ValueError): + configure_spm( + canonical_bundle, canonical_bundle["measurements"]["spm"], component + ) + + +def _registry_responses(monkeypatch, wheel_bytes, download_bytes): + import spm_bundle + + expected = hashlib.sha256(wheel_bytes).hexdigest() + wheel_url = ( + "https://files.pythonhosted.org/unit-test/spm_calculator-1.0.0-py3-none-any.whl" + ) + payload = { + "info": {"version": "1.0.0"}, + "urls": [ + { + "packagetype": "bdist_wheel", + "filename": "spm_calculator-1.0.0-py3-none-any.whl", + "url": wheel_url, + "digests": {"sha256": expected}, + } + ], + } + calls = [] + + def get(url, **kwargs): + calls.append(url) + return SimpleNamespace( + json=lambda: payload, content=download_bytes, raise_for_status=lambda: None + ) + + monkeypatch.setattr(spm_bundle.requests, "get", get) + return expected, calls, wheel_url + + +def test_registry_pin_hashes_downloaded_bytes(monkeypatch): + expected, calls, url = _registry_responses( + monkeypatch, b"test-wheel", b"test-wheel" + ) + component = published_calculator_component("1.0.0") + assert component["sha256"] == expected + assert component["role"] == "runtime_dependency" + assert calls == ["https://pypi.org/pypi/spm-calculator/1.0.0/json", url] + + +def test_registry_pin_rejects_changed_wheel_bytes(monkeypatch): + _registry_responses(monkeypatch, b"expected-wheel", b"changed-wheel") + with pytest.raises(ValueError, match="do not match"): + published_calculator_component("1.0.0") + + +def _write_wheel(tmp_path, name, version): + path = tmp_path / f"{name}-{version}-py3-none-any.whl" + with zipfile.ZipFile(path, "w") as archive: + archive.writestr( + f"{name}-{version}.dist-info/METADATA", + f"Name: {name}\nVersion: {version}\n", + ) + return path + + +def test_development_fixture_uses_actual_wheel_pins_without_certification( + canonical_bundle, tmp_path +): + country = _write_wheel(tmp_path, "policyengine-us", "1.824.7") + calculator = _write_wheel(tmp_path, "spm-calculator", "1.0.0") + before = copy.deepcopy(canonical_bundle) + development = development_manifest(canonical_bundle, country, calculator) + assert development["development"]["data_certification"] == "not_certified" + assert development["development"]["promotion_status"] == "pending" + assert development["packages"]["policyengine-us"]["version"] == "1.824.7" + assert ( + development["packages"]["spm-calculator"]["sha256"] + == hashlib.sha256(calculator.read_bytes()).hexdigest() + ) + assert "certification" not in development["data_releases"]["us"] + assert "certified_data_artifact" not in development["data_releases"]["us"] + assert development["data_releases"]["uk"] == before["data_releases"]["uk"] + assert canonical_bundle == before + with pytest.raises(ValueError, match="Development fixtures"): + validate_bundle_measurements(development, require_published_spm=True) + + +def test_development_fixture_cannot_replace_packaged_manifest( + canonical_bundle, tmp_path +): + from generate_bundle_artifacts import BUNDLE_MANIFEST + + with pytest.raises(ValueError, match="cannot overwrite"): + write_development_manifest( + canonical_bundle, + tmp_path / "missing", + tmp_path / "missing", + BUNDLE_MANIFEST, + ) + + +def test_cli_rejects_invalid_configuration_without_writing( + monkeypatch, canonical_bundle +): + import generate_bundle_artifacts + + monkeypatch.setattr( + generate_bundle_artifacts, "load_bundle_manifest", lambda: canonical_bundle + ) + monkeypatch.setattr( + generate_bundle_artifacts, + "write_bundle_manifest", + lambda _: pytest.fail("Invalid configuration wrote a manifest"), + ) + with pytest.raises(SystemExit) as error: + maintenance.main( + [ + "set-spm", + "--forecast-content-sha256", + "invalid", + "--scenario", + "ce_trend", + ] + ) + assert error.value.code == 2 + + +@pytest.mark.parametrize( + "version", + [ + "1", + "1.0.0", + "1.0rc1", + "1.0.post0.dev1", + "2!1.0+linux.x86.1", + "1.0+01", + "1.0+Linux", + "01.0", + "1.01", + "0!1.0", + "v1.0", + "1.0RC1", + "1.0-1", + "1.0.post", + "1.0.dev", + "1.*", + ">=1.0", + "", + None, + ], +) +def test_dependency_free_exact_version_validation_matches_packaging(version): + try: + expected = isinstance(version, str) and str(Version(version)) == version + except InvalidVersion: + expected = False + assert _is_normalized_version(version) is expected diff --git a/tests/test_spm_decile.py b/tests/test_spm_decile.py new file mode 100644 index 00000000..db217b61 --- /dev/null +++ b/tests/test_spm_decile.py @@ -0,0 +1,44 @@ +"""Decile convenience construction preserves explicit measurement choices.""" + +from types import SimpleNamespace + +import pytest + +from policyengine.core.spm import SPMSelection +from policyengine.outputs import decile_impact + + +def test_decile_factory_uses_same_explicit_selection_for_baseline_and_reform( + monkeypatch, +): + selection = SPMSelection(geography_kind="national", scenario="zero_real") + created = [] + + def construct(**kwargs): + simulation = SimpleNamespace(**kwargs, ensure=lambda: None) + created.append(simulation) + return simulation + + class AnalysisReached(Exception): + pass + + def analyse(baseline, reform, **kwargs): + assert baseline.spm == reform.spm == selection + raise AnalysisReached + + monkeypatch.setattr(decile_impact, "Simulation", construct) + monkeypatch.setattr(decile_impact, "_prepare_decile_analysis", analyse) + with pytest.raises(AnalysisReached): + decile_impact.calculate_decile_impacts( + dataset=object(), tax_benefit_model_version=object(), spm=selection + ) + assert len(created) == 2 + + +def test_decile_supplied_simulations_cannot_silently_ignore_selection(): + with pytest.raises(ValueError, match="supplied simulations"): + decile_impact.calculate_decile_impacts( + baseline_simulation=object(), + reform_simulation=object(), + spm=SPMSelection(geography_kind="national"), + ) diff --git a/tests/test_spm_development_authentication.py b/tests/test_spm_development_authentication.py new file mode 100644 index 00000000..05da8c7b --- /dev/null +++ b/tests/test_spm_development_authentication.py @@ -0,0 +1,95 @@ +"""Candidate metadata must authenticate the code Python actually resolves.""" + +import json +import os +import subprocess +import sys +from pathlib import Path + +import pytest + + +def _probe(tmp_path, payload, setup): + manifest = tmp_path / "development.json" + manifest.write_text(json.dumps(payload)) + env = os.environ.copy() + env.pop("PYTHONPATH", None) + env["PYTHONDONTWRITEBYTECODE"] = "1" + script = "\n".join( + [ + "import importlib.util, json, sys, types", + "from pathlib import Path", + "from tests.fixtures.spm_development import _verify_local_wheel_installations", + f"payload = json.loads(Path({str(manifest)!r}).read_text())", + setup, + "_verify_local_wheel_installations(payload)", + "assert 'policyengine_us' not in sys.modules", + "assert 'spm_calculator' not in sys.modules", + "print('Candidate import sources authenticated before country imports')", + ] + ) + return subprocess.run( + [sys.executable, "-c", script], + cwd=Path(__file__).resolve().parents[1], + env=env, + capture_output=True, + text=True, + timeout=60, + ) + + +@pytest.fixture +def candidate_manifest(): + from tests.fixtures.spm_development import _DEVELOPMENT_MANIFEST + + if _DEVELOPMENT_MANIFEST is None: + pytest.skip("Requires the explicit local-wheel development manifest") + return _DEVELOPMENT_MANIFEST + + +def test_authenticates_real_candidate_before_import(tmp_path, candidate_manifest): + result = _probe(tmp_path, candidate_manifest, "") + assert result.returncode == 0, result.stderr + assert "authenticated before country imports" in result.stdout + + +@pytest.mark.parametrize( + "import_name", ["policyengine_us", "spm_calculator", "policyengine_core"] +) +def test_rejects_pythonpath_shadow_before_executing_it( + tmp_path, candidate_manifest, import_name +): + shadow = tmp_path / "shadow" + package = shadow / import_name + package.mkdir(parents=True) + (package / "__init__.py").write_text( + "raise AssertionError('Shadow package was executed')\n" + ) + result = _probe( + tmp_path, candidate_manifest, f"sys.path.insert(0, {str(shadow)!r})" + ) + assert result.returncode != 0 + assert f"Unexpected import source for {import_name}" in result.stderr + assert "Shadow package was executed" not in result.stderr + + +def test_rejects_preloaded_submodule_outside_authenticated_wheel( + tmp_path, candidate_manifest +): + source = tmp_path / "foreign_spm.py" + source.write_text("# Foreign module, never executed\n") + result = _probe( + tmp_path, + candidate_manifest, + "\n".join( + [ + "module = types.ModuleType('policyengine_us.spm')", + f"module.__file__ = {str(source)!r}", + "module.__spec__ = importlib.util.spec_from_file_location(" + "module.__name__, module.__file__)", + "sys.modules[module.__name__] = module", + ] + ), + ) + assert result.returncode != 0 + assert "Unexpected loaded import source for policyengine_us.spm" in result.stderr diff --git a/tests/test_spm_household.py b/tests/test_spm_household.py new file mode 100644 index 00000000..5e63ca8e --- /dev/null +++ b/tests/test_spm_household.py @@ -0,0 +1,398 @@ +"""Real household acceptance for the canonical SPM measurement contract. + +Development runs use the explicit local-wheel manifest pytest plugin; these +tests never replace the packaged production manifest or claim certification. +""" + +import json +import math +from copy import deepcopy + +import numpy as np +import pytest + +# These cases must run against the real published calculator module, never a +# substitute; skip only if the pinned module is absent from the environment. +pytest.importorskip("spm_calculator.errors") +pytest.importorskip("spm_calculator.release") +pytest.importorskip("spm_calculator.rolling_forecast") +pytest.importorskip("spm_calculator.policyengine_adapter") + +from spm_calculator.errors import SPMInputError # noqa: E402 +from spm_calculator.release import SPMUnit # noqa: E402 +from spm_calculator.rolling_forecast import load_forecast # noqa: E402 + +import policyengine as pe +from policyengine.tax_benefit_models.us.spm import resolve_spm_selection + +AMOUNT_VARIABLES = { + "spm_unit_reference_spm_threshold": "reference_threshold", + "spm_unit_unadjusted_spm_threshold": "unadjusted_threshold", + "spm_unit_geographic_adjustment": "geographic_factor", + "spm_unit_spm_threshold": "threshold", + "spm_unit_spm_threshold_housing_portion": "housing_portion", +} + +# Spell out the public boundary independently of the adapter's validation set +# so accidentally removing an owned output cannot also remove its test case. +FORMULA_OWNED_VARIABLES = [ + *AMOUNT_VARIABLES, + "spm_measurement_adults", + "spm_measurement_children", + "spm_unit_capped_housing_subsidy", + "spm_unit_net_income", + "spm_unit_benefits", + "spm_unit_is_in_spm_poverty", + "spm_unit_is_in_deep_spm_poverty", + "poverty_line", + "poverty_gap", +] + + +def household_inputs(**overrides): + """One adult with observed location, tenure and earnings, no baked outputs.""" + values = { + "people": [{"age": 40, "employment_income": 50_000}], + "tax_unit": {"filing_status": "SINGLE"}, + "spm_unit": {"spm_unit_tenure_type": "RENTER"}, + "household": {"state_code": "CA", "county_fips": "06037"}, + "year": 2026, + } + values.update(overrides) + return values + + +@pytest.mark.parametrize("year", [2024, 2026, 2035]) +def test_explicit_national_amounts_and_component_provenance(year, tmp_path): + result = pe.us.calculate_household( + **household_inputs(year=year), + spm={"geography_kind": "national"}, + extra_variables=list(AMOUNT_VARIABLES), + ) + forecast = load_forecast() + expected = forecast.calculate_unit( + SPMUnit( + unit_id="wrapper-acceptance", + year=year, + num_adults=1, + num_children=0, + tenure="renter", + geography_kind="national", + ), + scenario=resolve_spm_selection()["scenario"], + ) + # Each exported amount is the canonical final value cast once to the + # country's float32 storage, rather than recombined stored intermediates. + for variable, field in AMOUNT_VARIABLES.items(): + assert result.spm_unit[variable] == float(np.float32(expected[field])) + assert math.isfinite(result.household.household_net_income) + assert result.to_dict()["provenance"]["spm_config"] == resolve_spm_selection( + {"geography_kind": "national"} + ) + receipt = result.to_dict()["provenance"]["spm"] + assert ( + receipt["forecast_sha256"] == resolve_spm_selection()["forecast_content_sha256"] + ) + assert receipt["geography_kind"] == "national" + assert receipt["years"][str(year)] == forecast.entry( + year, scenario=receipt["scenario"] + ) + assert receipt["geographies"] + assert all(item["county_assignment"] is None for item in receipt["geographies"]) + assert receipt["runtime_versions"]["spm-calculator"] + assert json.loads(json.dumps(result.to_dict())) == result.to_dict() + path = result.write(tmp_path / f"household-{year}.json") + assert json.loads(path.read_text())["provenance"]["spm"] == receipt + + +def test_default_county_and_explicit_metro_resolve_the_same_area(): + inputs = household_inputs() + county = pe.us.calculate_household(**inputs, extra_variables=list(AMOUNT_VARIABLES)) + receipt = county.to_dict()["provenance"]["spm"] + assignment = receipt["geographies"][0]["county_assignment"] + expected_assignment = load_forecast().resolve_county( + 2026, "06037", county_vintage="2020", scenario=receipt["scenario"] + ) + assert assignment == expected_assignment + assert receipt["geography_kind"] == "county" + metro = pe.us.calculate_household( + **inputs, + spm={"geography_kind": "metro", "geography_id": assignment["area_id"]}, + extra_variables=list(AMOUNT_VARIABLES), + ) + for variable in AMOUNT_VARIABLES: + assert county.spm_unit[variable] == metro.spm_unit[variable] + assert inputs == household_inputs() + + +@pytest.mark.parametrize( + "location,settings,code", + [ + ({"state_code": "CA"}, {}, "SPM_GEOGRAPHY_REQUIRED"), + ( + {"state_code": "CA", "county_fips": "99999"}, + {}, + "SPM_GEOGRAPHY_UNAVAILABLE", + ), + ( + {"state_code": "CA"}, + {"geography_kind": "metro", "geography_id": "unavailable-area"}, + "SPM_GEOGRAPHY_UNAVAILABLE", + ), + ], +) +def test_default_resource_outputs_require_a_real_geography(location, settings, code): + with pytest.raises(SPMInputError) as caught: + pe.us.calculate_household(**household_inputs(household=location), spm=settings) + assert caught.value.code == code + assert caught.value.to_dict()["code"] == code + + +RESOURCE_VARIABLES = ( + "household_net_income", + "household_benefits", + "marginal_tax_rate", + "household_income_decile", + "equiv_household_net_income", +) + + +def test_state_only_graphs_require_geography_only_where_measurement_is_used( + monkeypatch, +): + """Geography is demanded by exactly the results that use the measurement. + + Ordinary benefits and income use the actual housing award independently of + SPM geography. The country's cap consults the canonical housing portion only + for units allocated assistance, so assisted SPM resources require geography. + The threshold and SPM poverty status require geography either way. + """ + from policyengine.tax_benefit_models.us.model import us_latest + + # Limit only this test's requested outputs. The public wrapper continues to + # include resources and SPM poverty by default. + monkeypatch.setattr(us_latest, "entity_variables", {"tax_unit": ["income_tax"]}) + inputs = household_inputs(household={"state_code": "CA"}) + # Match the country integration control: computed HUD award and contribution, + # with low enough earnings for a positive national/county SPM housing cap. + assisted = household_inputs( + year=2024, + people=[{"age": 40, "employment_income": 24_000, "pre_subsidy_rent": 36_000}], + household={"state_code": "CA", "pha_payment_standard": 36_000}, + spm_unit={ + "spm_unit_tenure_type": "RENTER", + "receives_housing_assistance": True, + }, + ) + + # A genuinely tax-only graph never consults the measurement. + tax = pe.us.calculate_household(**inputs, extra_variables=["housing_assistance"]) + assert tax.tax_unit.income_tax > 0 + assert tax.spm_unit.housing_assistance == 0 + assert tax.to_dict()["provenance"]["spm"]["years"] == {} + + # Unassisted: the capped subsidy is zero by construction, so every resource + # output computes on state alone and records an empty measurement receipt. + unassisted_cap = pe.us.calculate_household( + **inputs, extra_variables=["spm_unit_capped_housing_subsidy"] + ) + assert unassisted_cap.spm_unit.spm_unit_capped_housing_subsidy == 0 + assert unassisted_cap.to_dict()["provenance"]["spm"]["years"] == {} + for variable in RESOURCE_VARIABLES: + computed = pe.us.calculate_household(**inputs, extra_variables=[variable]) + assert computed.to_dict()["provenance"]["spm"]["years"] == {} + + # Assisted ordinary resources still use the actual award, without an SPM cap. + assisted_ordinary = {} + for variable in RESOURCE_VARIABLES: + computed = pe.us.calculate_household(**assisted, extra_variables=[variable]) + entity = ( + computed.person[0] + if variable == "marginal_tax_rate" + else computed.household + ) + assisted_ordinary[variable] = entity[variable] + assert math.isfinite(entity[variable]) + assert computed.to_dict()["provenance"]["spm"]["years"] == {} + + # Only the assisted SPM resource graph needs geography for its housing cap: + # the five variables the shipped contract names, and nothing household-level. + for variable in ( + "spm_unit_capped_housing_subsidy", + "spm_unit_benefits", + "spm_unit_net_income", + "spm_unit_oecd_equiv_net_income", + "spm_unit_income_decile", + ): + with pytest.raises(SPMInputError) as caught: + pe.us.calculate_household(**assisted, extra_variables=[variable]) + assert caught.value.code == "SPM_GEOGRAPHY_REQUIRED" + + # SPM measurement always requires geography, assisted or not. + for situation in (inputs, assisted): + for variable in ("spm_unit_spm_threshold", "spm_unit_is_in_spm_poverty"): + with pytest.raises(SPMInputError) as caught: + pe.us.calculate_household(**situation, extra_variables=[variable]) + assert caught.value.code == "SPM_GEOGRAPHY_REQUIRED" + + # Preserve the unassisted national and county controls. + for settings, located_inputs in ( + ({"geography_kind": "national"}, inputs), + ({"geography_kind": "county"}, household_inputs()), + ): + located = pe.us.calculate_household( + **located_inputs, + spm=settings, + extra_variables=["household_net_income", "marginal_tax_rate"], + ) + assert math.isfinite(located.household.household_net_income) + assert math.isfinite(located.person[0].marginal_tax_rate) + + # Persist assisted positive controls through the same public wrapper path. + assisted_county = deepcopy(assisted) + assisted_county["household"]["county_fips"] = "06037" + actual_awards = [] + for settings, located_inputs in ( + ({"geography_kind": "national"}, assisted), + ({"geography_kind": "county"}, assisted_county), + ): + located = pe.us.calculate_household( + **located_inputs, + spm=settings, + extra_variables=[ + *RESOURCE_VARIABLES, + "housing_assistance", + "spm_unit_allocated_housing_subsidy", + "spm_unit_allocated_tenant_payment", + "spm_unit_capped_housing_subsidy", + "spm_unit_benefits", + "spm_unit_net_income", + ], + ) + subsidy = located.spm_unit.spm_unit_allocated_housing_subsidy + capped = located.spm_unit.spm_unit_capped_housing_subsidy + assert 0 < capped < subsidy + assert located.spm_unit.spm_unit_allocated_tenant_payment > 0 + assert math.isfinite(located.spm_unit.spm_unit_benefits) + assert math.isfinite(located.spm_unit.spm_unit_net_income) + actual_awards.append(located.spm_unit.housing_assistance) + for variable in RESOURCE_VARIABLES: + entity = ( + located.person[0] + if variable == "marginal_tax_rate" + else located.household + ) + assert entity[variable] == assisted_ordinary[variable] + receipt = located.to_dict()["provenance"]["spm"] + assert receipt["years"][str(assisted["year"])] + assert receipt["geography_kind"] == settings["geography_kind"] + assert receipt["geographies"] + assert actual_awards[0] == actual_awards[1] > 0 + + +def test_adultless_measurement_has_a_structured_composition_error(): + with pytest.raises(SPMInputError) as caught: + pe.us.calculate_household(**household_inputs(people=[{"age": 8}])) + assert caught.value.code == "SPM_COMPOSITION_REQUIRED" + assert caught.value.to_dict()["code"] == "SPM_COMPOSITION_REQUIRED" + + +@pytest.mark.parametrize("variable", sorted(FORMULA_OWNED_VARIABLES)) +def test_formula_owned_household_inputs_are_rejected(variable): + with pytest.raises(ValueError, match="computed outputs supplied as inputs"): + pe.us.calculate_household( + **household_inputs(spm_unit={variable: 1}), + ) + + +@pytest.mark.parametrize("variable", sorted(FORMULA_OWNED_VARIABLES)) +def test_formula_owned_axes_are_rejected(variable): + with pytest.raises(ValueError, match="computed outputs supplied as inputs"): + pe.us.calculate_household( + **household_inputs(), + axes=[{"name": variable, "min": 0, "max": 1, "count": 2}], + ) + + +@pytest.mark.parametrize( + "adult", + [ + {"age": 40, "is_adult": False}, + {"age": 16, "is_adult": False, "is_spm_independent_minor_role": True}, + ], +) +def test_generic_adult_inputs_do_not_override_measurement_counts(adult): + result = pe.us.calculate_household( + **household_inputs( + people=[adult, {"age": 8}], + spm_unit={"spm_unit_count_adults": 7, "spm_unit_count_children": 9}, + ), + extra_variables=[ + "spm_unit_count_adults", + "spm_unit_count_children", + "spm_measurement_adults", + "spm_measurement_children", + ], + ) + assert result.person[0].is_adult == 0 + assert result.spm_unit.spm_unit_count_adults == 7 + assert result.spm_unit.spm_unit_count_children == 9 + assert result.spm_unit.spm_measurement_adults == 1 + assert result.spm_unit.spm_measurement_children == 1 + + +def test_axes_reforms_and_scenarios_keep_detached_receipts(): + inputs = household_inputs() + selection = {"geography_kind": "national", "scenario": "ce_trend"} + baseline = pe.us.calculate_household( + **inputs, spm=selection, extra_variables=["spm_unit_spm_threshold"] + ) + baseline_receipt = deepcopy(baseline.to_dict()["provenance"]["spm"]) + axes = pe.us.calculate_household( + **inputs, + spm=selection, + axes=[{"name": "employment_income", "min": 40_000, "max": 60_000, "count": 3}], + extra_variables=["spm_unit_spm_threshold"], + ) + assert axes.person[0].employment_income == [40_000, 50_000, 60_000] + assert ( + axes.spm_unit.spm_unit_spm_threshold + == [baseline.spm_unit.spm_unit_spm_threshold] * 3 + ) + assert ( + axes.household.household_net_income[1] + == baseline.household.household_net_income + ) + reform = pe.us.calculate_household( + **inputs, + spm=selection, + reform={"gov.irs.deductions.standard.amount.SINGLE": 5_000}, + extra_variables=["spm_unit_spm_threshold"], + ) + assert reform.tax_unit.income_tax > baseline.tax_unit.income_tax + assert ( + reform.spm_unit.spm_unit_spm_threshold + == baseline.spm_unit.spm_unit_spm_threshold + ) + sensitivity = pe.us.calculate_household( + **inputs, + spm={"geography_kind": "national", "scenario": "zero_real"}, + extra_variables=["spm_unit_spm_threshold"], + ) + assert ( + sensitivity.spm_unit.spm_unit_spm_threshold + != baseline.spm_unit.spm_unit_spm_threshold + ) + assert baseline.to_dict()["provenance"]["spm"] == baseline_receipt + exported = baseline.to_dict() + exported["provenance"]["spm"]["years"].clear() + assert baseline.to_dict()["provenance"]["spm"] == baseline_receipt + assert selection == {"geography_kind": "national", "scenario": "ce_trend"} + + +@pytest.mark.parametrize("year", [2021, 2036]) +def test_year_outside_canonical_horizon_is_not_silently_extrapolated(year): + with pytest.raises(ValueError, match="no entry|unavailable|not available"): + pe.us.calculate_household( + **household_inputs(year=year), spm={"geography_kind": "national"} + ) diff --git a/tests/test_spm_model.py b/tests/test_spm_model.py new file mode 100644 index 00000000..7ec2a618 --- /dev/null +++ b/tests/test_spm_model.py @@ -0,0 +1,245 @@ +"""Small real-country checks for the high-level canonical SPM contract.""" + +import json + +import pandas as pd +import pytest +from microdf import MicroDataFrame + +pytest.importorskip("spm_calculator.policyengine_adapter") +pytest.importorskip("policyengine_us") + +import policyengine as pe +from policyengine.core.simulation import Simulation, _cache +from policyengine.core.spm import SPMSelection +from policyengine.tax_benefit_models.us.datasets import ( + PolicyEngineUSDataset, + USYearData, +) + + +@pytest.fixture +def tiny_spm_dataset(tmp_path): + """One adult, one child, observed CA county and native group membership.""" + tables = {} + for entity in ( + "person", + "household", + "spm_unit", + "tax_unit", + "family", + "marital_unit", + ): + count = 2 if entity in {"person", "marital_unit"} else 1 + frame = pd.DataFrame( + {f"{entity}_id": range(1, count + 1), f"{entity}_weight": [1.0] * count} + ) + if entity == "person": + frame["age"] = [35, 8] + frame["employment_income"] = [35_000, 0] + for group in ("household", "spm_unit", "tax_unit", "family"): + frame[f"{group}_id"] = [1, 1] + frame["marital_unit_id"] = [1, 2] + elif entity == "household": + frame["state_code"] = ["CA"] + frame["county_fips"] = ["06037"] + tables[entity] = MicroDataFrame(frame, weights=f"{entity}_weight") + return PolicyEngineUSDataset( + name="synthetic-spm", + description="Uncertified two-person unit fixture", + filepath=str(tmp_path / "input.h5"), + year=2026, + data=USYearData(**tables), + ) + + +def test_high_level_selection_is_typed_and_rejects_provider_paths(): + sim = Simulation(spm={"geography_kind": "national"}) + assert isinstance(sim.spm, SPMSelection) + assert ( + json.loads(sim.model_dump_json(include={"spm"}))["spm"]["geography_kind"] + == "national" + ) + with pytest.raises(ValueError): + Simulation(spm={"path": "/tmp/forecast.json"}) + + +@pytest.mark.parametrize( + "reform", [None, {"gov.irs.credits.ctc.amount.base[0].amount": 3_000}] +) +@pytest.mark.parametrize( + "column", + ["spm_measurement_adults", "spm_unit_spm_threshold", "spm_unit_net_income"], +) +def test_computed_dataframe_inputs_rejected_before_any_set_input( + tiny_spm_dataset, monkeypatch, reform, column +): + from policyengine_us.spm import SPMSimulationMixin + + tiny_spm_dataset.data.spm_unit[column] = [1] + calls = [] + monkeypatch.setattr( + SPMSimulationMixin, "set_input", lambda *args: calls.append(args) + ) + sim = Simulation( + dataset=tiny_spm_dataset, tax_benefit_model_version=pe.us.model, policy=reform + ) + with pytest.raises(ValueError, match="computed outputs supplied as inputs"): + sim.run() + assert calls == [] + + +def test_real_high_level_provenance_cache_and_disk_round_trip( + tiny_spm_dataset, monkeypatch +): + _cache.clear() + county = Simulation( + id="spm-replay", dataset=tiny_spm_dataset, tax_benefit_model_version=pe.us.model + ) + county.ensure() + county_receipt = county.spm_provenance() + assert county_receipt["years"]["2026"] + assert county_receipt["geography_kind"] == "county" + assert county.spm_config["geography_kind"] == "county" + assert ( + county.spm.forecast_content_sha256 + == county.spm_config["forecast_content_sha256"] + ) + assert county.spm.scenario == "ce_trend" + assert county.release_bundle["spm"] == county.spm_provenance() + from policyengine import bundle + + original_bundle = bundle.get_current_bundle() + original_bundle["measurements"]["spm"]["scenario"] = "zero_real" + with monkeypatch.context() as context: + context.setattr(bundle, "get_current_bundle", lambda: original_bundle) + assert county.spm_config["scenario"] == "ce_trend" + county_receipt["years"].clear() + assert county.spm_provenance()["years"] + assert json.loads(county.model_dump_json(include={"spm", "spm_receipt"}))[ + "spm_receipt" + ]["years"] + + cached = Simulation( + id=county.id, dataset=tiny_spm_dataset, tax_benefit_model_version=pe.us.model + ) + cached.ensure() + assert cached.spm_provenance() == county.spm_provenance() + _cache.clear() + restored = Simulation( + id=county.id, dataset=tiny_spm_dataset, tax_benefit_model_version=pe.us.model + ) + restored.load() + assert restored.spm_provenance() == county.spm_provenance() + + county.spm = SPMSelection(geography_kind="national", scenario="zero_real") + with pytest.raises(ValueError, match="SPM settings changed"): + county.save() + county.ensure() + assert county.spm_provenance()["geography_kind"] == "national" + assert county.spm_provenance()["scenario"] == "zero_real" + assert county.output_dataset.filepath != restored.output_dataset.filepath + assert restored.spm_provenance()["geography_kind"] == "county" + + +def test_generic_composition_inputs_remain_allowed(tiny_spm_dataset): + tiny_spm_dataset.data.person["is_adult"] = [True, False] + tiny_spm_dataset.data.spm_unit["spm_unit_count_adults"] = [1] + tiny_spm_dataset.data.spm_unit["spm_unit_count_children"] = [1] + sim = Simulation(dataset=tiny_spm_dataset, tax_benefit_model_version=pe.us.model) + sim.run() + assert sim.output_dataset.data.person["is_adult"].tolist() == [True, False] + assert sim.spm_provenance()["years"]["2026"] + + +def test_state_only_high_level_default_requires_geography(tiny_spm_dataset): + from spm_calculator.errors import SPMInputError + + tiny_spm_dataset.data.household.drop(columns=["county_fips"], inplace=True) + sim = Simulation(dataset=tiny_spm_dataset, tax_benefit_model_version=pe.us.model) + with pytest.raises(SPMInputError) as error: + sim.run() + assert error.value.code == "SPM_GEOGRAPHY_REQUIRED" + sim.spm = SPMSelection(geography_kind="national") + sim.run() + assert sim.spm_provenance()["geography_kind"] == "national" + + +def test_run_record_binds_spm_settings_and_receipt(tiny_spm_dataset): + from policyengine.core.run_record import build_simulation_run_record_payloads + + tiny_spm_dataset.save() + sim = Simulation( + dataset=tiny_spm_dataset, + tax_benefit_model_version=pe.us.model, + spm={"geography_kind": "national"}, + ) + sim.run() + sim.save() + payloads = build_simulation_run_record_payloads(sim) + assert payloads["input"]["spm"] == sim.spm_config + assert payloads["results"]["spm"] == sim.spm_provenance() + sim.spm = SPMSelection(geography_kind="national", scenario="zero_real") + with pytest.raises(ValueError, match="SPM settings changed"): + build_simulation_run_record_payloads(sim) + + +def test_real_high_level_reform_and_baseline_share_selection( + tiny_spm_dataset, monkeypatch +): + from policyengine.tax_benefit_models.us.model import PolicyEngineUSLatest + + tiny_spm_dataset.data.household.drop(columns=["county_fips"], inplace=True) + configurations = [] + original = PolicyEngineUSLatest._build_simulation_from_dataset + + def capture(self, country_simulation, dataset, system): + configurations.append(country_simulation.spm_config) + return original(self, country_simulation, dataset, system) + + monkeypatch.setattr(PolicyEngineUSLatest, "_build_simulation_from_dataset", capture) + sim = Simulation( + dataset=tiny_spm_dataset, + tax_benefit_model_version=pe.us.model, + spm={"geography_kind": "national", "scenario": "zero_real"}, + policy={"gov.irs.credits.ctc.amount.base[0].amount": 3_000}, + ) + sim.run() + assert len(configurations) == 2 + assert configurations == [sim.spm_config, sim.spm_config] + assert sim.spm_provenance()["scenario"] == "zero_real" + + +def test_managed_native_h5_selection_and_clone_provenance(tiny_spm_dataset, tmp_path): + """The actual managed loader can consume a tiny native H5 in development.""" + import h5py + import numpy as np + + path = tmp_path / "synthetic-native.h5" + with h5py.File(path, "w") as stream: + for entity, frame in tiny_spm_dataset.data.entity_data.items(): + for name in frame.columns: + output_name = name + if entity == "person" and name.endswith("_id") and name != "person_id": + output_name = f"person_{name}" + values = frame[name].to_numpy() + if values.dtype.kind in {"O", "U"}: + values = np.asarray(values, dtype="S") + stream.create_dataset(f"{output_name}/2026", data=values) + managed = pe.us.managed_microsimulation( + dataset=str(path), + allow_unmanaged=True, + spm={"geography_kind": "national", "scenario": "zero_real"}, + ) + assert managed.policyengine_bundle["spm"] == managed.spm_config + managed.calculate("spm_unit_spm_threshold", 2026) + clone = managed.clone() + assert clone.spm_config == managed.spm_config + assert clone.spm_provenance() == managed.spm_provenance() + clone.calculate("spm_unit_spm_threshold", 2025) + assert "2025" in clone.spm_provenance()["years"] + assert "2025" not in managed.spm_provenance()["years"] + returned = managed.spm_provenance() + returned["years"].clear() + assert managed.spm_provenance()["years"] + json.dumps(managed.spm_provenance()) diff --git a/tests/test_spm_selection.py b/tests/test_spm_selection.py new file mode 100644 index 00000000..1b127c9a --- /dev/null +++ b/tests/test_spm_selection.py @@ -0,0 +1,247 @@ +"""The wrapper pins measurement provenance and accepts explicit locations.""" + +import json + +import pytest +from pydantic import ValidationError + +import policyengine as pe +from policyengine.tax_benefit_models.us.spm import SPMSelection, resolve_spm_selection + + +@pytest.fixture +def pinned_bundle(monkeypatch): + value = { + "measurements": { + "spm": { + "forecast_content_sha256": "a" * 64, + "scenario": "ce_trend", + "geography_kind": "county", + } + } + } + monkeypatch.setattr("policyengine.bundle.get_current_bundle", lambda: value) + return value + + +def test_default_selection_preserves_independent_bundle_hash(pinned_bundle): + config = resolve_spm_selection() + assert config["forecast_content_sha256"] == "a" * 64 + assert config["geography_kind"] == "county" + assert config["scenario"] == "ce_trend" + assert SPMSelection.model_validate_json(json.dumps(config)).model_dump() == config + + +def test_explicit_national_and_scenario_do_not_replace_artifact(pinned_bundle): + config = resolve_spm_selection( + SPMSelection(geography_kind="national", scenario="zero_real") + ) + assert config["forecast_content_sha256"] == "a" * 64 + assert config["geography_kind"] == "national" + assert config["scenario"] == "zero_real" + assert pinned_bundle["measurements"]["spm"]["scenario"] == "ce_trend" + + +def test_no_null_or_unmatched_digest_bypass(pinned_bundle): + assert ( + resolve_spm_selection({"forecast_content_sha256": None})[ + "forecast_content_sha256" + ] + == "a" * 64 + ) + with pytest.raises(ValueError, match="artifact hash"): + resolve_spm_selection({"forecast_content_sha256": "b" * 64}) + pinned_bundle["measurements"]["spm"].pop("forecast_content_sha256") + with pytest.raises(ValueError, match="must pin"): + resolve_spm_selection() + + +@pytest.mark.parametrize( + "value", + [ + {"geography_kind": "state"}, + {"geography_kind": "congressional_district"}, + {"geography_kind": "national", "geography_id": "A"}, + {"geography_kind": "metro"}, + {"year_policy": "pe_cpi_u"}, + {"allow_estimated": True}, + {"path": "/tmp/other.json"}, + ], +) +def test_old_fallback_policies_and_ambiguous_locations_rejected(value): + with pytest.raises(ValidationError): + SPMSelection.model_validate(value) + + +def test_missing_bundle_configuration_is_not_silently_inferred(monkeypatch): + monkeypatch.setattr("policyengine.bundle.get_current_bundle", lambda: {}) + with pytest.raises(ValueError, match="no certified SPM"): + resolve_spm_selection() + + +@pytest.mark.parametrize("value", [[], "", False, 0]) +def test_falsey_non_selection_is_rejected(pinned_bundle, value): + with pytest.raises(ValidationError): + resolve_spm_selection(value) + + +@pytest.mark.parametrize( + "value", + [ + {"scenario": ""}, + {"scenario": " "}, + {"county_vintage": ""}, + {"county_vintage": 2020}, + {"as_of": "2026-02-30"}, + {"as_of": "20260909"}, + {"as_of": "2026-09-09T00:00:00"}, + ], +) +def test_malformed_selection_values_rejected(value): + with pytest.raises(ValidationError): + SPMSelection.model_validate(value) + + +def test_switching_explicit_geography_drops_default_area(pinned_bundle): + pinned_bundle["measurements"]["spm"].update( + geography_kind="metro", geography_id="12345" + ) + config = resolve_spm_selection({"geography_kind": "national"}) + assert config["geography_id"] is None + assert config["geography_kind"] == "national" + + +@pytest.mark.parametrize("geography_kind", ["county", "national", "metro"]) +def test_partial_selection_preserves_omissions_in_public_round_trips( + pinned_bundle, geography_kind +): + pinned_bundle["measurements"]["spm"].update( + geography_kind=geography_kind, + geography_id="12345" if geography_kind == "metro" else None, + county_vintage="2010", + as_of="2026-06-01", + ) + selection = SPMSelection(scenario="zero_real") + envelope = pe.Simulation(spm=selection) + nested_json = envelope.model_dump_json(include={"spm"}) + assert json.loads(nested_json) == {"spm": {"scenario": "zero_real"}} + restored_selections = [ + SPMSelection.model_validate(selection.model_dump()), + SPMSelection.model_validate_json(selection.model_dump_json()), + pe.Simulation.model_validate_json(nested_json).spm, + ] + expected = resolve_spm_selection(selection) + for restored in restored_selections: + assert restored.model_fields_set == {"scenario"} + assert resolve_spm_selection(restored) == expected + assert resolve_spm_selection(restored)["geography_kind"] == geography_kind + assert resolve_spm_selection(restored)["county_vintage"] == "2010" + assert resolve_spm_selection(restored)["as_of"] == "2026-06-01" + + +@pytest.mark.parametrize("geography_kind", ["national", "metro"]) +def test_resolved_selection_retains_all_settings_after_public_json_round_trip( + pinned_bundle, geography_kind +): + defaults = pinned_bundle["measurements"]["spm"] + defaults.update( + geography_kind=geography_kind, + geography_id="12345" if geography_kind == "metro" else None, + ) + expected = resolve_spm_selection({"scenario": "zero_real"}) + resolved = SPMSelection.model_validate(expected) + assert set(json.loads(resolved.model_dump_json())) == set(SPMSelection.model_fields) + restored = pe.Simulation.model_validate_json( + pe.Simulation(spm=resolved).model_dump_json(include={"spm"}) + ).spm + defaults.update( + geography_kind="county", + geography_id=None, + scenario="ce_trend", + county_vintage="2010", + as_of="2026-06-01", + ) + assert restored.model_fields_set == set(SPMSelection.model_fields) + assert resolve_spm_selection(restored) == expected + + +@pytest.mark.parametrize("geography_kind", ["national", "metro"]) +def test_explicit_class_defaults_remain_overrides_after_json_round_trip( + pinned_bundle, geography_kind +): + pinned_bundle["measurements"]["spm"].update( + geography_kind=geography_kind, + geography_id="12345" if geography_kind == "metro" else None, + county_vintage="2010", + as_of="2026-06-01", + ) + selection = SPMSelection(geography_kind="county", county_vintage="2020", as_of=None) + restored = SPMSelection.model_validate_json(selection.model_dump_json()) + config = resolve_spm_selection(restored) + assert config["geography_kind"] == "county" + assert config["geography_id"] is None + assert config["county_vintage"] == "2020" + assert config["as_of"] is None + + +@pytest.mark.parametrize("geography_kind", ["national", "metro"]) +def test_partial_selection_json_round_trip_preserves_real_household_threshold( + monkeypatch, geography_kind +): + """State-only inputs must still use the inherited national or metro area.""" + pytest.importorskip("spm_calculator.policyengine_adapter") + pytest.importorskip("policyengine_us.spm") + import numpy as np + from spm_calculator.release import SPMUnit + from spm_calculator.rolling_forecast import load_forecast + + from policyengine import bundle + + configured = bundle.get_current_bundle() + forecast = load_forecast() + area_id = ( + forecast.resolve_county(2026, "06037", scenario="zero_real")["area_id"] + if geography_kind == "metro" + else None + ) + configured["measurements"]["spm"].update( + geography_kind=geography_kind, geography_id=area_id + ) + monkeypatch.setattr(bundle, "get_current_bundle", lambda: configured) + selection = SPMSelection(scenario="zero_real") + restored_selections = [ + selection, + SPMSelection.model_validate_json(selection.model_dump_json()), + pe.Simulation.model_validate_json( + pe.Simulation(spm=selection).model_dump_json(include={"spm"}) + ).spm, + ] + expected = forecast.calculate_unit( + SPMUnit( + unit_id="selection-round-trip", + year=2026, + num_adults=1, + num_children=0, + tenure="renter", + geography_kind=geography_kind, + geography_id=area_id, + ), + scenario="zero_real", + ) + for restored in restored_selections: + result = pe.us.calculate_household( + people=[{"age": 40, "employment_income": 50_000}], + tax_unit={"filing_status": "SINGLE"}, + spm_unit={"spm_unit_tenure_type": "RENTER"}, + household={"state_code": "CA"}, + year=2026, + spm=restored, + extra_variables=["spm_unit_spm_threshold"], + ) + assert result.spm_unit.spm_unit_spm_threshold == float( + np.float32(expected["threshold"]) + ) + assert result.to_dict()["provenance"]["spm_config"] == resolve_spm_selection( + selection + ) + assert result.to_dict()["provenance"]["spm"]["geography_kind"] == geography_kind diff --git a/tests/test_spm_selection_schema.py b/tests/test_spm_selection_schema.py new file mode 100644 index 00000000..e665435f --- /dev/null +++ b/tests/test_spm_selection_schema.py @@ -0,0 +1,52 @@ +"""Public schemas must let clients leave bundle-dependent settings omitted.""" + +import pytest +from jsonschema import Draft202012Validator +from pydantic import BaseModel + +from policyengine.core.spm import SPMSelection + + +class SelectionEnvelope(BaseModel): + spm: SPMSelection | None = None + + +@pytest.mark.parametrize("mode", ["validation", "serialization"]) +@pytest.mark.parametrize("nested", [False, True]) +def test_spm_schema_keeps_typed_optional_fields_without_client_defaults(mode, nested): + schema = ( + SelectionEnvelope.model_json_schema(mode=mode)["$defs"]["SPMSelection"] + if nested + else SPMSelection.model_json_schema(mode=mode) + ) + assert set(schema["properties"]) == set(SPMSelection.model_fields) + assert schema.get("required", []) == [] + assert schema["additionalProperties"] is False + for field in schema["properties"].values(): + assert "default" not in field + assert schema["properties"]["geography_kind"]["enum"] == [ + "county", + "national", + "metro", + ] + assert schema["properties"]["county_vintage"]["pattern"] == r"^[0-9]{4}$" + validator = Draft202012Validator(schema) + validator.validate({}) + validator.validate({"scenario": "zero_real"}) + validator.validate({"geography_kind": "county", "as_of": None}) + assert not validator.is_valid({"geography_kind": "state"}) + assert not validator.is_valid({"provider": "/tmp/unpinned.json"}) + + +def test_schema_inheritance_contract_preserves_python_runtime_defaults(): + selection = SPMSelection() + assert selection.geography_kind == "county" + assert selection.county_vintage == "2020" + assert selection.model_dump() == {} + assert selection.model_dump_json() == "{}" + explicit = SPMSelection(geography_kind="county", county_vintage="2020", as_of=None) + assert explicit.model_dump() == { + "geography_kind": "county", + "county_vintage": "2020", + "as_of": None, + } diff --git a/tests/test_us_microsim_structural_reforms.py b/tests/test_us_microsim_structural_reforms.py index 0d9d0803..0babb9a1 100644 --- a/tests/test_us_microsim_structural_reforms.py +++ b/tests/test_us_microsim_structural_reforms.py @@ -64,6 +64,11 @@ def _us_fixture_dataset(tmp_path, year: int = 2025): { "household_id": [1, 2], "state_code": ["CA", "TX"], + # The certified bundle measures SPM at county geography + # (manifest ``measurements.spm.geography_kind == "county"``), + # so a US household fixture must name its county. State alone + # does not identify an SPM area. + "county_fips": ["06037", "48201"], "household_weight": [1_000.0, 1_000.0], } ), diff --git a/tests/test_us_native_alignment.py b/tests/test_us_native_alignment.py new file mode 100644 index 00000000..59b39f01 --- /dev/null +++ b/tests/test_us_native_alignment.py @@ -0,0 +1,226 @@ +"""Real country calculations keep independent native entity orders aligned.""" + +import hashlib +import importlib + +import numpy as np +import pandas as pd +import pytest + +# These cases must run against the real published calculator module, never a +# substitute; skip only if the pinned module is absent from the environment. +pytest.importorskip("spm_calculator.release") +pytest.importorskip("spm_calculator.rolling_forecast") +pytest.importorskip("spm_calculator.policyengine_adapter") + +from spm_calculator.release import SPMUnit # noqa: E402 +from spm_calculator.rolling_forecast import load_forecast # noqa: E402 + +import policyengine as pe +from policyengine.tax_benefit_models.us.datasets import PolicyEngineUSDataset +from policyengine.tax_benefit_models.us.model import PolicyEngineUSLatest +from policyengine.tax_benefit_models.us.spm import resolve_spm_selection + + +@pytest.fixture +def independent_entity_frames(): + # Person order, group IDs, table orders, and indexes differ independently. + # The California unit also has a child, so aggregate outputs exercise real + # membership joins instead of only one-to-one relabeling. + person = pd.DataFrame( + { + "person_id": [300, 101, 200, 100], + "person_household_id": [30, 10, 20, 10], + "person_tax_unit_id": [3, 1, 2, 1], + "person_spm_unit_id": [330, 110, 220, 110], + "person_family_id": [3000, 1000, 2000, 1000], + "person_marital_unit_id": [33, 44, 22, 11], + "age": [50, 8, 40, 30], + "employment_income": [31_000.0, 1_000.0, 23_000.0, 13_000.0], + }, + index=[91, -4, 300, 17], + ) + return { + "person": person, + "household": pd.DataFrame( + { + "household_id": [20, 10, 30], + "household_weight": [2.5, 7.0, 4.0], + "county_fips": ["36061", "06037", "48201"], + "state_fips": [36, 6, 48], + }, + index=[77, 42, -9], + ), + "tax_unit": pd.DataFrame({"tax_unit_id": [3, 2, 1]}, index=[8, 12, -7]), + "spm_unit": pd.DataFrame( + { + "spm_unit_id": [220, 330, 110], + "spm_unit_tenure_type": [ + "OWNER_WITHOUT_MORTGAGE", + "OWNER_WITH_MORTGAGE", + "RENTER", + ], + }, + index=[13, -8, 50], + ), + "family": pd.DataFrame({"family_id": [1000, 3000, 2000]}, index=[9, -2, 17]), + "marital_unit": pd.DataFrame( + {"marital_unit_id": [22, 11, 44, 33]}, index=[87, -1, 11, 44] + ), + } + + +@pytest.mark.parametrize("format", ["fixed", "table"]) +@pytest.mark.parametrize("link_style", ["native", "plain"]) +def test_native_inputs_and_calculated_outputs_align_by_id( + tmp_path, independent_entity_frames, format, link_style, monkeypatch +): + frames = independent_entity_frames + if link_style == "plain": + frames["person"] = frames["person"].rename( + columns={ + f"person_{entity}_id": f"{entity}_id" + for entity in frames + if entity != "person" + } + ) + else: + # Conflicting plain aliases must not override native memberships at + # either input construction or output assembly. + for entity, frame in frames.items(): + if entity != "person": + frames["person"][f"{entity}_id"] = frame[f"{entity}_id"].iloc[0] + path = tmp_path / f"shuffled-{format}.h5" + with pd.HDFStore(path, mode="w") as store: + for entity, frame in frames.items(): + store.put(entity, frame, format=format) + source_sha256 = hashlib.sha256(path.read_bytes()).hexdigest() + dataset = PolicyEngineUSDataset( + name="independently shuffled native tables", + description="Uncertified four-person numeric alignment fixture", + filepath=str(path), + year=2026, + ) + inputs = { + entity: pd.DataFrame(frame).copy(deep=True) + for entity, frame in dataset.data.entity_data.items() + } + + # Keep the real high-level input builder, country calculations, result + # assembly and SPM receipt. Limit requested variables to this regression. + outputs = {entity: ["employment_income"] for entity in frames} + outputs["person"] += ["county_fips", "state_fips"] + outputs["household"] += ["county_fips", "state_fips"] + outputs["spm_unit"] += ["spm_unit_spm_threshold"] + monkeypatch.setattr( + PolicyEngineUSLatest, "resolve_entity_variables", lambda self, sim: outputs + ) + country_simulations = [] + build = PolicyEngineUSLatest._build_simulation_from_dataset + + def capture(self, country_simulation, dataset, system): + build(self, country_simulation, dataset, system) + country_simulations.append(country_simulation) + + monkeypatch.setattr(PolicyEngineUSLatest, "_build_simulation_from_dataset", capture) + simulation = pe.Simulation(dataset=dataset, tax_benefit_model_version=pe.us.model) + simulation.run() + output = simulation.output_dataset.data.entity_data + country = country_simulations[0] + + # Assert numeric geography at both boundaries, including the two CA people. + assert output["person"]["county_fips"].tolist() == [ + "48201", + "06037", + "36061", + "06037", + ] + assert output["person"]["state_fips"].tolist() == [48, 6, 36, 6] + assert output["household"]["county_fips"].tolist() == ["36061", "06037", "48201"] + assert output["household"]["state_fips"].tolist() == [36, 6, 48] + np.testing.assert_array_equal( + country.calculate("state_fips", 2026, map_to="person").values, + [48, 6, 36, 6], + ) + + incomes = frames["person"] + for entity, frame in inputs.items(): + entity_id = f"{entity}_id" + assert output[entity][entity_id].tolist() == frame[entity_id].tolist() + assert output[entity].weights.tolist() == frame[f"{entity}_weight"].tolist() + if entity == "person": + expected = incomes.set_index("person_id")["employment_income"] + else: + link = f"person_{entity_id}" if link_style == "native" else entity_id + expected = incomes.groupby(link)["employment_income"].sum() + assert output["person"][entity_id].tolist() == incomes[link].tolist() + np.testing.assert_array_equal( + output[entity]["employment_income"].to_numpy(), + expected.loc[frame[entity_id]].to_numpy(), + ) + # Each entity receives its native weight in the country's own ID order. + expected_weights = frame.set_index(entity_id)[f"{entity}_weight"] + np.testing.assert_array_equal( + country.calculate(f"{entity}_weight", 2026).values, + expected_weights.loc[country.populations[entity].ids].to_numpy(), + ) + pd.testing.assert_frame_equal( + pd.DataFrame(dataset.data.entity_data[entity]), frame + ) + assert output["spm_unit"]["employment_income"].sum() == 279_500.0 + + # Thresholds are compared with the real standalone calculator for each + # county/composition/tenure; merely retaining the IDs cannot pass this test. + forecast = load_forecast() + scenario = resolve_spm_selection()["scenario"] + expected_thresholds = [] + for spm_id, county, children, tenure in ( + (220, "36061", 0, "owner_without_mortgage"), + (330, "48201", 0, "owner_with_mortgage"), + (110, "06037", 1, "renter"), + ): + assignment = forecast.resolve_county( + 2026, county, county_vintage="2020", scenario=scenario + ) + result = forecast.calculate_unit( + SPMUnit( + unit_id=str(spm_id), + year=2026, + num_adults=1, + num_children=children, + tenure=tenure, + geography_kind="metro", + geography_id=assignment["area_id"], + ), + scenario=scenario, + ) + expected_thresholds.append(np.float32(result["threshold"])) + np.testing.assert_array_equal( + output["spm_unit"]["spm_unit_spm_threshold"].to_numpy(), expected_thresholds + ) + assert simulation.spm_provenance()["geography_kind"] == "county" + assert hashlib.sha256(path.read_bytes()).hexdigest() == source_sha256 + + +@pytest.mark.parametrize( + "selection", + [ + {"path": "/tmp/invalid.json"}, + {"geography_kind": "unsupported"}, + {"forecast_content_sha256": "0" * 64}, + ], +) +def test_invalid_managed_spm_rejected_before_dataset_materialization( + monkeypatch, selection +): + module = importlib.import_module("policyengine.tax_benefit_models.us.model") + calls = [] + + def sentinel(*args, **kwargs): + calls.append((args, kwargs)) + raise AssertionError("Invalid SPM must not materialize data") + + monkeypatch.setattr(module, "materialize_dataset", sentinel) + with pytest.raises(ValueError): + pe.us.managed_microsimulation(spm=selection) + assert calls == [] diff --git a/tests/test_us_native_hdf_weights.py b/tests/test_us_native_hdf_weights.py new file mode 100644 index 00000000..50f89412 --- /dev/null +++ b/tests/test_us_native_hdf_weights.py @@ -0,0 +1,244 @@ +"""Native pandas HDF weights are projected through IDs without editing the H5.""" + +import hashlib + +import numpy as np +import pandas as pd +import pytest + +from policyengine.tax_benefit_models.us.datasets import PolicyEngineUSDataset + + +@pytest.fixture +def native_frames(): + """Small BuildP-shaped source with only calibrated household weights. + + ID order and pandas indexes intentionally differ across every table. Three + people share one SPM unit; its weight must remain one household weight. + """ + return { + "person": pd.DataFrame( + { + "person_id": [101, 20, 888, 42, 5], + "person_household_id": [12, 900, 12, 900, 12], + "person_tax_unit_id": [21, 700, 21, 9, 21], + "person_spm_unit_id": [6, 8800, 6, 8800, 6], + "person_family_id": [8, 99, 8, 99, 8], + "person_marital_unit_id": [3, 67, 10, 25, 3], + "age": np.array([16, 40, 8, 33, 3], dtype=np.int16), + "is_spm_independent_minor_role": [True, True, False, True, False], + "employment_income": [0.0, 24_000.0, 0.0, 30_000.0, 0.0], + }, + index=pd.Index([19, -4, 60, 3, 100], name="source_row"), + ), + "household": pd.DataFrame( + { + "household_id": [900, 12], + "household_weight": [3.25, 8.5], + "county_fips": np.array(["06037", "48201"], dtype=object), + }, + index=pd.Index([44, -10], name="source_row"), + ), + "tax_unit": pd.DataFrame({"tax_unit_id": [700, 9, 21]}, index=[50, -2, 19]), + "spm_unit": pd.DataFrame( + {"spm_unit_id": [6, 8800], "source_audit_amount": [10.0, 100.0]}, + index=[81, 3], + ), + "family": pd.DataFrame({"family_id": [99, 8]}, index=[0, -80]), + "marital_unit": pd.DataFrame( + {"marital_unit_id": [25, 3, 67, 10]}, index=[7, 200, -12, 31] + ), + } + + +EXPECTED_WEIGHTS = { + "person": [8.5, 3.25, 8.5, 3.25, 8.5], + "household": [3.25, 8.5], + "tax_unit": [3.25, 3.25, 8.5], + "spm_unit": [8.5, 3.25], + "family": [3.25, 8.5], + "marital_unit": [3.25, 8.5, 3.25, 8.5], +} + + +def write_native_hdf(tmp_path, frames, format): + path = tmp_path / f"native-{format}.h5" + with pd.HDFStore(path, mode="w") as store: + for entity, frame in frames.items(): + store.put(entity, frame, format=format) + return path + + +def load_native_hdf(path): + return PolicyEngineUSDataset( + name="Synthetic native household-weight fixture", + description="Uncertified five-person loader regression", + filepath=str(path), + year=2024, + ) + + +@pytest.mark.parametrize("format", ["fixed", "table"]) +@pytest.mark.parametrize("link_style", ["native", "plain"]) +def test_native_hdf_derives_missing_weights_by_ids_without_changing_source( + tmp_path, native_frames, format, link_style +): + if link_style == "plain": + native_frames["person"] = native_frames["person"].rename( + columns={ + f"person_{entity}_id": f"{entity}_id" + for entity in native_frames + if entity != "person" + } + ) + path = write_native_hdf(tmp_path, native_frames, format) + before = hashlib.sha256(path.read_bytes()).hexdigest() + + dataset = load_native_hdf(path) + + for entity, original in native_frames.items(): + loaded = getattr(dataset.data, entity) + # Existing arrays, dtypes, county, primitive roles and native indexes + # survive unchanged; derived weight columns exist only in memory. + pd.testing.assert_frame_equal(pd.DataFrame(loaded)[original.columns], original) + expected = pd.Series( + EXPECTED_WEIGHTS[entity], index=original.index, name=f"{entity}_weight" + ) + pd.testing.assert_series_equal( + pd.DataFrame(loaded)[f"{entity}_weight"], expected + ) + pd.testing.assert_series_equal(loaded.weights, expected, check_names=False) + + assert dataset.data.person.employment_income.sum() == 175_500.0 + assert dataset.data.spm_unit.source_audit_amount.sum() == 410.0 + assert hashlib.sha256(path.read_bytes()).hexdigest() == before + with pd.HDFStore(path, mode="r") as store: + for entity, original in native_frames.items(): + pd.testing.assert_frame_equal(store[entity], original) + + +@pytest.mark.parametrize("format", ["fixed", "table"]) +def test_existing_person_and_entity_weights_are_preserved( + tmp_path, native_frames, format +): + # Deliberately distinct supplied weights catch both overwriting and using + # person weights as the source for missing group weights. + native_frames["person"]["person_weight"] = [11.0, 12.0, 13.0, 14.0, 15.0] + native_frames["tax_unit"]["tax_unit_weight"] = [23.0, 24.0, 25.0] + native_frames["family"]["family_weight"] = [31.0, 32.0] + path = write_native_hdf(tmp_path, native_frames, format) + before = hashlib.sha256(path.read_bytes()).hexdigest() + + dataset = load_native_hdf(path) + + for entity, original in native_frames.items(): + loaded = getattr(dataset.data, entity) + pd.testing.assert_frame_equal(pd.DataFrame(loaded)[original.columns], original) + expected = ( + original[f"{entity}_weight"].tolist() + if f"{entity}_weight" in original + else EXPECTED_WEIGHTS[entity] + ) + assert loaded.weights.tolist() == expected + assert pd.DataFrame(loaded)[f"{entity}_weight"].tolist() == expected + assert hashlib.sha256(path.read_bytes()).hexdigest() == before + + +def test_native_person_links_take_precedence_over_plain_aliases( + tmp_path, native_frames +): + for entity, frame in native_frames.items(): + if entity != "person": + native_frames["person"][f"{entity}_id"] = frame[f"{entity}_id"].iloc[0] + path = write_native_hdf(tmp_path, native_frames, "table") + before = hashlib.sha256(path.read_bytes()).hexdigest() + + dataset = load_native_hdf(path) + + for entity, original in native_frames.items(): + loaded = getattr(dataset.data, entity) + pd.testing.assert_frame_equal(pd.DataFrame(loaded)[original.columns], original) + assert loaded.weights.tolist() == EXPECTED_WEIGHTS[entity] + assert hashlib.sha256(path.read_bytes()).hexdigest() == before + + +def test_empty_native_entity_tables_keep_indexes_and_source_weight_dtype( + tmp_path, native_frames +): + frames = {entity: frame.iloc[:0].copy() for entity, frame in native_frames.items()} + path = write_native_hdf(tmp_path, frames, "fixed") + before = hashlib.sha256(path.read_bytes()).hexdigest() + + dataset = load_native_hdf(path) + + for entity, original in frames.items(): + loaded = pd.DataFrame(getattr(dataset.data, entity)) + pd.testing.assert_frame_equal(loaded[original.columns], original) + assert loaded[f"{entity}_weight"].empty + assert ( + loaded[f"{entity}_weight"].dtype + == frames["household"]["household_weight"].dtype + ) + assert hashlib.sha256(path.read_bytes()).hexdigest() == before + + +@pytest.mark.parametrize( + "problem", + [ + "missing_person_household_link", + "unknown_household_id", + "duplicate_household_id", + "orphan_spm_unit", + "spm_unit_spans_households", + ], +) +def test_missing_or_ambiguous_native_links_raise_without_mutating_hdf( + tmp_path, native_frames, problem +): + if problem == "missing_person_household_link": + native_frames["person"] = native_frames["person"].drop( + columns="person_household_id" + ) + elif problem == "unknown_household_id": + native_frames["person"].loc[19, "person_household_id"] = 404 + elif problem == "duplicate_household_id": + native_frames["household"].loc[-10, "household_id"] = 900 + elif problem == "orphan_spm_unit": + native_frames["spm_unit"].loc[81, "spm_unit_id"] = 404 + elif problem == "spm_unit_spans_households": + native_frames["person"].loc[-4, "person_spm_unit_id"] = 6 + path = write_native_hdf(tmp_path, native_frames, "table") + before = hashlib.sha256(path.read_bytes()).hexdigest() + + with pytest.raises(ValueError, match="weight|household|spm_unit"): + load_native_hdf(path) + + assert hashlib.sha256(path.read_bytes()).hexdigest() == before + + +@pytest.mark.parametrize("format", ["fixed", "table"]) +@pytest.mark.parametrize("entity", ["tax_unit", "spm_unit", "family", "marital_unit"]) +@pytest.mark.parametrize("problem", ["missing", "duplicate"]) +def test_invalid_group_ids_rejected_before_weight_mapping( + tmp_path, native_frames, format, entity, problem +): + entity_id = f"{entity}_id" + frame = native_frames[entity] + if problem == "missing": + # Matching null membership used to map successfully through pandas. + old_id = frame[entity_id].iloc[0] + frame.loc[frame.index[0], entity_id] = np.nan + link = f"person_{entity_id}" + person = native_frames["person"] + person.loc[person[link] == old_id, link] = np.nan + else: + # A repeated complete row used to duplicate its household weight and + # silently increase weighted totals (SPM audit total 410 -> 495). + native_frames[entity] = pd.concat([frame, frame.iloc[[0]]]) + path = write_native_hdf(tmp_path, native_frames, format) + before = hashlib.sha256(path.read_bytes()).hexdigest() + + with pytest.raises(ValueError, match=f"unique nonmissing {entity_id}"): + load_native_hdf(path) + + assert hashlib.sha256(path.read_bytes()).hexdigest() == before diff --git a/tests/test_us_reform_application.py b/tests/test_us_reform_application.py index 6e3b4145..28da60d8 100644 --- a/tests/test_us_reform_application.py +++ b/tests/test_us_reform_application.py @@ -6,6 +6,11 @@ MARRIED_COUPLE_WITH_KIDS, ) +# These fixtures declare no county, and the certified bundle's measurements.spm +# block selects geography_kind="county". Declare national geography explicitly, +# as the SPM contract requires; these tests assert federal income tax only. +NATIONAL_SPM = {"geography_kind": "national"} + def _double_standard_deduction(year: int) -> dict: """Dict reform: standard deduction doubled from ~$14,600 / $29,200 baseline.""" @@ -17,22 +22,28 @@ def _double_standard_deduction(year: int) -> dict: class TestUSHouseholdReformApplication: def test__baseline__then_income_tax_positive(self): - result = pe.us.calculate_household(**HIGH_INCOME_SINGLE_FILER) + result = pe.us.calculate_household(**HIGH_INCOME_SINGLE_FILER, spm=NATIONAL_SPM) assert result.tax_unit.income_tax > 0 def test__doubled_standard_deduction__then_tax_lower(self): - baseline = pe.us.calculate_household(**HIGH_INCOME_SINGLE_FILER) + baseline = pe.us.calculate_household( + **HIGH_INCOME_SINGLE_FILER, spm=NATIONAL_SPM + ) reformed = pe.us.calculate_household( **HIGH_INCOME_SINGLE_FILER, reform=_double_standard_deduction(2024), + spm=NATIONAL_SPM, ) assert reformed.tax_unit.income_tax < baseline.tax_unit.income_tax def test__doubled_standard_deduction__then_reduction_is_meaningful(self): - baseline = pe.us.calculate_household(**HIGH_INCOME_SINGLE_FILER) + baseline = pe.us.calculate_household( + **HIGH_INCOME_SINGLE_FILER, spm=NATIONAL_SPM + ) reformed = pe.us.calculate_household( **HIGH_INCOME_SINGLE_FILER, reform=_double_standard_deduction(2024), + spm=NATIONAL_SPM, ) reduction = baseline.tax_unit.income_tax - reformed.tax_unit.income_tax assert reduction >= 1000, ( @@ -40,17 +51,24 @@ def test__doubled_standard_deduction__then_reduction_is_meaningful(self): ) def test__married_couple_joint_deduction__then_tax_lower(self): - baseline = pe.us.calculate_household(**MARRIED_COUPLE_WITH_KIDS) + baseline = pe.us.calculate_household( + **MARRIED_COUPLE_WITH_KIDS, spm=NATIONAL_SPM + ) reformed = pe.us.calculate_household( **MARRIED_COUPLE_WITH_KIDS, reform=_double_standard_deduction(2024), + spm=NATIONAL_SPM, ) assert reformed.tax_unit.income_tax < baseline.tax_unit.income_tax def test__same_reform_twice__then_deterministic(self): reform = _double_standard_deduction(2024) - first = pe.us.calculate_household(**HIGH_INCOME_SINGLE_FILER, reform=reform) - second = pe.us.calculate_household(**HIGH_INCOME_SINGLE_FILER, reform=reform) + first = pe.us.calculate_household( + **HIGH_INCOME_SINGLE_FILER, reform=reform, spm=NATIONAL_SPM + ) + second = pe.us.calculate_household( + **HIGH_INCOME_SINGLE_FILER, reform=reform, spm=NATIONAL_SPM + ) assert first.tax_unit.income_tax == second.tax_unit.income_tax def test__custom_deduction_values__then_tax_reflects_values(self): @@ -63,9 +81,9 @@ def test__custom_deduction_values__then_tax_reflects_values(self): "gov.irs.deductions.standard.amount.JOINT": {"2024-01-01": 100000}, } small = pe.us.calculate_household( - **HIGH_INCOME_SINGLE_FILER, reform=small_reform + **HIGH_INCOME_SINGLE_FILER, reform=small_reform, spm=NATIONAL_SPM ) large = pe.us.calculate_household( - **HIGH_INCOME_SINGLE_FILER, reform=large_reform + **HIGH_INCOME_SINGLE_FILER, reform=large_reform, spm=NATIONAL_SPM ) assert large.tax_unit.income_tax < small.tax_unit.income_tax diff --git a/tests/test_us_regions.py b/tests/test_us_regions.py index 2158dd53..57414498 100644 --- a/tests/test_us_regions.py +++ b/tests/test_us_regions.py @@ -107,7 +107,7 @@ def test__given_us_registry__then_has_national_region(self): assert national.region_type == "national" assert national.dataset_path == ( "hf://policyengine/populace-us/populace_us_2024.h5" - "@populace-us-2024-buildp-sparse-rmloss100-cae8640-20260728T011454Z" + "@populace-us-2024-spm-20260915" ) def test__given_us_registry__then_has_51_states(self): diff --git a/uv.lock b/uv.lock index 4a50b731..279fc5e0 100644 --- a/uv.lock +++ b/uv.lock @@ -1,13 +1,16 @@ version = 1 revision = 3 -requires-python = ">=3.9" +requires-python = ">=3.11, <3.15" resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", - "python_full_version == '3.10.*'", - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] [[package]] @@ -98,15 +101,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, ] -[[package]] -name = "backports-asyncio-runner" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, -] - [[package]] name = "beautifulsoup4" version = "4.13.5" @@ -120,78 +114,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/eb/f4151e0c7377a6e08a38108609ba5cede57986802757848688aeedd1b9e8/beautifulsoup4-4.13.5-py3-none-any.whl", hash = "sha256:642085eaa22233aceadff9c69651bc51e8bf3f874fb6d7104ece2beb24b47c4a", size = 105113, upload-time = "2025-08-24T14:06:14.884Z" }, ] -[[package]] -name = "blosc2" -version = "2.5.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -dependencies = [ - { name = "msgpack", marker = "python_full_version < '3.10'" }, - { name = "ndindex", marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "py-cpuinfo", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f7/60/5bc8601f8ffcd5d8787b346898de8a0b454d031c3e158e3bbc312003984e/blosc2-2.5.1.tar.gz", hash = "sha256:47d5df50e7286edf81e629ece35f87f13f55c13c5e8545832188c420c75d1659", size = 4676483, upload-time = "2024-01-25T12:31:31.168Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/75/4511f1f9cea0aad8c5464736720a9de1e762e60e359cde6b7d69186e97b0/blosc2-2.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c861262b7fe317c1614a9b59b6c9edf409532b4a6aaf5b2f4ad0d79c6f800b57", size = 4417584, upload-time = "2024-01-25T12:30:31.487Z" }, - { url = "https://files.pythonhosted.org/packages/c0/8a/c7ce322f90750eb7fdde0948e5c6b9c47ed03e276f2481b701f9d2360d3a/blosc2-2.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f35b5d69a7a41e9d5054297d2540c25f8af5ea3c62e4a80ca7359292d783c04", size = 3471945, upload-time = "2024-01-25T12:30:34.745Z" }, - { url = "https://files.pythonhosted.org/packages/32/72/32bbaf583eea51b27c91ddb145de017d970ec0a835ec70e966925b00c9cf/blosc2-2.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:546fa39f397dd54b13d7c42a4f890afaf16c70fe478712070942d464c440ce03", size = 4210024, upload-time = "2024-01-25T12:30:36.556Z" }, - { url = "https://files.pythonhosted.org/packages/a5/8c/d4ab68a40004f93c1fd20dceb1899e54477425542bffa1f5ebab2647956f/blosc2-2.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5455af77e7e94159bb4966cae554f232ca2d52bb80cd3f878ecef39cf569da2a", size = 4374211, upload-time = "2024-01-25T12:30:38.355Z" }, - { url = "https://files.pythonhosted.org/packages/16/7d/c81b1caa0aded9f3e63c4dbab7a334681ded12ad80a18de34e7199939df5/blosc2-2.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b4dc4f595bf95c350c50bb77a8749cdd08a5dc2bdf3bdb18983d49a52d60b595", size = 4467025, upload-time = "2024-01-25T12:30:40.682Z" }, - { url = "https://files.pythonhosted.org/packages/9b/cd/56150f9367516f039b62923a5cb243771e2a1f440de0232b195f6e79f1ab/blosc2-2.5.1-cp310-cp310-win32.whl", hash = "sha256:873483bd5c6afb8d139039180ee57b74373232e87b032cb80389fd8bb883ea8e", size = 1948573, upload-time = "2024-01-25T12:30:43.019Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/0cf994e434b5ae860546cfe6941c58bdcdc27ed10009caffac9a29b66651/blosc2-2.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:d5a7ef00b82fbca069e949335f9c92ce7cbe2039a9fa2e2bd4f5f418043d6262", size = 2343858, upload-time = "2024-01-25T12:30:44.621Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f4/25a82b7191109beb0fc20c580f35667a2e86087ebac2f6a9e50469f618d7/blosc2-2.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:da826d42d616f8a939f27e1501b40e764fded66bc80177eeaefcebdbf3b3afb8", size = 4419509, upload-time = "2024-01-25T12:30:46.347Z" }, - { url = "https://files.pythonhosted.org/packages/03/50/b1d2bad6b9f77c3540b511b5dee77f213c8b179e942bbc34f5c410b92ede/blosc2-2.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ae2e0c5dc8561a6b17842ee4320b49621434c20e622c9e9f5c67c9c6eb3b06a3", size = 3472870, upload-time = "2024-01-25T12:30:48.698Z" }, - { url = "https://files.pythonhosted.org/packages/17/26/d34d521fc60b164738d986144642c58675bcdc344cc9fbb8c08e58bb11ab/blosc2-2.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af3cab9c12a4364c643266ee7d9583b526c0f484a291d72ec6efb09ea7ffbbf9", size = 4208463, upload-time = "2024-01-25T12:30:51.019Z" }, - { url = "https://files.pythonhosted.org/packages/2a/00/35169a0765044be69a3bfb73441ff8b9ea05d92d4660be622bbf90c46913/blosc2-2.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f03a723130cf07e4309fe34b1360c868f4376e862f8ff664eb40d019fdd3f6", size = 4371942, upload-time = "2024-01-25T12:30:53.304Z" }, - { url = "https://files.pythonhosted.org/packages/8f/9b/923e29af25f4c34b5b331829a87bc8397b18e8a361551230b70e6b0a785a/blosc2-2.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0fd109eef815ea1e50fde4f676388aa2f3bb5543502d125fb63f16ec7a014464", size = 4468495, upload-time = "2024-01-25T12:30:55.165Z" }, - { url = "https://files.pythonhosted.org/packages/9f/39/21d6d1dc00d765f5c1671f8c0b323096f0dde360e8e1d8005160679b4831/blosc2-2.5.1-cp311-cp311-win32.whl", hash = "sha256:1a3edc3256bad04d3db30c9de7eac3a820f96e741fc754cdabb6a9991e5c37e8", size = 1948151, upload-time = "2024-01-25T12:30:57.515Z" }, - { url = "https://files.pythonhosted.org/packages/d1/48/7d4be4e57342ec60c05d11478257b235cf33dc96c31049fb47e6ae98b652/blosc2-2.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:e7499e277c13334d54f84e74f429f32341f99f7b978deaf9a7c2e963904cb48c", size = 2344506, upload-time = "2024-01-25T12:30:59.641Z" }, - { url = "https://files.pythonhosted.org/packages/5b/4d/00824754a3b5b5c6ffb92a043b5122be6202039fc76a25fa913fe0d4e235/blosc2-2.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ab849d3adaeb035f2f16cf495cff1792b28d58dfb3de21b9459ee355c6bb8df3", size = 4414447, upload-time = "2024-01-25T12:31:01.39Z" }, - { url = "https://files.pythonhosted.org/packages/9b/21/9a887c9fdc46a5cfe4aa250eac4ed4f18213bf1996e113c1d1c662e31678/blosc2-2.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd66e60dafcc93d4c1f815d726d76f9fb067ecc9106a6c661010e709135c79ce", size = 3470989, upload-time = "2024-01-25T12:31:03.626Z" }, - { url = "https://files.pythonhosted.org/packages/92/8d/b13ea33ea4d5da344d1170638b2d7b3cc63a921b97f9a8184128fd78aacd/blosc2-2.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb5fcd1775b3884d9825aa51fb45253f45cfa21c77f4135fad5dc5db710c2a34", size = 4190080, upload-time = "2024-01-25T12:31:06.036Z" }, - { url = "https://files.pythonhosted.org/packages/33/bf/52c8385aa71ed8c42296016d48c80dcc41dd005e951a83970bde4e6d4ff2/blosc2-2.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19f79071a336fcf1eda01cd0171291a4ab82b16cf9a15d2b4d26c010146f13b5", size = 4355536, upload-time = "2024-01-25T12:31:07.839Z" }, - { url = "https://files.pythonhosted.org/packages/78/21/f1815f711f98c04eba5116d55041b2f64e17e8a9437d7c832d1168ed2fa8/blosc2-2.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:956a63231f1b448803e9b4bc3e704ea424c89fc14418d99093472c74f19c19e1", size = 4448554, upload-time = "2024-01-25T12:31:09.514Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e6/e05e987db27986ca8bdaaf442b94212e116a1eb937a8cd35250548704aa6/blosc2-2.5.1-cp312-cp312-win32.whl", hash = "sha256:5856e57e0e81f9018f1a12e803b9f768fa5533175092d72d165ac60069c7d2ab", size = 1940131, upload-time = "2024-01-25T12:31:12.02Z" }, - { url = "https://files.pythonhosted.org/packages/29/80/871cf959e5e0d3d2a177caf7b9f4b714850a50c33bb5cedd86a95c6e05b2/blosc2-2.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:585d780c5e85f251dec72b75a47666e4a261dbfe1d228769bca545e9fe07f480", size = 2338243, upload-time = "2024-01-25T12:31:13.967Z" }, - { url = "https://files.pythonhosted.org/packages/c9/4b/799dcec670a1d511d11a40e919241c26a273cfd2305a4f3fe7444c39758a/blosc2-2.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0cb9a6ac1abc466c12bdc90052f17545512de8f854e672a1ea4d2b40292323f5", size = 4417609, upload-time = "2024-01-25T12:31:15.635Z" }, - { url = "https://files.pythonhosted.org/packages/07/cb/e97de5b2c40b6c2106d5b68c1d823707a16386fc08781a6c7c609000866e/blosc2-2.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3def4650faa1db43143d821228ef58797108cc95d6698c4b1581909cc2b149ca", size = 3471971, upload-time = "2024-01-25T12:31:18.231Z" }, - { url = "https://files.pythonhosted.org/packages/65/c4/0c9740c4f5efb7adde87ed8b5af88c171a3ec007df912cc2fbfb2736963d/blosc2-2.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf6efecc1a22da26c73ff5c60d0dc086db1e7edcceb6b360dd193cda893bef28", size = 4209984, upload-time = "2024-01-25T12:31:20.855Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e2/a1367ad174006e5625a1c8ef1b526fc135adc6f546bfc46264ac8e508ebe/blosc2-2.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b473472b977b770aab3bf20d0feeee84ecd5bb8b15a675287e090ce818c1cd40", size = 4374484, upload-time = "2024-01-25T12:31:22.587Z" }, - { url = "https://files.pythonhosted.org/packages/62/88/a185ab2a50b930011396b601ae8e82e83dfa24597e5f1fd91f483f178a94/blosc2-2.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7afe59d35d93bf8da7db8de43f4d8aef277514de43953c1e5e416ca839b9023a", size = 4466917, upload-time = "2024-01-25T12:31:24.387Z" }, - { url = "https://files.pythonhosted.org/packages/b5/7c/6a7d46a6094a100e1bdfba7e1df584971efea00732006fdddcf0320d4846/blosc2-2.5.1-cp39-cp39-win32.whl", hash = "sha256:4315ae8d467fe91efa0dbe22004e967008f5fe021ebb3945518f5213d7c4511f", size = 1948282, upload-time = "2024-01-25T12:31:26.074Z" }, - { url = "https://files.pythonhosted.org/packages/5c/c1/f80115c66a181e2ff18027818d3e1f37a3d5133b350b3e25189e53e28cd8/blosc2-2.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:73eb5e569a91fbe67f7dd78efe6a1ca9a54afff2c847db5dfa675bfd6a424f60", size = 2343778, upload-time = "2024-01-25T12:31:27.669Z" }, -] - [[package]] name = "blosc2" version = "3.7.2" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", - "python_full_version == '3.10.*'", -] dependencies = [ - { name = "msgpack", marker = "python_full_version >= '3.10'" }, - { name = "ndindex", marker = "python_full_version >= '3.10'" }, - { name = "numexpr", version = "2.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' and platform_machine != 'wasm32'" }, - { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "platformdirs", marker = "python_full_version >= '3.10'" }, - { name = "py-cpuinfo", marker = "python_full_version >= '3.10' and platform_machine != 'wasm32'" }, - { name = "requests", marker = "python_full_version >= '3.10'" }, + { name = "msgpack" }, + { name = "ndindex" }, + { name = "numexpr", marker = "platform_machine != 'wasm32'" }, + { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "platformdirs" }, + { name = "py-cpuinfo", marker = "platform_machine != 'wasm32'" }, + { name = "requests" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1b/ba/c1d8a8303197d8d8d4ec9b83851fcabd2d57dc5d17af2406643dd015544e/blosc2-3.7.2.tar.gz", hash = "sha256:3e80bd0399241829e4a2100bef9d4de042da979514f5df6aa3378981823f1d9b", size = 3804422, upload-time = "2025-08-19T10:21:46.164Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/40/94e2a330f92d61e61e258dabc4ce76b8b4838e8cfa44f8b69e44fedc09d5/blosc2-3.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f2059d43521cdb5535af2c7b5656afc879502e12769a43178415327507aeb16f", size = 3942041, upload-time = "2025-08-19T10:21:04.382Z" }, - { url = "https://files.pythonhosted.org/packages/b2/1e/368789ab00752bfa3be9ace9e93cba7f434355c06ccabecae68cb9e2e156/blosc2-3.7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7be5e9d2d47f11f0e22f1a0dc8eb8cc94f388ebf0babb0cd2736b90a541f796", size = 3425736, upload-time = "2025-08-19T10:21:06.235Z" }, - { url = "https://files.pythonhosted.org/packages/db/2f/72ba2d0d2261f11cdccd060658232d092163446a6773a6c4568912076a49/blosc2-3.7.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ac311f60750fd7cb3526cfb139af363ed17b41c7f54ce2c8866b01029a925624", size = 4324634, upload-time = "2025-08-19T10:21:07.359Z" }, - { url = "https://files.pythonhosted.org/packages/6a/21/62c230beebe2a7d9104e3a8765bb1fa2d0337ee7a2ebaf5258ce23f66c61/blosc2-3.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4dfdde206c34a18c5a5c1abaf765c6aee39776946998a017bf48f5fb6ea6eaa6", size = 4461245, upload-time = "2025-08-19T10:21:08.603Z" }, - { url = "https://files.pythonhosted.org/packages/6b/76/b67437bc6f43c07a2068f4e47a101c53da55293c439a937175d2631e37d6/blosc2-3.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:c3566c0c2710a4ff7342a9536b55d8998f5b1c81a1bbec530a42934923c290b2", size = 2256284, upload-time = "2025-08-19T10:21:10.209Z" }, { url = "https://files.pythonhosted.org/packages/2e/6d/27aaf8c1b270498d9132482c17651fe47c2262e9537b2d4d22ac894f83d1/blosc2-3.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:be0a789d7a5a802e212e529fc33702b978e1a04d89007efa4efe8afc0c4b8da2", size = 3945327, upload-time = "2025-08-19T10:21:11.615Z" }, { url = "https://files.pythonhosted.org/packages/3f/8a/dae4ff33f37ec371ac6f62caf15bf0a863d66ac71d1b8a58df46a0cd9cda/blosc2-3.7.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bee13af632090512868e4e7dc37e5f2dbe426041437562af967c4f1948d36d6a", size = 3429381, upload-time = "2025-08-19T10:21:13.313Z" }, { url = "https://files.pythonhosted.org/packages/1b/1b/1858397556ae354041242e3137183400c625c3b53e584b29c3b19d805480/blosc2-3.7.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e7092e26a12f62f25ae3cef8c6ee7abff67ba6c46b73a71305880f236d050600", size = 4330058, upload-time = "2025-08-19T10:21:14.575Z" }, @@ -225,10 +163,8 @@ version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "os_name == 'nt'" }, - { name = "importlib-metadata", marker = "python_full_version < '3.10.2'" }, { name = "packaging" }, { name = "pyproject-hooks" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/25/1c/23e33405a7c9eac261dff640926b8b5adaed6a6eb3e1767d441ed611d0c0/build-1.3.0.tar.gz", hash = "sha256:698edd0ea270bde950f53aed21f3a0135672206f3911e0176261a31e0e07b397", size = 48544, upload-time = "2025-08-01T21:27:09.268Z" } wheels = [ @@ -265,18 +201,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, - { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, - { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, - { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, - { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, - { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, - { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, - { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, - { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, - { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, - { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, - { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, @@ -336,18 +260,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cc/08ed5a43f2996a16b462f64a7055c6e962803534924b9b2f1371d8c00b7b/cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf", size = 184288, upload-time = "2025-09-08T23:23:48.404Z" }, - { url = "https://files.pythonhosted.org/packages/3d/de/38d9726324e127f727b4ecc376bc85e505bfe61ef130eaf3f290c6847dd4/cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7", size = 180509, upload-time = "2025-09-08T23:23:49.73Z" }, - { url = "https://files.pythonhosted.org/packages/9b/13/c92e36358fbcc39cf0962e83223c9522154ee8630e1df7c0b3a39a8124e2/cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c", size = 208813, upload-time = "2025-09-08T23:23:51.263Z" }, - { url = "https://files.pythonhosted.org/packages/15/12/a7a79bd0df4c3bff744b2d7e52cc1b68d5e7e427b384252c42366dc1ecbc/cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165", size = 216498, upload-time = "2025-09-08T23:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/a3/ad/5c51c1c7600bdd7ed9a24a203ec255dccdd0ebf4527f7b922a0bde2fb6ed/cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534", size = 203243, upload-time = "2025-09-08T23:23:53.836Z" }, - { url = "https://files.pythonhosted.org/packages/32/f2/81b63e288295928739d715d00952c8c6034cb6c6a516b17d37e0c8be5600/cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f", size = 203158, upload-time = "2025-09-08T23:23:55.169Z" }, - { url = "https://files.pythonhosted.org/packages/1f/74/cc4096ce66f5939042ae094e2e96f53426a979864aa1f96a621ad128be27/cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63", size = 216548, upload-time = "2025-09-08T23:23:56.506Z" }, - { url = "https://files.pythonhosted.org/packages/e8/be/f6424d1dc46b1091ffcc8964fa7c0ab0cd36839dd2761b49c90481a6ba1b/cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2", size = 218897, upload-time = "2025-09-08T23:23:57.825Z" }, - { url = "https://files.pythonhosted.org/packages/f7/e0/dda537c2309817edf60109e39265f24f24aa7f050767e22c98c53fe7f48b/cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65", size = 211249, upload-time = "2025-09-08T23:23:59.139Z" }, - { url = "https://files.pythonhosted.org/packages/2b/e7/7c769804eb75e4c4b35e658dba01de1640a351a9653c3d49ca89d16ccc91/cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322", size = 218041, upload-time = "2025-09-08T23:24:00.496Z" }, - { url = "https://files.pythonhosted.org/packages/aa/d9/6218d78f920dcd7507fc16a766b5ef8f3b913cc7aa938e7fc80b9978d089/cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a", size = 172138, upload-time = "2025-09-08T23:24:01.7Z" }, - { url = "https://files.pythonhosted.org/packages/54/8f/a1e836f82d8e32a97e6b29cc8f641779181ac7363734f12df27db803ebda/cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9", size = 182794, upload-time = "2025-09-08T23:24:02.943Z" }, ] [[package]] @@ -356,17 +268,6 @@ version = "3.4.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/98/f3b8013223728a99b908c9344da3aa04ee6e3fa235f19409033eda92fb78/charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72", size = 207695, upload-time = "2025-08-09T07:55:36.452Z" }, - { url = "https://files.pythonhosted.org/packages/21/40/5188be1e3118c82dcb7c2a5ba101b783822cfb413a0268ed3be0468532de/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe", size = 147153, upload-time = "2025-08-09T07:55:38.467Z" }, - { url = "https://files.pythonhosted.org/packages/37/60/5d0d74bc1e1380f0b72c327948d9c2aca14b46a9efd87604e724260f384c/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601", size = 160428, upload-time = "2025-08-09T07:55:40.072Z" }, - { url = "https://files.pythonhosted.org/packages/85/9a/d891f63722d9158688de58d050c59dc3da560ea7f04f4c53e769de5140f5/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c", size = 157627, upload-time = "2025-08-09T07:55:41.706Z" }, - { url = "https://files.pythonhosted.org/packages/65/1a/7425c952944a6521a9cfa7e675343f83fd82085b8af2b1373a2409c683dc/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2", size = 152388, upload-time = "2025-08-09T07:55:43.262Z" }, - { url = "https://files.pythonhosted.org/packages/f0/c9/a2c9c2a355a8594ce2446085e2ec97fd44d323c684ff32042e2a6b718e1d/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0", size = 150077, upload-time = "2025-08-09T07:55:44.903Z" }, - { url = "https://files.pythonhosted.org/packages/3b/38/20a1f44e4851aa1c9105d6e7110c9d020e093dfa5836d712a5f074a12bf7/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0", size = 161631, upload-time = "2025-08-09T07:55:46.346Z" }, - { url = "https://files.pythonhosted.org/packages/a4/fa/384d2c0f57edad03d7bec3ebefb462090d8905b4ff5a2d2525f3bb711fac/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0", size = 159210, upload-time = "2025-08-09T07:55:47.539Z" }, - { url = "https://files.pythonhosted.org/packages/33/9e/eca49d35867ca2db336b6ca27617deed4653b97ebf45dfc21311ce473c37/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a", size = 153739, upload-time = "2025-08-09T07:55:48.744Z" }, - { url = "https://files.pythonhosted.org/packages/2a/91/26c3036e62dfe8de8061182d33be5025e2424002125c9500faff74a6735e/charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f", size = 99825, upload-time = "2025-08-09T07:55:50.305Z" }, - { url = "https://files.pythonhosted.org/packages/e2/c6/f05db471f81af1fa01839d44ae2a8bfeec8d2a8b4590f16c4e7393afd323/charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669", size = 107452, upload-time = "2025-08-09T07:55:51.461Z" }, { url = "https://files.pythonhosted.org/packages/7f/b5/991245018615474a60965a7c9cd2b4efbaabd16d582a5547c47ee1c7730b/charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b", size = 204483, upload-time = "2025-08-09T07:55:53.12Z" }, { url = "https://files.pythonhosted.org/packages/c7/2a/ae245c41c06299ec18262825c1569c5d3298fc920e4ddf56ab011b417efd/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64", size = 145520, upload-time = "2025-08-09T07:55:54.712Z" }, { url = "https://files.pythonhosted.org/packages/3a/a4/b3b6c76e7a635748c4421d2b92c7b8f90a432f98bda5082049af37ffc8e3/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91", size = 158876, upload-time = "2025-08-09T07:55:56.024Z" }, @@ -411,48 +312,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224, upload-time = "2025-08-09T07:56:51.369Z" }, { url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086, upload-time = "2025-08-09T07:56:52.722Z" }, { url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400, upload-time = "2025-08-09T07:56:55.172Z" }, - { url = "https://files.pythonhosted.org/packages/c2/ca/9a0983dd5c8e9733565cf3db4df2b0a2e9a82659fd8aa2a868ac6e4a991f/charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05", size = 207520, upload-time = "2025-08-09T07:57:11.026Z" }, - { url = "https://files.pythonhosted.org/packages/39/c6/99271dc37243a4f925b09090493fb96c9333d7992c6187f5cfe5312008d2/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e", size = 147307, upload-time = "2025-08-09T07:57:12.4Z" }, - { url = "https://files.pythonhosted.org/packages/e4/69/132eab043356bba06eb333cc2cc60c6340857d0a2e4ca6dc2b51312886b3/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99", size = 160448, upload-time = "2025-08-09T07:57:13.712Z" }, - { url = "https://files.pythonhosted.org/packages/04/9a/914d294daa4809c57667b77470533e65def9c0be1ef8b4c1183a99170e9d/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7", size = 157758, upload-time = "2025-08-09T07:57:14.979Z" }, - { url = "https://files.pythonhosted.org/packages/b0/a8/6f5bcf1bcf63cb45625f7c5cadca026121ff8a6c8a3256d8d8cd59302663/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7", size = 152487, upload-time = "2025-08-09T07:57:16.332Z" }, - { url = "https://files.pythonhosted.org/packages/c4/72/d3d0e9592f4e504f9dea08b8db270821c909558c353dc3b457ed2509f2fb/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19", size = 150054, upload-time = "2025-08-09T07:57:17.576Z" }, - { url = "https://files.pythonhosted.org/packages/20/30/5f64fe3981677fe63fa987b80e6c01042eb5ff653ff7cec1b7bd9268e54e/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312", size = 161703, upload-time = "2025-08-09T07:57:20.012Z" }, - { url = "https://files.pythonhosted.org/packages/e1/ef/dd08b2cac9284fd59e70f7d97382c33a3d0a926e45b15fc21b3308324ffd/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc", size = 159096, upload-time = "2025-08-09T07:57:21.329Z" }, - { url = "https://files.pythonhosted.org/packages/45/8c/dcef87cfc2b3f002a6478f38906f9040302c68aebe21468090e39cde1445/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34", size = 153852, upload-time = "2025-08-09T07:57:22.608Z" }, - { url = "https://files.pythonhosted.org/packages/63/86/9cbd533bd37883d467fcd1bd491b3547a3532d0fbb46de2b99feeebf185e/charset_normalizer-3.4.3-cp39-cp39-win32.whl", hash = "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432", size = 99840, upload-time = "2025-08-09T07:57:23.883Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d6/7e805c8e5c46ff9729c49950acc4ee0aeb55efb8b3a56687658ad10c3216/charset_normalizer-3.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca", size = 107438, upload-time = "2025-08-09T07:57:25.287Z" }, { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" }, ] -[[package]] -name = "click" -version = "8.1.8" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, -] - [[package]] name = "click" version = "8.2.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", - "python_full_version == '3.10.*'", -] dependencies = [ - { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } wheels = [ @@ -477,152 +345,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, ] -[[package]] -name = "coverage" -version = "7.10.7" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -sdist = { url = "https://files.pythonhosted.org/packages/51/26/d22c300112504f5f9a9fd2297ce33c35f3d353e4aeb987c8419453b2a7c2/coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239", size = 827704, upload-time = "2025-09-21T20:03:56.815Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/6c/3a3f7a46888e69d18abe3ccc6fe4cb16cccb1e6a2f99698931dafca489e6/coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a", size = 217987, upload-time = "2025-09-21T20:00:57.218Z" }, - { url = "https://files.pythonhosted.org/packages/03/94/952d30f180b1a916c11a56f5c22d3535e943aa22430e9e3322447e520e1c/coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5", size = 218388, upload-time = "2025-09-21T20:01:00.081Z" }, - { url = "https://files.pythonhosted.org/packages/50/2b/9e0cf8ded1e114bcd8b2fd42792b57f1c4e9e4ea1824cde2af93a67305be/coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17", size = 245148, upload-time = "2025-09-21T20:01:01.768Z" }, - { url = "https://files.pythonhosted.org/packages/19/20/d0384ac06a6f908783d9b6aa6135e41b093971499ec488e47279f5b846e6/coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b", size = 246958, upload-time = "2025-09-21T20:01:03.355Z" }, - { url = "https://files.pythonhosted.org/packages/60/83/5c283cff3d41285f8eab897651585db908a909c572bdc014bcfaf8a8b6ae/coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87", size = 248819, upload-time = "2025-09-21T20:01:04.968Z" }, - { url = "https://files.pythonhosted.org/packages/60/22/02eb98fdc5ff79f423e990d877693e5310ae1eab6cb20ae0b0b9ac45b23b/coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e", size = 245754, upload-time = "2025-09-21T20:01:06.321Z" }, - { url = "https://files.pythonhosted.org/packages/b4/bc/25c83bcf3ad141b32cd7dc45485ef3c01a776ca3aa8ef0a93e77e8b5bc43/coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e", size = 246860, upload-time = "2025-09-21T20:01:07.605Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b7/95574702888b58c0928a6e982038c596f9c34d52c5e5107f1eef729399b5/coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df", size = 244877, upload-time = "2025-09-21T20:01:08.829Z" }, - { url = "https://files.pythonhosted.org/packages/47/b6/40095c185f235e085df0e0b158f6bd68cc6e1d80ba6c7721dc81d97ec318/coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0", size = 245108, upload-time = "2025-09-21T20:01:10.527Z" }, - { url = "https://files.pythonhosted.org/packages/c8/50/4aea0556da7a4b93ec9168420d170b55e2eb50ae21b25062513d020c6861/coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13", size = 245752, upload-time = "2025-09-21T20:01:11.857Z" }, - { url = "https://files.pythonhosted.org/packages/6a/28/ea1a84a60828177ae3b100cb6723838523369a44ec5742313ed7db3da160/coverage-7.10.7-cp310-cp310-win32.whl", hash = "sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b", size = 220497, upload-time = "2025-09-21T20:01:13.459Z" }, - { url = "https://files.pythonhosted.org/packages/fc/1a/a81d46bbeb3c3fd97b9602ebaa411e076219a150489bcc2c025f151bd52d/coverage-7.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807", size = 221392, upload-time = "2025-09-21T20:01:14.722Z" }, - { url = "https://files.pythonhosted.org/packages/d2/5d/c1a17867b0456f2e9ce2d8d4708a4c3a089947d0bec9c66cdf60c9e7739f/coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59", size = 218102, upload-time = "2025-09-21T20:01:16.089Z" }, - { url = "https://files.pythonhosted.org/packages/54/f0/514dcf4b4e3698b9a9077f084429681bf3aad2b4a72578f89d7f643eb506/coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a", size = 218505, upload-time = "2025-09-21T20:01:17.788Z" }, - { url = "https://files.pythonhosted.org/packages/20/f6/9626b81d17e2a4b25c63ac1b425ff307ecdeef03d67c9a147673ae40dc36/coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699", size = 248898, upload-time = "2025-09-21T20:01:19.488Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ef/bd8e719c2f7417ba03239052e099b76ea1130ac0cbb183ee1fcaa58aaff3/coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d", size = 250831, upload-time = "2025-09-21T20:01:20.817Z" }, - { url = "https://files.pythonhosted.org/packages/a5/b6/bf054de41ec948b151ae2b79a55c107f5760979538f5fb80c195f2517718/coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e", size = 252937, upload-time = "2025-09-21T20:01:22.171Z" }, - { url = "https://files.pythonhosted.org/packages/0f/e5/3860756aa6f9318227443c6ce4ed7bf9e70bb7f1447a0353f45ac5c7974b/coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23", size = 249021, upload-time = "2025-09-21T20:01:23.907Z" }, - { url = "https://files.pythonhosted.org/packages/26/0f/bd08bd042854f7fd07b45808927ebcce99a7ed0f2f412d11629883517ac2/coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab", size = 250626, upload-time = "2025-09-21T20:01:25.721Z" }, - { url = "https://files.pythonhosted.org/packages/8e/a7/4777b14de4abcc2e80c6b1d430f5d51eb18ed1d75fca56cbce5f2db9b36e/coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82", size = 248682, upload-time = "2025-09-21T20:01:27.105Z" }, - { url = "https://files.pythonhosted.org/packages/34/72/17d082b00b53cd45679bad682fac058b87f011fd8b9fe31d77f5f8d3a4e4/coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2", size = 248402, upload-time = "2025-09-21T20:01:28.629Z" }, - { url = "https://files.pythonhosted.org/packages/81/7a/92367572eb5bdd6a84bfa278cc7e97db192f9f45b28c94a9ca1a921c3577/coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61", size = 249320, upload-time = "2025-09-21T20:01:30.004Z" }, - { url = "https://files.pythonhosted.org/packages/2f/88/a23cc185f6a805dfc4fdf14a94016835eeb85e22ac3a0e66d5e89acd6462/coverage-7.10.7-cp311-cp311-win32.whl", hash = "sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14", size = 220536, upload-time = "2025-09-21T20:01:32.184Z" }, - { url = "https://files.pythonhosted.org/packages/fe/ef/0b510a399dfca17cec7bc2f05ad8bd78cf55f15c8bc9a73ab20c5c913c2e/coverage-7.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2", size = 221425, upload-time = "2025-09-21T20:01:33.557Z" }, - { url = "https://files.pythonhosted.org/packages/51/7f/023657f301a276e4ba1850f82749bc136f5a7e8768060c2e5d9744a22951/coverage-7.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a", size = 220103, upload-time = "2025-09-21T20:01:34.929Z" }, - { url = "https://files.pythonhosted.org/packages/13/e4/eb12450f71b542a53972d19117ea5a5cea1cab3ac9e31b0b5d498df1bd5a/coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417", size = 218290, upload-time = "2025-09-21T20:01:36.455Z" }, - { url = "https://files.pythonhosted.org/packages/37/66/593f9be12fc19fb36711f19a5371af79a718537204d16ea1d36f16bd78d2/coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973", size = 218515, upload-time = "2025-09-21T20:01:37.982Z" }, - { url = "https://files.pythonhosted.org/packages/66/80/4c49f7ae09cafdacc73fbc30949ffe77359635c168f4e9ff33c9ebb07838/coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c", size = 250020, upload-time = "2025-09-21T20:01:39.617Z" }, - { url = "https://files.pythonhosted.org/packages/a6/90/a64aaacab3b37a17aaedd83e8000142561a29eb262cede42d94a67f7556b/coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7", size = 252769, upload-time = "2025-09-21T20:01:41.341Z" }, - { url = "https://files.pythonhosted.org/packages/98/2e/2dda59afd6103b342e096f246ebc5f87a3363b5412609946c120f4e7750d/coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6", size = 253901, upload-time = "2025-09-21T20:01:43.042Z" }, - { url = "https://files.pythonhosted.org/packages/53/dc/8d8119c9051d50f3119bb4a75f29f1e4a6ab9415cd1fa8bf22fcc3fb3b5f/coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59", size = 250413, upload-time = "2025-09-21T20:01:44.469Z" }, - { url = "https://files.pythonhosted.org/packages/98/b3/edaff9c5d79ee4d4b6d3fe046f2b1d799850425695b789d491a64225d493/coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b", size = 251820, upload-time = "2025-09-21T20:01:45.915Z" }, - { url = "https://files.pythonhosted.org/packages/11/25/9a0728564bb05863f7e513e5a594fe5ffef091b325437f5430e8cfb0d530/coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a", size = 249941, upload-time = "2025-09-21T20:01:47.296Z" }, - { url = "https://files.pythonhosted.org/packages/e0/fd/ca2650443bfbef5b0e74373aac4df67b08180d2f184b482c41499668e258/coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb", size = 249519, upload-time = "2025-09-21T20:01:48.73Z" }, - { url = "https://files.pythonhosted.org/packages/24/79/f692f125fb4299b6f963b0745124998ebb8e73ecdfce4ceceb06a8c6bec5/coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1", size = 251375, upload-time = "2025-09-21T20:01:50.529Z" }, - { url = "https://files.pythonhosted.org/packages/5e/75/61b9bbd6c7d24d896bfeec57acba78e0f8deac68e6baf2d4804f7aae1f88/coverage-7.10.7-cp312-cp312-win32.whl", hash = "sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256", size = 220699, upload-time = "2025-09-21T20:01:51.941Z" }, - { url = "https://files.pythonhosted.org/packages/ca/f3/3bf7905288b45b075918d372498f1cf845b5b579b723c8fd17168018d5f5/coverage-7.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba", size = 221512, upload-time = "2025-09-21T20:01:53.481Z" }, - { url = "https://files.pythonhosted.org/packages/5c/44/3e32dbe933979d05cf2dac5e697c8599cfe038aaf51223ab901e208d5a62/coverage-7.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf", size = 220147, upload-time = "2025-09-21T20:01:55.2Z" }, - { url = "https://files.pythonhosted.org/packages/9a/94/b765c1abcb613d103b64fcf10395f54d69b0ef8be6a0dd9c524384892cc7/coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d", size = 218320, upload-time = "2025-09-21T20:01:56.629Z" }, - { url = "https://files.pythonhosted.org/packages/72/4f/732fff31c119bb73b35236dd333030f32c4bfe909f445b423e6c7594f9a2/coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b", size = 218575, upload-time = "2025-09-21T20:01:58.203Z" }, - { url = "https://files.pythonhosted.org/packages/87/02/ae7e0af4b674be47566707777db1aa375474f02a1d64b9323e5813a6cdd5/coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e", size = 249568, upload-time = "2025-09-21T20:01:59.748Z" }, - { url = "https://files.pythonhosted.org/packages/a2/77/8c6d22bf61921a59bce5471c2f1f7ac30cd4ac50aadde72b8c48d5727902/coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b", size = 252174, upload-time = "2025-09-21T20:02:01.192Z" }, - { url = "https://files.pythonhosted.org/packages/b1/20/b6ea4f69bbb52dac0aebd62157ba6a9dddbfe664f5af8122dac296c3ee15/coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49", size = 253447, upload-time = "2025-09-21T20:02:02.701Z" }, - { url = "https://files.pythonhosted.org/packages/f9/28/4831523ba483a7f90f7b259d2018fef02cb4d5b90bc7c1505d6e5a84883c/coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911", size = 249779, upload-time = "2025-09-21T20:02:04.185Z" }, - { url = "https://files.pythonhosted.org/packages/a7/9f/4331142bc98c10ca6436d2d620c3e165f31e6c58d43479985afce6f3191c/coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0", size = 251604, upload-time = "2025-09-21T20:02:06.034Z" }, - { url = "https://files.pythonhosted.org/packages/ce/60/bda83b96602036b77ecf34e6393a3836365481b69f7ed7079ab85048202b/coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f", size = 249497, upload-time = "2025-09-21T20:02:07.619Z" }, - { url = "https://files.pythonhosted.org/packages/5f/af/152633ff35b2af63977edd835d8e6430f0caef27d171edf2fc76c270ef31/coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c", size = 249350, upload-time = "2025-09-21T20:02:10.34Z" }, - { url = "https://files.pythonhosted.org/packages/9d/71/d92105d122bd21cebba877228990e1646d862e34a98bb3374d3fece5a794/coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f", size = 251111, upload-time = "2025-09-21T20:02:12.122Z" }, - { url = "https://files.pythonhosted.org/packages/a2/9e/9fdb08f4bf476c912f0c3ca292e019aab6712c93c9344a1653986c3fd305/coverage-7.10.7-cp313-cp313-win32.whl", hash = "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698", size = 220746, upload-time = "2025-09-21T20:02:13.919Z" }, - { url = "https://files.pythonhosted.org/packages/b1/b1/a75fd25df44eab52d1931e89980d1ada46824c7a3210be0d3c88a44aaa99/coverage-7.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843", size = 221541, upload-time = "2025-09-21T20:02:15.57Z" }, - { url = "https://files.pythonhosted.org/packages/14/3a/d720d7c989562a6e9a14b2c9f5f2876bdb38e9367126d118495b89c99c37/coverage-7.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546", size = 220170, upload-time = "2025-09-21T20:02:17.395Z" }, - { url = "https://files.pythonhosted.org/packages/bb/22/e04514bf2a735d8b0add31d2b4ab636fc02370730787c576bb995390d2d5/coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c", size = 219029, upload-time = "2025-09-21T20:02:18.936Z" }, - { url = "https://files.pythonhosted.org/packages/11/0b/91128e099035ece15da3445d9015e4b4153a6059403452d324cbb0a575fa/coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15", size = 219259, upload-time = "2025-09-21T20:02:20.44Z" }, - { url = "https://files.pythonhosted.org/packages/8b/51/66420081e72801536a091a0c8f8c1f88a5c4bf7b9b1bdc6222c7afe6dc9b/coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4", size = 260592, upload-time = "2025-09-21T20:02:22.313Z" }, - { url = "https://files.pythonhosted.org/packages/5d/22/9b8d458c2881b22df3db5bb3e7369e63d527d986decb6c11a591ba2364f7/coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0", size = 262768, upload-time = "2025-09-21T20:02:24.287Z" }, - { url = "https://files.pythonhosted.org/packages/f7/08/16bee2c433e60913c610ea200b276e8eeef084b0d200bdcff69920bd5828/coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0", size = 264995, upload-time = "2025-09-21T20:02:26.133Z" }, - { url = "https://files.pythonhosted.org/packages/20/9d/e53eb9771d154859b084b90201e5221bca7674ba449a17c101a5031d4054/coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65", size = 259546, upload-time = "2025-09-21T20:02:27.716Z" }, - { url = "https://files.pythonhosted.org/packages/ad/b0/69bc7050f8d4e56a89fb550a1577d5d0d1db2278106f6f626464067b3817/coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541", size = 262544, upload-time = "2025-09-21T20:02:29.216Z" }, - { url = "https://files.pythonhosted.org/packages/ef/4b/2514b060dbd1bc0aaf23b852c14bb5818f244c664cb16517feff6bb3a5ab/coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6", size = 260308, upload-time = "2025-09-21T20:02:31.226Z" }, - { url = "https://files.pythonhosted.org/packages/54/78/7ba2175007c246d75e496f64c06e94122bdb914790a1285d627a918bd271/coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999", size = 258920, upload-time = "2025-09-21T20:02:32.823Z" }, - { url = "https://files.pythonhosted.org/packages/c0/b3/fac9f7abbc841409b9a410309d73bfa6cfb2e51c3fada738cb607ce174f8/coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2", size = 261434, upload-time = "2025-09-21T20:02:34.86Z" }, - { url = "https://files.pythonhosted.org/packages/ee/51/a03bec00d37faaa891b3ff7387192cef20f01604e5283a5fabc95346befa/coverage-7.10.7-cp313-cp313t-win32.whl", hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a", size = 221403, upload-time = "2025-09-21T20:02:37.034Z" }, - { url = "https://files.pythonhosted.org/packages/53/22/3cf25d614e64bf6d8e59c7c669b20d6d940bb337bdee5900b9ca41c820bb/coverage-7.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb", size = 222469, upload-time = "2025-09-21T20:02:39.011Z" }, - { url = "https://files.pythonhosted.org/packages/49/a1/00164f6d30d8a01c3c9c48418a7a5be394de5349b421b9ee019f380df2a0/coverage-7.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb", size = 220731, upload-time = "2025-09-21T20:02:40.939Z" }, - { url = "https://files.pythonhosted.org/packages/23/9c/5844ab4ca6a4dd97a1850e030a15ec7d292b5c5cb93082979225126e35dd/coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520", size = 218302, upload-time = "2025-09-21T20:02:42.527Z" }, - { url = "https://files.pythonhosted.org/packages/f0/89/673f6514b0961d1f0e20ddc242e9342f6da21eaba3489901b565c0689f34/coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32", size = 218578, upload-time = "2025-09-21T20:02:44.468Z" }, - { url = "https://files.pythonhosted.org/packages/05/e8/261cae479e85232828fb17ad536765c88dd818c8470aca690b0ac6feeaa3/coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f", size = 249629, upload-time = "2025-09-21T20:02:46.503Z" }, - { url = "https://files.pythonhosted.org/packages/82/62/14ed6546d0207e6eda876434e3e8475a3e9adbe32110ce896c9e0c06bb9a/coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a", size = 252162, upload-time = "2025-09-21T20:02:48.689Z" }, - { url = "https://files.pythonhosted.org/packages/ff/49/07f00db9ac6478e4358165a08fb41b469a1b053212e8a00cb02f0d27a05f/coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360", size = 253517, upload-time = "2025-09-21T20:02:50.31Z" }, - { url = "https://files.pythonhosted.org/packages/a2/59/c5201c62dbf165dfbc91460f6dbbaa85a8b82cfa6131ac45d6c1bfb52deb/coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69", size = 249632, upload-time = "2025-09-21T20:02:51.971Z" }, - { url = "https://files.pythonhosted.org/packages/07/ae/5920097195291a51fb00b3a70b9bbd2edbfe3c84876a1762bd1ef1565ebc/coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14", size = 251520, upload-time = "2025-09-21T20:02:53.858Z" }, - { url = "https://files.pythonhosted.org/packages/b9/3c/a815dde77a2981f5743a60b63df31cb322c944843e57dbd579326625a413/coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe", size = 249455, upload-time = "2025-09-21T20:02:55.807Z" }, - { url = "https://files.pythonhosted.org/packages/aa/99/f5cdd8421ea656abefb6c0ce92556709db2265c41e8f9fc6c8ae0f7824c9/coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e", size = 249287, upload-time = "2025-09-21T20:02:57.784Z" }, - { url = "https://files.pythonhosted.org/packages/c3/7a/e9a2da6a1fc5d007dd51fca083a663ab930a8c4d149c087732a5dbaa0029/coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd", size = 250946, upload-time = "2025-09-21T20:02:59.431Z" }, - { url = "https://files.pythonhosted.org/packages/ef/5b/0b5799aa30380a949005a353715095d6d1da81927d6dbed5def2200a4e25/coverage-7.10.7-cp314-cp314-win32.whl", hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2", size = 221009, upload-time = "2025-09-21T20:03:01.324Z" }, - { url = "https://files.pythonhosted.org/packages/da/b0/e802fbb6eb746de006490abc9bb554b708918b6774b722bb3a0e6aa1b7de/coverage-7.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681", size = 221804, upload-time = "2025-09-21T20:03:03.4Z" }, - { url = "https://files.pythonhosted.org/packages/9e/e8/71d0c8e374e31f39e3389bb0bd19e527d46f00ea8571ec7ec8fd261d8b44/coverage-7.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880", size = 220384, upload-time = "2025-09-21T20:03:05.111Z" }, - { url = "https://files.pythonhosted.org/packages/62/09/9a5608d319fa3eba7a2019addeacb8c746fb50872b57a724c9f79f146969/coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63", size = 219047, upload-time = "2025-09-21T20:03:06.795Z" }, - { url = "https://files.pythonhosted.org/packages/f5/6f/f58d46f33db9f2e3647b2d0764704548c184e6f5e014bef528b7f979ef84/coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2", size = 219266, upload-time = "2025-09-21T20:03:08.495Z" }, - { url = "https://files.pythonhosted.org/packages/74/5c/183ffc817ba68e0b443b8c934c8795553eb0c14573813415bd59941ee165/coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d", size = 260767, upload-time = "2025-09-21T20:03:10.172Z" }, - { url = "https://files.pythonhosted.org/packages/0f/48/71a8abe9c1ad7e97548835e3cc1adbf361e743e9d60310c5f75c9e7bf847/coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0", size = 262931, upload-time = "2025-09-21T20:03:11.861Z" }, - { url = "https://files.pythonhosted.org/packages/84/fd/193a8fb132acfc0a901f72020e54be5e48021e1575bb327d8ee1097a28fd/coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699", size = 265186, upload-time = "2025-09-21T20:03:13.539Z" }, - { url = "https://files.pythonhosted.org/packages/b1/8f/74ecc30607dd95ad50e3034221113ccb1c6d4e8085cc761134782995daae/coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9", size = 259470, upload-time = "2025-09-21T20:03:15.584Z" }, - { url = "https://files.pythonhosted.org/packages/0f/55/79ff53a769f20d71b07023ea115c9167c0bb56f281320520cf64c5298a96/coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f", size = 262626, upload-time = "2025-09-21T20:03:17.673Z" }, - { url = "https://files.pythonhosted.org/packages/88/e2/dac66c140009b61ac3fc13af673a574b00c16efdf04f9b5c740703e953c0/coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1", size = 260386, upload-time = "2025-09-21T20:03:19.36Z" }, - { url = "https://files.pythonhosted.org/packages/a2/f1/f48f645e3f33bb9ca8a496bc4a9671b52f2f353146233ebd7c1df6160440/coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0", size = 258852, upload-time = "2025-09-21T20:03:21.007Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3b/8442618972c51a7affeead957995cfa8323c0c9bcf8fa5a027421f720ff4/coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399", size = 261534, upload-time = "2025-09-21T20:03:23.12Z" }, - { url = "https://files.pythonhosted.org/packages/b2/dc/101f3fa3a45146db0cb03f5b4376e24c0aac818309da23e2de0c75295a91/coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235", size = 221784, upload-time = "2025-09-21T20:03:24.769Z" }, - { url = "https://files.pythonhosted.org/packages/4c/a1/74c51803fc70a8a40d7346660379e144be772bab4ac7bb6e6b905152345c/coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d", size = 222905, upload-time = "2025-09-21T20:03:26.93Z" }, - { url = "https://files.pythonhosted.org/packages/12/65/f116a6d2127df30bcafbceef0302d8a64ba87488bf6f73a6d8eebf060873/coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a", size = 220922, upload-time = "2025-09-21T20:03:28.672Z" }, - { url = "https://files.pythonhosted.org/packages/a3/ad/d1c25053764b4c42eb294aae92ab617d2e4f803397f9c7c8295caa77a260/coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3", size = 217978, upload-time = "2025-09-21T20:03:30.362Z" }, - { url = "https://files.pythonhosted.org/packages/52/2f/b9f9daa39b80ece0b9548bbb723381e29bc664822d9a12c2135f8922c22b/coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c", size = 218370, upload-time = "2025-09-21T20:03:32.147Z" }, - { url = "https://files.pythonhosted.org/packages/dd/6e/30d006c3b469e58449650642383dddf1c8fb63d44fdf92994bfd46570695/coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396", size = 244802, upload-time = "2025-09-21T20:03:33.919Z" }, - { url = "https://files.pythonhosted.org/packages/b0/49/8a070782ce7e6b94ff6a0b6d7c65ba6bc3091d92a92cef4cd4eb0767965c/coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40", size = 246625, upload-time = "2025-09-21T20:03:36.09Z" }, - { url = "https://files.pythonhosted.org/packages/6a/92/1c1c5a9e8677ce56d42b97bdaca337b2d4d9ebe703d8c174ede52dbabd5f/coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594", size = 248399, upload-time = "2025-09-21T20:03:38.342Z" }, - { url = "https://files.pythonhosted.org/packages/c0/54/b140edee7257e815de7426d5d9846b58505dffc29795fff2dfb7f8a1c5a0/coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a", size = 245142, upload-time = "2025-09-21T20:03:40.591Z" }, - { url = "https://files.pythonhosted.org/packages/e4/9e/6d6b8295940b118e8b7083b29226c71f6154f7ff41e9ca431f03de2eac0d/coverage-7.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b", size = 246284, upload-time = "2025-09-21T20:03:42.355Z" }, - { url = "https://files.pythonhosted.org/packages/db/e5/5e957ca747d43dbe4d9714358375c7546cb3cb533007b6813fc20fce37ad/coverage-7.10.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3", size = 244353, upload-time = "2025-09-21T20:03:44.218Z" }, - { url = "https://files.pythonhosted.org/packages/9a/45/540fc5cc92536a1b783b7ef99450bd55a4b3af234aae35a18a339973ce30/coverage-7.10.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0", size = 244430, upload-time = "2025-09-21T20:03:46.065Z" }, - { url = "https://files.pythonhosted.org/packages/75/0b/8287b2e5b38c8fe15d7e3398849bb58d382aedc0864ea0fa1820e8630491/coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f", size = 245311, upload-time = "2025-09-21T20:03:48.19Z" }, - { url = "https://files.pythonhosted.org/packages/0c/1d/29724999984740f0c86d03e6420b942439bf5bd7f54d4382cae386a9d1e9/coverage-7.10.7-cp39-cp39-win32.whl", hash = "sha256:5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431", size = 220500, upload-time = "2025-09-21T20:03:50.024Z" }, - { url = "https://files.pythonhosted.org/packages/43/11/4b1e6b129943f905ca54c339f343877b55b365ae2558806c1be4f7476ed5/coverage-7.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07", size = 221408, upload-time = "2025-09-21T20:03:51.803Z" }, - { url = "https://files.pythonhosted.org/packages/ec/16/114df1c291c22cac3b0c127a73e0af5c12ed7bbb6558d310429a0ae24023/coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260", size = 209952, upload-time = "2025-09-21T20:03:53.918Z" }, -] - -[package.optional-dependencies] -toml = [ - { name = "tomli", marker = "python_full_version < '3.10'" }, -] - [[package]] name = "coverage" version = "7.13.5" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", - "python_full_version == '3.10.*'", -] sdist = { url = "https://files.pythonhosted.org/packages/9d/e0/70553e3000e345daff267cec284ce4cbf3fc141b6da229ac52775b5428f1/coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179", size = 915967, upload-time = "2026-03-17T10:33:18.341Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/33/e8c48488c29a73fd089f9d71f9653c1be7478f2ad6b5bc870db11a55d23d/coverage-7.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0723d2c96324561b9aa76fb982406e11d93cdb388a7a7da2b16e04719cf7ca5", size = 219255, upload-time = "2026-03-17T10:29:51.081Z" }, - { url = "https://files.pythonhosted.org/packages/da/bd/b0ebe9f677d7f4b74a3e115eec7ddd4bcf892074963a00d91e8b164a6386/coverage-7.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52f444e86475992506b32d4e5ca55c24fc88d73bcbda0e9745095b28ef4dc0cf", size = 219772, upload-time = "2026-03-17T10:29:52.867Z" }, - { url = "https://files.pythonhosted.org/packages/48/cc/5cb9502f4e01972f54eedd48218bb203fe81e294be606a2bc93970208013/coverage-7.13.5-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:704de6328e3d612a8f6c07000a878ff38181ec3263d5a11da1db294fa6a9bdf8", size = 246532, upload-time = "2026-03-17T10:29:54.688Z" }, - { url = "https://files.pythonhosted.org/packages/7d/d8/3217636d86c7e7b12e126e4f30ef1581047da73140614523af7495ed5f2d/coverage-7.13.5-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a1a6d79a14e1ec1832cabc833898636ad5f3754a678ef8bb4908515208bf84f4", size = 248333, upload-time = "2026-03-17T10:29:56.221Z" }, - { url = "https://files.pythonhosted.org/packages/2b/30/2002ac6729ba2d4357438e2ed3c447ad8562866c8c63fc16f6dfc33afe56/coverage-7.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79060214983769c7ba3f0cee10b54c97609dca4d478fa1aa32b914480fd5738d", size = 250211, upload-time = "2026-03-17T10:29:57.938Z" }, - { url = "https://files.pythonhosted.org/packages/6c/85/552496626d6b9359eb0e2f86f920037c9cbfba09b24d914c6e1528155f7d/coverage-7.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:356e76b46783a98c2a2fe81ec79df4883a1e62895ea952968fb253c114e7f930", size = 252125, upload-time = "2026-03-17T10:29:59.388Z" }, - { url = "https://files.pythonhosted.org/packages/44/21/40256eabdcbccdb6acf6b381b3016a154399a75fe39d406f790ae84d1f3c/coverage-7.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0cef0cdec915d11254a7f549c1170afecce708d30610c6abdded1f74e581666d", size = 247219, upload-time = "2026-03-17T10:30:01.199Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e8/96e2a6c3f21a0ea77d7830b254a1542d0328acc8d7bdf6a284ba7e529f77/coverage-7.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dc022073d063b25a402454e5712ef9e007113e3a676b96c5f29b2bda29352f40", size = 248248, upload-time = "2026-03-17T10:30:03.317Z" }, - { url = "https://files.pythonhosted.org/packages/da/ba/8477f549e554827da390ec659f3c38e4b6d95470f4daafc2d8ff94eaa9c2/coverage-7.13.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9b74db26dfea4f4e50d48a4602207cd1e78be33182bc9cbf22da94f332f99878", size = 246254, upload-time = "2026-03-17T10:30:04.832Z" }, - { url = "https://files.pythonhosted.org/packages/55/59/bc22aef0e6aa179d5b1b001e8b3654785e9adf27ef24c93dc4228ebd5d68/coverage-7.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ad146744ca4fd09b50c482650e3c1b1f4dfa1d4792e0a04a369c7f23336f0400", size = 250067, upload-time = "2026-03-17T10:30:06.535Z" }, - { url = "https://files.pythonhosted.org/packages/de/1b/c6a023a160806a5137dca53468fd97530d6acad24a22003b1578a9c2e429/coverage-7.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c555b48be1853fe3997c11c4bd521cdd9a9612352de01fa4508f16ec341e6fe0", size = 246521, upload-time = "2026-03-17T10:30:08.486Z" }, - { url = "https://files.pythonhosted.org/packages/2d/3f/3532c85a55aa2f899fa17c186f831cfa1aa434d88ff792a709636f64130e/coverage-7.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7034b5c56a58ae5e85f23949d52c14aca2cfc6848a31764995b7de88f13a1ea0", size = 247126, upload-time = "2026-03-17T10:30:09.966Z" }, - { url = "https://files.pythonhosted.org/packages/aa/2e/b9d56af4a24ef45dfbcda88e06870cb7d57b2b0bfa3a888d79b4c8debd76/coverage-7.13.5-cp310-cp310-win32.whl", hash = "sha256:eb7fdf1ef130660e7415e0253a01a7d5a88c9c4d158bcf75cbbd922fd65a5b58", size = 221860, upload-time = "2026-03-17T10:30:11.393Z" }, - { url = "https://files.pythonhosted.org/packages/9f/cc/d938417e7a4d7f0433ad4edee8bb2acdc60dc7ac5af19e2a07a048ecbee3/coverage-7.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:3e1bb5f6c78feeb1be3475789b14a0f0a5b47d505bfc7267126ccbd50289999e", size = 222788, upload-time = "2026-03-17T10:30:12.886Z" }, { 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 = 219381, upload-time = "2026-03-17T10:30:14.68Z" }, { url = "https://files.pythonhosted.org/packages/35/8b/cd129b0ca4afe886a6ce9d183c44d8301acbd4ef248622e7c49a23145605/coverage-7.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587", size = 219880, upload-time = "2026-03-17T10:30:16.231Z" }, { 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 = 250303, upload-time = "2026-03-17T10:30:17.748Z" }, @@ -718,7 +446,7 @@ wheels = [ [package.optional-dependencies] toml = [ - { name = "tomli", marker = "python_full_version >= '3.10' and python_full_version <= '3.11'" }, + { name = "tomli", marker = "python_full_version <= '3.11'" }, ] [[package]] @@ -740,10 +468,6 @@ version = "1.8.16" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ca/d4/722d0bcc7986172ac2ef3c979ad56a1030e3afd44ced136d45f8142b1f4a/debugpy-1.8.16.tar.gz", hash = "sha256:31e69a1feb1cf6b51efbed3f6c9b0ef03bc46ff050679c4be7ea6d2e23540870", size = 1643809, upload-time = "2025-08-06T18:00:02.647Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/fd/f1b75ebc61d90882595b81d808efd3573c082e1c3407850d9dccac4ae904/debugpy-1.8.16-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:2a3958fb9c2f40ed8ea48a0d34895b461de57a1f9862e7478716c35d76f56c65", size = 2085511, upload-time = "2025-08-06T18:00:05.067Z" }, - { url = "https://files.pythonhosted.org/packages/df/5e/c5c1934352871128b30a1a144a58b5baa546e1b57bd47dbed788bad4431c/debugpy-1.8.16-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5ca7314042e8a614cc2574cd71f6ccd7e13a9708ce3c6d8436959eae56f2378", size = 3562094, upload-time = "2025-08-06T18:00:06.66Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d5/2ebe42377e5a78dc786afc25e61ee83c5628d63f32dfa41092597d52fe83/debugpy-1.8.16-cp310-cp310-win32.whl", hash = "sha256:8624a6111dc312ed8c363347a0b59c5acc6210d897e41a7c069de3c53235c9a6", size = 5234277, upload-time = "2025-08-06T18:00:08.429Z" }, - { url = "https://files.pythonhosted.org/packages/54/f8/e774ad16a60b9913213dbabb7472074c5a7b0d84f07c1f383040a9690057/debugpy-1.8.16-cp310-cp310-win_amd64.whl", hash = "sha256:fee6db83ea5c978baf042440cfe29695e1a5d48a30147abf4c3be87513609817", size = 5266011, upload-time = "2025-08-06T18:00:10.162Z" }, { url = "https://files.pythonhosted.org/packages/63/d6/ad70ba8b49b23fa286fb21081cf732232cc19374af362051da9c7537ae52/debugpy-1.8.16-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:67371b28b79a6a12bcc027d94a06158f2fde223e35b5c4e0783b6f9d3b39274a", size = 2184063, upload-time = "2025-08-06T18:00:11.885Z" }, { url = "https://files.pythonhosted.org/packages/aa/49/7b03e88dea9759a4c7910143f87f92beb494daaae25560184ff4ae883f9e/debugpy-1.8.16-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2abae6dd02523bec2dee16bd6b0781cccb53fd4995e5c71cc659b5f45581898", size = 3134837, upload-time = "2025-08-06T18:00:13.782Z" }, { url = "https://files.pythonhosted.org/packages/5d/52/b348930316921de7565fbe37a487d15409041713004f3d74d03eb077dbd4/debugpy-1.8.16-cp311-cp311-win32.whl", hash = "sha256:f8340a3ac2ed4f5da59e064aa92e39edd52729a88fbde7bbaa54e08249a04493", size = 5159142, upload-time = "2025-08-06T18:00:15.391Z" }, @@ -756,10 +480,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4d/a0/c95baae08a75bceabb79868d663a0736655e427ab9c81fb848da29edaeac/debugpy-1.8.16-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee89e948bc236a5c43c4214ac62d28b29388453f5fd328d739035e205365f0b", size = 4222491, upload-time = "2025-08-06T18:00:25.806Z" }, { url = "https://files.pythonhosted.org/packages/5b/2f/1c8db6ddd8a257c3cd2c46413b267f1d5fa3df910401c899513ce30392d6/debugpy-1.8.16-cp313-cp313-win32.whl", hash = "sha256:cf358066650439847ec5ff3dae1da98b5461ea5da0173d93d5e10f477c94609a", size = 5281126, upload-time = "2025-08-06T18:00:27.207Z" }, { url = "https://files.pythonhosted.org/packages/d3/ba/c3e154ab307366d6c5a9c1b68de04914e2ce7fa2f50d578311d8cc5074b2/debugpy-1.8.16-cp313-cp313-win_amd64.whl", hash = "sha256:b5aea1083f6f50023e8509399d7dc6535a351cc9f2e8827d1e093175e4d9fa4c", size = 5323094, upload-time = "2025-08-06T18:00:29.03Z" }, - { url = "https://files.pythonhosted.org/packages/35/40/acdad5944e508d5e936979ad3e96e56b78ba6d7fa75aaffc4426cb921e12/debugpy-1.8.16-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:135ccd2b1161bade72a7a099c9208811c137a150839e970aeaf121c2467debe8", size = 2086696, upload-time = "2025-08-06T18:00:36.469Z" }, - { url = "https://files.pythonhosted.org/packages/2d/eb/8d6a2cf3b29e272b5dfebe6f384f8457977d4fd7a02dab2cae4d421dbae2/debugpy-1.8.16-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:211238306331a9089e253fd997213bc4a4c65f949271057d6695953254095376", size = 3557329, upload-time = "2025-08-06T18:00:38.189Z" }, - { url = "https://files.pythonhosted.org/packages/00/7b/63b9cc4d3c6980c702911c0f6a9748933ce4e4f16ae0ec4fdef7690f6662/debugpy-1.8.16-cp39-cp39-win32.whl", hash = "sha256:88eb9ffdfb59bf63835d146c183d6dba1f722b3ae2a5f4b9fc03e925b3358922", size = 5235114, upload-time = "2025-08-06T18:00:39.586Z" }, - { url = "https://files.pythonhosted.org/packages/05/cf/80947f57e0ef4d6e33ec9c3a109a542678eba465723bf8b599719238eb93/debugpy-1.8.16-cp39-cp39-win_amd64.whl", hash = "sha256:c2c47c2e52b40449552843b913786499efcc3dbc21d6c49287d939cd0dbc49fd", size = 5266799, upload-time = "2025-08-06T18:00:41.013Z" }, { url = "https://files.pythonhosted.org/packages/52/57/ecc9ae29fa5b2d90107cd1d9bf8ed19aacb74b2264d986ae9d44fe9bdf87/debugpy-1.8.16-py2.py3-none-any.whl", hash = "sha256:19c9521962475b87da6f673514f7fd610328757ec993bf7ec0d8c96f9a325f9e", size = 5287700, upload-time = "2025-08-06T18:00:42.333Z" }, ] @@ -799,18 +519,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" }, ] -[[package]] -name = "exceptiongroup" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - [[package]] name = "executing" version = "2.2.1" @@ -869,16 +577,6 @@ version = "3.2.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260, upload-time = "2025-08-07T13:24:33.51Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/ed/6bfa4109fcb23a58819600392564fea69cdc6551ffd5e69ccf1d52a40cbc/greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c", size = 271061, upload-time = "2025-08-07T13:17:15.373Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fc/102ec1a2fc015b3a7652abab7acf3541d58c04d3d17a8d3d6a44adae1eb1/greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590", size = 629475, upload-time = "2025-08-07T13:42:54.009Z" }, - { url = "https://files.pythonhosted.org/packages/c5/26/80383131d55a4ac0fb08d71660fd77e7660b9db6bdb4e8884f46d9f2cc04/greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c", size = 640802, upload-time = "2025-08-07T13:45:25.52Z" }, - { url = "https://files.pythonhosted.org/packages/e9/49/547b93b7c0428ede7b3f309bc965986874759f7d89e4e04aeddbc9699acb/greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31", size = 635417, upload-time = "2025-08-07T13:18:25.189Z" }, - { url = "https://files.pythonhosted.org/packages/7f/91/ae2eb6b7979e2f9b035a9f612cf70f1bf54aad4e1d125129bef1eae96f19/greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d", size = 584358, upload-time = "2025-08-07T13:18:23.708Z" }, - { url = "https://files.pythonhosted.org/packages/f7/85/433de0c9c0252b22b16d413c9407e6cb3b41df7389afc366ca204dbc1393/greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5", size = 1113550, upload-time = "2025-08-07T13:42:37.467Z" }, - { url = "https://files.pythonhosted.org/packages/a1/8d/88f3ebd2bc96bf7747093696f4335a0a8a4c5acfcf1b757717c0d2474ba3/greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f", size = 1137126, upload-time = "2025-08-07T13:18:20.239Z" }, - { url = "https://files.pythonhosted.org/packages/f1/29/74242b7d72385e29bcc5563fba67dad94943d7cd03552bac320d597f29b2/greenlet-3.2.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f47617f698838ba98f4ff4189aef02e7343952df3a615f847bb575c3feb177a7", size = 1544904, upload-time = "2025-11-04T12:42:04.763Z" }, - { url = "https://files.pythonhosted.org/packages/c8/e2/1572b8eeab0f77df5f6729d6ab6b141e4a84ee8eb9bc8c1e7918f94eda6d/greenlet-3.2.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af41be48a4f60429d5cad9d22175217805098a9ef7c40bfef44f7669fb9d74d8", size = 1611228, upload-time = "2025-11-04T12:42:08.423Z" }, - { url = "https://files.pythonhosted.org/packages/d6/6f/b60b0291d9623c496638c582297ead61f43c4b72eef5e9c926ef4565ec13/greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c", size = 298654, upload-time = "2025-08-07T13:50:00.469Z" }, { url = "https://files.pythonhosted.org/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2", size = 272305, upload-time = "2025-08-07T13:15:41.288Z" }, { url = "https://files.pythonhosted.org/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246", size = 632472, upload-time = "2025-08-07T13:42:55.044Z" }, { url = "https://files.pythonhosted.org/packages/ae/8f/95d48d7e3d433e6dae5b1682e4292242a53f22df82e6d3dda81b1701a960/greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3", size = 644646, upload-time = "2025-08-07T13:45:26.523Z" }, @@ -917,81 +615,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508, upload-time = "2025-11-04T12:42:23.427Z" }, { url = "https://files.pythonhosted.org/packages/0d/da/343cd760ab2f92bac1845ca07ee3faea9fe52bee65f7bcb19f16ad7de08b/greenlet-3.2.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:015d48959d4add5d6c9f6c5210ee3803a830dce46356e3bc326d6776bde54681", size = 1680760, upload-time = "2025-11-04T12:42:25.341Z" }, { url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425, upload-time = "2025-08-07T13:32:27.59Z" }, - { url = "https://files.pythonhosted.org/packages/f7/c0/93885c4106d2626bf51fdec377d6aef740dfa5c4877461889a7cf8e565cc/greenlet-3.2.4-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:b6a7c19cf0d2742d0809a4c05975db036fdff50cd294a93632d6a310bf9ac02c", size = 269859, upload-time = "2025-08-07T13:16:16.003Z" }, - { url = "https://files.pythonhosted.org/packages/4d/f5/33f05dc3ba10a02dedb1485870cf81c109227d3d3aa280f0e48486cac248/greenlet-3.2.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:27890167f55d2387576d1f41d9487ef171849ea0359ce1510ca6e06c8bece11d", size = 627610, upload-time = "2025-08-07T13:43:01.345Z" }, - { url = "https://files.pythonhosted.org/packages/b2/a7/9476decef51a0844195f99ed5dc611d212e9b3515512ecdf7321543a7225/greenlet-3.2.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:18d9260df2b5fbf41ae5139e1be4e796d99655f023a636cd0e11e6406cca7d58", size = 639417, upload-time = "2025-08-07T13:45:32.094Z" }, - { url = "https://files.pythonhosted.org/packages/5f/d3/844e714a9bbd39034144dca8b658dcd01839b72bb0ec7d8014e33e3705f0/greenlet-3.2.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:16458c245a38991aa19676900d48bd1a6f2ce3e16595051a4db9d012154e8433", size = 634020, upload-time = "2025-08-07T13:18:36.841Z" }, - { url = "https://files.pythonhosted.org/packages/6b/4c/f3de2a8de0e840ecb0253ad0dc7e2bb3747348e798ec7e397d783a3cb380/greenlet-3.2.4-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9913f1a30e4526f432991f89ae263459b1c64d1608c0d22a5c79c287b3c70df", size = 582817, upload-time = "2025-08-07T13:18:35.48Z" }, - { url = "https://files.pythonhosted.org/packages/89/80/7332915adc766035c8980b161c2e5d50b2f941f453af232c164cff5e0aeb/greenlet-3.2.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b90654e092f928f110e0007f572007c9727b5265f7632c2fa7415b4689351594", size = 1111985, upload-time = "2025-08-07T13:42:42.425Z" }, - { url = "https://files.pythonhosted.org/packages/66/71/1928e2c80197353bcb9b50aa19c4d8e26ee6d7a900c564907665cf4b9a41/greenlet-3.2.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81701fd84f26330f0d5f4944d4e92e61afe6319dcd9775e39396e39d7c3e5f98", size = 1136137, upload-time = "2025-08-07T13:18:26.168Z" }, - { url = "https://files.pythonhosted.org/packages/4b/bf/7bd33643e48ed45dcc0e22572f650767832bd4e1287f97434943cc402148/greenlet-3.2.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:28a3c6b7cd72a96f61b0e4b2a36f681025b60ae4779cc73c1535eb5f29560b10", size = 1542941, upload-time = "2025-11-04T12:42:27.427Z" }, - { url = "https://files.pythonhosted.org/packages/9b/74/4bc433f91d0d09a1c22954a371f9df928cb85e72640870158853a83415e5/greenlet-3.2.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:52206cd642670b0b320a1fd1cbfd95bca0e043179c1d8a045f2c6109dfe973be", size = 1609685, upload-time = "2025-11-04T12:42:29.242Z" }, - { url = "https://files.pythonhosted.org/packages/89/48/a5dc74dde38aeb2b15d418cec76ed50e1dd3d620ccda84d8199703248968/greenlet-3.2.4-cp39-cp39-win32.whl", hash = "sha256:65458b409c1ed459ea899e939f0e1cdb14f58dbc803f2f93c5eab5694d32671b", size = 281400, upload-time = "2025-08-07T14:02:20.263Z" }, - { url = "https://files.pythonhosted.org/packages/e5/44/342c4591db50db1076b8bda86ed0ad59240e3e1da17806a4cf10a6d0e447/greenlet-3.2.4-cp39-cp39-win_amd64.whl", hash = "sha256:d2e685ade4dafd447ede19c31277a224a239a0a1a4eca4e6390efedf20260cfb", size = 298533, upload-time = "2025-08-07T13:56:34.168Z" }, -] - -[[package]] -name = "h5py" -version = "3.14.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -dependencies = [ - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5d/57/dfb3c5c3f1bf5f5ef2e59a22dec4ff1f3d7408b55bfcefcfb0ea69ef21c6/h5py-3.14.0.tar.gz", hash = "sha256:2372116b2e0d5d3e5e705b7f663f7c8d96fa79a4052d250484ef91d24d6a08f4", size = 424323, upload-time = "2025-06-06T14:06:15.01Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/89/06cbb421e01dea2e338b3154326523c05d9698f89a01f9d9b65e1ec3fb18/h5py-3.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:24df6b2622f426857bda88683b16630014588a0e4155cba44e872eb011c4eaed", size = 3332522, upload-time = "2025-06-06T14:04:13.775Z" }, - { url = "https://files.pythonhosted.org/packages/c3/e7/6c860b002329e408348735bfd0459e7b12f712c83d357abeef3ef404eaa9/h5py-3.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ff2389961ee5872de697054dd5a033b04284afc3fb52dc51d94561ece2c10c6", size = 2831051, upload-time = "2025-06-06T14:04:18.206Z" }, - { url = "https://files.pythonhosted.org/packages/fa/cd/3dd38cdb7cc9266dc4d85f27f0261680cb62f553f1523167ad7454e32b11/h5py-3.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:016e89d3be4c44f8d5e115fab60548e518ecd9efe9fa5c5324505a90773e6f03", size = 4324677, upload-time = "2025-06-06T14:04:23.438Z" }, - { url = "https://files.pythonhosted.org/packages/b1/45/e1a754dc7cd465ba35e438e28557119221ac89b20aaebef48282654e3dc7/h5py-3.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1223b902ef0b5d90bcc8a4778218d6d6cd0f5561861611eda59fa6c52b922f4d", size = 4557272, upload-time = "2025-06-06T14:04:28.863Z" }, - { url = "https://files.pythonhosted.org/packages/5c/06/f9506c1531645829d302c420851b78bb717af808dde11212c113585fae42/h5py-3.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:852b81f71df4bb9e27d407b43071d1da330d6a7094a588efa50ef02553fa7ce4", size = 2866734, upload-time = "2025-06-06T14:04:33.5Z" }, - { url = "https://files.pythonhosted.org/packages/61/1b/ad24a8ce846cf0519695c10491e99969d9d203b9632c4fcd5004b1641c2e/h5py-3.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f30dbc58f2a0efeec6c8836c97f6c94afd769023f44e2bb0ed7b17a16ec46088", size = 3352382, upload-time = "2025-06-06T14:04:37.95Z" }, - { url = "https://files.pythonhosted.org/packages/36/5b/a066e459ca48b47cc73a5c668e9924d9619da9e3c500d9fb9c29c03858ec/h5py-3.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:543877d7f3d8f8a9828ed5df6a0b78ca3d8846244b9702e99ed0d53610b583a8", size = 2852492, upload-time = "2025-06-06T14:04:42.092Z" }, - { url = "https://files.pythonhosted.org/packages/08/0c/5e6aaf221557314bc15ba0e0da92e40b24af97ab162076c8ae009320a42b/h5py-3.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c497600c0496548810047257e36360ff551df8b59156d3a4181072eed47d8ad", size = 4298002, upload-time = "2025-06-06T14:04:47.106Z" }, - { url = "https://files.pythonhosted.org/packages/21/d4/d461649cafd5137088fb7f8e78fdc6621bb0c4ff2c090a389f68e8edc136/h5py-3.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:723a40ee6505bd354bfd26385f2dae7bbfa87655f4e61bab175a49d72ebfc06b", size = 4516618, upload-time = "2025-06-06T14:04:52.467Z" }, - { url = "https://files.pythonhosted.org/packages/db/0c/6c3f879a0f8e891625817637fad902da6e764e36919ed091dc77529004ac/h5py-3.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:d2744b520440a996f2dae97f901caa8a953afc055db4673a993f2d87d7f38713", size = 2874888, upload-time = "2025-06-06T14:04:56.95Z" }, - { url = "https://files.pythonhosted.org/packages/3e/77/8f651053c1843391e38a189ccf50df7e261ef8cd8bfd8baba0cbe694f7c3/h5py-3.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e0045115d83272090b0717c555a31398c2c089b87d212ceba800d3dc5d952e23", size = 3312740, upload-time = "2025-06-06T14:05:01.193Z" }, - { url = "https://files.pythonhosted.org/packages/ff/10/20436a6cf419b31124e59fefc78d74cb061ccb22213226a583928a65d715/h5py-3.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6da62509b7e1d71a7d110478aa25d245dd32c8d9a1daee9d2a42dba8717b047a", size = 2829207, upload-time = "2025-06-06T14:05:05.061Z" }, - { url = "https://files.pythonhosted.org/packages/3f/19/c8bfe8543bfdd7ccfafd46d8cfd96fce53d6c33e9c7921f375530ee1d39a/h5py-3.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554ef0ced3571366d4d383427c00c966c360e178b5fb5ee5bb31a435c424db0c", size = 4708455, upload-time = "2025-06-06T14:05:11.528Z" }, - { url = "https://files.pythonhosted.org/packages/86/f9/f00de11c82c88bfc1ef22633557bfba9e271e0cb3189ad704183fc4a2644/h5py-3.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cbd41f4e3761f150aa5b662df991868ca533872c95467216f2bec5fcad84882", size = 4929422, upload-time = "2025-06-06T14:05:18.399Z" }, - { url = "https://files.pythonhosted.org/packages/7a/6d/6426d5d456f593c94b96fa942a9b3988ce4d65ebaf57d7273e452a7222e8/h5py-3.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:bf4897d67e613ecf5bdfbdab39a1158a64df105827da70ea1d90243d796d367f", size = 2862845, upload-time = "2025-06-06T14:05:23.699Z" }, - { url = "https://files.pythonhosted.org/packages/6c/c2/7efe82d09ca10afd77cd7c286e42342d520c049a8c43650194928bcc635c/h5py-3.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:aa4b7bbce683379b7bf80aaba68e17e23396100336a8d500206520052be2f812", size = 3289245, upload-time = "2025-06-06T14:05:28.24Z" }, - { url = "https://files.pythonhosted.org/packages/4f/31/f570fab1239b0d9441024b92b6ad03bb414ffa69101a985e4c83d37608bd/h5py-3.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9603a501a04fcd0ba28dd8f0995303d26a77a980a1f9474b3417543d4c6174", size = 2807335, upload-time = "2025-06-06T14:05:31.997Z" }, - { url = "https://files.pythonhosted.org/packages/0d/ce/3a21d87896bc7e3e9255e0ad5583ae31ae9e6b4b00e0bcb2a67e2b6acdbc/h5py-3.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8cbaf6910fa3983c46172666b0b8da7b7bd90d764399ca983236f2400436eeb", size = 4700675, upload-time = "2025-06-06T14:05:37.38Z" }, - { url = "https://files.pythonhosted.org/packages/e7/ec/86f59025306dcc6deee5fda54d980d077075b8d9889aac80f158bd585f1b/h5py-3.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d90e6445ab7c146d7f7981b11895d70bc1dd91278a4f9f9028bc0c95e4a53f13", size = 4921632, upload-time = "2025-06-06T14:05:43.464Z" }, - { url = "https://files.pythonhosted.org/packages/3f/6d/0084ed0b78d4fd3e7530c32491f2884140d9b06365dac8a08de726421d4a/h5py-3.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:ae18e3de237a7a830adb76aaa68ad438d85fe6e19e0d99944a3ce46b772c69b3", size = 2852929, upload-time = "2025-06-06T14:05:47.659Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ac/9ea82488c8790ee5b6ad1a807cd7dc3b9dadfece1cd0e0e369f68a7a8937/h5py-3.14.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5cc1601e78027cedfec6dd50efb4802f018551754191aeb58d948bd3ec3bd7a", size = 3345097, upload-time = "2025-06-06T14:05:51.984Z" }, - { url = "https://files.pythonhosted.org/packages/6c/bc/a172ecaaf287e3af2f837f23b470b0a2229c79555a0da9ac8b5cc5bed078/h5py-3.14.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e59d2136a8b302afd25acdf7a89b634e0eb7c66b1a211ef2d0457853768a2ef", size = 2843320, upload-time = "2025-06-06T14:05:55.754Z" }, - { url = "https://files.pythonhosted.org/packages/66/40/b423b57696514e05aa7bb06150ef96667d0e0006cc6de7ab52c71734ab51/h5py-3.14.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:573c33ad056ac7c1ab6d567b6db9df3ffc401045e3f605736218f96c1e0490c6", size = 4326368, upload-time = "2025-06-06T14:06:00.782Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/e088f89f04fdbe57ddf9de377f857158d3daa38cf5d0fb20ef9bd489e313/h5py-3.14.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ccbe17dc187c0c64178f1a10aa274ed3a57d055117588942b8a08793cc448216", size = 4559686, upload-time = "2025-06-06T14:06:07.416Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e4/fb8032d0e5480b1db9b419b5b50737b61bb3c7187c49d809975d62129fb0/h5py-3.14.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f025cf30ae738c4c4e38c7439a761a71ccfcce04c2b87b2a2ac64e8c5171d43", size = 2877166, upload-time = "2025-06-06T14:06:13.05Z" }, ] [[package]] name = "h5py" version = "3.16.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", - "python_full_version == '3.10.*'", -] dependencies = [ - { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/db/33/acd0ce6863b6c0d7735007df01815403f5589a21ff8c2e1ee2587a38f548/h5py-3.16.0.tar.gz", hash = "sha256:a0dbaad796840ccaa67a4c144a0d0c8080073c34c76d5a6941d6818678ef2738", size = 446526, upload-time = "2026-03-06T13:49:08.07Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/6b/231413e58a787a89b316bb0d1777da3c62257e4797e09afd8d17ad3549dc/h5py-3.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e06f864bedb2c8e7c1358e6c73af48519e317457c444d6f3d332bb4e8fa6d7d9", size = 3724137, upload-time = "2026-03-06T13:47:35.242Z" }, - { url = "https://files.pythonhosted.org/packages/74/f9/557ce3aad0fe8471fb5279bab0fc56ea473858a022c4ce8a0b8f303d64e9/h5py-3.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec86d4fffd87a0f4cb3d5796ceb5a50123a2a6d99b43e616e5504e66a953eca3", size = 3090112, upload-time = "2026-03-06T13:47:37.634Z" }, - { url = "https://files.pythonhosted.org/packages/7a/f5/e15b3d0dc8a18e56409a839e6468d6fb589bc5207c917399c2e0706eeb44/h5py-3.16.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:86385ea895508220b8a7e45efa428aeafaa586bd737c7af9ee04661d8d84a10d", size = 4844847, upload-time = "2026-03-06T13:47:39.811Z" }, - { url = "https://files.pythonhosted.org/packages/cb/92/a8851d936547efe30cc0ce5245feac01f3ec6171f7899bc3f775c72030b3/h5py-3.16.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:8975273c2c5921c25700193b408e28d6bdd0111c37468b2d4e25dcec4cd1d84d", size = 5065352, upload-time = "2026-03-06T13:47:41.489Z" }, - { url = "https://files.pythonhosted.org/packages/2b/ae/f2adc5d0ca9626db3277a3d87516e124cbc5d0eea0bd79bc085702d04f2c/h5py-3.16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1677ad48b703f44efc9ea0c3ab284527f81bc4f318386aaaebc5fede6bbae56f", size = 4839173, upload-time = "2026-03-06T13:47:43.586Z" }, - { url = "https://files.pythonhosted.org/packages/64/0b/e0c8c69da1d8838da023a50cd3080eae5d475691f7636b35eff20bb6ef20/h5py-3.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c4dd4cf5f0a4e36083f73172f6cfc25a5710789269547f132a20975bfe2434c", size = 5076216, upload-time = "2026-03-06T13:47:45.315Z" }, - { url = "https://files.pythonhosted.org/packages/66/35/d88fd6718832133c885004c61ceeeb24dbd6397ef877dbed6b3a64d6a286/h5py-3.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:bdef06507725b455fccba9c16529121a5e1fbf56aa375f7d9713d9e8ff42454d", size = 3183639, upload-time = "2026-03-06T13:47:47.041Z" }, { url = "https://files.pythonhosted.org/packages/ba/95/a825894f3e45cbac7554c4e97314ce886b233a20033787eda755ca8fecc7/h5py-3.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:719439d14b83f74eeb080e9650a6c7aa6d0d9ea0ca7f804347b05fac6fbf18af", size = 3721663, upload-time = "2026-03-06T13:47:49.599Z" }, { url = "https://files.pythonhosted.org/packages/bf/3b/38ff88b347c3e346cda1d3fc1b65a7aa75d40632228d8b8a5d7b58508c24/h5py-3.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3f0a0e136f2e95dd0b67146abb6668af4f1a69c81ef8651a2d316e8e01de447", size = 3087630, upload-time = "2026-03-06T13:47:51.249Z" }, { url = "https://files.pythonhosted.org/packages/98/a8/2594cef906aee761601eff842c7dc598bea2b394a3e1c00966832b8eeb7c/h5py-3.16.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a6fbc5367d4046801f9b7db9191b31895f22f1c6df1f9987d667854cac493538", size = 4823472, upload-time = "2026-03-06T13:47:53.085Z" }, @@ -1098,18 +733,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, ] -[[package]] -name = "importlib-resources" -version = "6.5.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, -] - [[package]] name = "iniconfig" version = "2.1.0" @@ -1127,8 +750,7 @@ dependencies = [ { name = "appnope", marker = "sys_platform == 'darwin'" }, { name = "comm" }, { name = "debugpy" }, - { name = "ipython", version = "8.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "ipython" }, { name = "jupyter-client" }, { name = "jupyter-core" }, { name = "matplotlib-inline" }, @@ -1144,54 +766,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fc/c7/b445faca8deb954fe536abebff4ece5b097b923de482b26e78448c89d1dd/ipykernel-6.30.1-py3-none-any.whl", hash = "sha256:aa6b9fb93dca949069d8b85b6c79b2518e32ac583ae9c7d37c51d119e18b3fb4", size = 117484, upload-time = "2025-08-04T15:47:32.622Z" }, ] -[[package]] -name = "ipython" -version = "8.18.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, - { name = "decorator", marker = "python_full_version < '3.10'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.10'" }, - { name = "jedi", marker = "python_full_version < '3.10'" }, - { name = "matplotlib-inline", marker = "python_full_version < '3.10'" }, - { name = "pexpect", marker = "python_full_version < '3.10' and sys_platform != 'win32'" }, - { name = "prompt-toolkit", marker = "python_full_version < '3.10'" }, - { name = "pygments", marker = "python_full_version < '3.10'" }, - { name = "stack-data", marker = "python_full_version < '3.10'" }, - { name = "traitlets", marker = "python_full_version < '3.10'" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/b9/3ba6c45a6df813c09a48bac313c22ff83efa26cbb55011218d925a46e2ad/ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27", size = 5486330, upload-time = "2023-11-27T09:58:34.596Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/6b/d9fdcdef2eb6a23f391251fde8781c38d42acd82abe84d054cb74f7863b0/ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397", size = 808161, upload-time = "2023-11-27T09:58:30.538Z" }, -] - [[package]] name = "ipython" version = "8.37.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", - "python_full_version == '3.10.*'", -] dependencies = [ - { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, - { name = "decorator", marker = "python_full_version >= '3.10'" }, - { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" }, - { name = "jedi", marker = "python_full_version >= '3.10'" }, - { name = "matplotlib-inline", marker = "python_full_version >= '3.10'" }, - { name = "pexpect", marker = "python_full_version >= '3.10' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit", marker = "python_full_version >= '3.10'" }, - { name = "pygments", marker = "python_full_version >= '3.10'" }, - { name = "stack-data", marker = "python_full_version >= '3.10'" }, - { name = "traitlets", marker = "python_full_version >= '3.10'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.12'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/85/31/10ac88f3357fc276dc8a64e8880c82e80e7459326ae1d0a211b40abf6665/ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216", size = 5606088, upload-time = "2025-05-31T16:39:09.613Z" } wheels = [ @@ -1203,10 +792,9 @@ name = "itables" version = "2.5.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ipython", version = "8.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "ipython" }, + { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, { name = "pandas" }, ] sdist = { url = "https://files.pythonhosted.org/packages/02/a2/4652db589b5767ead6d1dd8016e94e6adc5ec9e9552ccd17cf1886900b04/itables-2.5.2.tar.gz", hash = "sha256:ec34bbacfbf4305570ea75b36970de442f924126f3701c323a5a46018de84c8a", size = 2356416, upload-time = "2025-09-02T20:14:41.722Z" } @@ -1232,16 +820,6 @@ version = "1.2.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/0b/14/fc5bdb637996df181e5c4fa3b15dcc27d33215e6c41753564ae453bdb40f/jellyfish-1.2.1.tar.gz", hash = "sha256:72d2fda61b23babe862018729be73c8b0dc12e3e6601f36f6e65d905e249f4db", size = 364417, upload-time = "2025-10-11T19:36:37.219Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/3f/b1734e45d4fe1620916616829a87486b26b7455352a23b9939110a26dbde/jellyfish-1.2.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b35d4b5b688f759ffd075190a9850b04671bad14c5b37124eb43e99306ec16ea", size = 325627, upload-time = "2025-10-11T19:34:53.199Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a3/eb770060544a553281654aa6b03f379231c1ef677e6b9a62735c4a9b3c69/jellyfish-1.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b37b76ea338c4a473c34a9b9e1e033a78aafb9040a8c0eea579fc5805d8e4b46", size = 320325, upload-time = "2025-10-11T19:34:54.914Z" }, - { url = "https://files.pythonhosted.org/packages/4b/81/461c20d8e60801a4da33c81f970991910db0dc398e7ddfad9a25bf0da764/jellyfish-1.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:137cfcc26396d0f2e1265ac61f800bb921921ea722a43dd897e58190f767c474", size = 353308, upload-time = "2025-10-11T19:34:56.195Z" }, - { url = "https://files.pythonhosted.org/packages/bc/ca/b53c9869d4cc8183f0d09b073459937d8f39df2b02d1ab05b25c6f4cfc14/jellyfish-1.2.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab1bfea271ce4bda09d975080d5465cf5a8b127e7c0ea61ea3f972417a7a2193", size = 362931, upload-time = "2025-10-11T19:34:57.527Z" }, - { url = "https://files.pythonhosted.org/packages/b3/5f/a08be31c51249d13b324be4c443acd18e172f9f511b70d63c791209ec9f9/jellyfish-1.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2348f698f9c1d72023afc8d39939045421a01da9b7e3078e3029227e35f28419", size = 360672, upload-time = "2025-10-11T19:34:58.821Z" }, - { url = "https://files.pythonhosted.org/packages/ec/7e/a04b682872295507437f96567cfea5cc71199cbbd8a541c8310b9b48126e/jellyfish-1.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4072e21ad4036af41bd57b447b1dda64fe60aa679cfa8854ba0a0338152439f1", size = 533943, upload-time = "2025-10-11T19:35:00.839Z" }, - { url = "https://files.pythonhosted.org/packages/70/8d/a4f6e6b8fc626e4a42ec190428efe74806290bf7fd9d61a864e68b8a9f74/jellyfish-1.2.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cf6cd68921f2bacc547ba1cf64ad0e76bc1727f3bab13bba2e5f5869aba038b1", size = 554469, upload-time = "2025-10-11T19:35:02.152Z" }, - { url = "https://files.pythonhosted.org/packages/f9/80/66ad92b292e0a8096ebd2c2d6363219cf23df02e90a275b6d3b9df740d29/jellyfish-1.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:01647c12261bc1f7b102e918e7665497176d87f6fc96271439c8855872bc2606", size = 523928, upload-time = "2025-10-11T19:35:03.47Z" }, - { url = "https://files.pythonhosted.org/packages/9f/de/fab776c8c4eb997d147bc6a1374d14e7229f42915ccd3471619e761020f2/jellyfish-1.2.1-cp310-cp310-win32.whl", hash = "sha256:ddf05ea471da2808d77ecfa425d8884124b4754f4d483afa7703b6655530cf5c", size = 209286, upload-time = "2025-10-11T19:35:04.438Z" }, - { url = "https://files.pythonhosted.org/packages/f3/95/3257c5483c9a31819aad394db64451b81937f7749aed98b26a51928da2cb/jellyfish-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:e4a210a960f3917da757b0581750b6e0a8db9acef68dafbc1b6e2ae39e847ba8", size = 213572, upload-time = "2025-10-11T19:35:05.605Z" }, { url = "https://files.pythonhosted.org/packages/ee/67/5d5ec4004d92573cbccd33fc84d0ad61e523b29f7b17b062913b183961e1/jellyfish-1.2.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9913789a98ccf49213fbb1dabc597847a0ec33d3b0e151689498f4b38ba9be0f", size = 325488, upload-time = "2025-10-11T19:35:06.87Z" }, { url = "https://files.pythonhosted.org/packages/83/21/6cf3add349cd0002cc586178bd8f1fd006894e5c70f959a8db5507cfe075/jellyfish-1.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4e36d9000d4f7e1a35689a74ec7749d27a216dfa6c47cac2e5ad3de8a523bd69", size = 320226, upload-time = "2025-10-11T19:35:08.314Z" }, { url = "https://files.pythonhosted.org/packages/1c/ed/b5458b09482913caece2e9f807599318e48490b01c3c3134b636ecd7af8c/jellyfish-1.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7853d2ed7d6929c029312ec849410f1ea7ae76ce72ad1140fb73f6e8a1e6aa4f", size = 353091, upload-time = "2025-10-11T19:35:09.395Z" }, @@ -1282,24 +860,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f9/e1/fcc7c5919d871537942425f707b764af65b76c7b88377aa71083c5280e37/jellyfish-1.2.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:29cfa8bfb72aacf2d611a3313b358ed4d4140fa3d3efcffea750c8e7f8acb1aa", size = 523057, upload-time = "2025-10-11T19:35:54.423Z" }, { url = "https://files.pythonhosted.org/packages/95/65/ee5289540b2015643493cc29b50350dbe63ca1977a902de5295a4df8c25a/jellyfish-1.2.1-cp314-cp314-win32.whl", hash = "sha256:f121218dc33fb318c34ddd889dc7362606ce1316af2bb63b73cc1df81523ca34", size = 209340, upload-time = "2025-10-11T19:35:55.69Z" }, { url = "https://files.pythonhosted.org/packages/bc/e2/fa5de38380b0f5bd531b27a78acb0dc6118dab0b21f56d36008b829aa7de/jellyfish-1.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:9a73b5c6425a70ebd440579a677eb4f03b327b2f59090db34e6c937aeea5aabd", size = 213399, upload-time = "2025-10-11T19:35:56.776Z" }, - { url = "https://files.pythonhosted.org/packages/7c/0c/2255426e82d24ec16bed2d146d659c0c32717860b131749c8738fed46b1b/jellyfish-1.2.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5335f622458aa105289a8e358bc32ecd1b9634b6ffec3e77ea3577e49c297171", size = 325549, upload-time = "2025-10-11T19:35:58.004Z" }, - { url = "https://files.pythonhosted.org/packages/07/74/42370f075b63425d461b7f9d56e2b46007e58bec869c7baa76fb311e87b2/jellyfish-1.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c51e565f85ce38cf9388c4f916d53888b0fa34788fcebe3aff3db24948e0960", size = 320368, upload-time = "2025-10-11T19:35:59.139Z" }, - { url = "https://files.pythonhosted.org/packages/6c/0e/6bf5a04a256e129e2c8bb3b22bf2a41126f3937c27f1c5671f7b7bc3aef4/jellyfish-1.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14bbb30d988dec1d12183cf5d4621c908f98add2009c72a185e8c3e8d00b804f", size = 353549, upload-time = "2025-10-11T19:36:00.13Z" }, - { url = "https://files.pythonhosted.org/packages/b3/19/3a1c7f99bcfb2281460d166624876d388c452b1e78536b90c27b40444ac9/jellyfish-1.2.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9930e20f0e9f65ad1d57d98290c2be3abd75812d058815605f44a56056fb9a66", size = 362965, upload-time = "2025-10-11T19:36:01.144Z" }, - { url = "https://files.pythonhosted.org/packages/3a/ff/f9cc3182e3da6797b58451a9607575c0e3707dea171142459d4b0bb8d6e1/jellyfish-1.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0028857c5381c9d55e21cc6cb0d7f9545c3a9a7bb7dbca3960fe0a898c691ac2", size = 361231, upload-time = "2025-10-11T19:36:02.155Z" }, - { url = "https://files.pythonhosted.org/packages/13/98/8ef4064872d87162c29f9227cb55936c2bb3942ce89ad032ad2a6bc7e6bc/jellyfish-1.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:56da7632e029912af25e25422fae3b6df318400297d552791f4b21da6d815ed6", size = 534251, upload-time = "2025-10-11T19:36:03.461Z" }, - { url = "https://files.pythonhosted.org/packages/2e/71/17e93464e23aa3eab9b1c781b091e40221c64f36433da393d5e285efc150/jellyfish-1.2.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a3cab91020e3ff7565e55a611ec3e3257c093ac950d55778a48bfc8c57562b6e", size = 554514, upload-time = "2025-10-11T19:36:04.585Z" }, - { url = "https://files.pythonhosted.org/packages/3e/1b/4d993f0c1d79fdf62ff76cb5b6ba8b591766ed8f03baae395bb31c534a6e/jellyfish-1.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0b21c1596ce283fd7ee954eb0eeb007d59e480364324bcd91ad55146e91f3936", size = 524005, upload-time = "2025-10-11T19:36:05.632Z" }, - { url = "https://files.pythonhosted.org/packages/94/d9/47e84d15927babd9c4b9f6635e81e085c7062467b9b1f536ed40ebf1a201/jellyfish-1.2.1-cp39-cp39-win32.whl", hash = "sha256:1098ce1f84ae3f147f0a18a6803ffb09b9c8cd5fedce42465643ca0b5c9d0224", size = 209401, upload-time = "2025-10-11T19:36:06.658Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d2/c2f103e8a70d54553d7df28fc65db371d2f684cdfdc5666d7ae34246f714/jellyfish-1.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:4b013876109d91fa6fc871ffa4e0dbfda11820c33dc4ad0e2967b3fc1187f804", size = 213806, upload-time = "2025-10-11T19:36:07.557Z" }, - { url = "https://files.pythonhosted.org/packages/f5/2b/ef99cc9ed0c14171b62dfb68dca7e1cecec02b4c007ea47709a3d51ffba6/jellyfish-1.2.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c499ea3a134130797c50e367687a6a46a12653c59af381bee92c41a5ab0bd55d", size = 327121, upload-time = "2025-10-11T19:36:09.777Z" }, - { url = "https://files.pythonhosted.org/packages/c1/c9/1159831bf5c2356affab834039cb6fce596ea74e39fc77533ea349f1eef2/jellyfish-1.2.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:91cad49a4fb731b726afc5ae385a3217a7016ed88a04da40c131cff8136a5db5", size = 321825, upload-time = "2025-10-11T19:36:10.887Z" }, - { url = "https://files.pythonhosted.org/packages/bf/66/4328b3ecfb79266b261d195b920c78975e9a0ee793c9fe1e08904e924ad6/jellyfish-1.2.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bda2275f31a64adf3483e39f7a4e2107f7dfe3a3f85f0d2c0cb6ae5fbe4a443", size = 355093, upload-time = "2025-10-11T19:36:11.921Z" }, - { url = "https://files.pythonhosted.org/packages/bb/9d/5bbc2ec2e3fd0f93e54642aa1a0772bd909e7bb2755a6a8ad8b7d75e97b8/jellyfish-1.2.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:98a133b40dc00cfda6609e1b0cb0ab0b77796fc2719aae886a12009514f73499", size = 364202, upload-time = "2025-10-11T19:36:12.867Z" }, - { url = "https://files.pythonhosted.org/packages/99/88/bec6bd9899194355f8007e376bbf0824ff6bb9dd78754a5e8b095ee00c1c/jellyfish-1.2.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa30c7b59bd1c5e105693108a6d7a98f3e7a1a59e23e15bc5897b91fd5849f5", size = 362053, upload-time = "2025-10-11T19:36:14.086Z" }, - { url = "https://files.pythonhosted.org/packages/6a/15/ca2f05b39145460579b7bfb164d210ce30a68bc1136b74f48bae33e3442b/jellyfish-1.2.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:db97d873f23b0c15b4ed911ece10e5cc0bb96cdc53666d5c3788bd0af81807f1", size = 535693, upload-time = "2025-10-11T19:36:15.205Z" }, - { url = "https://files.pythonhosted.org/packages/d1/82/8889c441d768a80eeea575e471d54613ea88c579883a2c30afa04fa0987b/jellyfish-1.2.1-pp310-pypy310_pp73-musllinux_1_1_i686.whl", hash = "sha256:393f609fd6139ce782e747e22c399483ffc58341009e6a97e39ffe5f5b2c674c", size = 555789, upload-time = "2025-10-11T19:36:16.433Z" }, - { url = "https://files.pythonhosted.org/packages/af/65/abca7f780fb66840500ab7842c2285de6c09c5130a769fe3712a3a647af9/jellyfish-1.2.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fb3c6e537cb4605c22895a8d4a10cdb26611ba2bbfc7f0b4c1d06bb9d8aad648", size = 525431, upload-time = "2025-10-11T19:36:17.788Z" }, { url = "https://files.pythonhosted.org/packages/96/72/e4897449abd844d501412873d1d15bd846bcc919648c0b1043e225268b21/jellyfish-1.2.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:748dc45a0394fbe9120b8b3b9a39fab0967c7e2d6ecdd5304af018e774f80f96", size = 326967, upload-time = "2025-10-11T19:36:18.851Z" }, { url = "https://files.pythonhosted.org/packages/60/3f/c7a550abd212ae40c2a555055a3f16ba39376e486ba0189e150fb25cf6b1/jellyfish-1.2.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:13f1ac9caba22af10bfe42f674822643c0266009f882e0fe652079706dc5d13a", size = 321759, upload-time = "2025-10-11T19:36:19.882Z" }, { url = "https://files.pythonhosted.org/packages/19/58/a268365ba659f04d4db0c94325042aa9aee69c3a9a5823a5b2a2db308a5c/jellyfish-1.2.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ffeeb6c78c45fbb6d2a22b0173fb8a6af849001d6c26fab49c525136dbd9734", size = 354847, upload-time = "2025-10-11T19:36:20.976Z" }, @@ -1308,14 +868,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/74/5c/82455195b77cd1996c3618bd5aa8f25a9fc254d401a3c1425fb60cf97742/jellyfish-1.2.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:536c80d8d4ec7f39cbb10b85d926ff96cef3cde4a83ca0991c07cd9835d5dc13", size = 535488, upload-time = "2025-10-11T19:36:24.252Z" }, { url = "https://files.pythonhosted.org/packages/74/3d/295468c5df5a8d03f522b0c21fc3e694d6be376602a6d755bf7815947522/jellyfish-1.2.1-pp311-pypy311_pp73-musllinux_1_1_i686.whl", hash = "sha256:21baa92d4a5112167721156f6d061c2ae105f2995b3a5e19cec6662928f0c439", size = 555818, upload-time = "2025-10-11T19:36:25.667Z" }, { url = "https://files.pythonhosted.org/packages/41/6d/4029265138a5a0b18e4df381560e467339bec477f4efaa7614736dbc433e/jellyfish-1.2.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:68ea3ddd4dae1152a7f7155ef02a7bfad919611158d71b301f9aa167685819af", size = 525259, upload-time = "2025-10-11T19:36:26.726Z" }, - { url = "https://files.pythonhosted.org/packages/3a/31/42f4f1b80f2de28ac455a04d7d4d15765003d41ff790485dd65467618973/jellyfish-1.2.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d7be8021658b46b22500a77f1707901bd98fc210f185c229b81c74efd3c1baf2", size = 327035, upload-time = "2025-10-11T19:36:28.134Z" }, - { url = "https://files.pythonhosted.org/packages/31/bd/e926937450a1064dd2240b14fd4f5bea2e8a4c5b4809427b4c98daa0e112/jellyfish-1.2.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:bcdcd603a7737cd3f5a2ab10ce9b49844329deb81c2daafcd8131e54fc730205", size = 321844, upload-time = "2025-10-11T19:36:29.14Z" }, - { url = "https://files.pythonhosted.org/packages/0d/65/a7bee935f8e9c7fb1968eedb39bff2ffd440ca924148b40f1cd82e8e6f1e/jellyfish-1.2.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c28a4ae3e201e1c1b7bacacd40e2e76c4068b90c9ae3a0d525e0ac98206f1cc", size = 355124, upload-time = "2025-10-11T19:36:30.187Z" }, - { url = "https://files.pythonhosted.org/packages/5f/ad/3b3d60001ba7b168fc78c275470e7cf03387962c600437304232ead06f56/jellyfish-1.2.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bebccd0652ac1c7e438ae1f451edefde63d14b3af6f6daa30c599919dcb92886", size = 364462, upload-time = "2025-10-11T19:36:31.243Z" }, - { url = "https://files.pythonhosted.org/packages/7b/43/e63cccdbf4616e1160c2d3ca350b49e31a3c151766b6c7da1db34dee985b/jellyfish-1.2.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05be396aebe3dce7a8cb2f97727ecdf99e86457c48e97190775dce33f8b7e39d", size = 362230, upload-time = "2025-10-11T19:36:32.303Z" }, - { url = "https://files.pythonhosted.org/packages/04/49/621afee4f6908166a7d1862b6dada4f76a0efc43ab9d89799502ccac06d5/jellyfish-1.2.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:9d4448c874959ae012cda0f6d570ac0bd7f0fcf12007714eaebf86b86919b66f", size = 535739, upload-time = "2025-10-11T19:36:33.698Z" }, - { url = "https://files.pythonhosted.org/packages/f5/29/f913b8199b9f85e432085271e29081bf68111969a69d7c46127e38994374/jellyfish-1.2.1-pp39-pypy39_pp73-musllinux_1_1_i686.whl", hash = "sha256:4a21d7eda5e6996772055f798e3fe1de1b33b3edad7f6cf0567097a21585a812", size = 555991, upload-time = "2025-10-11T19:36:34.911Z" }, - { url = "https://files.pythonhosted.org/packages/04/be/2e3ebd88d0937d5e69e2ad09a4e31ed419211d702051a8f09cb509eac282/jellyfish-1.2.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:a0ef6f0ecc085c1f8fddb048f538c8bb89989e5d470eab45d4e9bd48ee73a40d", size = 525411, upload-time = "2025-10-11T19:36:36.032Z" }, ] [[package]] @@ -1371,8 +923,7 @@ name = "jupyter-book" version = "1.0.4.post1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "click" }, { name = "jinja2" }, { name = "jsonschema" }, { name = "linkify-it-py" }, @@ -1402,8 +953,7 @@ version = "1.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, - { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "click" }, { name = "importlib-metadata" }, { name = "nbclient" }, { name = "nbformat" }, @@ -1421,7 +971,6 @@ name = "jupyter-client" version = "8.6.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, { name = "jupyter-core" }, { name = "python-dateutil" }, { name = "pyzmq" }, @@ -1462,18 +1011,6 @@ version = "0.8.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/56/9c/b4b0c54d84da4a94b37bd44151e46d5e583c9534c7e02250b961b1b6d8a8/librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73", size = 177471, upload-time = "2026-02-17T16:13:06.101Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/5f/63f5fa395c7a8a93558c0904ba8f1c8d1b997ca6a3de61bc7659970d66bf/librt-0.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc", size = 65697, upload-time = "2026-02-17T16:11:06.903Z" }, - { url = "https://files.pythonhosted.org/packages/ff/e0/0472cf37267b5920eff2f292ccfaede1886288ce35b7f3203d8de00abfe6/librt-0.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7", size = 68376, upload-time = "2026-02-17T16:11:08.395Z" }, - { url = "https://files.pythonhosted.org/packages/c8/be/8bd1359fdcd27ab897cd5963294fa4a7c83b20a8564678e4fd12157e56a5/librt-0.8.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6", size = 197084, upload-time = "2026-02-17T16:11:09.774Z" }, - { url = "https://files.pythonhosted.org/packages/e2/fe/163e33fdd091d0c2b102f8a60cc0a61fd730ad44e32617cd161e7cd67a01/librt-0.8.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0", size = 207337, upload-time = "2026-02-17T16:11:11.311Z" }, - { url = "https://files.pythonhosted.org/packages/01/99/f85130582f05dcf0c8902f3d629270231d2f4afdfc567f8305a952ac7f14/librt-0.8.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b", size = 219980, upload-time = "2026-02-17T16:11:12.499Z" }, - { url = "https://files.pythonhosted.org/packages/6f/54/cb5e4d03659e043a26c74e08206412ac9a3742f0477d96f9761a55313b5f/librt-0.8.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6", size = 212921, upload-time = "2026-02-17T16:11:14.484Z" }, - { url = "https://files.pythonhosted.org/packages/b1/81/a3a01e4240579c30f3487f6fed01eb4bc8ef0616da5b4ebac27ca19775f3/librt-0.8.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71", size = 221381, upload-time = "2026-02-17T16:11:17.459Z" }, - { url = "https://files.pythonhosted.org/packages/08/b0/fc2d54b4b1c6fb81e77288ff31ff25a2c1e62eaef4424a984f228839717b/librt-0.8.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7", size = 216714, upload-time = "2026-02-17T16:11:19.197Z" }, - { url = "https://files.pythonhosted.org/packages/96/96/85daa73ffbd87e1fb287d7af6553ada66bf25a2a6b0de4764344a05469f6/librt-0.8.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05", size = 214777, upload-time = "2026-02-17T16:11:20.443Z" }, - { url = "https://files.pythonhosted.org/packages/12/9c/c3aa7a2360383f4bf4f04d98195f2739a579128720c603f4807f006a4225/librt-0.8.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891", size = 237398, upload-time = "2026-02-17T16:11:22.083Z" }, - { url = "https://files.pythonhosted.org/packages/61/19/d350ea89e5274665185dabc4bbb9c3536c3411f862881d316c8b8e00eb66/librt-0.8.1-cp310-cp310-win32.whl", hash = "sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7", size = 54285, upload-time = "2026-02-17T16:11:23.27Z" }, - { url = "https://files.pythonhosted.org/packages/4f/d6/45d587d3d41c112e9543a0093d883eb57a24a03e41561c127818aa2a6bcc/librt-0.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2", size = 61352, upload-time = "2026-02-17T16:11:24.207Z" }, { url = "https://files.pythonhosted.org/packages/1d/01/0e748af5e4fee180cf7cd12bd12b0513ad23b045dccb2a83191bde82d168/librt-0.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd", size = 65315, upload-time = "2026-02-17T16:11:25.152Z" }, { url = "https://files.pythonhosted.org/packages/9d/4d/7184806efda571887c798d573ca4134c80ac8642dcdd32f12c31b939c595/librt-0.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965", size = 68021, upload-time = "2026-02-17T16:11:26.129Z" }, { url = "https://files.pythonhosted.org/packages/ae/88/c3c52d2a5d5101f28d3dc89298444626e7874aa904eed498464c2af17627/librt-0.8.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da", size = 194500, upload-time = "2026-02-17T16:11:27.177Z" }, @@ -1539,18 +1076,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b9/eb/7697f60fbe7042ab4e88f4ee6af496b7f222fffb0a4e3593ef1f29f81652/librt-0.8.1-cp314-cp314t-win32.whl", hash = "sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a", size = 51328, upload-time = "2026-02-17T16:12:45.148Z" }, { url = "https://files.pythonhosted.org/packages/7c/72/34bf2eb7a15414a23e5e70ecb9440c1d3179f393d9349338a91e2781c0fb/librt-0.8.1-cp314-cp314t-win_amd64.whl", hash = "sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4", size = 58722, upload-time = "2026-02-17T16:12:46.85Z" }, { url = "https://files.pythonhosted.org/packages/b2/c8/d148e041732d631fc76036f8b30fae4e77b027a1e95b7a84bb522481a940/librt-0.8.1-cp314-cp314t-win_arm64.whl", hash = "sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61", size = 48755, upload-time = "2026-02-17T16:12:47.943Z" }, - { url = "https://files.pythonhosted.org/packages/01/1f/c7d8b66a3ca3ca3ed8ded4b32c96ee58a45920ebbbaa934355c74adcc33e/librt-0.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac", size = 65990, upload-time = "2026-02-17T16:12:48.972Z" }, - { url = "https://files.pythonhosted.org/packages/56/be/ee9ba1730052313d08457f19beaa1b878619978863fba09b40aed5b5c123/librt-0.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed", size = 68640, upload-time = "2026-02-17T16:12:50.24Z" }, - { url = "https://files.pythonhosted.org/packages/81/27/b7309298b96f7690cec3ceee38004c1a7f60fcd96d952d3ac344a1e3e8b3/librt-0.8.1-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd", size = 196099, upload-time = "2026-02-17T16:12:52.788Z" }, - { url = "https://files.pythonhosted.org/packages/10/48/160a5aacdcb21824b10a52378c39e88c46a29bb31efdaf3910dd1f9b670e/librt-0.8.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851", size = 206663, upload-time = "2026-02-17T16:12:55.017Z" }, - { url = "https://files.pythonhosted.org/packages/ee/65/33dd1d8caabb7c6805d87d095b143417dc96b0277c06ffa0508361422c82/librt-0.8.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128", size = 219318, upload-time = "2026-02-17T16:12:56.145Z" }, - { url = "https://files.pythonhosted.org/packages/09/d4/353805aa6181c7950a2462bd6e855366eeca21a501f375228d72a51547df/librt-0.8.1-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac", size = 212191, upload-time = "2026-02-17T16:12:57.326Z" }, - { url = "https://files.pythonhosted.org/packages/06/08/725b3f304d61eba56c713c251fb833a06d84bf93381caad5152366f5d2bb/librt-0.8.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551", size = 220672, upload-time = "2026-02-17T16:12:58.497Z" }, - { url = "https://files.pythonhosted.org/packages/0e/55/e8cdf04145872b3b97cb9b68287b22d1c08348227063f305aec11a3e6ce7/librt-0.8.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5", size = 216172, upload-time = "2026-02-17T16:12:59.751Z" }, - { url = "https://files.pythonhosted.org/packages/8f/d8/23b1c6592d2422dd6829c672f45b1f1c257f219926b0d216fedb572d0184/librt-0.8.1-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6", size = 214116, upload-time = "2026-02-17T16:13:01.056Z" }, - { url = "https://files.pythonhosted.org/packages/c9/92/2b44fd3cc3313f44e43bdbb41343735b568fa675fa351642b408ee48d418/librt-0.8.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed", size = 236664, upload-time = "2026-02-17T16:13:02.314Z" }, - { url = "https://files.pythonhosted.org/packages/00/23/92313ecdab80e142d8ea10e8dfa6297694359dbaacc9e81679bdc8cbceb6/librt-0.8.1-cp39-cp39-win32.whl", hash = "sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc", size = 54368, upload-time = "2026-02-17T16:13:03.549Z" }, - { url = "https://files.pythonhosted.org/packages/68/36/18f6e768afad6b55a690d38427c53251b69b7ba8795512730fd2508b31a9/librt-0.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7", size = 61507, upload-time = "2026-02-17T16:13:04.556Z" }, ] [[package]] @@ -1583,16 +1108,6 @@ version = "3.0.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357, upload-time = "2024-10-18T15:20:51.44Z" }, - { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393, upload-time = "2024-10-18T15:20:52.426Z" }, - { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732, upload-time = "2024-10-18T15:20:53.578Z" }, - { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866, upload-time = "2024-10-18T15:20:55.06Z" }, - { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964, upload-time = "2024-10-18T15:20:55.906Z" }, - { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977, upload-time = "2024-10-18T15:20:57.189Z" }, - { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366, upload-time = "2024-10-18T15:20:58.235Z" }, - { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091, upload-time = "2024-10-18T15:20:59.235Z" }, - { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065, upload-time = "2024-10-18T15:21:00.307Z" }, - { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514, upload-time = "2024-10-18T15:21:01.122Z" }, { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" }, { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" }, { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" }, @@ -1633,16 +1148,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, - { url = "https://files.pythonhosted.org/packages/a7/ea/9b1530c3fdeeca613faeb0fb5cbcf2389d816072fab72a71b45749ef6062/MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", size = 14344, upload-time = "2024-10-18T15:21:43.721Z" }, - { url = "https://files.pythonhosted.org/packages/4b/c2/fbdbfe48848e7112ab05e627e718e854d20192b674952d9042ebd8c9e5de/MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", size = 12389, upload-time = "2024-10-18T15:21:44.666Z" }, - { url = "https://files.pythonhosted.org/packages/f0/25/7a7c6e4dbd4f867d95d94ca15449e91e52856f6ed1905d58ef1de5e211d0/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", size = 21607, upload-time = "2024-10-18T15:21:45.452Z" }, - { url = "https://files.pythonhosted.org/packages/53/8f/f339c98a178f3c1e545622206b40986a4c3307fe39f70ccd3d9df9a9e425/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", size = 20728, upload-time = "2024-10-18T15:21:46.295Z" }, - { url = "https://files.pythonhosted.org/packages/1a/03/8496a1a78308456dbd50b23a385c69b41f2e9661c67ea1329849a598a8f9/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", size = 20826, upload-time = "2024-10-18T15:21:47.134Z" }, - { url = "https://files.pythonhosted.org/packages/e6/cf/0a490a4bd363048c3022f2f475c8c05582179bb179defcee4766fb3dcc18/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", size = 21843, upload-time = "2024-10-18T15:21:48.334Z" }, - { url = "https://files.pythonhosted.org/packages/19/a3/34187a78613920dfd3cdf68ef6ce5e99c4f3417f035694074beb8848cd77/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", size = 21219, upload-time = "2024-10-18T15:21:49.587Z" }, - { url = "https://files.pythonhosted.org/packages/17/d8/5811082f85bb88410ad7e452263af048d685669bbbfb7b595e8689152498/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", size = 20946, upload-time = "2024-10-18T15:21:50.441Z" }, - { url = "https://files.pythonhosted.org/packages/7c/31/bd635fb5989440d9365c5e3c47556cfea121c7803f5034ac843e8f37c2f2/MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", size = 15063, upload-time = "2024-10-18T15:21:51.385Z" }, - { url = "https://files.pythonhosted.org/packages/b3/73/085399401383ce949f727afec55ec3abd76648d04b9f22e1c0e99cb4bec3/MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", size = 15506, upload-time = "2024-10-18T15:21:52.974Z" }, ] [[package]] @@ -1657,34 +1162,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899, upload-time = "2024-04-15T13:44:43.265Z" }, ] -[[package]] -name = "mdit-py-plugins" -version = "0.4.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -dependencies = [ - { name = "markdown-it-py", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542, upload-time = "2024-09-09T20:27:49.564Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316, upload-time = "2024-09-09T20:27:48.397Z" }, -] - [[package]] name = "mdit-py-plugins" version = "0.5.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", - "python_full_version == '3.10.*'", -] dependencies = [ - { name = "markdown-it-py", marker = "python_full_version >= '3.10'" }, + { name = "markdown-it-py" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b2/fd/a756d36c0bfba5f6e39a1cdbdbfdd448dc02692467d83816dff4592a1ebc/mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6", size = 44655, upload-time = "2025-08-11T07:25:49.083Z" } wheels = [ @@ -1705,8 +1188,8 @@ name = "microdf-python" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, { name = "pandas" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a3/1b/a4b6a53c8e6668c190e1928ebf9f8c67d6a4995ff4a4b0e3403ded30d4ab/microdf_python-1.3.0.tar.gz", hash = "sha256:f352fe32c25a4fd6d6188ef6aaca0214b53b29a8a7913aca6af0362dd9c922bd", size = 28141, upload-time = "2026-04-17T23:20:57.018Z" } @@ -1720,16 +1203,6 @@ version = "1.1.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/45/b1/ea4f68038a18c77c9467400d166d74c4ffa536f34761f7983a104357e614/msgpack-1.1.1.tar.gz", hash = "sha256:77b79ce34a2bdab2594f490c8e80dd62a02d650b91a75159a63ec413b8d104cd", size = 173555, upload-time = "2025-06-13T06:52:51.324Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/52/f30da112c1dc92cf64f57d08a273ac771e7b29dea10b4b30369b2d7e8546/msgpack-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:353b6fc0c36fde68b661a12949d7d49f8f51ff5fa019c1e47c87c4ff34b080ed", size = 81799, upload-time = "2025-06-13T06:51:37.228Z" }, - { url = "https://files.pythonhosted.org/packages/e4/35/7bfc0def2f04ab4145f7f108e3563f9b4abae4ab0ed78a61f350518cc4d2/msgpack-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:79c408fcf76a958491b4e3b103d1c417044544b68e96d06432a189b43d1215c8", size = 78278, upload-time = "2025-06-13T06:51:38.534Z" }, - { url = "https://files.pythonhosted.org/packages/e8/c5/df5d6c1c39856bc55f800bf82778fd4c11370667f9b9e9d51b2f5da88f20/msgpack-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78426096939c2c7482bf31ef15ca219a9e24460289c00dd0b94411040bb73ad2", size = 402805, upload-time = "2025-06-13T06:51:39.538Z" }, - { url = "https://files.pythonhosted.org/packages/20/8e/0bb8c977efecfe6ea7116e2ed73a78a8d32a947f94d272586cf02a9757db/msgpack-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b17ba27727a36cb73aabacaa44b13090feb88a01d012c0f4be70c00f75048b4", size = 408642, upload-time = "2025-06-13T06:51:41.092Z" }, - { url = "https://files.pythonhosted.org/packages/59/a1/731d52c1aeec52006be6d1f8027c49fdc2cfc3ab7cbe7c28335b2910d7b6/msgpack-1.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a17ac1ea6ec3c7687d70201cfda3b1e8061466f28f686c24f627cae4ea8efd0", size = 395143, upload-time = "2025-06-13T06:51:42.575Z" }, - { url = "https://files.pythonhosted.org/packages/2b/92/b42911c52cda2ba67a6418ffa7d08969edf2e760b09015593c8a8a27a97d/msgpack-1.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:88d1e966c9235c1d4e2afac21ca83933ba59537e2e2727a999bf3f515ca2af26", size = 395986, upload-time = "2025-06-13T06:51:43.807Z" }, - { url = "https://files.pythonhosted.org/packages/61/dc/8ae165337e70118d4dab651b8b562dd5066dd1e6dd57b038f32ebc3e2f07/msgpack-1.1.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f6d58656842e1b2ddbe07f43f56b10a60f2ba5826164910968f5933e5178af75", size = 402682, upload-time = "2025-06-13T06:51:45.534Z" }, - { url = "https://files.pythonhosted.org/packages/58/27/555851cb98dcbd6ce041df1eacb25ac30646575e9cd125681aa2f4b1b6f1/msgpack-1.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:96decdfc4adcbc087f5ea7ebdcfd3dee9a13358cae6e81d54be962efc38f6338", size = 406368, upload-time = "2025-06-13T06:51:46.97Z" }, - { url = "https://files.pythonhosted.org/packages/d4/64/39a26add4ce16f24e99eabb9005e44c663db00e3fce17d4ae1ae9d61df99/msgpack-1.1.1-cp310-cp310-win32.whl", hash = "sha256:6640fd979ca9a212e4bcdf6eb74051ade2c690b862b679bfcb60ae46e6dc4bfd", size = 65004, upload-time = "2025-06-13T06:51:48.582Z" }, - { url = "https://files.pythonhosted.org/packages/7d/18/73dfa3e9d5d7450d39debde5b0d848139f7de23bd637a4506e36c9800fd6/msgpack-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:8b65b53204fe1bd037c40c4148d00ef918eb2108d24c9aaa20bc31f9810ce0a8", size = 71548, upload-time = "2025-06-13T06:51:49.558Z" }, { url = "https://files.pythonhosted.org/packages/7f/83/97f24bf9848af23fe2ba04380388216defc49a8af6da0c28cc636d722502/msgpack-1.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:71ef05c1726884e44f8b1d1773604ab5d4d17729d8491403a705e649116c9558", size = 82728, upload-time = "2025-06-13T06:51:50.68Z" }, { url = "https://files.pythonhosted.org/packages/aa/7f/2eaa388267a78401f6e182662b08a588ef4f3de6f0eab1ec09736a7aaa2b/msgpack-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:36043272c6aede309d29d56851f8841ba907a1a3d04435e43e8a19928e243c1d", size = 79279, upload-time = "2025-06-13T06:51:51.72Z" }, { url = "https://files.pythonhosted.org/packages/f8/46/31eb60f4452c96161e4dfd26dbca562b4ec68c72e4ad07d9566d7ea35e8a/msgpack-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a32747b1b39c3ac27d0670122b57e6e57f28eefb725e0b625618d1b59bf9d1e0", size = 423859, upload-time = "2025-06-13T06:51:52.749Z" }, @@ -1760,100 +1233,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bc/66/36c78af2efaffcc15a5a61ae0df53a1d025f2680122e2a9eb8442fed3ae4/msgpack-1.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4147151acabb9caed4e474c3344181e91ff7a388b888f1e19ea04f7e73dc7ad5", size = 424172, upload-time = "2025-06-13T06:52:25.704Z" }, { url = "https://files.pythonhosted.org/packages/8c/87/a75eb622b555708fe0427fab96056d39d4c9892b0c784b3a721088c7ee37/msgpack-1.1.1-cp313-cp313-win32.whl", hash = "sha256:500e85823a27d6d9bba1d057c871b4210c1dd6fb01fbb764e37e4e8847376323", size = 65347, upload-time = "2025-06-13T06:52:26.846Z" }, { url = "https://files.pythonhosted.org/packages/ca/91/7dc28d5e2a11a5ad804cf2b7f7a5fcb1eb5a4966d66a5d2b41aee6376543/msgpack-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:6d489fba546295983abd142812bda76b57e33d0b9f5d5b71c09a583285506f69", size = 72341, upload-time = "2025-06-13T06:52:27.835Z" }, - { url = "https://files.pythonhosted.org/packages/1f/bd/0792be119d7fe7dc2148689ef65c90507d82d20a204aab3b98c74a1f8684/msgpack-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5be6b6bc52fad84d010cb45433720327ce886009d862f46b26d4d154001994b", size = 81882, upload-time = "2025-06-13T06:52:39.316Z" }, - { url = "https://files.pythonhosted.org/packages/75/77/ce06c8e26a816ae8730a8e030d263c5289adcaff9f0476f9b270bdd7c5c2/msgpack-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3a89cd8c087ea67e64844287ea52888239cbd2940884eafd2dcd25754fb72232", size = 78414, upload-time = "2025-06-13T06:52:40.341Z" }, - { url = "https://files.pythonhosted.org/packages/73/27/190576c497677fb4a0d05d896b24aea6cdccd910f206aaa7b511901befed/msgpack-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d75f3807a9900a7d575d8d6674a3a47e9f227e8716256f35bc6f03fc597ffbf", size = 400927, upload-time = "2025-06-13T06:52:41.399Z" }, - { url = "https://files.pythonhosted.org/packages/ed/af/6a0aa5a06762e70726ec3c10fb966600d84a7220b52635cb0ab2dc64d32f/msgpack-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d182dac0221eb8faef2e6f44701812b467c02674a322c739355c39e94730cdbf", size = 405903, upload-time = "2025-06-13T06:52:42.699Z" }, - { url = "https://files.pythonhosted.org/packages/1e/80/3f3da358cecbbe8eb12360814bd1277d59d2608485934742a074d99894a9/msgpack-1.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b13fe0fb4aac1aa5320cd693b297fe6fdef0e7bea5518cbc2dd5299f873ae90", size = 393192, upload-time = "2025-06-13T06:52:43.986Z" }, - { url = "https://files.pythonhosted.org/packages/98/c6/3a0ec7fdebbb4f3f8f254696cd91d491c29c501dbebd86286c17e8f68cd7/msgpack-1.1.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:435807eeb1bc791ceb3247d13c79868deb22184e1fc4224808750f0d7d1affc1", size = 393851, upload-time = "2025-06-13T06:52:45.177Z" }, - { url = "https://files.pythonhosted.org/packages/39/37/df50d5f8e68514b60fbe70f6e8337ea2b32ae2be030871bcd9d1cf7d4b62/msgpack-1.1.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4835d17af722609a45e16037bb1d4d78b7bdf19d6c0128116d178956618c4e88", size = 400292, upload-time = "2025-06-13T06:52:46.381Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ec/1e067292e02d2ceb4c8cb5ba222c4f7bb28730eef5676740609dc2627e0f/msgpack-1.1.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8ef6e342c137888ebbfb233e02b8fbd689bb5b5fcc59b34711ac47ebd504478", size = 401873, upload-time = "2025-06-13T06:52:47.957Z" }, - { url = "https://files.pythonhosted.org/packages/d3/31/e8c9c6b5b58d64c9efa99c8d181fcc25f38ead357b0360379fbc8a4234ad/msgpack-1.1.1-cp39-cp39-win32.whl", hash = "sha256:61abccf9de335d9efd149e2fff97ed5974f2481b3353772e8e2dd3402ba2bd57", size = 65028, upload-time = "2025-06-13T06:52:49.166Z" }, - { url = "https://files.pythonhosted.org/packages/20/d6/cd62cded572e5e25892747a5d27850170bcd03c855e9c69c538e024de6f9/msgpack-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:40eae974c873b2992fd36424a5d9407f93e97656d999f43fca9d29f820899084", size = 71700, upload-time = "2025-06-13T06:52:50.244Z" }, -] - -[[package]] -name = "mypy" -version = "1.19.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -dependencies = [ - { name = "librt", marker = "python_full_version < '3.10' and platform_python_implementation != 'PyPy'" }, - { name = "mypy-extensions", marker = "python_full_version < '3.10'" }, - { name = "pathspec", marker = "python_full_version < '3.10'" }, - { name = "tomli", marker = "python_full_version < '3.10'" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/63/e499890d8e39b1ff2df4c0c6ce5d371b6844ee22b8250687a99fd2f657a8/mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec", size = 13101333, upload-time = "2025-12-15T05:03:03.28Z" }, - { url = "https://files.pythonhosted.org/packages/72/4b/095626fc136fba96effc4fd4a82b41d688ab92124f8c4f7564bffe5cf1b0/mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b", size = 12164102, upload-time = "2025-12-15T05:02:33.611Z" }, - { url = "https://files.pythonhosted.org/packages/0c/5b/952928dd081bf88a83a5ccd49aaecfcd18fd0d2710c7ff07b8fb6f7032b9/mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6", size = 12765799, upload-time = "2025-12-15T05:03:28.44Z" }, - { url = "https://files.pythonhosted.org/packages/2a/0d/93c2e4a287f74ef11a66fb6d49c7a9f05e47b0a4399040e6719b57f500d2/mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74", size = 13522149, upload-time = "2025-12-15T05:02:36.011Z" }, - { url = "https://files.pythonhosted.org/packages/7b/0e/33a294b56aaad2b338d203e3a1d8b453637ac36cb278b45005e0901cf148/mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1", size = 13810105, upload-time = "2025-12-15T05:02:40.327Z" }, - { url = "https://files.pythonhosted.org/packages/0e/fd/3e82603a0cb66b67c5e7abababce6bf1a929ddf67bf445e652684af5c5a0/mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac", size = 10057200, upload-time = "2025-12-15T05:02:51.012Z" }, - { url = "https://files.pythonhosted.org/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288", size = 13028539, upload-time = "2025-12-15T05:03:44.129Z" }, - { url = "https://files.pythonhosted.org/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab", size = 12083163, upload-time = "2025-12-15T05:03:37.679Z" }, - { url = "https://files.pythonhosted.org/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6", size = 12687629, upload-time = "2025-12-15T05:02:31.526Z" }, - { url = "https://files.pythonhosted.org/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331", size = 13436933, upload-time = "2025-12-15T05:03:15.606Z" }, - { url = "https://files.pythonhosted.org/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925", size = 13661754, upload-time = "2025-12-15T05:02:26.731Z" }, - { url = "https://files.pythonhosted.org/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042", size = 10055772, upload-time = "2025-12-15T05:03:26.179Z" }, - { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, - { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, - { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, - { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, - { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, - { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, - { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, - { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, - { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, - { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, - { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, - { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, - { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, - { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, - { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, - { url = "https://files.pythonhosted.org/packages/b5/f7/88436084550ca9af5e610fa45286be04c3b63374df3e021c762fe8c4369f/mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3", size = 13102606, upload-time = "2025-12-15T05:02:46.833Z" }, - { url = "https://files.pythonhosted.org/packages/ca/a5/43dfad311a734b48a752790571fd9e12d61893849a01bff346a54011957f/mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a", size = 12164496, upload-time = "2025-12-15T05:03:41.947Z" }, - { url = "https://files.pythonhosted.org/packages/88/f0/efbfa391395cce2f2771f937e0620cfd185ec88f2b9cd88711028a768e96/mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67", size = 12772068, upload-time = "2025-12-15T05:02:53.689Z" }, - { url = "https://files.pythonhosted.org/packages/25/05/58b3ba28f5aed10479e899a12d2120d582ba9fa6288851b20bf1c32cbb4f/mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e", size = 13520385, upload-time = "2025-12-15T05:02:38.328Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a0/c006ccaff50b31e542ae69b92fe7e2f55d99fba3a55e01067dd564325f85/mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376", size = 13796221, upload-time = "2025-12-15T05:03:22.147Z" }, - { url = "https://files.pythonhosted.org/packages/b2/ff/8bdb051cd710f01b880472241bd36b3f817a8e1c5d5540d0b761675b6de2/mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24", size = 10055456, upload-time = "2025-12-15T05:03:35.169Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, ] [[package]] name = "mypy" version = "1.20.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", - "python_full_version == '3.10.*'", -] dependencies = [ - { name = "librt", marker = "python_full_version >= '3.10' and platform_python_implementation != 'PyPy'" }, - { name = "mypy-extensions", marker = "python_full_version >= '3.10'" }, - { name = "pathspec", marker = "python_full_version >= '3.10'" }, - { name = "tomli", marker = "python_full_version == '3.10.*'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.10'" }, + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f8/5c/b0089fe7fef0a994ae5ee07029ced0526082c6cfaaa4c10d40a10e33b097/mypy-1.20.0.tar.gz", hash = "sha256:eb96c84efcc33f0b5e0e04beacf00129dd963b67226b01c00b9dfc8affb464c3", size = 3815028, upload-time = "2026-03-31T16:55:14.959Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/a2/a965c8c3fcd4fa8b84ba0d46606181b0d0a1d50f274c67877f3e9ed4882c/mypy-1.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d99f515f95fd03a90875fdb2cca12ff074aa04490db4d190905851bdf8a549a8", size = 14430138, upload-time = "2026-03-31T16:52:37.843Z" }, - { url = "https://files.pythonhosted.org/packages/53/6e/043477501deeb8eabbab7f1a2f6cac62cfb631806dc1d6862a04a7f5011b/mypy-1.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bd0212976dc57a5bfeede7c219e7cd66568a32c05c9129686dd487c059c1b88a", size = 13311282, upload-time = "2026-03-31T16:55:11.021Z" }, - { url = "https://files.pythonhosted.org/packages/65/aa/bd89b247b83128197a214f29f0632ff3c14f54d4cd70d144d157bd7d7d6e/mypy-1.20.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8426d4d75d68714abc17a4292d922f6ba2cfb984b72c2278c437f6dae797865", size = 13750889, upload-time = "2026-03-31T16:52:02.909Z" }, - { url = "https://files.pythonhosted.org/packages/fa/9d/2860be7355c45247ccc0be1501c91176318964c2a137bd4743f58ce6200e/mypy-1.20.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02cca0761c75b42a20a2757ae58713276605eb29a08dd8a6e092aa347c4115ca", size = 14619788, upload-time = "2026-03-31T16:50:48.928Z" }, - { url = "https://files.pythonhosted.org/packages/75/7f/3ef3e360c91f3de120f205c8ce405e9caf9fc52ef14b65d37073e322c114/mypy-1.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3a49064504be59e59da664c5e149edc1f26c67c4f8e8456f6ba6aba55033018", size = 14918849, upload-time = "2026-03-31T16:51:10.478Z" }, - { url = "https://files.pythonhosted.org/packages/ae/72/af970dfe167ef788df7c5e6109d2ed0229f164432ce828bc9741a4250e64/mypy-1.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:ebea00201737ad4391142808ed16e875add5c17f676e0912b387739f84991e13", size = 10822007, upload-time = "2026-03-31T16:50:25.268Z" }, - { url = "https://files.pythonhosted.org/packages/93/94/ba9065c2ebe5421619aff684b793d953e438a8bfe31a320dd6d1e0706e81/mypy-1.20.0-cp310-cp310-win_arm64.whl", hash = "sha256:e80cf77847d0d3e6e3111b7b25db32a7f8762fd4b9a3a72ce53fe16a2863b281", size = 9756158, upload-time = "2026-03-31T16:48:36.213Z" }, { url = "https://files.pythonhosted.org/packages/6e/1c/74cb1d9993236910286865679d1c616b136b2eae468493aa939431eda410/mypy-1.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4525e7010b1b38334516181c5b81e16180b8e149e6684cee5a727c78186b4e3b", size = 14343972, upload-time = "2026-03-31T16:49:04.887Z" }, { url = "https://files.pythonhosted.org/packages/d5/0d/01399515eca280386e308cf57901e68d3a52af18691941b773b3380c1df8/mypy-1.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a17c5d0bdcca61ce24a35beb828a2d0d323d3fcf387d7512206888c900193367", size = 13225007, upload-time = "2026-03-31T16:50:08.151Z" }, { url = "https://files.pythonhosted.org/packages/56/ac/b4ba5094fb2d7fe9d2037cd8d18bbe02bcf68fd22ab9ff013f55e57ba095/mypy-1.20.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75ff57defcd0f1d6e006d721ccdec6c88d4f6a7816eb92f1c4890d979d9ee62", size = 13663752, upload-time = "2026-03-31T16:49:26.064Z" }, @@ -1908,8 +1301,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "importlib-metadata" }, { name = "ipykernel" }, - { name = "ipython", version = "8.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "ipython" }, { name = "jupyter-cache" }, { name = "myst-parser" }, { name = "nbclient" }, @@ -1931,8 +1323,7 @@ dependencies = [ { name = "docutils" }, { name = "jinja2" }, { name = "markdown-it-py" }, - { name = "mdit-py-plugins", version = "0.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "mdit-py-plugins", version = "0.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "mdit-py-plugins" }, { name = "pyyaml" }, { name = "sphinx" }, ] @@ -1977,14 +1368,6 @@ version = "1.10.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/dc/a0/f584c0b6b998e4981201a1383200663a725f556f439cf58d02a093cb9f91/ndindex-1.10.0.tar.gz", hash = "sha256:20e3a2f0a8ed4646abf0f13296aab0b5b9cc8c5bc182b71b5945e76eb6f558bb", size = 258688, upload-time = "2025-05-21T17:42:22.718Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/de/45af0f9b0abe5795228ca79577541c1c79b664996a5c9d15df21789e2ced/ndindex-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3d96dc319c39dce679d85a997f4eeb439f6de73c0793956b66598954ca61365c", size = 162311, upload-time = "2025-05-21T17:40:36.873Z" }, - { url = "https://files.pythonhosted.org/packages/d9/dd/d950718536c3898580c3f903888209d75057659b862b3d8d8af17bdb4fa8/ndindex-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b082de3c042b6da7ca327f17d088de3695333c30e0f9717d2ed5de5dc4d70802", size = 161621, upload-time = "2025-05-21T17:40:38.792Z" }, - { url = "https://files.pythonhosted.org/packages/bd/00/462ef86c63590e1f2e56d31ce46e9f13ae6aebd7506d33c08b927d2f1594/ndindex-1.10.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69cf517d138f47163d6c94cd9ccaafb91606a2aab386c05aaa0718975da09c88", size = 482241, upload-time = "2025-05-21T17:40:40.671Z" }, - { url = "https://files.pythonhosted.org/packages/e3/a6/975bfec7bec7f274853b0c33953b5f2df4ad51f62d1aab0c7142fee98261/ndindex-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9cea2a5f7a432dafadb6c5732a9af3e7139adbf9085320f284885fe5d4776e4", size = 501603, upload-time = "2025-05-21T17:40:42.394Z" }, - { url = "https://files.pythonhosted.org/packages/13/4a/8d39f8ab1d20cd246360d7af707107bc4a332c6758ea45780a5bff6ec29f/ndindex-1.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a3d2ea706c80e21022f6661524efb0aeed89a714a8fda4712df8d4a90ef507f5", size = 1620040, upload-time = "2025-05-21T17:40:44.638Z" }, - { url = "https://files.pythonhosted.org/packages/87/83/ba24c57073c29ba3f69c52767bec64dc818e90ac23f6ee43c98172b9f888/ndindex-1.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d5b3b8f99970ce40fbff1e55ad9ddf9ea708e82ace91271784e7ff1d08707c4c", size = 1529863, upload-time = "2025-05-21T17:40:46.886Z" }, - { url = "https://files.pythonhosted.org/packages/cd/2c/61e88acae938898994a6cfe83716db0e440f44f7b0c821a7adb2ab4cedbd/ndindex-1.10.0-cp310-cp310-win32.whl", hash = "sha256:6a5a401b867530fe4f1022cc8d578c8092cfdc726348e6d1569ec91881da365f", size = 149122, upload-time = "2025-05-21T17:40:48.921Z" }, - { url = "https://files.pythonhosted.org/packages/a9/61/2bc88b2b5f71649f9e07fcf3509ce8eb187adbb3e787e4600b28ce00139c/ndindex-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:88504651ddcb6733ba0caf0cdfc214d8ba9f140609b69f6566ad143322ce5a96", size = 156550, upload-time = "2025-05-21T17:40:50.819Z" }, { url = "https://files.pythonhosted.org/packages/b4/1c/a53253d68bb269e5591c39b96ae2c4dd671132a82f63d70aea486f76d70c/ndindex-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e42198c8636eaf468cf28b7e1700738de37841853f5f15a0671bad4c3876a85", size = 162556, upload-time = "2025-05-21T17:40:52.668Z" }, { url = "https://files.pythonhosted.org/packages/0d/2a/4e268ff5992d4b42755ee19cf46c3e954632aadd57810db7173fe945ad47/ndindex-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ec9865e787eababc9aa1be973bf8545c044e2b68297fe37adf7aeefe0ec61f59", size = 161769, upload-time = "2025-05-21T17:40:54.55Z" }, { url = "https://files.pythonhosted.org/packages/14/67/28ef988483e1ff446873150979b20fa87833c711fbe3a816e0e6a3e6e7d3/ndindex-1.10.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72377bc5d15229eeefa73a4370212d0bdb8992c76c2228df0771e0dcdeb5354a", size = 504542, upload-time = "2025-05-21T17:40:56.771Z" }, @@ -2015,29 +1398,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/19/6c/f9b449d0d9db404637d026798a208b677c04c349ab740db33ab78065603d/ndindex-1.10.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cb68232e58ca6cc92ddc8cdddcff8dcdfa5de030e89de8457e5d43de77bcc331", size = 1639541, upload-time = "2025-05-21T17:41:42.33Z" }, { url = "https://files.pythonhosted.org/packages/2c/14/0bfe948a092ddba3c23f18a6f4e3fc2029adfc3e433e634410ba98b7700f/ndindex-1.10.0-cp313-cp313t-win32.whl", hash = "sha256:af8ecd5a0221482e9b467918b90e78f85241572102fdcf0a941ef087e7dcf2e4", size = 157843, upload-time = "2025-05-21T17:41:43.981Z" }, { url = "https://files.pythonhosted.org/packages/50/49/0e7d831e918db3e8819f7327e835e4b106fe91ed0c865e96fb952f936b7f/ndindex-1.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2fb32342379547032fd25dbf5bfc7003ebc1bde582779e9a171373a738d6fb8b", size = 166116, upload-time = "2025-05-21T17:41:45.506Z" }, - { url = "https://files.pythonhosted.org/packages/77/85/66a504a430e28cf0ed12a5092253814114dae2b842ec2894e5a81ee6df45/ndindex-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4542247a231a8a998636109d6241bc692b583de06afa3fc2b8fbbbd6424bb965", size = 163972, upload-time = "2025-05-21T17:41:47.653Z" }, - { url = "https://files.pythonhosted.org/packages/bb/59/ced90fe3e5d2a695310334162dec2f7bf00659a1d2c760e089cef1e58dbe/ndindex-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:442ee28aaaa471bcc845406d7f006715e685087217e1d58b516b8d58a8c778eb", size = 162983, upload-time = "2025-05-21T17:41:49.186Z" }, - { url = "https://files.pythonhosted.org/packages/2c/97/d7e08e27ac65a1869659985ec4e026f154d3b11e5298bdc97cdbec9b0aae/ndindex-1.10.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f0274114cc986f017093d1f92e8c3c38cc4aaf7ed39facecdbb4bf636d1bc95", size = 489636, upload-time = "2025-05-21T17:41:50.766Z" }, - { url = "https://files.pythonhosted.org/packages/84/45/0beba49f64c9e06398c037b580467bf1001d186dc73de5aeafbad55e1ca7/ndindex-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009dde7c7216ea784744ed1d4bc1606c39f6d1b192395377e95a657b59e524c7", size = 510063, upload-time = "2025-05-21T17:41:52.427Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/255a54840d0cac8f3e735e2ce2bba8bbe8dc33bf80b909d1cc39b9f97fce/ndindex-1.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:660cae3a9fae4fbdb12417fa4efe1f476c37793a5230a0fb28ea9cf38ad71658", size = 1628733, upload-time = "2025-05-21T17:41:54.197Z" }, - { url = "https://files.pythonhosted.org/packages/2d/27/da2fd1a1e2b670e3fab1ab217561b3bb6fe38b6b52f0edb56762f2a80f6f/ndindex-1.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:304de39ba6bbea3bf0b4069c6240c87e9697fe355128324bc74cb6e92813df58", size = 1538961, upload-time = "2025-05-21T17:41:56.326Z" }, - { url = "https://files.pythonhosted.org/packages/e7/18/eecb8a9df326b861fe021d1401026d7694f7d910c9f9baef4ae95448d3fe/ndindex-1.10.0-cp39-cp39-win32.whl", hash = "sha256:bf7aba235a994a0c488d9edfd00558a96eef3ae0f18c1504d9e50677ba82fbd5", size = 149916, upload-time = "2025-05-21T17:41:57.904Z" }, - { url = "https://files.pythonhosted.org/packages/89/de/83add3ed9a94c6e5f7401d2a3547a51ef9c89f69ff9b8fd8a8ece598886d/ndindex-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:041be825a10e4bd7368e481a7893a96e96680a9623860e58d65c843e7734cccc", size = 157607, upload-time = "2025-05-21T17:41:59.346Z" }, - { url = "https://files.pythonhosted.org/packages/b0/61/afde1bf918386625e477a7ac0fa518ca83f9239e2675affccf8d48d05e25/ndindex-1.10.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1851d2d490413edc5c5734f5f74e8d3b59cfc23eae561d10bd4db6e4162dcf02", size = 146659, upload-time = "2025-05-21T17:42:00.855Z" }, - { url = "https://files.pythonhosted.org/packages/63/22/90a3e3aa613d4d7e5432e8d7cf0188049f61b34b104eef7f014b7e35a3aa/ndindex-1.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:490c577e6915f8d2d045239a14e70b1dfd14b703028a41f6a3713821598d0db8", size = 146160, upload-time = "2025-05-21T17:42:02.227Z" }, - { url = "https://files.pythonhosted.org/packages/80/a5/677dc41756ac9b2ac3bd0b458abda4dee0c74ee1c6560be3a1b36cc2c9d1/ndindex-1.10.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:21f4c61db28b7ba8dc03548a3b2c3561feb8d61f7293dfc310df52aa2676463f", size = 163067, upload-time = "2025-05-21T17:42:03.615Z" }, - { url = "https://files.pythonhosted.org/packages/01/8d/319499a3f9da41695a75243b8fd8576d42c1e382f5dc935b885f590a42be/ndindex-1.10.0-pp310-pypy310_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd41c7cce386bc21a38a2153427ce47f92d6bdb097dc3c5c42fa24e75090c8f", size = 160109, upload-time = "2025-05-21T17:42:05.137Z" }, - { url = "https://files.pythonhosted.org/packages/7c/66/a6721aac78028ee1dd35106a20a2f5c940f17587bc8c8fb9d98040eeddec/ndindex-1.10.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ba5f6d09ad320e0045ea39d7efd66a21d73cd4875d114be08e7ba6204a8feb7", size = 148094, upload-time = "2025-05-21T17:42:06.563Z" }, { url = "https://files.pythonhosted.org/packages/c3/61/1333424bdfcebdcea63f5ed86ac98dccaf07ebb7e1463ca845a06e321d91/ndindex-1.10.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:aa17ea725f85af9285b298f72ccc8012949c0916d4426b0215d1c556dd995246", size = 146929, upload-time = "2025-05-21T17:42:08.04Z" }, { url = "https://files.pythonhosted.org/packages/eb/7c/0813615d958ec78c521b9c09518b1f49ec553a0bec0646b5f4ebbf33bdcb/ndindex-1.10.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:219fdef9d6a557913fd92418275088b46c727944356f3fe59f4f72d62efd6f3d", size = 146417, upload-time = "2025-05-21T17:42:09.534Z" }, { url = "https://files.pythonhosted.org/packages/d8/a1/b340a47409253f05c78d400f98b43477549ad1a1f7a5358acb784c79ed48/ndindex-1.10.0-pp311-pypy311_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1962137fcb69c00e2db42d5d045f9b7413fc37f44b143e7ae4a8c2c68ba3832", size = 163867, upload-time = "2025-05-21T17:42:10.994Z" }, { url = "https://files.pythonhosted.org/packages/02/24/e5192ffb87070e9ff2328d715e5aa3a7f6b673e86c1ee8f48136815564e1/ndindex-1.10.0-pp311-pypy311_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18c9c8271926fb16c59e827b61bb77f45ee31a824eaa50b386edcd77a6a7c9a3", size = 160644, upload-time = "2025-05-21T17:42:12.415Z" }, { url = "https://files.pythonhosted.org/packages/09/c5/b894cc961460e608b869d91164e9f825e3bb0579defb37c0eea61dce584e/ndindex-1.10.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:76e4fb082c83ccbc67c7a64b80e33bc5dfe9379f30c3b40a865914ae79947071", size = 147721, upload-time = "2025-05-21T17:42:13.825Z" }, - { url = "https://files.pythonhosted.org/packages/09/90/0a8e43e9ade1ff08dfe60ffe8bbc7751dbb6d19ffb45717b126445aa4ac7/ndindex-1.10.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:074be9fc105e87f0eab6b464e78ecea6ffbb78d8bee07c5bbc2476f7900796ce", size = 146460, upload-time = "2025-05-21T17:42:15.269Z" }, - { url = "https://files.pythonhosted.org/packages/70/ce/8796f89989ad6fc3603991bb20e55f28a9279ea905a9d63c127a0440df7c/ndindex-1.10.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:058002945c1e956761d9a9ae620a1ae1c01b613fc9808d09e6f65efd977e169d", size = 145977, upload-time = "2025-05-21T17:42:16.771Z" }, - { url = "https://files.pythonhosted.org/packages/19/5a/67d0e21d21ccf19d396cb84dbf65b089f7076406400281e0b0bfc269602a/ndindex-1.10.0-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:004cfdde6106891e63f1a22e5d53d2884c277fd749839c6869e0bfffe5be2ed0", size = 162824, upload-time = "2025-05-21T17:42:18.214Z" }, - { url = "https://files.pythonhosted.org/packages/bc/ac/72dbdbedce254317e83bcaec269f166450cf10cf5130296c0a7ca65a1bb9/ndindex-1.10.0-pp39-pypy39_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0a9bbd54500cebe3d481611acd420a46099ce65181b81e8347e11a6bac68445", size = 159876, upload-time = "2025-05-21T17:42:19.706Z" }, - { url = "https://files.pythonhosted.org/packages/d4/84/5d74108950086ea209672dff368c42bd47e9d2d4890438e763a906a4a401/ndindex-1.10.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:367a2af7fbb84395a9d66f92dd0c4287b86e7b2c60cb96505c13ff36571bc9af", size = 147961, upload-time = "2025-05-21T17:42:21.2Z" }, ] [[package]] @@ -2049,116 +1414,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, ] -[[package]] -name = "networkx" -version = "3.2.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -sdist = { url = "https://files.pythonhosted.org/packages/c4/80/a84676339aaae2f1cfdf9f418701dd634aef9cc76f708ef55c36ff39c3ca/networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6", size = 2073928, upload-time = "2023-10-28T08:41:39.364Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/f0/8fbc882ca80cf077f1b246c0e3c3465f7f415439bdea6b899f6b19f61f70/networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2", size = 1647772, upload-time = "2023-10-28T08:41:36.945Z" }, -] - -[[package]] -name = "networkx" -version = "3.4.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version == '3.10.*'", -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, -] - [[package]] name = "networkx" version = "3.5" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", -] sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065, upload-time = "2025-05-29T11:35:07.804Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" }, ] -[[package]] -name = "numexpr" -version = "2.10.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -dependencies = [ - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/21/67/c7415cf04ebe418193cfd6595ae03e3a64d76dac7b9c010098b39cc7992e/numexpr-2.10.2.tar.gz", hash = "sha256:b0aff6b48ebc99d2f54f27b5f73a58cb92fde650aeff1b397c71c8788b4fff1a", size = 106787, upload-time = "2024-11-23T13:34:23.127Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/dc/bd84219318826d138b7e729ac3ffce3c706ab9d810ce74326a55c7252dd1/numexpr-2.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5b0e82d2109c1d9e63fcd5ea177d80a11b881157ab61178ddbdebd4c561ea46", size = 145011, upload-time = "2024-11-23T13:33:24.846Z" }, - { url = "https://files.pythonhosted.org/packages/31/6a/b1f08141283327478a57490c0ab3f26a634d4741ff33b9e22f760a7cedb0/numexpr-2.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3fc2b8035a0c2cdc352e58c3875cb668836018065cbf5752cb531015d9a568d8", size = 134777, upload-time = "2024-11-23T13:33:26.693Z" }, - { url = "https://files.pythonhosted.org/packages/7c/d6/6641864b0446ce472330de7644c78f90bd7e55d902046b44161f92721279/numexpr-2.10.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0db5ff5183935d1612653559c319922143e8fa3019007696571b13135f216458", size = 408893, upload-time = "2024-11-23T13:33:28.44Z" }, - { url = "https://files.pythonhosted.org/packages/25/ab/cb5809cb1f66431632d63dc028c58cb91492725c74dddc4b97ba62e88a92/numexpr-2.10.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15f59655458056fdb3a621b1bb8e071581ccf7e823916c7568bb7c9a3e393025", size = 397305, upload-time = "2024-11-23T13:33:30.181Z" }, - { url = "https://files.pythonhosted.org/packages/9c/a0/29bcb31a9debb743e3dc46bacd55f4f6ee6a77d95eda5c8dca19a29c0627/numexpr-2.10.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ce8cccf944339051e44a49a124a06287fe3066d0acbff33d1aa5aee10a96abb7", size = 1378789, upload-time = "2024-11-23T13:33:32.263Z" }, - { url = "https://files.pythonhosted.org/packages/cc/72/415262a7bdda706c41bf8254311a5ca13d3b8532341ab478be4583d7061a/numexpr-2.10.2-cp310-cp310-win32.whl", hash = "sha256:ba85371c9a8d03e115f4dfb6d25dfbce05387002b9bc85016af939a1da9624f0", size = 151935, upload-time = "2024-11-23T13:33:33.653Z" }, - { url = "https://files.pythonhosted.org/packages/71/fa/0124f0c2a502a0bac4553c8a171c551f154cf80a83a15e40d30c43e48a7e/numexpr-2.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:deb64235af9eeba59fcefa67e82fa80cfc0662e1b0aa373b7118a28da124d51d", size = 144961, upload-time = "2024-11-23T13:33:34.883Z" }, - { url = "https://files.pythonhosted.org/packages/de/b7/f25d6166f92ef23737c1c90416144492a664f0a56510d90f7c6577c2cd14/numexpr-2.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b360eb8d392483410fe6a3d5a7144afa298c9a0aa3e9fe193e89590b47dd477", size = 145055, upload-time = "2024-11-23T13:33:36.154Z" }, - { url = "https://files.pythonhosted.org/packages/66/64/428361ea6415826332f38ef2dd5c3abf4e7e601f033bfc9be68b680cb765/numexpr-2.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d9a42f5c24880350d88933c4efee91b857c378aaea7e8b86221fff569069841e", size = 134743, upload-time = "2024-11-23T13:33:37.273Z" }, - { url = "https://files.pythonhosted.org/packages/3f/fb/639ec91d2ea7b4a5d66e26e8ef8e06b020c8e9b9ebaf3bab7b0a9bee472e/numexpr-2.10.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83fcb11988b57cc25b028a36d285287d706d1f536ebf2662ea30bd990e0de8b9", size = 410397, upload-time = "2024-11-23T13:33:38.474Z" }, - { url = "https://files.pythonhosted.org/packages/89/5a/0f5c5b8a3a6d34eeecb30d0e2f722d50b9b38c0e175937e7c6268ffab997/numexpr-2.10.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4213a92efa9770bc28e3792134e27c7e5c7e97068bdfb8ba395baebbd12f991b", size = 398902, upload-time = "2024-11-23T13:33:39.802Z" }, - { url = "https://files.pythonhosted.org/packages/a2/d5/ec734e735eba5a753efed5be3707ee7447ebd371772f8081b65a4153fb97/numexpr-2.10.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebdbef5763ca057eea0c2b5698e4439d084a0505d9d6e94f4804f26e8890c45e", size = 1380354, upload-time = "2024-11-23T13:33:41.68Z" }, - { url = "https://files.pythonhosted.org/packages/30/51/406e572531d817480bd612ee08239a36ee82865fea02fce569f15631f4ee/numexpr-2.10.2-cp311-cp311-win32.whl", hash = "sha256:3bf01ec502d89944e49e9c1b5cc7c7085be8ca2eb9dd46a0eafd218afbdbd5f5", size = 151938, upload-time = "2024-11-23T13:33:43.998Z" }, - { url = "https://files.pythonhosted.org/packages/04/32/5882ed1dbd96234f327a73316a481add151ff827cfaf2ea24fb4d5ad04db/numexpr-2.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:e2d0ae24b0728e4bc3f1d3f33310340d67321d36d6043f7ce26897f4f1042db0", size = 144961, upload-time = "2024-11-23T13:33:45.329Z" }, - { url = "https://files.pythonhosted.org/packages/2b/96/d5053dea06d8298ae8052b4b049cbf8ef74998e28d57166cc27b8ae909e2/numexpr-2.10.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5323a46e75832334f1af86da1ef6ff0add00fbacdd266250be872b438bdf2be", size = 145029, upload-time = "2024-11-23T13:33:46.892Z" }, - { url = "https://files.pythonhosted.org/packages/3e/3c/fcd5a812ed5dda757b2d9ef2764a3e1cca6f6d1f02dbf113dc23a2c7702a/numexpr-2.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a42963bd4c62d8afa4f51e7974debfa39a048383f653544ab54f50a2f7ec6c42", size = 134851, upload-time = "2024-11-23T13:33:47.986Z" }, - { url = "https://files.pythonhosted.org/packages/0a/52/0ed3b306d8c9944129bce97fec73a2caff13adbd7e1df148d546d7eb2d4d/numexpr-2.10.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5191ba8f2975cb9703afc04ae845a929e193498c0e8bcd408ecb147b35978470", size = 411837, upload-time = "2024-11-23T13:33:49.223Z" }, - { url = "https://files.pythonhosted.org/packages/7d/9c/6b671dd3fb67d7e7da93cb76b7c5277743f310a216b7856bb18776bb3371/numexpr-2.10.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:97298b14f0105a794bea06fd9fbc5c423bd3ff4d88cbc618860b83eb7a436ad6", size = 400577, upload-time = "2024-11-23T13:33:50.559Z" }, - { url = "https://files.pythonhosted.org/packages/ea/4d/a167d1a215fe10ce58c45109f2869fd13aa0eef66f7e8c69af68be45d436/numexpr-2.10.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9d7805ccb6be2d3b0f7f6fad3707a09ac537811e8e9964f4074d28cb35543db", size = 1381735, upload-time = "2024-11-23T13:33:51.918Z" }, - { url = "https://files.pythonhosted.org/packages/c1/d4/17e4434f989e4917d31cbd88a043e1c9c16958149cf43fa622987111392b/numexpr-2.10.2-cp312-cp312-win32.whl", hash = "sha256:cb845b2d4f9f8ef0eb1c9884f2b64780a85d3b5ae4eeb26ae2b0019f489cd35e", size = 152102, upload-time = "2024-11-23T13:33:53.93Z" }, - { url = "https://files.pythonhosted.org/packages/b8/25/9ae599994076ef2a42d35ff6b0430da002647f212567851336a6c7b132d6/numexpr-2.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:57b59cbb5dcce4edf09cd6ce0b57ff60312479930099ca8d944c2fac896a1ead", size = 145061, upload-time = "2024-11-23T13:33:55.161Z" }, - { url = "https://files.pythonhosted.org/packages/8c/cb/2ea1848c46e4d75073c038dd75628d1aa442975303264ed230bf90f74f44/numexpr-2.10.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a37d6a51ec328c561b2ca8a2bef07025642eca995b8553a5267d0018c732976d", size = 145035, upload-time = "2024-11-23T13:33:56.778Z" }, - { url = "https://files.pythonhosted.org/packages/ec/cf/bb2bcd81d6f3243590e19ac3e7795a1a370f3ebcd8ecec1f46dcd5333f37/numexpr-2.10.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:81d1dde7dd6166d8ff5727bb46ab42a6b0048db0e97ceb84a121334a404a800f", size = 134858, upload-time = "2024-11-23T13:33:57.953Z" }, - { url = "https://files.pythonhosted.org/packages/48/9b/c9128ffb453205c2a4c84a3abed35447c7591c2c2812e77e34fd238cb2bb/numexpr-2.10.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3f814437d5a10797f8d89d2037cca2c9d9fa578520fc911f894edafed6ea3e", size = 415517, upload-time = "2024-11-23T13:33:59.163Z" }, - { url = "https://files.pythonhosted.org/packages/7e/b0/64c04c9f8b4a563218d00daa1ec4563364961b79025162c5276ab2c7c407/numexpr-2.10.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9309f2e43fe6e4560699ef5c27d7a848b3ff38549b6b57194207cf0e88900527", size = 403846, upload-time = "2024-11-23T13:34:01.006Z" }, - { url = "https://files.pythonhosted.org/packages/80/35/60e9041fd709fe98dd3109d73a03cdffaeb6ee2089179155f5c3754e9934/numexpr-2.10.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ebb73b93f5c4d6994f357fa5a47a9f7a5485577e633b3c46a603cb01445bbb19", size = 1381659, upload-time = "2024-11-23T13:34:02.979Z" }, - { url = "https://files.pythonhosted.org/packages/bd/5a/955bf5b5cf8f3de7b044a999e36327e14191fa073ed0e329456ed0f8161d/numexpr-2.10.2-cp313-cp313-win32.whl", hash = "sha256:ec04c9a3c050c175348801e27c18c68d28673b7bfb865ef88ce333be523bbc01", size = 152105, upload-time = "2024-11-23T13:34:04.374Z" }, - { url = "https://files.pythonhosted.org/packages/be/7a/8ce360a1848bb5bcc30a414493371678f43790ece397f8652d5f65757e57/numexpr-2.10.2-cp313-cp313-win_amd64.whl", hash = "sha256:d7a3fc83c959288544db3adc70612475d8ad53a66c69198105c74036182d10dd", size = 145060, upload-time = "2024-11-23T13:34:06.112Z" }, - { url = "https://files.pythonhosted.org/packages/41/6a/06a225ac970c5921f41bc069a30438ff64fd79ef5e828f5ec2d4f6658307/numexpr-2.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0495f8111c3633e265248709b8b3b521bbfa646ba384909edd10e2b9a588a83a", size = 145100, upload-time = "2024-11-23T13:33:12.013Z" }, - { url = "https://files.pythonhosted.org/packages/bb/c5/9ecfa0da1d93d57e3f447d10da8cf6d695c93131cec085625e5092b37631/numexpr-2.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2aa05ac71bee3b1253e73173c4d7fa96a09a18970c0226f1c2c07a71ffe988dc", size = 134839, upload-time = "2024-11-23T13:33:13.608Z" }, - { url = "https://files.pythonhosted.org/packages/f5/30/f1a48c485183da517bd28e0df6fee337d12bbb0cd2d6bf13f8f5695afd37/numexpr-2.10.2-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3a23c3002ab330056fbdd2785871937a6f2f2fa85d06c8d0ff74ea8418119d1", size = 408149, upload-time = "2024-11-23T13:33:15.549Z" }, - { url = "https://files.pythonhosted.org/packages/ed/f2/009d9dd8cd22f253fd6ead4165f81fafbe22489c1cfea612e18aa3dcb0fa/numexpr-2.10.2-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a018a7d81326f4c73d8b5aee61794d7d8514512f43957c0db61eb2a8a86848c7", size = 396740, upload-time = "2024-11-23T13:33:17.396Z" }, - { url = "https://files.pythonhosted.org/packages/47/90/e3f12670b3cca9bed85096671265e0f65cde6cf4646baadd4ee9c33944a8/numexpr-2.10.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:037859b17a0abe2b489d4c2cfdadd2bf458ec80dd83f338ea5544c7987e06b85", size = 1377883, upload-time = "2024-11-23T13:33:20.385Z" }, - { url = "https://files.pythonhosted.org/packages/06/1d/068c09a3c013c1178495f73a21ebd6ee25b9f0fc4202cea38b7a826c43a2/numexpr-2.10.2-cp39-cp39-win32.whl", hash = "sha256:eb278ccda6f893a312aa0452701bb17d098b7b14eb7c9381517d509cce0a39a3", size = 151878, upload-time = "2024-11-23T13:33:21.67Z" }, - { url = "https://files.pythonhosted.org/packages/70/81/affb9ff26e8accb210fe5585b095bd6872f5614d18b76cd53888e955ed9a/numexpr-2.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:734b64c6d6a597601ce9d0ef7b666e678ec015b446f1d1412c23903c021436c3", size = 144960, upload-time = "2024-11-23T13:33:23.617Z" }, -] - [[package]] name = "numexpr" version = "2.11.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", - "python_full_version == '3.10.*'", -] dependencies = [ - { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d2/8f/2cc977e91adbfbcdb6b49fdb9147e1d1c7566eb2c0c1e737e9a47020b5ca/numexpr-2.11.0.tar.gz", hash = "sha256:75b2c01a4eda2e7c357bc67a3f5c3dd76506c15b5fd4dc42845ef2e182181bad", size = 108960, upload-time = "2025-06-09T11:05:56.79Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/3a/99d5c9fb7f1cbb465798b79b9fd6d5df5ab10fee0d499c2b72a76634c80e/numexpr-2.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f471fd055a9e13cf5f4337ee12379b30b4dcda1ae0d85018d4649e841578c02", size = 147492, upload-time = "2025-06-09T11:04:59.605Z" }, - { url = "https://files.pythonhosted.org/packages/f4/32/914b8bb3d9a40e27ee56bfa915dcdfd60a460a6a9006bab80aa25df91c91/numexpr-2.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6e68a9800a3fa37c438b73a669f507c4973801a456a864ac56b62c3bd63d08af", size = 136741, upload-time = "2025-06-09T11:05:01.096Z" }, - { url = "https://files.pythonhosted.org/packages/5c/89/177fae13baaa9380a9f714bdf8b88ae941ed2c2f89bd228f2f089a651afa/numexpr-2.11.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad5cf0ebc3cdb12edb5aa50472108807ffd0a0ce95f87c0366a479fa83a7c346", size = 409327, upload-time = "2025-06-09T11:05:02.706Z" }, - { url = "https://files.pythonhosted.org/packages/83/03/0718f1ac2d7cc0422096ab0ac16cc04597539a2c69a22616d781a2be4359/numexpr-2.11.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8c9e6b07c136d06495c792f603099039bb1e7c6c29854cc5eb3d7640268df016", size = 399827, upload-time = "2025-06-09T11:05:04.33Z" }, - { url = "https://files.pythonhosted.org/packages/81/7d/8225d6fcafaa937606543bee6e985966c91d8741d25a8eb6d0143f64ce77/numexpr-2.11.0-cp310-cp310-win32.whl", hash = "sha256:4aba2f640d9d45b986a613ce94fcf008c42cc72eeba2990fefdb575228b1d3d1", size = 153165, upload-time = "2025-06-09T11:05:06.583Z" }, - { url = "https://files.pythonhosted.org/packages/8d/c8/abd6371906c2690852dbbd4cb8faa3d26c51bc8ce849cb4b16dc24e799c1/numexpr-2.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:7f75797bc75a2e7edf52a1c9e68a1295fa84250161c8f4e41df9e72723332c65", size = 146233, upload-time = "2025-06-09T11:05:07.614Z" }, { url = "https://files.pythonhosted.org/packages/d8/d1/1cf8137990b3f3d445556ed63b9bc347aec39bde8c41146b02d3b35c1adc/numexpr-2.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:450eba3c93c3e3e8070566ad8d70590949d6e574b1c960bf68edd789811e7da8", size = 147535, upload-time = "2025-06-09T11:05:08.929Z" }, { url = "https://files.pythonhosted.org/packages/b6/5e/bac7649d043f47c7c14c797efe60dbd19476468a149399cd706fe2e47f8c/numexpr-2.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f0eb88dbac8a7e61ee433006d0ddfd6eb921f5c6c224d1b50855bc98fb304c44", size = 136710, upload-time = "2025-06-09T11:05:10.366Z" }, { url = "https://files.pythonhosted.org/packages/1b/9f/c88fc34d82d23c66ea0b78b00a1fb3b64048e0f7ac7791b2cd0d2a4ce14d/numexpr-2.11.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a194e3684b3553ea199c3f4837f422a521c7e2f0cce13527adc3a6b4049f9e7c", size = 411169, upload-time = "2025-06-09T11:05:11.797Z" }, @@ -2185,84 +1459,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/75/d7/41287384e413e8d20457d35e264d9c9754e65eb13a988af51ceb7057f61b/numexpr-2.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a69b5c02014448a412012752dc46091902d28932c3be0c6e02e73cecceffb700", size = 147207, upload-time = "2025-06-09T11:05:39.011Z" }, ] -[[package]] -name = "numpy" -version = "2.0.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -sdist = { url = "https://files.pythonhosted.org/packages/a9/75/10dd1f8116a8b796cb2c737b674e02d02e80454bda953fa7e65d8c12b016/numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", size = 18902015, upload-time = "2024-08-26T20:19:40.945Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/21/91/3495b3237510f79f5d81f2508f9f13fea78ebfdf07538fc7444badda173d/numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", size = 21165245, upload-time = "2024-08-26T20:04:14.625Z" }, - { url = "https://files.pythonhosted.org/packages/05/33/26178c7d437a87082d11019292dce6d3fe6f0e9026b7b2309cbf3e489b1d/numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", size = 13738540, upload-time = "2024-08-26T20:04:36.784Z" }, - { url = "https://files.pythonhosted.org/packages/ec/31/cc46e13bf07644efc7a4bf68df2df5fb2a1a88d0cd0da9ddc84dc0033e51/numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", size = 5300623, upload-time = "2024-08-26T20:04:46.491Z" }, - { url = "https://files.pythonhosted.org/packages/6e/16/7bfcebf27bb4f9d7ec67332ffebee4d1bf085c84246552d52dbb548600e7/numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", size = 6901774, upload-time = "2024-08-26T20:04:58.173Z" }, - { url = "https://files.pythonhosted.org/packages/f9/a3/561c531c0e8bf082c5bef509d00d56f82e0ea7e1e3e3a7fc8fa78742a6e5/numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", size = 13907081, upload-time = "2024-08-26T20:05:19.098Z" }, - { url = "https://files.pythonhosted.org/packages/fa/66/f7177ab331876200ac7563a580140643d1179c8b4b6a6b0fc9838de2a9b8/numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", size = 19523451, upload-time = "2024-08-26T20:05:47.479Z" }, - { url = "https://files.pythonhosted.org/packages/25/7f/0b209498009ad6453e4efc2c65bcdf0ae08a182b2b7877d7ab38a92dc542/numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", size = 19927572, upload-time = "2024-08-26T20:06:17.137Z" }, - { url = "https://files.pythonhosted.org/packages/3e/df/2619393b1e1b565cd2d4c4403bdd979621e2c4dea1f8532754b2598ed63b/numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", size = 14400722, upload-time = "2024-08-26T20:06:39.16Z" }, - { url = "https://files.pythonhosted.org/packages/22/ad/77e921b9f256d5da36424ffb711ae79ca3f451ff8489eeca544d0701d74a/numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", size = 6472170, upload-time = "2024-08-26T20:06:50.361Z" }, - { url = "https://files.pythonhosted.org/packages/10/05/3442317535028bc29cf0c0dd4c191a4481e8376e9f0db6bcf29703cadae6/numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", size = 15905558, upload-time = "2024-08-26T20:07:13.881Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cf/034500fb83041aa0286e0fb16e7c76e5c8b67c0711bb6e9e9737a717d5fe/numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448", size = 21169137, upload-time = "2024-08-26T20:07:45.345Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d9/32de45561811a4b87fbdee23b5797394e3d1504b4a7cf40c10199848893e/numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195", size = 13703552, upload-time = "2024-08-26T20:08:06.666Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ca/2f384720020c7b244d22508cb7ab23d95f179fcfff33c31a6eeba8d6c512/numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57", size = 5298957, upload-time = "2024-08-26T20:08:15.83Z" }, - { url = "https://files.pythonhosted.org/packages/0e/78/a3e4f9fb6aa4e6fdca0c5428e8ba039408514388cf62d89651aade838269/numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a", size = 6905573, upload-time = "2024-08-26T20:08:27.185Z" }, - { url = "https://files.pythonhosted.org/packages/a0/72/cfc3a1beb2caf4efc9d0b38a15fe34025230da27e1c08cc2eb9bfb1c7231/numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669", size = 13914330, upload-time = "2024-08-26T20:08:48.058Z" }, - { url = "https://files.pythonhosted.org/packages/ba/a8/c17acf65a931ce551fee11b72e8de63bf7e8a6f0e21add4c937c83563538/numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951", size = 19534895, upload-time = "2024-08-26T20:09:16.536Z" }, - { url = "https://files.pythonhosted.org/packages/ba/86/8767f3d54f6ae0165749f84648da9dcc8cd78ab65d415494962c86fac80f/numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9", size = 19937253, upload-time = "2024-08-26T20:09:46.263Z" }, - { url = "https://files.pythonhosted.org/packages/df/87/f76450e6e1c14e5bb1eae6836478b1028e096fd02e85c1c37674606ab752/numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15", size = 14414074, upload-time = "2024-08-26T20:10:08.483Z" }, - { url = "https://files.pythonhosted.org/packages/5c/ca/0f0f328e1e59f73754f06e1adfb909de43726d4f24c6a3f8805f34f2b0fa/numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4", size = 6470640, upload-time = "2024-08-26T20:10:19.732Z" }, - { url = "https://files.pythonhosted.org/packages/eb/57/3a3f14d3a759dcf9bf6e9eda905794726b758819df4663f217d658a58695/numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc", size = 15910230, upload-time = "2024-08-26T20:10:43.413Z" }, - { url = "https://files.pythonhosted.org/packages/45/40/2e117be60ec50d98fa08c2f8c48e09b3edea93cfcabd5a9ff6925d54b1c2/numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b", size = 20895803, upload-time = "2024-08-26T20:11:13.916Z" }, - { url = "https://files.pythonhosted.org/packages/46/92/1b8b8dee833f53cef3e0a3f69b2374467789e0bb7399689582314df02651/numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e", size = 13471835, upload-time = "2024-08-26T20:11:34.779Z" }, - { url = "https://files.pythonhosted.org/packages/7f/19/e2793bde475f1edaea6945be141aef6c8b4c669b90c90a300a8954d08f0a/numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c", size = 5038499, upload-time = "2024-08-26T20:11:43.902Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ff/ddf6dac2ff0dd50a7327bcdba45cb0264d0e96bb44d33324853f781a8f3c/numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c", size = 6633497, upload-time = "2024-08-26T20:11:55.09Z" }, - { url = "https://files.pythonhosted.org/packages/72/21/67f36eac8e2d2cd652a2e69595a54128297cdcb1ff3931cfc87838874bd4/numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692", size = 13621158, upload-time = "2024-08-26T20:12:14.95Z" }, - { url = "https://files.pythonhosted.org/packages/39/68/e9f1126d757653496dbc096cb429014347a36b228f5a991dae2c6b6cfd40/numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a", size = 19236173, upload-time = "2024-08-26T20:12:44.049Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e9/1f5333281e4ebf483ba1c888b1d61ba7e78d7e910fdd8e6499667041cc35/numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c", size = 19634174, upload-time = "2024-08-26T20:13:13.634Z" }, - { url = "https://files.pythonhosted.org/packages/71/af/a469674070c8d8408384e3012e064299f7a2de540738a8e414dcfd639996/numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded", size = 14099701, upload-time = "2024-08-26T20:13:34.851Z" }, - { url = "https://files.pythonhosted.org/packages/d0/3d/08ea9f239d0e0e939b6ca52ad403c84a2bce1bde301a8eb4888c1c1543f1/numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5", size = 6174313, upload-time = "2024-08-26T20:13:45.653Z" }, - { url = "https://files.pythonhosted.org/packages/b2/b5/4ac39baebf1fdb2e72585c8352c56d063b6126be9fc95bd2bb5ef5770c20/numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a", size = 15606179, upload-time = "2024-08-26T20:14:08.786Z" }, - { url = "https://files.pythonhosted.org/packages/43/c1/41c8f6df3162b0c6ffd4437d729115704bd43363de0090c7f913cfbc2d89/numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", size = 21169942, upload-time = "2024-08-26T20:14:40.108Z" }, - { url = "https://files.pythonhosted.org/packages/39/bc/fd298f308dcd232b56a4031fd6ddf11c43f9917fbc937e53762f7b5a3bb1/numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", size = 13711512, upload-time = "2024-08-26T20:15:00.985Z" }, - { url = "https://files.pythonhosted.org/packages/96/ff/06d1aa3eeb1c614eda245c1ba4fb88c483bee6520d361641331872ac4b82/numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", size = 5306976, upload-time = "2024-08-26T20:15:10.876Z" }, - { url = "https://files.pythonhosted.org/packages/2d/98/121996dcfb10a6087a05e54453e28e58694a7db62c5a5a29cee14c6e047b/numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", size = 6906494, upload-time = "2024-08-26T20:15:22.055Z" }, - { url = "https://files.pythonhosted.org/packages/15/31/9dffc70da6b9bbf7968f6551967fc21156207366272c2a40b4ed6008dc9b/numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", size = 13912596, upload-time = "2024-08-26T20:15:42.452Z" }, - { url = "https://files.pythonhosted.org/packages/b9/14/78635daab4b07c0930c919d451b8bf8c164774e6a3413aed04a6d95758ce/numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd", size = 19526099, upload-time = "2024-08-26T20:16:11.048Z" }, - { url = "https://files.pythonhosted.org/packages/26/4c/0eeca4614003077f68bfe7aac8b7496f04221865b3a5e7cb230c9d055afd/numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", size = 19932823, upload-time = "2024-08-26T20:16:40.171Z" }, - { url = "https://files.pythonhosted.org/packages/f1/46/ea25b98b13dccaebddf1a803f8c748680d972e00507cd9bc6dcdb5aa2ac1/numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", size = 14404424, upload-time = "2024-08-26T20:17:02.604Z" }, - { url = "https://files.pythonhosted.org/packages/c8/a6/177dd88d95ecf07e722d21008b1b40e681a929eb9e329684d449c36586b2/numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", size = 6476809, upload-time = "2024-08-26T20:17:13.553Z" }, - { url = "https://files.pythonhosted.org/packages/ea/2b/7fc9f4e7ae5b507c1a3a21f0f15ed03e794c1242ea8a242ac158beb56034/numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", size = 15911314, upload-time = "2024-08-26T20:17:36.72Z" }, - { url = "https://files.pythonhosted.org/packages/8f/3b/df5a870ac6a3be3a86856ce195ef42eec7ae50d2a202be1f5a4b3b340e14/numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", size = 21025288, upload-time = "2024-08-26T20:18:07.732Z" }, - { url = "https://files.pythonhosted.org/packages/2c/97/51af92f18d6f6f2d9ad8b482a99fb74e142d71372da5d834b3a2747a446e/numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", size = 6762793, upload-time = "2024-08-26T20:18:19.125Z" }, - { url = "https://files.pythonhosted.org/packages/12/46/de1fbd0c1b5ccaa7f9a005b66761533e2f6a3e560096682683a223631fe9/numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", size = 19334885, upload-time = "2024-08-26T20:18:47.237Z" }, - { url = "https://files.pythonhosted.org/packages/cc/dc/d330a6faefd92b446ec0f0dfea4c3207bb1fef3c4771d19cf4543efd2c78/numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", size = 15828784, upload-time = "2024-08-26T20:19:11.19Z" }, -] - [[package]] name = "numpy" version = "2.1.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", - "python_full_version == '3.10.*'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] sdist = { url = "https://files.pythonhosted.org/packages/25/ca/1166b75c21abd1da445b97bf1fa2f14f423c6cfb4fc7c4ef31dccf9f6a94/numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761", size = 20166090, upload-time = "2024-11-02T17:48:55.832Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/80/d572a4737626372915bca41c3afbfec9d173561a39a0a61bacbbfd1dafd4/numpy-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c894b4305373b9c5576d7a12b473702afdf48ce5369c074ba304cc5ad8730dff", size = 21152472, upload-time = "2024-11-02T17:30:37.354Z" }, - { url = "https://files.pythonhosted.org/packages/6f/bb/7bfba10c791ae3bb6716da77ad85a82d5fac07fc96fb0023ef0571df9d20/numpy-2.1.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b47fbb433d3260adcd51eb54f92a2ffbc90a4595f8970ee00e064c644ac788f5", size = 13747967, upload-time = "2024-11-02T17:30:59.602Z" }, - { url = "https://files.pythonhosted.org/packages/da/d6/2df7bde35f0478455f0be5934877b3e5a505f587b00230f54a519a6b55a5/numpy-2.1.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:825656d0743699c529c5943554d223c021ff0494ff1442152ce887ef4f7561a1", size = 5354921, upload-time = "2024-11-02T17:31:09.428Z" }, - { url = "https://files.pythonhosted.org/packages/d1/bb/75b945874f931494891eac6ca06a1764d0e8208791f3addadb2963b83527/numpy-2.1.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a4825252fcc430a182ac4dee5a505053d262c807f8a924603d411f6718b88fd", size = 6888603, upload-time = "2024-11-02T17:31:20.835Z" }, - { url = "https://files.pythonhosted.org/packages/68/a7/fde73636f6498dbfa6d82fc336164635fe592f1ad0d13285fcb6267fdc1c/numpy-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e711e02f49e176a01d0349d82cb5f05ba4db7d5e7e0defd026328e5cfb3226d3", size = 13889862, upload-time = "2024-11-02T17:31:41.486Z" }, - { url = "https://files.pythonhosted.org/packages/05/db/5d9c91b2e1e2e72be1369278f696356d44975befcae830daf2e667dcb54f/numpy-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098", size = 16328151, upload-time = "2024-11-02T17:32:08.262Z" }, - { url = "https://files.pythonhosted.org/packages/3e/6a/7eb732109b53ae64a29e25d7e68eb9d6611037f6354875497008a49e74d3/numpy-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c7662f0e3673fe4e832fe07b65c50342ea27d989f92c80355658c7f888fcc83c", size = 16704107, upload-time = "2024-11-02T17:32:34.361Z" }, - { url = "https://files.pythonhosted.org/packages/88/cc/278113b66a1141053cbda6f80e4200c6da06b3079c2d27bda1fde41f2c1f/numpy-2.1.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fa2d1337dc61c8dc417fbccf20f6d1e139896a30721b7f1e832b2bb6ef4eb6c4", size = 14385789, upload-time = "2024-11-02T17:32:57.152Z" }, - { url = "https://files.pythonhosted.org/packages/f5/69/eb20f5e1bfa07449bc67574d2f0f7c1e6b335fb41672e43861a7727d85f2/numpy-2.1.3-cp310-cp310-win32.whl", hash = "sha256:72dcc4a35a8515d83e76b58fdf8113a5c969ccd505c8a946759b24e3182d1f23", size = 6536706, upload-time = "2024-11-02T17:33:09.12Z" }, - { url = "https://files.pythonhosted.org/packages/8e/8b/1c131ab5a94c1086c289c6e1da1d843de9dbd95fe5f5ee6e61904c9518e2/numpy-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:ecc76a9ba2911d8d37ac01de72834d8849e55473457558e12995f4cd53e778e0", size = 12864165, upload-time = "2024-11-02T17:33:28.974Z" }, { url = "https://files.pythonhosted.org/packages/ad/81/c8167192eba5247593cd9d305ac236847c2912ff39e11402e72ae28a4985/numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d", size = 21156252, upload-time = "2024-11-02T17:34:01.372Z" }, { url = "https://files.pythonhosted.org/packages/da/74/5a60003fc3d8a718d830b08b654d0eea2d2db0806bab8f3c2aca7e18e010/numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41", size = 13784119, upload-time = "2024-11-02T17:34:23.809Z" }, { url = "https://files.pythonhosted.org/packages/47/7c/864cb966b96fce5e63fcf25e1e4d957fe5725a635e5f11fe03f39dd9d6b5/numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9", size = 5352978, upload-time = "2024-11-02T17:34:34.001Z" }, @@ -2303,10 +1513,62 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ef/62/1d3204313357591c913c32132a28f09a26357e33ea3c4e2fe81269e0dca1/numpy-2.1.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17", size = 14067180, upload-time = "2024-11-02T17:45:37.234Z" }, { url = "https://files.pythonhosted.org/packages/24/d7/78a40ed1d80e23a774cb8a34ae8a9493ba1b4271dde96e56ccdbab1620ef/numpy-2.1.3-cp313-cp313t-win32.whl", hash = "sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48", size = 6291907, upload-time = "2024-11-02T17:45:48.951Z" }, { url = "https://files.pythonhosted.org/packages/86/09/a5ab407bd7f5f5599e6a9261f964ace03a73e7c6928de906981c31c38082/numpy-2.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4", size = 12644098, upload-time = "2024-11-02T17:46:07.941Z" }, - { url = "https://files.pythonhosted.org/packages/00/e7/8d8bb791b62586cc432ecbb70632b4f23b7b7c88df41878de7528264f6d7/numpy-2.1.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4f2015dfe437dfebbfce7c85c7b53d81ba49e71ba7eadbf1df40c915af75979f", size = 20983893, upload-time = "2024-11-02T17:47:09.365Z" }, - { url = "https://files.pythonhosted.org/packages/5e/f3/cb8118a044b5007586245a650360c9f5915b2f4232dd7658bb7a63dd1d02/numpy-2.1.3-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:3522b0dfe983a575e6a9ab3a4a4dfe156c3e428468ff08ce582b9bb6bd1d71d4", size = 6752501, upload-time = "2024-11-02T17:47:21.52Z" }, - { url = "https://files.pythonhosted.org/packages/53/f5/365b46439b518d2ec6ebb880cc0edf90f225145dfd4db7958334f7164530/numpy-2.1.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c006b607a865b07cd981ccb218a04fc86b600411d83d6fc261357f1c0966755d", size = 16142601, upload-time = "2024-11-02T17:47:45.575Z" }, - { url = "https://files.pythonhosted.org/packages/03/c2/d1fee6ba999aa7cd41ca6856937f2baaf604c3eec1565eae63451ec31e5e/numpy-2.1.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e14e26956e6f1696070788252dcdff11b4aca4c3e8bd166e0df1bb8f315a67cb", size = 12771397, upload-time = "2024-11-02T17:48:05.988Z" }, +] + +[[package]] +name = "numpy" +version = "2.5.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/13/01/11703282db468b85f6f7b8c7f22d058de5970d5c7e60a3a8aaa313c3de36/numpy-2.5.3.tar.gz", hash = "sha256:df2d5874ff183595a4ba404edd04f6bd9b5505c1d7708573f6a6c17489a67563", size = 20791231, upload-time = "2026-09-06T16:27:47.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/50/8fdbb16af64895706a45f06a4068e29db732ec180f3c1375f14123359138/numpy-2.5.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cb189f09db39283b26bfd061ec16189e14f71c6755207f72a0f7540867afe5b9", size = 16994982, upload-time = "2026-09-06T16:24:29.244Z" }, + { url = "https://files.pythonhosted.org/packages/60/39/789131c1188c078dcb3a1692e72e1e050c68b88ffe72c9ccaac9bcd7a9cd/numpy-2.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f59a878c33d6b88122d80d239bb3b845d58708750b0cb06a09aebb9b18ec696c", size = 12009327, upload-time = "2026-09-06T16:24:32.491Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/a312e95696e5f601914dd8b6dd844692ba61670807417e24b68e337b5c70/numpy-2.5.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a72f874bc9e10e4b8f80426fb49716d5141f64442a0c8418065093ec8017fbb0", size = 5445405, upload-time = "2026-09-06T16:24:35.071Z" }, + { url = "https://files.pythonhosted.org/packages/30/d0/5623a1707ed4fe16e3909fe3cf5ee3da004ae677ad23d83bbf3adf1a6faf/numpy-2.5.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:fc36dc566135b5eceec4cf89758fcb719266a019ef07dae1754ae7c9f617ef3e", size = 6783213, upload-time = "2026-09-06T16:24:37.253Z" }, + { url = "https://files.pythonhosted.org/packages/f1/32/84146fc020ad3c25f805f70ab60da46fe3c540a21369754a7e4369754b6f/numpy-2.5.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76c2c1e6bfa5c84adc6434dfbf013aa92096a7985221762c8f11fedfd20fff58", size = 15687872, upload-time = "2026-09-06T16:24:39.751Z" }, + { url = "https://files.pythonhosted.org/packages/65/af/aa78d1a88805456e212b65461354cd943197fb9acecc4c90fd12295123a3/numpy-2.5.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7e18c623bb5c95acb3b3328861272816ba199fb531921c5d6d0b675f1fde9e3", size = 16717410, upload-time = "2026-09-06T16:24:42.745Z" }, + { url = "https://files.pythonhosted.org/packages/3b/24/faa79d865e69a97ba17473b23a1b74094b2259c03e820c70297293b9ea49/numpy-2.5.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4f8929ee6c96bfbd7b4ed2032e0c03af86fe1826740ab61ddabf9072d06e57ff", size = 17040975, upload-time = "2026-09-06T16:24:45.961Z" }, + { url = "https://files.pythonhosted.org/packages/62/4a/8877e629445a7176297dffcaf9c485faa96a95d81728a62521ad55bd4c0f/numpy-2.5.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b5d93cf48f687479941d12b69c873ad2cc76bbd487f0091c2200636497f34034", size = 18476479, upload-time = "2026-09-06T16:24:49.35Z" }, + { url = "https://files.pythonhosted.org/packages/c8/db/35e1c2d38b04cbd5b731f9d71495e055e813197669d22b612f11748d2ff9/numpy-2.5.3-cp312-cp312-win32.whl", hash = "sha256:bf63afbe037eb5d2fe87fbcc7778e61da53ebaf21d938a4515aa73b62532a5d4", size = 6133378, upload-time = "2026-09-06T16:24:51.915Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/accf6d4f0c80c5d9ba9735d6b1550e444180599f34dec69ca01360f717ad/numpy-2.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0a59a421a32580a009e8a1751345bf829631b990dc1794b80514ab722b435def", size = 12567828, upload-time = "2026-09-06T16:24:54.255Z" }, + { url = "https://files.pythonhosted.org/packages/22/43/1764aff32e4652526ae2f71fa8b3efd8d25c8a3d6926914454e47138ed1e/numpy-2.5.3-cp312-cp312-win_arm64.whl", hash = "sha256:ccb32e0525d29e8b0572eb84c9a57af0e7a4e615726927506f55063c62414034", size = 10485432, upload-time = "2026-09-06T16:24:57.278Z" }, + { url = "https://files.pythonhosted.org/packages/79/e5/8fb89cd46d14e35699d13bf943a5f5f441ecee8667120a1f6105ab89e349/numpy-2.5.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:66a78fe4556c60aceda5916f9eacd638b18e9e681016ec302dcb4682d6d4d034", size = 16991061, upload-time = "2026-09-06T16:25:00.411Z" }, + { url = "https://files.pythonhosted.org/packages/2f/06/9dc9e48b5e5e941c8b10350c5ff2d721da42a20517d911d15544246775ff/numpy-2.5.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92f30e89b8ee0ecf363033576c422b2f58fed6a80bed0aa48dff6d14c654663e", size = 12003676, upload-time = "2026-09-06T16:25:03.475Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2a/98282aa5b8f58b1157d440bb6282eed47e3632a5de53a714fbab17e659fe/numpy-2.5.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f9a2353b37a1a9e78fd82b27ad7e2a32a2d036604d18f02b05e3136c62ca3b09", size = 5439695, upload-time = "2026-09-06T16:25:05.978Z" }, + { url = "https://files.pythonhosted.org/packages/a1/f9/b6533d777be9d6ffd29dc1be0867e563e6e8cc9a220ff1b716adc317f060/numpy-2.5.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:ccbc4665079665c3cf3bab4db9f6b095370cd6437d66be549b6c2a1fd19e1958", size = 6779395, upload-time = "2026-09-06T16:25:08.599Z" }, + { url = "https://files.pythonhosted.org/packages/73/85/735720d04ec197c5dcfacdfc9922667c7f1f5f496a279b7ba4d7c74c4cc7/numpy-2.5.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c76d5dde9f445058f83d0c02af00557a4db91de9a9a57c0df87d1535001d654b", size = 15681750, upload-time = "2026-09-06T16:25:11.173Z" }, + { url = "https://files.pythonhosted.org/packages/3a/1b/3b16a9bc514a440a7a0883684111dcb1ef1aee960af2ca95da8fc775f124/numpy-2.5.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5fa86b80fd24bcd1aff83ad23be44ea323de3f787be8f8b15d4a65621e25321", size = 16708577, upload-time = "2026-09-06T16:25:14.171Z" }, + { url = "https://files.pythonhosted.org/packages/69/c4/386f397831b07328b639c96c5b62719346cf4baf07c68d927239752b1534/numpy-2.5.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd4cb9ad3c7889b9b3fe0a9a9fb5d2ed26f9879bff2608d9f01aed147a20d231", size = 17042047, upload-time = "2026-09-06T16:25:17.582Z" }, + { url = "https://files.pythonhosted.org/packages/5f/3e/a700ecbf36e85ae8328fd3b0e12eeddc22ed6358a64cb2bd913e0d195d65/numpy-2.5.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1302b90c0e52281681b2975adfe8a860cb7b12216a27b4b0b4207c44bf7bccf0", size = 18465724, upload-time = "2026-09-06T16:25:20.949Z" }, + { url = "https://files.pythonhosted.org/packages/41/ee/38e785e88a4045f6ad1d1f2808dcdfafdca48c760260c0587bf171e29fc9/numpy-2.5.3-cp313-cp313-win32.whl", hash = "sha256:1c80eabb4035ecf4ca9cd49cde8a9fdd69a729e63e6474887d1523ade7aa277f", size = 6129003, upload-time = "2026-09-06T16:25:23.664Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ec/100f2b1794ede74a9b3d7ec6b9736927f56713414c1dfe19ab6c383494bf/numpy-2.5.3-cp313-cp313-win_amd64.whl", hash = "sha256:71cad2b2a7451ab79d8f5e71b453485b6775963d5cf794179144a7463fe6e8ec", size = 12560965, upload-time = "2026-09-06T16:25:26.602Z" }, + { url = "https://files.pythonhosted.org/packages/80/b1/7dc825ca94c12acebbce4c37caa5e198695eb31424bc579679f32b1bb49d/numpy-2.5.3-cp313-cp313-win_arm64.whl", hash = "sha256:8e4dd766076855b5ff7ea52fa5f07ce26286726e0f8bff446b7739d02e6ea204", size = 10482343, upload-time = "2026-09-06T16:25:29.772Z" }, + { url = "https://files.pythonhosted.org/packages/70/78/cf416f15dc29375a229d9dfebf8db6e313f291580b39fa1a568b6052bb07/numpy-2.5.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:350ba9783ce969cf9f7ce6e6a9a58e1a6e2a19ca025b7ee448c4db727706212a", size = 16998686, upload-time = "2026-09-06T16:25:33.171Z" }, + { url = "https://files.pythonhosted.org/packages/9e/59/abcc2d8def4fd60eec7d87f92d27c13448ffd9ab14339bcc63a0d7a2fdea/numpy-2.5.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:012e66aca395d795496446e52aeeb5866312a5d4d3f27da270e5a0b43f70dc5c", size = 12013862, upload-time = "2026-09-06T16:25:36.748Z" }, + { url = "https://files.pythonhosted.org/packages/94/75/4640d2d6e4b64a049e48425a82728a41ef4adb61332d2cba68055774878b/numpy-2.5.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:adc1ada2662f8a5f960b8a10d9986897e7499ef07e06d4cfe7197f8cce923c07", size = 5449793, upload-time = "2026-09-06T16:25:39.476Z" }, + { url = "https://files.pythonhosted.org/packages/96/cd/625b57ae33d4ca560f32cc0b47b4a5922146d9beb998ddf773900d440a73/numpy-2.5.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:54a115e5a73b8fc44f0cebef486365a1894b5c9760685d4558b72b7c3eb846e0", size = 6785176, upload-time = "2026-09-06T16:25:42.069Z" }, + { url = "https://files.pythonhosted.org/packages/9c/72/12918652e7912ef9751e8694c88820fcd1908e0618cb23f5f3caa6004b7b/numpy-2.5.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be5a8381859b6da607c84f4f7d6847725f1cf1853ef8a2c9e115b7d58bef47dc", size = 15703377, upload-time = "2026-09-06T16:25:45.135Z" }, + { url = "https://files.pythonhosted.org/packages/45/8f/9beacf79ca7c650688ad0baa80931adb988fe6e6e5d5903c23cc3dbd70eb/numpy-2.5.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b0521d0f4aebb6e06189451025fa17a913287b13c03d5fe05c017333b654ea5b", size = 16711928, upload-time = "2026-09-06T16:25:48.461Z" }, + { url = "https://files.pythonhosted.org/packages/09/8d/41d0a56e1ac4c87495c897a211b1368691b7237aadabec8b3b8f3a74d48f/numpy-2.5.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9deb49575e5b0b94ed72c8a64ec4d033381adc27e9060ae842971f697ba96104", size = 17059507, upload-time = "2026-09-06T16:25:51.873Z" }, + { url = "https://files.pythonhosted.org/packages/08/1e/0dfbc5cc251d54e2af790f254d24ec38637fa97ec7d5d11de7ffed787098/numpy-2.5.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b00eefbcf0f292945c4b4dec2ae845389ef5bcdcd596e6e4328051db5b5ba694", size = 18471002, upload-time = "2026-09-06T16:25:55.233Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2c/dfa40f6991f8185c8c30ffd023dfcbb11888e823cfab9557b920f3bb7bed/numpy-2.5.3-cp314-cp314-win32.whl", hash = "sha256:c2381f82999704f818e2c987a865050e285ec3621262c66d40f5a96c8f899f8e", size = 6180485, upload-time = "2026-09-06T16:25:58.157Z" }, + { url = "https://files.pythonhosted.org/packages/a4/73/d2c08231e4fde7e415501fd02c715d96e98599b2d8384445933944152984/numpy-2.5.3-cp314-cp314-win_amd64.whl", hash = "sha256:2c25dfa72943e4336ddb6b0ee4277b47a0c85bede0807530ec68103bf58e2c10", size = 12698179, upload-time = "2026-09-06T16:26:00.789Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e9/dcdcc9b95cf5f49815055573aee1b11cfbf5299f38a180e437ded050810f/numpy-2.5.3-cp314-cp314-win_arm64.whl", hash = "sha256:15aa985ac73a8db02db7663381aa109510449d3819d37206caed27b33a65a8a6", size = 10769383, upload-time = "2026-09-06T16:26:04.011Z" }, + { url = "https://files.pythonhosted.org/packages/49/c4/af8bc08a7ef4e1529a7c0cf24969accce316b783999802089a581ec99272/numpy-2.5.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ac7bb1c52d445bd4f8f7f97fefe6abc3a084dc4d63df50d79b17fa2b78e89297", size = 12132668, upload-time = "2026-09-06T16:26:07.138Z" }, + { url = "https://files.pythonhosted.org/packages/c5/ae/0f15eb56d4ec5e13c1f7ff04ff407f997d1acbadb45d3e1f2e2645a8f43c/numpy-2.5.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e6ab667ba76450084eb64013762c438ea76d9d29cc676dcd6c2e9892ba37f841", size = 5568580, upload-time = "2026-09-06T16:26:09.828Z" }, + { url = "https://files.pythonhosted.org/packages/23/fb/c72a8f25d4b6e96c354e7ab45ace3b27dc11e5d6a13b6c7d0cd6b08bf112/numpy-2.5.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:f7fabeb6cea87d65f3b926de33d03fb016cfdc29314c90974383b5582ae72891", size = 6882634, upload-time = "2026-09-06T16:26:12.524Z" }, + { url = "https://files.pythonhosted.org/packages/07/a9/968c90ed2ab15060c338e8137f1215b5a60756ae07328e0a60d1c6734df4/numpy-2.5.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fb6f8fb9ff0b3a69f52c66ce397b0246583e9f28616231b0e32ca49259a5fa6", size = 15748923, upload-time = "2026-09-06T16:26:15.092Z" }, + { url = "https://files.pythonhosted.org/packages/59/08/9df04103947b95e3b6b1f2ed1a70521f325647a31b82da6a2aae3a485508/numpy-2.5.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93e1f5447e2b1e479d7bd74701e84746b86450cff1fc368b132d195e2b8f8211", size = 16746748, upload-time = "2026-09-06T16:26:18.43Z" }, + { url = "https://files.pythonhosted.org/packages/41/a0/14c8d5fe5b53a334aabb653deb391c0fef49558f491880ea300ed6785224/numpy-2.5.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c00abe94c1a69d75d827dcf1c025b25c8a45d230b3bcd77a9020883a1b047653", size = 17111561, upload-time = "2026-09-06T16:26:22.113Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a6/d7e96e42f01522e154c32489640f16dfc4f6181d165d05fc3bec8c2c4999/numpy-2.5.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:536f963710a4e63934d80ac0dc4f478804a83e9a84b6828018f25d09953ada33", size = 18513945, upload-time = "2026-09-06T16:26:25.401Z" }, + { url = "https://files.pythonhosted.org/packages/25/39/3453afb7119d0449ef11c886874120ff180e2c337760e0e2d88f70f1a945/numpy-2.5.3-cp314-cp314t-win32.whl", hash = "sha256:4c8a6d2ebce6305fd82fbefca827775437147052a976ee7c94b36a0c1b52ac6c", size = 6335421, upload-time = "2026-09-06T16:26:28.175Z" }, + { url = "https://files.pythonhosted.org/packages/99/01/22815d2b19a1a746b1d45205cffebb3fe511a18acb75fba6c88491fc9894/numpy-2.5.3-cp314-cp314t-win_amd64.whl", hash = "sha256:9a37475425b431b4d060f23b4f52cd2f3aef6bc7c654bd760adf0040eec9d435", size = 12896420, upload-time = "2026-09-06T16:26:31.265Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ee/a7cbba67eeaff038dc29ca8b98a88396c8b0cc9c89d4924f4a27a5c9150b/numpy-2.5.3-cp314-cp314t-win_arm64.whl", hash = "sha256:2d8240cb4c16fd831074aa2b2cf9fc54664d826341d61c372245b96a74a49a9a", size = 10857177, upload-time = "2026-09-06T16:26:34.167Z" }, ] [[package]] @@ -2332,58 +1594,57 @@ wheels = [ [[package]] name = "pandas" -version = "2.3.2" +version = "3.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, { name = "python-dateutil" }, - { name = "pytz" }, - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/79/8e/0e90233ac205ad182bd6b422532695d2b9414944a280488105d598c70023/pandas-2.3.2.tar.gz", hash = "sha256:ab7b58f8f82706890924ccdfb5f48002b83d2b5a3845976a9fb705d36c34dcdb", size = 4488684, upload-time = "2025-08-21T10:28:29.257Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/16/a8eeb70aad84ccbf14076793f90e0031eded63c1899aeae9fdfbf37881f4/pandas-2.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52bc29a946304c360561974c6542d1dd628ddafa69134a7131fdfd6a5d7a1a35", size = 11539648, upload-time = "2025-08-21T10:26:36.236Z" }, - { url = "https://files.pythonhosted.org/packages/47/f1/c5bdaea13bf3708554d93e948b7ea74121ce6e0d59537ca4c4f77731072b/pandas-2.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:220cc5c35ffaa764dd5bb17cf42df283b5cb7fdf49e10a7b053a06c9cb48ee2b", size = 10786923, upload-time = "2025-08-21T10:26:40.518Z" }, - { url = "https://files.pythonhosted.org/packages/bb/10/811fa01476d29ffed692e735825516ad0e56d925961819e6126b4ba32147/pandas-2.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42c05e15111221384019897df20c6fe893b2f697d03c811ee67ec9e0bb5a3424", size = 11726241, upload-time = "2025-08-21T10:26:43.175Z" }, - { url = "https://files.pythonhosted.org/packages/c4/6a/40b043b06e08df1ea1b6d20f0e0c2f2c4ec8c4f07d1c92948273d943a50b/pandas-2.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc03acc273c5515ab69f898df99d9d4f12c4d70dbfc24c3acc6203751d0804cf", size = 12349533, upload-time = "2025-08-21T10:26:46.611Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ea/2e081a2302e41a9bca7056659fdd2b85ef94923723e41665b42d65afd347/pandas-2.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d25c20a03e8870f6339bcf67281b946bd20b86f1a544ebbebb87e66a8d642cba", size = 13202407, upload-time = "2025-08-21T10:26:49.068Z" }, - { url = "https://files.pythonhosted.org/packages/f4/12/7ff9f6a79e2ee8869dcf70741ef998b97ea20050fe25f83dc759764c1e32/pandas-2.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21bb612d148bb5860b7eb2c10faacf1a810799245afd342cf297d7551513fbb6", size = 13837212, upload-time = "2025-08-21T10:26:51.832Z" }, - { url = "https://files.pythonhosted.org/packages/d8/df/5ab92fcd76455a632b3db34a746e1074d432c0cdbbd28d7cd1daba46a75d/pandas-2.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:b62d586eb25cb8cb70a5746a378fc3194cb7f11ea77170d59f889f5dfe3cec7a", size = 11338099, upload-time = "2025-08-21T10:26:54.382Z" }, - { url = "https://files.pythonhosted.org/packages/7a/59/f3e010879f118c2d400902d2d871c2226cef29b08c09fb8dc41111730400/pandas-2.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1333e9c299adcbb68ee89a9bb568fc3f20f9cbb419f1dd5225071e6cddb2a743", size = 11563308, upload-time = "2025-08-21T10:26:56.656Z" }, - { url = "https://files.pythonhosted.org/packages/38/18/48f10f1cc5c397af59571d638d211f494dba481f449c19adbd282aa8f4ca/pandas-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:76972bcbd7de8e91ad5f0ca884a9f2c477a2125354af624e022c49e5bd0dfff4", size = 10820319, upload-time = "2025-08-21T10:26:59.162Z" }, - { url = "https://files.pythonhosted.org/packages/95/3b/1e9b69632898b048e223834cd9702052bcf06b15e1ae716eda3196fb972e/pandas-2.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b98bdd7c456a05eef7cd21fd6b29e3ca243591fe531c62be94a2cc987efb5ac2", size = 11790097, upload-time = "2025-08-21T10:27:02.204Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ef/0e2ffb30b1f7fbc9a588bd01e3c14a0d96854d09a887e15e30cc19961227/pandas-2.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d81573b3f7db40d020983f78721e9bfc425f411e616ef019a10ebf597aedb2e", size = 12397958, upload-time = "2025-08-21T10:27:05.409Z" }, - { url = "https://files.pythonhosted.org/packages/23/82/e6b85f0d92e9afb0e7f705a51d1399b79c7380c19687bfbf3d2837743249/pandas-2.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e190b738675a73b581736cc8ec71ae113d6c3768d0bd18bffa5b9a0927b0b6ea", size = 13225600, upload-time = "2025-08-21T10:27:07.791Z" }, - { url = "https://files.pythonhosted.org/packages/e8/f1/f682015893d9ed51611948bd83683670842286a8edd4f68c2c1c3b231eef/pandas-2.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c253828cb08f47488d60f43c5fc95114c771bbfff085da54bfc79cb4f9e3a372", size = 13879433, upload-time = "2025-08-21T10:27:10.347Z" }, - { url = "https://files.pythonhosted.org/packages/a7/e7/ae86261695b6c8a36d6a4c8d5f9b9ede8248510d689a2f379a18354b37d7/pandas-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:9467697b8083f9667b212633ad6aa4ab32436dcbaf4cd57325debb0ddef2012f", size = 11336557, upload-time = "2025-08-21T10:27:12.983Z" }, - { url = "https://files.pythonhosted.org/packages/ec/db/614c20fb7a85a14828edd23f1c02db58a30abf3ce76f38806155d160313c/pandas-2.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fbb977f802156e7a3f829e9d1d5398f6192375a3e2d1a9ee0803e35fe70a2b9", size = 11587652, upload-time = "2025-08-21T10:27:15.888Z" }, - { url = "https://files.pythonhosted.org/packages/99/b0/756e52f6582cade5e746f19bad0517ff27ba9c73404607c0306585c201b3/pandas-2.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b9b52693123dd234b7c985c68b709b0b009f4521000d0525f2b95c22f15944b", size = 10717686, upload-time = "2025-08-21T10:27:18.486Z" }, - { url = "https://files.pythonhosted.org/packages/37/4c/dd5ccc1e357abfeee8353123282de17997f90ff67855f86154e5a13b81e5/pandas-2.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bd281310d4f412733f319a5bc552f86d62cddc5f51d2e392c8787335c994175", size = 11278722, upload-time = "2025-08-21T10:27:21.149Z" }, - { url = "https://files.pythonhosted.org/packages/d3/a4/f7edcfa47e0a88cda0be8b068a5bae710bf264f867edfdf7b71584ace362/pandas-2.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96d31a6b4354e3b9b8a2c848af75d31da390657e3ac6f30c05c82068b9ed79b9", size = 11987803, upload-time = "2025-08-21T10:27:23.767Z" }, - { url = "https://files.pythonhosted.org/packages/f6/61/1bce4129f93ab66f1c68b7ed1c12bac6a70b1b56c5dab359c6bbcd480b52/pandas-2.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:df4df0b9d02bb873a106971bb85d448378ef14b86ba96f035f50bbd3688456b4", size = 12766345, upload-time = "2025-08-21T10:27:26.6Z" }, - { url = "https://files.pythonhosted.org/packages/8e/46/80d53de70fee835531da3a1dae827a1e76e77a43ad22a8cd0f8142b61587/pandas-2.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:213a5adf93d020b74327cb2c1b842884dbdd37f895f42dcc2f09d451d949f811", size = 13439314, upload-time = "2025-08-21T10:27:29.213Z" }, - { url = "https://files.pythonhosted.org/packages/28/30/8114832daff7489f179971dbc1d854109b7f4365a546e3ea75b6516cea95/pandas-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c13b81a9347eb8c7548f53fd9a4f08d4dfe996836543f805c987bafa03317ae", size = 10983326, upload-time = "2025-08-21T10:27:31.901Z" }, - { url = "https://files.pythonhosted.org/packages/27/64/a2f7bf678af502e16b472527735d168b22b7824e45a4d7e96a4fbb634b59/pandas-2.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0c6ecbac99a354a051ef21c5307601093cb9e0f4b1855984a084bfec9302699e", size = 11531061, upload-time = "2025-08-21T10:27:34.647Z" }, - { url = "https://files.pythonhosted.org/packages/54/4c/c3d21b2b7769ef2f4c2b9299fcadd601efa6729f1357a8dbce8dd949ed70/pandas-2.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c6f048aa0fd080d6a06cc7e7537c09b53be6642d330ac6f54a600c3ace857ee9", size = 10668666, upload-time = "2025-08-21T10:27:37.203Z" }, - { url = "https://files.pythonhosted.org/packages/50/e2/f775ba76ecfb3424d7f5862620841cf0edb592e9abd2d2a5387d305fe7a8/pandas-2.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0064187b80a5be6f2f9c9d6bdde29372468751dfa89f4211a3c5871854cfbf7a", size = 11332835, upload-time = "2025-08-21T10:27:40.188Z" }, - { url = "https://files.pythonhosted.org/packages/8f/52/0634adaace9be2d8cac9ef78f05c47f3a675882e068438b9d7ec7ef0c13f/pandas-2.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ac8c320bded4718b298281339c1a50fb00a6ba78cb2a63521c39bec95b0209b", size = 12057211, upload-time = "2025-08-21T10:27:43.117Z" }, - { url = "https://files.pythonhosted.org/packages/0b/9d/2df913f14b2deb9c748975fdb2491da1a78773debb25abbc7cbc67c6b549/pandas-2.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:114c2fe4f4328cf98ce5716d1532f3ab79c5919f95a9cfee81d9140064a2e4d6", size = 12749277, upload-time = "2025-08-21T10:27:45.474Z" }, - { url = "https://files.pythonhosted.org/packages/87/af/da1a2417026bd14d98c236dba88e39837182459d29dcfcea510b2ac9e8a1/pandas-2.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:48fa91c4dfb3b2b9bfdb5c24cd3567575f4e13f9636810462ffed8925352be5a", size = 13415256, upload-time = "2025-08-21T10:27:49.885Z" }, - { url = "https://files.pythonhosted.org/packages/22/3c/f2af1ce8840ef648584a6156489636b5692c162771918aa95707c165ad2b/pandas-2.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:12d039facec710f7ba305786837d0225a3444af7bbd9c15c32ca2d40d157ed8b", size = 10982579, upload-time = "2025-08-21T10:28:08.435Z" }, - { url = "https://files.pythonhosted.org/packages/f3/98/8df69c4097a6719e357dc249bf437b8efbde808038268e584421696cbddf/pandas-2.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c624b615ce97864eb588779ed4046186f967374185c047070545253a52ab2d57", size = 12028163, upload-time = "2025-08-21T10:27:52.232Z" }, - { url = "https://files.pythonhosted.org/packages/0e/23/f95cbcbea319f349e10ff90db488b905c6883f03cbabd34f6b03cbc3c044/pandas-2.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0cee69d583b9b128823d9514171cabb6861e09409af805b54459bd0c821a35c2", size = 11391860, upload-time = "2025-08-21T10:27:54.673Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1b/6a984e98c4abee22058aa75bfb8eb90dce58cf8d7296f8bc56c14bc330b0/pandas-2.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2319656ed81124982900b4c37f0e0c58c015af9a7bbc62342ba5ad07ace82ba9", size = 11309830, upload-time = "2025-08-21T10:27:56.957Z" }, - { url = "https://files.pythonhosted.org/packages/15/d5/f0486090eb18dd8710bf60afeaf638ba6817047c0c8ae5c6a25598665609/pandas-2.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b37205ad6f00d52f16b6d09f406434ba928c1a1966e2771006a9033c736d30d2", size = 11883216, upload-time = "2025-08-21T10:27:59.302Z" }, - { url = "https://files.pythonhosted.org/packages/10/86/692050c119696da19e20245bbd650d8dfca6ceb577da027c3a73c62a047e/pandas-2.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:837248b4fc3a9b83b9c6214699a13f069dc13510a6a6d7f9ba33145d2841a012", size = 12699743, upload-time = "2025-08-21T10:28:02.447Z" }, - { url = "https://files.pythonhosted.org/packages/cd/d7/612123674d7b17cf345aad0a10289b2a384bff404e0463a83c4a3a59d205/pandas-2.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d2c3554bd31b731cd6490d94a28f3abb8dd770634a9e06eb6d2911b9827db370", size = 13186141, upload-time = "2025-08-21T10:28:05.377Z" }, - { url = "https://files.pythonhosted.org/packages/e0/c3/b37e090d0aceda9b4dd85c8dbd1bea65b1de9e7a4f690d6bd3a40bd16390/pandas-2.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:88080a0ff8a55eac9c84e3ff3c7665b3b5476c6fbc484775ca1910ce1c3e0b87", size = 11551511, upload-time = "2025-08-21T10:28:11.111Z" }, - { url = "https://files.pythonhosted.org/packages/b9/47/381fb1e7adcfcf4230fa6dc3a741acbac6c6fe072f19f4e7a46bddf3e5f6/pandas-2.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d4a558c7620340a0931828d8065688b3cc5b4c8eb674bcaf33d18ff4a6870b4a", size = 10797930, upload-time = "2025-08-21T10:28:13.436Z" }, - { url = "https://files.pythonhosted.org/packages/36/ca/d42467829080b92fc46d451288af8068f129fbcfb6578d573f45120de5cf/pandas-2.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45178cf09d1858a1509dc73ec261bf5b25a625a389b65be2e47b559905f0ab6a", size = 11738470, upload-time = "2025-08-21T10:28:16.065Z" }, - { url = "https://files.pythonhosted.org/packages/60/76/7d0f0a0deed7867c51163982d7b79c0a089096cd7ad50e1b87c2c82220e9/pandas-2.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77cefe00e1b210f9c76c697fedd8fdb8d3dd86563e9c8adc9fa72b90f5e9e4c2", size = 12366640, upload-time = "2025-08-21T10:28:18.557Z" }, - { url = "https://files.pythonhosted.org/packages/21/31/56784743e421cf51e34358fe7e5954345e5942168897bf8eb5707b71eedb/pandas-2.3.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:13bd629c653856f00c53dc495191baa59bcafbbf54860a46ecc50d3a88421a96", size = 13211567, upload-time = "2025-08-21T10:28:20.998Z" }, - { url = "https://files.pythonhosted.org/packages/7a/4e/50a399dc7d9dd4aa09a03b163751d428026cf0f16c419b4010f6aca26ebd/pandas-2.3.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:36d627906fd44b5fd63c943264e11e96e923f8de77d6016dc2f667b9ad193438", size = 13854073, upload-time = "2025-08-21T10:28:24.056Z" }, - { url = "https://files.pythonhosted.org/packages/29/72/8978a84861a5124e56ce1048376569545412501fcb9a83f035393d6d85bc/pandas-2.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:a9d7ec92d71a420185dec44909c32e9a362248c4ae2238234b76d5be37f208cc", size = 11346452, upload-time = "2025-08-21T10:28:26.691Z" }, + { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/4f/5f3422a2afec5ffc46308b79e53291365a93748b498ac2e58bead0197916/pandas-3.0.5.tar.gz", hash = "sha256:dca3734d6ab7c906e6730f0788b0a1dbb9f2467731f9711f77995c8e9d62d712", size = 4658219, upload-time = "2026-07-22T22:19:28.819Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/ef/f1fd7431d635bf20015489bf0bd69c17fff1018de773540f651455a3916b/pandas-3.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2946e77e4a53cd248cbde631a12f0e51c8324ce354c3eba4d20147c1ad6f4282", size = 10397178, upload-time = "2026-07-22T22:17:48.274Z" }, + { url = "https://files.pythonhosted.org/packages/31/b4/0eafac990a431561187694126de01f9b12559549b4d86360c0c4bd870fde/pandas-3.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71ecc8fb7ed1a7aa4392316b5309a6347e8e7f832f38fd897846b3a1457a9298", size = 9990736, upload-time = "2026-07-22T22:17:52.388Z" }, + { url = "https://files.pythonhosted.org/packages/de/21/359880af3ea9b7cb23bea5b51e8e70ef3866c03be09da9a2787e18e330a8/pandas-3.0.5-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b173f5951ff6b8b0ec7675e20dff3c97b7e7a57dfcce387c2d7c5afe87cb7899", size = 10814438, upload-time = "2026-07-22T22:17:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/d1/50/d6cc4d7e508bbccf5d6027314a8312bc7ac73d0ec7f195f53838daafab40/pandas-3.0.5-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2c0cf1dd9b55a22d105fc46c1b489af3bd42264fcba7c66297bf47a9a1d9c78a", size = 11323634, upload-time = "2026-07-22T22:17:56.858Z" }, + { url = "https://files.pythonhosted.org/packages/70/2b/d5f0a8c90dd0ae04e64ba53b871afb796ec026b615086d382ddc2ade729b/pandas-3.0.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0fac0010c75e4efb6b99e249c183a8993ce0dc95c240f9b120a5e67c727b7928", size = 11850860, upload-time = "2026-07-22T22:17:59.1Z" }, + { url = "https://files.pythonhosted.org/packages/5c/30/183aec2e19adf778a98d29b5729a0a68f4cc4ebf9b9c3b70d0297355bcb1/pandas-3.0.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:08d24fe11a17dc33bd6e937dc9c665f9cba08fbdc9f657f405713515febe300d", size = 12411100, upload-time = "2026-07-22T22:18:01.485Z" }, + { url = "https://files.pythonhosted.org/packages/fa/9a/31f4983f191af51ab2a8f2d0c7b33dff3a84da26533f982fff02c2f9e28b/pandas-3.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:b1261758dfb6cf12c3cff8300e21cefad30e7ec709abb4c24ac7318e6a52462a", size = 9968804, upload-time = "2026-07-22T22:18:03.903Z" }, + { url = "https://files.pythonhosted.org/packages/49/97/7886c89a39045c69ad82cbceaf3343810480c8ef49a216319ce8183860a6/pandas-3.0.5-cp311-cp311-win_arm64.whl", hash = "sha256:679f4e85b30ddb1515458ab1e788d3e260eae369b1f78da7a3aa4cac8ebf4a2a", size = 9205447, upload-time = "2026-07-22T22:18:06.134Z" }, + { url = "https://files.pythonhosted.org/packages/1c/54/1dc810ea558d1320b597aa140a514f2fdf1d2ea09c38cf556f13ea712ec9/pandas-3.0.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fa290c16964d4963fbfbc358928239cf3bd755b20e988ce944877def2f44471d", size = 10411717, upload-time = "2026-07-22T22:18:08.307Z" }, + { url = "https://files.pythonhosted.org/packages/68/56/fbe81c09195924d8b7b8d4461a20458fe80a6a5ed6b24f0314da684277e1/pandas-3.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c2e26bb46934b8a2ca0c3de1d3d606fc5f6746584791b2db264d58cf370e08dc", size = 9957095, upload-time = "2026-07-22T22:18:10.6Z" }, + { url = "https://files.pythonhosted.org/packages/e0/51/fac252f4a913ed5eabf3c11b880a9e8d5a6c10f0b2129d0462212d238b4d/pandas-3.0.5-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73fa87b08a7ef706f8aafda39ddaccf2a99047bea62d8c88a0361bcafb2237bc", size = 10485458, upload-time = "2026-07-22T22:18:12.834Z" }, + { url = "https://files.pythonhosted.org/packages/12/98/e976540c1addf70442be7842a18cf70884a964abbf69442504f4d2939989/pandas-3.0.5-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d373ce03ffd84010ed9839fa73672a9c8256990532e158440c0085db7d914b34", size = 10998091, upload-time = "2026-07-22T22:18:15.209Z" }, + { url = "https://files.pythonhosted.org/packages/a4/8c/1f29b5be8d3fc47dd7567eb167fabba2085879b31e0287ce7cba6d3d2ff4/pandas-3.0.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2a29c53d85ea98c5e792c59ef82ee9fbe6ca902c0d0adb6b23f45ef894cd7bf6", size = 11499501, upload-time = "2026-07-22T22:18:17.689Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e2/bd9c98ad2df7b38bde002adde4cdf353519da51881634323b126c55997f9/pandas-3.0.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a5ad3b02ed6bc7d7ae9b70804b2c6aa31827489d150f8e623ce82491b82085d7", size = 12060559, upload-time = "2026-07-22T22:18:20.147Z" }, + { url = "https://files.pythonhosted.org/packages/f3/9a/ffbd852d58bd74a617fe2f8ee6a58a96982271ce41cf981eab22190b4a4b/pandas-3.0.5-cp312-cp312-pyemscripten_2024_0_wasm32.whl", hash = "sha256:b2acb4650527eec6822c3dadb2b771277b65e7dae7a267d4bccf65fd1bb3fbce", size = 7197652, upload-time = "2026-07-22T22:18:22.502Z" }, + { url = "https://files.pythonhosted.org/packages/70/b5/d2d3e9ae73362ba4229651b0ee1455cf78073a1ce585f6ff693782ce263e/pandas-3.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:80a611068e8a3ac23f7398c6c14eb46dc974e5cc9997f653e2dcfd1da74edd41", size = 9831691, upload-time = "2026-07-22T22:18:24.534Z" }, + { url = "https://files.pythonhosted.org/packages/52/51/dea1e89d6a6796b9c43f85a09b484ee03edb8a4c4842e73e200a8c11301c/pandas-3.0.5-cp312-cp312-win_arm64.whl", hash = "sha256:25ff585b972a18ef1fe9ffa3ac6544d9950508aa76832e5147640b6022821e49", size = 9105796, upload-time = "2026-07-22T22:18:27.064Z" }, + { url = "https://files.pythonhosted.org/packages/bf/09/7b95c4a0025227d6f118c4039b423412ac6a982db02864166185d812fbc7/pandas-3.0.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c1c05a767fe8e5b4fe9e1c29806829c582052eaedb9120a3da83ba3f69e24a5b", size = 10385742, upload-time = "2026-07-22T22:18:29.346Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0c/dc78fd8c4da477b4b5e8ad37295af352190d21ef63a9ee1bc071753074cc/pandas-3.0.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b86765f268b56f7e665b93bce9d5df69dee7f99e595cf8fb839483ab315942a3", size = 9932067, upload-time = "2026-07-22T22:18:31.833Z" }, + { url = "https://files.pythonhosted.org/packages/3e/71/3592c055cf44df9808550f9368ceda80ff2b224d355ef73fe251dcda1802/pandas-3.0.5-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c597ecf5616b5c420372c1d4d4c00dbbfba7398bea857dcc984347e1ea48417b", size = 10466756, upload-time = "2026-07-22T22:18:34.195Z" }, + { url = "https://files.pythonhosted.org/packages/e3/70/4363150359f95b4cb4bcbb34ca23572bb5495749a621a8f3d5a1ddfd293c/pandas-3.0.5-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4b11c36e218331d0387cbe3a0a5f75162357a1d92d57b2b08a336ff94b19b2be", size = 10938525, upload-time = "2026-07-22T22:18:36.81Z" }, + { url = "https://files.pythonhosted.org/packages/f7/d0/317e7a0c67c0e69fa905a0161409397a7dc2d46ff611f6ca4803352c042b/pandas-3.0.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf52e1f61d229496da17dc7ab54acdee627357e7008fd4fecba3d0ba2937fa58", size = 11489303, upload-time = "2026-07-22T22:18:39.287Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8d/36dade89b49e4f9d5cbdbe863772581f98c0c6d78fc39ad4c557f6f2e17e/pandas-3.0.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:db172144bb56422bd157812f3b021eacc255451470b31e2c633c349490a1cfee", size = 11989004, upload-time = "2026-07-22T22:18:42.208Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ba/18c4ec8a746e177da05a9e7a7963781d8ea195780724f854601b6ebd6b78/pandas-3.0.5-cp313-cp313-win_amd64.whl", hash = "sha256:0d298e951f23016ce4699951d044ae6418dbc91bf68cefca0f77666fcbb4e5c6", size = 9826896, upload-time = "2026-07-22T22:18:44.539Z" }, + { url = "https://files.pythonhosted.org/packages/de/ec/28a57266b753799a87b8bc79e7887ac6fd981b8c6d2978a0b7e7b6bd708c/pandas-3.0.5-cp313-cp313-win_arm64.whl", hash = "sha256:66266d3442a5e8b3c90274c2b8b230bee42dd1c286bc822cc2f9f2c7e12b883e", size = 9094790, upload-time = "2026-07-22T22:18:47.468Z" }, + { url = "https://files.pythonhosted.org/packages/51/2f/cf6aae281264f4463f0875bcbb15fd2bb6d291cc535187dad1732475e4a9/pandas-3.0.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2f264fc46911cc8131a7322a16199bbf8e353d27c10bb211f5bd0c814324dc36", size = 10390034, upload-time = "2026-07-22T22:18:49.818Z" }, + { url = "https://files.pythonhosted.org/packages/06/ec/5189518c7a7659c4bdcc6b1eb32c46c6f3c86b0661ffd84143d1112c7732/pandas-3.0.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:53730687fcd161883b24e10411c06d6a4c0f2275d2faf3bb2bc25deb4ba8007c", size = 9980065, upload-time = "2026-07-22T22:18:52.249Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f1/598503ce8d7e3c35601e0747ba288c7864baae66380725bc12f13f884dfe/pandas-3.0.5-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:960d3ebcf249f75206899fcd2c6de53f736b7265759ced0d3e559df0b8b709b0", size = 10545532, upload-time = "2026-07-22T22:18:54.813Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/ceae2adf7034e07e9910299fe412e1819c4f0dd520700a888bcb03625448/pandas-3.0.5-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e94c2c5ca43bd3ca32bf64d32308887b65e5f9bfd8023ea52755107a999f93b", size = 10963120, upload-time = "2026-07-22T22:18:57.42Z" }, + { url = "https://files.pythonhosted.org/packages/66/25/86e0f4451874eb79e688deeebe3c451fec4557f8952005818d800ee8ac7e/pandas-3.0.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e819dd5f62966b481a8cb649d3299ebd886a1ea91ed5a99bf7ce77c98d18ab94", size = 11563178, upload-time = "2026-07-22T22:18:59.729Z" }, + { url = "https://files.pythonhosted.org/packages/f3/45/8643daa3b4147e433adfcccefdd0380d3aad79d86b15d8999730fe1944d5/pandas-3.0.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3c5ed2e7c06e91d340dfd091d7934f9bc82e4a36b95f647f090b9d1c9ac649da", size = 12028708, upload-time = "2026-07-22T22:19:02.164Z" }, + { url = "https://files.pythonhosted.org/packages/96/58/ad979ae617615576e8aafd569c9d4b62f1191d896e38f51d66ba06f3b89a/pandas-3.0.5-cp314-cp314-win_amd64.whl", hash = "sha256:cd8f7c6dc98527058ee6264219343f5392240a6f1bfa654fc5d79023020d0c92", size = 9951806, upload-time = "2026-07-22T22:19:04.596Z" }, + { url = "https://files.pythonhosted.org/packages/69/32/7ac03886b304049a9d2625ee88f59af760d8a93bd30ed9239bce7b9869a8/pandas-3.0.5-cp314-cp314-win_arm64.whl", hash = "sha256:5183427f5a8156d480f30333777bc978be93650a49a7c01db26adffe95b31e85", size = 9238297, upload-time = "2026-07-22T22:19:06.836Z" }, + { url = "https://files.pythonhosted.org/packages/be/ed/1d1f2ee5547d5167face2376d11c8b2a4c7bfff5a416ee7a9046891fab1e/pandas-3.0.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:303da736987d481074ca720ada325f8bd80c64ebc2d45ed79b29df3aaa4a26ca", size = 10849690, upload-time = "2026-07-22T22:19:09.391Z" }, + { url = "https://files.pythonhosted.org/packages/57/55/17e17152e98fbb0c4b1e562bc65387a2f20a80db0f4a86bf8d3a0e4248d4/pandas-3.0.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3b2801bbb049d0136f6c213eae02b5fca969384fc2064dd728d8620552aa49da", size = 10509945, upload-time = "2026-07-22T22:19:11.773Z" }, + { url = "https://files.pythonhosted.org/packages/88/90/817d44dbf83facf9556f33576d9af0a241981e7bb5c00606c0bcb5df8dda/pandas-3.0.5-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cce3a9d11d2b1f82c69a27ec1f4948a170e2c403c4bbfa8cca62e3fdebe2ef3a", size = 10392197, upload-time = "2026-07-22T22:19:14.024Z" }, + { url = "https://files.pythonhosted.org/packages/f1/da/889f00c0a6f5aa1545add70abbf01502dff87ab577adb855bd631c54d2f2/pandas-3.0.5-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef01af4d8dc6cd2c8d6c7736f149574ef93fe043811eeb5e445f2647154b5040", size = 10862726, upload-time = "2026-07-22T22:19:16.351Z" }, + { url = "https://files.pythonhosted.org/packages/bc/98/f1e934fb3c98fce859c6147c6785816c7b5b9ab7821115c5d8c4de9842b9/pandas-3.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e2759e890db96dfcffdbd9b86c3c2cb6afaf58def482820317e06163ec1066cd", size = 11414864, upload-time = "2026-07-22T22:19:18.981Z" }, + { url = "https://files.pythonhosted.org/packages/fe/be/d448af7d657d82e1888dd8551f79c6d6fb161080b5b9752d84d910ec2319/pandas-3.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b58b1b39d46a5862e3fb18f50d1a201398619d16a0f9f73f57eea5583cf0e63c", size = 11925105, upload-time = "2026-07-22T22:19:21.515Z" }, + { url = "https://files.pythonhosted.org/packages/29/c1/ccb4238212c8c4f496c584f3044d94e0c030ed8e1d68999db46c91c2242f/pandas-3.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:1c10461f6eeb35d8f05b6184c65c8b9991663b66c46b1d559b682cb34ae7c6ea", size = 10387612, upload-time = "2026-07-22T22:19:24.257Z" }, + { url = "https://files.pythonhosted.org/packages/d2/cf/6a51b2c38980e04c279fd2fa908a1b0982064e860444acfca4ec2e2c8359/pandas-3.0.5-cp314-cp314t-win_arm64.whl", hash = "sha256:3c5015fd1730fbf883647e88068176c839c102cea883ba1769a6f4593bfc1f8c", size = 9509776, upload-time = "2026-07-22T22:19:26.694Z" }, ] [[package]] @@ -2418,7 +1679,7 @@ name = "pexpect" version = "4.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ptyprocess" }, + { name = "ptyprocess", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } wheels = [ @@ -2458,11 +1719,10 @@ wheels = [ [[package]] name = "policyengine" -version = "5.3.0" +version = "5.3.1" source = { editable = "." } dependencies = [ - { name = "h5py", version = "3.14.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "h5py", version = "3.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "h5py" }, { name = "jsonschema" }, { name = "microdf-python" }, { name = "packaging" }, @@ -2479,8 +1739,7 @@ dev = [ { name = "furo" }, { name = "itables" }, { name = "jupyter-book" }, - { name = "mypy", version = "1.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "mypy", version = "1.20.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "mypy" }, { name = "plotly" }, { name = "policyengine-core" }, { name = "policyengine-uk" }, @@ -2494,9 +1753,7 @@ dev = [ { name = "yaml-changelog" }, ] graph = [ - { name = "networkx", version = "3.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "networkx", version = "3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "networkx" }, ] models = [ { name = "policyengine-core" }, @@ -2533,16 +1790,16 @@ requires-dist = [ { name = "pandas", specifier = ">=2.0.0" }, { name = "plotly", marker = "extra == 'dev'", specifier = ">=5.0.0" }, { name = "plotly", marker = "extra == 'plotting'", specifier = ">=5.0.0" }, - { name = "policyengine-core", marker = "extra == 'dev'", specifier = "==3.30.1" }, - { name = "policyengine-core", marker = "extra == 'models'", specifier = "==3.30.1" }, - { name = "policyengine-core", marker = "extra == 'uk'", specifier = "==3.30.1" }, - { name = "policyengine-core", marker = "extra == 'us'", specifier = "==3.30.1" }, + { name = "policyengine-core", marker = "extra == 'dev'", specifier = "==3.32.5" }, + { name = "policyengine-core", marker = "extra == 'models'", specifier = "==3.32.5" }, + { name = "policyengine-core", marker = "extra == 'uk'", specifier = "==3.32.5" }, + { name = "policyengine-core", marker = "extra == 'us'", specifier = "==3.32.5" }, { name = "policyengine-uk", marker = "extra == 'dev'", specifier = "==2.90.2" }, { name = "policyengine-uk", marker = "extra == 'models'", specifier = "==2.90.2" }, { name = "policyengine-uk", marker = "extra == 'uk'", specifier = "==2.90.2" }, - { name = "policyengine-us", marker = "extra == 'dev'", specifier = "==1.764.6" }, - { name = "policyengine-us", marker = "extra == 'models'", specifier = "==1.764.6" }, - { name = "policyengine-us", marker = "extra == 'us'", specifier = "==1.764.6" }, + { name = "policyengine-us", marker = "extra == 'dev'", specifier = "==2.2.1" }, + { name = "policyengine-us", marker = "extra == 'models'", specifier = "==2.2.1" }, + { name = "policyengine-us", marker = "extra == 'us'", specifier = "==2.2.1" }, { name = "psutil", specifier = ">=5.9.0" }, { name = "pydantic", specifier = ">=2.0.0" }, { name = "pytest", marker = "extra == 'dev'" }, @@ -2550,9 +1807,9 @@ requires-dist = [ { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=5.0.0" }, { name = "requests", specifier = ">=2.31.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.9.0" }, - { name = "spm-calculator", marker = "extra == 'dev'", specifier = "==0.3.1" }, - { name = "spm-calculator", marker = "extra == 'models'", specifier = "==0.3.1" }, - { name = "spm-calculator", marker = "extra == 'us'", specifier = "==0.3.1" }, + { name = "spm-calculator", marker = "extra == 'dev'", specifier = "==1.0.0" }, + { name = "spm-calculator", marker = "extra == 'models'", specifier = "==1.0.0" }, + { name = "spm-calculator", marker = "extra == 'us'", specifier = "==1.0.0" }, { name = "towncrier", marker = "extra == 'dev'", specifier = ">=24.8.0" }, { name = "yaml-changelog", marker = "extra == 'dev'", specifier = ">=0.1.7" }, ] @@ -2560,20 +1817,17 @@ provides-extras = ["plotting", "graph", "models", "uk", "us", "dev"] [[package]] name = "policyengine-core" -version = "3.30.1" +version = "3.32.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dpath" }, - { name = "h5py", version = "3.14.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "h5py", version = "3.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "h5py" }, { name = "huggingface-hub" }, - { name = "ipython", version = "8.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "ipython" }, { name = "microdf-python" }, - { name = "numexpr", version = "2.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "numexpr", version = "2.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "numexpr" }, + { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, { name = "pandas" }, { name = "plotly" }, { name = "psutil" }, @@ -2584,9 +1838,9 @@ dependencies = [ { name = "standard-imghdr" }, { name = "wheel" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/9d/f71da3e348ddc52e058c76b72bbc4b2f20abcf0e7389ecd5aca5d550fd14/policyengine_core-3.30.1.tar.gz", hash = "sha256:a16f29fe51ec01a7be2171386f8bd26b9ba55ffc338adb70adcadafe33d9c55f", size = 500419, upload-time = "2026-07-19T15:50:41.288Z" } +sdist = { url = "https://files.pythonhosted.org/packages/12/54/cfd2138584de9cddaa4459bba7fdc381101ca8b8ffd38356a1b256850d5f/policyengine_core-3.32.5.tar.gz", hash = "sha256:f051c269b538fe77ee7868f7cfbc0138551960b1b677a0ad6fdc1a93f1660460", size = 420112, upload-time = "2026-09-10T04:16:48.889Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/7e/07624d8add8889af1b4af796d36c38b2e2af494b1d78a064fab29e9bea00/policyengine_core-3.30.1-py3-none-any.whl", hash = "sha256:2dbcf5f590a0199a7b7c77fcbbda2ff6bc289f6c6169ca0af3beace35d8b3e63", size = 244846, upload-time = "2026-07-19T15:50:39.651Z" }, + { url = "https://files.pythonhosted.org/packages/c6/d9/72c3707465e9fe77e6f7a55ea64b49a287cbf875b78f6e862d945df26975/policyengine_core-3.32.5-py3-none-any.whl", hash = "sha256:9d8c162d5fe5c784ae16c885da3ffbfc39a536add0e111133144d8edd555935f", size = 245925, upload-time = "2026-09-10T04:16:47.153Z" }, ] [[package]] @@ -2597,9 +1851,7 @@ dependencies = [ { name = "microdf-python" }, { name = "policyengine-core" }, { name = "pydantic" }, - { name = "tables", version = "3.9.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "tables", version = "3.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "tables", version = "3.11.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "tables" }, ] sdist = { url = "https://files.pythonhosted.org/packages/40/e1/6652fd4ff8806d81b7c846ce2baeef4b9a8457ed380bb0853419d3cf36d9/policyengine_uk-2.90.2.tar.gz", hash = "sha256:c9baeaded1bb6d6f5ca5fcd962b80a198509513737c194aa867b0ef6d13d6a09", size = 1237478, upload-time = "2026-08-13T05:30:05.961Z" } wheels = [ @@ -2608,21 +1860,19 @@ wheels = [ [[package]] name = "policyengine-us" -version = "1.764.6" +version = "2.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "microdf-python" }, { name = "pandas" }, { name = "policyengine-core" }, { name = "spm-calculator" }, - { name = "tables", version = "3.9.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "tables", version = "3.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "tables", version = "3.11.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "tables" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6e/cc/9e65a9586069beab1f02102a23f7a0e6332f1a9c2d590cb9f0e68f9ac987/policyengine_us-1.764.6.tar.gz", hash = "sha256:9817d48abc5b7d690b8abe33047005e5cf40b71654a85a2534bb5e3bcf921f79", size = 11009555, upload-time = "2026-07-06T13:13:07.254Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/09/763b21371a62635e15e62b53fb696e299f01460907c27313d40f63031eb2/policyengine_us-2.2.1.tar.gz", hash = "sha256:d5300c91f3eae2e47a61f485fdfde91a892b80e95db3e46ff2c4bbd417d76501", size = 11946510, upload-time = "2026-09-15T04:45:50.33Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/5a/0566666f30ad06e416f0cf25b24bdee73acd71897fb2de3c0ac43b59e3e3/policyengine_us-1.764.6-py3-none-any.whl", hash = "sha256:2b2c5c02c26ab212270948db1f30129728b625fc6109fc9ca0c604b714546909", size = 13117921, upload-time = "2026-07-06T13:13:03.476Z" }, + { url = "https://files.pythonhosted.org/packages/5e/09/86ac3968d2a18e28fce3ba1186fc21728e9f299b96a806d6f204f1e0aaed/policyengine_us-2.2.1-py3-none-any.whl", hash = "sha256:0993a6c73fcdfbe171a796aca9d302741bd8e00ab83388ad81c15a08740318c6", size = 14885537, upload-time = "2026-09-15T04:45:46.649Z" }, ] [[package]] @@ -2684,7 +1934,6 @@ name = "pybtex" version = "0.25.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, { name = "latexcodec" }, { name = "pyyaml" }, ] @@ -2739,19 +1988,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" }, - { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" }, - { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" }, - { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" }, - { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" }, - { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" }, - { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" }, - { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" }, - { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" }, - { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" }, - { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" }, - { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" }, - { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" }, { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, @@ -2822,19 +2058,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/54/db/160dffb57ed9a3705c4cbcbff0ac03bdae45f1ca7d58ab74645550df3fbd/pydantic_core-2.41.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf", size = 2107999, upload-time = "2025-11-04T13:42:03.885Z" }, - { url = "https://files.pythonhosted.org/packages/a3/7d/88e7de946f60d9263cc84819f32513520b85c0f8322f9b8f6e4afc938383/pydantic_core-2.41.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5", size = 1929745, upload-time = "2025-11-04T13:42:06.075Z" }, - { url = "https://files.pythonhosted.org/packages/d5/c2/aef51e5b283780e85e99ff19db0f05842d2d4a8a8cd15e63b0280029b08f/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d", size = 1920220, upload-time = "2025-11-04T13:42:08.457Z" }, - { url = "https://files.pythonhosted.org/packages/c7/97/492ab10f9ac8695cd76b2fdb24e9e61f394051df71594e9bcc891c9f586e/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60", size = 2067296, upload-time = "2025-11-04T13:42:10.817Z" }, - { url = "https://files.pythonhosted.org/packages/ec/23/984149650e5269c59a2a4c41d234a9570adc68ab29981825cfaf4cfad8f4/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82", size = 2231548, upload-time = "2025-11-04T13:42:13.843Z" }, - { url = "https://files.pythonhosted.org/packages/71/0c/85bcbb885b9732c28bec67a222dbed5ed2d77baee1f8bba2002e8cd00c5c/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5", size = 2362571, upload-time = "2025-11-04T13:42:16.208Z" }, - { url = "https://files.pythonhosted.org/packages/c0/4a/412d2048be12c334003e9b823a3fa3d038e46cc2d64dd8aab50b31b65499/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3", size = 2068175, upload-time = "2025-11-04T13:42:18.911Z" }, - { url = "https://files.pythonhosted.org/packages/73/f4/c58b6a776b502d0a5540ad02e232514285513572060f0d78f7832ca3c98b/pydantic_core-2.41.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425", size = 2177203, upload-time = "2025-11-04T13:42:22.578Z" }, - { url = "https://files.pythonhosted.org/packages/ed/ae/f06ea4c7e7a9eead3d165e7623cd2ea0cb788e277e4f935af63fc98fa4e6/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504", size = 2148191, upload-time = "2025-11-04T13:42:24.89Z" }, - { url = "https://files.pythonhosted.org/packages/c1/57/25a11dcdc656bf5f8b05902c3c2934ac3ea296257cc4a3f79a6319e61856/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5", size = 2343907, upload-time = "2025-11-04T13:42:27.683Z" }, - { url = "https://files.pythonhosted.org/packages/96/82/e33d5f4933d7a03327c0c43c65d575e5919d4974ffc026bc917a5f7b9f61/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3", size = 2322174, upload-time = "2025-11-04T13:42:30.776Z" }, - { url = "https://files.pythonhosted.org/packages/81/45/4091be67ce9f469e81656f880f3506f6a5624121ec5eb3eab37d7581897d/pydantic_core-2.41.5-cp39-cp39-win32.whl", hash = "sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460", size = 1990353, upload-time = "2025-11-04T13:42:33.111Z" }, - { url = "https://files.pythonhosted.org/packages/44/8a/a98aede18db6e9cd5d66bcacd8a409fcf8134204cdede2e7de35c5a2c5ef/pydantic_core-2.41.5-cp39-cp39-win_amd64.whl", hash = "sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b", size = 2015698, upload-time = "2025-11-04T13:42:35.484Z" }, { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, @@ -2843,14 +2066,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, - { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" }, - { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" }, - { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" }, - { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" }, - { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" }, - { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" }, - { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" }, - { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" }, { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, @@ -2918,12 +2133,10 @@ version = "8.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, { name = "pygments" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } wheels = [ @@ -2935,9 +2148,7 @@ name = "pytest-asyncio" version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, { name = "pytest" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea", size = 46652, upload-time = "2025-07-16T04:29:26.393Z" } wheels = [ @@ -2949,8 +2160,7 @@ name = "pytest-cov" version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "coverage", version = "7.10.7", source = { registry = "https://pypi.org/simple" }, extra = ["toml"], marker = "python_full_version < '3.10'" }, - { name = "coverage", version = "7.13.5", source = { registry = "https://pypi.org/simple" }, extra = ["toml"], marker = "python_full_version >= '3.10'" }, + { name = "coverage", extra = ["toml"] }, { name = "pluggy" }, { name = "pytest" }, ] @@ -2994,13 +2204,10 @@ name = "pyvis" version = "0.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ipython", version = "8.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "ipython" }, { name = "jinja2" }, { name = "jsonpickle" }, - { name = "networkx", version = "3.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "networkx", version = "3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "networkx" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/ab/4b/e37e4e5d5ee1179694917b445768bdbfb084f5a59ecd38089d3413d4c70f/pyvis-0.3.2-py3-none-any.whl", hash = "sha256:5720c4ca8161dc5d9ab352015723abb7a8bb8fb443edeb07f7a322db34a97555", size = 756038, upload-time = "2023-02-24T20:29:46.758Z" }, @@ -3011,9 +2218,6 @@ name = "pywin32" version = "311" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" }, - { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" }, - { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" }, { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" }, { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" }, { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" }, @@ -3026,9 +2230,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, - { url = "https://files.pythonhosted.org/packages/59/42/b86689aac0cdaee7ae1c58d464b0ff04ca909c19bb6502d4973cdd9f9544/pywin32-311-cp39-cp39-win32.whl", hash = "sha256:aba8f82d551a942cb20d4a83413ccbac30790b50efb89a75e4f586ac0bb8056b", size = 8760837, upload-time = "2025-07-14T20:12:59.59Z" }, - { url = "https://files.pythonhosted.org/packages/9f/8a/1403d0353f8c5a2f0829d2b1c4becbf9da2f0a4d040886404fc4a5431e4d/pywin32-311-cp39-cp39-win_amd64.whl", hash = "sha256:e0c4cfb0621281fe40387df582097fd796e80430597cb9944f0ae70447bacd91", size = 9590187, upload-time = "2025-07-14T20:13:01.419Z" }, - { url = "https://files.pythonhosted.org/packages/60/22/e0e8d802f124772cec9c75430b01a212f86f9de7546bda715e54140d5aeb/pywin32-311-cp39-cp39-win_arm64.whl", hash = "sha256:62ea666235135fee79bb154e695f3ff67370afefd71bd7fea7512fc70ef31e3d", size = 8778162, upload-time = "2025-07-14T20:13:03.544Z" }, ] [[package]] @@ -3037,15 +2238,6 @@ version = "6.0.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, - { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, - { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, - { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, - { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, - { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, - { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, - { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, @@ -3073,15 +2265,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, - { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777, upload-time = "2024-08-06T20:33:25.896Z" }, - { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318, upload-time = "2024-08-06T20:33:27.212Z" }, - { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891, upload-time = "2024-08-06T20:33:28.974Z" }, - { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614, upload-time = "2024-08-06T20:33:34.157Z" }, - { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360, upload-time = "2024-08-06T20:33:35.84Z" }, - { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006, upload-time = "2024-08-06T20:33:37.501Z" }, - { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577, upload-time = "2024-08-06T20:33:39.389Z" }, - { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593, upload-time = "2024-08-06T20:33:46.63Z" }, - { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312, upload-time = "2024-08-06T20:33:49.073Z" }, ] [[package]] @@ -3093,16 +2276,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/f8/66/159f38d184f08b5f971b467f87b1ab142ab1320d5200825c824b32b84b66/pyzmq-27.0.2.tar.gz", hash = "sha256:b398dd713b18de89730447347e96a0240225e154db56e35b6bb8447ffdb07798", size = 281440, upload-time = "2025-08-21T04:23:26.334Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/4d/2081cd7e41e340004d2051821efe1d0d67d31bdb5ac33bffc7e628d5f1bd/pyzmq-27.0.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:8b32c4636ced87dce0ac3d671e578b3400215efab372f1b4be242e8cf0b11384", size = 1329839, upload-time = "2025-08-21T04:20:55.8Z" }, - { url = "https://files.pythonhosted.org/packages/ad/f1/1300b7e932671e31accb3512c19b43e6a3e8d08c54ab8b920308e53427ce/pyzmq-27.0.2-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f9528a4b3e24189cb333a9850fddbbafaa81df187297cfbddee50447cdb042cf", size = 906367, upload-time = "2025-08-21T04:20:58.476Z" }, - { url = "https://files.pythonhosted.org/packages/e6/80/61662db85eb3255a58c1bb59f6d4fc0d31c9c75b9a14983deafab12b2329/pyzmq-27.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b02ba0c0b2b9ebe74688002e6c56c903429924a25630804b9ede1f178aa5a3f", size = 666545, upload-time = "2025-08-21T04:20:59.775Z" }, - { url = "https://files.pythonhosted.org/packages/5c/6e/49fb9c75b039978cbb1f3657811d8056b0ebe6ecafd78a4457fc6de19799/pyzmq-27.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4dc5c9a6167617251dea0d024d67559795761aabb4b7ea015518be898be076", size = 854219, upload-time = "2025-08-21T04:21:01.807Z" }, - { url = "https://files.pythonhosted.org/packages/b0/3c/9951b302d221e471b7c659e70f9cb64db5f68fa3b7da45809ec4e6c6ef17/pyzmq-27.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f1151b33aaf3b4fa9da26f4d696e38eebab67d1b43c446184d733c700b3ff8ce", size = 1655103, upload-time = "2025-08-21T04:21:03.239Z" }, - { url = "https://files.pythonhosted.org/packages/88/ca/d7adea6100fdf7f87f3856db02d2a0a45ce2764b9f60ba08c48c655b762f/pyzmq-27.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4ecfc7999ac44c9ef92b5ae8f0b44fb935297977df54d8756b195a3cd12f38f0", size = 2033712, upload-time = "2025-08-21T04:21:05.121Z" }, - { url = "https://files.pythonhosted.org/packages/e9/63/b34e601b36ba4864d02ac1460443fc39bf533dedbdeead2a4e0df7dfc8ee/pyzmq-27.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:31c26a5d0b00befcaeeb600d8b15ad09f5604b6f44e2057ec5e521a9e18dcd9a", size = 1891847, upload-time = "2025-08-21T04:21:06.586Z" }, - { url = "https://files.pythonhosted.org/packages/cf/a2/9479e6af779da44f788d5fcda5f77dff1af988351ef91682b92524eab2db/pyzmq-27.0.2-cp310-cp310-win32.whl", hash = "sha256:25a100d2de2ac0c644ecf4ce0b509a720d12e559c77aff7e7e73aa684f0375bc", size = 567136, upload-time = "2025-08-21T04:21:07.885Z" }, - { url = "https://files.pythonhosted.org/packages/58/46/e1c2be469781fc56ba092fecb1bb336cedde0fd87d9e1a547aaeb5d1a968/pyzmq-27.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a1acf091f53bb406e9e5e7383e467d1dd1b94488b8415b890917d30111a1fef3", size = 631969, upload-time = "2025-08-21T04:21:09.5Z" }, - { url = "https://files.pythonhosted.org/packages/d5/8d/d20a62f1f77e3f04633a80bb83df085e4314f0e9404619cc458d0005d6ab/pyzmq-27.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:b38e01f11e9e95f6668dc8a62dccf9483f454fed78a77447507a0e8dcbd19a63", size = 559459, upload-time = "2025-08-21T04:21:11.208Z" }, { url = "https://files.pythonhosted.org/packages/42/73/034429ab0f4316bf433eb6c20c3f49d1dc13b2ed4e4d951b283d300a0f35/pyzmq-27.0.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:063845960df76599ad4fad69fa4d884b3ba38304272104fdcd7e3af33faeeb1d", size = 1333169, upload-time = "2025-08-21T04:21:12.483Z" }, { url = "https://files.pythonhosted.org/packages/35/02/c42b3b526eb03a570c889eea85a5602797f800a50ba8b09ddbf7db568b78/pyzmq-27.0.2-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:845a35fb21b88786aeb38af8b271d41ab0967985410f35411a27eebdc578a076", size = 909176, upload-time = "2025-08-21T04:21:13.835Z" }, { url = "https://files.pythonhosted.org/packages/1b/35/a1c0b988fabbdf2dc5fe94b7c2bcfd61e3533e5109297b8e0daf1d7a8d2d/pyzmq-27.0.2-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:515d20b5c3c86db95503faa989853a8ab692aab1e5336db011cd6d35626c4cb1", size = 668972, upload-time = "2025-08-21T04:21:15.315Z" }, @@ -3145,31 +2318,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4b/ed/42de80b7ab4e8fcf13376f81206cf8041740672ac1fd2e1c598d63f595bf/pyzmq-27.0.2-cp314-cp314t-win32.whl", hash = "sha256:0f6e9b00d81b58f859fffc112365d50413954e02aefe36c5b4c8fb4af79f8cc3", size = 587526, upload-time = "2025-08-21T04:22:14.18Z" }, { url = "https://files.pythonhosted.org/packages/ed/c8/8f3c72d6f0bfbf090aa5e283576073ca5c59839b85a5cc8c66ddb9b59801/pyzmq-27.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:2e73cf3b127a437fef4100eb3ac2ebe6b49e655bb721329f667f59eca0a26221", size = 661368, upload-time = "2025-08-21T04:22:15.677Z" }, { url = "https://files.pythonhosted.org/packages/69/a4/7ee652ea1c77d872f5d99ed937fa8bbd1f6f4b7a39a6d3a0076c286e0c3e/pyzmq-27.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:4108785f2e5ac865d06f678a07a1901e3465611356df21a545eeea8b45f56265", size = 574901, upload-time = "2025-08-21T04:22:17.423Z" }, - { url = "https://files.pythonhosted.org/packages/c8/57/a4aff0dbb29001cebf94848a980ac36bea8e1cb65ac0f72ff03e484df208/pyzmq-27.0.2-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:aa9c1c208c263b84386ac25bed6af5672397dc3c232638114fc09bca5c7addf9", size = 1330291, upload-time = "2025-08-21T04:22:34.433Z" }, - { url = "https://files.pythonhosted.org/packages/e1/a6/8519ed48b5bdd72e1a3946a76987afb6659a355180cd15e51cea6afe1983/pyzmq-27.0.2-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:795c4884cfe7ea59f2b67d82b417e899afab889d332bfda13b02f8e0c155b2e4", size = 906596, upload-time = "2025-08-21T04:22:36.168Z" }, - { url = "https://files.pythonhosted.org/packages/3c/19/cb6f463d6454079a566160d10cf175c3c59274b1378104d2b01a5a1ca560/pyzmq-27.0.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47eb65bb25478358ba3113dd9a08344f616f417ad3ffcbb190cd874fae72b1b1", size = 863605, upload-time = "2025-08-21T04:22:37.825Z" }, - { url = "https://files.pythonhosted.org/packages/9d/37/afe25d5f9b91b7422067bb7cd3f75691d78baa0c2c2f76bd0643389ac600/pyzmq-27.0.2-cp39-cp39-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6fc24f00293f10aff04d55ca37029b280474c91f4de2cad5e911e5e10d733b7", size = 666777, upload-time = "2025-08-21T04:22:39.551Z" }, - { url = "https://files.pythonhosted.org/packages/15/4a/97b0b59a7eacfd9e61c40abaa9c7e2587bbb39671816a9fb0c5dc32d87bc/pyzmq-27.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58d4cc9b6b768478adfc40a5cbee545303db8dbc81ba688474e0f499cc581028", size = 1655404, upload-time = "2025-08-21T04:22:41.082Z" }, - { url = "https://files.pythonhosted.org/packages/27/db/bcf03bea232839ea749562f76bda3807b16a25c68fdd153339a56699bb1d/pyzmq-27.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea2f26c5972796e02b222968a21a378d09eb4ff590eb3c5fafa8913f8c2bdf5", size = 2034051, upload-time = "2025-08-21T04:22:43.216Z" }, - { url = "https://files.pythonhosted.org/packages/57/3f/9cc291101ee766ab41bae25654af84c11d259f27d73b1ed985256dae8a16/pyzmq-27.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a0621ec020c49fc1b6e31304f1a820900d54e7d9afa03ea1634264bf9387519e", size = 1892080, upload-time = "2025-08-21T04:22:44.81Z" }, - { url = "https://files.pythonhosted.org/packages/16/52/51d4c56c553e7368abc2d010a783092c68a1888045f899eabcfd3bc345f3/pyzmq-27.0.2-cp39-cp39-win32.whl", hash = "sha256:1326500792a9cb0992db06bbaf5d0098459133868932b81a6e90d45c39eca99d", size = 567463, upload-time = "2025-08-21T04:22:46.853Z" }, - { url = "https://files.pythonhosted.org/packages/93/20/3b0ac853b5bbe1fd05e1898ea60335f9d402312328fa17514a876418ab11/pyzmq-27.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:5ee9560cb1e3094ef01fc071b361121a57ebb8d4232912b6607a6d7d2d0a97b4", size = 632200, upload-time = "2025-08-21T04:22:48.34Z" }, - { url = "https://files.pythonhosted.org/packages/b0/14/78b192a7864a38c71e30c3fd668eff949b44db13c1906035e6095fecd00d/pyzmq-27.0.2-cp39-cp39-win_arm64.whl", hash = "sha256:85e3c6fb0d25ea046ebcfdc2bcb9683d663dc0280645c79a616ff5077962a15b", size = 559638, upload-time = "2025-08-21T04:22:50.002Z" }, - { url = "https://files.pythonhosted.org/packages/19/d7/e388e80107b7c438c9698ce59c2a3b950021cd4ab3fe641485e4ed6b0960/pyzmq-27.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d67a0960803a37b60f51b460c58444bc7033a804c662f5735172e21e74ee4902", size = 836008, upload-time = "2025-08-21T04:22:51.842Z" }, - { url = "https://files.pythonhosted.org/packages/65/ef/58d3eb85f1b67a16e22adb07d084f975a7b9641463d18e27230550bb436a/pyzmq-27.0.2-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:dd4d3e6a567ffd0d232cfc667c49d0852d0ee7481458a2a1593b9b1bc5acba88", size = 799932, upload-time = "2025-08-21T04:22:53.529Z" }, - { url = "https://files.pythonhosted.org/packages/3c/63/66b9f6db19ee8c86105ffd4475a4f5d93cdd62b1edcb1e894d971df0728c/pyzmq-27.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e558be423631704803bc6a642e2caa96083df759e25fe6eb01f2d28725f80bd", size = 567458, upload-time = "2025-08-21T04:22:55.289Z" }, - { url = "https://files.pythonhosted.org/packages/10/af/d92207fe8b6e3d9f588d0591219a86dd7b4ed27bb3e825c1d9cf48467fc0/pyzmq-27.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c4c20ba8389f495c7b4f6b896bb1ca1e109a157d4f189267a902079699aaf787", size = 747087, upload-time = "2025-08-21T04:22:56.994Z" }, - { url = "https://files.pythonhosted.org/packages/82/e9/d9f8b4b191c6733e31de28974d608a2475a6598136ac901a8c5b67c11432/pyzmq-27.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c5be232f7219414ff672ff7ab8c5a7e8632177735186d8a42b57b491fafdd64e", size = 544641, upload-time = "2025-08-21T04:22:58.87Z" }, { url = "https://files.pythonhosted.org/packages/c7/60/027d0032a1e3b1aabcef0e309b9ff8a4099bdd5a60ab38b36a676ff2bd7b/pyzmq-27.0.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e297784aea724294fe95e442e39a4376c2f08aa4fae4161c669f047051e31b02", size = 836007, upload-time = "2025-08-21T04:23:00.447Z" }, { url = "https://files.pythonhosted.org/packages/25/20/2ed1e6168aaea323df9bb2c451309291f53ba3af372ffc16edd4ce15b9e5/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e3659a79ded9745bc9c2aef5b444ac8805606e7bc50d2d2eb16dc3ab5483d91f", size = 799932, upload-time = "2025-08-21T04:23:02.052Z" }, { url = "https://files.pythonhosted.org/packages/fd/25/5c147307de546b502c9373688ce5b25dc22288d23a1ebebe5d587bf77610/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3dba49ff037d02373a9306b58d6c1e0be031438f822044e8767afccfdac4c6b", size = 567459, upload-time = "2025-08-21T04:23:03.593Z" }, { url = "https://files.pythonhosted.org/packages/71/06/0dc56ffc615c8095cd089c9b98ce5c733e990f09ce4e8eea4aaf1041a532/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de84e1694f9507b29e7b263453a2255a73e3d099d258db0f14539bad258abe41", size = 747088, upload-time = "2025-08-21T04:23:05.334Z" }, { url = "https://files.pythonhosted.org/packages/06/f6/4a50187e023b8848edd3f0a8e197b1a7fb08d261d8c60aae7cb6c3d71612/pyzmq-27.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f0944d65ba2b872b9fcece08411d6347f15a874c775b4c3baae7f278550da0fb", size = 544639, upload-time = "2025-08-21T04:23:07.279Z" }, - { url = "https://files.pythonhosted.org/packages/c6/ce/36c82244b111c408c99037b61eedcaac9a3f17300c3f857cf175a7b8832f/pyzmq-27.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:400f34321e3bd89b1165b91ea6b18ad26042ba9ad0dfed8b35049e2e24eeab9b", size = 836001, upload-time = "2025-08-21T04:23:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/6f/dc/4aa8e518f1c0d0ee7fb98cf54cf3854e471815040062d26d948df638d9bc/pyzmq-27.0.2-pp39-pypy39_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9cbad4ef12e4c15c94d2c24ecd15a8ed56bf091c62f121a2b0c618ddd4b7402b", size = 799927, upload-time = "2025-08-21T04:23:19.218Z" }, - { url = "https://files.pythonhosted.org/packages/43/4a/cca781133ce015898b7c4ad63240d05af5c54e8736d099c95b0a6c9aaca9/pyzmq-27.0.2-pp39-pypy39_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6b2b74aac3392b8cf508ccb68c980a8555298cd378434a2d065d6ce0f4211dff", size = 758427, upload-time = "2025-08-21T04:23:21.204Z" }, - { url = "https://files.pythonhosted.org/packages/e3/66/772bf1e3aed8b20587f2e0122bc929711d44ddd27dbbd6eb308a5badd0c3/pyzmq-27.0.2-pp39-pypy39_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7db5db88c24cf9253065d69229a148ff60821e5d6f8ff72579b1f80f8f348bab", size = 567453, upload-time = "2025-08-21T04:23:22.825Z" }, - { url = "https://files.pythonhosted.org/packages/e0/fb/fdc3dd8ec46f5226b4cde299839f10c625886bd18adbeaa8a59ffe104356/pyzmq-27.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8ffe40c216c41756ca05188c3e24a23142334b304f7aebd75c24210385e35573", size = 544636, upload-time = "2025-08-21T04:23:24.736Z" }, ] [[package]] @@ -3207,20 +2360,6 @@ version = "0.27.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size = 27479, upload-time = "2025-08-27T12:16:36.024Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/ed/3aef893e2dd30e77e35d20d4ddb45ca459db59cead748cad9796ad479411/rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef", size = 371606, upload-time = "2025-08-27T12:12:25.189Z" }, - { url = "https://files.pythonhosted.org/packages/6d/82/9818b443e5d3eb4c83c3994561387f116aae9833b35c484474769c4a8faf/rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be", size = 353452, upload-time = "2025-08-27T12:12:27.433Z" }, - { url = "https://files.pythonhosted.org/packages/99/c7/d2a110ffaaa397fc6793a83c7bd3545d9ab22658b7cdff05a24a4535cc45/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61", size = 381519, upload-time = "2025-08-27T12:12:28.719Z" }, - { url = "https://files.pythonhosted.org/packages/5a/bc/e89581d1f9d1be7d0247eaef602566869fdc0d084008ba139e27e775366c/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb", size = 394424, upload-time = "2025-08-27T12:12:30.207Z" }, - { url = "https://files.pythonhosted.org/packages/ac/2e/36a6861f797530e74bb6ed53495f8741f1ef95939eed01d761e73d559067/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657", size = 523467, upload-time = "2025-08-27T12:12:31.808Z" }, - { url = "https://files.pythonhosted.org/packages/c4/59/c1bc2be32564fa499f988f0a5c6505c2f4746ef96e58e4d7de5cf923d77e/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013", size = 402660, upload-time = "2025-08-27T12:12:33.444Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ec/ef8bf895f0628dd0a59e54d81caed6891663cb9c54a0f4bb7da918cb88cf/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a", size = 384062, upload-time = "2025-08-27T12:12:34.857Z" }, - { url = "https://files.pythonhosted.org/packages/69/f7/f47ff154be8d9a5e691c083a920bba89cef88d5247c241c10b9898f595a1/rpds_py-0.27.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1", size = 401289, upload-time = "2025-08-27T12:12:36.085Z" }, - { url = "https://files.pythonhosted.org/packages/3b/d9/ca410363efd0615814ae579f6829cafb39225cd63e5ea5ed1404cb345293/rpds_py-0.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10", size = 417718, upload-time = "2025-08-27T12:12:37.401Z" }, - { url = "https://files.pythonhosted.org/packages/e3/a0/8cb5c2ff38340f221cc067cc093d1270e10658ba4e8d263df923daa18e86/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808", size = 558333, upload-time = "2025-08-27T12:12:38.672Z" }, - { url = "https://files.pythonhosted.org/packages/6f/8c/1b0de79177c5d5103843774ce12b84caa7164dfc6cd66378768d37db11bf/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8", size = 589127, upload-time = "2025-08-27T12:12:41.48Z" }, - { url = "https://files.pythonhosted.org/packages/c8/5e/26abb098d5e01266b0f3a2488d299d19ccc26849735d9d2b95c39397e945/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9", size = 554899, upload-time = "2025-08-27T12:12:42.925Z" }, - { url = "https://files.pythonhosted.org/packages/de/41/905cc90ced13550db017f8f20c6d8e8470066c5738ba480d7ba63e3d136b/rpds_py-0.27.1-cp310-cp310-win32.whl", hash = "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4", size = 217450, upload-time = "2025-08-27T12:12:44.813Z" }, - { url = "https://files.pythonhosted.org/packages/75/3d/6bef47b0e253616ccdf67c283e25f2d16e18ccddd38f92af81d5a3420206/rpds_py-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1", size = 228447, upload-time = "2025-08-27T12:12:46.204Z" }, { url = "https://files.pythonhosted.org/packages/b5/c1/7907329fbef97cbd49db6f7303893bd1dd5a4a3eae415839ffdfb0762cae/rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881", size = 371063, upload-time = "2025-08-27T12:12:47.856Z" }, { url = "https://files.pythonhosted.org/packages/11/94/2aab4bc86228bcf7c48760990273653a4900de89c7537ffe1b0d6097ed39/rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5", size = 353210, upload-time = "2025-08-27T12:12:49.187Z" }, { url = "https://files.pythonhosted.org/packages/3a/57/f5eb3ecf434342f4f1a46009530e93fd201a0b5b83379034ebdb1d7c1a58/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e", size = 381636, upload-time = "2025-08-27T12:12:50.492Z" }, @@ -3309,33 +2448,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dd/10/6b283707780a81919f71625351182b4f98932ac89a09023cb61865136244/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0", size = 555813, upload-time = "2025-08-27T12:15:00.334Z" }, { url = "https://files.pythonhosted.org/packages/04/2e/30b5ea18c01379da6272a92825dd7e53dc9d15c88a19e97932d35d430ef7/rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a", size = 217385, upload-time = "2025-08-27T12:15:01.937Z" }, { url = "https://files.pythonhosted.org/packages/32/7d/97119da51cb1dd3f2f3c0805f155a3aa4a95fa44fe7d78ae15e69edf4f34/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772", size = 230097, upload-time = "2025-08-27T12:15:03.961Z" }, - { url = "https://files.pythonhosted.org/packages/7f/6c/252e83e1ce7583c81f26d1d884b2074d40a13977e1b6c9c50bbf9a7f1f5a/rpds_py-0.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c918c65ec2e42c2a78d19f18c553d77319119bf43aa9e2edf7fb78d624355527", size = 372140, upload-time = "2025-08-27T12:15:05.441Z" }, - { url = "https://files.pythonhosted.org/packages/9d/71/949c195d927c5aeb0d0629d329a20de43a64c423a6aa53836290609ef7ec/rpds_py-0.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1fea2b1a922c47c51fd07d656324531adc787e415c8b116530a1d29c0516c62d", size = 354086, upload-time = "2025-08-27T12:15:07.404Z" }, - { url = "https://files.pythonhosted.org/packages/9f/02/e43e332ad8ce4f6c4342d151a471a7f2900ed1d76901da62eb3762663a71/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbf94c58e8e0cd6b6f38d8de67acae41b3a515c26169366ab58bdca4a6883bb8", size = 382117, upload-time = "2025-08-27T12:15:09.275Z" }, - { url = "https://files.pythonhosted.org/packages/d0/05/b0fdeb5b577197ad72812bbdfb72f9a08fa1e64539cc3940b1b781cd3596/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2a8fed130ce946d5c585eddc7c8eeef0051f58ac80a8ee43bd17835c144c2cc", size = 394520, upload-time = "2025-08-27T12:15:10.727Z" }, - { url = "https://files.pythonhosted.org/packages/67/1f/4cfef98b2349a7585181e99294fa2a13f0af06902048a5d70f431a66d0b9/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:037a2361db72ee98d829bc2c5b7cc55598ae0a5e0ec1823a56ea99374cfd73c1", size = 522657, upload-time = "2025-08-27T12:15:12.613Z" }, - { url = "https://files.pythonhosted.org/packages/44/55/ccf37ddc4c6dce7437b335088b5ca18da864b334890e2fe9aa6ddc3f79a9/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5281ed1cc1d49882f9997981c88df1a22e140ab41df19071222f7e5fc4e72125", size = 402967, upload-time = "2025-08-27T12:15:14.113Z" }, - { url = "https://files.pythonhosted.org/packages/74/e5/5903f92e41e293b07707d5bf00ef39a0eb2af7190aff4beaf581a6591510/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd50659a069c15eef8aa3d64bbef0d69fd27bb4a50c9ab4f17f83a16cbf8905", size = 384372, upload-time = "2025-08-27T12:15:15.842Z" }, - { url = "https://files.pythonhosted.org/packages/8f/e3/fbb409e18aeefc01e49f5922ac63d2d914328430e295c12183ce56ebf76b/rpds_py-0.27.1-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:c4b676c4ae3921649a15d28ed10025548e9b561ded473aa413af749503c6737e", size = 401264, upload-time = "2025-08-27T12:15:17.388Z" }, - { url = "https://files.pythonhosted.org/packages/55/79/529ad07794e05cb0f38e2f965fc5bb20853d523976719400acecc447ec9d/rpds_py-0.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:079bc583a26db831a985c5257797b2b5d3affb0386e7ff886256762f82113b5e", size = 418691, upload-time = "2025-08-27T12:15:19.144Z" }, - { url = "https://files.pythonhosted.org/packages/33/39/6554a7fd6d9906fda2521c6d52f5d723dca123529fb719a5b5e074c15e01/rpds_py-0.27.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e44099bd522cba71a2c6b97f68e19f40e7d85399de899d66cdb67b32d7cb786", size = 558989, upload-time = "2025-08-27T12:15:21.087Z" }, - { url = "https://files.pythonhosted.org/packages/19/b2/76fa15173b6f9f445e5ef15120871b945fb8dd9044b6b8c7abe87e938416/rpds_py-0.27.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e202e6d4188e53c6661af813b46c37ca2c45e497fc558bacc1a7630ec2695aec", size = 589835, upload-time = "2025-08-27T12:15:22.696Z" }, - { url = "https://files.pythonhosted.org/packages/ee/9e/5560a4b39bab780405bed8a88ee85b30178061d189558a86003548dea045/rpds_py-0.27.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f41f814b8eaa48768d1bb551591f6ba45f87ac76899453e8ccd41dba1289b04b", size = 555227, upload-time = "2025-08-27T12:15:24.278Z" }, - { url = "https://files.pythonhosted.org/packages/52/d7/cd9c36215111aa65724c132bf709c6f35175973e90b32115dedc4ced09cb/rpds_py-0.27.1-cp39-cp39-win32.whl", hash = "sha256:9e71f5a087ead99563c11fdaceee83ee982fd39cf67601f4fd66cb386336ee52", size = 217899, upload-time = "2025-08-27T12:15:25.926Z" }, - { url = "https://files.pythonhosted.org/packages/5b/e0/d75ab7b4dd8ba777f6b365adbdfc7614bbfe7c5f05703031dfa4b61c3d6c/rpds_py-0.27.1-cp39-cp39-win_amd64.whl", hash = "sha256:71108900c9c3c8590697244b9519017a400d9ba26a36c48381b3f64743a44aab", size = 228725, upload-time = "2025-08-27T12:15:27.398Z" }, - { url = "https://files.pythonhosted.org/packages/d5/63/b7cc415c345625d5e62f694ea356c58fb964861409008118f1245f8c3347/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf", size = 371360, upload-time = "2025-08-27T12:15:29.218Z" }, - { url = "https://files.pythonhosted.org/packages/e5/8c/12e1b24b560cf378b8ffbdb9dc73abd529e1adcfcf82727dfd29c4a7b88d/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3", size = 353933, upload-time = "2025-08-27T12:15:30.837Z" }, - { url = "https://files.pythonhosted.org/packages/9b/85/1bb2210c1f7a1b99e91fea486b9f0f894aa5da3a5ec7097cbad7dec6d40f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636", size = 382962, upload-time = "2025-08-27T12:15:32.348Z" }, - { url = "https://files.pythonhosted.org/packages/cc/c9/a839b9f219cf80ed65f27a7f5ddbb2809c1b85c966020ae2dff490e0b18e/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8", size = 394412, upload-time = "2025-08-27T12:15:33.839Z" }, - { url = "https://files.pythonhosted.org/packages/02/2d/b1d7f928b0b1f4fc2e0133e8051d199b01d7384875adc63b6ddadf3de7e5/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc", size = 523972, upload-time = "2025-08-27T12:15:35.377Z" }, - { url = "https://files.pythonhosted.org/packages/a9/af/2cbf56edd2d07716df1aec8a726b3159deb47cb5c27e1e42b71d705a7c2f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8", size = 403273, upload-time = "2025-08-27T12:15:37.051Z" }, - { url = "https://files.pythonhosted.org/packages/c0/93/425e32200158d44ff01da5d9612c3b6711fe69f606f06e3895511f17473b/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc", size = 385278, upload-time = "2025-08-27T12:15:38.571Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1a/1a04a915ecd0551bfa9e77b7672d1937b4b72a0fc204a17deef76001cfb2/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71", size = 402084, upload-time = "2025-08-27T12:15:40.529Z" }, - { url = "https://files.pythonhosted.org/packages/51/f7/66585c0fe5714368b62951d2513b684e5215beaceab2c6629549ddb15036/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad", size = 419041, upload-time = "2025-08-27T12:15:42.191Z" }, - { url = "https://files.pythonhosted.org/packages/8e/7e/83a508f6b8e219bba2d4af077c35ba0e0cdd35a751a3be6a7cba5a55ad71/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab", size = 560084, upload-time = "2025-08-27T12:15:43.839Z" }, - { url = "https://files.pythonhosted.org/packages/66/66/bb945683b958a1b19eb0fe715594630d0f36396ebdef4d9b89c2fa09aa56/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059", size = 590115, upload-time = "2025-08-27T12:15:46.647Z" }, - { url = "https://files.pythonhosted.org/packages/12/00/ccfaafaf7db7e7adace915e5c2f2c2410e16402561801e9c7f96683002d3/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b", size = 556561, upload-time = "2025-08-27T12:15:48.219Z" }, - { url = "https://files.pythonhosted.org/packages/e1/b7/92b6ed9aad103bfe1c45df98453dfae40969eef2cb6c6239c58d7e96f1b3/rpds_py-0.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819", size = 229125, upload-time = "2025-08-27T12:15:49.956Z" }, { url = "https://files.pythonhosted.org/packages/0c/ed/e1fba02de17f4f76318b834425257c8ea297e415e12c68b4361f63e8ae92/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df", size = 371402, upload-time = "2025-08-27T12:15:51.561Z" }, { url = "https://files.pythonhosted.org/packages/af/7c/e16b959b316048b55585a697e94add55a4ae0d984434d279ea83442e460d/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3", size = 354084, upload-time = "2025-08-27T12:15:53.219Z" }, { url = "https://files.pythonhosted.org/packages/de/c1/ade645f55de76799fdd08682d51ae6724cb46f318573f18be49b1e040428/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9", size = 383090, upload-time = "2025-08-27T12:15:55.158Z" }, @@ -3348,19 +2460,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/24/e3e72d265121e00b063aef3e3501e5b2473cf1b23511d56e529531acf01e/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf", size = 560003, upload-time = "2025-08-27T12:16:08.06Z" }, { url = "https://files.pythonhosted.org/packages/26/ca/f5a344c534214cc2d41118c0699fffbdc2c1bc7046f2a2b9609765ab9c92/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6", size = 590482, upload-time = "2025-08-27T12:16:10.137Z" }, { url = "https://files.pythonhosted.org/packages/ce/08/4349bdd5c64d9d193c360aa9db89adeee6f6682ab8825dca0a3f535f434f/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a", size = 556523, upload-time = "2025-08-27T12:16:12.188Z" }, - { url = "https://files.pythonhosted.org/packages/4e/ea/5463cd5048a7a2fcdae308b6e96432802132c141bfb9420260142632a0f1/rpds_py-0.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aa8933159edc50be265ed22b401125c9eebff3171f570258854dbce3ecd55475", size = 371778, upload-time = "2025-08-27T12:16:13.851Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c8/f38c099db07f5114029c1467649d308543906933eebbc226d4527a5f4693/rpds_py-0.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a50431bf02583e21bf273c71b89d710e7a710ad5e39c725b14e685610555926f", size = 354394, upload-time = "2025-08-27T12:16:15.609Z" }, - { url = "https://files.pythonhosted.org/packages/7d/79/b76f97704d9dd8ddbd76fed4c4048153a847c5d6003afe20a6b5c3339065/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78af06ddc7fe5cc0e967085a9115accee665fb912c22a3f54bad70cc65b05fe6", size = 382348, upload-time = "2025-08-27T12:16:17.251Z" }, - { url = "https://files.pythonhosted.org/packages/8a/3f/ef23d3c1be1b837b648a3016d5bbe7cfe711422ad110b4081c0a90ef5a53/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70d0738ef8fee13c003b100c2fbd667ec4f133468109b3472d249231108283a3", size = 394159, upload-time = "2025-08-27T12:16:19.251Z" }, - { url = "https://files.pythonhosted.org/packages/74/8a/9e62693af1a34fd28b1a190d463d12407bd7cf561748cb4745845d9548d3/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2f6fd8a1cea5bbe599b6e78a6e5ee08db434fc8ffea51ff201c8765679698b3", size = 522775, upload-time = "2025-08-27T12:16:20.929Z" }, - { url = "https://files.pythonhosted.org/packages/36/0d/8d5bb122bf7a60976b54c5c99a739a3819f49f02d69df3ea2ca2aff47d5c/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8177002868d1426305bb5de1e138161c2ec9eb2d939be38291d7c431c4712df8", size = 402633, upload-time = "2025-08-27T12:16:22.548Z" }, - { url = "https://files.pythonhosted.org/packages/0f/0e/237948c1f425e23e0cf5a566d702652a6e55c6f8fbd332a1792eb7043daf/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:008b839781d6c9bf3b6a8984d1d8e56f0ec46dc56df61fd669c49b58ae800400", size = 384867, upload-time = "2025-08-27T12:16:24.29Z" }, - { url = "https://files.pythonhosted.org/packages/d6/0a/da0813efcd998d260cbe876d97f55b0f469ada8ba9cbc47490a132554540/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:a55b9132bb1ade6c734ddd2759c8dc132aa63687d259e725221f106b83a0e485", size = 401791, upload-time = "2025-08-27T12:16:25.954Z" }, - { url = "https://files.pythonhosted.org/packages/51/78/c6c9e8a8aaca416a6f0d1b6b4a6ee35b88fe2c5401d02235d0a056eceed2/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a46fdec0083a26415f11d5f236b79fa1291c32aaa4a17684d82f7017a1f818b1", size = 419525, upload-time = "2025-08-27T12:16:27.659Z" }, - { url = "https://files.pythonhosted.org/packages/a3/69/5af37e1d71487cf6d56dd1420dc7e0c2732c1b6ff612aa7a88374061c0a8/rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8a63b640a7845f2bdd232eb0d0a4a2dd939bcdd6c57e6bb134526487f3160ec5", size = 559255, upload-time = "2025-08-27T12:16:29.343Z" }, - { url = "https://files.pythonhosted.org/packages/40/7f/8b7b136069ef7ac3960eda25d832639bdb163018a34c960ed042dd1707c8/rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:7e32721e5d4922deaaf963469d795d5bde6093207c52fec719bd22e5d1bedbc4", size = 590384, upload-time = "2025-08-27T12:16:31.005Z" }, - { url = "https://files.pythonhosted.org/packages/d8/06/c316d3f6ff03f43ccb0eba7de61376f8ec4ea850067dddfafe98274ae13c/rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:2c426b99a068601b5f4623573df7a7c3d72e87533a2dd2253353a03e7502566c", size = 555959, upload-time = "2025-08-27T12:16:32.73Z" }, - { url = "https://files.pythonhosted.org/packages/60/94/384cf54c430b9dac742bbd2ec26c23feb78ded0d43d6d78563a281aec017/rpds_py-0.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4fc9b7fe29478824361ead6e14e4f5aed570d477e06088826537e202d25fe859", size = 228784, upload-time = "2025-08-27T12:16:34.428Z" }, ] [[package]] @@ -3444,7 +2543,6 @@ dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "docutils" }, { name = "imagesize" }, - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, { name = "jinja2" }, { name = "packaging" }, { name = "pygments" }, @@ -3456,7 +2554,6 @@ dependencies = [ { name = "sphinxcontrib-jsmath" }, { name = "sphinxcontrib-qthelp" }, { name = "sphinxcontrib-serializinghtml" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5b/be/50e50cb4f2eff47df05673d361095cafd95521d2a22521b920c67a372dcb/sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe", size = 8067911, upload-time = "2024-07-20T14:46:56.059Z" } wheels = [ @@ -3529,8 +2626,7 @@ name = "sphinx-external-toc" version = "1.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "click" }, { name = "pyyaml" }, { name = "sphinx" }, ] @@ -3606,7 +2702,6 @@ version = "2.6.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docutils" }, - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, { name = "pybtex" }, { name = "pybtex-docutils" }, { name = "sphinx" }, @@ -3663,20 +2758,20 @@ wheels = [ [[package]] name = "spm-calculator" -version = "0.3.1" +version = "1.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "census" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, { name = "openpyxl" }, { name = "pandas" }, { name = "requests" }, { name = "us" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/54/3b/b805c7e3e18c5b5c00f61b60112f9690d084c910e2481bc020f35390d8fd/spm_calculator-0.3.1.tar.gz", hash = "sha256:41f2f4d00d8c03422a7d57b800052e7760b88e463a5884802f83ed58d35c18c1", size = 75945, upload-time = "2026-04-17T19:52:39.707Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/5e/3f28b212c401795990250bb3daec741f699a8c7dc2a23a3db7005df6feb6/spm_calculator-1.0.0.tar.gz", hash = "sha256:a99aac8c2c0bf81a9455105bbf872366f1ea9066a99cd793714881cd3db846dc", size = 7150825, upload-time = "2026-09-11T15:45:12.732Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/1b/29f705f8a96fc7f55f2c07dfcddbbae78efdc6f174d25d4a0560fc3f5cf9/spm_calculator-0.3.1-py3-none-any.whl", hash = "sha256:52c57ecc5a240ec941b0f2b0d93bc4fa437ef6250e233baed8e11916fa9c1150", size = 57826, upload-time = "2026-04-17T19:52:38.444Z" }, + { url = "https://files.pythonhosted.org/packages/19/a0/c484f69a0ebf88a9b9fd0ac28176f8df66550f46714f1b0c5ec0b360824e/spm_calculator-1.0.0-py3-none-any.whl", hash = "sha256:e354937a5e1a4045d4966ed594a528d8b02866fabaac9bb5672017004b627305", size = 7395384, upload-time = "2026-09-11T15:45:10.492Z" }, ] [[package]] @@ -3689,14 +2784,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/d7/bc/d59b5d97d27229b0e009bd9098cd81af71c2fa5549c580a0a67b9bed0496/sqlalchemy-2.0.43.tar.gz", hash = "sha256:788bfcef6787a7764169cfe9859fe425bf44559619e1d9f56f5bddf2ebf6f417", size = 9762949, upload-time = "2025-08-11T14:24:58.438Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/4e/985f7da36f09592c5ade99321c72c15101d23c0bb7eecfd1daaca5714422/sqlalchemy-2.0.43-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70322986c0c699dca241418fcf18e637a4369e0ec50540a2b907b184c8bca069", size = 2133162, upload-time = "2025-08-11T15:52:17.854Z" }, - { url = "https://files.pythonhosted.org/packages/37/34/798af8db3cae069461e3bc0898a1610dc469386a97048471d364dc8aae1c/sqlalchemy-2.0.43-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:87accdbba88f33efa7b592dc2e8b2a9c2cdbca73db2f9d5c510790428c09c154", size = 2123082, upload-time = "2025-08-11T15:52:19.181Z" }, - { url = "https://files.pythonhosted.org/packages/fb/0f/79cf4d9dad42f61ec5af1e022c92f66c2d110b93bb1dc9b033892971abfa/sqlalchemy-2.0.43-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c00e7845d2f692ebfc7d5e4ec1a3fd87698e4337d09e58d6749a16aedfdf8612", size = 3208871, upload-time = "2025-08-11T15:50:30.656Z" }, - { url = "https://files.pythonhosted.org/packages/56/b3/59befa58fb0e1a9802c87df02344548e6d007e77e87e6084e2131c29e033/sqlalchemy-2.0.43-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:022e436a1cb39b13756cf93b48ecce7aa95382b9cfacceb80a7d263129dfd019", size = 3209583, upload-time = "2025-08-11T15:57:47.697Z" }, - { url = "https://files.pythonhosted.org/packages/29/d2/124b50c0eb8146e8f0fe16d01026c1a073844f0b454436d8544fe9b33bd7/sqlalchemy-2.0.43-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c5e73ba0d76eefc82ec0219d2301cb33bfe5205ed7a2602523111e2e56ccbd20", size = 3148177, upload-time = "2025-08-11T15:50:32.078Z" }, - { url = "https://files.pythonhosted.org/packages/83/f5/e369cd46aa84278107624617034a5825fedfc5c958b2836310ced4d2eadf/sqlalchemy-2.0.43-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9c2e02f06c68092b875d5cbe4824238ab93a7fa35d9c38052c033f7ca45daa18", size = 3172276, upload-time = "2025-08-11T15:57:49.477Z" }, - { url = "https://files.pythonhosted.org/packages/de/2b/4602bf4c3477fa4c837c9774e6dd22e0389fc52310c4c4dfb7e7ba05e90d/sqlalchemy-2.0.43-cp310-cp310-win32.whl", hash = "sha256:e7a903b5b45b0d9fa03ac6a331e1c1d6b7e0ab41c63b6217b3d10357b83c8b00", size = 2101491, upload-time = "2025-08-11T15:54:59.191Z" }, - { url = "https://files.pythonhosted.org/packages/38/2d/bfc6b6143adef553a08295490ddc52607ee435b9c751c714620c1b3dd44d/sqlalchemy-2.0.43-cp310-cp310-win_amd64.whl", hash = "sha256:4bf0edb24c128b7be0c61cd17eef432e4bef507013292415f3fb7023f02b7d4b", size = 2125148, upload-time = "2025-08-11T15:55:00.593Z" }, { url = "https://files.pythonhosted.org/packages/9d/77/fa7189fe44114658002566c6fe443d3ed0ec1fa782feb72af6ef7fbe98e7/sqlalchemy-2.0.43-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:52d9b73b8fb3e9da34c2b31e6d99d60f5f99fd8c1225c9dad24aeb74a91e1d29", size = 2136472, upload-time = "2025-08-11T15:52:21.789Z" }, { url = "https://files.pythonhosted.org/packages/99/ea/92ac27f2fbc2e6c1766bb807084ca455265707e041ba027c09c17d697867/sqlalchemy-2.0.43-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f42f23e152e4545157fa367b2435a1ace7571cab016ca26038867eb7df2c3631", size = 2126535, upload-time = "2025-08-11T15:52:23.109Z" }, { url = "https://files.pythonhosted.org/packages/94/12/536ede80163e295dc57fff69724caf68f91bb40578b6ac6583a293534849/sqlalchemy-2.0.43-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fb1a8c5438e0c5ea51afe9c6564f951525795cf432bed0c028c1cb081276685", size = 3297521, upload-time = "2025-08-11T15:50:33.536Z" }, @@ -3721,14 +2808,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/86/85/29d216002d4593c2ce1c0ec2cec46dda77bfbcd221e24caa6e85eff53d89/sqlalchemy-2.0.43-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9df7126fd9db49e3a5a3999442cc67e9ee8971f3cb9644250107d7296cb2a164", size = 3219363, upload-time = "2025-08-11T15:56:39.11Z" }, { url = "https://files.pythonhosted.org/packages/b6/e4/bd78b01919c524f190b4905d47e7630bf4130b9f48fd971ae1c6225b6f6a/sqlalchemy-2.0.43-cp313-cp313-win32.whl", hash = "sha256:7f1ac7828857fcedb0361b48b9ac4821469f7694089d15550bbcf9ab22564a1d", size = 2096718, upload-time = "2025-08-11T15:55:05.349Z" }, { url = "https://files.pythonhosted.org/packages/ac/a5/ca2f07a2a201f9497de1928f787926613db6307992fe5cda97624eb07c2f/sqlalchemy-2.0.43-cp313-cp313-win_amd64.whl", hash = "sha256:971ba928fcde01869361f504fcff3b7143b47d30de188b11c6357c0505824197", size = 2123200, upload-time = "2025-08-11T15:55:07.932Z" }, - { url = "https://files.pythonhosted.org/packages/92/95/ddb5acf74a71e0fa4f9410c7d8555f169204ae054a49693b3cd31d0bf504/sqlalchemy-2.0.43-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ceb5c832cc30663aeaf5e39657712f4c4241ad1f638d487ef7216258f6d41fe7", size = 2136445, upload-time = "2025-08-12T17:29:06.145Z" }, - { url = "https://files.pythonhosted.org/packages/ea/d4/7d7ea7dfbc1ddb0aa54dd63a686cd43842192b8e1bfb5315bb052925f704/sqlalchemy-2.0.43-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11f43c39b4b2ec755573952bbcc58d976779d482f6f832d7f33a8d869ae891bf", size = 2126411, upload-time = "2025-08-12T17:29:08.138Z" }, - { url = "https://files.pythonhosted.org/packages/07/bd/123ba09bec14112de10e49d8835e6561feb24fd34131099d98d28d34f106/sqlalchemy-2.0.43-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:413391b2239db55be14fa4223034d7e13325a1812c8396ecd4f2c08696d5ccad", size = 3221776, upload-time = "2025-08-11T16:00:30.938Z" }, - { url = "https://files.pythonhosted.org/packages/ae/35/553e45d5b91b15980c13e1dbcd7591f49047589843fff903c086d7985afb/sqlalchemy-2.0.43-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c379e37b08c6c527181a397212346be39319fb64323741d23e46abd97a400d34", size = 3221665, upload-time = "2025-08-12T17:29:11.307Z" }, - { url = "https://files.pythonhosted.org/packages/07/4d/ff03e516087251da99bd879b5fdb2c697ff20295c836318dda988e12ec19/sqlalchemy-2.0.43-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:03d73ab2a37d9e40dec4984d1813d7878e01dbdc742448d44a7341b7a9f408c7", size = 3160067, upload-time = "2025-08-11T16:00:33.148Z" }, - { url = "https://files.pythonhosted.org/packages/ae/88/cbc7caa186ecdc5dea013e9ccc00d78b93a6638dc39656a42369a9536458/sqlalchemy-2.0.43-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8cee08f15d9e238ede42e9bbc1d6e7158d0ca4f176e4eab21f88ac819ae3bd7b", size = 3184462, upload-time = "2025-08-12T17:29:14.919Z" }, - { url = "https://files.pythonhosted.org/packages/ab/69/f8bbd43080b6fa75cb44ff3a1cc99aaae538dd0ade1a58206912b2565d72/sqlalchemy-2.0.43-cp39-cp39-win32.whl", hash = "sha256:b3edaec7e8b6dc5cd94523c6df4f294014df67097c8217a89929c99975811414", size = 2104031, upload-time = "2025-08-11T15:48:56.453Z" }, - { url = "https://files.pythonhosted.org/packages/36/39/2ec1b0e7a4f44d833d924e7bfca8054c72e37eb73f4d02795d16d8b0230a/sqlalchemy-2.0.43-cp39-cp39-win_amd64.whl", hash = "sha256:227119ce0a89e762ecd882dc661e0aa677a690c914e358f0dd8932a2e8b2765b", size = 2128007, upload-time = "2025-08-11T15:48:57.872Z" }, { url = "https://files.pythonhosted.org/packages/b8/d9/13bdde6521f322861fab67473cec4b1cc8999f3871953531cf61945fad92/sqlalchemy-2.0.43-py3-none-any.whl", hash = "sha256:1681c21dd2ccee222c2fe0bef671d1aef7c504087c9c4e800371cfcc8ac966fc", size = 1924759, upload-time = "2025-08-11T15:39:53.024Z" }, ] @@ -3755,94 +2834,17 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/cb/e1da7e340586a078404c7e4328bfefc930867ace8a9a55916fd220cf9547/standard_imghdr-3.13.0-py3-none-any.whl", hash = "sha256:30a1bff5465605bb496f842a6ac3cc1f2131bf3025b0da28d4877d6d4b7cc8e9", size = 4639, upload-time = "2024-10-30T16:01:13.829Z" }, ] -[[package]] -name = "tables" -version = "3.9.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -dependencies = [ - { name = "blosc2", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "numexpr", version = "2.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "packaging", marker = "python_full_version < '3.10'" }, - { name = "py-cpuinfo", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/31/83/8a13be8338219c3fe0aa7357d1ec4edb27bc346e0f224df7212892b243b5/tables-3.9.2.tar.gz", hash = "sha256:d470263c2e50c4b7c8635a0d99ac1ff2f9e704c24d71e5fa33c4529e7d0ad9c3", size = 4683437, upload-time = "2023-11-27T11:53:17.229Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/95/dea498a083e1fbe60bf0ea999da38cb8e33b5506ba99237b5416267a9bc0/tables-3.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8a4e71fc9d2a3a0cacce4994afd47cd5f4797093ff9cee2cc7dc87e51f308107", size = 7392824, upload-time = "2023-11-27T11:52:19.054Z" }, - { url = "https://files.pythonhosted.org/packages/49/b0/e424d4d409925cdf38689bb85f33e2a7c7bca310fb188de65af031c933b7/tables-3.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fbea426ce9bdd60cda435a265823b31d18f2b36e9045fb2d565679825a7aa46", size = 7113863, upload-time = "2023-11-27T11:52:24.217Z" }, - { url = "https://files.pythonhosted.org/packages/22/86/5a474f002126e37d9ee1062e71dc99223d1618ffcfbd5683a7662766c4af/tables-3.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e756c272bb111d016fec1d03a60095403a8fb42a5fbaf5f317dcf6e3b9d8e92e", size = 7481194, upload-time = "2023-11-27T11:52:29.369Z" }, - { url = "https://files.pythonhosted.org/packages/90/eb/8b998ae92009d6e9a696d8ec43a5b5ab9200a715f8da86d9f952a1a1bde5/tables-3.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:eea41cb32dd22b30d6f3dd4e113f6d693384d301c89f3c4b4712f90c9c955875", size = 4391242, upload-time = "2023-11-27T11:52:33.314Z" }, - { url = "https://files.pythonhosted.org/packages/14/41/ea4928ece6c79614d1b61215a52760d88a134e55eca42a3a09815468afa9/tables-3.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d71913fb8147dc6132595b94fc82f88f6c2436a3b5c57aadfe26c680f96aa387", size = 7323512, upload-time = "2023-11-27T11:52:37.305Z" }, - { url = "https://files.pythonhosted.org/packages/d2/de/a3f2a4c6ac32b81e4e2789d7364db0f8fff0657f1b767004c3e63b353142/tables-3.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d6bbc477d038a17c5062ab6ccd94c8b1fa365cf017b9a2ad6c2dff1a07abb2b", size = 7112660, upload-time = "2023-11-27T11:52:40.427Z" }, - { url = "https://files.pythonhosted.org/packages/03/76/53d432936c6955de1346b629b9f92213b11ee9e8aeab8831027fac9e053d/tables-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e67c71070b871fade3694a4c764504e03836bb1843321766cf2e40b7d280e84", size = 7478756, upload-time = "2023-11-27T11:52:44.182Z" }, - { url = "https://files.pythonhosted.org/packages/64/09/1071a5cef60108dc3fd8d44596371d223834171800f43a652a9bd523588f/tables-3.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:ab9291ff4d243e7966b6706a2675b83138bd9bbe82721d695b78971660d59632", size = 4391764, upload-time = "2023-11-27T11:52:47.4Z" }, - { url = "https://files.pythonhosted.org/packages/01/4f/cb9e44384982889ff720b8fabddb24aa4b0deb69afb1a5f09dd17a93b4ca/tables-3.9.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c14dc879b041cf53be1afe9e5ed581e1aeacdcee9e2e1ee79110dc96a4c8d97c", size = 7351431, upload-time = "2023-11-27T11:52:51.371Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e0/070c3ebc11614f1c3fc413f2327ea9d1818444eab7ae4975a2fea7de03cb/tables-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2848fb3dce30a7b83fa099d026a91d7b10ad48afae04fa10f974f1da3f1e2bbf", size = 7077135, upload-time = "2023-11-27T11:52:54.361Z" }, - { url = "https://files.pythonhosted.org/packages/dd/50/8efbd7339a1fd69658f623bcee5e4c0318d823cc39bb23dbc6b0ff1a7491/tables-3.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b131c9b4e003816a45e2efe5c5c797d01d8308cac4aee72597a15837cedb605c", size = 7442059, upload-time = "2023-11-27T11:52:57.999Z" }, - { url = "https://files.pythonhosted.org/packages/8b/84/93dd082a991c36f3e55e170cb71c4011256e45549c0a3b0d3693cf9053e3/tables-3.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:c6304d321452fd56865e5c309e38373011b0f0f6c714786c5660613ceb623acb", size = 4374051, upload-time = "2023-11-27T11:53:00.801Z" }, - { url = "https://files.pythonhosted.org/packages/64/5d/eb0c1b4401cbd9006589cb1f2a4288e70faa246538c7115ee7e54805c2d7/tables-3.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c52087ed8b90a5f6ba87f0adcd1c433e5f5db7c7ca5984b08ff45f2247635f7d", size = 7396210, upload-time = "2023-11-27T11:53:03.627Z" }, - { url = "https://files.pythonhosted.org/packages/a9/9c/fb98d7a5b9e1c7f588bf71ea75c87711212c964f0c1c8e0cb1246bda0a35/tables-3.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:164b945d0cb731c7232775fd3657f150bcf05413928b86033b023a1dc8dbeb05", size = 7116751, upload-time = "2023-11-27T11:53:07.406Z" }, - { url = "https://files.pythonhosted.org/packages/62/cf/897ad10477897bd55bcb3431833b274adda1c01bc8f856b8de57f6de0b83/tables-3.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a068d4ad08d5a6b2ad457f60ac6676efdab9e29459e776e433d5537a46e62e41", size = 7483600, upload-time = "2023-11-27T11:53:10.155Z" }, - { url = "https://files.pythonhosted.org/packages/28/67/f710befc6a57a339ac0aa7272c7df58014fff4f8863c16f683894b8c7893/tables-3.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:bca5a6bf162a84a6ef74ca4017b28c59c1526cffdbd93ce94c98ff8f9593f1d5", size = 4394103, upload-time = "2023-11-27T11:53:13.632Z" }, -] - -[[package]] -name = "tables" -version = "3.10.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version == '3.10.*'", -] -dependencies = [ - { name = "blosc2", version = "3.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "numexpr", version = "2.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "packaging", marker = "python_full_version == '3.10.*'" }, - { name = "py-cpuinfo", marker = "python_full_version == '3.10.*'" }, - { name = "typing-extensions", marker = "python_full_version == '3.10.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0d/5d/96708a84e9fcd29d1f684d56d4c38a23d29b1c934599a072a49f27ccfa71/tables-3.10.1.tar.gz", hash = "sha256:4aa07ac734b9c037baeaf44aec64ec902ad247f57811b59f30c4e31d31f126cf", size = 4762413, upload-time = "2024-08-17T09:57:47.127Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/69/a768ec8104ada032c9be09f521f548766ddd0351bc941c9d42fa5db001de/tables-3.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bca9d11a570ca1bc57f0845e54e55c3093d5a1ace376faee639e09503a73745b", size = 6823691, upload-time = "2024-08-17T09:56:50.229Z" }, - { url = "https://files.pythonhosted.org/packages/e4/2d/074bc14b39de9b552eec02ee583eff2997d903da1355f4450506335a6055/tables-3.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b62881cb682438d1e92b9178db42b160638aef3ca23341f7d98e9b27821b1eb4", size = 5471221, upload-time = "2024-08-17T09:56:54.84Z" }, - { url = "https://files.pythonhosted.org/packages/4a/30/29411ab804b5ac4bee25c82ba38f4e7a8c0b52c6a1cdbeea7d1db33a53fe/tables-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9cf1bfd8b0e0195196205fc8a134628219cff85d20da537facd67a291e6b347", size = 7170201, upload-time = "2024-08-17T09:56:59.011Z" }, - { url = "https://files.pythonhosted.org/packages/0a/7d/3165c7538b8e89b22fa17ad68e04106cca7023cf68e94011ae7b3b6d2a78/tables-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77f0e6dd45b91d99bf3976c8655c48fe3816baf390b9098e4fb2f0fdf9da7078", size = 7571035, upload-time = "2024-08-17T09:57:03.115Z" }, - { url = "https://files.pythonhosted.org/packages/46/b3/985a23d2cf27aad383301a5e99e1851228a1941b868515612b5357bded5f/tables-3.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:d90542ec172d1d60df0b796c48ad446f2b69a5d5cd3077bd6450891b854d1ffb", size = 6311650, upload-time = "2024-08-17T09:57:06.593Z" }, - { url = "https://files.pythonhosted.org/packages/dc/04/957264eb35e60251830a965e2d02332eb36ed14fbd8345df06981bbf3ece/tables-3.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8917262a2bb3cd79d37e108557e34ec4b365fdcc806e01dd10765a84c65dab6", size = 6790492, upload-time = "2024-08-17T09:57:10.247Z" }, - { url = "https://files.pythonhosted.org/packages/b2/19/eb7af9d92aaf6766f5fedfce11a97ab03cf39856561c5f562dc0c769a682/tables-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f93f6db623b484bb6606537c2a71e95ee34fae19b0d891867642dd8c7be05af6", size = 5506835, upload-time = "2024-08-17T09:57:13.883Z" }, - { url = "https://files.pythonhosted.org/packages/b0/8f/897324e1ad543ca439b2c91f04c406f3eeda6e7ff2f43b4cd939f05043e4/tables-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01ca51624bca1a87e703d6d6b796368bc3460ff007ea8b1341be03bedd863833", size = 7166960, upload-time = "2024-08-17T09:57:17.463Z" }, - { url = "https://files.pythonhosted.org/packages/4e/5c/3f21d1135bf60af99ac79a17bbffd333d69763df2197ba04f47dd30bbd4e/tables-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9372516c76be3a05a573df63a69ce38315d03b5816d2a1e89c48129ec8b161b0", size = 7568724, upload-time = "2024-08-17T09:57:23.02Z" }, - { url = "https://files.pythonhosted.org/packages/1f/e3/3ee6b66263902eccadc4e0e23bca7fb480fd190904b7ce0bea4777b5b799/tables-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:09190fb504888aeacafb7739c13d5c5a3e87af3d261f4d2f832b1f8407be133a", size = 6312200, upload-time = "2024-08-17T09:57:26.322Z" }, - { url = "https://files.pythonhosted.org/packages/95/ec/ea6c476e33602c172c797fe8f8ab96d007d964137068276d142b142a28e5/tables-3.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a7090af37909e3bf229d5599fa442633e5a93b6082960b01038dc0106e07a8da", size = 6791597, upload-time = "2024-08-17T09:57:29.598Z" }, - { url = "https://files.pythonhosted.org/packages/74/02/a967a506e9204e3328a8c03f67e6f3c919defc8df11aba83ae5b2abf7b0f/tables-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:203ed50c0c5f30f007df7633089b2a567b99856cd25d68f19d91624a8db2e7ad", size = 5474779, upload-time = "2024-08-17T09:57:32.43Z" }, - { url = "https://files.pythonhosted.org/packages/c3/26/925793f753664ec698b2c6315c818269313db143da38150897cf260405c2/tables-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e36ce9f10471c69c1f0b06c6966de762558a35d62592c55df7994a8019adaf0c", size = 7130683, upload-time = "2024-08-17T09:57:36.181Z" }, - { url = "https://files.pythonhosted.org/packages/d8/79/2b34f22284459e940a84e71dba19b2a34c7cc0ce3cdf685923c50d5b9611/tables-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f233e78cc9fa4157ec4c3ef2abf01a731fe7969bc6ed73539e5f4cd3b94c98b2", size = 7531367, upload-time = "2024-08-17T09:57:39.864Z" }, - { url = "https://files.pythonhosted.org/packages/3d/27/5a23830f611e26dd7ee104096c6bb82e481b16f3f17ccaed3075f8d48312/tables-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:34357d2f2f75843a44e6fe54d1f11fc2e35a8fd3cb134df3d3362cff78010adb", size = 6295046, upload-time = "2024-08-17T09:57:43.561Z" }, - { url = "https://files.pythonhosted.org/packages/d3/d4/e7c25df877e054b05f146d6ccb920bcdbe8d39b35a0962868b80547532c7/tables-3.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6fc5b46a4f359249c3ab9a0a0a2448d7e680e68cffd63fdf3fb7171781edd46e", size = 6824253, upload-time = "2024-11-09T19:26:06.428Z" }, - { url = "https://files.pythonhosted.org/packages/c6/49/091865d75090a24493bd1b66e52d72f4d9627ff42983a13d4dcd89455d02/tables-3.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2ecabd7f459d40b7f9f5256850dd5f43773fda7b789f827de92c3d26df1e320f", size = 5499587, upload-time = "2024-11-09T19:26:12.402Z" }, - { url = "https://files.pythonhosted.org/packages/23/83/9dac8af333149fa01add439f710d4a312b70faf81c2f59a16b8bfaebb75e/tables-3.10.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40a4ee18f3c9339d9dd8fd3777c75cda5768f2ff347064a2796f59161a190af8", size = 7128236, upload-time = "2024-11-09T19:26:15.716Z" }, - { url = "https://files.pythonhosted.org/packages/89/fd/62f31643596f6ab71fc6d2a87acdee0bc01a03fbe1a7f3f6dc0c91e2546d/tables-3.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757c6ea257c174af8036cf8f273ede756bbcd6db5ac7e2a4d64e788b0f371152", size = 7527953, upload-time = "2024-11-09T19:26:20.229Z" }, -] - [[package]] name = "tables" version = "3.11.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version == '3.12.*'", - "python_full_version == '3.11.*'", -] dependencies = [ - { name = "blosc2", version = "3.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "numexpr", version = "2.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "packaging", marker = "python_full_version >= '3.11'" }, - { name = "py-cpuinfo", marker = "python_full_version >= '3.11'" }, + { name = "blosc2" }, + { name = "numexpr" }, + { name = "numpy", version = "2.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "packaging" }, + { name = "py-cpuinfo" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cc/a3/d213ebe7376d48055bd55a29cd9f99061afa0dcece608f94a5025d797b0a/tables-3.11.1.tar.gz", hash = "sha256:78abcf413091bc7c1e4e8c10fbbb438d1ac0b5a87436c5b972c3e8253871b6fb", size = 4790533, upload-time = "2026-03-01T11:43:36.036Z" } wheels = [ @@ -3954,12 +2956,8 @@ name = "towncrier" version = "25.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, - { name = "importlib-resources", marker = "python_full_version < '3.10'" }, + { name = "click" }, { name = "jinja2" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c2/eb/5bf25a34123698d3bbab39c5bc5375f8f8bcbcc5a136964ade66935b8b9d/towncrier-25.8.0.tar.gz", hash = "sha256:eef16d29f831ad57abb3ae32a0565739866219f1ebfbdd297d32894eb9940eb1", size = 76322, upload-time = "2025-08-30T11:41:55.393Z" } wheels = [ @@ -4099,12 +3097,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/30/93/9210e7606be57a2dfc6277ac97dcc864fd8d39f142ca194fdc186d596fda/zope.interface-7.2.tar.gz", hash = "sha256:8b49f1a3d1ee4cdaf5b32d2e738362c7f5e40ac8b46dd7d1a65e82a4872728fe", size = 252960, upload-time = "2024-11-28T08:45:39.224Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/71/e6177f390e8daa7e75378505c5ab974e0bf59c1d3b19155638c7afbf4b2d/zope.interface-7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2", size = 208243, upload-time = "2024-11-28T08:47:29.781Z" }, - { url = "https://files.pythonhosted.org/packages/52/db/7e5f4226bef540f6d55acfd95cd105782bc6ee044d9b5587ce2c95558a5e/zope.interface-7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a", size = 208759, upload-time = "2024-11-28T08:47:31.908Z" }, - { url = "https://files.pythonhosted.org/packages/28/ea/fdd9813c1eafd333ad92464d57a4e3a82b37ae57c19497bcffa42df673e4/zope.interface-7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550f1c6588ecc368c9ce13c44a49b8d6b6f3ca7588873c679bd8fd88a1b557b6", size = 254922, upload-time = "2024-11-28T09:18:11.795Z" }, - { url = "https://files.pythonhosted.org/packages/3b/d3/0000a4d497ef9fbf4f66bb6828b8d0a235e690d57c333be877bec763722f/zope.interface-7.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ef9e2f865721553c6f22a9ff97da0f0216c074bd02b25cf0d3af60ea4d6931d", size = 249367, upload-time = "2024-11-28T08:48:24.238Z" }, - { url = "https://files.pythonhosted.org/packages/3e/e5/0b359e99084f033d413419eff23ee9c2bd33bca2ca9f4e83d11856f22d10/zope.interface-7.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27f926f0dcb058211a3bb3e0e501c69759613b17a553788b2caeb991bed3b61d", size = 254488, upload-time = "2024-11-28T08:48:28.816Z" }, - { url = "https://files.pythonhosted.org/packages/7b/90/12d50b95f40e3b2fc0ba7f7782104093b9fd62806b13b98ef4e580f2ca61/zope.interface-7.2-cp310-cp310-win_amd64.whl", hash = "sha256:144964649eba4c5e4410bb0ee290d338e78f179cdbfd15813de1a664e7649b3b", size = 211947, upload-time = "2024-11-28T08:48:18.831Z" }, { url = "https://files.pythonhosted.org/packages/98/7d/2e8daf0abea7798d16a58f2f3a2bf7588872eee54ac119f99393fdd47b65/zope.interface-7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1909f52a00c8c3dcab6c4fad5d13de2285a4b3c7be063b239b8dc15ddfb73bd2", size = 208776, upload-time = "2024-11-28T08:47:53.009Z" }, { url = "https://files.pythonhosted.org/packages/a0/2a/0c03c7170fe61d0d371e4c7ea5b62b8cb79b095b3d630ca16719bf8b7b18/zope.interface-7.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:80ecf2451596f19fd607bb09953f426588fc1e79e93f5968ecf3367550396b22", size = 209296, upload-time = "2024-11-28T08:47:57.993Z" }, { url = "https://files.pythonhosted.org/packages/49/b4/451f19448772b4a1159519033a5f72672221e623b0a1bd2b896b653943d8/zope.interface-7.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:033b3923b63474800b04cba480b70f6e6243a62208071fc148354f3f89cc01b7", size = 260997, upload-time = "2024-11-28T09:18:13.935Z" }, @@ -4123,10 +3115,4 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b8/f6/54548df6dc73e30ac6c8a7ff1da73ac9007ba38f866397091d5a82237bd3/zope.interface-7.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb23f58a446a7f09db85eda09521a498e109f137b85fb278edb2e34841055398", size = 259237, upload-time = "2024-11-28T08:48:31.71Z" }, { url = "https://files.pythonhosted.org/packages/b6/66/ac05b741c2129fdf668b85631d2268421c5cd1a9ff99be1674371139d665/zope.interface-7.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a71a5b541078d0ebe373a81a3b7e71432c61d12e660f1d67896ca62d9628045b", size = 264696, upload-time = "2024-11-28T08:48:41.161Z" }, { url = "https://files.pythonhosted.org/packages/0a/2f/1bccc6f4cc882662162a1158cda1a7f616add2ffe322b28c99cb031b4ffc/zope.interface-7.2-cp313-cp313-win_amd64.whl", hash = "sha256:4893395d5dd2ba655c38ceb13014fd65667740f09fa5bb01caa1e6284e48c0cd", size = 212472, upload-time = "2024-11-28T08:49:56.587Z" }, - { url = "https://files.pythonhosted.org/packages/8c/2c/1f49dc8b4843c4f0848d8e43191aed312bad946a1563d1bf9e46cf2816ee/zope.interface-7.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bd449c306ba006c65799ea7912adbbfed071089461a19091a228998b82b1fdb", size = 208349, upload-time = "2024-11-28T08:49:28.872Z" }, - { url = "https://files.pythonhosted.org/packages/ed/7d/83ddbfc8424c69579a90fc8edc2b797223da2a8083a94d8dfa0e374c5ed4/zope.interface-7.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a19a6cc9c6ce4b1e7e3d319a473cf0ee989cbbe2b39201d7c19e214d2dfb80c7", size = 208799, upload-time = "2024-11-28T08:49:30.616Z" }, - { url = "https://files.pythonhosted.org/packages/36/22/b1abd91854c1be03f5542fe092e6a745096d2eca7704d69432e119100583/zope.interface-7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72cd1790b48c16db85d51fbbd12d20949d7339ad84fd971427cf00d990c1f137", size = 254267, upload-time = "2024-11-28T09:18:21.059Z" }, - { url = "https://files.pythonhosted.org/packages/2a/dd/fcd313ee216ad0739ae00e6126bc22a0af62a74f76a9ca668d16cd276222/zope.interface-7.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52e446f9955195440e787596dccd1411f543743c359eeb26e9b2c02b077b0519", size = 248614, upload-time = "2024-11-28T08:48:41.953Z" }, - { url = "https://files.pythonhosted.org/packages/88/d4/4ba1569b856870527cec4bf22b91fe704b81a3c1a451b2ccf234e9e0666f/zope.interface-7.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ad9913fd858274db8dd867012ebe544ef18d218f6f7d1e3c3e6d98000f14b75", size = 253800, upload-time = "2024-11-28T08:48:46.637Z" }, - { url = "https://files.pythonhosted.org/packages/69/da/c9cfb384c18bd3a26d9fc6a9b5f32ccea49ae09444f097eaa5ca9814aff9/zope.interface-7.2-cp39-cp39-win_amd64.whl", hash = "sha256:1090c60116b3da3bfdd0c03406e2f14a1ff53e5771aebe33fec1edc0a350175d", size = 211980, upload-time = "2024-11-28T08:50:35.681Z" }, ]