From 09cb848b382d848042201658e5352661c811cda3 Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Tue, 23 Sep 2025 17:13:37 +0100 Subject: [PATCH 01/11] Add supabase db --- Makefile | 50 +++++ README.md | 29 ++- projects/supabase/.gitignore | 8 + projects/supabase/config.toml | 335 ++++++++++++++++++++++++++++++++++ test.ipynb | 59 ++++++ 5 files changed, 477 insertions(+), 4 deletions(-) create mode 100644 projects/supabase/.gitignore create mode 100644 projects/supabase/config.toml create mode 100644 test.ipynb diff --git a/Makefile b/Makefile index 092ec74a..b7677fde 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,29 @@ setup: # Development commands dev: + @echo "Starting PolicyEngine services..." + docker-compose -f deployment/docker-compose.yml up --build -d + @echo "" + @echo "Starting Supabase (requires Supabase CLI installed locally)..." + @if command -v supabase >/dev/null 2>&1; then \ + cd projects/supabase && supabase start; \ + echo ""; \ + echo "✅ Services are running:"; \ + echo " PolicyEngine API Full: http://localhost:8081"; \ + echo " PolicyEngine API Simulation: http://localhost:8082"; \ + echo " PolicyEngine API Tagger: http://localhost:8083"; \ + echo " Supabase Studio: http://localhost:54323"; \ + echo " Supabase API: http://localhost:54321"; \ + echo " PostgreSQL: localhost:54322"; \ + else \ + echo "⚠️ Supabase CLI not found. Install with: brew install supabase/tap/supabase"; \ + echo " PolicyEngine services are still running."; \ + fi + @echo "" + @echo "Following logs from PolicyEngine services..." + docker-compose -f deployment/docker-compose.yml logs -f + +dev-no-supabase: docker-compose -f deployment/docker-compose.yml up --build up: @@ -71,7 +94,13 @@ else endif down: + @echo "Stopping PolicyEngine services..." docker-compose -f deployment/docker-compose.yml down + @echo "Stopping Supabase..." + @if command -v supabase >/dev/null 2>&1; then \ + cd projects/supabase && supabase stop; \ + fi + @echo "✅ All services stopped" logs: ifdef service @@ -373,3 +402,24 @@ dev-sim: dev-tagger: docker-compose -f deployment/docker-compose.yml up api-tagger + + +clean: + @echo "Removing Python cache files..." + find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true + find . -type f -name "*.pyc" -delete 2>/dev/null || true + find . -type f -name "*.pyo" -delete 2>/dev/null || true + find . -type f -name "*.pyd" -delete 2>/dev/null || true + find . -type f -name ".coverage" -delete 2>/dev/null || true + find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true + find . -type d -name "*.egg" -exec rm -rf {} + 2>/dev/null || true + find . -type d -name ".pytest_cache" -exec rm -rf {} + 2>/dev/null || true + find . -type d -name ".ruff_cache" -exec rm -rf {} + 2>/dev/null || true + find . -type d -name ".mypy_cache" -exec rm -rf {} + 2>/dev/null || true + find . -type d -name "htmlcov" -exec rm -rf {} + 2>/dev/null || true + find . -type d -name "dist" -exec rm -rf {} + 2>/dev/null || true + find . -type d -name "build" -exec rm -rf {} + 2>/dev/null || true + find . -name ".DS_Store" -delete 2>/dev/null || true + @echo "Removing empty directories..." + find . -type d -empty -delete 2>/dev/null || true + @echo "Clean complete." \ No newline at end of file diff --git a/README.md b/README.md index 6f6f75ce..e2b2afea 100644 --- a/README.md +++ b/README.md @@ -9,18 +9,39 @@ Monorepo for PolicyEngine's API infrastructure, containing all services, librari - Docker and Docker Compose - Python 3.13+ - [uv](https://docs.astral.sh/uv/) package manager +- [Supabase CLI](https://supabase.com/docs/guides/cli) (optional, for database features) - gcloud CLI (for deployment) - Terraform 1.5+ (for deployment) +#### Installing Supabase CLI (macOS) +```bash +brew install supabase/tap/supabase +``` + ### Local development -Start all services: +Start all services including Supabase: ```bash -make up # Start services on ports 8081-8083 -make logs # View logs -make down # Stop services +make dev # Start PolicyEngine services + Supabase (if installed) +make down # Stop all services ``` +Or run PolicyEngine services only: +```bash +make dev-no-supabase # Start only PolicyEngine services +make up # Start services detached on ports 8081-8083 +make logs # View logs +make down # Stop services +``` + +When running with `make dev`, services are available at: +- PolicyEngine API Full: http://localhost:8081 +- PolicyEngine API Simulation: http://localhost:8082 +- PolicyEngine API Tagger: http://localhost:8083 +- Supabase API: http://localhost:54321 (if Supabase CLI installed) +- PostgreSQL: localhost:54322 (if Supabase CLI installed) +- Email testing (Inbucket): http://localhost:54324 (if Supabase CLI installed) + Run the test suite: ```bash make test # Unit tests only diff --git a/projects/supabase/.gitignore b/projects/supabase/.gitignore new file mode 100644 index 00000000..ad9264f0 --- /dev/null +++ b/projects/supabase/.gitignore @@ -0,0 +1,8 @@ +# Supabase +.branches +.temp + +# dotenvx +.env.keys +.env.local +.env.*.local diff --git a/projects/supabase/config.toml b/projects/supabase/config.toml new file mode 100644 index 00000000..61b45acb --- /dev/null +++ b/projects/supabase/config.toml @@ -0,0 +1,335 @@ +# For detailed configuration reference documentation, visit: +# https://supabase.com/docs/guides/local-development/cli/config +# A string used to distinguish different Supabase projects on the same host. Defaults to the +# working directory name when running `supabase init`. +project_id = "supabase" + +[api] +enabled = true +# Port to use for the API URL. +port = 54321 +# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API +# endpoints. `public` and `graphql_public` schemas are included by default. +schemas = ["public", "graphql_public"] +# Extra schemas to add to the search_path of every request. +extra_search_path = ["public", "extensions"] +# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size +# for accidental or malicious requests. +max_rows = 1000 + +[api.tls] +# Enable HTTPS endpoints locally using a self-signed certificate. +enabled = false + +[db] +# Port to use for the local database URL. +port = 54322 +# Port used by db diff command to initialize the shadow database. +shadow_port = 54320 +# The database major version to use. This has to be the same as your remote database's. Run `SHOW +# server_version;` on the remote database to check. +major_version = 17 + +[db.pooler] +enabled = false +# Port to use for the local connection pooler. +port = 54329 +# Specifies when a server connection can be reused by other clients. +# Configure one of the supported pooler modes: `transaction`, `session`. +pool_mode = "transaction" +# How many server connections to allow per user/database pair. +default_pool_size = 20 +# Maximum number of client connections allowed. +max_client_conn = 100 + +# [db.vault] +# secret_key = "env(SECRET_VALUE)" + +[db.migrations] +# If disabled, migrations will be skipped during a db push or reset. +enabled = true +# Specifies an ordered list of schema files that describe your database. +# Supports glob patterns relative to supabase directory: "./schemas/*.sql" +schema_paths = [] + +[db.seed] +# If enabled, seeds the database after migrations during a db reset. +enabled = true +# Specifies an ordered list of seed files to load during db reset. +# Supports glob patterns relative to supabase directory: "./seeds/*.sql" +sql_paths = ["./seed.sql"] + +[db.network_restrictions] +# Enable management of network restrictions. +enabled = false +# List of IPv4 CIDR blocks allowed to connect to the database. +# Defaults to allow all IPv4 connections. Set empty array to block all IPs. +allowed_cidrs = ["0.0.0.0/0"] +# List of IPv6 CIDR blocks allowed to connect to the database. +# Defaults to allow all IPv6 connections. Set empty array to block all IPs. +allowed_cidrs_v6 = ["::/0"] + +[realtime] +enabled = true +# Bind realtime via either IPv4 or IPv6. (default: IPv4) +# ip_version = "IPv6" +# The maximum length in bytes of HTTP request headers. (default: 4096) +# max_header_length = 4096 + +[studio] +enabled = true +# Port to use for Supabase Studio. +port = 54323 +# External URL of the API server that frontend connects to. +api_url = "http://127.0.0.1" +# OpenAI API Key to use for Supabase AI in the Supabase Studio. +openai_api_key = "env(OPENAI_API_KEY)" + +# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they +# are monitored, and you can view the emails that would have been sent from the web interface. +[inbucket] +enabled = true +# Port to use for the email testing server web interface. +port = 54324 +# Uncomment to expose additional ports for testing user applications that send emails. +# smtp_port = 54325 +# pop3_port = 54326 +# admin_email = "admin@email.com" +# sender_name = "Admin" + +[storage] +enabled = true +# The maximum file size allowed (e.g. "5MB", "500KB"). +file_size_limit = "50MiB" + +# Image transformation API is available to Supabase Pro plan. +# [storage.image_transformation] +# enabled = true + +# Uncomment to configure local storage buckets +# [storage.buckets.images] +# public = false +# file_size_limit = "50MiB" +# allowed_mime_types = ["image/png", "image/jpeg"] +# objects_path = "./images" + +[auth] +enabled = true +# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used +# in emails. +site_url = "http://127.0.0.1:3000" +# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. +additional_redirect_urls = ["https://127.0.0.1:3000"] +# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week). +jwt_expiry = 3600 +# Path to JWT signing key. DO NOT commit your signing keys file to git. +# signing_keys_path = "./signing_keys.json" +# If disabled, the refresh token will never expire. +enable_refresh_token_rotation = true +# Allows refresh tokens to be reused after expiry, up to the specified interval in seconds. +# Requires enable_refresh_token_rotation = true. +refresh_token_reuse_interval = 10 +# Allow/disallow new user signups to your project. +enable_signup = true +# Allow/disallow anonymous sign-ins to your project. +enable_anonymous_sign_ins = false +# Allow/disallow testing manual linking of accounts +enable_manual_linking = false +# Passwords shorter than this value will be rejected as weak. Minimum 6, recommended 8 or more. +minimum_password_length = 6 +# Passwords that do not meet the following requirements will be rejected as weak. Supported values +# are: `letters_digits`, `lower_upper_letters_digits`, `lower_upper_letters_digits_symbols` +password_requirements = "" + +[auth.rate_limit] +# Number of emails that can be sent per hour. Requires auth.email.smtp to be enabled. +email_sent = 2 +# Number of SMS messages that can be sent per hour. Requires auth.sms to be enabled. +sms_sent = 30 +# Number of anonymous sign-ins that can be made per hour per IP address. Requires enable_anonymous_sign_ins = true. +anonymous_users = 30 +# Number of sessions that can be refreshed in a 5 minute interval per IP address. +token_refresh = 150 +# Number of sign up and sign-in requests that can be made in a 5 minute interval per IP address (excludes anonymous users). +sign_in_sign_ups = 30 +# Number of OTP / Magic link verifications that can be made in a 5 minute interval per IP address. +token_verifications = 30 +# Number of Web3 logins that can be made in a 5 minute interval per IP address. +web3 = 30 + +# Configure one of the supported captcha providers: `hcaptcha`, `turnstile`. +# [auth.captcha] +# enabled = true +# provider = "hcaptcha" +# secret = "" + +[auth.email] +# Allow/disallow new user signups via email to your project. +enable_signup = true +# If enabled, a user will be required to confirm any email change on both the old, and new email +# addresses. If disabled, only the new email is required to confirm. +double_confirm_changes = true +# If enabled, users need to confirm their email address before signing in. +enable_confirmations = false +# If enabled, users will need to reauthenticate or have logged in recently to change their password. +secure_password_change = false +# Controls the minimum amount of time that must pass before sending another signup confirmation or password reset email. +max_frequency = "1s" +# Number of characters used in the email OTP. +otp_length = 6 +# Number of seconds before the email OTP expires (defaults to 1 hour). +otp_expiry = 3600 + +# Use a production-ready SMTP server +# [auth.email.smtp] +# enabled = true +# host = "smtp.sendgrid.net" +# port = 587 +# user = "apikey" +# pass = "env(SENDGRID_API_KEY)" +# admin_email = "admin@email.com" +# sender_name = "Admin" + +# Uncomment to customize email template +# [auth.email.template.invite] +# subject = "You have been invited" +# content_path = "./supabase/templates/invite.html" + +[auth.sms] +# Allow/disallow new user signups via SMS to your project. +enable_signup = false +# If enabled, users need to confirm their phone number before signing in. +enable_confirmations = false +# Template for sending OTP to users +template = "Your code is {{ .Code }}" +# Controls the minimum amount of time that must pass before sending another sms otp. +max_frequency = "5s" + +# Use pre-defined map of phone number to OTP for testing. +# [auth.sms.test_otp] +# 4152127777 = "123456" + +# Configure logged in session timeouts. +# [auth.sessions] +# Force log out after the specified duration. +# timebox = "24h" +# Force log out if the user has been inactive longer than the specified duration. +# inactivity_timeout = "8h" + +# This hook runs before a new user is created and allows developers to reject the request based on the incoming user object. +# [auth.hook.before_user_created] +# enabled = true +# uri = "pg-functions://postgres/auth/before-user-created-hook" + +# This hook runs before a token is issued and allows you to add additional claims based on the authentication method used. +# [auth.hook.custom_access_token] +# enabled = true +# uri = "pg-functions:////" + +# Configure one of the supported SMS providers: `twilio`, `twilio_verify`, `messagebird`, `textlocal`, `vonage`. +[auth.sms.twilio] +enabled = false +account_sid = "" +message_service_sid = "" +# DO NOT commit your Twilio auth token to git. Use environment variable substitution instead: +auth_token = "env(SUPABASE_AUTH_SMS_TWILIO_AUTH_TOKEN)" + +# Multi-factor-authentication is available to Supabase Pro plan. +[auth.mfa] +# Control how many MFA factors can be enrolled at once per user. +max_enrolled_factors = 10 + +# Control MFA via App Authenticator (TOTP) +[auth.mfa.totp] +enroll_enabled = false +verify_enabled = false + +# Configure MFA via Phone Messaging +[auth.mfa.phone] +enroll_enabled = false +verify_enabled = false +otp_length = 6 +template = "Your code is {{ .Code }}" +max_frequency = "5s" + +# Configure MFA via WebAuthn +# [auth.mfa.web_authn] +# enroll_enabled = true +# verify_enabled = true + +# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, +# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `notion`, `twitch`, +# `twitter`, `slack`, `spotify`, `workos`, `zoom`. +[auth.external.apple] +enabled = false +client_id = "" +# DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead: +secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)" +# Overrides the default auth redirectUrl. +redirect_uri = "" +# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, +# or any other third-party OIDC providers. +url = "" +# If enabled, the nonce check will be skipped. Required for local sign in with Google auth. +skip_nonce_check = false + +# Allow Solana wallet holders to sign in to your project via the Sign in with Solana (SIWS, EIP-4361) standard. +# You can configure "web3" rate limit in the [auth.rate_limit] section and set up [auth.captcha] if self-hosting. +[auth.web3.solana] +enabled = false + +# Use Firebase Auth as a third-party provider alongside Supabase Auth. +[auth.third_party.firebase] +enabled = false +# project_id = "my-firebase-project" + +# Use Auth0 as a third-party provider alongside Supabase Auth. +[auth.third_party.auth0] +enabled = false +# tenant = "my-auth0-tenant" +# tenant_region = "us" + +# Use AWS Cognito (Amplify) as a third-party provider alongside Supabase Auth. +[auth.third_party.aws_cognito] +enabled = false +# user_pool_id = "my-user-pool-id" +# user_pool_region = "us-east-1" + +# Use Clerk as a third-party provider alongside Supabase Auth. +[auth.third_party.clerk] +enabled = false +# Obtain from https://clerk.com/setup/supabase +# domain = "example.clerk.accounts.dev" + +[edge_runtime] +enabled = true +# Supported request policies: `oneshot`, `per_worker`. +# `per_worker` (default) — enables hot reload during local development. +# `oneshot` — fallback mode if hot reload causes issues (e.g. in large repos or with symlinks). +policy = "per_worker" +# Port to attach the Chrome inspector for debugging edge functions. +inspector_port = 8083 +# The Deno major version to use. +deno_version = 2 + +# [edge_runtime.secrets] +# secret_key = "env(SECRET_VALUE)" + +[analytics] +enabled = true +port = 54327 +# Configure one of the supported backends: `postgres`, `bigquery`. +backend = "postgres" + +# Experimental features may be deprecated any time +[experimental] +# Configures Postgres storage engine to use OrioleDB (S3) +orioledb_version = "" +# Configures S3 bucket URL, eg. .s3-.amazonaws.com +s3_host = "env(S3_HOST)" +# Configures S3 bucket region, eg. us-east-1 +s3_region = "env(S3_REGION)" +# Configures AWS_ACCESS_KEY_ID for S3 bucket +s3_access_key = "env(S3_ACCESS_KEY)" +# Configures AWS_SECRET_ACCESS_KEY for S3 bucket +s3_secret_key = "env(S3_SECRET_KEY)" diff --git a/test.ipynb b/test.ipynb new file mode 100644 index 00000000..38271224 --- /dev/null +++ b/test.ipynb @@ -0,0 +1,59 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "fae368db", + "metadata": {}, + "outputs": [], + "source": [ + "from policyengine.database import Database" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "20ec3c48", + "metadata": {}, + "outputs": [], + "source": [ + "db = Database(url=\"postgresql://postgres:postgres@127.0.0.1:54322/postgres\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ebf1d5bf", + "metadata": {}, + "outputs": [], + "source": [ + "from policyengine.models.policyengine_uk import policyengine_uk_latest_version\n", + "from policyengine.models.policyengine_us import policyengine_us_latest_version\n", + "\n", + "db.register_model_version(policyengine_uk_latest_version)\n", + "db.register_model_version(policyengine_us_latest_version)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "policyengine", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From e32cfc713e49aee495f26eb6d23c13cdb03fd9f9 Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Thu, 25 Sep 2025 10:32:55 +0100 Subject: [PATCH 02/11] Update --- Makefile | 20 - projects/policyengine-api-full/pyproject.toml | 4 +- .../src/policyengine_api_full/api/__init__.py | 5 + .../api/database_router.py | 424 ++++++++++++++ .../src/policyengine_api_full/database.py | 32 ++ .../src/policyengine_api_full/main.py | 24 +- projects/policyengine-api-full/uv.lock | 530 ++++++++++++++++++ test.ipynb | 45 +- 8 files changed, 1057 insertions(+), 27 deletions(-) create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/database_router.py create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/database.py diff --git a/Makefile b/Makefile index b7677fde..cd52a925 100644 --- a/Makefile +++ b/Makefile @@ -403,23 +403,3 @@ dev-sim: dev-tagger: docker-compose -f deployment/docker-compose.yml up api-tagger - -clean: - @echo "Removing Python cache files..." - find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true - find . -type f -name "*.pyc" -delete 2>/dev/null || true - find . -type f -name "*.pyo" -delete 2>/dev/null || true - find . -type f -name "*.pyd" -delete 2>/dev/null || true - find . -type f -name ".coverage" -delete 2>/dev/null || true - find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true - find . -type d -name "*.egg" -exec rm -rf {} + 2>/dev/null || true - find . -type d -name ".pytest_cache" -exec rm -rf {} + 2>/dev/null || true - find . -type d -name ".ruff_cache" -exec rm -rf {} + 2>/dev/null || true - find . -type d -name ".mypy_cache" -exec rm -rf {} + 2>/dev/null || true - find . -type d -name "htmlcov" -exec rm -rf {} + 2>/dev/null || true - find . -type d -name "dist" -exec rm -rf {} + 2>/dev/null || true - find . -type d -name "build" -exec rm -rf {} + 2>/dev/null || true - find . -name ".DS_Store" -delete 2>/dev/null || true - @echo "Removing empty directories..." - find . -type d -empty -delete 2>/dev/null || true - @echo "Clean complete." \ No newline at end of file diff --git a/projects/policyengine-api-full/pyproject.toml b/projects/policyengine-api-full/pyproject.toml index b45c6142..d9c61300 100644 --- a/projects/policyengine-api-full/pyproject.toml +++ b/projects/policyengine-api-full/pyproject.toml @@ -16,7 +16,9 @@ dependencies = [ "opentelemetry-instrumentation-sqlalchemy (>=0.51b0,<0.52)", "pydantic-settings (>=2.7.1,<3.0.0)", "opentelemetry-instrumentation-fastapi (>=0.51b0,<0.52)", - "policyengine-fastapi" + "policyengine-fastapi", + "policyengine[full]>=3.0.0", + "psycopg2-binary>=2.9.0" ] [tool.hatch.build.targets.wheel] diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/__init__.py b/projects/policyengine-api-full/src/policyengine_api_full/api/__init__.py index 0f3c38b4..f60d775d 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/__init__.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/api/__init__.py @@ -4,6 +4,7 @@ from policyengine_fastapi.auth.jwt_decoder import JWTDecoder from policyengine_fastapi.database import create_session_dep from .household import include_all_routers +from .database_router import create_database_router """ Application defined as routers completely indipendent of environment allowing it @@ -24,3 +25,7 @@ def initialize(app: FastAPI, engine: Engine, jwt_issuer: str, jwt_audience: str) optional_auth=optional_auth, auth=auth, ) + + # Add the database router for PolicyEngine tables + database_router = create_database_router() + app.include_router(database_router) diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/database_router.py b/projects/policyengine-api-full/src/policyengine_api_full/api/database_router.py new file mode 100644 index 00000000..0d9b31f1 --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/api/database_router.py @@ -0,0 +1,424 @@ +from fastapi import APIRouter, Depends, HTTPException, Query +from sqlmodel import Session, select +from typing import List, Optional, Any, Dict + +# Import all the database tables and models from policyengine package +from policyengine.database import ( + UserTable, + ModelTable, + ModelVersionTable, + DatasetTable, + VersionedDatasetTable, + PolicyTable, + SimulationTable, + ParameterTable, + ParameterValueTable, + BaselineParameterValueTable, + BaselineVariableTable, + DynamicTable, +) + +from policyengine.models import ( + User, + Model, + ModelVersion, + Dataset, + VersionedDataset, + Policy, + Simulation, + Parameter, + ParameterValue, + BaselineParameterValue, + BaselineVariable, + Dynamic, +) + +from policyengine_api_full.database import get_session + +def create_database_router() -> APIRouter: + """Create a router with CRUD endpoints for all database tables.""" + router = APIRouter(prefix="/database", tags=["database"]) + + # User endpoints + @router.get("/users", response_model=List[User]) + def list_users( + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + session: Session = Depends(get_session) + ): + """List all users with pagination.""" + statement = select(UserTable).offset(skip).limit(limit) + users = session.exec(statement).all() + return [User.model_validate(user.model_dump()) for user in users] + + @router.get("/users/{user_id}", response_model=User) + def get_user(user_id: str, session: Session = Depends(get_session)): + """Get a specific user by ID.""" + user = session.get(UserTable, user_id) + if not user: + raise HTTPException(status_code=404, detail="User not found") + return User.model_validate(user.model_dump()) + + @router.post("/users", response_model=User) + def create_user(user: User, session: Session = Depends(get_session)): + """Create a new user.""" + db_user = UserTable(**user.model_dump()) + session.add(db_user) + session.commit() + session.refresh(db_user) + return User.model_validate(db_user.model_dump()) + + @router.put("/users/{user_id}", response_model=User) + def update_user(user_id: str, user: User, session: Session = Depends(get_session)): + """Update an existing user.""" + db_user = session.get(UserTable, user_id) + if not db_user: + raise HTTPException(status_code=404, detail="User not found") + user_data = user.model_dump(exclude_unset=True) + for key, value in user_data.items(): + setattr(db_user, key, value) + session.add(db_user) + session.commit() + session.refresh(db_user) + return User.model_validate(db_user.model_dump()) + + @router.delete("/users/{user_id}") + def delete_user(user_id: str, session: Session = Depends(get_session)): + """Delete a user.""" + user = session.get(UserTable, user_id) + if not user: + raise HTTPException(status_code=404, detail="User not found") + session.delete(user) + session.commit() + return {"message": "User deleted successfully"} + + # Model endpoints + @router.get("/models", response_model=List[Dict[str, Any]]) + def list_models( + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + session: Session = Depends(get_session) + ): + """List all models with pagination.""" + statement = select(ModelTable).offset(skip).limit(limit) + models = session.exec(statement).all() + # Return as dict since Model has callable fields + return [{"id": m.id, "name": m.name, "description": m.description} for m in models] + + @router.get("/models/{model_id}") + def get_model(model_id: str, session: Session = Depends(get_session)): + """Get a specific model by ID.""" + model = session.get(ModelTable, model_id) + if not model: + raise HTTPException(status_code=404, detail="Model not found") + return {"id": model.id, "name": model.name, "description": model.description} + + # Model Version endpoints + @router.get("/model-versions", response_model=List[ModelVersion]) + def list_model_versions( + model_id: Optional[str] = None, + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + session: Session = Depends(get_session) + ): + """List all model versions with optional filtering by model_id.""" + statement = select(ModelVersionTable) + if model_id: + statement = statement.where(ModelVersionTable.model_id == model_id) + statement = statement.offset(skip).limit(limit) + versions = session.exec(statement).all() + return [ModelVersion.model_validate(v.model_dump()) for v in versions] + + @router.post("/model-versions", response_model=ModelVersion) + def create_model_version(version: ModelVersion, session: Session = Depends(get_session)): + """Create a new model version.""" + db_version = ModelVersionTable(**version.model_dump()) + session.add(db_version) + session.commit() + session.refresh(db_version) + return ModelVersion.model_validate(db_version.model_dump()) + + # Dataset endpoints + @router.get("/datasets", response_model=List[Dataset]) + def list_datasets( + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + session: Session = Depends(get_session) + ): + """List all datasets with pagination.""" + statement = select(DatasetTable).offset(skip).limit(limit) + datasets = session.exec(statement).all() + return [Dataset.model_validate(d.model_dump()) for d in datasets] + + @router.get("/datasets/{dataset_id}", response_model=Dataset) + def get_dataset(dataset_id: str, session: Session = Depends(get_session)): + """Get a specific dataset by ID.""" + dataset = session.get(DatasetTable, dataset_id) + if not dataset: + raise HTTPException(status_code=404, detail="Dataset not found") + return Dataset.model_validate(dataset.model_dump()) + + @router.post("/datasets", response_model=Dataset) + def create_dataset(dataset: Dataset, session: Session = Depends(get_session)): + """Create a new dataset.""" + db_dataset = DatasetTable(**dataset.model_dump()) + session.add(db_dataset) + session.commit() + session.refresh(db_dataset) + return Dataset.model_validate(db_dataset.model_dump()) + + # Versioned Dataset endpoints + @router.get("/versioned-datasets", response_model=List[VersionedDataset]) + def list_versioned_datasets( + dataset_id: Optional[str] = None, + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + session: Session = Depends(get_session) + ): + """List all versioned datasets with optional filtering.""" + statement = select(VersionedDatasetTable) + if dataset_id: + statement = statement.where(VersionedDatasetTable.dataset_id == dataset_id) + statement = statement.offset(skip).limit(limit) + versioned = session.exec(statement).all() + return [VersionedDataset.model_validate(v.model_dump()) for v in versioned] + + @router.post("/versioned-datasets", response_model=VersionedDataset) + def create_versioned_dataset(versioned: VersionedDataset, session: Session = Depends(get_session)): + """Create a new versioned dataset.""" + db_versioned = VersionedDatasetTable(**versioned.model_dump()) + session.add(db_versioned) + session.commit() + session.refresh(db_versioned) + return VersionedDataset.model_validate(db_versioned.model_dump()) + + # Policy endpoints + @router.get("/policies", response_model=List[Policy]) + def list_policies( + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + session: Session = Depends(get_session) + ): + """List all policies with pagination.""" + statement = select(PolicyTable).offset(skip).limit(limit) + policies = session.exec(statement).all() + return [Policy.model_validate(p.model_dump()) for p in policies] + + @router.get("/policies/{policy_id}", response_model=Policy) + def get_policy(policy_id: str, session: Session = Depends(get_session)): + """Get a specific policy by ID.""" + policy = session.get(PolicyTable, policy_id) + if not policy: + raise HTTPException(status_code=404, detail="Policy not found") + return Policy.model_validate(policy.model_dump()) + + @router.post("/policies", response_model=Policy) + def create_policy(policy: Policy, session: Session = Depends(get_session)): + """Create a new policy.""" + db_policy = PolicyTable(**policy.model_dump()) + session.add(db_policy) + session.commit() + session.refresh(db_policy) + return Policy.model_validate(db_policy.model_dump()) + + @router.put("/policies/{policy_id}", response_model=Policy) + def update_policy(policy_id: str, policy: Policy, session: Session = Depends(get_session)): + """Update an existing policy.""" + db_policy = session.get(PolicyTable, policy_id) + if not db_policy: + raise HTTPException(status_code=404, detail="Policy not found") + policy_data = policy.model_dump(exclude_unset=True) + for key, value in policy_data.items(): + setattr(db_policy, key, value) + session.add(db_policy) + session.commit() + session.refresh(db_policy) + return Policy.model_validate(db_policy.model_dump()) + + @router.delete("/policies/{policy_id}") + def delete_policy(policy_id: str, session: Session = Depends(get_session)): + """Delete a policy.""" + policy = session.get(PolicyTable, policy_id) + if not policy: + raise HTTPException(status_code=404, detail="Policy not found") + session.delete(policy) + session.commit() + return {"message": "Policy deleted successfully"} + + # Simulation endpoints + @router.get("/simulations", response_model=List[Simulation]) + def list_simulations( + policy_id: Optional[str] = None, + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + session: Session = Depends(get_session) + ): + """List all simulations with optional filtering.""" + statement = select(SimulationTable) + if policy_id: + statement = statement.where(SimulationTable.policy_id == policy_id) + statement = statement.offset(skip).limit(limit) + simulations = session.exec(statement).all() + return [Simulation.model_validate(s.model_dump()) for s in simulations] + + @router.get("/simulations/{simulation_id}", response_model=Simulation) + def get_simulation(simulation_id: str, session: Session = Depends(get_session)): + """Get a specific simulation by ID.""" + simulation = session.get(SimulationTable, simulation_id) + if not simulation: + raise HTTPException(status_code=404, detail="Simulation not found") + return Simulation.model_validate(simulation.model_dump()) + + @router.post("/simulations", response_model=Simulation) + def create_simulation(simulation: Simulation, session: Session = Depends(get_session)): + """Create a new simulation.""" + db_simulation = SimulationTable(**simulation.model_dump()) + session.add(db_simulation) + session.commit() + session.refresh(db_simulation) + return Simulation.model_validate(db_simulation.model_dump()) + + # Parameter endpoints + @router.get("/parameters", response_model=List[Parameter]) + def list_parameters( + model_id: Optional[str] = None, + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + session: Session = Depends(get_session) + ): + """List all parameters with optional filtering.""" + statement = select(ParameterTable) + if model_id: + statement = statement.where(ParameterTable.model_id == model_id) + statement = statement.offset(skip).limit(limit) + parameters = session.exec(statement).all() + return [Parameter.model_validate(p.model_dump()) for p in parameters] + + @router.get("/parameters/{parameter_id}", response_model=Parameter) + def get_parameter(parameter_id: str, session: Session = Depends(get_session)): + """Get a specific parameter by ID.""" + parameter = session.get(ParameterTable, parameter_id) + if not parameter: + raise HTTPException(status_code=404, detail="Parameter not found") + return Parameter.model_validate(parameter.model_dump()) + + @router.post("/parameters", response_model=Parameter) + def create_parameter(parameter: Parameter, session: Session = Depends(get_session)): + """Create a new parameter.""" + db_parameter = ParameterTable(**parameter.model_dump()) + session.add(db_parameter) + session.commit() + session.refresh(db_parameter) + return Parameter.model_validate(db_parameter.model_dump()) + + # Parameter Value endpoints + @router.get("/parameter-values", response_model=List[ParameterValue]) + def list_parameter_values( + parameter_id: Optional[str] = None, + policy_id: Optional[str] = None, + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + session: Session = Depends(get_session) + ): + """List all parameter values with optional filtering.""" + statement = select(ParameterValueTable) + if parameter_id: + statement = statement.where(ParameterValueTable.parameter_id == parameter_id) + if policy_id: + statement = statement.where(ParameterValueTable.policy_id == policy_id) + statement = statement.offset(skip).limit(limit) + values = session.exec(statement).all() + return [ParameterValue.model_validate(v.model_dump()) for v in values] + + @router.post("/parameter-values", response_model=ParameterValue) + def create_parameter_value(value: ParameterValue, session: Session = Depends(get_session)): + """Create a new parameter value.""" + db_value = ParameterValueTable(**value.model_dump()) + session.add(db_value) + session.commit() + session.refresh(db_value) + return ParameterValue.model_validate(db_value.model_dump()) + + # Baseline Parameter Value endpoints + @router.get("/baseline-parameter-values", response_model=List[BaselineParameterValue]) + def list_baseline_parameter_values( + parameter_id: Optional[str] = None, + model_version_id: Optional[str] = None, + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + session: Session = Depends(get_session) + ): + """List all baseline parameter values with optional filtering.""" + statement = select(BaselineParameterValueTable) + if parameter_id: + statement = statement.where(BaselineParameterValueTable.parameter_id == parameter_id) + if model_version_id: + statement = statement.where(BaselineParameterValueTable.model_version_id == model_version_id) + statement = statement.offset(skip).limit(limit) + values = session.exec(statement).all() + return [BaselineParameterValue.model_validate(v.model_dump()) for v in values] + + @router.post("/baseline-parameter-values", response_model=BaselineParameterValue) + def create_baseline_parameter_value(value: BaselineParameterValue, session: Session = Depends(get_session)): + """Create a new baseline parameter value.""" + db_value = BaselineParameterValueTable(**value.model_dump()) + session.add(db_value) + session.commit() + session.refresh(db_value) + return BaselineParameterValue.model_validate(db_value.model_dump()) + + # Baseline Variable endpoints + @router.get("/baseline-variables", response_model=List[BaselineVariable]) + def list_baseline_variables( + model_version_id: Optional[str] = None, + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + session: Session = Depends(get_session) + ): + """List all baseline variables with optional filtering.""" + statement = select(BaselineVariableTable) + if model_version_id: + statement = statement.where(BaselineVariableTable.model_version_id == model_version_id) + statement = statement.offset(skip).limit(limit) + variables = session.exec(statement).all() + return [BaselineVariable.model_validate(v.model_dump()) for v in variables] + + @router.post("/baseline-variables", response_model=BaselineVariable) + def create_baseline_variable(variable: BaselineVariable, session: Session = Depends(get_session)): + """Create a new baseline variable.""" + db_variable = BaselineVariableTable(**variable.model_dump()) + session.add(db_variable) + session.commit() + session.refresh(db_variable) + return BaselineVariable.model_validate(db_variable.model_dump()) + + # Dynamic endpoints + @router.get("/dynamics", response_model=List[Dynamic]) + def list_dynamics( + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + session: Session = Depends(get_session) + ): + """List all dynamics with pagination.""" + statement = select(DynamicTable).offset(skip).limit(limit) + dynamics = session.exec(statement).all() + return [Dynamic.model_validate(d.model_dump()) for d in dynamics] + + @router.get("/dynamics/{dynamic_id}", response_model=Dynamic) + def get_dynamic(dynamic_id: str, session: Session = Depends(get_session)): + """Get a specific dynamic by ID.""" + dynamic = session.get(DynamicTable, dynamic_id) + if not dynamic: + raise HTTPException(status_code=404, detail="Dynamic not found") + return Dynamic.model_validate(dynamic.model_dump()) + + @router.post("/dynamics", response_model=Dynamic) + def create_dynamic(dynamic: Dynamic, session: Session = Depends(get_session)): + """Create a new dynamic.""" + db_dynamic = DynamicTable(**dynamic.model_dump()) + session.add(db_dynamic) + session.commit() + session.refresh(db_dynamic) + return Dynamic.model_validate(db_dynamic.model_dump()) + + return router \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/database.py b/projects/policyengine-api-full/src/policyengine_api_full/database.py new file mode 100644 index 00000000..234356f6 --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/database.py @@ -0,0 +1,32 @@ +from sqlmodel import create_engine, Session +from sqlalchemy.pool import NullPool +import os +from typing import Generator + +def get_supabase_url() -> str: + """Get Supabase database URL from environment or localhost.""" + # Default to local Supabase instance + db_host = os.getenv("SUPABASE_DB_HOST", "localhost") + db_port = os.getenv("SUPABASE_DB_PORT", "54322") + db_name = os.getenv("SUPABASE_DB_NAME", "postgres") + db_user = os.getenv("SUPABASE_DB_USER", "postgres") + db_password = os.getenv("SUPABASE_DB_PASSWORD", "postgres") + + return f"postgresql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}" + +def create_supabase_engine(): + """Create SQLModel engine for Supabase PostgreSQL.""" + database_url = get_supabase_url() + # Use NullPool to avoid connection pooling issues with serverless + engine = create_engine( + database_url, + poolclass=NullPool, + echo=os.getenv("SQL_ECHO", "false").lower() == "true" + ) + return engine + +def get_session() -> Generator[Session, None, None]: + """Dependency to get database session.""" + engine = create_supabase_engine() + with Session(engine) as session: + yield session \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/main.py b/projects/policyengine-api-full/src/policyengine_api_full/main.py index c059325e..59cdf125 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/main.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/main.py @@ -2,7 +2,6 @@ from typing import Any from fastapi import FastAPI from sqlmodel import SQLModel -from policyengine_fastapi.database import create_sqlite_engine from policyengine_fastapi import ping from policyengine_fastapi.health import ( HealthRegistry, @@ -23,20 +22,37 @@ ) from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor from policyengine_api_full.api import initialize +from policyengine_api_full.database import create_supabase_engine import logging """ specific example instantiation of the app configured by a .env file -* in all environments we use sqlite +* Uses Supabase PostgreSQL database * on desktop we print opentelemetry instrumentation to the console. * in "production" we use GCP trace/metrics bindings. """ logger = logging.getLogger(__name__) +# Import all tables to ensure they're registered with SQLModel +from policyengine.database import ( + UserTable, + ModelTable, + ModelVersionTable, + DatasetTable, + VersionedDatasetTable, + PolicyTable, + SimulationTable, + ParameterTable, + ParameterValueTable, + BaselineParameterValueTable, + BaselineVariableTable, + DynamicTable, +) + # configure database -# manage tables directly from defined models. -engine = create_sqlite_engine() +# Use Supabase PostgreSQL instead of SQLite +engine = create_supabase_engine() @asynccontextmanager diff --git a/projects/policyengine-api-full/uv.lock b/projects/policyengine-api-full/uv.lock index 39b4800f..50d50704 100644 --- a/projects/policyengine-api-full/uv.lock +++ b/projects/policyengine-api-full/uv.lock @@ -2,6 +2,20 @@ version = 1 revision = 3 requires-python = ">=3.13" +[[package]] +name = "alembic" +version = "1.16.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mako" }, + { name = "sqlalchemy" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9a/ca/4dc52902cf3491892d464f5265a81e9dff094692c8a049a3ed6a05fe7ee8/alembic-1.16.5.tar.gz", hash = "sha256:a88bb7f6e513bd4301ecf4c7f2206fe93f9913f9b48dac3b78babde2d6fe765e", size = 1969868, upload-time = "2025-08-27T18:02:05.668Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/4a/4c61d4c84cfd9befb6fa08a702535b27b21fff08c946bc2f6139decbf7f7/alembic-1.16.5-py3-none-any.whl", hash = "sha256:e845dfe090c5ffa7b92593ae6687c5cb1a101e91fa53868497dbd79847f9dbe3", size = 247355, upload-time = "2025-08-27T18:02:07.37Z" }, +] + [[package]] name = "annotated-types" version = "0.7.0" @@ -33,6 +47,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7c/3c/0464dcada90d5da0e71018c04a140ad6349558afb30b3051b4264cc5b965/asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c", size = 23790, upload-time = "2025-07-08T09:07:41.548Z" }, ] +[[package]] +name = "asttokens" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978, upload-time = "2024-11-30T04:30:14.439Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918, upload-time = "2024-11-30T04:30:10.946Z" }, +] + [[package]] name = "attrs" version = "25.3.0" @@ -62,6 +85,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" }, ] +[[package]] +name = "blosc" +version = "1.11.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/ca/3ec5a5d05e10ad200d887c8cfb9492d9a02e05f9f8f726aa178123b1711b/blosc-1.11.3.tar.gz", hash = "sha256:89ed658eba7814a92e89c44d8c524148d55921595bc133bd1a90f8888a9e088e", size = 1439627, upload-time = "2025-05-17T11:50:03.713Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/23/6ee0e7270ad6299e73483dfad31b17f8acf66f7768094316a35ee0534f1d/blosc-1.11.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9b474c70b9765587323dd1d7ff8e9fa9e9b35ccb3bee77e7658ce9faf2e05f7f", size = 2291576, upload-time = "2025-05-17T11:49:41.013Z" }, + { url = "https://files.pythonhosted.org/packages/51/8f/d8097dd6bf952d4bc1a31852f717d5a1157b32c1bea50dac723ed8e6bc8d/blosc-1.11.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:291d153864f53960861a48c2a5f6706adc2a84a2bdd9c3d1c5353d9c32748a03", size = 1801973, upload-time = "2025-05-17T11:49:42.259Z" }, + { url = "https://files.pythonhosted.org/packages/1e/cb/7fdf0756e6a38d6a28c5063bc8ba8a8c8b1a1ab6980d777c52ca7dd942b1/blosc-1.11.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece67bb34741a147e4120cff3ee3784121709a112d16795716b8f4239aaddfa4", size = 2485043, upload-time = "2025-05-17T11:49:44.034Z" }, + { url = "https://files.pythonhosted.org/packages/7c/b8/d21a1305356312ca0fc6bd54ad6fb91e7434f0efef545972eb72f040c815/blosc-1.11.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e70216dbddb85b69a8d0f62a4a5c09b7a1fce9ca2f329793e799f8b6f9fa3ab0", size = 2619988, upload-time = "2025-05-17T11:49:45.346Z" }, + { url = "https://files.pythonhosted.org/packages/a0/79/9ed273c9493e02f0bc5deacd3854ecabd6c6ba5371ed04b6c7702fd16f77/blosc-1.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:220865ffcac638f8f0f4b51259d4e4f3236165e5b43fffd1e836cd7cd29b9367", size = 2678176, upload-time = "2025-05-17T11:49:47.12Z" }, + { url = "https://files.pythonhosted.org/packages/79/0e/c50458a1e038c0f0da70c3223d2a34ad702b86a79d0921f23a8ffaae035f/blosc-1.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d57dde8c335378e8443757b69d0b29e90dfc53047d01311e952aecc815167dec", size = 2752740, upload-time = "2025-05-17T11:49:48.909Z" }, + { url = "https://files.pythonhosted.org/packages/f1/0e/3a5ed949e0e23eb576c08017bb39e8612607cf8f591d8149b0fb82469a03/blosc-1.11.3-cp313-cp313-win32.whl", hash = "sha256:d3d72046580a50177811916c78130d6ae7307420733de6e950cb567c896b1ca5", size = 1530991, upload-time = "2025-05-17T11:49:50.121Z" }, + { url = "https://files.pythonhosted.org/packages/06/d4/0c3cdaf34b3ef705fdab465ad8df4a3bce5bbdf2bca8f2515eae90ae28a0/blosc-1.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:73721c1949f2b8d2f4168cababbfe6280511f0da9a971ba7ec9c56eab9603824", size = 1815688, upload-time = "2025-05-17T11:49:51.434Z" }, +] + [[package]] name = "cachetools" version = "5.5.2" @@ -71,6 +110,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" }, ] +[[package]] +name = "caugetch" +version = "0.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/ec/519cb37e3e58e23a5b02a74049128f6e701ccd8892b0cebecf701fac6177/caugetch-0.0.1.tar.gz", hash = "sha256:6f6ddb3b928fa272071b02aabb3342941cd99992f27413ba8c189eb4dc3e33b0", size = 2071, upload-time = "2019-10-15T22:39:49.315Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/70/33/64fee4626ec943c2d0c4eee31c784dab8452dfe014916190730880d4ea62/caugetch-0.0.1-py3-none-any.whl", hash = "sha256:ee743dcbb513409cd24cfc42435418073683ba2f4bb7ee9f8440088a47d59277", size = 3439, upload-time = "2019-10-15T22:39:47.122Z" }, +] + [[package]] name = "certifi" version = "2025.8.3" @@ -123,6 +171,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, ] +[[package]] +name = "clipboard" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyperclip" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/38/17f3885713d0f39994563029942b1d31c93d4e56d80da505abfbfb3a3bc4/clipboard-0.0.4.tar.gz", hash = "sha256:a72a78e9c9bf68da1c3f29ee022417d13ec9e3824b511559fd2b702b1dd5b817", size = 1713, upload-time = "2014-05-22T12:49:08.683Z" } + [[package]] name = "colorama" version = "0.4.6" @@ -132,6 +189,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "comm" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } +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.6" @@ -185,6 +251,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/44/0c/50db5379b615854b5cf89146f8f5bd1d5a9693d7f3a987e269693521c404/coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3", size = 208986, upload-time = "2025-08-29T15:35:14.506Z" }, ] +[[package]] +name = "decorator" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, +] + [[package]] name = "deprecated" version = "1.2.18" @@ -219,6 +294,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, ] +[[package]] +name = "executing" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, +] + [[package]] name = "fastapi" version = "0.115.14" @@ -281,6 +365,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e5/a6/5aa862489a2918a096166fd98d9fe86b7fd53c607678b3fa9d8c432d88d5/fastapi_cloud_cli-0.1.5-py3-none-any.whl", hash = "sha256:d80525fb9c0e8af122370891f9fa83cf5d496e4ad47a8dd26c0496a6c85a012a", size = 18992, upload-time = "2025-07-28T13:30:47.427Z" }, ] +[[package]] +name = "getpass4" +version = "0.0.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "caugetch" }, + { name = "clipboard" }, + { name = "colorama" }, + { name = "pyperclip" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/f9/312f84afc384f693d02eb4ff7306a7268577a8b808aa08f0124c9abba683/getpass4-0.0.14.1.tar.gz", hash = "sha256:80aa4e3a665f2eccc6cda3ee22125eeb5c6338e91c40c4fd010b3c94c7aa4d3a", size = 5078, upload-time = "2021-11-28T17:08:47.276Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/d3/ea114aba31f76418b2162e811793cde2e822c9d9ea8ca98d67f9e1f1bde6/getpass4-0.0.14.1-py3-none-any.whl", hash = "sha256:6642c11fb99db1bec90b963e863ec71cdb0b8888000f5089c6377bfbf833f8a9", size = 8683, upload-time = "2021-11-28T17:08:45.468Z" }, +] + [[package]] name = "google-api-core" version = "2.25.1" @@ -317,6 +416,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/17/63/b19553b658a1692443c62bd07e5868adaa0ad746a0751ba62c59568cd45b/google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca", size = 216137, upload-time = "2025-06-04T18:04:55.573Z" }, ] +[[package]] +name = "google-cloud-core" +version = "2.4.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core" }, + { name = "google-auth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/b8/2b53838d2acd6ec6168fd284a990c76695e84c65deee79c9f3a4276f6b4f/google_cloud_core-2.4.3.tar.gz", hash = "sha256:1fab62d7102844b278fe6dead3af32408b1df3eb06f5c7e8634cbd40edc4da53", size = 35861, upload-time = "2025-03-10T21:05:38.948Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/86/bda7241a8da2d28a754aad2ba0f6776e35b67e37c36ae0c45d49370f1014/google_cloud_core-2.4.3-py2.py3-none-any.whl", hash = "sha256:5130f9f4c14b4fafdff75c79448f9495cfade0d8775facf1b09c3bf67e027f6e", size = 29348, upload-time = "2025-03-10T21:05:37.785Z" }, +] + [[package]] name = "google-cloud-monitoring" version = "2.27.2" @@ -332,6 +444,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b4/64/00a0027ee6bb6a69bc210037720477157e161ebcea20704c50fb0a7ba76f/google_cloud_monitoring-2.27.2-py3-none-any.whl", hash = "sha256:70b2e877d6267a3548ca17be301a4253fe83d4bebf7ea5cd8ee68b9dd3a70a02", size = 383687, upload-time = "2025-06-11T23:21:22.88Z" }, ] +[[package]] +name = "google-cloud-storage" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core" }, + { name = "google-auth" }, + { name = "google-cloud-core" }, + { name = "google-crc32c" }, + { name = "google-resumable-media" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4e/a6/6e0a318f70975a3c048c0e1a18aee4f7b6d7dac1e798fdc5353c5248d418/google_cloud_storage-3.4.0.tar.gz", hash = "sha256:4c77ec00c98ccc6428e4c39404926f41e2152f48809b02af29d5116645c3c317", size = 17226847, upload-time = "2025-09-15T10:40:05.045Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/12/164a90e4692423ed5532274928b0e19c8cae345ae1aa413d78c6b688231b/google_cloud_storage-3.4.0-py3-none-any.whl", hash = "sha256:16eeca305e4747a6871f8f7627eef3b862fdd365b872ca74d4a89e9841d0f8e8", size = 278423, upload-time = "2025-09-15T10:40:03.349Z" }, +] + [[package]] name = "google-cloud-trace" version = "1.16.2" @@ -347,6 +476,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/08/96/7a8d271e91effa9ccc2fd7cfd5cf287a2d7900080a475477c2ac0c7a331d/google_cloud_trace-1.16.2-py3-none-any.whl", hash = "sha256:40fb74607752e4ee0f3d7e5fc6b8f6eb1803982254a1507ba918172484131456", size = 103755, upload-time = "2025-06-12T00:53:00.672Z" }, ] +[[package]] +name = "google-crc32c" +version = "1.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/ae/87802e6d9f9d69adfaedfcfd599266bf386a54d0be058b532d04c794f76d/google_crc32c-1.7.1.tar.gz", hash = "sha256:2bff2305f98846f3e825dbeec9ee406f89da7962accdb29356e4eadc251bd472", size = 14495, upload-time = "2025-03-26T14:29:13.32Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/72/b8d785e9184ba6297a8620c8a37cf6e39b81a8ca01bb0796d7cbb28b3386/google_crc32c-1.7.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:df8b38bdaf1629d62d51be8bdd04888f37c451564c2042d36e5812da9eff3c35", size = 30467, upload-time = "2025-03-26T14:36:06.909Z" }, + { url = "https://files.pythonhosted.org/packages/34/25/5f18076968212067c4e8ea95bf3b69669f9fc698476e5f5eb97d5b37999f/google_crc32c-1.7.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:e42e20a83a29aa2709a0cf271c7f8aefaa23b7ab52e53b322585297bb94d4638", size = 30309, upload-time = "2025-03-26T15:06:15.318Z" }, + { url = "https://files.pythonhosted.org/packages/92/83/9228fe65bf70e93e419f38bdf6c5ca5083fc6d32886ee79b450ceefd1dbd/google_crc32c-1.7.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:905a385140bf492ac300026717af339790921f411c0dfd9aa5a9e69a08ed32eb", size = 33133, upload-time = "2025-03-26T14:41:34.388Z" }, + { url = "https://files.pythonhosted.org/packages/c3/ca/1ea2fd13ff9f8955b85e7956872fdb7050c4ace8a2306a6d177edb9cf7fe/google_crc32c-1.7.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b211ddaf20f7ebeec5c333448582c224a7c90a9d98826fbab82c0ddc11348e6", size = 32773, upload-time = "2025-03-26T14:41:35.19Z" }, + { url = "https://files.pythonhosted.org/packages/89/32/a22a281806e3ef21b72db16f948cad22ec68e4bdd384139291e00ff82fe2/google_crc32c-1.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:0f99eaa09a9a7e642a61e06742856eec8b19fc0037832e03f941fe7cf0c8e4db", size = 33475, upload-time = "2025-03-26T14:29:11.771Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c5/002975aff514e57fc084ba155697a049b3f9b52225ec3bc0f542871dd524/google_crc32c-1.7.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32d1da0d74ec5634a05f53ef7df18fc646666a25efaaca9fc7dcfd4caf1d98c3", size = 33243, upload-time = "2025-03-26T14:41:35.975Z" }, + { url = "https://files.pythonhosted.org/packages/61/cb/c585282a03a0cea70fcaa1bf55d5d702d0f2351094d663ec3be1c6c67c52/google_crc32c-1.7.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e10554d4abc5238823112c2ad7e4560f96c7bf3820b202660373d769d9e6e4c9", size = 32870, upload-time = "2025-03-26T14:41:37.08Z" }, +] + +[[package]] +name = "google-resumable-media" +version = "2.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-crc32c" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/5a/0efdc02665dca14e0837b62c8a1a93132c264bd02054a15abb2218afe0ae/google_resumable_media-2.7.2.tar.gz", hash = "sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0", size = 2163099, upload-time = "2024-08-07T22:20:38.555Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/35/b8d3baf8c46695858cb9d8835a53baa1eeb9906ddaf2f728a5f5b640fd1e/google_resumable_media-2.7.2-py2.py3-none-any.whl", hash = "sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa", size = 81251, upload-time = "2024-08-07T22:20:36.409Z" }, +] + [[package]] name = "googleapis-common-protos" version = "1.70.0" @@ -497,6 +653,67 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, ] +[[package]] +name = "ipython" +version = "9.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "ipython-pygments-lexers" }, + { 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" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/71/a86262bf5a68bf211bcc71fe302af7e05f18a2852fdc610a854d20d085e6/ipython-9.5.0.tar.gz", hash = "sha256:129c44b941fe6d9b82d36fc7a7c18127ddb1d6f02f78f867f402e2e3adde3113", size = 4389137, upload-time = "2025-08-29T12:15:21.519Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/2a/5628a99d04acb2d2f2e749cdf4ea571d2575e898df0528a090948018b726/ipython-9.5.0-py3-none-any.whl", hash = "sha256:88369ffa1d5817d609120daa523a6da06d02518e582347c29f8451732a9c5e72", size = 612426, upload-time = "2025-08-29T12:15:18.866Z" }, +] + +[[package]] +name = "ipython-pygments-lexers" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, +] + +[[package]] +name = "ipywidgets" +version = "8.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "comm" }, + { name = "ipython" }, + { name = "jupyterlab-widgets" }, + { name = "traitlets" }, + { name = "widgetsnbextension" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3e/48/d3dbac45c2814cb73812f98dd6b38bbcc957a4e7bb31d6ea9c03bf94ed87/ipywidgets-8.1.7.tar.gz", hash = "sha256:15f1ac050b9ccbefd45dccfbb2ef6bed0029d8278682d569d71b8dd96bee0376", size = 116721, upload-time = "2025-05-05T12:42:03.489Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/6a/9166369a2f092bd286d24e6307de555d63616e8ddb373ebad2b5635ca4cd/ipywidgets-8.1.7-py3-none-any.whl", hash = "sha256:764f2602d25471c213919b8a1997df04bef869251db4ca8efba1b76b1bd9f7bb", size = 139806, upload-time = "2025-05-05T12:41:56.833Z" }, +] + +[[package]] +name = "jedi" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, +] + [[package]] name = "jinja2" version = "3.1.6" @@ -509,6 +726,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] +[[package]] +name = "jupyterlab-widgets" +version = "3.0.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/7d/160595ca88ee87ac6ba95d82177d29ec60aaa63821d3077babb22ce031a5/jupyterlab_widgets-3.0.15.tar.gz", hash = "sha256:2920888a0c2922351a9202817957a68c07d99673504d6cd37345299e971bb08b", size = 213149, upload-time = "2025-05-05T12:32:31.004Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/6a/ca128561b22b60bd5a0c4ea26649e68c8556b82bc70a0c396eebc977fe86/jupyterlab_widgets-3.0.15-py3-none-any.whl", hash = "sha256:d59023d7d7ef71400d51e6fee9a88867f6e65e10a4201605d2d7f3e8f012a31c", size = 216571, upload-time = "2025-05-05T12:32:29.534Z" }, +] + +[[package]] +name = "mako" +version = "1.3.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" }, +] + [[package]] name = "markdown-it-py" version = "4.0.0" @@ -549,6 +787,18 @@ wheels = [ { 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" }, ] +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159, upload-time = "2024-04-15T13:44:44.803Z" } +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 = "mdurl" version = "0.1.2" @@ -558,6 +808,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] +[[package]] +name = "microdf-python" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "pandas" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/25/55c2b0495ae4c3142d61f1283d675494aac4c254e40ecf1ea4b337a051c7/microdf_python-1.0.2.tar.gz", hash = "sha256:5c845974d485598a7002c151f58ec7438e94c04954fc8fdea9238265e7bf02f5", size = 14826, upload-time = "2025-07-24T12:21:08.17Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/1a/aac40a7e58de4133a9cc7630913a8b8e6c76326288b168cbb47f7714c4fd/microdf_python-1.0.2-py3-none-any.whl", hash = "sha256:f7883785e4557d1c8822dbf0d69d7eeab9399f8e67a9bdb716f74554c7580ae7", size = 15823, upload-time = "2025-07-24T12:21:07.356Z" }, +] + [[package]] name = "mypy-extensions" version = "1.1.0" @@ -576,6 +839,58 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, ] +[[package]] +name = "numpy" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/19/95b3d357407220ed24c139018d2518fab0a61a948e68286a25f1a4d049ff/numpy-2.3.3.tar.gz", hash = "sha256:ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029", size = 20576648, upload-time = "2025-09-09T16:54:12.543Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/b9/984c2b1ee61a8b803bf63582b4ac4242cf76e2dbd663efeafcb620cc0ccb/numpy-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f5415fb78995644253370985342cd03572ef8620b934da27d77377a2285955bf", size = 20949588, upload-time = "2025-09-09T15:56:59.087Z" }, + { url = "https://files.pythonhosted.org/packages/a6/e4/07970e3bed0b1384d22af1e9912527ecbeb47d3b26e9b6a3bced068b3bea/numpy-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d00de139a3324e26ed5b95870ce63be7ec7352171bc69a4cf1f157a48e3eb6b7", size = 14177802, upload-time = "2025-09-09T15:57:01.73Z" }, + { url = "https://files.pythonhosted.org/packages/35/c7/477a83887f9de61f1203bad89cf208b7c19cc9fef0cebef65d5a1a0619f2/numpy-2.3.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9dc13c6a5829610cc07422bc74d3ac083bd8323f14e2827d992f9e52e22cd6a6", size = 5106537, upload-time = "2025-09-09T15:57:03.765Z" }, + { url = "https://files.pythonhosted.org/packages/52/47/93b953bd5866a6f6986344d045a207d3f1cfbad99db29f534ea9cee5108c/numpy-2.3.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d79715d95f1894771eb4e60fb23f065663b2298f7d22945d66877aadf33d00c7", size = 6640743, upload-time = "2025-09-09T15:57:07.921Z" }, + { url = "https://files.pythonhosted.org/packages/23/83/377f84aaeb800b64c0ef4de58b08769e782edcefa4fea712910b6f0afd3c/numpy-2.3.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:952cfd0748514ea7c3afc729a0fc639e61655ce4c55ab9acfab14bda4f402b4c", size = 14278881, upload-time = "2025-09-09T15:57:11.349Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a5/bf3db6e66c4b160d6ea10b534c381a1955dfab34cb1017ea93aa33c70ed3/numpy-2.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b83648633d46f77039c29078751f80da65aa64d5622a3cd62aaef9d835b6c93", size = 16636301, upload-time = "2025-09-09T15:57:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/a2/59/1287924242eb4fa3f9b3a2c30400f2e17eb2707020d1c5e3086fe7330717/numpy-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b001bae8cea1c7dfdb2ae2b017ed0a6f2102d7a70059df1e338e307a4c78a8ae", size = 16053645, upload-time = "2025-09-09T15:57:16.534Z" }, + { url = "https://files.pythonhosted.org/packages/e6/93/b3d47ed882027c35e94ac2320c37e452a549f582a5e801f2d34b56973c97/numpy-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e9aced64054739037d42fb84c54dd38b81ee238816c948c8f3ed134665dcd86", size = 18578179, upload-time = "2025-09-09T15:57:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/20/d9/487a2bccbf7cc9d4bfc5f0f197761a5ef27ba870f1e3bbb9afc4bbe3fcc2/numpy-2.3.3-cp313-cp313-win32.whl", hash = "sha256:9591e1221db3f37751e6442850429b3aabf7026d3b05542d102944ca7f00c8a8", size = 6312250, upload-time = "2025-09-09T15:57:21.296Z" }, + { url = "https://files.pythonhosted.org/packages/1b/b5/263ebbbbcede85028f30047eab3d58028d7ebe389d6493fc95ae66c636ab/numpy-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f0dadeb302887f07431910f67a14d57209ed91130be0adea2f9793f1a4f817cf", size = 12783269, upload-time = "2025-09-09T15:57:23.034Z" }, + { url = "https://files.pythonhosted.org/packages/fa/75/67b8ca554bbeaaeb3fac2e8bce46967a5a06544c9108ec0cf5cece559b6c/numpy-2.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:3c7cf302ac6e0b76a64c4aecf1a09e51abd9b01fc7feee80f6c43e3ab1b1dbc5", size = 10195314, upload-time = "2025-09-09T15:57:25.045Z" }, + { url = "https://files.pythonhosted.org/packages/11/d0/0d1ddec56b162042ddfafeeb293bac672de9b0cfd688383590090963720a/numpy-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eda59e44957d272846bb407aad19f89dc6f58fecf3504bd144f4c5cf81a7eacc", size = 21048025, upload-time = "2025-09-09T15:57:27.257Z" }, + { url = "https://files.pythonhosted.org/packages/36/9e/1996ca6b6d00415b6acbdd3c42f7f03ea256e2c3f158f80bd7436a8a19f3/numpy-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:823d04112bc85ef5c4fda73ba24e6096c8f869931405a80aa8b0e604510a26bc", size = 14301053, upload-time = "2025-09-09T15:57:30.077Z" }, + { url = "https://files.pythonhosted.org/packages/05/24/43da09aa764c68694b76e84b3d3f0c44cb7c18cdc1ba80e48b0ac1d2cd39/numpy-2.3.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:40051003e03db4041aa325da2a0971ba41cf65714e65d296397cc0e32de6018b", size = 5229444, upload-time = "2025-09-09T15:57:32.733Z" }, + { url = "https://files.pythonhosted.org/packages/bc/14/50ffb0f22f7218ef8af28dd089f79f68289a7a05a208db9a2c5dcbe123c1/numpy-2.3.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ee9086235dd6ab7ae75aba5662f582a81ced49f0f1c6de4260a78d8f2d91a19", size = 6738039, upload-time = "2025-09-09T15:57:34.328Z" }, + { url = "https://files.pythonhosted.org/packages/55/52/af46ac0795e09657d45a7f4db961917314377edecf66db0e39fa7ab5c3d3/numpy-2.3.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94fcaa68757c3e2e668ddadeaa86ab05499a70725811e582b6a9858dd472fb30", size = 14352314, upload-time = "2025-09-09T15:57:36.255Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b1/dc226b4c90eb9f07a3fff95c2f0db3268e2e54e5cce97c4ac91518aee71b/numpy-2.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da1a74b90e7483d6ce5244053399a614b1d6b7bc30a60d2f570e5071f8959d3e", size = 16701722, upload-time = "2025-09-09T15:57:38.622Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9d/9d8d358f2eb5eced14dba99f110d83b5cd9a4460895230f3b396ad19a323/numpy-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2990adf06d1ecee3b3dcbb4977dfab6e9f09807598d647f04d385d29e7a3c3d3", size = 16132755, upload-time = "2025-09-09T15:57:41.16Z" }, + { url = "https://files.pythonhosted.org/packages/b6/27/b3922660c45513f9377b3fb42240bec63f203c71416093476ec9aa0719dc/numpy-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ed635ff692483b8e3f0fcaa8e7eb8a75ee71aa6d975388224f70821421800cea", size = 18651560, upload-time = "2025-09-09T15:57:43.459Z" }, + { url = "https://files.pythonhosted.org/packages/5b/8e/3ab61a730bdbbc201bb245a71102aa609f0008b9ed15255500a99cd7f780/numpy-2.3.3-cp313-cp313t-win32.whl", hash = "sha256:a333b4ed33d8dc2b373cc955ca57babc00cd6f9009991d9edc5ddbc1bac36bcd", size = 6442776, upload-time = "2025-09-09T15:57:45.793Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3a/e22b766b11f6030dc2decdeff5c2fb1610768055603f9f3be88b6d192fb2/numpy-2.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4384a169c4d8f97195980815d6fcad04933a7e1ab3b530921c3fef7a1c63426d", size = 12927281, upload-time = "2025-09-09T15:57:47.492Z" }, + { url = "https://files.pythonhosted.org/packages/7b/42/c2e2bc48c5e9b2a83423f99733950fbefd86f165b468a3d85d52b30bf782/numpy-2.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:75370986cc0bc66f4ce5110ad35aae6d182cc4ce6433c40ad151f53690130bf1", size = 10265275, upload-time = "2025-09-09T15:57:49.647Z" }, + { url = "https://files.pythonhosted.org/packages/6b/01/342ad585ad82419b99bcf7cebe99e61da6bedb89e213c5fd71acc467faee/numpy-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cd052f1fa6a78dee696b58a914b7229ecfa41f0a6d96dc663c1220a55e137593", size = 20951527, upload-time = "2025-09-09T15:57:52.006Z" }, + { url = "https://files.pythonhosted.org/packages/ef/d8/204e0d73fc1b7a9ee80ab1fe1983dd33a4d64a4e30a05364b0208e9a241a/numpy-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:414a97499480067d305fcac9716c29cf4d0d76db6ebf0bf3cbce666677f12652", size = 14186159, upload-time = "2025-09-09T15:57:54.407Z" }, + { url = "https://files.pythonhosted.org/packages/22/af/f11c916d08f3a18fb8ba81ab72b5b74a6e42ead4c2846d270eb19845bf74/numpy-2.3.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:50a5fe69f135f88a2be9b6ca0481a68a136f6febe1916e4920e12f1a34e708a7", size = 5114624, upload-time = "2025-09-09T15:57:56.5Z" }, + { url = "https://files.pythonhosted.org/packages/fb/11/0ed919c8381ac9d2ffacd63fd1f0c34d27e99cab650f0eb6f110e6ae4858/numpy-2.3.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:b912f2ed2b67a129e6a601e9d93d4fa37bef67e54cac442a2f588a54afe5c67a", size = 6642627, upload-time = "2025-09-09T15:57:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/ee/83/deb5f77cb0f7ba6cb52b91ed388b47f8f3c2e9930d4665c600408d9b90b9/numpy-2.3.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e318ee0596d76d4cb3d78535dc005fa60e5ea348cd131a51e99d0bdbe0b54fe", size = 14296926, upload-time = "2025-09-09T15:58:00.035Z" }, + { url = "https://files.pythonhosted.org/packages/77/cc/70e59dcb84f2b005d4f306310ff0a892518cc0c8000a33d0e6faf7ca8d80/numpy-2.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce020080e4a52426202bdb6f7691c65bb55e49f261f31a8f506c9f6bc7450421", size = 16638958, upload-time = "2025-09-09T15:58:02.738Z" }, + { url = "https://files.pythonhosted.org/packages/b6/5a/b2ab6c18b4257e099587d5b7f903317bd7115333ad8d4ec4874278eafa61/numpy-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e6687dc183aa55dae4a705b35f9c0f8cb178bcaa2f029b241ac5356221d5c021", size = 16071920, upload-time = "2025-09-09T15:58:05.029Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f1/8b3fdc44324a259298520dd82147ff648979bed085feeacc1250ef1656c0/numpy-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d8f3b1080782469fdc1718c4ed1d22549b5fb12af0d57d35e992158a772a37cf", size = 18577076, upload-time = "2025-09-09T15:58:07.745Z" }, + { url = "https://files.pythonhosted.org/packages/f0/a1/b87a284fb15a42e9274e7fcea0dad259d12ddbf07c1595b26883151ca3b4/numpy-2.3.3-cp314-cp314-win32.whl", hash = "sha256:cb248499b0bc3be66ebd6578b83e5acacf1d6cb2a77f2248ce0e40fbec5a76d0", size = 6366952, upload-time = "2025-09-09T15:58:10.096Z" }, + { url = "https://files.pythonhosted.org/packages/70/5f/1816f4d08f3b8f66576d8433a66f8fa35a5acfb3bbd0bf6c31183b003f3d/numpy-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:691808c2b26b0f002a032c73255d0bd89751425f379f7bcd22d140db593a96e8", size = 12919322, upload-time = "2025-09-09T15:58:12.138Z" }, + { url = "https://files.pythonhosted.org/packages/8c/de/072420342e46a8ea41c324a555fa90fcc11637583fb8df722936aed1736d/numpy-2.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:9ad12e976ca7b10f1774b03615a2a4bab8addce37ecc77394d8e986927dc0dfe", size = 10478630, upload-time = "2025-09-09T15:58:14.64Z" }, + { url = "https://files.pythonhosted.org/packages/d5/df/ee2f1c0a9de7347f14da5dd3cd3c3b034d1b8607ccb6883d7dd5c035d631/numpy-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9cc48e09feb11e1db00b320e9d30a4151f7369afb96bd0e48d942d09da3a0d00", size = 21047987, upload-time = "2025-09-09T15:58:16.889Z" }, + { url = "https://files.pythonhosted.org/packages/d6/92/9453bdc5a4e9e69cf4358463f25e8260e2ffc126d52e10038b9077815989/numpy-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:901bf6123879b7f251d3631967fd574690734236075082078e0571977c6a8e6a", size = 14301076, upload-time = "2025-09-09T15:58:20.343Z" }, + { url = "https://files.pythonhosted.org/packages/13/77/1447b9eb500f028bb44253105bd67534af60499588a5149a94f18f2ca917/numpy-2.3.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:7f025652034199c301049296b59fa7d52c7e625017cae4c75d8662e377bf487d", size = 5229491, upload-time = "2025-09-09T15:58:22.481Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f9/d72221b6ca205f9736cb4b2ce3b002f6e45cd67cd6a6d1c8af11a2f0b649/numpy-2.3.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:533ca5f6d325c80b6007d4d7fb1984c303553534191024ec6a524a4c92a5935a", size = 6737913, upload-time = "2025-09-09T15:58:24.569Z" }, + { url = "https://files.pythonhosted.org/packages/3c/5f/d12834711962ad9c46af72f79bb31e73e416ee49d17f4c797f72c96b6ca5/numpy-2.3.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0edd58682a399824633b66885d699d7de982800053acf20be1eaa46d92009c54", size = 14352811, upload-time = "2025-09-09T15:58:26.416Z" }, + { url = "https://files.pythonhosted.org/packages/a1/0d/fdbec6629d97fd1bebed56cd742884e4eead593611bbe1abc3eb40d304b2/numpy-2.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:367ad5d8fbec5d9296d18478804a530f1191e24ab4d75ab408346ae88045d25e", size = 16702689, upload-time = "2025-09-09T15:58:28.831Z" }, + { url = "https://files.pythonhosted.org/packages/9b/09/0a35196dc5575adde1eb97ddfbc3e1687a814f905377621d18ca9bc2b7dd/numpy-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8f6ac61a217437946a1fa48d24c47c91a0c4f725237871117dea264982128097", size = 16133855, upload-time = "2025-09-09T15:58:31.349Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ca/c9de3ea397d576f1b6753eaa906d4cdef1bf97589a6d9825a349b4729cc2/numpy-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:179a42101b845a816d464b6fe9a845dfaf308fdfc7925387195570789bb2c970", size = 18652520, upload-time = "2025-09-09T15:58:33.762Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c2/e5ed830e08cd0196351db55db82f65bc0ab05da6ef2b72a836dcf1936d2f/numpy-2.3.3-cp314-cp314t-win32.whl", hash = "sha256:1250c5d3d2562ec4174bce2e3a1523041595f9b651065e4a4473f5f48a6bc8a5", size = 6515371, upload-time = "2025-09-09T15:58:36.04Z" }, + { url = "https://files.pythonhosted.org/packages/47/c7/b0f6b5b67f6788a0725f744496badbb604d226bf233ba716683ebb47b570/numpy-2.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b37a0b2e5935409daebe82c1e42274d30d9dd355852529eab91dab8dcca7419f", size = 13112576, upload-time = "2025-09-09T15:58:37.927Z" }, + { url = "https://files.pythonhosted.org/packages/06/b9/33bba5ff6fb679aa0b1f8a07e853f002a6b04b9394db3069a1270a7784ca/numpy-2.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:78c9f6560dc7e6b3990e32df7ea1a50bbd0e2a111e05209963f5ddcab7073b0b", size = 10545953, upload-time = "2025-09-09T15:58:40.576Z" }, +] + [[package]] name = "openapi-python-client" version = "0.26.0" @@ -777,6 +1092,42 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] +[[package]] +name = "pandas" +version = "2.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { 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/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" }, +] + +[[package]] +name = "parso" +version = "0.8.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205, upload-time = "2025-08-23T15:15:28.028Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" }, +] + [[package]] name = "pathspec" version = "0.12.1" @@ -786,6 +1137,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, ] +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +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 = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, +] + [[package]] name = "platformdirs" version = "4.4.0" @@ -804,6 +1167,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] +[[package]] +name = "policyengine" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "alembic" }, + { name = "blosc" }, + { name = "getpass4" }, + { name = "google-cloud-storage" }, + { name = "ipywidgets" }, + { name = "microdf-python" }, + { name = "pandas" }, + { name = "psycopg2-binary" }, + { name = "pydantic" }, + { name = "pymysql" }, + { name = "rich" }, + { name = "sqlalchemy" }, + { name = "sqlmodel" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/39/5cf9b8135f38d994b7da48f13639d3f1b4853b1d6b95ca858eaecfb152c4/policyengine-3.0.0.tar.gz", hash = "sha256:ba7ce61b7a82f8835b6768e5f7aea90f335a18119bab516b72305fc950b38eaa", size = 149539, upload-time = "2025-09-23T09:36:58.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/8e/458a1283f69b2ecb4c76e9acf288fff4d25f87b526bc2553ad65f68964cc/policyengine-3.0.0-py3-none-any.whl", hash = "sha256:55d7be28e9888329be84fc7e1a74d6d880c70f62852c1c3e80830464a3f9b88e", size = 54609, upload-time = "2025-09-23T09:36:56.872Z" }, +] + [[package]] name = "policyengine-api-full" version = "0.1.0" @@ -811,7 +1199,9 @@ source = { editable = "." } dependencies = [ { name = "opentelemetry-instrumentation-fastapi" }, { name = "opentelemetry-instrumentation-sqlalchemy" }, + { name = "policyengine" }, { name = "policyengine-fastapi" }, + { name = "psycopg2-binary" }, { name = "pydantic-settings" }, ] @@ -833,7 +1223,9 @@ requires-dist = [ { name = "openapi-python-client", marker = "extra == 'build'", specifier = ">=0.21.6" }, { name = "opentelemetry-instrumentation-fastapi", specifier = ">=0.51b0,<0.52" }, { name = "opentelemetry-instrumentation-sqlalchemy", specifier = ">=0.51b0,<0.52" }, + { name = "policyengine", specifier = ">=3.0.0" }, { name = "policyengine-fastapi", editable = "../../libs/policyengine-fastapi" }, + { name = "psycopg2-binary", specifier = ">=2.9.0" }, { name = "pydantic-settings", specifier = ">=2.7.1,<3.0.0" }, { name = "pyright", marker = "extra == 'build'", specifier = ">=1.1.401" }, { name = "pytest", marker = "extra == 'test'", specifier = ">=8.3.4" }, @@ -881,6 +1273,18 @@ requires-dist = [ ] provides-extras = ["test", "build"] +[[package]] +name = "prompt-toolkit" +version = "3.0.52" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, +] + [[package]] name = "proto-plus" version = "1.26.1" @@ -907,6 +1311,43 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9c/f2/80ffc4677aac1bc3519b26bc7f7f5de7fce0ee2f7e36e59e27d8beb32dd1/protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783", size = 169287, upload-time = "2025-08-14T21:21:23.515Z" }, ] +[[package]] +name = "psycopg2-binary" +version = "2.9.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/bdc8274dc0585090b4e3432267d7be4dfbfd8971c0fa59167c711105a6bf/psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", size = 385764, upload-time = "2024-10-16T11:24:58.126Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/30/d41d3ba765609c0763505d565c4d12d8f3c79793f0d0f044ff5a28bf395b/psycopg2_binary-2.9.10-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", size = 3044699, upload-time = "2024-10-16T11:21:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/35/44/257ddadec7ef04536ba71af6bc6a75ec05c5343004a7ec93006bee66c0bc/psycopg2_binary-2.9.10-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", size = 3275245, upload-time = "2024-10-16T11:21:51.989Z" }, + { url = "https://files.pythonhosted.org/packages/1b/11/48ea1cd11de67f9efd7262085588790a95d9dfcd9b8a687d46caf7305c1a/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", size = 2851631, upload-time = "2024-10-16T11:21:57.584Z" }, + { url = "https://files.pythonhosted.org/packages/62/e0/62ce5ee650e6c86719d621a761fe4bc846ab9eff8c1f12b1ed5741bf1c9b/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", size = 3082140, upload-time = "2024-10-16T11:22:02.005Z" }, + { url = "https://files.pythonhosted.org/packages/27/ce/63f946c098611f7be234c0dd7cb1ad68b0b5744d34f68062bb3c5aa510c8/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", size = 3264762, upload-time = "2024-10-16T11:22:06.412Z" }, + { url = "https://files.pythonhosted.org/packages/43/25/c603cd81402e69edf7daa59b1602bd41eb9859e2824b8c0855d748366ac9/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", size = 3020967, upload-time = "2024-10-16T11:22:11.583Z" }, + { url = "https://files.pythonhosted.org/packages/5f/d6/8708d8c6fca531057fa170cdde8df870e8b6a9b136e82b361c65e42b841e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", size = 2872326, upload-time = "2024-10-16T11:22:16.406Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712, upload-time = "2024-10-16T11:22:21.366Z" }, + { url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155, upload-time = "2024-10-16T11:22:25.684Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356, upload-time = "2024-10-16T11:22:30.562Z" }, + { url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224, upload-time = "2025-01-04T20:09:19.234Z" }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, +] + [[package]] name = "pyasn1" version = "0.6.1" @@ -1008,6 +1449,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, ] +[[package]] +name = "pymysql" +version = "1.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/ae/1fe3fcd9f959efa0ebe200b8de88b5a5ce3e767e38c7ac32fb179f16a388/pymysql-1.1.2.tar.gz", hash = "sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03", size = 48258, upload-time = "2025-08-24T12:55:55.146Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/4c/ad33b92b9864cbde84f259d5df035a6447f91891f5be77788e2a3892bce3/pymysql-1.1.2-py3-none-any.whl", hash = "sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9", size = 45300, upload-time = "2025-08-24T12:55:53.394Z" }, +] + +[[package]] +name = "pyperclip" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/99/25f4898cf420efb6f45f519de018f4faea5391114a8618b16736ef3029f1/pyperclip-1.10.0.tar.gz", hash = "sha256:180c8346b1186921c75dfd14d9048a6b5d46bfc499778811952c6dd6eb1ca6be", size = 12193, upload-time = "2025-09-18T00:54:00.384Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/bc/22540e73c5f5ae18f02924cd3954a6c9a4aa6b713c841a94c98335d333a1/pyperclip-1.10.0-py3-none-any.whl", hash = "sha256:596fbe55dc59263bff26e61d2afbe10223e2fccb5210c9c96a28d6887cfcc7ec", size = 11062, upload-time = "2025-09-18T00:53:59.252Z" }, +] + [[package]] name = "pyright" version = "1.1.405" @@ -1102,6 +1561,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, ] +[[package]] +name = "pytz" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, +] + [[package]] name = "pyyaml" version = "6.0.2" @@ -1334,6 +1802,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dd/b1/3af5104b716c420e40a6ea1b09886cae3a1b9f4538343875f637755cae5b/sqlmodel-0.0.22-py3-none-any.whl", hash = "sha256:a1ed13e28a1f4057cbf4ff6cdb4fc09e85702621d3259ba17b3c230bfb2f941b", size = 28276, upload-time = "2024-08-31T09:43:22.358Z" }, ] +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, +] + [[package]] name = "starlette" version = "0.46.2" @@ -1346,6 +1828,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037, upload-time = "2025-04-13T13:56:16.21Z" }, ] +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, +] + +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, +] + [[package]] name = "typer" version = "0.16.1" @@ -1382,6 +1885,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, ] +[[package]] +name = "tzdata" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, +] + [[package]] name = "urllib3" version = "2.5.0" @@ -1483,6 +1995,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/fa/a4f5c2046385492b2273213ef815bf71a0d4c1943b784fb904e184e30201/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db", size = 623315, upload-time = "2025-06-15T19:06:29.076Z" }, ] +[[package]] +name = "wcwidth" +version = "0.2.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605", size = 102293, upload-time = "2025-09-22T16:29:53.023Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", size = 37286, upload-time = "2025-09-22T16:29:51.641Z" }, +] + [[package]] name = "websockets" version = "15.0.1" @@ -1503,6 +2024,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, ] +[[package]] +name = "widgetsnbextension" +version = "4.0.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/53/2e0253c5efd69c9656b1843892052a31c36d37ad42812b5da45c62191f7e/widgetsnbextension-4.0.14.tar.gz", hash = "sha256:a3629b04e3edb893212df862038c7232f62973373869db5084aed739b437b5af", size = 1097428, upload-time = "2025-04-10T13:01:25.628Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/51/5447876806d1088a0f8f71e16542bf350918128d0a69437df26047c8e46f/widgetsnbextension-4.0.14-py3-none-any.whl", hash = "sha256:4875a9eaf72fbf5079dc372a51a9f268fc38d46f767cbf85c43a36da5cb9b575", size = 2196503, upload-time = "2025-04-10T13:01:23.086Z" }, +] + [[package]] name = "wrapt" version = "1.17.3" diff --git a/test.ipynb b/test.ipynb index 38271224..49f29451 100644 --- a/test.ipynb +++ b/test.ipynb @@ -22,10 +22,35 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "ebf1d5bf", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Warning: Invalid date 2021-06-31, using 2021-06-30\n", + "Warning: Invalid date 2021-06-31, using 2021-06-30\n", + "Warning: Invalid date 2021-06-31, using 2021-06-30\n", + "Warning: Invalid date 2021-06-31, using 2021-06-30\n" + ] + } + ], "source": [ "from policyengine.models.policyengine_uk import policyengine_uk_latest_version\n", "from policyengine.models.policyengine_us import policyengine_us_latest_version\n", @@ -33,6 +58,22 @@ "db.register_model_version(policyengine_uk_latest_version)\n", "db.register_model_version(policyengine_us_latest_version)" ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "c468407b", + "metadata": {}, + "outputs": [], + "source": [ + "from policyengine.utils.datasets import create_uk_dataset, create_us_dataset\n", + "\n", + "uk_dataset = create_uk_dataset()\n", + "us_dataset = create_us_dataset()\n", + "\n", + "db.set(uk_dataset)\n", + "db.set(us_dataset)" + ] } ], "metadata": { From fe555887b3cdd7847d6634ef8d84adc61d648c3e Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Fri, 26 Sep 2025 12:52:49 +0100 Subject: [PATCH 03/11] Rewrite policyengine-api-full to use db models --- Makefile | 3 +- libs/policyengine-fastapi/uv.lock | 282 +++--- projects/policyengine-api-full/pyproject.toml | 2 +- .../src/policyengine_api_full/api/__init__.py | 31 - .../src/policyengine_api_full/api/country.py | 522 ----------- .../api/data/regions/ca_regions.json | 3 - .../api/data/regions/il_regions.json | 3 - .../api/data/regions/ng_regions.json | 3 - .../api/data/regions/uk_regions.json | 7 - .../api/data/regions/us_regions.json | 56 -- .../time_periods/default_time_periods.json | 3 - .../data/time_periods/uk_time_periods.json | 5 - .../data/time_periods/us_time_periods.json | 5 - .../api/database_router.py | 424 --------- .../api/enums/__init__.py | 9 - .../api/enums/country_id.py | 11 - .../api/enums/entities.py | 24 - .../api/enums/entity_groups.py | 21 - .../api/enums/periods.py | 8 - .../api/enums/value_types.py | 10 - .../api/household/__init__.py | 21 - .../api/household/household.py | 54 -- .../api/household/user.py | 145 --- .../api/models/household.py | 93 -- .../api/models/metadata/__init__.py | 0 .../api/models/metadata/economy_options.py | 16 - .../api/models/metadata/entity.py | 16 - .../api/models/metadata/metadata_module.py | 28 - .../api/models/metadata/modeled_policies.py | 22 - .../api/models/metadata/parameter.py | 21 - .../api/models/metadata/variable.py | 36 - .../api/models/periods.py | 13 - .../api/routers/calculate.py | 32 - .../api/routers/metadata.py | 12 - .../api/utils/__init__.py | 8 - .../api/utils/constants.py | 33 - .../policyengine_api_full/api/utils/json.py | 19 - .../api/utils/metadata.py | 23 - .../src/policyengine_api_full/database.py | 26 +- .../src/policyengine_api_full/main.py | 124 ++- .../policyengine_api_full/routers/__init__.py | 1 + .../routers/baseline_variables.py | 90 ++ .../policyengine_api_full/routers/datasets.py | 158 ++++ .../policyengine_api_full/routers/dynamics.py | 84 ++ .../routers/model_versions.py | 84 ++ .../policyengine_api_full/routers/models.py | 84 ++ .../routers/parameters.py | 210 +++++ .../policyengine_api_full/routers/policies.py | 93 ++ .../routers/simulations.py | 116 +++ .../src/policyengine_api_full/schemas.py | 350 ++++++++ projects/policyengine-api-full/uv.lock | 844 +++++++++++++----- .../pyproject.toml | 4 +- projects/policyengine-api-simulation/uv.lock | 649 +++++++++----- projects/policyengine-api-tagger/uv.lock | 294 +++--- projects/policyengine-apis-integ/uv.lock | 97 +- 55 files changed, 2740 insertions(+), 2592 deletions(-) delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/__init__.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/country.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/ca_regions.json delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/il_regions.json delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/ng_regions.json delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/uk_regions.json delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/us_regions.json delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/data/time_periods/default_time_periods.json delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/data/time_periods/uk_time_periods.json delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/data/time_periods/us_time_periods.json delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/database_router.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/enums/__init__.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/enums/country_id.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/enums/entities.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/enums/entity_groups.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/enums/periods.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/enums/value_types.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/household/__init__.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/household/household.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/household/user.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/models/household.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/__init__.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/economy_options.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/entity.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/metadata_module.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/modeled_policies.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/parameter.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/variable.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/models/periods.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/routers/calculate.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/routers/metadata.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/utils/__init__.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/utils/constants.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/utils/json.py delete mode 100644 projects/policyengine-api-full/src/policyengine_api_full/api/utils/metadata.py create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/routers/__init__.py create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/routers/baseline_variables.py create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/routers/datasets.py create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/routers/dynamics.py create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/routers/model_versions.py create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/routers/models.py create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/routers/parameters.py create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/routers/policies.py create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/routers/simulations.py create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/schemas.py diff --git a/Makefile b/Makefile index cd52a925..dcb62f5e 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,8 @@ help: @echo " make init-gcp - Initialize GCP project (APIs, registry, bucket)" @echo "" @echo "Development:" - @echo " make dev - Start all services in development mode" + @echo " make dev - Start all services in development mode (Docker)" + @echo " make dev-local - Start services using local venvs (no Docker)" @echo " make up [service=x] - Start specific service or all services" @echo " make down - Stop all services" @echo " make logs [service=x] - Show logs for service" diff --git a/libs/policyengine-fastapi/uv.lock b/libs/policyengine-fastapi/uv.lock index 5b401c19..35084da8 100644 --- a/libs/policyengine-fastapi/uv.lock +++ b/libs/policyengine-fastapi/uv.lock @@ -13,29 +13,29 @@ wheels = [ [[package]] name = "anyio" -version = "4.10.0" +version = "4.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "sniffio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, ] [[package]] name = "asgiref" -version = "3.9.1" +version = "3.9.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/61/0aa957eec22ff70b830b22ff91f825e70e1ef732c06666a805730f28b36b/asgiref-3.9.1.tar.gz", hash = "sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142", size = 36870, upload-time = "2025-07-08T09:07:43.344Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/bf/0f3ecda32f1cb3bf1dca480aca08a7a8a3bdc4bed2343a103f30731565c9/asgiref-3.9.2.tar.gz", hash = "sha256:a0249afacb66688ef258ffe503528360443e2b9a8d8c4581b6ebefa58c841ef1", size = 36894, upload-time = "2025-09-23T15:00:55.136Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/3c/0464dcada90d5da0e71018c04a140ad6349558afb30b3051b4264cc5b965/asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c", size = 23790, upload-time = "2025-07-08T09:07:41.548Z" }, + { url = "https://files.pythonhosted.org/packages/c7/d1/69d02ce34caddb0a7ae088b84c356a625a93cd4ff57b2f97644c03fad905/asgiref-3.9.2-py3-none-any.whl", hash = "sha256:0b61526596219d70396548fc003635056856dba5d0d086f86476f10b33c75960", size = 23788, upload-time = "2025-09-23T15:00:53.627Z" }, ] [[package]] name = "black" -version = "25.1.0" +version = "25.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -43,14 +43,15 @@ dependencies = [ { name = "packaging" }, { name = "pathspec" }, { name = "platformdirs" }, + { name = "pytokens" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload-time = "2025-01-29T04:15:40.373Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/43/20b5c90612d7bdb2bdbcceeb53d588acca3bb8f0e4c5d5c751a2c8fdd55a/black-25.9.0.tar.gz", hash = "sha256:0474bca9a0dd1b51791fcc507a4e02078a1c63f6d4e4ae5544b9848c7adfb619", size = 648393, upload-time = "2025-09-19T00:27:37.758Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673, upload-time = "2025-01-29T05:37:20.574Z" }, - { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190, upload-time = "2025-01-29T05:37:22.106Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926, upload-time = "2025-01-29T04:18:58.564Z" }, - { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613, upload-time = "2025-01-29T04:19:27.63Z" }, - { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" }, + { url = "https://files.pythonhosted.org/packages/48/99/3acfea65f5e79f45472c45f87ec13037b506522719cd9d4ac86484ff51ac/black-25.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0172a012f725b792c358d57fe7b6b6e8e67375dd157f64fa7a3097b3ed3e2175", size = 1742165, upload-time = "2025-09-19T00:34:10.402Z" }, + { url = "https://files.pythonhosted.org/packages/3a/18/799285282c8236a79f25d590f0222dbd6850e14b060dfaa3e720241fd772/black-25.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3bec74ee60f8dfef564b573a96b8930f7b6a538e846123d5ad77ba14a8d7a64f", size = 1581259, upload-time = "2025-09-19T00:32:49.685Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ce/883ec4b6303acdeca93ee06b7622f1fa383c6b3765294824165d49b1a86b/black-25.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b756fc75871cb1bcac5499552d771822fd9db5a2bb8db2a7247936ca48f39831", size = 1655583, upload-time = "2025-09-19T00:30:44.505Z" }, + { url = "https://files.pythonhosted.org/packages/21/17/5c253aa80a0639ccc427a5c7144534b661505ae2b5a10b77ebe13fa25334/black-25.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:846d58e3ce7879ec1ffe816bb9df6d006cd9590515ed5d17db14e17666b2b357", size = 1343428, upload-time = "2025-09-19T00:32:13.839Z" }, + { url = "https://files.pythonhosted.org/packages/1b/46/863c90dcd3f9d41b109b7f19032ae0db021f0b2a81482ba0a1e28c84de86/black-25.9.0-py3-none-any.whl", hash = "sha256:474b34c1342cdc157d307b56c4c65bce916480c4a8f6551fdc6bf9b486a7c4ae", size = 203363, upload-time = "2025-09-19T00:27:35.724Z" }, ] [[package]] @@ -104,14 +105,14 @@ wheels = [ [[package]] name = "click" -version = "8.2.1" +version = "8.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, + { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, ] [[package]] @@ -125,55 +126,63 @@ wheels = [ [[package]] name = "coverage" -version = "7.10.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/14/70/025b179c993f019105b79575ac6edb5e084fb0f0e63f15cdebef4e454fb5/coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90", size = 823736, upload-time = "2025-08-29T15:35:16.668Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/e7/917e5953ea29a28c1057729c1d5af9084ab6d9c66217523fd0e10f14d8f6/coverage-7.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6", size = 217351, upload-time = "2025-08-29T15:33:45.438Z" }, - { url = "https://files.pythonhosted.org/packages/eb/86/2e161b93a4f11d0ea93f9bebb6a53f113d5d6e416d7561ca41bb0a29996b/coverage-7.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80", size = 217600, upload-time = "2025-08-29T15:33:47.269Z" }, - { url = "https://files.pythonhosted.org/packages/0e/66/d03348fdd8df262b3a7fb4ee5727e6e4936e39e2f3a842e803196946f200/coverage-7.10.6-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003", size = 248600, upload-time = "2025-08-29T15:33:48.953Z" }, - { url = "https://files.pythonhosted.org/packages/73/dd/508420fb47d09d904d962f123221bc249f64b5e56aa93d5f5f7603be475f/coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27", size = 251206, upload-time = "2025-08-29T15:33:50.697Z" }, - { url = "https://files.pythonhosted.org/packages/e9/1f/9020135734184f439da85c70ea78194c2730e56c2d18aee6e8ff1719d50d/coverage-7.10.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4", size = 252478, upload-time = "2025-08-29T15:33:52.303Z" }, - { url = "https://files.pythonhosted.org/packages/a4/a4/3d228f3942bb5a2051fde28c136eea23a761177dc4ff4ef54533164ce255/coverage-7.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d", size = 250637, upload-time = "2025-08-29T15:33:53.67Z" }, - { url = "https://files.pythonhosted.org/packages/36/e3/293dce8cdb9a83de971637afc59b7190faad60603b40e32635cbd15fbf61/coverage-7.10.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc", size = 248529, upload-time = "2025-08-29T15:33:55.022Z" }, - { url = "https://files.pythonhosted.org/packages/90/26/64eecfa214e80dd1d101e420cab2901827de0e49631d666543d0e53cf597/coverage-7.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc", size = 250143, upload-time = "2025-08-29T15:33:56.386Z" }, - { url = "https://files.pythonhosted.org/packages/3e/70/bd80588338f65ea5b0d97e424b820fb4068b9cfb9597fbd91963086e004b/coverage-7.10.6-cp313-cp313-win32.whl", hash = "sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e", size = 219770, upload-time = "2025-08-29T15:33:58.063Z" }, - { url = "https://files.pythonhosted.org/packages/a7/14/0b831122305abcc1060c008f6c97bbdc0a913ab47d65070a01dc50293c2b/coverage-7.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32", size = 220566, upload-time = "2025-08-29T15:33:59.766Z" }, - { url = "https://files.pythonhosted.org/packages/83/c6/81a83778c1f83f1a4a168ed6673eeedc205afb562d8500175292ca64b94e/coverage-7.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2", size = 219195, upload-time = "2025-08-29T15:34:01.191Z" }, - { url = "https://files.pythonhosted.org/packages/d7/1c/ccccf4bf116f9517275fa85047495515add43e41dfe8e0bef6e333c6b344/coverage-7.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b", size = 218059, upload-time = "2025-08-29T15:34:02.91Z" }, - { url = "https://files.pythonhosted.org/packages/92/97/8a3ceff833d27c7492af4f39d5da6761e9ff624831db9e9f25b3886ddbca/coverage-7.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393", size = 218287, upload-time = "2025-08-29T15:34:05.106Z" }, - { url = "https://files.pythonhosted.org/packages/92/d8/50b4a32580cf41ff0423777a2791aaf3269ab60c840b62009aec12d3970d/coverage-7.10.6-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27", size = 259625, upload-time = "2025-08-29T15:34:06.575Z" }, - { url = "https://files.pythonhosted.org/packages/7e/7e/6a7df5a6fb440a0179d94a348eb6616ed4745e7df26bf2a02bc4db72c421/coverage-7.10.6-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df", size = 261801, upload-time = "2025-08-29T15:34:08.006Z" }, - { url = "https://files.pythonhosted.org/packages/3a/4c/a270a414f4ed5d196b9d3d67922968e768cd971d1b251e1b4f75e9362f75/coverage-7.10.6-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb", size = 264027, upload-time = "2025-08-29T15:34:09.806Z" }, - { url = "https://files.pythonhosted.org/packages/9c/8b/3210d663d594926c12f373c5370bf1e7c5c3a427519a8afa65b561b9a55c/coverage-7.10.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282", size = 261576, upload-time = "2025-08-29T15:34:11.585Z" }, - { url = "https://files.pythonhosted.org/packages/72/d0/e1961eff67e9e1dba3fc5eb7a4caf726b35a5b03776892da8d79ec895775/coverage-7.10.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4", size = 259341, upload-time = "2025-08-29T15:34:13.159Z" }, - { url = "https://files.pythonhosted.org/packages/3a/06/d6478d152cd189b33eac691cba27a40704990ba95de49771285f34a5861e/coverage-7.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21", size = 260468, upload-time = "2025-08-29T15:34:14.571Z" }, - { url = "https://files.pythonhosted.org/packages/ed/73/737440247c914a332f0b47f7598535b29965bf305e19bbc22d4c39615d2b/coverage-7.10.6-cp313-cp313t-win32.whl", hash = "sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0", size = 220429, upload-time = "2025-08-29T15:34:16.394Z" }, - { url = "https://files.pythonhosted.org/packages/bd/76/b92d3214740f2357ef4a27c75a526eb6c28f79c402e9f20a922c295c05e2/coverage-7.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5", size = 221493, upload-time = "2025-08-29T15:34:17.835Z" }, - { url = "https://files.pythonhosted.org/packages/fc/8e/6dcb29c599c8a1f654ec6cb68d76644fe635513af16e932d2d4ad1e5ac6e/coverage-7.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b", size = 219757, upload-time = "2025-08-29T15:34:19.248Z" }, - { url = "https://files.pythonhosted.org/packages/d3/aa/76cf0b5ec00619ef208da4689281d48b57f2c7fde883d14bf9441b74d59f/coverage-7.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6008a021907be8c4c02f37cdc3ffb258493bdebfeaf9a839f9e71dfdc47b018e", size = 217331, upload-time = "2025-08-29T15:34:20.846Z" }, - { url = "https://files.pythonhosted.org/packages/65/91/8e41b8c7c505d398d7730206f3cbb4a875a35ca1041efc518051bfce0f6b/coverage-7.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5e75e37f23eb144e78940b40395b42f2321951206a4f50e23cfd6e8a198d3ceb", size = 217607, upload-time = "2025-08-29T15:34:22.433Z" }, - { url = "https://files.pythonhosted.org/packages/87/7f/f718e732a423d442e6616580a951b8d1ec3575ea48bcd0e2228386805e79/coverage-7.10.6-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0f7cb359a448e043c576f0da00aa8bfd796a01b06aa610ca453d4dde09cc1034", size = 248663, upload-time = "2025-08-29T15:34:24.425Z" }, - { url = "https://files.pythonhosted.org/packages/e6/52/c1106120e6d801ac03e12b5285e971e758e925b6f82ee9b86db3aa10045d/coverage-7.10.6-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c68018e4fc4e14b5668f1353b41ccf4bc83ba355f0e1b3836861c6f042d89ac1", size = 251197, upload-time = "2025-08-29T15:34:25.906Z" }, - { url = "https://files.pythonhosted.org/packages/3d/ec/3a8645b1bb40e36acde9c0609f08942852a4af91a937fe2c129a38f2d3f5/coverage-7.10.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd4b2b0707fc55afa160cd5fc33b27ccbf75ca11d81f4ec9863d5793fc6df56a", size = 252551, upload-time = "2025-08-29T15:34:27.337Z" }, - { url = "https://files.pythonhosted.org/packages/a1/70/09ecb68eeb1155b28a1d16525fd3a9b65fbe75337311a99830df935d62b6/coverage-7.10.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cec13817a651f8804a86e4f79d815b3b28472c910e099e4d5a0e8a3b6a1d4cb", size = 250553, upload-time = "2025-08-29T15:34:29.065Z" }, - { url = "https://files.pythonhosted.org/packages/c6/80/47df374b893fa812e953b5bc93dcb1427a7b3d7a1a7d2db33043d17f74b9/coverage-7.10.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f2a6a8e06bbda06f78739f40bfb56c45d14eb8249d0f0ea6d4b3d48e1f7c695d", size = 248486, upload-time = "2025-08-29T15:34:30.897Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/9f98640979ecee1b0d1a7164b589de720ddf8100d1747d9bbdb84be0c0fb/coverage-7.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:081b98395ced0d9bcf60ada7661a0b75f36b78b9d7e39ea0790bb4ed8da14747", size = 249981, upload-time = "2025-08-29T15:34:32.365Z" }, - { url = "https://files.pythonhosted.org/packages/1f/55/eeb6603371e6629037f47bd25bef300387257ed53a3c5fdb159b7ac8c651/coverage-7.10.6-cp314-cp314-win32.whl", hash = "sha256:6937347c5d7d069ee776b2bf4e1212f912a9f1f141a429c475e6089462fcecc5", size = 220054, upload-time = "2025-08-29T15:34:34.124Z" }, - { url = "https://files.pythonhosted.org/packages/15/d1/a0912b7611bc35412e919a2cd59ae98e7ea3b475e562668040a43fb27897/coverage-7.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:adec1d980fa07e60b6ef865f9e5410ba760e4e1d26f60f7e5772c73b9a5b0713", size = 220851, upload-time = "2025-08-29T15:34:35.651Z" }, - { url = "https://files.pythonhosted.org/packages/ef/2d/11880bb8ef80a45338e0b3e0725e4c2d73ffbb4822c29d987078224fd6a5/coverage-7.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:a80f7aef9535442bdcf562e5a0d5a5538ce8abe6bb209cfbf170c462ac2c2a32", size = 219429, upload-time = "2025-08-29T15:34:37.16Z" }, - { url = "https://files.pythonhosted.org/packages/83/c0/1f00caad775c03a700146f55536ecd097a881ff08d310a58b353a1421be0/coverage-7.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0de434f4fbbe5af4fa7989521c655c8c779afb61c53ab561b64dcee6149e4c65", size = 218080, upload-time = "2025-08-29T15:34:38.919Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c4/b1c5d2bd7cc412cbeb035e257fd06ed4e3e139ac871d16a07434e145d18d/coverage-7.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6e31b8155150c57e5ac43ccd289d079eb3f825187d7c66e755a055d2c85794c6", size = 218293, upload-time = "2025-08-29T15:34:40.425Z" }, - { url = "https://files.pythonhosted.org/packages/3f/07/4468d37c94724bf6ec354e4ec2f205fda194343e3e85fd2e59cec57e6a54/coverage-7.10.6-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:98cede73eb83c31e2118ae8d379c12e3e42736903a8afcca92a7218e1f2903b0", size = 259800, upload-time = "2025-08-29T15:34:41.996Z" }, - { url = "https://files.pythonhosted.org/packages/82/d8/f8fb351be5fee31690cd8da768fd62f1cfab33c31d9f7baba6cd8960f6b8/coverage-7.10.6-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f863c08f4ff6b64fa8045b1e3da480f5374779ef187f07b82e0538c68cb4ff8e", size = 261965, upload-time = "2025-08-29T15:34:43.61Z" }, - { url = "https://files.pythonhosted.org/packages/e8/70/65d4d7cfc75c5c6eb2fed3ee5cdf420fd8ae09c4808723a89a81d5b1b9c3/coverage-7.10.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b38261034fda87be356f2c3f42221fdb4171c3ce7658066ae449241485390d5", size = 264220, upload-time = "2025-08-29T15:34:45.387Z" }, - { url = "https://files.pythonhosted.org/packages/98/3c/069df106d19024324cde10e4ec379fe2fb978017d25e97ebee23002fbadf/coverage-7.10.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e93b1476b79eae849dc3872faeb0bf7948fd9ea34869590bc16a2a00b9c82a7", size = 261660, upload-time = "2025-08-29T15:34:47.288Z" }, - { url = "https://files.pythonhosted.org/packages/fc/8a/2974d53904080c5dc91af798b3a54a4ccb99a45595cc0dcec6eb9616a57d/coverage-7.10.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ff8a991f70f4c0cf53088abf1e3886edcc87d53004c7bb94e78650b4d3dac3b5", size = 259417, upload-time = "2025-08-29T15:34:48.779Z" }, - { url = "https://files.pythonhosted.org/packages/30/38/9616a6b49c686394b318974d7f6e08f38b8af2270ce7488e879888d1e5db/coverage-7.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ac765b026c9f33044419cbba1da913cfb82cca1b60598ac1c7a5ed6aac4621a0", size = 260567, upload-time = "2025-08-29T15:34:50.718Z" }, - { url = "https://files.pythonhosted.org/packages/76/16/3ed2d6312b371a8cf804abf4e14895b70e4c3491c6e53536d63fd0958a8d/coverage-7.10.6-cp314-cp314t-win32.whl", hash = "sha256:441c357d55f4936875636ef2cfb3bee36e466dcf50df9afbd398ce79dba1ebb7", size = 220831, upload-time = "2025-08-29T15:34:52.653Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e5/d38d0cb830abede2adb8b147770d2a3d0e7fecc7228245b9b1ae6c24930a/coverage-7.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:073711de3181b2e204e4870ac83a7c4853115b42e9cd4d145f2231e12d670930", size = 221950, upload-time = "2025-08-29T15:34:54.212Z" }, - { url = "https://files.pythonhosted.org/packages/f4/51/e48e550f6279349895b0ffcd6d2a690e3131ba3a7f4eafccc141966d4dea/coverage-7.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:137921f2bac5559334ba66122b753db6dc5d1cf01eb7b64eb412bb0d064ef35b", size = 219969, upload-time = "2025-08-29T15:34:55.83Z" }, - { url = "https://files.pythonhosted.org/packages/44/0c/50db5379b615854b5cf89146f8f5bd1d5a9693d7f3a987e269693521c404/coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3", size = 208986, upload-time = "2025-08-29T15:35:14.506Z" }, +version = "7.10.7" +source = { registry = "https://pypi.org/simple" } +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/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/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]] @@ -236,16 +245,16 @@ standard = [ [[package]] name = "fastapi-cli" -version = "0.0.11" +version = "0.0.13" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "rich-toolkit" }, { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/23/08/0af729f6231ebdc17a0356397f966838cbe2efa38529951e24017c7435d5/fastapi_cli-0.0.11.tar.gz", hash = "sha256:4f01d751c14d3d2760339cca0f45e81d816218cae8174d1dc757b5375868cde5", size = 17550, upload-time = "2025-09-09T12:50:38.917Z" } +sdist = { url = "https://files.pythonhosted.org/packages/32/4e/3f61850012473b097fc5297d681bd85788e186fadb8555b67baf4c7707f4/fastapi_cli-0.0.13.tar.gz", hash = "sha256:312addf3f57ba7139457cf0d345c03e2170cc5a034057488259c33cd7e494529", size = 17780, upload-time = "2025-09-20T16:37:31.089Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/8f/9e3ad391d1c4183de55c256b481899bbd7bbd06d389e4986741bb289fe94/fastapi_cli-0.0.11-py3-none-any.whl", hash = "sha256:bcdd1123c6077c7466452b9490ca47821f00eb784d58496674793003f9f8e33a", size = 11095, upload-time = "2025-09-09T12:50:37.658Z" }, + { url = "https://files.pythonhosted.org/packages/08/36/7432750f3638324b055496d2c952000bea824259fca70df5577a6a3c172f/fastapi_cli-0.0.13-py3-none-any.whl", hash = "sha256:219b73ccfde7622559cef1d43197da928516acb4f21f2ec69128c4b90057baba", size = 11142, upload-time = "2025-09-20T16:37:29.695Z" }, ] [package.optional-dependencies] @@ -256,7 +265,7 @@ standard = [ [[package]] name = "fastapi-cloud-cli" -version = "0.1.5" +version = "0.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -267,9 +276,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/2e/3b6e5016affc310e5109bc580f760586eabecea0c8a7ab067611cd849ac0/fastapi_cloud_cli-0.1.5.tar.gz", hash = "sha256:341ee585eb731a6d3c3656cb91ad38e5f39809bf1a16d41de1333e38635a7937", size = 22710, upload-time = "2025-07-28T13:30:48.216Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/38/1971f9dc8141e359d2435e6fae8bb228632adc55cff00cd00efed2a98456/fastapi_cloud_cli-0.2.1.tar.gz", hash = "sha256:aa22a4b867bf53165b6551d2f4eb21b079bad4aa74047cb889acf941e34699d9", size = 23676, upload-time = "2025-09-25T13:53:32.901Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/a6/5aa862489a2918a096166fd98d9fe86b7fd53c607678b3fa9d8c432d88d5/fastapi_cloud_cli-0.1.5-py3-none-any.whl", hash = "sha256:d80525fb9c0e8af122370891f9fa83cf5d496e4ad47a8dd26c0496a6c85a012a", size = 18992, upload-time = "2025-07-28T13:30:47.427Z" }, + { url = "https://files.pythonhosted.org/packages/3b/1f/5fa06afce6e4bb7fc7e54651236bad3b849340480967c54cbd7c13563c3f/fastapi_cloud_cli-0.2.1-py3-none-any.whl", hash = "sha256:245447bfb17b01ae5f7bc15dec0833bce85381ecf34532e8fa4bcf279ad1c361", size = 19894, upload-time = "2025-09-25T13:53:31.635Z" }, ] [[package]] @@ -376,34 +385,37 @@ wheels = [ [[package]] name = "grpcio" -version = "1.74.0" +version = "1.75.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/38/b4/35feb8f7cab7239c5b94bd2db71abb3d6adb5f335ad8f131abb6060840b6/grpcio-1.74.0.tar.gz", hash = "sha256:80d1f4fbb35b0742d3e3d3bb654b7381cd5f015f8497279a1e9c21ba623e01b1", size = 12756048, upload-time = "2025-07-24T18:54:23.039Z" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/88/fe2844eefd3d2188bc0d7a2768c6375b46dfd96469ea52d8aeee8587d7e0/grpcio-1.75.0.tar.gz", hash = "sha256:b989e8b09489478c2d19fecc744a298930f40d8b27c3638afbfe84d22f36ce4e", size = 12722485, upload-time = "2025-09-16T09:20:21.731Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/d8/1004a5f468715221450e66b051c839c2ce9a985aa3ee427422061fcbb6aa/grpcio-1.74.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:2bc2d7d8d184e2362b53905cb1708c84cb16354771c04b490485fa07ce3a1d89", size = 5449488, upload-time = "2025-07-24T18:53:41.174Z" }, - { url = "https://files.pythonhosted.org/packages/94/0e/33731a03f63740d7743dced423846c831d8e6da808fcd02821a4416df7fa/grpcio-1.74.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:c14e803037e572c177ba54a3e090d6eb12efd795d49327c5ee2b3bddb836bf01", size = 10974059, upload-time = "2025-07-24T18:53:43.066Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c6/3d2c14d87771a421205bdca991467cfe473ee4c6a1231c1ede5248c62ab8/grpcio-1.74.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f6ec94f0e50eb8fa1744a731088b966427575e40c2944a980049798b127a687e", size = 5945647, upload-time = "2025-07-24T18:53:45.269Z" }, - { url = "https://files.pythonhosted.org/packages/c5/83/5a354c8aaff58594eef7fffebae41a0f8995a6258bbc6809b800c33d4c13/grpcio-1.74.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:566b9395b90cc3d0d0c6404bc8572c7c18786ede549cdb540ae27b58afe0fb91", size = 6626101, upload-time = "2025-07-24T18:53:47.015Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ca/4fdc7bf59bf6994aa45cbd4ef1055cd65e2884de6113dbd49f75498ddb08/grpcio-1.74.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1ea6176d7dfd5b941ea01c2ec34de9531ba494d541fe2057c904e601879f249", size = 6182562, upload-time = "2025-07-24T18:53:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/fd/48/2869e5b2c1922583686f7ae674937986807c2f676d08be70d0a541316270/grpcio-1.74.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:64229c1e9cea079420527fa8ac45d80fc1e8d3f94deaa35643c381fa8d98f362", size = 6303425, upload-time = "2025-07-24T18:53:50.847Z" }, - { url = "https://files.pythonhosted.org/packages/a6/0e/bac93147b9a164f759497bc6913e74af1cb632c733c7af62c0336782bd38/grpcio-1.74.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:0f87bddd6e27fc776aacf7ebfec367b6d49cad0455123951e4488ea99d9b9b8f", size = 6996533, upload-time = "2025-07-24T18:53:52.747Z" }, - { url = "https://files.pythonhosted.org/packages/84/35/9f6b2503c1fd86d068b46818bbd7329db26a87cdd8c01e0d1a9abea1104c/grpcio-1.74.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3b03d8f2a07f0fea8c8f74deb59f8352b770e3900d143b3d1475effcb08eec20", size = 6491489, upload-time = "2025-07-24T18:53:55.06Z" }, - { url = "https://files.pythonhosted.org/packages/75/33/a04e99be2a82c4cbc4039eb3a76f6c3632932b9d5d295221389d10ac9ca7/grpcio-1.74.0-cp313-cp313-win32.whl", hash = "sha256:b6a73b2ba83e663b2480a90b82fdae6a7aa6427f62bf43b29912c0cfd1aa2bfa", size = 3805811, upload-time = "2025-07-24T18:53:56.798Z" }, - { url = "https://files.pythonhosted.org/packages/34/80/de3eb55eb581815342d097214bed4c59e806b05f1b3110df03b2280d6dfd/grpcio-1.74.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd3c71aeee838299c5887230b8a1822795325ddfea635edd82954c1eaa831e24", size = 4489214, upload-time = "2025-07-24T18:53:59.771Z" }, + { url = "https://files.pythonhosted.org/packages/00/64/dbce0ffb6edaca2b292d90999dd32a3bd6bc24b5b77618ca28440525634d/grpcio-1.75.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:1bb78d052948d8272c820bb928753f16a614bb2c42fbf56ad56636991b427518", size = 5666860, upload-time = "2025-09-16T09:19:25.417Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e6/da02c8fa882ad3a7f868d380bb3da2c24d35dd983dd12afdc6975907a352/grpcio-1.75.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:9dc4a02796394dd04de0b9673cb79a78901b90bb16bf99ed8cb528c61ed9372e", size = 11455148, upload-time = "2025-09-16T09:19:28.615Z" }, + { url = "https://files.pythonhosted.org/packages/ba/a0/84f87f6c2cf2a533cfce43b2b620eb53a51428ec0c8fe63e5dd21d167a70/grpcio-1.75.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:437eeb16091d31498585d73b133b825dc80a8db43311e332c08facf820d36894", size = 6243865, upload-time = "2025-09-16T09:19:31.342Z" }, + { url = "https://files.pythonhosted.org/packages/be/12/53da07aa701a4839dd70d16e61ce21ecfcc9e929058acb2f56e9b2dd8165/grpcio-1.75.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:c2c39984e846bd5da45c5f7bcea8fafbe47c98e1ff2b6f40e57921b0c23a52d0", size = 6915102, upload-time = "2025-09-16T09:19:33.658Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c0/7eaceafd31f52ec4bf128bbcf36993b4bc71f64480f3687992ddd1a6e315/grpcio-1.75.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38d665f44b980acdbb2f0e1abf67605ba1899f4d2443908df9ec8a6f26d2ed88", size = 6432042, upload-time = "2025-09-16T09:19:36.583Z" }, + { url = "https://files.pythonhosted.org/packages/6b/12/a2ce89a9f4fc52a16ed92951f1b05f53c17c4028b3db6a4db7f08332bee8/grpcio-1.75.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e8e752ab5cc0a9c5b949808c000ca7586223be4f877b729f034b912364c3964", size = 7062984, upload-time = "2025-09-16T09:19:39.163Z" }, + { url = "https://files.pythonhosted.org/packages/55/a6/2642a9b491e24482d5685c0f45c658c495a5499b43394846677abed2c966/grpcio-1.75.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3a6788b30aa8e6f207c417874effe3f79c2aa154e91e78e477c4825e8b431ce0", size = 8001212, upload-time = "2025-09-16T09:19:41.726Z" }, + { url = "https://files.pythonhosted.org/packages/19/20/530d4428750e9ed6ad4254f652b869a20a40a276c1f6817b8c12d561f5ef/grpcio-1.75.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc33e67cab6141c54e75d85acd5dec616c5095a957ff997b4330a6395aa9b51", size = 7457207, upload-time = "2025-09-16T09:19:44.368Z" }, + { url = "https://files.pythonhosted.org/packages/e2/6f/843670007e0790af332a21468d10059ea9fdf97557485ae633b88bd70efc/grpcio-1.75.0-cp313-cp313-win32.whl", hash = "sha256:c8cfc780b7a15e06253aae5f228e1e84c0d3c4daa90faf5bc26b751174da4bf9", size = 3934235, upload-time = "2025-09-16T09:19:46.815Z" }, + { url = "https://files.pythonhosted.org/packages/4b/92/c846b01b38fdf9e2646a682b12e30a70dc7c87dfe68bd5e009ee1501c14b/grpcio-1.75.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c91d5b16eff3cbbe76b7a1eaaf3d91e7a954501e9d4f915554f87c470475c3d", size = 4637558, upload-time = "2025-09-16T09:19:49.698Z" }, ] [[package]] name = "grpcio-status" -version = "1.74.0" +version = "1.75.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, { name = "grpcio" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/93/22/238c5f01e6837df54494deb08d5c772bc3f5bf5fb80a15dce254892d1a81/grpcio_status-1.74.0.tar.gz", hash = "sha256:c58c1b24aa454e30f1fc6a7e0dbbc194c54a408143971a94b5f4e40bb5831432", size = 13662, upload-time = "2025-07-24T19:01:56.874Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/8a/2e45ec0512d4ce9afa136c6e4186d063721b5b4c192eec7536ce6b7ba615/grpcio_status-1.75.0.tar.gz", hash = "sha256:69d5b91be1b8b926f086c1c483519a968c14640773a0ccdd6c04282515dbedf7", size = 13646, upload-time = "2025-09-16T09:24:51.069Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/aa/1b1fe7d8ab699e1ec26d3a36b91d3df9f83a30abc07d4c881d0296b17b67/grpcio_status-1.74.0-py3-none-any.whl", hash = "sha256:52cdbd759a6760fc8f668098a03f208f493dd5c76bf8e02598bbbaf1f6fc2876", size = 14425, upload-time = "2025-07-24T19:01:19.963Z" }, + { url = "https://files.pythonhosted.org/packages/2b/24/d536f0a0fda3a3eeb334893e5fb9d567c2777de6a5384413f71b35cfd0e5/grpcio_status-1.75.0-py3-none-any.whl", hash = "sha256:de62557ef97b7e19c3ce6da19793a12c5f6c1fbbb918d233d9671aba9d9e1d78", size = 14424, upload-time = "2025-09-16T09:23:33.843Z" }, ] [[package]] @@ -837,16 +849,16 @@ wheels = [ [[package]] name = "protobuf" -version = "6.32.0" +version = "6.32.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c0/df/fb4a8eeea482eca989b51cffd274aac2ee24e825f0bf3cbce5281fa1567b/protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2", size = 440614, upload-time = "2025-08-14T21:21:25.015Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/a4/cc17347aa2897568beece2e674674359f911d6fe21b0b8d6268cd42727ac/protobuf-6.32.1.tar.gz", hash = "sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d", size = 440635, upload-time = "2025-09-11T21:38:42.935Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/18/df8c87da2e47f4f1dcc5153a81cd6bca4e429803f4069a299e236e4dd510/protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741", size = 424409, upload-time = "2025-08-14T21:21:12.366Z" }, - { url = "https://files.pythonhosted.org/packages/e1/59/0a820b7310f8139bd8d5a9388e6a38e1786d179d6f33998448609296c229/protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e", size = 435735, upload-time = "2025-08-14T21:21:15.046Z" }, - { url = "https://files.pythonhosted.org/packages/cc/5b/0d421533c59c789e9c9894683efac582c06246bf24bb26b753b149bd88e4/protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0", size = 426449, upload-time = "2025-08-14T21:21:16.687Z" }, - { url = "https://files.pythonhosted.org/packages/ec/7b/607764ebe6c7a23dcee06e054fd1de3d5841b7648a90fd6def9a3bb58c5e/protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1", size = 322869, upload-time = "2025-08-14T21:21:18.282Z" }, - { url = "https://files.pythonhosted.org/packages/40/01/2e730bd1c25392fc32e3268e02446f0d77cb51a2c3a8486b1798e34d5805/protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c", size = 322009, upload-time = "2025-08-14T21:21:19.893Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f2/80ffc4677aac1bc3519b26bc7f7f5de7fce0ee2f7e36e59e27d8beb32dd1/protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783", size = 169287, upload-time = "2025-08-14T21:21:23.515Z" }, + { url = "https://files.pythonhosted.org/packages/c0/98/645183ea03ab3995d29086b8bf4f7562ebd3d10c9a4b14ee3f20d47cfe50/protobuf-6.32.1-cp310-abi3-win32.whl", hash = "sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085", size = 424411, upload-time = "2025-09-11T21:38:27.427Z" }, + { url = "https://files.pythonhosted.org/packages/8c/f3/6f58f841f6ebafe076cebeae33fc336e900619d34b1c93e4b5c97a81fdfa/protobuf-6.32.1-cp310-abi3-win_amd64.whl", hash = "sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1", size = 435738, upload-time = "2025-09-11T21:38:30.959Z" }, + { url = "https://files.pythonhosted.org/packages/10/56/a8a3f4e7190837139e68c7002ec749190a163af3e330f65d90309145a210/protobuf-6.32.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281", size = 426454, upload-time = "2025-09-11T21:38:34.076Z" }, + { url = "https://files.pythonhosted.org/packages/3f/be/8dd0a927c559b37d7a6c8ab79034fd167dcc1f851595f2e641ad62be8643/protobuf-6.32.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4", size = 322874, upload-time = "2025-09-11T21:38:35.509Z" }, + { url = "https://files.pythonhosted.org/packages/5c/f6/88d77011b605ef979aace37b7703e4eefad066f7e84d935e5a696515c2dd/protobuf-6.32.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710", size = 322013, upload-time = "2025-09-11T21:38:37.017Z" }, + { url = "https://files.pythonhosted.org/packages/97/b7/15cc7d93443d6c6a84626ae3258a91f4c6ac8c0edd5df35ea7658f71b79c/protobuf-6.32.1-py3-none-any.whl", hash = "sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346", size = 169289, upload-time = "2025-09-11T21:38:41.234Z" }, ] [[package]] @@ -872,7 +884,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.11.7" +version = "2.11.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -880,9 +892,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/5d/09a551ba512d7ca404d785072700d3f6727a02f6f3c24ecfd081c7cf0aa8/pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2", size = 788495, upload-time = "2025-09-13T11:26:39.325Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d3/108f2006987c58e76691d5ae5d200dd3e0f532cb4e5fa3560751c3a1feba/pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2", size = 444855, upload-time = "2025-09-13T11:26:36.909Z" }, ] [package.optional-dependencies] @@ -967,14 +979,14 @@ wheels = [ [[package]] name = "pytest-asyncio" -version = "1.1.0" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -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" } +sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157, upload-time = "2025-07-16T04:29:24.929Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" }, ] [[package]] @@ -1019,20 +1031,48 @@ wheels = [ ] [[package]] -name = "pyyaml" -version = "6.0.2" +name = "pytokens" +version = "0.1.10" 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" } +sdist = { url = "https://files.pythonhosted.org/packages/30/5f/e959a442435e24f6fb5a01aec6c657079ceaca1b3baf18561c3728d681da/pytokens-0.1.10.tar.gz", hash = "sha256:c9a4bfa0be1d26aebce03e6884ba454e842f186a59ea43a6d3b25af58223c044", size = 12171, upload-time = "2025-02-19T14:51:22.001Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, - { 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/60/e5/63bed382f6a7a5ba70e7e132b8b7b8abbcf4888ffa6be4877698dcfbed7d/pytokens-0.1.10-py3-none-any.whl", hash = "sha256:db7b72284e480e69fb085d9f251f66b3d2df8b7166059261258ff35f50fb711b", size = 12046, upload-time = "2025-02-19T14:51:18.694Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] [[package]] @@ -1123,15 +1163,15 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.37.1" +version = "2.39.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/be/ffc232c32d0be18f8e4eff7a22dffc1f1fef2894703d64cc281a80e75da6/sentry_sdk-2.37.1.tar.gz", hash = "sha256:531751da91aa62a909b42a7be155b41f6bb0de9df6ae98441d23b95de2f98475", size = 346235, upload-time = "2025-09-09T13:48:27.137Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/72/43294fa4bdd75c51610b5104a3ff834459ba653abb415150aa7826a249dd/sentry_sdk-2.39.0.tar.gz", hash = "sha256:8c185854d111f47f329ab6bc35993f28f7a6b7114db64aa426b326998cfa14e9", size = 348556, upload-time = "2025-09-25T09:15:39.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/c3/cba447ab531331d165d9003c04473be944a308ad916ca2345b5ef1969ed9/sentry_sdk-2.37.1-py2.py3-none-any.whl", hash = "sha256:baaaea6608ed3a639766a69ded06b254b106d32ad9d180bdbe58f3db9364592b", size = 368307, upload-time = "2025-09-09T13:48:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/dd/44/4356cc64246ba7b2b920f7c97a85c3c52748e213e250b512ee8152eb559d/sentry_sdk-2.39.0-py2.py3-none-any.whl", hash = "sha256:ba655ca5e57b41569b18e2a5552cb3375209760a5d332cdd87c6c3f28f729602", size = 370851, upload-time = "2025-09-25T09:15:36.35Z" }, ] [[package]] @@ -1200,7 +1240,7 @@ wheels = [ [[package]] name = "typer" -version = "0.17.4" +version = "0.19.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -1208,9 +1248,9 @@ dependencies = [ { name = "shellingham" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/92/e8/2a73ccf9874ec4c7638f172efc8972ceab13a0e3480b389d6ed822f7a822/typer-0.17.4.tar.gz", hash = "sha256:b77dc07d849312fd2bb5e7f20a7af8985c7ec360c45b051ed5412f64d8dc1580", size = 103734, upload-time = "2025-09-05T18:14:40.746Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/ca/950278884e2ca20547ff3eb109478c6baf6b8cf219318e6bc4f666fad8e8/typer-0.19.2.tar.gz", hash = "sha256:9ad824308ded0ad06cc716434705f691d4ee0bfd0fb081839d2e426860e7fdca", size = 104755, upload-time = "2025-09-23T09:47:48.256Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/72/6b3e70d32e89a5cbb6a4513726c1ae8762165b027af569289e19ec08edd8/typer-0.17.4-py3-none-any.whl", hash = "sha256:015534a6edaa450e7007eba705d5c18c3349dcea50a6ad79a5ed530967575824", size = 46643, upload-time = "2025-09-05T18:14:39.166Z" }, + { url = "https://files.pythonhosted.org/packages/00/22/35617eee79080a5d071d0f14ad698d325ee6b3bf824fc0467c03b30e7fa8/typer-0.19.2-py3-none-any.whl", hash = "sha256:755e7e19670ffad8283db353267cb81ef252f595aa6834a0d1ca9312d9326cb9", size = 46748, upload-time = "2025-09-23T09:47:46.777Z" }, ] [[package]] @@ -1245,15 +1285,15 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.35.0" +version = "0.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } +sdist = { url = "https://files.pythonhosted.org/packages/71/57/1616c8274c3442d802621abf5deb230771c7a0fec9414cb6763900eb3868/uvicorn-0.37.0.tar.gz", hash = "sha256:4115c8add6d3fd536c8ee77f0e14a7fd2ebba939fed9b02583a97f80648f9e13", size = 80367, upload-time = "2025-09-23T13:33:47.486Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, + { url = "https://files.pythonhosted.org/packages/85/cd/584a2ceb5532af99dd09e50919e3615ba99aa127e9850eafe5f31ddfdb9a/uvicorn-0.37.0-py3-none-any.whl", hash = "sha256:913b2b88672343739927ce381ff9e2ad62541f9f8289664fa1d1d3803fa2ce6c", size = 67976, upload-time = "2025-09-23T13:33:45.842Z" }, ] [package.optional-dependencies] diff --git a/projects/policyengine-api-full/pyproject.toml b/projects/policyengine-api-full/pyproject.toml index d9c61300..b7b78706 100644 --- a/projects/policyengine-api-full/pyproject.toml +++ b/projects/policyengine-api-full/pyproject.toml @@ -17,7 +17,7 @@ dependencies = [ "pydantic-settings (>=2.7.1,<3.0.0)", "opentelemetry-instrumentation-fastapi (>=0.51b0,<0.52)", "policyengine-fastapi", - "policyengine[full]>=3.0.0", + "policyengine[uk,us]>=3.0.0", "psycopg2-binary>=2.9.0" ] diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/__init__.py b/projects/policyengine-api-full/src/policyengine_api_full/api/__init__.py deleted file mode 100644 index f60d775d..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -from fastapi import FastAPI -from sqlalchemy import Engine - -from policyengine_fastapi.auth.jwt_decoder import JWTDecoder -from policyengine_fastapi.database import create_session_dep -from .household import include_all_routers -from .database_router import create_database_router - -""" -Application defined as routers completely indipendent of environment allowing it -to easily be run in whatever cloud provider container or desktop or test environment. -""" - - -def initialize(app: FastAPI, engine: Engine, jwt_issuer: str, jwt_audience: str): - """ - attach all routes to the app and configure them to use the provided SQLModel engine - and jwt settings. - """ - optional_auth = JWTDecoder(jwt_issuer, audience=jwt_audience, auto_error=False) - auth = JWTDecoder(jwt_issuer, audience=jwt_audience, auto_error=True) - include_all_routers( - app, - session_depedency=create_session_dep(engine), - optional_auth=optional_auth, - auth=auth, - ) - - # Add the database router for PolicyEngine tables - database_router = create_database_router() - app.include_router(database_router) diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/country.py b/projects/policyengine-api-full/src/policyengine_api_full/api/country.py deleted file mode 100644 index 38f32564..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/country.py +++ /dev/null @@ -1,522 +0,0 @@ -import importlib -import json -import logging -from policyengine_core.taxbenefitsystems import TaxBenefitSystem -from policyengine_core.variables import Variable as CoreVariable -from policyengine_core.simulations import Simulation -from policyengine_core.populations import Population -from policyengine_api_full.api.utils.constants import CURRENT_LAW_IDS -from policyengine_api_full.api.utils.json import get_safe_json -from policyengine_api_full.api.utils.metadata import ( - parse_enum_possible_values, - parse_default_value, -) -from policyengine_api_full.api.models.household import ( - HouseholdUS, - HouseholdUK, - HouseholdGeneric, -) -from policyengine_api_full.api.models.metadata.variable import ( - Variable, - VariableModule, -) -from policyengine_api_full.api.models.metadata.entity import Entity -from policyengine_api_full.api.models.metadata.modeled_policies import ( - ModeledPolicies, -) -from policyengine_api_full.api.models.metadata.economy_options import ( - Region, - TimePeriod, - EconomyOptions, -) -from policyengine_api_full.api.models.metadata.parameter import ( - ParameterScaleItem, - ParameterNode, - Parameter, -) -from policyengine_api_full.api.models.metadata.metadata_module import MetadataModule -from policyengine_api_full.api.models.periods import ISO8601Date -from typing import Union, Any -from numpy.typing import ArrayLike - -from policyengine_core.entities import Entity as CoreEntity -from policyengine_core.parameters import ( - ParameterNode as CoreParameterNode, - Parameter as CoreParameter, - ParameterScale as CoreParameterScale, - ParameterScaleBracket as CoreParameterScaleBracket, -) -from policyengine_core.parameters import get_parameter -import pkg_resources -from copy import deepcopy -from policyengine_core.model_api import Enum -from policyengine_core.periods import instant -import dpath -from pathlib import Path -import math - -import logging - -logger = logging.getLogger(__name__) - - -class PolicyEngineCountry: - def __init__(self, country_package_name: str, country_id: str): - self.country_package_name = country_package_name - self.country_id = country_id - self.country_package = importlib.import_module(country_package_name) - self.tax_benefit_system: TaxBenefitSystem = ( - self.country_package.CountryTaxBenefitSystem() - ) - self.metadata: MetadataModule = self._build_metadata( - country_id=self.country_id, - system=self.tax_benefit_system, - country_package_name=self.country_package_name, - ) - - def _build_metadata( - self, - country_id: str, - system: TaxBenefitSystem, - country_package_name: str, - ) -> MetadataModule: - return MetadataModule( - variables=self._build_variables(system=system), - parameters=self._build_parameters(system=system), - entities=self._build_entities(system=system), - variableModules=self._build_variable_modules(system=system), - economy_options=self._build_economy_options(country_id=country_id), - current_law_id=CURRENT_LAW_IDS[country_id], - basicInputs=system.basic_inputs, - modeled_policies=self._build_modeled_policies(system=system), - version=pkg_resources.get_distribution(country_package_name).version, - ) - - def _build_economy_options(self, country_id: str) -> EconomyOptions: - regions: list[Region] = self._build_regions(country_id=country_id) - time_periods: list[TimePeriod] = self._build_time_periods(country_id=country_id) - return EconomyOptions(region=regions, time_period=time_periods) - - def _build_variables(self, system: TaxBenefitSystem) -> dict[str, Variable]: - variables: dict[str, CoreVariable] = system.variables - variable_data = {} - for variable_name, variable in variables.items(): - variable_data[variable_name] = Variable( - documentation=variable.documentation, - entity=variable.entity.key, - valueType=variable.value_type.__name__, - definitionPeriod=variable.definition_period, - name=variable_name, - label=variable.label, - category=variable.category, - unit=variable.unit, - moduleName=variable.module_name, - indexInModule=variable.index_in_module, - isInputVariable=variable.is_input_variable(), - defaultValue=parse_default_value(variable), - adds=variable.adds, - subtracts=variable.subtracts, - hidden_input=variable.hidden_input, - possibleValues=parse_enum_possible_values(variable), - ) - return variable_data - - def _build_parameters( - self, system: TaxBenefitSystem - ) -> dict[str, ParameterScaleItem | ParameterNode | Parameter]: - APPROVED_TOP_LEVEL_FOLDERS = ["gov"] - - parameters: list[ - CoreParameter - | CoreParameterNode - | CoreParameterScaleBracket - | CoreParameterScale - ] = system.parameters - parameter_data = {} - for parameter in parameters.get_descendants(): - # Only include parameters from approved folders - if not any( - parameter.name.startswith(folder) - for folder in APPROVED_TOP_LEVEL_FOLDERS - ): - continue - - match parameter: - case p if isinstance(parameter, CoreParameterScale): - parameter_data[parameter.name] = self._build_parameter_scale( - parameter - ) - case p if isinstance(parameter, CoreParameterScaleBracket): - parameter_data[parameter.name] = ( - self._build_parameter_scale_bracket(parameter) - ) - case p if isinstance(parameter, CoreParameter): - parameter_data[parameter.name] = self._build_parameter(parameter) - case p if isinstance(parameter, CoreParameterNode): - parameter_data[parameter.name] = self._build_parameter_node( - parameter - ) - case p: - continue - - return parameter_data - - def _build_entities(self, system: TaxBenefitSystem) -> dict[str, Entity]: - entities: list[CoreEntity] = system.entities - data = {} - - for entity in entities: - roles = {} - if hasattr(entity, "roles"): - roles = { - role.key: { - "plural": role.plural, - "label": role.label, - "doc": role.doc, - } - for role in entity.roles - } - - data[entity.key] = Entity( - plural=entity.plural, - label=entity.label, - doc=entity.doc, - is_person=entity.is_person, - key=entity.key, - roles=roles, - ) - - return data - - def _build_variable_modules( - self, system: TaxBenefitSystem - ) -> dict[str, VariableModule]: - variable_modules: dict[str, dict[str, Any]] = system.variable_module_metadata - modules = {} - for module_path, module in variable_modules.items(): - modules[module_path] = VariableModule( - label=module.get("label", None), - description=module.get("description", None), - index=module.get("index", None), - ) - return modules - - def _build_modeled_policies( - self, system: TaxBenefitSystem - ) -> ModeledPolicies | None: - if system.modelled_policies: - return ModeledPolicies(**system.modelled_policies) - return None - - def _build_parameter_scale( - self, parameter: CoreParameterScale - ) -> ParameterScaleItem: - end_name = parameter.name.split(".")[-1] - return ParameterScaleItem( - type="parameterNode", - parameter=parameter.name, - description=parameter.description, - label=parameter.metadata.get("label", end_name.replace("_", " ")), - ) - - def _build_parameter_scale_bracket( - self, parameter: CoreParameterScaleBracket - ) -> ParameterScaleItem: - # Set the label to 'bracket 1' for the first bracket, 'bracket 2' for the second, etc. - bracket_index = int(parameter.name[parameter.name.index("[") + 1 : -1]) - bracket_label = f"bracket {bracket_index + 1}" - return ParameterScaleItem( - type="parameterNode", - parameter=parameter.name, - description=parameter.description, - label=parameter.metadata.get("label", bracket_label), - ) - - def _build_parameter(self, parameter: CoreParameter) -> Parameter: - end_name = parameter.name.split(".")[-1] - values_list = { - value_at_instant.instant_str: get_safe_json(value_at_instant.value) - for value_at_instant in parameter.values_list - } - - return Parameter( - type="parameter", - parameter=parameter.name, - description=parameter.description, - label=parameter.metadata.get("label", end_name.replace("_", " ")), - unit=parameter.metadata.get("unit"), - period=parameter.metadata.get("period"), - values=values_list, - economy=parameter.metadata.get("economy", True), - household=parameter.metadata.get("household", True), - ) - - def _build_parameter_node(self, parameter: CoreParameterNode) -> ParameterNode: - end_name = parameter.name.split(".")[-1] - return ParameterNode( - type="parameterNode", - parameter=parameter.name, - description=parameter.description, - label=parameter.metadata.get("label", end_name.replace("_", " ")), - economy=parameter.metadata.get("economy", True), - household=parameter.metadata.get("household", True), - ) - - def _build_regions(self, country_id: str) -> list[Region]: - cwd = Path(__file__).parent - region_file_path = cwd.joinpath(f"data/regions/{country_id}_regions.json") - with open(region_file_path, "r") as region_file: - regions = json.load(region_file) - return [Region(**region) for region in regions] - - def _build_time_periods(self, country_id: str) -> list[TimePeriod]: - cwd = Path(__file__).parent - country_time_period_path = cwd.joinpath( - f"data/time_periods/{country_id}_time_periods.json" - ) - if country_time_period_path.exists(): - with open(country_time_period_path, "r") as time_period_file: - time_periods = json.load(time_period_file) - else: - time_period_path = cwd.joinpath( - "data/time_periods/default_time_periods.json" - ) - with open(time_period_path, "r") as time_period_file: - time_periods = json.load(time_period_file) - return [TimePeriod(**time_period) for time_period in time_periods] - - def calculate( - self, - household: HouseholdUS | HouseholdUK | HouseholdGeneric, - reform: Union[dict, None] = None, - ) -> HouseholdGeneric | HouseholdUK | HouseholdUS: - system: TaxBenefitSystem = self._prepare_tax_benefit_system(reform) - household_raw: dict[str, Any] = household.model_dump() - - simulation: Simulation = self.country_package.Simulation( - tax_benefit_system=system, - situation=household_raw, - ) - - household_result: dict[str, Any] = deepcopy(household_raw) - requested_computations: list[tuple[str, str, str, str]] = ( - get_requested_computations(household_raw) - ) - - for computation in requested_computations: - self._process_computation(simulation, system, household_result, computation) - - if self.country_id == "us": - return HouseholdUS.model_validate(household_result) - if self.country_id == "uk": - return HouseholdUK.model_validate(household_result) - return HouseholdGeneric.model_validate(household_result) - - def _prepare_tax_benefit_system( - self, reform: Union[dict, None] = None - ) -> TaxBenefitSystem: - """Prepare the tax benefit system with optional reforms applied.""" - if not reform: - return self.tax_benefit_system - - system: TaxBenefitSystem = self.tax_benefit_system.clone() - - for parameter_name, periods in reform.items(): - for time_period, value in periods.items(): - self._update_parameter(system, parameter_name, time_period, value) - - return system - - def _update_parameter( - self, - system: TaxBenefitSystem, - parameter_name: str, - time_period: str, - value: Any, - ) -> None: - """Update a specific parameter in the tax benefit system.""" - start_instant: ISO8601Date - end_instant: ISO8601Date - start_instant, end_instant = time_period.split(".") - parameter: CoreParameter = get_parameter(system.parameters, parameter_name) - - # Determine the appropriate type for the value - node_type = type(parameter.values_list[-1].value) - - # Cast int to float to harmonize numeric handling - # Int-float casting copied/pasted from the original household API at - # https://github.com/PolicyEngine/policyengine-household-api/blob/96ebe4440f9cba81b09f64d53aa4f7e6e7003d77/policyengine_household_api/country.py#L319 - if node_type == int: - node_type = float - - # Convert value to float if possible - try: - value = float(value) - except (ValueError, TypeError): - pass - - parameter.update( - start=instant(start_instant), - stop=instant(end_instant), - value=node_type(value), - ) - - def _process_computation( - self, - simulation: Simulation, - system: TaxBenefitSystem, - household: dict[str, Any], - computation, - ): - """Process a single computation request and update the household result.""" - entity_plural, entity_id, variable_name, period = computation - - try: - variable: Variable = system.get_variable(variable_name) - result: ArrayLike = simulation.calculate(variable_name, period) - - if getattr(household, "axes", None): - self._handle_axes_computation( - household, - entity_plural, - entity_id, - variable_name, - period, - result, - ) - else: - self._handle_single_computation( - simulation, - household, - entity_plural, - entity_id, - variable_name, - period, - result, - variable, - ) - except Exception as e: - self._handle_computation_error( - household, entity_plural, entity_id, variable_name, period, e - ) - - def _handle_axes_computation( - self, - household: dict[str, Any], - entity_plural: str, - entity_id, - variable_name: str, - period, - result, - ) -> None: - """Handle computation for households with axes.""" - count_entities: int = len(household[entity_plural]) - entity_index: int = self._find_entity_index(household, entity_plural, entity_id) - - # Reshape result and get values for the specific entity - result_values: list[float] = ( - result.astype(float).reshape((-1, count_entities)).T[entity_index].tolist() - ) - - # Check for infinite values - if any(math.isinf(value) for value in result_values): - raise ValueError("Infinite value") - - # Update household with results - household[entity_plural][entity_id][variable_name][period] = result_values - - def _find_entity_index( - self, household: dict[str, Any], entity_plural: str, entity_id - ): - """Find the index of an entity within its plural group.""" - entity_index = 0 - - _entity_id: str - for _entity_id in household[entity_plural].keys(): - if _entity_id == entity_id: - break - entity_index += 1 - return entity_index - - def _handle_single_computation( - self, - simulation: Simulation, - household: dict[str, Any], - entity_plural: str, - entity_id, - variable_name: str, - period, - result: ArrayLike, - variable: Variable, - ): - """Handle computation for a single entity.""" - population: Population = simulation.get_population(entity_plural) - entity_index: int = population.get_index(entity_id) - - # Format the result based on variable type - entity_result = self._format_result(result, entity_index, variable) - - # Update household with result - household[entity_plural][entity_id][variable_name][period] = entity_result - - def _format_result( - self, result: ArrayLike, entity_index, variable: Variable - ) -> Any: - """Format calculation result based on variable type.""" - if variable.value_type == Enum: - return result.decode()[entity_index].name - if variable.value_type == float: - value = float(str(result[entity_index])) - # Convert infinities to JSON-compatible strings - if value == float("inf"): - return "Infinity" - if value == float("-inf"): - return "-Infinity" - return value - if variable.value_type == str: - return str(result[entity_index]) - return result.tolist()[entity_index] - - def _handle_computation_error( - self, - household: dict[str, Any], - entity_plural: str, - entity_id: str, - variable_name: str, - period, - error, - ): - """Handle errors during computation.""" - - # Original code passes if "axes" in household - why? - if "axes" not in household: - household[entity_plural][entity_id][variable_name][period] = None - print(f"Error computing {variable_name} for {entity_id}: {error}") - - -def get_requested_computations(household: dict[str, Any]): - requested_computations = dpath.util.search( - household, - "*/*/*/*", - afilter=lambda t: t is None, - yielded=True, - ) - requested_computation_data = [] - - for computation in requested_computations: - path = computation[0] - entity_plural, entity_id, variable_name, period = path.split("/") - requested_computation_data.append( - (entity_plural, entity_id, variable_name, period) - ) - - return requested_computation_data - - -COUNTRIES = { - "uk": PolicyEngineCountry("policyengine_uk", "uk"), - "us": PolicyEngineCountry("policyengine_us", "us"), - "ca": PolicyEngineCountry("policyengine_canada", "ca"), - "ng": PolicyEngineCountry("policyengine_ng", "ng"), - "il": PolicyEngineCountry("policyengine_il", "il"), -} diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/ca_regions.json b/projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/ca_regions.json deleted file mode 100644 index 5f855ea1..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/ca_regions.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - {"name": "ca", "label": "Canada"} -] \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/il_regions.json b/projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/il_regions.json deleted file mode 100644 index 3ec569dd..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/il_regions.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - {"name": "il", "label": "Israel"} -] \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/ng_regions.json b/projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/ng_regions.json deleted file mode 100644 index fd90f83c..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/ng_regions.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - {"name": "ng", "label": "Nigeria"} -] \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/uk_regions.json b/projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/uk_regions.json deleted file mode 100644 index 38d1fc2f..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/uk_regions.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - {"name": "uk", "label": "the UK"}, - {"name": "eng", "label": "England"}, - {"name": "scot", "label": "Scotland"}, - {"name": "wales", "label": "Wales"}, - {"name": "ni", "label": "Northern Ireland"} -] \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/us_regions.json b/projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/us_regions.json deleted file mode 100644 index 3a64bc27..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/data/regions/us_regions.json +++ /dev/null @@ -1,56 +0,0 @@ -[ - {"name": "us", "label": "the US"}, - {"name": "enhanced_us", "label": "the US (enhanced CPS)"}, - {"name": "al", "label": "Alabama"}, - {"name": "ak", "label": "Alaska"}, - {"name": "az", "label": "Arizona"}, - {"name": "ar", "label": "Arkansas"}, - {"name": "ca", "label": "California"}, - {"name": "co", "label": "Colorado"}, - {"name": "ct", "label": "Connecticut"}, - {"name": "de", "label": "Delaware"}, - {"name": "dc", "label": "District of Columbia"}, - {"name": "fl", "label": "Florida"}, - {"name": "ga", "label": "Georgia"}, - {"name": "hi", "label": "Hawaii"}, - {"name": "id", "label": "Idaho"}, - {"name": "il", "label": "Illinois"}, - {"name": "in", "label": "Indiana"}, - {"name": "ia", "label": "Iowa"}, - {"name": "ks", "label": "Kansas"}, - {"name": "ky", "label": "Kentucky"}, - {"name": "la", "label": "Louisiana"}, - {"name": "me", "label": "Maine"}, - {"name": "md", "label": "Maryland"}, - {"name": "ma", "label": "Massachusetts"}, - {"name": "mi", "label": "Michigan"}, - {"name": "mn", "label": "Minnesota"}, - {"name": "ms", "label": "Mississippi"}, - {"name": "mo", "label": "Missouri"}, - {"name": "mt", "label": "Montana"}, - {"name": "ne", "label": "Nebraska"}, - {"name": "nv", "label": "Nevada"}, - {"name": "nh", "label": "New Hampshire"}, - {"name": "nj", "label": "New Jersey"}, - {"name": "nm", "label": "New Mexico"}, - {"name": "ny", "label": "New York"}, - {"name": "nyc", "label": "New York City"}, - {"name": "nc", "label": "North Carolina"}, - {"name": "nd", "label": "North Dakota"}, - {"name": "oh", "label": "Ohio"}, - {"name": "ok", "label": "Oklahoma"}, - {"name": "or", "label": "Oregon"}, - {"name": "pa", "label": "Pennsylvania"}, - {"name": "ri", "label": "Rhode Island"}, - {"name": "sc", "label": "South Carolina"}, - {"name": "sd", "label": "South Dakota"}, - {"name": "tn", "label": "Tennessee"}, - {"name": "tx", "label": "Texas"}, - {"name": "ut", "label": "Utah"}, - {"name": "vt", "label": "Vermont"}, - {"name": "va", "label": "Virginia"}, - {"name": "wa", "label": "Washington"}, - {"name": "wv", "label": "West Virginia"}, - {"name": "wi", "label": "Wisconsin"}, - {"name": "wy", "label": "Wyoming"} -] \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/data/time_periods/default_time_periods.json b/projects/policyengine-api-full/src/policyengine_api_full/api/data/time_periods/default_time_periods.json deleted file mode 100644 index ea9f1e2c..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/data/time_periods/default_time_periods.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - {"name": 2023, "label": "2023"} -] \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/data/time_periods/uk_time_periods.json b/projects/policyengine-api-full/src/policyengine_api_full/api/data/time_periods/uk_time_periods.json deleted file mode 100644 index e75fdacf..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/data/time_periods/uk_time_periods.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - {"name": 2023, "label": "2023"}, - {"name": 2024, "label": "2024"}, - {"name": 2022, "label": "2022"} -] \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/data/time_periods/us_time_periods.json b/projects/policyengine-api-full/src/policyengine_api_full/api/data/time_periods/us_time_periods.json deleted file mode 100644 index d9941a6f..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/data/time_periods/us_time_periods.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - {"name": 2023, "label": "2023"}, - {"name": 2022, "label": "2022"}, - {"name": 2021, "label": "2021"} -] \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/database_router.py b/projects/policyengine-api-full/src/policyengine_api_full/api/database_router.py deleted file mode 100644 index 0d9b31f1..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/database_router.py +++ /dev/null @@ -1,424 +0,0 @@ -from fastapi import APIRouter, Depends, HTTPException, Query -from sqlmodel import Session, select -from typing import List, Optional, Any, Dict - -# Import all the database tables and models from policyengine package -from policyengine.database import ( - UserTable, - ModelTable, - ModelVersionTable, - DatasetTable, - VersionedDatasetTable, - PolicyTable, - SimulationTable, - ParameterTable, - ParameterValueTable, - BaselineParameterValueTable, - BaselineVariableTable, - DynamicTable, -) - -from policyengine.models import ( - User, - Model, - ModelVersion, - Dataset, - VersionedDataset, - Policy, - Simulation, - Parameter, - ParameterValue, - BaselineParameterValue, - BaselineVariable, - Dynamic, -) - -from policyengine_api_full.database import get_session - -def create_database_router() -> APIRouter: - """Create a router with CRUD endpoints for all database tables.""" - router = APIRouter(prefix="/database", tags=["database"]) - - # User endpoints - @router.get("/users", response_model=List[User]) - def list_users( - skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), - session: Session = Depends(get_session) - ): - """List all users with pagination.""" - statement = select(UserTable).offset(skip).limit(limit) - users = session.exec(statement).all() - return [User.model_validate(user.model_dump()) for user in users] - - @router.get("/users/{user_id}", response_model=User) - def get_user(user_id: str, session: Session = Depends(get_session)): - """Get a specific user by ID.""" - user = session.get(UserTable, user_id) - if not user: - raise HTTPException(status_code=404, detail="User not found") - return User.model_validate(user.model_dump()) - - @router.post("/users", response_model=User) - def create_user(user: User, session: Session = Depends(get_session)): - """Create a new user.""" - db_user = UserTable(**user.model_dump()) - session.add(db_user) - session.commit() - session.refresh(db_user) - return User.model_validate(db_user.model_dump()) - - @router.put("/users/{user_id}", response_model=User) - def update_user(user_id: str, user: User, session: Session = Depends(get_session)): - """Update an existing user.""" - db_user = session.get(UserTable, user_id) - if not db_user: - raise HTTPException(status_code=404, detail="User not found") - user_data = user.model_dump(exclude_unset=True) - for key, value in user_data.items(): - setattr(db_user, key, value) - session.add(db_user) - session.commit() - session.refresh(db_user) - return User.model_validate(db_user.model_dump()) - - @router.delete("/users/{user_id}") - def delete_user(user_id: str, session: Session = Depends(get_session)): - """Delete a user.""" - user = session.get(UserTable, user_id) - if not user: - raise HTTPException(status_code=404, detail="User not found") - session.delete(user) - session.commit() - return {"message": "User deleted successfully"} - - # Model endpoints - @router.get("/models", response_model=List[Dict[str, Any]]) - def list_models( - skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), - session: Session = Depends(get_session) - ): - """List all models with pagination.""" - statement = select(ModelTable).offset(skip).limit(limit) - models = session.exec(statement).all() - # Return as dict since Model has callable fields - return [{"id": m.id, "name": m.name, "description": m.description} for m in models] - - @router.get("/models/{model_id}") - def get_model(model_id: str, session: Session = Depends(get_session)): - """Get a specific model by ID.""" - model = session.get(ModelTable, model_id) - if not model: - raise HTTPException(status_code=404, detail="Model not found") - return {"id": model.id, "name": model.name, "description": model.description} - - # Model Version endpoints - @router.get("/model-versions", response_model=List[ModelVersion]) - def list_model_versions( - model_id: Optional[str] = None, - skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), - session: Session = Depends(get_session) - ): - """List all model versions with optional filtering by model_id.""" - statement = select(ModelVersionTable) - if model_id: - statement = statement.where(ModelVersionTable.model_id == model_id) - statement = statement.offset(skip).limit(limit) - versions = session.exec(statement).all() - return [ModelVersion.model_validate(v.model_dump()) for v in versions] - - @router.post("/model-versions", response_model=ModelVersion) - def create_model_version(version: ModelVersion, session: Session = Depends(get_session)): - """Create a new model version.""" - db_version = ModelVersionTable(**version.model_dump()) - session.add(db_version) - session.commit() - session.refresh(db_version) - return ModelVersion.model_validate(db_version.model_dump()) - - # Dataset endpoints - @router.get("/datasets", response_model=List[Dataset]) - def list_datasets( - skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), - session: Session = Depends(get_session) - ): - """List all datasets with pagination.""" - statement = select(DatasetTable).offset(skip).limit(limit) - datasets = session.exec(statement).all() - return [Dataset.model_validate(d.model_dump()) for d in datasets] - - @router.get("/datasets/{dataset_id}", response_model=Dataset) - def get_dataset(dataset_id: str, session: Session = Depends(get_session)): - """Get a specific dataset by ID.""" - dataset = session.get(DatasetTable, dataset_id) - if not dataset: - raise HTTPException(status_code=404, detail="Dataset not found") - return Dataset.model_validate(dataset.model_dump()) - - @router.post("/datasets", response_model=Dataset) - def create_dataset(dataset: Dataset, session: Session = Depends(get_session)): - """Create a new dataset.""" - db_dataset = DatasetTable(**dataset.model_dump()) - session.add(db_dataset) - session.commit() - session.refresh(db_dataset) - return Dataset.model_validate(db_dataset.model_dump()) - - # Versioned Dataset endpoints - @router.get("/versioned-datasets", response_model=List[VersionedDataset]) - def list_versioned_datasets( - dataset_id: Optional[str] = None, - skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), - session: Session = Depends(get_session) - ): - """List all versioned datasets with optional filtering.""" - statement = select(VersionedDatasetTable) - if dataset_id: - statement = statement.where(VersionedDatasetTable.dataset_id == dataset_id) - statement = statement.offset(skip).limit(limit) - versioned = session.exec(statement).all() - return [VersionedDataset.model_validate(v.model_dump()) for v in versioned] - - @router.post("/versioned-datasets", response_model=VersionedDataset) - def create_versioned_dataset(versioned: VersionedDataset, session: Session = Depends(get_session)): - """Create a new versioned dataset.""" - db_versioned = VersionedDatasetTable(**versioned.model_dump()) - session.add(db_versioned) - session.commit() - session.refresh(db_versioned) - return VersionedDataset.model_validate(db_versioned.model_dump()) - - # Policy endpoints - @router.get("/policies", response_model=List[Policy]) - def list_policies( - skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), - session: Session = Depends(get_session) - ): - """List all policies with pagination.""" - statement = select(PolicyTable).offset(skip).limit(limit) - policies = session.exec(statement).all() - return [Policy.model_validate(p.model_dump()) for p in policies] - - @router.get("/policies/{policy_id}", response_model=Policy) - def get_policy(policy_id: str, session: Session = Depends(get_session)): - """Get a specific policy by ID.""" - policy = session.get(PolicyTable, policy_id) - if not policy: - raise HTTPException(status_code=404, detail="Policy not found") - return Policy.model_validate(policy.model_dump()) - - @router.post("/policies", response_model=Policy) - def create_policy(policy: Policy, session: Session = Depends(get_session)): - """Create a new policy.""" - db_policy = PolicyTable(**policy.model_dump()) - session.add(db_policy) - session.commit() - session.refresh(db_policy) - return Policy.model_validate(db_policy.model_dump()) - - @router.put("/policies/{policy_id}", response_model=Policy) - def update_policy(policy_id: str, policy: Policy, session: Session = Depends(get_session)): - """Update an existing policy.""" - db_policy = session.get(PolicyTable, policy_id) - if not db_policy: - raise HTTPException(status_code=404, detail="Policy not found") - policy_data = policy.model_dump(exclude_unset=True) - for key, value in policy_data.items(): - setattr(db_policy, key, value) - session.add(db_policy) - session.commit() - session.refresh(db_policy) - return Policy.model_validate(db_policy.model_dump()) - - @router.delete("/policies/{policy_id}") - def delete_policy(policy_id: str, session: Session = Depends(get_session)): - """Delete a policy.""" - policy = session.get(PolicyTable, policy_id) - if not policy: - raise HTTPException(status_code=404, detail="Policy not found") - session.delete(policy) - session.commit() - return {"message": "Policy deleted successfully"} - - # Simulation endpoints - @router.get("/simulations", response_model=List[Simulation]) - def list_simulations( - policy_id: Optional[str] = None, - skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), - session: Session = Depends(get_session) - ): - """List all simulations with optional filtering.""" - statement = select(SimulationTable) - if policy_id: - statement = statement.where(SimulationTable.policy_id == policy_id) - statement = statement.offset(skip).limit(limit) - simulations = session.exec(statement).all() - return [Simulation.model_validate(s.model_dump()) for s in simulations] - - @router.get("/simulations/{simulation_id}", response_model=Simulation) - def get_simulation(simulation_id: str, session: Session = Depends(get_session)): - """Get a specific simulation by ID.""" - simulation = session.get(SimulationTable, simulation_id) - if not simulation: - raise HTTPException(status_code=404, detail="Simulation not found") - return Simulation.model_validate(simulation.model_dump()) - - @router.post("/simulations", response_model=Simulation) - def create_simulation(simulation: Simulation, session: Session = Depends(get_session)): - """Create a new simulation.""" - db_simulation = SimulationTable(**simulation.model_dump()) - session.add(db_simulation) - session.commit() - session.refresh(db_simulation) - return Simulation.model_validate(db_simulation.model_dump()) - - # Parameter endpoints - @router.get("/parameters", response_model=List[Parameter]) - def list_parameters( - model_id: Optional[str] = None, - skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), - session: Session = Depends(get_session) - ): - """List all parameters with optional filtering.""" - statement = select(ParameterTable) - if model_id: - statement = statement.where(ParameterTable.model_id == model_id) - statement = statement.offset(skip).limit(limit) - parameters = session.exec(statement).all() - return [Parameter.model_validate(p.model_dump()) for p in parameters] - - @router.get("/parameters/{parameter_id}", response_model=Parameter) - def get_parameter(parameter_id: str, session: Session = Depends(get_session)): - """Get a specific parameter by ID.""" - parameter = session.get(ParameterTable, parameter_id) - if not parameter: - raise HTTPException(status_code=404, detail="Parameter not found") - return Parameter.model_validate(parameter.model_dump()) - - @router.post("/parameters", response_model=Parameter) - def create_parameter(parameter: Parameter, session: Session = Depends(get_session)): - """Create a new parameter.""" - db_parameter = ParameterTable(**parameter.model_dump()) - session.add(db_parameter) - session.commit() - session.refresh(db_parameter) - return Parameter.model_validate(db_parameter.model_dump()) - - # Parameter Value endpoints - @router.get("/parameter-values", response_model=List[ParameterValue]) - def list_parameter_values( - parameter_id: Optional[str] = None, - policy_id: Optional[str] = None, - skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), - session: Session = Depends(get_session) - ): - """List all parameter values with optional filtering.""" - statement = select(ParameterValueTable) - if parameter_id: - statement = statement.where(ParameterValueTable.parameter_id == parameter_id) - if policy_id: - statement = statement.where(ParameterValueTable.policy_id == policy_id) - statement = statement.offset(skip).limit(limit) - values = session.exec(statement).all() - return [ParameterValue.model_validate(v.model_dump()) for v in values] - - @router.post("/parameter-values", response_model=ParameterValue) - def create_parameter_value(value: ParameterValue, session: Session = Depends(get_session)): - """Create a new parameter value.""" - db_value = ParameterValueTable(**value.model_dump()) - session.add(db_value) - session.commit() - session.refresh(db_value) - return ParameterValue.model_validate(db_value.model_dump()) - - # Baseline Parameter Value endpoints - @router.get("/baseline-parameter-values", response_model=List[BaselineParameterValue]) - def list_baseline_parameter_values( - parameter_id: Optional[str] = None, - model_version_id: Optional[str] = None, - skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), - session: Session = Depends(get_session) - ): - """List all baseline parameter values with optional filtering.""" - statement = select(BaselineParameterValueTable) - if parameter_id: - statement = statement.where(BaselineParameterValueTable.parameter_id == parameter_id) - if model_version_id: - statement = statement.where(BaselineParameterValueTable.model_version_id == model_version_id) - statement = statement.offset(skip).limit(limit) - values = session.exec(statement).all() - return [BaselineParameterValue.model_validate(v.model_dump()) for v in values] - - @router.post("/baseline-parameter-values", response_model=BaselineParameterValue) - def create_baseline_parameter_value(value: BaselineParameterValue, session: Session = Depends(get_session)): - """Create a new baseline parameter value.""" - db_value = BaselineParameterValueTable(**value.model_dump()) - session.add(db_value) - session.commit() - session.refresh(db_value) - return BaselineParameterValue.model_validate(db_value.model_dump()) - - # Baseline Variable endpoints - @router.get("/baseline-variables", response_model=List[BaselineVariable]) - def list_baseline_variables( - model_version_id: Optional[str] = None, - skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), - session: Session = Depends(get_session) - ): - """List all baseline variables with optional filtering.""" - statement = select(BaselineVariableTable) - if model_version_id: - statement = statement.where(BaselineVariableTable.model_version_id == model_version_id) - statement = statement.offset(skip).limit(limit) - variables = session.exec(statement).all() - return [BaselineVariable.model_validate(v.model_dump()) for v in variables] - - @router.post("/baseline-variables", response_model=BaselineVariable) - def create_baseline_variable(variable: BaselineVariable, session: Session = Depends(get_session)): - """Create a new baseline variable.""" - db_variable = BaselineVariableTable(**variable.model_dump()) - session.add(db_variable) - session.commit() - session.refresh(db_variable) - return BaselineVariable.model_validate(db_variable.model_dump()) - - # Dynamic endpoints - @router.get("/dynamics", response_model=List[Dynamic]) - def list_dynamics( - skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), - session: Session = Depends(get_session) - ): - """List all dynamics with pagination.""" - statement = select(DynamicTable).offset(skip).limit(limit) - dynamics = session.exec(statement).all() - return [Dynamic.model_validate(d.model_dump()) for d in dynamics] - - @router.get("/dynamics/{dynamic_id}", response_model=Dynamic) - def get_dynamic(dynamic_id: str, session: Session = Depends(get_session)): - """Get a specific dynamic by ID.""" - dynamic = session.get(DynamicTable, dynamic_id) - if not dynamic: - raise HTTPException(status_code=404, detail="Dynamic not found") - return Dynamic.model_validate(dynamic.model_dump()) - - @router.post("/dynamics", response_model=Dynamic) - def create_dynamic(dynamic: Dynamic, session: Session = Depends(get_session)): - """Create a new dynamic.""" - db_dynamic = DynamicTable(**dynamic.model_dump()) - session.add(db_dynamic) - session.commit() - session.refresh(db_dynamic) - return Dynamic.model_validate(db_dynamic.model_dump()) - - return router \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/enums/__init__.py b/projects/policyengine-api-full/src/policyengine_api_full/api/enums/__init__.py deleted file mode 100644 index 93e97258..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/enums/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from .country_id import COUNTRY_ID -from .entities import ENTITIES_GENERIC, ENTITIES_UK, ENTITIES_US -from .entity_groups import ( - ENTITY_GROUPS_GENERIC, - ENTITY_GROUPS_UK, - ENTITY_GROUPS_US, -) -from .periods import PERIODS -from .value_types import VALUE_TYPES diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/enums/country_id.py b/projects/policyengine-api-full/src/policyengine_api_full/api/enums/country_id.py deleted file mode 100644 index b32700c8..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/enums/country_id.py +++ /dev/null @@ -1,11 +0,0 @@ -from enum import Enum - - -# Question: Is there a way to instead base these upon COUNTRIES? -# Perhaps build Enum at runtime? -class COUNTRY_ID(Enum): - US = "us" - UK = "uk" - IL = "il" - CA = "ca" - NG = "ng" diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/enums/entities.py b/projects/policyengine-api-full/src/policyengine_api_full/api/enums/entities.py deleted file mode 100644 index 7a70dcff..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/enums/entities.py +++ /dev/null @@ -1,24 +0,0 @@ -from enum import Enum - - -class ENTITIES_US(Enum): - PERSON = "person" - FAMILY = "family" - SPM_UNIT = "spm_unit" - TAX_UNIT = "tax_unit" - HOUSEHOLD = "household" - MARITAL_UNIT = "marital_unit" - STATE = "state" - - -class ENTITIES_UK(Enum): - BENUNIT = "benunit" - PERSON = "person" - HOUSEHOLD = "household" - STATE = "state" - - -class ENTITIES_GENERIC(Enum): - PERSON = "person" - HOUSEHOLD = "household" - STATE = "state" diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/enums/entity_groups.py b/projects/policyengine-api-full/src/policyengine_api_full/api/enums/entity_groups.py deleted file mode 100644 index 6b89c498..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/enums/entity_groups.py +++ /dev/null @@ -1,21 +0,0 @@ -from enum import Enum - - -class ENTITY_GROUPS_US(Enum): - PEOPLE = "people" - FAMILIES = "families" - SPM_UNITS = "spm_units" - TAX_UNITS = "tax_units" - HOUSEHOLDS = "households" - MARITAL_UNITS = "marital_units" - - -class ENTITY_GROUPS_UK(Enum): - BENUNITS = "benunits" - PEOPLE = "people" - HOUSEHOLDS = "households" - - -class ENTITY_GROUPS_GENERIC(Enum): - PEOPLE = "people" - HOUSEHOLDS = "households" diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/enums/periods.py b/projects/policyengine-api-full/src/policyengine_api_full/api/enums/periods.py deleted file mode 100644 index a841c265..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/enums/periods.py +++ /dev/null @@ -1,8 +0,0 @@ -from enum import Enum - - -class PERIODS(Enum): - DAY = "day" - MONTH = "month" - YEAR = "year" - ETERNITY = "eternity" diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/enums/value_types.py b/projects/policyengine-api-full/src/policyengine_api_full/api/enums/value_types.py deleted file mode 100644 index fb84a08e..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/enums/value_types.py +++ /dev/null @@ -1,10 +0,0 @@ -from enum import Enum - - -class VALUE_TYPES(Enum): - BOOL = "bool" - INT = "int" - FLOAT = "float" - STR = "str" - ENUM = "Enum" - DATETIME = "datetime.datetime" diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/household/__init__.py b/projects/policyengine-api-full/src/policyengine_api_full/api/household/__init__.py deleted file mode 100644 index 1acbab99..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/household/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -from fastapi import FastAPI -from policyengine_fastapi.auth.jwt_decoder import JWTDecoder -from .household import create_router as create_household_router -from .user import create_router as create_user_router -from policyengine_fastapi.database import SessionGeneratorFactory - - -def include_all_routers( - app: FastAPI, - session_depedency: SessionGeneratorFactory, - optional_auth: JWTDecoder, - auth: JWTDecoder, -): - app.include_router(create_household_router(session_dependency=session_depedency)) - app.include_router( - create_user_router( - session_dependency=session_depedency, - optional_auth=optional_auth, - auth=auth, - ) - ) diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/household/household.py b/projects/policyengine-api-full/src/policyengine_api_full/api/household/household.py deleted file mode 100644 index 675b15c6..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/household/household.py +++ /dev/null @@ -1,54 +0,0 @@ -from typing import Annotated -from fastapi import APIRouter, Depends, HTTPException -from sqlmodel import Session, SQLModel, Field -from policyengine_fastapi.database import SessionGeneratorFactory - - -class HouseholdBase(SQLModel): - pass - - -class Household(HouseholdBase, table=True): - id: int | None = Field(None, primary_key=True) - - -class HouseholdCreate(HouseholdBase): - pass - - -def create_router(session_dependency: SessionGeneratorFactory): - router = APIRouter() - - @router.post("/household") - async def create_houshold( - item: HouseholdCreate, - session: Annotated[Session, Depends(session_dependency)], - ) -> Household: - model = Household.model_validate(item) - session.add(model) - session.commit() - session.refresh(model) - - return model - - def _get_household(id: int, session: Session) -> Household: - model = session.get(Household, id) - if not model: - raise HTTPException(status_code=404, detail="Household not found") - return model - - @router.get("/household/{id}") - async def get_household( - id: int, session: Annotated[Session, Depends(session_dependency)] - ) -> Household: - return _get_household(id, session) - - @router.delete("/household/{id}") - async def delete_household( - id: int, session: Annotated[Session, Depends(session_dependency)] - ) -> None: - model = _get_household(id, session) - session.delete(model) - session.commit() - - return router diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/household/user.py b/projects/policyengine-api-full/src/policyengine_api_full/api/household/user.py deleted file mode 100644 index 1af07af7..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/household/user.py +++ /dev/null @@ -1,145 +0,0 @@ -from typing import Annotated, Any, Callable, Tuple -from fastapi import APIRouter, Depends, HTTPException, Security, status -from fastapi.security import HTTPAuthorizationCredentials -from pydantic import BaseModel -from sqlmodel import Field, SQLModel, Session -from policyengine_fastapi.database import SessionGeneratorFactory -from policyengine_fastapi.auth import JWTDecoder -import logging - -# Use standard python logging -LOG = logging.getLogger(__name__) - -# https://fastapi.tiangolo.com/tutorial/sql-databases/#update-the-app-with-multiple-models -# Reduce duplication by defining request/reponse models in terms of database models. - - -# SQLModel models -class UserBase(SQLModel): - username: str = Field(..., description="Username") - pass - - -class User(UserBase, table=True): - id: int | None = Field(None, primary_key=True) - auth0_sub: str = Field(..., description="Auth0 subject identifier") - - -# Request/Response Models -class UserPublic(UserBase): - id: int - pass - - -class UserCreate(UserBase): - auth0_sub: str - pass - - -class UserPrivate(User): - pass - - -class AuthorizedUser(BaseModel): - user: User - authorized: bool - - -AuthType = Callable[[HTTPAuthorizationCredentials | None], Any] - - -def _get_user(id: int, session: Session) -> User: - user = session.get(User, id) - if user == None: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) - return user - - -# If you don't want to hard code the implementation of all dependencies in the router -# (which means the app can't change them) the only way I found that works is this -# at least for the security dependencies if you use app.override_dependencies it -# looses the security annotation information and doesn't generate the client properly. -def create_router( - session_dependency: SessionGeneratorFactory, - optional_auth: AuthType, - auth: AuthType, -) -> APIRouter: - router = APIRouter() - - # Replace this with whatever authorization scheme you prefer - # Currently verifies the bearer token is the same user as the - # user row being accessed. - class AuthUser: - """ - Authenticate the currently logged in user's bearer token against - the user row being accessed to determine if the bearer owns the row - or not - """ - - def __init__(self, auto_error=True): - self.auto_error = auto_error - - def _check(self, user: User, token: dict[str, str] | None): - if token is None: - LOG.info("No auth token provided") - return False - sub = token["sub"] - if user.auth0_sub != sub: - LOG.info( - f"auth sub {sub} cannot access user {user.id}, {user.auth0_sub}" - ) - return False - return True - - async def __call__( - self, - id: int, - session: Annotated[Session, Depends(session_dependency)], - token=Security(optional_auth), - ) -> AuthorizedUser: - user = _get_user(id, session) - authorized = self._check(user, token) - if not (authorized) and self.auto_error: - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) - return AuthorizedUser(user=user, authorized=authorized) - - auth_user = AuthUser(auto_error=True) - auth_user_optional = AuthUser(auto_error=False) - - @router.post("/user") - def create_user( - user_create: UserCreate, - session: Annotated[Session, Depends(session_dependency)], - token=Security(auth), - ) -> UserPrivate: - if user_create.auth0_sub != token["sub"]: - LOG.info( - f"User {token['sub']} attempted to create a record for {user_create.auth0_sub}. Rejecting." - ) - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) - user = User.model_validate(user_create) - session.add(user) - session.commit() - session.refresh(user) - return UserPrivate.model_validate(user) - - @router.get("/user/{id}") - def get_user( - authUser: Annotated[AuthorizedUser, Depends(auth_user_optional)], - ) -> UserPublic | UserPrivate: - # model_validate will automatically convert the data in the - # database model for a full row into just the fields defined in the - # target model. - if authUser.authorized: - return UserPrivate.model_validate(authUser.user) - return UserPublic.model_validate(authUser.user) - - @router.delete("/user/{id}") - def delete_user( - session: Annotated[Session, Depends(session_dependency)], - authUser: Annotated[AuthorizedUser, Depends(auth_user)], - ) -> None: - session.delete(authUser.user) - session.commit() - - return router diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/models/household.py b/projects/policyengine-api-full/src/policyengine_api_full/api/models/household.py deleted file mode 100644 index 663716f2..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/models/household.py +++ /dev/null @@ -1,93 +0,0 @@ -from pydantic import BaseModel, RootModel, Field -from typing import Union, Any, Optional - -example_people_us = { - "you": {"age": {"2024": 40}, "employment_income": {"2024": 29000}}, - "your first dependent": { - "age": {"2024": 5}, - "employment_income": {"2024": 0}, - "is_tax_unit_dependent": {"2024": True}, - }, -} - -example_families_us = {"your family": {"members": ["you", "your first dependent"]}} - -example_spm_units_us = {"your household": {"members": ["you", "your first dependent"]}} - -example_tax_units_us = { - "your tax unit": { - "members": ["you", "your first dependent"], - "eitc": {"2024": 39_000}, - "ctc": {"2024": None}, - } -} - -example_households_us = { - "your household": { - "members": ["you", "your first dependent"], - "state_name": {"2024": "CA"}, - } -} - -example_marital_units_us = { - "your marital unit": {"members": ["you"]}, - "your first dependent's marital unit": { - "members": ["your first dependent"], - "marital_unit_id": {"2024": 1}, - }, -} - - -example_household_input_us = { - "people": example_people_us, - "families": example_families_us, - "spm_units": example_spm_units_us, - "tax_units": example_tax_units_us, - "households": example_households_us, - "marital_units": example_marital_units_us, -} - - -class HouseholdAxes(BaseModel): - name: str # Variable over which to apply axes - period: int | str # The month or year to which the axes apply - count: int # The number of axes - min: int # The lowest axis - max: int # The highest axis - - -class HouseholdVariable(RootModel): - root: Union[dict[str, Any], list[str]] - - -class HouseholdEntity(RootModel): - root: dict[str, HouseholdVariable] - - -class HouseholdGeneric(BaseModel): - households: dict[str, HouseholdEntity] = Field(examples=[example_households_us]) - people: dict[str, HouseholdEntity] = Field(examples=[example_people_us]) - axes: Optional[dict[str, HouseholdAxes]] = None - - -class HouseholdUS(HouseholdGeneric): - families: Optional[dict[str, HouseholdEntity]] = Field( - default={}, examples=[example_families_us] - ) - spm_units: Optional[dict[str, HouseholdEntity]] = Field( - default={}, examples=[example_spm_units_us] - ) - tax_units: Optional[dict[str, HouseholdEntity]] = Field( - default={}, examples=[example_tax_units_us] - ) - marital_units: Optional[dict[str, HouseholdEntity]] = Field( - default={}, examples=[example_marital_units_us] - ) - - -class HouseholdUK(HouseholdGeneric): - benunits: Optional[dict[str, HouseholdEntity]] = {} - - -# Typing alias for all three possible household models -HouseholdData = Union[HouseholdUS, HouseholdUK, HouseholdGeneric] diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/__init__.py b/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/economy_options.py b/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/economy_options.py deleted file mode 100644 index 58d5e989..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/economy_options.py +++ /dev/null @@ -1,16 +0,0 @@ -from pydantic import BaseModel - - -class Region(BaseModel): - name: str - label: str - - -class TimePeriod(BaseModel): - name: int - label: str - - -class EconomyOptions(BaseModel): - region: list[Region] - time_period: list[TimePeriod] diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/entity.py b/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/entity.py deleted file mode 100644 index d26f0296..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/entity.py +++ /dev/null @@ -1,16 +0,0 @@ -from pydantic import BaseModel - - -class EntityRole(BaseModel): - plural: str - label: str - doc: str - - -class Entity(BaseModel): - plural: str - label: str - doc: str - is_person: bool - key: str # Unsure if this is actually str or int; will check when validation errors occur - roles: dict[str, EntityRole] = {} diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/metadata_module.py b/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/metadata_module.py deleted file mode 100644 index 1f3e8ad5..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/metadata_module.py +++ /dev/null @@ -1,28 +0,0 @@ -from pydantic import BaseModel -from typing import Optional -from policyengine_api_full.api.models.metadata.entity import Entity -from policyengine_api_full.api.models.metadata.economy_options import EconomyOptions -from policyengine_api_full.api.models.metadata.modeled_policies import ( - ModeledPolicies, -) -from policyengine_api_full.api.models.metadata.parameter import ( - Parameter, - ParameterNode, - ParameterScaleItem, -) -from policyengine_api_full.api.models.metadata.variable import ( - Variable, - VariableModule, -) - - -class MetadataModule(BaseModel): - variables: dict[str, Variable] - parameters: dict[str, Parameter | ParameterNode | ParameterScaleItem] - entities: dict[str, Entity] - variableModules: dict[str, VariableModule] - economy_options: EconomyOptions - basicInputs: list[str] - modeled_policies: Optional[ModeledPolicies] - current_law_id: int - version: str diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/modeled_policies.py b/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/modeled_policies.py deleted file mode 100644 index 1a1dab82..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/modeled_policies.py +++ /dev/null @@ -1,22 +0,0 @@ -from pydantic import BaseModel, RootModel -from typing import Optional - - -class ModeledPoliciesBreakdown(BaseModel): - modelled: list[str] - not_modelled: Optional[list[str]] = [] - - -# E.g., "IRELAND" in UK, "AZ" in US -class FilteredPoliciesEntity(RootModel): - root: dict[str, ModeledPoliciesBreakdown] - - -# E.g., "country" in UK, "state" in US -class FilteredPoliciesEntityType(RootModel): - root: dict[str, FilteredPoliciesEntity] - - -class ModeledPolicies(BaseModel): - core: ModeledPoliciesBreakdown - filtered: FilteredPoliciesEntityType diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/parameter.py b/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/parameter.py deleted file mode 100644 index e75bcc2d..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/parameter.py +++ /dev/null @@ -1,21 +0,0 @@ -from pydantic import BaseModel -from typing import Literal, Optional, Any - - -# Used for both ParameterScale and ParameterScaleBracket -class ParameterScaleItem(BaseModel): - type: Literal["parameterNode", "parameter"] - parameter: str - description: Optional[str] - label: str - - -class ParameterNode(ParameterScaleItem): - economy: bool - household: bool - - -class Parameter(ParameterNode): - unit: Optional[str] - period: Optional[str] - values: dict[str, Any] diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/variable.py b/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/variable.py deleted file mode 100644 index c887a535..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/models/metadata/variable.py +++ /dev/null @@ -1,36 +0,0 @@ -from pydantic import BaseModel -from typing import Any, Optional -from policyengine_api_full.api.enums import ( - PERIODS, - VALUE_TYPES, - ENTITIES_US, - ENTITIES_UK, - ENTITIES_GENERIC, -) - - -class Variable(BaseModel): - documentation: Optional[str] # Perhaps enforce no None's? - entity: ENTITIES_US | ENTITIES_UK | ENTITIES_GENERIC - valueType: VALUE_TYPES - definitionPeriod: PERIODS - name: str - label: str - category: Optional[str] - unit: Optional[str] # Perhaps enforce no None's? - moduleName: str - indexInModule: Optional[int] # Perhaps enforce no None's? - isInputVariable: bool - defaultValue: int | float | bool | str | None - adds: list[str] | str | None - subtracts: list[str] | str | None - hidden_input: bool - possibleValues: Optional[list[dict[str, Any]]] | None = [] - - -class VariableModule(BaseModel): - label: str - description: Optional[str] - index: Optional[ - int - ] # This is None in only one case in the UK that I've found; perhaps enforce no None's? diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/models/periods.py b/projects/policyengine-api-full/src/policyengine_api_full/api/models/periods.py deleted file mode 100644 index c4d999dc..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/models/periods.py +++ /dev/null @@ -1,13 +0,0 @@ -from pydantic import BaseModel, field_validator -import re - - -class ISO8601Date(BaseModel): - date: str - - @field_validator("date", mode="after") - @classmethod - def is_iso_8601_date(cls, value: str) -> str: - if not re.match(r"\d{4}-\d{2}-\d{2}", value): - raise ValueError("date must be in ISO 8601 format") - return value diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/routers/calculate.py b/projects/policyengine-api-full/src/policyengine_api_full/api/routers/calculate.py deleted file mode 100644 index cc6af662..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/routers/calculate.py +++ /dev/null @@ -1,32 +0,0 @@ -from fastapi import APIRouter, Body -from policyengine_api_full.api.enums import COUNTRY_ID -from policyengine_api_full.api.models.household import ( - HouseholdUS, - HouseholdUK, - HouseholdGeneric, - example_household_input_us, -) -from policyengine_api_full.api.country import COUNTRIES -from typing import Annotated - - -router = APIRouter() - - -@router.post("/{country_id}/calculate") -async def calculate( - country_id: COUNTRY_ID, - household: Annotated[ - HouseholdGeneric | HouseholdUK | HouseholdUS, - Body(examples=[example_household_input_us], embed=True), - ], -) -> HouseholdGeneric | HouseholdUK | HouseholdUS: - # Household models above currently conflict with models defined in - # household/household.py; the household routes will be brought in - # line in later iteration - country = COUNTRIES.get(country_id.value) - - result: HouseholdGeneric | HouseholdUK | HouseholdUS = country.calculate( - household=household, reform=None - ) - return result diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/routers/metadata.py b/projects/policyengine-api-full/src/policyengine_api_full/api/routers/metadata.py deleted file mode 100644 index 9ed57617..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/routers/metadata.py +++ /dev/null @@ -1,12 +0,0 @@ -from fastapi import APIRouter, Depends -from policyengine_api_full.api.enums import COUNTRY_ID -from policyengine_api_full.api.models.metadata.metadata_module import MetadataModule -from policyengine_api_full.api.country import COUNTRIES - -router = APIRouter() - - -@router.get("/{country_id}/metadata") -async def metadata(country_id: COUNTRY_ID) -> MetadataModule: - country = COUNTRIES.get(country_id.value) - return country.metadata diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/utils/__init__.py b/projects/policyengine-api-full/src/policyengine_api_full/api/utils/__init__.py deleted file mode 100644 index 6814b8c7..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/utils/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .json import get_safe_json -from .constants import ( - COUNTRIES_SET, - COUNTRY_PACKAGE_NAMES, - COUNTRY_PACKAGE_VERSIONS, - __version__, -) -from .metadata import parse_enum_possible_values, parse_default_value diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/utils/constants.py b/projects/policyengine-api-full/src/policyengine_api_full/api/utils/constants.py deleted file mode 100644 index 9c6b29ac..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/utils/constants.py +++ /dev/null @@ -1,33 +0,0 @@ -from pathlib import Path -import pkg_resources - -REPO = Path(__file__).parents[1] -GET = "GET" -POST = "POST" -UPDATE = "UPDATE" -LIST = "LIST" -VERSION = "0.5.126" -COUNTRIES_SET = ("uk", "us", "ca", "ng", "il") -COUNTRY_PACKAGE_NAMES = ( - "policyengine_uk", - "policyengine_us", - "policyengine_canada", - "policyengine_ng", - "policyengine_il", -) -try: - COUNTRY_PACKAGE_VERSIONS = { - country: pkg_resources.get_distribution(package_name).version - for country, package_name in zip(COUNTRIES_SET, COUNTRY_PACKAGE_NAMES) - } -except: - COUNTRY_PACKAGE_VERSIONS = {country: "0.0.0" for country in COUNTRIES_SET} -__version__ = VERSION - -CURRENT_LAW_IDS = { - "uk": 1, - "us": 2, - "ca": 3, - "ng": 4, - "il": 5, -} diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/utils/json.py b/projects/policyengine-api-full/src/policyengine_api_full/api/utils/json.py deleted file mode 100644 index 71788796..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/utils/json.py +++ /dev/null @@ -1,19 +0,0 @@ -import numpy as np - - -def get_safe_json(value): - # This function is used when constructing metadata, - # which will be consumed by JS, hence Infinity, not .inf - if isinstance(value, (int, float)): - if value == np.inf: - return "Infinity" - elif value == -np.inf: - return "-Infinity" - return value - if isinstance(value, str): - return value - if isinstance(value, dict): - return {k: get_safe_json(v) for k, v in value.items()} - if isinstance(value, list): - return [get_safe_json(v) for v in value] - return None diff --git a/projects/policyengine-api-full/src/policyengine_api_full/api/utils/metadata.py b/projects/policyengine-api-full/src/policyengine_api_full/api/utils/metadata.py deleted file mode 100644 index 09dffecf..00000000 --- a/projects/policyengine-api-full/src/policyengine_api_full/api/utils/metadata.py +++ /dev/null @@ -1,23 +0,0 @@ -from policyengine_core.variables import Variable as CoreVariable - - -def parse_enum_possible_values( - variable: CoreVariable, -) -> list[dict[str, str]] | None: - if variable.value_type.__name__ == "Enum": - return [ - {"value": value.name, "label": value.value} - for value in variable.possible_values - ] - else: - return None - - -def parse_default_value(variable: CoreVariable) -> str: - if variable.value_type.__name__ == "Enum": - return variable.default_value.name - - if isinstance(variable.default_value, (int, float, bool)): - return variable.default_value - - return None diff --git a/projects/policyengine-api-full/src/policyengine_api_full/database.py b/projects/policyengine-api-full/src/policyengine_api_full/database.py index 234356f6..382b296c 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/database.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/database.py @@ -2,31 +2,13 @@ from sqlalchemy.pool import NullPool import os from typing import Generator +from policyengine.database import Database -def get_supabase_url() -> str: - """Get Supabase database URL from environment or localhost.""" - # Default to local Supabase instance - db_host = os.getenv("SUPABASE_DB_HOST", "localhost") - db_port = os.getenv("SUPABASE_DB_PORT", "54322") - db_name = os.getenv("SUPABASE_DB_NAME", "postgres") - db_user = os.getenv("SUPABASE_DB_USER", "postgres") - db_password = os.getenv("SUPABASE_DB_PASSWORD", "postgres") +database = Database(os.environ.get("DATABASE_URL", "postgresql://postgres:postgres@127.0.0.1:54322/postgres")) - return f"postgresql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}" - -def create_supabase_engine(): - """Create SQLModel engine for Supabase PostgreSQL.""" - database_url = get_supabase_url() - # Use NullPool to avoid connection pooling issues with serverless - engine = create_engine( - database_url, - poolclass=NullPool, - echo=os.getenv("SQL_ECHO", "false").lower() == "true" - ) - return engine +engine = database.engine def get_session() -> Generator[Session, None, None]: """Dependency to get database session.""" - engine = create_supabase_engine() with Session(engine) as session: - yield session \ No newline at end of file + yield session diff --git a/projects/policyengine-api-full/src/policyengine_api_full/main.py b/projects/policyengine-api-full/src/policyengine_api_full/main.py index 59cdf125..8f502028 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/main.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/main.py @@ -1,106 +1,88 @@ from contextlib import asynccontextmanager -from typing import Any from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware from sqlmodel import SQLModel -from policyengine_fastapi import ping -from policyengine_fastapi.health import ( - HealthRegistry, - HealthSystemReporter, -) from policyengine_fastapi.exit import exit -from .settings import get_settings, Environment -from policyengine_fastapi.opentelemetry import ( - GCPLoggingInstrumentor, - FastAPIEnhancedInstrumenter, - export_ot_to_console, - export_ot_to_gcp, -) -from opentelemetry.sdk.resources import ( - SERVICE_NAME, - SERVICE_INSTANCE_ID, - Resource, -) -from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor -from policyengine_api_full.api import initialize -from policyengine_api_full.database import create_supabase_engine +from .settings import get_settings import logging -""" -specific example instantiation of the app configured by a .env file -* Uses Supabase PostgreSQL database -* on desktop we print opentelemetry instrumentation to the console. -* in "production" we use GCP trace/metrics bindings. -""" +# Import routers +from .routers import ( + models, + policies, + simulations, + datasets, + parameters, + dynamics, + model_versions, + baseline_variables +) -logger = logging.getLogger(__name__) +# Import database setup +from .database import engine # Import all tables to ensure they're registered with SQLModel from policyengine.database import ( - UserTable, ModelTable, - ModelVersionTable, - DatasetTable, - VersionedDatasetTable, PolicyTable, SimulationTable, + DatasetTable, + VersionedDatasetTable, ParameterTable, ParameterValueTable, BaselineParameterValueTable, BaselineVariableTable, DynamicTable, + ModelVersionTable ) -# configure database -# Use Supabase PostgreSQL instead of SQLite -engine = create_supabase_engine() +""" +specific example instantiation of the app configured by a .env file +* Uses Supabase PostgreSQL database +* on desktop we print opentelemetry instrumentation to the console. +* in "production" we use GCP trace/metrics bindings. +""" + +logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): + # Create tables on startup SQLModel.metadata.create_all(engine) - async with exit.lifespan(): - yield + yield app = FastAPI( - lifespan=lifespan, title="policyengine-api-full", summary="External facing policyengineAPI containing all features", + lifespan=lifespan ) -# attach the api defined in the app package -initialize( - app=app, - engine=engine, - jwt_issuer=get_settings().jwt_issuer, - jwt_audience=get_settings().jwt_audience, -) - -health_registry = HealthRegistry() -# TODO: we can use this to verify the db connection, etc. -# For now, we don't register any probes and it will just report -# healthy all the time. -health_registry.register(HealthSystemReporter("general", {})) -ping.include_all_routers(app, health_registry) - -# configure tracing and metrics -GCPLoggingInstrumentor().instrument() -FastAPIEnhancedInstrumenter().instrument(app) -SQLAlchemyInstrumentor().instrument( - engine=engine, enable_commenter=True, commenter_options={} +# Add CORS middleware +app.add_middleware( + CORSMiddleware, + allow_origins=["http://localhost:5173", "http://localhost:5174"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], ) -resource = Resource.create( - attributes={ - SERVICE_NAME: get_settings().ot_service_name, - SERVICE_INSTANCE_ID: get_settings().ot_service_instance_id, - } -) +# Include routers +app.include_router(models.router) +app.include_router(policies.router) +app.include_router(simulations.router) +app.include_router(datasets.datasets_router) +app.include_router(datasets.versioned_datasets_router) +app.include_router(parameters.parameters_router) +app.include_router(parameters.parameter_values_router) +app.include_router(parameters.baseline_parameter_values_router) +app.include_router(dynamics.router) +app.include_router(model_versions.router) +app.include_router(baseline_variables.router) -match get_settings().environment: - case Environment.DESKTOP: - pass # export_ot_to_console(resource) - case Environment.PRODUCTION: - export_ot_to_gcp(resource) - case value: - raise Exception(f"Forgot to handle environment value {value}") +# Health check endpoint +@app.get("/health") +def health_check(): + """Health check endpoint.""" + return {"status": "healthy"} diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/__init__.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/__init__.py new file mode 100644 index 00000000..62b4627b --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/__init__.py @@ -0,0 +1 @@ +# API routers \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/baseline_variables.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/baseline_variables.py new file mode 100644 index 00000000..c584b919 --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/baseline_variables.py @@ -0,0 +1,90 @@ +from fastapi import APIRouter, HTTPException, Depends, Query +from sqlmodel import Session, select +from policyengine.database import BaselineVariableTable +from policyengine_api_full.database import get_session +from typing import Optional + +router = APIRouter(prefix="/baseline-variables", tags=["baseline variables"]) + + +@router.get("/", response_model=list[BaselineVariableTable]) +def list_baseline_variables( + session: Session = Depends(get_session), + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + variable_name: Optional[str] = Query(None, description="Filter by variable name"), + model_id: Optional[str] = Query(None, description="Filter by model ID"), +): + """List all baseline variables with pagination and optional filters.""" + statement = select(BaselineVariableTable) + + if variable_name: + statement = statement.where(BaselineVariableTable.variable_name == variable_name) + if model_id: + statement = statement.where(BaselineVariableTable.model_id == model_id) + + statement = statement.offset(skip).limit(limit) + baseline_variables = session.exec(statement).all() + return baseline_variables + + +@router.post("/", response_model=BaselineVariableTable, status_code=201) +def create_baseline_variable( + baseline_variable: BaselineVariableTable, + session: Session = Depends(get_session), +): + """Create a new baseline variable.""" + session.add(baseline_variable) + session.commit() + session.refresh(baseline_variable) + return baseline_variable + + +@router.get("/{baseline_variable_id}", response_model=BaselineVariableTable) +def get_baseline_variable( + baseline_variable_id: str, + session: Session = Depends(get_session), +): + """Get a single baseline variable by ID.""" + baseline_variable = session.get(BaselineVariableTable, baseline_variable_id) + if not baseline_variable: + raise HTTPException(status_code=404, detail="Baseline variable not found") + return baseline_variable + + +@router.patch("/{baseline_variable_id}", response_model=BaselineVariableTable) +def update_baseline_variable( + baseline_variable_id: str, + variable_name: Optional[str] = None, + value: Optional[bytes] = None, + session: Session = Depends(get_session), +): + """Update a baseline variable.""" + db_baseline_variable = session.get(BaselineVariableTable, baseline_variable_id) + if not db_baseline_variable: + raise HTTPException(status_code=404, detail="Baseline variable not found") + + if variable_name is not None: + db_baseline_variable.variable_name = variable_name + if value is not None: + db_baseline_variable.value = value + + session.add(db_baseline_variable) + session.commit() + session.refresh(db_baseline_variable) + return db_baseline_variable + + +@router.delete("/{baseline_variable_id}", status_code=204) +def delete_baseline_variable( + baseline_variable_id: str, + session: Session = Depends(get_session), +): + """Delete a baseline variable.""" + baseline_variable = session.get(BaselineVariableTable, baseline_variable_id) + if not baseline_variable: + raise HTTPException(status_code=404, detail="Baseline variable not found") + + session.delete(baseline_variable) + session.commit() + return None \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/datasets.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/datasets.py new file mode 100644 index 00000000..74ca2b56 --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/datasets.py @@ -0,0 +1,158 @@ +from fastapi import APIRouter, HTTPException, Depends, Query +from sqlmodel import Session, select +from policyengine.database import DatasetTable, VersionedDatasetTable +from policyengine_api_full.database import get_session +from policyengine_api_full.schemas import ( + DatasetResponse, DatasetCreate, + VersionedDatasetResponse, VersionedDatasetCreate +) +from typing import Optional +from datetime import datetime +from uuid import uuid4 + +datasets_router = APIRouter(prefix="/datasets", tags=["datasets"]) +versioned_datasets_router = APIRouter(prefix="/versioned-datasets", tags=["versioned datasets"]) + + +# Dataset endpoints +@datasets_router.get("/", response_model=list[DatasetResponse]) +def list_datasets( + session: Session = Depends(get_session), + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), +): + """List all datasets with pagination.""" + statement = select(DatasetTable).offset(skip).limit(limit) + datasets = session.exec(statement).all() + return datasets + + +@datasets_router.post("/", response_model=DatasetResponse, status_code=201) +def create_dataset( + dataset: DatasetCreate, + session: Session = Depends(get_session), +): + """Create a new dataset.""" + db_dataset = DatasetTable( + created_at=datetime.utcnow(), + updated_at=datetime.utcnow(), + **dataset.model_dump() + ) + session.add(db_dataset) + session.commit() + session.refresh(db_dataset) + return db_dataset + + +@datasets_router.get("/{dataset_id}", response_model=DatasetResponse) +def get_dataset( + dataset_id: str, + session: Session = Depends(get_session), +): + """Get a single dataset by ID.""" + dataset = session.get(DatasetTable, dataset_id) + if not dataset: + raise HTTPException(status_code=404, detail="Dataset not found") + return dataset + + +@datasets_router.patch("/{dataset_id}", response_model=DatasetResponse) +def update_dataset( + dataset_id: str, + name: Optional[str] = None, + description: Optional[str] = None, + session: Session = Depends(get_session), +): + """Update a dataset.""" + db_dataset = session.get(DatasetTable, dataset_id) + if not db_dataset: + raise HTTPException(status_code=404, detail="Dataset not found") + + if name is not None: + db_dataset.name = name + if description is not None: + db_dataset.description = description + + db_dataset.updated_at = datetime.utcnow() + + session.add(db_dataset) + session.commit() + session.refresh(db_dataset) + return db_dataset + + +@datasets_router.delete("/{dataset_id}", status_code=204) +def delete_dataset( + dataset_id: str, + session: Session = Depends(get_session), +): + """Delete a dataset.""" + dataset = session.get(DatasetTable, dataset_id) + if not dataset: + raise HTTPException(status_code=404, detail="Dataset not found") + + session.delete(dataset) + session.commit() + return None + + +# Versioned Dataset endpoints +@versioned_datasets_router.get("/", response_model=list[VersionedDatasetResponse]) +def list_versioned_datasets( + session: Session = Depends(get_session), + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + dataset_id: Optional[str] = Query(None, description="Filter by dataset ID"), +): + """List all versioned datasets with pagination and optional filters.""" + statement = select(VersionedDatasetTable) + + if dataset_id: + statement = statement.where(VersionedDatasetTable.dataset_id == dataset_id) + + statement = statement.offset(skip).limit(limit) + versioned_datasets = session.exec(statement).all() + return [VersionedDatasetResponse.from_orm(v) for v in versioned_datasets] + + +@versioned_datasets_router.post("/", response_model=VersionedDatasetResponse, status_code=201) +def create_versioned_dataset( + versioned_dataset: VersionedDatasetCreate, + session: Session = Depends(get_session), +): + """Create a new versioned dataset.""" + db_versioned = VersionedDatasetTable( + id=str(uuid4()), + **versioned_dataset.to_orm_dict() + ) + session.add(db_versioned) + session.commit() + session.refresh(db_versioned) + return VersionedDatasetResponse.from_orm(db_versioned) + + +@versioned_datasets_router.get("/{versioned_dataset_id}", response_model=VersionedDatasetResponse) +def get_versioned_dataset( + versioned_dataset_id: str, + session: Session = Depends(get_session), +): + """Get a single versioned dataset by ID.""" + versioned_dataset = session.get(VersionedDatasetTable, versioned_dataset_id) + if not versioned_dataset: + raise HTTPException(status_code=404, detail="Versioned dataset not found") + return VersionedDatasetResponse.from_orm(versioned_dataset) + + +@versioned_datasets_router.delete("/{versioned_dataset_id}", status_code=204) +def delete_versioned_dataset( + versioned_dataset_id: str, + session: Session = Depends(get_session), +): + """Delete a versioned dataset.""" + versioned_dataset = session.get(VersionedDatasetTable, versioned_dataset_id) + if not versioned_dataset: + raise HTTPException(status_code=404, detail="Versioned dataset not found") + + session.delete(versioned_dataset) + session.commit() + return None \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/dynamics.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/dynamics.py new file mode 100644 index 00000000..20750ec6 --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/dynamics.py @@ -0,0 +1,84 @@ +from fastapi import APIRouter, HTTPException, Depends, Query +from sqlmodel import Session, select +from policyengine.database import DynamicTable +from policyengine_api_full.database import get_session +from typing import Optional + +router = APIRouter(prefix="/dynamics", tags=["dynamics"]) + + +@router.get("/", response_model=list[DynamicTable]) +def list_dynamics( + session: Session = Depends(get_session), + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), +): + """List all dynamics with pagination.""" + statement = select(DynamicTable).offset(skip).limit(limit) + dynamics = session.exec(statement).all() + return dynamics + + +@router.post("/", response_model=DynamicTable, status_code=201) +def create_dynamic( + dynamic: DynamicTable, + session: Session = Depends(get_session), +): + """Create a new dynamic.""" + session.add(dynamic) + session.commit() + session.refresh(dynamic) + return dynamic + + +@router.get("/{dynamic_id}", response_model=DynamicTable) +def get_dynamic( + dynamic_id: str, + session: Session = Depends(get_session), +): + """Get a single dynamic by ID.""" + dynamic = session.get(DynamicTable, dynamic_id) + if not dynamic: + raise HTTPException(status_code=404, detail="Dynamic not found") + return dynamic + + +@router.patch("/{dynamic_id}", response_model=DynamicTable) +def update_dynamic( + dynamic_id: str, + name: Optional[str] = None, + description: Optional[str] = None, + years: Optional[list[int]] = None, + session: Session = Depends(get_session), +): + """Update a dynamic.""" + db_dynamic = session.get(DynamicTable, dynamic_id) + if not db_dynamic: + raise HTTPException(status_code=404, detail="Dynamic not found") + + if name is not None: + db_dynamic.name = name + if description is not None: + db_dynamic.description = description + if years is not None: + db_dynamic.years = years + + session.add(db_dynamic) + session.commit() + session.refresh(db_dynamic) + return db_dynamic + + +@router.delete("/{dynamic_id}", status_code=204) +def delete_dynamic( + dynamic_id: str, + session: Session = Depends(get_session), +): + """Delete a dynamic.""" + dynamic = session.get(DynamicTable, dynamic_id) + if not dynamic: + raise HTTPException(status_code=404, detail="Dynamic not found") + + session.delete(dynamic) + session.commit() + return None \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/model_versions.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/model_versions.py new file mode 100644 index 00000000..c90099cb --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/model_versions.py @@ -0,0 +1,84 @@ +from fastapi import APIRouter, HTTPException, Depends, Query +from sqlmodel import Session, select +from policyengine.database import ModelVersionTable +from policyengine_api_full.database import get_session +from typing import Optional + +router = APIRouter(prefix="/model-versions", tags=["model versions"]) + + +@router.get("/", response_model=list[ModelVersionTable]) +def list_model_versions( + session: Session = Depends(get_session), + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + model_id: Optional[str] = Query(None, description="Filter by model ID"), +): + """List all model versions with pagination and optional filters.""" + statement = select(ModelVersionTable) + + if model_id: + statement = statement.where(ModelVersionTable.model_id == model_id) + + statement = statement.offset(skip).limit(limit) + model_versions = session.exec(statement).all() + return model_versions + + +@router.post("/", response_model=ModelVersionTable, status_code=201) +def create_model_version( + model_version: ModelVersionTable, + session: Session = Depends(get_session), +): + """Create a new model version.""" + session.add(model_version) + session.commit() + session.refresh(model_version) + return model_version + + +@router.get("/{model_version_id}", response_model=ModelVersionTable) +def get_model_version( + model_version_id: str, + session: Session = Depends(get_session), +): + """Get a single model version by ID.""" + model_version = session.get(ModelVersionTable, model_version_id) + if not model_version: + raise HTTPException(status_code=404, detail="Model version not found") + return model_version + + +@router.patch("/{model_version_id}", response_model=ModelVersionTable) +def update_model_version( + model_version_id: str, + version_name: Optional[str] = None, + session: Session = Depends(get_session), +): + """Update a model version.""" + db_model_version = session.get(ModelVersionTable, model_version_id) + if not db_model_version: + raise HTTPException(status_code=404, detail="Model version not found") + + if version_name is not None: + db_model_version.version_name = version_name + + session.add(db_model_version) + session.commit() + session.refresh(db_model_version) + return db_model_version + + +@router.delete("/{model_version_id}", status_code=204) +def delete_model_version( + model_version_id: str, + session: Session = Depends(get_session), +): + """Delete a model version.""" + model_version = session.get(ModelVersionTable, model_version_id) + if not model_version: + raise HTTPException(status_code=404, detail="Model version not found") + + session.delete(model_version) + session.commit() + return None \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/models.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/models.py new file mode 100644 index 00000000..b679df63 --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/models.py @@ -0,0 +1,84 @@ +from fastapi import APIRouter, HTTPException, Depends, Query +from sqlmodel import Session, select +from policyengine.database import ModelTable +from policyengine_api_full.database import get_session +from policyengine_api_full.schemas import ModelResponse, ModelCreate, ModelUpdate, decode_bytes +from typing import Optional + +router = APIRouter(prefix="/models", tags=["models"]) + + +@router.get("/", response_model=list[ModelResponse]) +def list_models( + session: Session = Depends(get_session), + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), +): + """List all models with pagination.""" + statement = select(ModelTable).offset(skip).limit(limit) + models = session.exec(statement).all() + return [ModelResponse.from_orm(m) for m in models] + + +@router.post("/", response_model=ModelResponse, status_code=201) +def create_model( + model: ModelCreate, + session: Session = Depends(get_session), +): + """Create a new model.""" + db_model = ModelTable(**model.to_orm_dict()) + session.add(db_model) + session.commit() + session.refresh(db_model) + return ModelResponse.from_orm(db_model) + + +@router.get("/{model_id}", response_model=ModelResponse) +def get_model( + model_id: str, + session: Session = Depends(get_session), +): + """Get a single model by ID.""" + model = session.get(ModelTable, model_id) + if not model: + raise HTTPException(status_code=404, detail="Model not found") + return ModelResponse.from_orm(model) + + +@router.patch("/{model_id}", response_model=ModelResponse) +def update_model( + model_id: str, + update: ModelUpdate, + session: Session = Depends(get_session), +): + """Update a model.""" + db_model = session.get(ModelTable, model_id) + if not db_model: + raise HTTPException(status_code=404, detail="Model not found") + + if update.name is not None: + db_model.name = update.name + if update.description is not None: + db_model.description = update.description + if update.simulation_function is not None: + db_model.simulation_function = decode_bytes(update.simulation_function) + + session.add(db_model) + session.commit() + session.refresh(db_model) + return ModelResponse.from_orm(db_model) + + +@router.delete("/{model_id}", status_code=204) +def delete_model( + model_id: str, + session: Session = Depends(get_session), +): + """Delete a model.""" + model = session.get(ModelTable, model_id) + if not model: + raise HTTPException(status_code=404, detail="Model not found") + + session.delete(model) + session.commit() + return None \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/parameters.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/parameters.py new file mode 100644 index 00000000..3bac4a92 --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/parameters.py @@ -0,0 +1,210 @@ +from fastapi import APIRouter, HTTPException, Depends, Query +from sqlmodel import Session, select +from policyengine.database import ( + ParameterTable, + ParameterValueTable, + BaselineParameterValueTable +) +from policyengine_api_full.database import get_session +from typing import Optional +from datetime import datetime + +parameters_router = APIRouter(prefix="/parameters", tags=["parameters"]) +parameter_values_router = APIRouter(prefix="/parameter-values", tags=["parameter values"]) +baseline_parameter_values_router = APIRouter(prefix="/baseline-parameter-values", tags=["baseline parameter values"]) + + +# Parameter endpoints +@parameters_router.get("/", response_model=list[ParameterTable]) +def list_parameters( + session: Session = Depends(get_session), + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), +): + """List all parameters with pagination.""" + statement = select(ParameterTable).offset(skip).limit(limit) + parameters = session.exec(statement).all() + return parameters + + +@parameters_router.post("/", response_model=ParameterTable, status_code=201) +def create_parameter( + parameter: ParameterTable, + session: Session = Depends(get_session), +): + """Create a new parameter.""" + session.add(parameter) + session.commit() + session.refresh(parameter) + return parameter + + +@parameters_router.get("/{parameter_id}", response_model=ParameterTable) +def get_parameter( + parameter_id: str, + session: Session = Depends(get_session), +): + """Get a single parameter by ID.""" + parameter = session.get(ParameterTable, parameter_id) + if not parameter: + raise HTTPException(status_code=404, detail="Parameter not found") + return parameter + + +@parameters_router.delete("/{parameter_id}", status_code=204) +def delete_parameter( + parameter_id: str, + session: Session = Depends(get_session), +): + """Delete a parameter.""" + parameter = session.get(ParameterTable, parameter_id) + if not parameter: + raise HTTPException(status_code=404, detail="Parameter not found") + + session.delete(parameter) + session.commit() + return None + + +# Parameter Value endpoints +@parameter_values_router.get("/", response_model=list[ParameterValueTable]) +def list_parameter_values( + session: Session = Depends(get_session), + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + parameter_id: Optional[str] = Query(None, description="Filter by parameter ID"), + policy_id: Optional[str] = Query(None, description="Filter by policy ID"), +): + """List all parameter values with pagination and optional filters.""" + statement = select(ParameterValueTable) + + if parameter_id: + statement = statement.where(ParameterValueTable.parameter_id == parameter_id) + if policy_id: + statement = statement.where(ParameterValueTable.policy_id == policy_id) + + statement = statement.offset(skip).limit(limit) + parameter_values = session.exec(statement).all() + return parameter_values + + +@parameter_values_router.post("/", response_model=ParameterValueTable, status_code=201) +def create_parameter_value( + parameter_value: ParameterValueTable, + session: Session = Depends(get_session), +): + """Create a new parameter value.""" + session.add(parameter_value) + session.commit() + session.refresh(parameter_value) + return parameter_value + + +@parameter_values_router.get("/{parameter_value_id}", response_model=ParameterValueTable) +def get_parameter_value( + parameter_value_id: str, + session: Session = Depends(get_session), +): + """Get a single parameter value by ID.""" + parameter_value = session.get(ParameterValueTable, parameter_value_id) + if not parameter_value: + raise HTTPException(status_code=404, detail="Parameter value not found") + return parameter_value + + +@parameter_values_router.patch("/{parameter_value_id}", response_model=ParameterValueTable) +def update_parameter_value( + parameter_value_id: str, + value: Optional[bytes] = None, + session: Session = Depends(get_session), +): + """Update a parameter value.""" + db_parameter_value = session.get(ParameterValueTable, parameter_value_id) + if not db_parameter_value: + raise HTTPException(status_code=404, detail="Parameter value not found") + + if value is not None: + db_parameter_value.value = value + + db_parameter_value.updated_at = datetime.utcnow() + + session.add(db_parameter_value) + session.commit() + session.refresh(db_parameter_value) + return db_parameter_value + + +@parameter_values_router.delete("/{parameter_value_id}", status_code=204) +def delete_parameter_value( + parameter_value_id: str, + session: Session = Depends(get_session), +): + """Delete a parameter value.""" + parameter_value = session.get(ParameterValueTable, parameter_value_id) + if not parameter_value: + raise HTTPException(status_code=404, detail="Parameter value not found") + + session.delete(parameter_value) + session.commit() + return None + + +# Baseline Parameter Value endpoints +@baseline_parameter_values_router.get("/", response_model=list[BaselineParameterValueTable]) +def list_baseline_parameter_values( + session: Session = Depends(get_session), + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + parameter_id: Optional[str] = Query(None, description="Filter by parameter ID"), + model_id: Optional[str] = Query(None, description="Filter by model ID"), +): + """List all baseline parameter values with pagination and optional filters.""" + statement = select(BaselineParameterValueTable) + + if parameter_id: + statement = statement.where(BaselineParameterValueTable.parameter_id == parameter_id) + if model_id: + statement = statement.where(BaselineParameterValueTable.model_id == model_id) + + statement = statement.offset(skip).limit(limit) + baseline_parameter_values = session.exec(statement).all() + return baseline_parameter_values + + +@baseline_parameter_values_router.post("/", response_model=BaselineParameterValueTable, status_code=201) +def create_baseline_parameter_value( + baseline_parameter_value: BaselineParameterValueTable, + session: Session = Depends(get_session), +): + """Create a new baseline parameter value.""" + session.add(baseline_parameter_value) + session.commit() + session.refresh(baseline_parameter_value) + return baseline_parameter_value + + +@baseline_parameter_values_router.get("/{baseline_parameter_value_id}", response_model=BaselineParameterValueTable) +def get_baseline_parameter_value( + baseline_parameter_value_id: str, + session: Session = Depends(get_session), +): + """Get a single baseline parameter value by ID.""" + baseline_parameter_value = session.get(BaselineParameterValueTable, baseline_parameter_value_id) + if not baseline_parameter_value: + raise HTTPException(status_code=404, detail="Baseline parameter value not found") + return baseline_parameter_value + + +@baseline_parameter_values_router.delete("/{baseline_parameter_value_id}", status_code=204) +def delete_baseline_parameter_value( + baseline_parameter_value_id: str, + session: Session = Depends(get_session), +): + """Delete a baseline parameter value.""" + baseline_parameter_value = session.get(BaselineParameterValueTable, baseline_parameter_value_id) + if not baseline_parameter_value: + raise HTTPException(status_code=404, detail="Baseline parameter value not found") + + session.delete(baseline_parameter_value) + session.commit() + return None \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/policies.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/policies.py new file mode 100644 index 00000000..4e0bf4c2 --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/policies.py @@ -0,0 +1,93 @@ +from fastapi import APIRouter, HTTPException, Depends, Query +from sqlmodel import Session, select +from policyengine.database import PolicyTable +from policyengine_api_full.database import get_session +from policyengine_api_full.schemas import PolicyResponse, PolicyCreate, PolicyUpdate, decode_bytes +from typing import Optional +from datetime import datetime +from uuid import uuid4 + +router = APIRouter(prefix="/policies", tags=["policies"]) + + +@router.get("/", response_model=list[PolicyResponse]) +def list_policies( + session: Session = Depends(get_session), + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), +): + """List all policies with pagination.""" + statement = select(PolicyTable).offset(skip).limit(limit) + policies = session.exec(statement).all() + return [PolicyResponse.from_orm(p) for p in policies] + + +@router.post("/", response_model=PolicyResponse, status_code=201) +def create_policy( + policy: PolicyCreate, + session: Session = Depends(get_session), +): + """Create a new policy.""" + db_policy = PolicyTable( + id=str(uuid4()), + created_at=datetime.utcnow(), + updated_at=datetime.utcnow(), + **policy.to_orm_dict() + ) + session.add(db_policy) + session.commit() + session.refresh(db_policy) + return PolicyResponse.from_orm(db_policy) + + +@router.get("/{policy_id}", response_model=PolicyResponse) +def get_policy( + policy_id: str, + session: Session = Depends(get_session), +): + """Get a single policy by ID.""" + policy = session.get(PolicyTable, policy_id) + if not policy: + raise HTTPException(status_code=404, detail="Policy not found") + return PolicyResponse.from_orm(policy) + + +@router.patch("/{policy_id}", response_model=PolicyResponse) +def update_policy( + policy_id: str, + update: PolicyUpdate, + session: Session = Depends(get_session), +): + """Update a policy.""" + db_policy = session.get(PolicyTable, policy_id) + if not db_policy: + raise HTTPException(status_code=404, detail="Policy not found") + + if update.name is not None: + db_policy.name = update.name + if update.description is not None: + db_policy.description = update.description + if update.simulation_modifier is not None: + db_policy.simulation_modifier = decode_bytes(update.simulation_modifier) + + db_policy.updated_at = datetime.utcnow() + + session.add(db_policy) + session.commit() + session.refresh(db_policy) + return PolicyResponse.from_orm(db_policy) + + +@router.delete("/{policy_id}", status_code=204) +def delete_policy( + policy_id: str, + session: Session = Depends(get_session), +): + """Delete a policy.""" + policy = session.get(PolicyTable, policy_id) + if not policy: + raise HTTPException(status_code=404, detail="Policy not found") + + session.delete(policy) + session.commit() + return None \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/simulations.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/simulations.py new file mode 100644 index 00000000..0f511201 --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/simulations.py @@ -0,0 +1,116 @@ +from fastapi import APIRouter, HTTPException, Depends, Query +from sqlmodel import Session, select +from policyengine.database import SimulationTable +from policyengine_api_full.database import get_session +from policyengine_api_full.schemas import SimulationResponse, SimulationCreate, decode_bytes +from typing import Optional +from datetime import datetime +from uuid import uuid4 + +router = APIRouter(prefix="/simulations", tags=["simulations"]) + + +@router.get("/", response_model=list[SimulationResponse]) +def list_simulations( + session: Session = Depends(get_session), + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + policy_id: Optional[str] = Query(None, description="Filter by policy ID"), + dataset_id: Optional[str] = Query(None, description="Filter by dataset ID"), + model_id: Optional[str] = Query(None, description="Filter by model ID"), +): + """List all simulations with pagination and optional filters.""" + statement = select(SimulationTable) + + if policy_id: + statement = statement.where(SimulationTable.policy_id == policy_id) + if dataset_id: + statement = statement.where(SimulationTable.dataset_id == dataset_id) + if model_id: + statement = statement.where(SimulationTable.model_id == model_id) + + statement = statement.offset(skip).limit(limit) + simulations = session.exec(statement).all() + return [SimulationResponse.from_orm(s) for s in simulations] + + +@router.post("/", response_model=SimulationResponse, status_code=201) +def create_simulation( + simulation: SimulationCreate, + session: Session = Depends(get_session), +): + """Create a new simulation.""" + db_simulation = SimulationTable( + id=str(uuid4()), + created_at=datetime.utcnow(), + updated_at=datetime.utcnow(), + **simulation.to_orm_dict() + ) + session.add(db_simulation) + session.commit() + session.refresh(db_simulation) + return SimulationResponse.from_orm(db_simulation) + + +@router.get("/{simulation_id}", response_model=SimulationResponse) +def get_simulation( + simulation_id: str, + session: Session = Depends(get_session), +): + """Get a single simulation by ID.""" + simulation = session.get(SimulationTable, simulation_id) + if not simulation: + raise HTTPException(status_code=404, detail="Simulation not found") + return SimulationResponse.from_orm(simulation) + + +@router.patch("/{simulation_id}", response_model=SimulationResponse) +def update_simulation( + simulation_id: str, + policy_id: Optional[str] = None, + dynamic_id: Optional[str] = None, + dataset_id: Optional[str] = None, + model_id: Optional[str] = None, + model_version_id: Optional[str] = None, + result: Optional[str] = None, + session: Session = Depends(get_session), +): + """Update a simulation.""" + db_simulation = session.get(SimulationTable, simulation_id) + if not db_simulation: + raise HTTPException(status_code=404, detail="Simulation not found") + + if policy_id is not None: + db_simulation.policy_id = policy_id + if dynamic_id is not None: + db_simulation.dynamic_id = dynamic_id + if dataset_id is not None: + db_simulation.dataset_id = dataset_id + if model_id is not None: + db_simulation.model_id = model_id + if model_version_id is not None: + db_simulation.model_version_id = model_version_id + if result is not None: + db_simulation.result = decode_bytes(result) + + db_simulation.updated_at = datetime.utcnow() + + session.add(db_simulation) + session.commit() + session.refresh(db_simulation) + return SimulationResponse.from_orm(db_simulation) + + +@router.delete("/{simulation_id}", status_code=204) +def delete_simulation( + simulation_id: str, + session: Session = Depends(get_session), +): + """Delete a simulation.""" + simulation = session.get(SimulationTable, simulation_id) + if not simulation: + raise HTTPException(status_code=404, detail="Simulation not found") + + session.delete(simulation) + session.commit() + return None \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/schemas.py b/projects/policyengine-api-full/src/policyengine_api_full/schemas.py new file mode 100644 index 00000000..1f98b57e --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/schemas.py @@ -0,0 +1,350 @@ +"""Pydantic schemas for API responses that handle binary data properly.""" + +from pydantic import BaseModel, Field, ConfigDict +from typing import Optional +from datetime import datetime +import base64 + + +def encode_bytes(b: bytes | None) -> str | None: + """Encode bytes to base64 string for JSON serialization.""" + if b is None: + return None + return base64.b64encode(b).decode('utf-8') + + +def decode_bytes(s: str | None) -> bytes | None: + """Decode base64 string back to bytes.""" + if s is None: + return None + return base64.b64decode(s) + + +# Model schemas +class ModelResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: str + name: str + description: Optional[str] = None + simulation_function: Optional[str] = Field(None, description="Base64 encoded simulation function") + + @staticmethod + def from_orm(obj): + return ModelResponse( + id=obj.id, + name=obj.name, + description=obj.description, + simulation_function=encode_bytes(obj.simulation_function) + ) + + +class ModelCreate(BaseModel): + id: str + name: str + description: Optional[str] = None + simulation_function: str # Base64 encoded + + def to_orm_dict(self): + return { + "id": self.id, + "name": self.name, + "description": self.description, + "simulation_function": decode_bytes(self.simulation_function) + } + + +class ModelUpdate(BaseModel): + name: Optional[str] = None + description: Optional[str] = None + simulation_function: Optional[str] = None # Base64 encoded + + +# Policy schemas +class PolicyResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: str + name: str + description: Optional[str] = None + simulation_modifier: Optional[str] = Field(None, description="Base64 encoded simulation modifier") + created_at: datetime + updated_at: datetime + + @staticmethod + def from_orm(obj): + return PolicyResponse( + id=obj.id, + name=obj.name, + description=obj.description, + simulation_modifier=encode_bytes(obj.simulation_modifier), + created_at=obj.created_at, + updated_at=obj.updated_at + ) + + +class PolicyCreate(BaseModel): + name: str + description: Optional[str] = None + simulation_modifier: Optional[str] = None # Base64 encoded + + def to_orm_dict(self): + return { + "name": self.name, + "description": self.description, + "simulation_modifier": decode_bytes(self.simulation_modifier) if self.simulation_modifier else None + } + + +class PolicyUpdate(BaseModel): + name: Optional[str] = None + description: Optional[str] = None + simulation_modifier: Optional[str] = None # Base64 encoded + + +# Simulation schemas +class SimulationResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: str + created_at: datetime + updated_at: datetime + policy_id: Optional[str] = None + dynamic_id: Optional[str] = None + dataset_id: str + model_id: str + model_version_id: Optional[str] = None + result: Optional[str] = Field(None, description="Base64 encoded result") + + @staticmethod + def from_orm(obj): + return SimulationResponse( + id=obj.id, + created_at=obj.created_at, + updated_at=obj.updated_at, + policy_id=obj.policy_id, + dynamic_id=obj.dynamic_id, + dataset_id=obj.dataset_id, + model_id=obj.model_id, + model_version_id=obj.model_version_id, + result=encode_bytes(obj.result) + ) + + +class SimulationCreate(BaseModel): + policy_id: Optional[str] = None + dynamic_id: Optional[str] = None + dataset_id: str + model_id: str + model_version_id: Optional[str] = None + result: Optional[str] = None # Base64 encoded + + def to_orm_dict(self): + return { + "policy_id": self.policy_id, + "dynamic_id": self.dynamic_id, + "dataset_id": self.dataset_id, + "model_id": self.model_id, + "model_version_id": self.model_version_id, + "result": decode_bytes(self.result) if self.result else None + } + + +# Dataset schemas +class DatasetResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: str + name: str + description: Optional[str] = None + created_at: datetime + updated_at: datetime + + +class DatasetCreate(BaseModel): + id: str + name: str + description: Optional[str] = None + + +# Versioned Dataset schemas +class VersionedDatasetResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: str + dataset_id: str + version_name: str + data: Optional[str] = Field(None, description="Base64 encoded data") + + @staticmethod + def from_orm(obj): + return VersionedDatasetResponse( + id=obj.id, + dataset_id=obj.dataset_id, + version_name=obj.version_name, + data=encode_bytes(obj.data) + ) + + +class VersionedDatasetCreate(BaseModel): + dataset_id: str + version_name: str + data: Optional[str] = None # Base64 encoded + + def to_orm_dict(self): + return { + "dataset_id": self.dataset_id, + "version_name": self.version_name, + "data": decode_bytes(self.data) if self.data else None + } + + +# Parameter schemas +class ParameterResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: str + name: str + description: Optional[str] = None + model_id: str + + +class ParameterCreate(BaseModel): + id: str + name: str + description: Optional[str] = None + model_id: str + + +# Parameter Value schemas +class ParameterValueResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: str + parameter_id: str + policy_id: str + value: Optional[str] = Field(None, description="Base64 encoded value") + created_at: datetime + updated_at: datetime + + @staticmethod + def from_orm(obj): + return ParameterValueResponse( + id=obj.id, + parameter_id=obj.parameter_id, + policy_id=obj.policy_id, + value=encode_bytes(obj.value), + created_at=obj.created_at, + updated_at=obj.updated_at + ) + + +class ParameterValueCreate(BaseModel): + parameter_id: str + policy_id: str + value: Optional[str] = None # Base64 encoded + + def to_orm_dict(self): + return { + "parameter_id": self.parameter_id, + "policy_id": self.policy_id, + "value": decode_bytes(self.value) if self.value else None + } + + +# Baseline Parameter Value schemas +class BaselineParameterValueResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: str + parameter_id: str + model_id: str + value: Optional[str] = Field(None, description="Base64 encoded value") + + @staticmethod + def from_orm(obj): + return BaselineParameterValueResponse( + id=obj.id, + parameter_id=obj.parameter_id, + model_id=obj.model_id, + value=encode_bytes(obj.value) + ) + + +class BaselineParameterValueCreate(BaseModel): + parameter_id: str + model_id: str + value: Optional[str] = None # Base64 encoded + + def to_orm_dict(self): + return { + "parameter_id": self.parameter_id, + "model_id": self.model_id, + "value": decode_bytes(self.value) if self.value else None + } + + +# Baseline Variable schemas +class BaselineVariableResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: str + variable_name: str + model_id: str + model_version_id: Optional[str] = None + value: Optional[str] = Field(None, description="Base64 encoded value") + + @staticmethod + def from_orm(obj): + return BaselineVariableResponse( + id=obj.id, + variable_name=obj.variable_name, + model_id=obj.model_id, + model_version_id=obj.model_version_id, + value=encode_bytes(obj.value) + ) + + +class BaselineVariableCreate(BaseModel): + variable_name: str + model_id: str + model_version_id: Optional[str] = None + value: Optional[str] = None # Base64 encoded + + def to_orm_dict(self): + return { + "variable_name": self.variable_name, + "model_id": self.model_id, + "model_version_id": self.model_version_id, + "value": decode_bytes(self.value) if self.value else None + } + + +# Dynamic schemas +class DynamicResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: str + name: str + description: Optional[str] = None + years: list[int] + + +class DynamicCreate(BaseModel): + name: str + description: Optional[str] = None + years: list[int] + + +# Model Version schemas +class ModelVersionResponse(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: str + model_id: str + version_name: str + + +class ModelVersionCreate(BaseModel): + model_id: str + version_name: str \ No newline at end of file diff --git a/projects/policyengine-api-full/uv.lock b/projects/policyengine-api-full/uv.lock index 50d50704..a0c1c20b 100644 --- a/projects/policyengine-api-full/uv.lock +++ b/projects/policyengine-api-full/uv.lock @@ -27,24 +27,24 @@ wheels = [ [[package]] name = "anyio" -version = "4.10.0" +version = "4.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "sniffio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, ] [[package]] name = "asgiref" -version = "3.9.1" +version = "3.9.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/61/0aa957eec22ff70b830b22ff91f825e70e1ef732c06666a805730f28b36b/asgiref-3.9.1.tar.gz", hash = "sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142", size = 36870, upload-time = "2025-07-08T09:07:43.344Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/bf/0f3ecda32f1cb3bf1dca480aca08a7a8a3bdc4bed2343a103f30731565c9/asgiref-3.9.2.tar.gz", hash = "sha256:a0249afacb66688ef258ffe503528360443e2b9a8d8c4581b6ebefa58c841ef1", size = 36894, upload-time = "2025-09-23T15:00:55.136Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/3c/0464dcada90d5da0e71018c04a140ad6349558afb30b3051b4264cc5b965/asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c", size = 23790, upload-time = "2025-07-08T09:07:41.548Z" }, + { url = "https://files.pythonhosted.org/packages/c7/d1/69d02ce34caddb0a7ae088b84c356a625a93cd4ff57b2f97644c03fad905/asgiref-3.9.2-py3-none-any.whl", hash = "sha256:0b61526596219d70396548fc003635056856dba5d0d086f86476f10b33c75960", size = 23788, upload-time = "2025-09-23T15:00:53.627Z" }, ] [[package]] @@ -67,7 +67,7 @@ wheels = [ [[package]] name = "black" -version = "25.1.0" +version = "25.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -75,14 +75,15 @@ dependencies = [ { name = "packaging" }, { name = "pathspec" }, { name = "platformdirs" }, + { name = "pytokens" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload-time = "2025-01-29T04:15:40.373Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/43/20b5c90612d7bdb2bdbcceeb53d588acca3bb8f0e4c5d5c751a2c8fdd55a/black-25.9.0.tar.gz", hash = "sha256:0474bca9a0dd1b51791fcc507a4e02078a1c63f6d4e4ae5544b9848c7adfb619", size = 648393, upload-time = "2025-09-19T00:27:37.758Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673, upload-time = "2025-01-29T05:37:20.574Z" }, - { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190, upload-time = "2025-01-29T05:37:22.106Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926, upload-time = "2025-01-29T04:18:58.564Z" }, - { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613, upload-time = "2025-01-29T04:19:27.63Z" }, - { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" }, + { url = "https://files.pythonhosted.org/packages/48/99/3acfea65f5e79f45472c45f87ec13037b506522719cd9d4ac86484ff51ac/black-25.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0172a012f725b792c358d57fe7b6b6e8e67375dd157f64fa7a3097b3ed3e2175", size = 1742165, upload-time = "2025-09-19T00:34:10.402Z" }, + { url = "https://files.pythonhosted.org/packages/3a/18/799285282c8236a79f25d590f0222dbd6850e14b060dfaa3e720241fd772/black-25.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3bec74ee60f8dfef564b573a96b8930f7b6a538e846123d5ad77ba14a8d7a64f", size = 1581259, upload-time = "2025-09-19T00:32:49.685Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ce/883ec4b6303acdeca93ee06b7622f1fa383c6b3765294824165d49b1a86b/black-25.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b756fc75871cb1bcac5499552d771822fd9db5a2bb8db2a7247936ca48f39831", size = 1655583, upload-time = "2025-09-19T00:30:44.505Z" }, + { url = "https://files.pythonhosted.org/packages/21/17/5c253aa80a0639ccc427a5c7144534b661505ae2b5a10b77ebe13fa25334/black-25.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:846d58e3ce7879ec1ffe816bb9df6d006cd9590515ed5d17db14e17666b2b357", size = 1343428, upload-time = "2025-09-19T00:32:13.839Z" }, + { url = "https://files.pythonhosted.org/packages/1b/46/863c90dcd3f9d41b109b7f19032ae0db021f0b2a81482ba0a1e28c84de86/black-25.9.0-py3-none-any.whl", hash = "sha256:474b34c1342cdc157d307b56c4c65bce916480c4a8f6551fdc6bf9b486a7c4ae", size = 203363, upload-time = "2025-09-19T00:27:35.724Z" }, ] [[package]] @@ -101,6 +102,38 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/06/d4/0c3cdaf34b3ef705fdab465ad8df4a3bce5bbdf2bca8f2515eae90ae28a0/blosc-1.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:73721c1949f2b8d2f4168cababbfe6280511f0da9a971ba7ec9c56eab9603824", size = 1815688, upload-time = "2025-05-17T11:49:51.434Z" }, ] +[[package]] +name = "blosc2" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "msgpack" }, + { name = "ndindex" }, + { name = "numexpr", marker = "platform_machine != 'wasm32'" }, + { name = "numpy" }, + { name = "platformdirs" }, + { name = "py-cpuinfo", marker = "platform_machine != 'wasm32'" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/1f/143ceeff8b6ab9b020465ad53b2b05693496b4b4f9b95732b9a22a9872b9/blosc2-3.8.0.tar.gz", hash = "sha256:7bc8a2424f44414254b8c22831aeca5e4d269211cfb308cceda2178d5d47988e", size = 3813474, upload-time = "2025-09-11T12:59:43.067Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/a3/455354b850c3431adb13e4934bd20df8febd91ea0e521a4fb0df517b7be1/blosc2-3.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8672ba19a5bb2d0273c1a818a4b0237783770ef12812dbfd51f9b06831474400", size = 3983120, upload-time = "2025-09-11T12:59:20.123Z" }, + { url = "https://files.pythonhosted.org/packages/b0/45/b0dca1e5d94603d0eb9c020b89c5f4fe280c985f7934a762a19e52f35fae/blosc2-3.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4fdfc95f49d759997eb015bee1e104c6259ca9371f67bfb6f540380cc37b3cf8", size = 3443798, upload-time = "2025-09-11T12:59:21.39Z" }, + { url = "https://files.pythonhosted.org/packages/a5/f0/ca6052c04f273927a0ec2c04fc050dd3f4f209f7e3b1034caec01baa38f8/blosc2-3.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0da90047dfdaa7a3113749cbe0aeca4a76125b4f0d9ac2ea25242b9e7bfaa7bc", size = 4320415, upload-time = "2025-09-11T12:59:22.929Z" }, + { url = "https://files.pythonhosted.org/packages/f5/b9/101c4c48b72f4c2c3a6dd3ba807a154e3aee93bce77317a7c3ffc60550a8/blosc2-3.8.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cf1dae9e7abbee17a2f33a0b4d3b3a7ed3853b8383c410e00897f91097fb22d2", size = 4458787, upload-time = "2025-09-11T12:59:24.73Z" }, + { url = "https://files.pythonhosted.org/packages/ed/4a/e299b1bb5a4ef23182d00561fade103cbb37914094351ffba418a3d08cda/blosc2-3.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:ddeb3cde6dbf645019a4b9608e5948e97d16e70a53e0c5653b8b713192efaa41", size = 2251703, upload-time = "2025-09-11T12:59:25.924Z" }, + { url = "https://files.pythonhosted.org/packages/de/1a/0c73c51366b582b9cc1ba55ba22e71fe37e40904a5d5f444fe33eb0295aa/blosc2-3.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0761e2364868556f1d90af9e6db7463914c8753152fc2203e1de586fbc8246ee", size = 3982673, upload-time = "2025-09-11T12:59:27.652Z" }, + { url = "https://files.pythonhosted.org/packages/50/3b/d999842c65704b4c23b861dcc83c11cb44d98f42f5fcb30a6bc33fd206b8/blosc2-3.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:21dd1417ceb0330565f25cc2578da2bee077b9b12fc96b62d15e10e46099e95f", size = 3445764, upload-time = "2025-09-11T12:59:28.896Z" }, + { url = "https://files.pythonhosted.org/packages/78/dc/4de44c8356f00276172e50e49ff2ce7e041baf0337668939132fc7c7d8f3/blosc2-3.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7311e05a66f20ad8c508231dacb4047a86a32c6fdbd4eed90e5d16d2cb4f344d", size = 4325844, upload-time = "2025-09-11T12:59:30.23Z" }, + { url = "https://files.pythonhosted.org/packages/29/76/de080cadbd784894f4c896f19c3dfa0a0b75e2aa1105648663f04831fe81/blosc2-3.8.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28c969da68d0e8cf5bdd680cb47871a7bbeecd2d2a447df747720a23e2d5f025", size = 4459072, upload-time = "2025-09-11T12:59:32.323Z" }, + { url = "https://files.pythonhosted.org/packages/91/e0/85395d1b6818af87f112a7668c036e06886db075b25b57ed8c36707614c9/blosc2-3.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:dcea243789f4d3878c612d29a15e246b357758f0d414b18ad8ce36ff3f5ee969", size = 2297197, upload-time = "2025-09-11T12:59:33.608Z" }, + { url = "https://files.pythonhosted.org/packages/f3/fe/d6bb2f7ee79700d80582f75f639e7b85ed8a1a970f6b12814a3d60ec8825/blosc2-3.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c6ee1452003e6e2b65832f762b3d72707c09c9c8245e3e10cbcc4a64299b9121", size = 3999629, upload-time = "2025-09-11T12:59:34.885Z" }, + { url = "https://files.pythonhosted.org/packages/6b/47/09ce0fbbe728c4e18e6fd452dd81086d6831f0b6c351f7f46fa6984cb50a/blosc2-3.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:310f602859f1a90511e65fe978f8630f1d9348eff8d56df34c8c6cc16b5497c0", size = 3466486, upload-time = "2025-09-11T12:59:36.199Z" }, + { url = "https://files.pythonhosted.org/packages/05/21/5f5e8e2dc51fde553b199f142a1a3fc40951dbdecd835e6bd3bf9c3d8552/blosc2-3.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:605d2173c9f8c906a5696c52d9ade9a6039f8ad1989f55a9c97839b43936b5f1", size = 4312492, upload-time = "2025-09-11T12:59:37.835Z" }, + { url = "https://files.pythonhosted.org/packages/01/2d/8d46ef891f69ff13a5de6a7d7d8975601f3063d381d8ebc0ddaff5e9cc6f/blosc2-3.8.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ec0ba98217b634c4aac108b31175599212f3811d6ca18a59372819ef88740a3e", size = 4446401, upload-time = "2025-09-11T12:59:39.294Z" }, + { url = "https://files.pythonhosted.org/packages/ec/17/8e6e97ea52c6483a7edb2cfc08e12849cbf34e6e9231d5bfe7f2dcf4b8c6/blosc2-3.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6d1bb803b3185d5bdd3e47a5dbcf578483e2b2f04155be5c4278cbd039a163cb", size = 2343326, upload-time = "2025-09-11T12:59:41.446Z" }, +] + [[package]] name = "cachetools" version = "5.5.2" @@ -161,14 +194,14 @@ wheels = [ [[package]] name = "click" -version = "8.2.1" +version = "8.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, + { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, ] [[package]] @@ -200,55 +233,63 @@ wheels = [ [[package]] name = "coverage" -version = "7.10.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/14/70/025b179c993f019105b79575ac6edb5e084fb0f0e63f15cdebef4e454fb5/coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90", size = 823736, upload-time = "2025-08-29T15:35:16.668Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/e7/917e5953ea29a28c1057729c1d5af9084ab6d9c66217523fd0e10f14d8f6/coverage-7.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6", size = 217351, upload-time = "2025-08-29T15:33:45.438Z" }, - { url = "https://files.pythonhosted.org/packages/eb/86/2e161b93a4f11d0ea93f9bebb6a53f113d5d6e416d7561ca41bb0a29996b/coverage-7.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80", size = 217600, upload-time = "2025-08-29T15:33:47.269Z" }, - { url = "https://files.pythonhosted.org/packages/0e/66/d03348fdd8df262b3a7fb4ee5727e6e4936e39e2f3a842e803196946f200/coverage-7.10.6-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003", size = 248600, upload-time = "2025-08-29T15:33:48.953Z" }, - { url = "https://files.pythonhosted.org/packages/73/dd/508420fb47d09d904d962f123221bc249f64b5e56aa93d5f5f7603be475f/coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27", size = 251206, upload-time = "2025-08-29T15:33:50.697Z" }, - { url = "https://files.pythonhosted.org/packages/e9/1f/9020135734184f439da85c70ea78194c2730e56c2d18aee6e8ff1719d50d/coverage-7.10.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4", size = 252478, upload-time = "2025-08-29T15:33:52.303Z" }, - { url = "https://files.pythonhosted.org/packages/a4/a4/3d228f3942bb5a2051fde28c136eea23a761177dc4ff4ef54533164ce255/coverage-7.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d", size = 250637, upload-time = "2025-08-29T15:33:53.67Z" }, - { url = "https://files.pythonhosted.org/packages/36/e3/293dce8cdb9a83de971637afc59b7190faad60603b40e32635cbd15fbf61/coverage-7.10.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc", size = 248529, upload-time = "2025-08-29T15:33:55.022Z" }, - { url = "https://files.pythonhosted.org/packages/90/26/64eecfa214e80dd1d101e420cab2901827de0e49631d666543d0e53cf597/coverage-7.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc", size = 250143, upload-time = "2025-08-29T15:33:56.386Z" }, - { url = "https://files.pythonhosted.org/packages/3e/70/bd80588338f65ea5b0d97e424b820fb4068b9cfb9597fbd91963086e004b/coverage-7.10.6-cp313-cp313-win32.whl", hash = "sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e", size = 219770, upload-time = "2025-08-29T15:33:58.063Z" }, - { url = "https://files.pythonhosted.org/packages/a7/14/0b831122305abcc1060c008f6c97bbdc0a913ab47d65070a01dc50293c2b/coverage-7.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32", size = 220566, upload-time = "2025-08-29T15:33:59.766Z" }, - { url = "https://files.pythonhosted.org/packages/83/c6/81a83778c1f83f1a4a168ed6673eeedc205afb562d8500175292ca64b94e/coverage-7.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2", size = 219195, upload-time = "2025-08-29T15:34:01.191Z" }, - { url = "https://files.pythonhosted.org/packages/d7/1c/ccccf4bf116f9517275fa85047495515add43e41dfe8e0bef6e333c6b344/coverage-7.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b", size = 218059, upload-time = "2025-08-29T15:34:02.91Z" }, - { url = "https://files.pythonhosted.org/packages/92/97/8a3ceff833d27c7492af4f39d5da6761e9ff624831db9e9f25b3886ddbca/coverage-7.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393", size = 218287, upload-time = "2025-08-29T15:34:05.106Z" }, - { url = "https://files.pythonhosted.org/packages/92/d8/50b4a32580cf41ff0423777a2791aaf3269ab60c840b62009aec12d3970d/coverage-7.10.6-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27", size = 259625, upload-time = "2025-08-29T15:34:06.575Z" }, - { url = "https://files.pythonhosted.org/packages/7e/7e/6a7df5a6fb440a0179d94a348eb6616ed4745e7df26bf2a02bc4db72c421/coverage-7.10.6-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df", size = 261801, upload-time = "2025-08-29T15:34:08.006Z" }, - { url = "https://files.pythonhosted.org/packages/3a/4c/a270a414f4ed5d196b9d3d67922968e768cd971d1b251e1b4f75e9362f75/coverage-7.10.6-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb", size = 264027, upload-time = "2025-08-29T15:34:09.806Z" }, - { url = "https://files.pythonhosted.org/packages/9c/8b/3210d663d594926c12f373c5370bf1e7c5c3a427519a8afa65b561b9a55c/coverage-7.10.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282", size = 261576, upload-time = "2025-08-29T15:34:11.585Z" }, - { url = "https://files.pythonhosted.org/packages/72/d0/e1961eff67e9e1dba3fc5eb7a4caf726b35a5b03776892da8d79ec895775/coverage-7.10.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4", size = 259341, upload-time = "2025-08-29T15:34:13.159Z" }, - { url = "https://files.pythonhosted.org/packages/3a/06/d6478d152cd189b33eac691cba27a40704990ba95de49771285f34a5861e/coverage-7.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21", size = 260468, upload-time = "2025-08-29T15:34:14.571Z" }, - { url = "https://files.pythonhosted.org/packages/ed/73/737440247c914a332f0b47f7598535b29965bf305e19bbc22d4c39615d2b/coverage-7.10.6-cp313-cp313t-win32.whl", hash = "sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0", size = 220429, upload-time = "2025-08-29T15:34:16.394Z" }, - { url = "https://files.pythonhosted.org/packages/bd/76/b92d3214740f2357ef4a27c75a526eb6c28f79c402e9f20a922c295c05e2/coverage-7.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5", size = 221493, upload-time = "2025-08-29T15:34:17.835Z" }, - { url = "https://files.pythonhosted.org/packages/fc/8e/6dcb29c599c8a1f654ec6cb68d76644fe635513af16e932d2d4ad1e5ac6e/coverage-7.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b", size = 219757, upload-time = "2025-08-29T15:34:19.248Z" }, - { url = "https://files.pythonhosted.org/packages/d3/aa/76cf0b5ec00619ef208da4689281d48b57f2c7fde883d14bf9441b74d59f/coverage-7.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6008a021907be8c4c02f37cdc3ffb258493bdebfeaf9a839f9e71dfdc47b018e", size = 217331, upload-time = "2025-08-29T15:34:20.846Z" }, - { url = "https://files.pythonhosted.org/packages/65/91/8e41b8c7c505d398d7730206f3cbb4a875a35ca1041efc518051bfce0f6b/coverage-7.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5e75e37f23eb144e78940b40395b42f2321951206a4f50e23cfd6e8a198d3ceb", size = 217607, upload-time = "2025-08-29T15:34:22.433Z" }, - { url = "https://files.pythonhosted.org/packages/87/7f/f718e732a423d442e6616580a951b8d1ec3575ea48bcd0e2228386805e79/coverage-7.10.6-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0f7cb359a448e043c576f0da00aa8bfd796a01b06aa610ca453d4dde09cc1034", size = 248663, upload-time = "2025-08-29T15:34:24.425Z" }, - { url = "https://files.pythonhosted.org/packages/e6/52/c1106120e6d801ac03e12b5285e971e758e925b6f82ee9b86db3aa10045d/coverage-7.10.6-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c68018e4fc4e14b5668f1353b41ccf4bc83ba355f0e1b3836861c6f042d89ac1", size = 251197, upload-time = "2025-08-29T15:34:25.906Z" }, - { url = "https://files.pythonhosted.org/packages/3d/ec/3a8645b1bb40e36acde9c0609f08942852a4af91a937fe2c129a38f2d3f5/coverage-7.10.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd4b2b0707fc55afa160cd5fc33b27ccbf75ca11d81f4ec9863d5793fc6df56a", size = 252551, upload-time = "2025-08-29T15:34:27.337Z" }, - { url = "https://files.pythonhosted.org/packages/a1/70/09ecb68eeb1155b28a1d16525fd3a9b65fbe75337311a99830df935d62b6/coverage-7.10.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cec13817a651f8804a86e4f79d815b3b28472c910e099e4d5a0e8a3b6a1d4cb", size = 250553, upload-time = "2025-08-29T15:34:29.065Z" }, - { url = "https://files.pythonhosted.org/packages/c6/80/47df374b893fa812e953b5bc93dcb1427a7b3d7a1a7d2db33043d17f74b9/coverage-7.10.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f2a6a8e06bbda06f78739f40bfb56c45d14eb8249d0f0ea6d4b3d48e1f7c695d", size = 248486, upload-time = "2025-08-29T15:34:30.897Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/9f98640979ecee1b0d1a7164b589de720ddf8100d1747d9bbdb84be0c0fb/coverage-7.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:081b98395ced0d9bcf60ada7661a0b75f36b78b9d7e39ea0790bb4ed8da14747", size = 249981, upload-time = "2025-08-29T15:34:32.365Z" }, - { url = "https://files.pythonhosted.org/packages/1f/55/eeb6603371e6629037f47bd25bef300387257ed53a3c5fdb159b7ac8c651/coverage-7.10.6-cp314-cp314-win32.whl", hash = "sha256:6937347c5d7d069ee776b2bf4e1212f912a9f1f141a429c475e6089462fcecc5", size = 220054, upload-time = "2025-08-29T15:34:34.124Z" }, - { url = "https://files.pythonhosted.org/packages/15/d1/a0912b7611bc35412e919a2cd59ae98e7ea3b475e562668040a43fb27897/coverage-7.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:adec1d980fa07e60b6ef865f9e5410ba760e4e1d26f60f7e5772c73b9a5b0713", size = 220851, upload-time = "2025-08-29T15:34:35.651Z" }, - { url = "https://files.pythonhosted.org/packages/ef/2d/11880bb8ef80a45338e0b3e0725e4c2d73ffbb4822c29d987078224fd6a5/coverage-7.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:a80f7aef9535442bdcf562e5a0d5a5538ce8abe6bb209cfbf170c462ac2c2a32", size = 219429, upload-time = "2025-08-29T15:34:37.16Z" }, - { url = "https://files.pythonhosted.org/packages/83/c0/1f00caad775c03a700146f55536ecd097a881ff08d310a58b353a1421be0/coverage-7.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0de434f4fbbe5af4fa7989521c655c8c779afb61c53ab561b64dcee6149e4c65", size = 218080, upload-time = "2025-08-29T15:34:38.919Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c4/b1c5d2bd7cc412cbeb035e257fd06ed4e3e139ac871d16a07434e145d18d/coverage-7.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6e31b8155150c57e5ac43ccd289d079eb3f825187d7c66e755a055d2c85794c6", size = 218293, upload-time = "2025-08-29T15:34:40.425Z" }, - { url = "https://files.pythonhosted.org/packages/3f/07/4468d37c94724bf6ec354e4ec2f205fda194343e3e85fd2e59cec57e6a54/coverage-7.10.6-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:98cede73eb83c31e2118ae8d379c12e3e42736903a8afcca92a7218e1f2903b0", size = 259800, upload-time = "2025-08-29T15:34:41.996Z" }, - { url = "https://files.pythonhosted.org/packages/82/d8/f8fb351be5fee31690cd8da768fd62f1cfab33c31d9f7baba6cd8960f6b8/coverage-7.10.6-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f863c08f4ff6b64fa8045b1e3da480f5374779ef187f07b82e0538c68cb4ff8e", size = 261965, upload-time = "2025-08-29T15:34:43.61Z" }, - { url = "https://files.pythonhosted.org/packages/e8/70/65d4d7cfc75c5c6eb2fed3ee5cdf420fd8ae09c4808723a89a81d5b1b9c3/coverage-7.10.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b38261034fda87be356f2c3f42221fdb4171c3ce7658066ae449241485390d5", size = 264220, upload-time = "2025-08-29T15:34:45.387Z" }, - { url = "https://files.pythonhosted.org/packages/98/3c/069df106d19024324cde10e4ec379fe2fb978017d25e97ebee23002fbadf/coverage-7.10.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e93b1476b79eae849dc3872faeb0bf7948fd9ea34869590bc16a2a00b9c82a7", size = 261660, upload-time = "2025-08-29T15:34:47.288Z" }, - { url = "https://files.pythonhosted.org/packages/fc/8a/2974d53904080c5dc91af798b3a54a4ccb99a45595cc0dcec6eb9616a57d/coverage-7.10.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ff8a991f70f4c0cf53088abf1e3886edcc87d53004c7bb94e78650b4d3dac3b5", size = 259417, upload-time = "2025-08-29T15:34:48.779Z" }, - { url = "https://files.pythonhosted.org/packages/30/38/9616a6b49c686394b318974d7f6e08f38b8af2270ce7488e879888d1e5db/coverage-7.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ac765b026c9f33044419cbba1da913cfb82cca1b60598ac1c7a5ed6aac4621a0", size = 260567, upload-time = "2025-08-29T15:34:50.718Z" }, - { url = "https://files.pythonhosted.org/packages/76/16/3ed2d6312b371a8cf804abf4e14895b70e4c3491c6e53536d63fd0958a8d/coverage-7.10.6-cp314-cp314t-win32.whl", hash = "sha256:441c357d55f4936875636ef2cfb3bee36e466dcf50df9afbd398ce79dba1ebb7", size = 220831, upload-time = "2025-08-29T15:34:52.653Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e5/d38d0cb830abede2adb8b147770d2a3d0e7fecc7228245b9b1ae6c24930a/coverage-7.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:073711de3181b2e204e4870ac83a7c4853115b42e9cd4d145f2231e12d670930", size = 221950, upload-time = "2025-08-29T15:34:54.212Z" }, - { url = "https://files.pythonhosted.org/packages/f4/51/e48e550f6279349895b0ffcd6d2a690e3131ba3a7f4eafccc141966d4dea/coverage-7.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:137921f2bac5559334ba66122b753db6dc5d1cf01eb7b64eb412bb0d064ef35b", size = 219969, upload-time = "2025-08-29T15:34:55.83Z" }, - { url = "https://files.pythonhosted.org/packages/44/0c/50db5379b615854b5cf89146f8f5bd1d5a9693d7f3a987e269693521c404/coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3", size = 208986, upload-time = "2025-08-29T15:35:14.506Z" }, +version = "7.10.7" +source = { registry = "https://pypi.org/simple" } +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/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/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]] @@ -281,6 +322,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, ] +[[package]] +name = "dpath" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/ce/e1fd64d36e4a5717bd5e6b2ad188f5eaa2e902fde871ea73a79875793fc9/dpath-2.2.0.tar.gz", hash = "sha256:34f7e630dc55ea3f219e555726f5da4b4b25f2200319c8e6902c394258dd6a3e", size = 28266, upload-time = "2024-06-12T22:08:03.686Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/d1/8952806fbf9583004ab479d8f58a9496c3d35f6b6009ddd458bdd9978eaf/dpath-2.2.0-py3-none-any.whl", hash = "sha256:b330a375ded0a0d2ed404440f6c6a715deae5313af40bbb01c8a41d891900576", size = 17618, upload-time = "2024-06-12T22:08:01.881Z" }, +] + [[package]] name = "email-validator" version = "2.3.0" @@ -329,16 +379,16 @@ standard = [ [[package]] name = "fastapi-cli" -version = "0.0.11" +version = "0.0.13" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "rich-toolkit" }, { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/23/08/0af729f6231ebdc17a0356397f966838cbe2efa38529951e24017c7435d5/fastapi_cli-0.0.11.tar.gz", hash = "sha256:4f01d751c14d3d2760339cca0f45e81d816218cae8174d1dc757b5375868cde5", size = 17550, upload-time = "2025-09-09T12:50:38.917Z" } +sdist = { url = "https://files.pythonhosted.org/packages/32/4e/3f61850012473b097fc5297d681bd85788e186fadb8555b67baf4c7707f4/fastapi_cli-0.0.13.tar.gz", hash = "sha256:312addf3f57ba7139457cf0d345c03e2170cc5a034057488259c33cd7e494529", size = 17780, upload-time = "2025-09-20T16:37:31.089Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/8f/9e3ad391d1c4183de55c256b481899bbd7bbd06d389e4986741bb289fe94/fastapi_cli-0.0.11-py3-none-any.whl", hash = "sha256:bcdd1123c6077c7466452b9490ca47821f00eb784d58496674793003f9f8e33a", size = 11095, upload-time = "2025-09-09T12:50:37.658Z" }, + { url = "https://files.pythonhosted.org/packages/08/36/7432750f3638324b055496d2c952000bea824259fca70df5577a6a3c172f/fastapi_cli-0.0.13-py3-none-any.whl", hash = "sha256:219b73ccfde7622559cef1d43197da928516acb4f21f2ec69128c4b90057baba", size = 11142, upload-time = "2025-09-20T16:37:29.695Z" }, ] [package.optional-dependencies] @@ -349,7 +399,7 @@ standard = [ [[package]] name = "fastapi-cloud-cli" -version = "0.1.5" +version = "0.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -360,9 +410,27 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/2e/3b6e5016affc310e5109bc580f760586eabecea0c8a7ab067611cd849ac0/fastapi_cloud_cli-0.1.5.tar.gz", hash = "sha256:341ee585eb731a6d3c3656cb91ad38e5f39809bf1a16d41de1333e38635a7937", size = 22710, upload-time = "2025-07-28T13:30:48.216Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/38/1971f9dc8141e359d2435e6fae8bb228632adc55cff00cd00efed2a98456/fastapi_cloud_cli-0.2.1.tar.gz", hash = "sha256:aa22a4b867bf53165b6551d2f4eb21b079bad4aa74047cb889acf941e34699d9", size = 23676, upload-time = "2025-09-25T13:53:32.901Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/1f/5fa06afce6e4bb7fc7e54651236bad3b849340480967c54cbd7c13563c3f/fastapi_cloud_cli-0.2.1-py3-none-any.whl", hash = "sha256:245447bfb17b01ae5f7bc15dec0833bce85381ecf34532e8fa4bcf279ad1c361", size = 19894, upload-time = "2025-09-25T13:53:31.635Z" }, +] + +[[package]] +name = "filelock" +version = "3.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, +] + +[[package]] +name = "fsspec" +version = "2025.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/e0/bab50af11c2d75c9c4a2a26a5254573c0bd97cea152254401510950486fa/fsspec-2025.9.0.tar.gz", hash = "sha256:19fd429483d25d28b65ec68f9f4adc16c17ea2c7c7bf54ec61360d478fb19c19", size = 304847, upload-time = "2025-09-02T19:10:49.215Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/a6/5aa862489a2918a096166fd98d9fe86b7fd53c607678b3fa9d8c432d88d5/fastapi_cloud_cli-0.1.5-py3-none-any.whl", hash = "sha256:d80525fb9c0e8af122370891f9fa83cf5d496e4ad47a8dd26c0496a6c85a012a", size = 18992, upload-time = "2025-07-28T13:30:47.427Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl", hash = "sha256:530dc2a2af60a414a832059574df4a6e10cce927f6f4a78209390fe38955cfb7", size = 199289, upload-time = "2025-09-02T19:10:47.708Z" }, ] [[package]] @@ -541,34 +609,37 @@ wheels = [ [[package]] name = "grpcio" -version = "1.74.0" +version = "1.75.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/38/b4/35feb8f7cab7239c5b94bd2db71abb3d6adb5f335ad8f131abb6060840b6/grpcio-1.74.0.tar.gz", hash = "sha256:80d1f4fbb35b0742d3e3d3bb654b7381cd5f015f8497279a1e9c21ba623e01b1", size = 12756048, upload-time = "2025-07-24T18:54:23.039Z" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/88/fe2844eefd3d2188bc0d7a2768c6375b46dfd96469ea52d8aeee8587d7e0/grpcio-1.75.0.tar.gz", hash = "sha256:b989e8b09489478c2d19fecc744a298930f40d8b27c3638afbfe84d22f36ce4e", size = 12722485, upload-time = "2025-09-16T09:20:21.731Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/d8/1004a5f468715221450e66b051c839c2ce9a985aa3ee427422061fcbb6aa/grpcio-1.74.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:2bc2d7d8d184e2362b53905cb1708c84cb16354771c04b490485fa07ce3a1d89", size = 5449488, upload-time = "2025-07-24T18:53:41.174Z" }, - { url = "https://files.pythonhosted.org/packages/94/0e/33731a03f63740d7743dced423846c831d8e6da808fcd02821a4416df7fa/grpcio-1.74.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:c14e803037e572c177ba54a3e090d6eb12efd795d49327c5ee2b3bddb836bf01", size = 10974059, upload-time = "2025-07-24T18:53:43.066Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c6/3d2c14d87771a421205bdca991467cfe473ee4c6a1231c1ede5248c62ab8/grpcio-1.74.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f6ec94f0e50eb8fa1744a731088b966427575e40c2944a980049798b127a687e", size = 5945647, upload-time = "2025-07-24T18:53:45.269Z" }, - { url = "https://files.pythonhosted.org/packages/c5/83/5a354c8aaff58594eef7fffebae41a0f8995a6258bbc6809b800c33d4c13/grpcio-1.74.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:566b9395b90cc3d0d0c6404bc8572c7c18786ede549cdb540ae27b58afe0fb91", size = 6626101, upload-time = "2025-07-24T18:53:47.015Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ca/4fdc7bf59bf6994aa45cbd4ef1055cd65e2884de6113dbd49f75498ddb08/grpcio-1.74.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1ea6176d7dfd5b941ea01c2ec34de9531ba494d541fe2057c904e601879f249", size = 6182562, upload-time = "2025-07-24T18:53:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/fd/48/2869e5b2c1922583686f7ae674937986807c2f676d08be70d0a541316270/grpcio-1.74.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:64229c1e9cea079420527fa8ac45d80fc1e8d3f94deaa35643c381fa8d98f362", size = 6303425, upload-time = "2025-07-24T18:53:50.847Z" }, - { url = "https://files.pythonhosted.org/packages/a6/0e/bac93147b9a164f759497bc6913e74af1cb632c733c7af62c0336782bd38/grpcio-1.74.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:0f87bddd6e27fc776aacf7ebfec367b6d49cad0455123951e4488ea99d9b9b8f", size = 6996533, upload-time = "2025-07-24T18:53:52.747Z" }, - { url = "https://files.pythonhosted.org/packages/84/35/9f6b2503c1fd86d068b46818bbd7329db26a87cdd8c01e0d1a9abea1104c/grpcio-1.74.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3b03d8f2a07f0fea8c8f74deb59f8352b770e3900d143b3d1475effcb08eec20", size = 6491489, upload-time = "2025-07-24T18:53:55.06Z" }, - { url = "https://files.pythonhosted.org/packages/75/33/a04e99be2a82c4cbc4039eb3a76f6c3632932b9d5d295221389d10ac9ca7/grpcio-1.74.0-cp313-cp313-win32.whl", hash = "sha256:b6a73b2ba83e663b2480a90b82fdae6a7aa6427f62bf43b29912c0cfd1aa2bfa", size = 3805811, upload-time = "2025-07-24T18:53:56.798Z" }, - { url = "https://files.pythonhosted.org/packages/34/80/de3eb55eb581815342d097214bed4c59e806b05f1b3110df03b2280d6dfd/grpcio-1.74.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd3c71aeee838299c5887230b8a1822795325ddfea635edd82954c1eaa831e24", size = 4489214, upload-time = "2025-07-24T18:53:59.771Z" }, + { url = "https://files.pythonhosted.org/packages/00/64/dbce0ffb6edaca2b292d90999dd32a3bd6bc24b5b77618ca28440525634d/grpcio-1.75.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:1bb78d052948d8272c820bb928753f16a614bb2c42fbf56ad56636991b427518", size = 5666860, upload-time = "2025-09-16T09:19:25.417Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e6/da02c8fa882ad3a7f868d380bb3da2c24d35dd983dd12afdc6975907a352/grpcio-1.75.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:9dc4a02796394dd04de0b9673cb79a78901b90bb16bf99ed8cb528c61ed9372e", size = 11455148, upload-time = "2025-09-16T09:19:28.615Z" }, + { url = "https://files.pythonhosted.org/packages/ba/a0/84f87f6c2cf2a533cfce43b2b620eb53a51428ec0c8fe63e5dd21d167a70/grpcio-1.75.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:437eeb16091d31498585d73b133b825dc80a8db43311e332c08facf820d36894", size = 6243865, upload-time = "2025-09-16T09:19:31.342Z" }, + { url = "https://files.pythonhosted.org/packages/be/12/53da07aa701a4839dd70d16e61ce21ecfcc9e929058acb2f56e9b2dd8165/grpcio-1.75.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:c2c39984e846bd5da45c5f7bcea8fafbe47c98e1ff2b6f40e57921b0c23a52d0", size = 6915102, upload-time = "2025-09-16T09:19:33.658Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c0/7eaceafd31f52ec4bf128bbcf36993b4bc71f64480f3687992ddd1a6e315/grpcio-1.75.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38d665f44b980acdbb2f0e1abf67605ba1899f4d2443908df9ec8a6f26d2ed88", size = 6432042, upload-time = "2025-09-16T09:19:36.583Z" }, + { url = "https://files.pythonhosted.org/packages/6b/12/a2ce89a9f4fc52a16ed92951f1b05f53c17c4028b3db6a4db7f08332bee8/grpcio-1.75.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e8e752ab5cc0a9c5b949808c000ca7586223be4f877b729f034b912364c3964", size = 7062984, upload-time = "2025-09-16T09:19:39.163Z" }, + { url = "https://files.pythonhosted.org/packages/55/a6/2642a9b491e24482d5685c0f45c658c495a5499b43394846677abed2c966/grpcio-1.75.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3a6788b30aa8e6f207c417874effe3f79c2aa154e91e78e477c4825e8b431ce0", size = 8001212, upload-time = "2025-09-16T09:19:41.726Z" }, + { url = "https://files.pythonhosted.org/packages/19/20/530d4428750e9ed6ad4254f652b869a20a40a276c1f6817b8c12d561f5ef/grpcio-1.75.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc33e67cab6141c54e75d85acd5dec616c5095a957ff997b4330a6395aa9b51", size = 7457207, upload-time = "2025-09-16T09:19:44.368Z" }, + { url = "https://files.pythonhosted.org/packages/e2/6f/843670007e0790af332a21468d10059ea9fdf97557485ae633b88bd70efc/grpcio-1.75.0-cp313-cp313-win32.whl", hash = "sha256:c8cfc780b7a15e06253aae5f228e1e84c0d3c4daa90faf5bc26b751174da4bf9", size = 3934235, upload-time = "2025-09-16T09:19:46.815Z" }, + { url = "https://files.pythonhosted.org/packages/4b/92/c846b01b38fdf9e2646a682b12e30a70dc7c87dfe68bd5e009ee1501c14b/grpcio-1.75.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c91d5b16eff3cbbe76b7a1eaaf3d91e7a954501e9d4f915554f87c470475c3d", size = 4637558, upload-time = "2025-09-16T09:19:49.698Z" }, ] [[package]] name = "grpcio-status" -version = "1.74.0" +version = "1.75.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, { name = "grpcio" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/93/22/238c5f01e6837df54494deb08d5c772bc3f5bf5fb80a15dce254892d1a81/grpcio_status-1.74.0.tar.gz", hash = "sha256:c58c1b24aa454e30f1fc6a7e0dbbc194c54a408143971a94b5f4e40bb5831432", size = 13662, upload-time = "2025-07-24T19:01:56.874Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/8a/2e45ec0512d4ce9afa136c6e4186d063721b5b4c192eec7536ce6b7ba615/grpcio_status-1.75.0.tar.gz", hash = "sha256:69d5b91be1b8b926f086c1c483519a968c14640773a0ccdd6c04282515dbedf7", size = 13646, upload-time = "2025-09-16T09:24:51.069Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/aa/1b1fe7d8ab699e1ec26d3a36b91d3df9f83a30abc07d4c881d0296b17b67/grpcio_status-1.74.0-py3-none-any.whl", hash = "sha256:52cdbd759a6760fc8f668098a03f208f493dd5c76bf8e02598bbbaf1f6fc2876", size = 14425, upload-time = "2025-07-24T19:01:19.963Z" }, + { url = "https://files.pythonhosted.org/packages/2b/24/d536f0a0fda3a3eeb334893e5fb9d567c2777de6a5384413f71b35cfd0e5/grpcio_status-1.75.0-py3-none-any.whl", hash = "sha256:de62557ef97b7e19c3ce6da19793a12c5f6c1fbbb918d233d9671aba9d9e1d78", size = 14424, upload-time = "2025-09-16T09:23:33.843Z" }, ] [[package]] @@ -580,6 +651,37 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] +[[package]] +name = "h5py" +version = "3.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +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/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" }, +] + +[[package]] +name = "hf-xet" +version = "1.1.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/74/31/feeddfce1748c4a233ec1aa5b7396161c07ae1aa9b7bdbc9a72c3c7dd768/hf_xet-1.1.10.tar.gz", hash = "sha256:408aef343800a2102374a883f283ff29068055c111f003ff840733d3b715bb97", size = 487910, upload-time = "2025-09-12T20:10:27.12Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/a2/343e6d05de96908366bdc0081f2d8607d61200be2ac802769c4284cc65bd/hf_xet-1.1.10-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:686083aca1a6669bc85c21c0563551cbcdaa5cf7876a91f3d074a030b577231d", size = 2761466, upload-time = "2025-09-12T20:10:22.836Z" }, + { url = "https://files.pythonhosted.org/packages/31/f9/6215f948ac8f17566ee27af6430ea72045e0418ce757260248b483f4183b/hf_xet-1.1.10-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:71081925383b66b24eedff3013f8e6bbd41215c3338be4b94ba75fd75b21513b", size = 2623807, upload-time = "2025-09-12T20:10:21.118Z" }, + { url = "https://files.pythonhosted.org/packages/15/07/86397573efefff941e100367bbda0b21496ffcdb34db7ab51912994c32a2/hf_xet-1.1.10-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6bceb6361c80c1cc42b5a7b4e3efd90e64630bcf11224dcac50ef30a47e435", size = 3186960, upload-time = "2025-09-12T20:10:19.336Z" }, + { url = "https://files.pythonhosted.org/packages/01/a7/0b2e242b918cc30e1f91980f3c4b026ff2eedaf1e2ad96933bca164b2869/hf_xet-1.1.10-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eae7c1fc8a664e54753ffc235e11427ca61f4b0477d757cc4eb9ae374b69f09c", size = 3087167, upload-time = "2025-09-12T20:10:17.255Z" }, + { url = "https://files.pythonhosted.org/packages/4a/25/3e32ab61cc7145b11eee9d745988e2f0f4fafda81b25980eebf97d8cff15/hf_xet-1.1.10-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0a0005fd08f002180f7a12d4e13b22be277725bc23ed0529f8add5c7a6309c06", size = 3248612, upload-time = "2025-09-12T20:10:24.093Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3d/ab7109e607ed321afaa690f557a9ada6d6d164ec852fd6bf9979665dc3d6/hf_xet-1.1.10-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f900481cf6e362a6c549c61ff77468bd59d6dd082f3170a36acfef2eb6a6793f", size = 3353360, upload-time = "2025-09-12T20:10:25.563Z" }, + { url = "https://files.pythonhosted.org/packages/ee/0e/471f0a21db36e71a2f1752767ad77e92d8cde24e974e03d662931b1305ec/hf_xet-1.1.10-cp37-abi3-win_amd64.whl", hash = "sha256:5f54b19cc347c13235ae7ee98b330c26dd65ef1df47e5316ffb1e87713ca7045", size = 2804691, upload-time = "2025-09-12T20:10:28.433Z" }, +] + [[package]] name = "httpcore" version = "1.0.9" @@ -623,6 +725,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] +[[package]] +name = "huggingface-hub" +version = "0.35.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/42/0e7be334a6851cd7d51cc11717cb95e89333ebf0064431c0255c56957526/huggingface_hub-0.35.1.tar.gz", hash = "sha256:3585b88c5169c64b7e4214d0e88163d4a709de6d1a502e0cd0459e9ee2c9c572", size = 461374, upload-time = "2025-09-23T13:43:47.074Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/60/4acf0c8a3925d9ff491dc08fe84d37e09cfca9c3b885e0db3d4dedb98cea/huggingface_hub-0.35.1-py3-none-any.whl", hash = "sha256:2f0e2709c711e3040e31d3e0418341f7092910f1462dd00350c4e97af47280a8", size = 563340, upload-time = "2025-09-23T13:43:45.343Z" }, +] + [[package]] name = "idna" version = "3.10" @@ -655,12 +776,11 @@ wheels = [ [[package]] name = "ipython" -version = "9.5.0" +version = "8.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "decorator" }, - { name = "ipython-pygments-lexers" }, { name = "jedi" }, { name = "matplotlib-inline" }, { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, @@ -669,21 +789,9 @@ dependencies = [ { name = "stack-data" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6e/71/a86262bf5a68bf211bcc71fe302af7e05f18a2852fdc610a854d20d085e6/ipython-9.5.0.tar.gz", hash = "sha256:129c44b941fe6d9b82d36fc7a7c18127ddb1d6f02f78f867f402e2e3adde3113", size = 4389137, upload-time = "2025-08-29T12:15:21.519Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/2a/5628a99d04acb2d2f2e749cdf4ea571d2575e898df0528a090948018b726/ipython-9.5.0-py3-none-any.whl", hash = "sha256:88369ffa1d5817d609120daa523a6da06d02518e582347c29f8451732a9c5e72", size = 612426, upload-time = "2025-08-29T12:15:18.866Z" }, -] - -[[package]] -name = "ipython-pygments-lexers" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } +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 = [ - { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, + { url = "https://files.pythonhosted.org/packages/91/d0/274fbf7b0b12643cbbc001ce13e6a5b1607ac4929d1b11c72460152c9fc3/ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2", size = 831864, upload-time = "2025-05-31T16:39:06.38Z" }, ] [[package]] @@ -726,6 +834,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] +[[package]] +name = "jsonpickle" +version = "4.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/a6/d07afcfdef402900229bcca795f80506b207af13a838d4d99ad45abf530c/jsonpickle-4.1.1.tar.gz", hash = "sha256:f86e18f13e2b96c1c1eede0b7b90095bbb61d99fedc14813c44dc2f361dbbae1", size = 316885, upload-time = "2025-06-02T20:36:11.57Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/73/04df8a6fa66d43a9fd45c30f283cc4afff17da671886e451d52af60bdc7e/jsonpickle-4.1.1-py3-none-any.whl", hash = "sha256:bb141da6057898aa2438ff268362b126826c812a1721e31cf08a6e142910dc91", size = 47125, upload-time = "2025-06-02T20:36:08.647Z" }, +] + [[package]] name = "jupyterlab-widgets" version = "3.0.15" @@ -821,6 +938,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9c/1a/aac40a7e58de4133a9cc7630913a8b8e6c76326288b168cbb47f7714c4fd/microdf_python-1.0.2-py3-none-any.whl", hash = "sha256:f7883785e4557d1c8822dbf0d69d7eeab9399f8e67a9bdb716f74554c7580ae7", size = 15823, upload-time = "2025-07-24T12:21:07.356Z" }, ] +[[package]] +name = "msgpack" +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/a1/38/561f01cf3577430b59b340b51329803d3a5bf6a45864a55f4ef308ac11e3/msgpack-1.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3765afa6bd4832fc11c3749be4ba4b69a0e8d7b728f78e68120a157a4c5d41f0", size = 81677, upload-time = "2025-06-13T06:52:16.64Z" }, + { url = "https://files.pythonhosted.org/packages/09/48/54a89579ea36b6ae0ee001cba8c61f776451fad3c9306cd80f5b5c55be87/msgpack-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8ddb2bcfd1a8b9e431c8d6f4f7db0773084e107730ecf3472f1dfe9ad583f3d9", size = 78603, upload-time = "2025-06-13T06:52:17.843Z" }, + { url = "https://files.pythonhosted.org/packages/a0/60/daba2699b308e95ae792cdc2ef092a38eb5ee422f9d2fbd4101526d8a210/msgpack-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:196a736f0526a03653d829d7d4c5500a97eea3648aebfd4b6743875f28aa2af8", size = 420504, upload-time = "2025-06-13T06:52:18.982Z" }, + { url = "https://files.pythonhosted.org/packages/20/22/2ebae7ae43cd8f2debc35c631172ddf14e2a87ffcc04cf43ff9df9fff0d3/msgpack-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d592d06e3cc2f537ceeeb23d38799c6ad83255289bb84c2e5792e5a8dea268a", size = 423749, upload-time = "2025-06-13T06:52:20.211Z" }, + { url = "https://files.pythonhosted.org/packages/40/1b/54c08dd5452427e1179a40b4b607e37e2664bca1c790c60c442c8e972e47/msgpack-1.1.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4df2311b0ce24f06ba253fda361f938dfecd7b961576f9be3f3fbd60e87130ac", size = 404458, upload-time = "2025-06-13T06:52:21.429Z" }, + { url = "https://files.pythonhosted.org/packages/2e/60/6bb17e9ffb080616a51f09928fdd5cac1353c9becc6c4a8abd4e57269a16/msgpack-1.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e4141c5a32b5e37905b5940aacbc59739f036930367d7acce7a64e4dec1f5e0b", size = 405976, upload-time = "2025-06-13T06:52:22.995Z" }, + { url = "https://files.pythonhosted.org/packages/ee/97/88983e266572e8707c1f4b99c8fd04f9eb97b43f2db40e3172d87d8642db/msgpack-1.1.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b1ce7f41670c5a69e1389420436f41385b1aa2504c3b0c30620764b15dded2e7", size = 408607, upload-time = "2025-06-13T06:52:24.152Z" }, + { 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" }, +] + [[package]] name = "mypy-extensions" version = "1.1.0" @@ -830,6 +965,37 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] +[[package]] +name = "ndindex" +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/03/ee/8f7aa7dde0f2d947c2e4034f4c58b308bf1f48a18780183e7f84298a573c/ndindex-1.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:50b579a0c57a4072fc97848f1d0db8cb228ca73d050c8bc9d4e7cf2e75510829", size = 161193, upload-time = "2025-05-21T17:41:24.452Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3b/9f2a49b5d3a558e9cd067e0911e1bb8d8d553e1d689bb9a9119c775636b9/ndindex-1.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0956611e29f51857a54ba0750568ebdbf0eacfad4a262253af2522e77b476369", size = 159952, upload-time = "2025-05-21T17:41:25.806Z" }, + { url = "https://files.pythonhosted.org/packages/76/b9/93273d8dd7a2e155af6ed0bad2f2618202794ffe537184b25ff666cf8e31/ndindex-1.10.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f82aada1f194c5ea11943ca89532cf449881de8c9c2c48b8baa43d467486fdb2", size = 502466, upload-time = "2025-05-21T17:41:27.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/07/c64b0c8416f604f6990da5d1fa97c9de1278a4eec1efcc63b71053b4f0c0/ndindex-1.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38a56a16edbd62ef039b93e393047e66238d02dbc1e95e95b79c0bdd0a4785f7", size = 526910, upload-time = "2025-05-21T17:41:29.071Z" }, + { url = "https://files.pythonhosted.org/packages/b3/a5/316f13eeda944db14015a6edaebd88fc83b196d86cae9f576be319b93873/ndindex-1.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8b11a3b8fd983adafea988b2a7e51fe8c0be819639b16506a472429069158f6d", size = 1642168, upload-time = "2025-05-21T17:41:31.213Z" }, + { url = "https://files.pythonhosted.org/packages/f3/13/4c1cf1b6280669f32e9960215d6cbed027084b0bb423c924095f247f3185/ndindex-1.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:be7cfaed1e7a72c7e0bbc4a0e1965d3cc8207cb3d56bd351c0cb2b2d94db0bdd", size = 1557347, upload-time = "2025-05-21T17:41:32.893Z" }, + { url = "https://files.pythonhosted.org/packages/2d/ac/36124ca146aaa6e84ac479e06a81b5ae9ebde2e3b4b2c77c49492bcfebae/ndindex-1.10.0-cp313-cp313-win32.whl", hash = "sha256:f779a0c20ffd617535bf57c7437d5521d5453daf2e0db0d148301df6b24c0932", size = 148623, upload-time = "2025-05-21T17:41:34.628Z" }, + { url = "https://files.pythonhosted.org/packages/23/38/13169cc35be65a6683784c5a1f2c7e6d2219f58fb56abe9d13ef762a634a/ndindex-1.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:1ef8d71e0ddf0c6e39e64f1e328a37ebefcca1b89218a4068c353851bcb4cb0f", size = 156188, upload-time = "2025-05-21T17:41:36.043Z" }, + { url = "https://files.pythonhosted.org/packages/29/f6/ba98045516f39b0414d03c466e7c46b79290cd54a73ff961b9081bc66a6e/ndindex-1.10.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6fcefeefc48815dd8e99999999477d91d4287d8034b1c81084042a49976b212c", size = 167198, upload-time = "2025-05-21T17:41:37.544Z" }, + { url = "https://files.pythonhosted.org/packages/ca/14/4c8b1256009cda78387e6e3035d4b86582d98b557e56f7ee8f58df3e57b4/ndindex-1.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:882367d3d5a4d20155c23d890bf01ffbac78019eee09a9456ff3322f62eb34c1", size = 167324, upload-time = "2025-05-21T17:41:39.004Z" }, + { url = "https://files.pythonhosted.org/packages/c5/34/a1e8117c0fe5a862da9e7f0162233340c7a9bbd728161a06cd0ad856514e/ndindex-1.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f04b3eeced5a10f1c00197ee93c913a691467c752306c0d97e6df9c02af4e6d", size = 608219, upload-time = "2025-05-21T17:41:40.556Z" }, + { 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" }, +] + +[[package]] +name = "networkx" +version = "3.5" +source = { registry = "https://pypi.org/simple" } +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 = "nodeenv" version = "1.9.1" @@ -839,61 +1005,80 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, ] +[[package]] +name = "numexpr" +version = "2.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/45/6e/8164bc96108991fcd74e9d3eda4a4bf5752c394dde5b2408ecbefeaa339f/numexpr-2.13.0.tar.gz", hash = "sha256:3363d804f202437586447a49b5c83b01322e8be72279d49e0bf524720edc01b6", size = 118688, upload-time = "2025-09-24T12:18:17.621Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b3/642ddcd4c497a988d419c87f6e19c852fa76237530d6fa3531dae5be57d7/numexpr-2.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:071e7dd7d3722c58736fb8713827582b0eb6e752dc54b7db7ad81375c5717409", size = 161905, upload-time = "2025-09-24T12:17:28.577Z" }, + { url = "https://files.pythonhosted.org/packages/b8/8e/ade15c0aad2057923b115a76ba5a96a8b5058daa5056962ec0b20efaa2ab/numexpr-2.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c0c9d82c1cc8a4fbaf98ca7184997d3d4a96d8f5950a7cac7b63c7c88f0d7364", size = 151359, upload-time = "2025-09-24T12:17:29.608Z" }, + { url = "https://files.pythonhosted.org/packages/1f/26/8e315939b2c27880ad87dc2db66bad3aef3789448b099d06496e492e0b70/numexpr-2.13.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22ee0c6bd4b9f591793a1fd8b823e693851ace86e4fddf4ab51b71cbae59a0fc", size = 454648, upload-time = "2025-09-24T12:17:30.604Z" }, + { url = "https://files.pythonhosted.org/packages/cf/be/c6bf7d74fb716cd8d25edee004e8f06918d4cb0f1471a10a2085c741a55d/numexpr-2.13.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:450206905604486c3841b1041e21a8126cadc34da73456aa3e10bf10c6549c6a", size = 445196, upload-time = "2025-09-24T12:17:32.723Z" }, + { url = "https://files.pythonhosted.org/packages/62/42/f947150b9b6281c656cdfef375534bcda0b79e0e799c2dfa6b436e68b5be/numexpr-2.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1d485687f016ce2385817a584fe5f9e007817dbe12ce6dcdeaefae91e8abc78e", size = 1416376, upload-time = "2025-09-24T12:17:34.426Z" }, + { url = "https://files.pythonhosted.org/packages/c5/9d/8824d24b500c52fef2143bfb5a78ede273dc9418ad4cb12a976584b47beb/numexpr-2.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a21ac5bac1752ffb22e71cf7b65087ccc4fd211a06f4a358ce78c4652d44ebc0", size = 1464991, upload-time = "2025-09-24T12:17:36.066Z" }, + { url = "https://files.pythonhosted.org/packages/8d/43/32af1d8c1c7b8bfe3d9067c7610e8bffddea2f352adc24190b8be214e7ab/numexpr-2.13.0-cp313-cp313-win32.whl", hash = "sha256:8ce807751cbaa0aa6eb2a82388096eee350e8d1b8f7ee8e0452be762bda6e929", size = 166254, upload-time = "2025-09-24T12:17:37.631Z" }, + { url = "https://files.pythonhosted.org/packages/6b/88/bcf6ada80b557e19ebf6ce154a62afcb0f594ce58b9393b4fb5b8c620529/numexpr-2.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9858c7cc4436c03bd2d305a6b7f0bb3db2782e18c4428068a9e0427e20f5e111", size = 159343, upload-time = "2025-09-24T12:17:38.812Z" }, + { url = "https://files.pythonhosted.org/packages/63/dc/21fb87fa099def2cab8d27935ad2f0f715f9877d73be34a76fd4e8a9dfa3/numexpr-2.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c00310a9b06f18ddd9b02993044a6b8cae354d0c416828bd620f48e428274e1f", size = 162668, upload-time = "2025-09-24T12:17:40.109Z" }, + { url = "https://files.pythonhosted.org/packages/8c/66/5201b25039302291123fdefdc9aa2d88ce5c0d16ede979d6a8745e6c777c/numexpr-2.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d7e1ff76e7452d58e780435bdb8f1940c0c350c9b49cb2b7cdb0b0cda00c9701", size = 152036, upload-time = "2025-09-24T12:17:41.345Z" }, + { url = "https://files.pythonhosted.org/packages/86/92/c6b0257d014bbf8c81237ebff3c8ca3fc7186cfd613f21a28b725e8e608c/numexpr-2.13.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd9c3ec61407abcd42c36a59bd9e107ec1f64de18072ffca5d6f3f6897b9e4ad", size = 464918, upload-time = "2025-09-24T12:17:42.75Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d9/34afe53caab8040a2a280d1348da88b0277cc555f45b17a60a5eca4624d6/numexpr-2.13.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e638c40fb68b5e6c396d719b769b75c05d49aa92aa81426d5e9e3e2be886819", size = 455538, upload-time = "2025-09-24T12:17:43.876Z" }, + { url = "https://files.pythonhosted.org/packages/72/ee/1fbf99f1b492ae9f500d0cde20152b877f796bab4491be495020e4aef5eb/numexpr-2.13.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d5d2b40d8de3579f5409707dc30dfaa49f3dcbea41b3c1ccbc902e0d4012a792", size = 1425090, upload-time = "2025-09-24T12:17:45.049Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d5/1bb752f3cb8e2cd6610dec637bb20877082765de23a5d805d52d4a279a42/numexpr-2.13.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8bbf064c3886eda9b2ef2d9090093bd92a45a4b9eaa7ed704952a84bb212de49", size = 1473044, upload-time = "2025-09-24T12:17:46.449Z" }, + { url = "https://files.pythonhosted.org/packages/57/db/408043accb22a7585a4c5353234b547c2f38125da8719516cce7679a465f/numexpr-2.13.0-cp313-cp313t-win32.whl", hash = "sha256:7ba92383aeac47a3c8f26d214b6ed11bd10e70b74efdd53e7defe9d1a67ca591", size = 166897, upload-time = "2025-09-24T12:17:47.779Z" }, + { url = "https://files.pythonhosted.org/packages/9a/6f/6483c3400d20a8522480480561b417dbe7b0856ba771b2efa8361f89c44b/numexpr-2.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:435336e014c81866da481e2a03417e30b4f3159260c1d2a6de87191ba2fb77d8", size = 160270, upload-time = "2025-09-24T12:17:48.769Z" }, + { url = "https://files.pythonhosted.org/packages/de/45/1e952d230a034584f563416cfda5fb28a496147c2108fe180f711542ea1c/numexpr-2.13.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fec119ba261b110beeac49b905ce0cde233a5d14ef27bc26b98f2a578c30c35b", size = 161949, upload-time = "2025-09-24T12:17:49.761Z" }, + { url = "https://files.pythonhosted.org/packages/04/b2/6e42abd6edb9e3260e2e09485adc9edd59267fbacd94f20621ea43727595/numexpr-2.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c1aa845393b812f104e821ba7837c2a205a143ec02b6b384434acb2dd57682f3", size = 151412, upload-time = "2025-09-24T12:17:50.704Z" }, + { url = "https://files.pythonhosted.org/packages/5c/30/929ae24625bc8af1bf63542b3525c256b27ec8aa56a08e73163841fe07e6/numexpr-2.13.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:801a25e1d1832dc6f4134ea89d47edae3298b9c994dac25c5a7f290ebac5e4a5", size = 454817, upload-time = "2025-09-24T12:17:51.712Z" }, + { url = "https://files.pythonhosted.org/packages/6a/01/55c51aa4aa7a9f69a349d23d6db0b1edc96506f39e6d4ab2ad7dd1b2a689/numexpr-2.13.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c95d1fbd7e45ef87f9d7f7a2d714dcae01fa65317bf50e63cf4c3f24f0d6c20", size = 445128, upload-time = "2025-09-24T12:17:52.882Z" }, + { url = "https://files.pythonhosted.org/packages/fe/fc/c49f76632c0fe6c9ae6b56ccf3ee3a43194c7ae0e9b7b2a2b9a62e904679/numexpr-2.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa462b876a6b678b788a28a3fa2841c23074f26bd3659af59244c3138fa0087b", size = 1416567, upload-time = "2025-09-24T12:17:54.076Z" }, + { url = "https://files.pythonhosted.org/packages/a3/81/8dc19e400d0e347997f83a820e33637d7378f2513414386012abd65c91a0/numexpr-2.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c5331e75881a0be7ec3097f61a0ad90ded288e8916b90545f1aa67f293079521", size = 1465018, upload-time = "2025-09-24T12:17:55.255Z" }, + { url = "https://files.pythonhosted.org/packages/39/5d/5b1ae53bb1d22f87fafb69b16f77b2cfe76c5f85127bc3b8981fd7c866b8/numexpr-2.13.0-cp314-cp314-win32.whl", hash = "sha256:89a288c4dd25c12394685b0b105e85a4beddb632b3ef52e3e383f25d46d7d86f", size = 168096, upload-time = "2025-09-24T12:17:56.441Z" }, + { url = "https://files.pythonhosted.org/packages/93/2f/07b5a3f0bf3ffa3314210abac1069560344e8cde0028c385a49e08a27f51/numexpr-2.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:ae6068310da1532072bccb6c87870d0f1b584bdae3de528d577e67319b78f56c", size = 161712, upload-time = "2025-09-24T12:17:57.779Z" }, + { url = "https://files.pythonhosted.org/packages/49/86/753a9634b22c7202d02d0c5e33792bc7f8daac1865de8239383732e96f5d/numexpr-2.13.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:08173e867548e0ad251d9024c0539cb121b2f2b3a09205014cdc11c99bad4517", size = 162666, upload-time = "2025-09-24T12:17:59.169Z" }, + { url = "https://files.pythonhosted.org/packages/90/68/f4f9fbf80fca5bcf656f353b899732a56fcf192e32483bbada201bac48c5/numexpr-2.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ffd42f1631d009f0b405e3fe195f2b2551d1ac293d3147bfd4d8835c1ee91a1e", size = 152028, upload-time = "2025-09-24T12:18:00.156Z" }, + { url = "https://files.pythonhosted.org/packages/a9/d3/1df5b69265162448af4e0688c55d4483e856ae2e98d338690a707900f718/numexpr-2.13.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b6160eda53c48e0aa1b3ee02f27dfbe375f28a21d1ace1e151ec5c09011bfa4a", size = 465112, upload-time = "2025-09-24T12:18:01.184Z" }, + { url = "https://files.pythonhosted.org/packages/75/ee/de8cac0ff21c795106d0ac84418035a611f5a4acd0318e83b8a0b9815271/numexpr-2.13.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce3dc0f3f3dcf05c372a624c0e048f159849b0689e4d91a655ab4f2c5ba5a7f6", size = 455809, upload-time = "2025-09-24T12:18:02.396Z" }, + { url = "https://files.pythonhosted.org/packages/04/5b/6e812264d08f440a429e6dd0d0e8fd3709d06c5089c4ead29f14a46ff4d3/numexpr-2.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d017db23f4be18e00d96053f16af93ff77f8b1027d47a2cee78a980393007bc0", size = 1425342, upload-time = "2025-09-24T12:18:03.707Z" }, + { url = "https://files.pythonhosted.org/packages/fd/75/73a7b7ed28550eada9b1aacded71a6941ad28b4026c75de93f87353b7d49/numexpr-2.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:15e51cc76ea2e7f2b3bfb08d2d01bc84f48ba6b2e2b27a9ce645edb06293b91b", size = 1473175, upload-time = "2025-09-24T12:18:04.977Z" }, + { url = "https://files.pythonhosted.org/packages/b8/67/914ab2261130ac0f210a26187d7c0bd9c0c4e3be5ba56f889aa7eec59c3f/numexpr-2.13.0-cp314-cp314t-win32.whl", hash = "sha256:3ae41249afc233e12f186149da1d80ac1ffcfb0ee65ebbd590bdc81f16a5fa77", size = 168768, upload-time = "2025-09-24T12:18:06.215Z" }, + { url = "https://files.pythonhosted.org/packages/29/62/dab5c5e41eef490b3ee759ee5aa6c36b1af4bb43733515bea32c87dd9deb/numexpr-2.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:43d0fe73398e4d3b9ff548d6bda3d37b3569539b1d8cabe3023727b690b4f118", size = 162765, upload-time = "2025-09-24T12:18:07.338Z" }, +] + [[package]] name = "numpy" -version = "2.3.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/19/95b3d357407220ed24c139018d2518fab0a61a948e68286a25f1a4d049ff/numpy-2.3.3.tar.gz", hash = "sha256:ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029", size = 20576648, upload-time = "2025-09-09T16:54:12.543Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/b9/984c2b1ee61a8b803bf63582b4ac4242cf76e2dbd663efeafcb620cc0ccb/numpy-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f5415fb78995644253370985342cd03572ef8620b934da27d77377a2285955bf", size = 20949588, upload-time = "2025-09-09T15:56:59.087Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e4/07970e3bed0b1384d22af1e9912527ecbeb47d3b26e9b6a3bced068b3bea/numpy-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d00de139a3324e26ed5b95870ce63be7ec7352171bc69a4cf1f157a48e3eb6b7", size = 14177802, upload-time = "2025-09-09T15:57:01.73Z" }, - { url = "https://files.pythonhosted.org/packages/35/c7/477a83887f9de61f1203bad89cf208b7c19cc9fef0cebef65d5a1a0619f2/numpy-2.3.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9dc13c6a5829610cc07422bc74d3ac083bd8323f14e2827d992f9e52e22cd6a6", size = 5106537, upload-time = "2025-09-09T15:57:03.765Z" }, - { url = "https://files.pythonhosted.org/packages/52/47/93b953bd5866a6f6986344d045a207d3f1cfbad99db29f534ea9cee5108c/numpy-2.3.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d79715d95f1894771eb4e60fb23f065663b2298f7d22945d66877aadf33d00c7", size = 6640743, upload-time = "2025-09-09T15:57:07.921Z" }, - { url = "https://files.pythonhosted.org/packages/23/83/377f84aaeb800b64c0ef4de58b08769e782edcefa4fea712910b6f0afd3c/numpy-2.3.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:952cfd0748514ea7c3afc729a0fc639e61655ce4c55ab9acfab14bda4f402b4c", size = 14278881, upload-time = "2025-09-09T15:57:11.349Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a5/bf3db6e66c4b160d6ea10b534c381a1955dfab34cb1017ea93aa33c70ed3/numpy-2.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b83648633d46f77039c29078751f80da65aa64d5622a3cd62aaef9d835b6c93", size = 16636301, upload-time = "2025-09-09T15:57:14.245Z" }, - { url = "https://files.pythonhosted.org/packages/a2/59/1287924242eb4fa3f9b3a2c30400f2e17eb2707020d1c5e3086fe7330717/numpy-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b001bae8cea1c7dfdb2ae2b017ed0a6f2102d7a70059df1e338e307a4c78a8ae", size = 16053645, upload-time = "2025-09-09T15:57:16.534Z" }, - { url = "https://files.pythonhosted.org/packages/e6/93/b3d47ed882027c35e94ac2320c37e452a549f582a5e801f2d34b56973c97/numpy-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e9aced64054739037d42fb84c54dd38b81ee238816c948c8f3ed134665dcd86", size = 18578179, upload-time = "2025-09-09T15:57:18.883Z" }, - { url = "https://files.pythonhosted.org/packages/20/d9/487a2bccbf7cc9d4bfc5f0f197761a5ef27ba870f1e3bbb9afc4bbe3fcc2/numpy-2.3.3-cp313-cp313-win32.whl", hash = "sha256:9591e1221db3f37751e6442850429b3aabf7026d3b05542d102944ca7f00c8a8", size = 6312250, upload-time = "2025-09-09T15:57:21.296Z" }, - { url = "https://files.pythonhosted.org/packages/1b/b5/263ebbbbcede85028f30047eab3d58028d7ebe389d6493fc95ae66c636ab/numpy-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f0dadeb302887f07431910f67a14d57209ed91130be0adea2f9793f1a4f817cf", size = 12783269, upload-time = "2025-09-09T15:57:23.034Z" }, - { url = "https://files.pythonhosted.org/packages/fa/75/67b8ca554bbeaaeb3fac2e8bce46967a5a06544c9108ec0cf5cece559b6c/numpy-2.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:3c7cf302ac6e0b76a64c4aecf1a09e51abd9b01fc7feee80f6c43e3ab1b1dbc5", size = 10195314, upload-time = "2025-09-09T15:57:25.045Z" }, - { url = "https://files.pythonhosted.org/packages/11/d0/0d1ddec56b162042ddfafeeb293bac672de9b0cfd688383590090963720a/numpy-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eda59e44957d272846bb407aad19f89dc6f58fecf3504bd144f4c5cf81a7eacc", size = 21048025, upload-time = "2025-09-09T15:57:27.257Z" }, - { url = "https://files.pythonhosted.org/packages/36/9e/1996ca6b6d00415b6acbdd3c42f7f03ea256e2c3f158f80bd7436a8a19f3/numpy-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:823d04112bc85ef5c4fda73ba24e6096c8f869931405a80aa8b0e604510a26bc", size = 14301053, upload-time = "2025-09-09T15:57:30.077Z" }, - { url = "https://files.pythonhosted.org/packages/05/24/43da09aa764c68694b76e84b3d3f0c44cb7c18cdc1ba80e48b0ac1d2cd39/numpy-2.3.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:40051003e03db4041aa325da2a0971ba41cf65714e65d296397cc0e32de6018b", size = 5229444, upload-time = "2025-09-09T15:57:32.733Z" }, - { url = "https://files.pythonhosted.org/packages/bc/14/50ffb0f22f7218ef8af28dd089f79f68289a7a05a208db9a2c5dcbe123c1/numpy-2.3.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ee9086235dd6ab7ae75aba5662f582a81ced49f0f1c6de4260a78d8f2d91a19", size = 6738039, upload-time = "2025-09-09T15:57:34.328Z" }, - { url = "https://files.pythonhosted.org/packages/55/52/af46ac0795e09657d45a7f4db961917314377edecf66db0e39fa7ab5c3d3/numpy-2.3.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94fcaa68757c3e2e668ddadeaa86ab05499a70725811e582b6a9858dd472fb30", size = 14352314, upload-time = "2025-09-09T15:57:36.255Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b1/dc226b4c90eb9f07a3fff95c2f0db3268e2e54e5cce97c4ac91518aee71b/numpy-2.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da1a74b90e7483d6ce5244053399a614b1d6b7bc30a60d2f570e5071f8959d3e", size = 16701722, upload-time = "2025-09-09T15:57:38.622Z" }, - { url = "https://files.pythonhosted.org/packages/9d/9d/9d8d358f2eb5eced14dba99f110d83b5cd9a4460895230f3b396ad19a323/numpy-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2990adf06d1ecee3b3dcbb4977dfab6e9f09807598d647f04d385d29e7a3c3d3", size = 16132755, upload-time = "2025-09-09T15:57:41.16Z" }, - { url = "https://files.pythonhosted.org/packages/b6/27/b3922660c45513f9377b3fb42240bec63f203c71416093476ec9aa0719dc/numpy-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ed635ff692483b8e3f0fcaa8e7eb8a75ee71aa6d975388224f70821421800cea", size = 18651560, upload-time = "2025-09-09T15:57:43.459Z" }, - { url = "https://files.pythonhosted.org/packages/5b/8e/3ab61a730bdbbc201bb245a71102aa609f0008b9ed15255500a99cd7f780/numpy-2.3.3-cp313-cp313t-win32.whl", hash = "sha256:a333b4ed33d8dc2b373cc955ca57babc00cd6f9009991d9edc5ddbc1bac36bcd", size = 6442776, upload-time = "2025-09-09T15:57:45.793Z" }, - { url = "https://files.pythonhosted.org/packages/1c/3a/e22b766b11f6030dc2decdeff5c2fb1610768055603f9f3be88b6d192fb2/numpy-2.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4384a169c4d8f97195980815d6fcad04933a7e1ab3b530921c3fef7a1c63426d", size = 12927281, upload-time = "2025-09-09T15:57:47.492Z" }, - { url = "https://files.pythonhosted.org/packages/7b/42/c2e2bc48c5e9b2a83423f99733950fbefd86f165b468a3d85d52b30bf782/numpy-2.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:75370986cc0bc66f4ce5110ad35aae6d182cc4ce6433c40ad151f53690130bf1", size = 10265275, upload-time = "2025-09-09T15:57:49.647Z" }, - { url = "https://files.pythonhosted.org/packages/6b/01/342ad585ad82419b99bcf7cebe99e61da6bedb89e213c5fd71acc467faee/numpy-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cd052f1fa6a78dee696b58a914b7229ecfa41f0a6d96dc663c1220a55e137593", size = 20951527, upload-time = "2025-09-09T15:57:52.006Z" }, - { url = "https://files.pythonhosted.org/packages/ef/d8/204e0d73fc1b7a9ee80ab1fe1983dd33a4d64a4e30a05364b0208e9a241a/numpy-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:414a97499480067d305fcac9716c29cf4d0d76db6ebf0bf3cbce666677f12652", size = 14186159, upload-time = "2025-09-09T15:57:54.407Z" }, - { url = "https://files.pythonhosted.org/packages/22/af/f11c916d08f3a18fb8ba81ab72b5b74a6e42ead4c2846d270eb19845bf74/numpy-2.3.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:50a5fe69f135f88a2be9b6ca0481a68a136f6febe1916e4920e12f1a34e708a7", size = 5114624, upload-time = "2025-09-09T15:57:56.5Z" }, - { url = "https://files.pythonhosted.org/packages/fb/11/0ed919c8381ac9d2ffacd63fd1f0c34d27e99cab650f0eb6f110e6ae4858/numpy-2.3.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:b912f2ed2b67a129e6a601e9d93d4fa37bef67e54cac442a2f588a54afe5c67a", size = 6642627, upload-time = "2025-09-09T15:57:58.206Z" }, - { url = "https://files.pythonhosted.org/packages/ee/83/deb5f77cb0f7ba6cb52b91ed388b47f8f3c2e9930d4665c600408d9b90b9/numpy-2.3.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e318ee0596d76d4cb3d78535dc005fa60e5ea348cd131a51e99d0bdbe0b54fe", size = 14296926, upload-time = "2025-09-09T15:58:00.035Z" }, - { url = "https://files.pythonhosted.org/packages/77/cc/70e59dcb84f2b005d4f306310ff0a892518cc0c8000a33d0e6faf7ca8d80/numpy-2.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce020080e4a52426202bdb6f7691c65bb55e49f261f31a8f506c9f6bc7450421", size = 16638958, upload-time = "2025-09-09T15:58:02.738Z" }, - { url = "https://files.pythonhosted.org/packages/b6/5a/b2ab6c18b4257e099587d5b7f903317bd7115333ad8d4ec4874278eafa61/numpy-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e6687dc183aa55dae4a705b35f9c0f8cb178bcaa2f029b241ac5356221d5c021", size = 16071920, upload-time = "2025-09-09T15:58:05.029Z" }, - { url = "https://files.pythonhosted.org/packages/b8/f1/8b3fdc44324a259298520dd82147ff648979bed085feeacc1250ef1656c0/numpy-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d8f3b1080782469fdc1718c4ed1d22549b5fb12af0d57d35e992158a772a37cf", size = 18577076, upload-time = "2025-09-09T15:58:07.745Z" }, - { url = "https://files.pythonhosted.org/packages/f0/a1/b87a284fb15a42e9274e7fcea0dad259d12ddbf07c1595b26883151ca3b4/numpy-2.3.3-cp314-cp314-win32.whl", hash = "sha256:cb248499b0bc3be66ebd6578b83e5acacf1d6cb2a77f2248ce0e40fbec5a76d0", size = 6366952, upload-time = "2025-09-09T15:58:10.096Z" }, - { url = "https://files.pythonhosted.org/packages/70/5f/1816f4d08f3b8f66576d8433a66f8fa35a5acfb3bbd0bf6c31183b003f3d/numpy-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:691808c2b26b0f002a032c73255d0bd89751425f379f7bcd22d140db593a96e8", size = 12919322, upload-time = "2025-09-09T15:58:12.138Z" }, - { url = "https://files.pythonhosted.org/packages/8c/de/072420342e46a8ea41c324a555fa90fcc11637583fb8df722936aed1736d/numpy-2.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:9ad12e976ca7b10f1774b03615a2a4bab8addce37ecc77394d8e986927dc0dfe", size = 10478630, upload-time = "2025-09-09T15:58:14.64Z" }, - { url = "https://files.pythonhosted.org/packages/d5/df/ee2f1c0a9de7347f14da5dd3cd3c3b034d1b8607ccb6883d7dd5c035d631/numpy-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9cc48e09feb11e1db00b320e9d30a4151f7369afb96bd0e48d942d09da3a0d00", size = 21047987, upload-time = "2025-09-09T15:58:16.889Z" }, - { url = "https://files.pythonhosted.org/packages/d6/92/9453bdc5a4e9e69cf4358463f25e8260e2ffc126d52e10038b9077815989/numpy-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:901bf6123879b7f251d3631967fd574690734236075082078e0571977c6a8e6a", size = 14301076, upload-time = "2025-09-09T15:58:20.343Z" }, - { url = "https://files.pythonhosted.org/packages/13/77/1447b9eb500f028bb44253105bd67534af60499588a5149a94f18f2ca917/numpy-2.3.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:7f025652034199c301049296b59fa7d52c7e625017cae4c75d8662e377bf487d", size = 5229491, upload-time = "2025-09-09T15:58:22.481Z" }, - { url = "https://files.pythonhosted.org/packages/3d/f9/d72221b6ca205f9736cb4b2ce3b002f6e45cd67cd6a6d1c8af11a2f0b649/numpy-2.3.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:533ca5f6d325c80b6007d4d7fb1984c303553534191024ec6a524a4c92a5935a", size = 6737913, upload-time = "2025-09-09T15:58:24.569Z" }, - { url = "https://files.pythonhosted.org/packages/3c/5f/d12834711962ad9c46af72f79bb31e73e416ee49d17f4c797f72c96b6ca5/numpy-2.3.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0edd58682a399824633b66885d699d7de982800053acf20be1eaa46d92009c54", size = 14352811, upload-time = "2025-09-09T15:58:26.416Z" }, - { url = "https://files.pythonhosted.org/packages/a1/0d/fdbec6629d97fd1bebed56cd742884e4eead593611bbe1abc3eb40d304b2/numpy-2.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:367ad5d8fbec5d9296d18478804a530f1191e24ab4d75ab408346ae88045d25e", size = 16702689, upload-time = "2025-09-09T15:58:28.831Z" }, - { url = "https://files.pythonhosted.org/packages/9b/09/0a35196dc5575adde1eb97ddfbc3e1687a814f905377621d18ca9bc2b7dd/numpy-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8f6ac61a217437946a1fa48d24c47c91a0c4f725237871117dea264982128097", size = 16133855, upload-time = "2025-09-09T15:58:31.349Z" }, - { url = "https://files.pythonhosted.org/packages/7a/ca/c9de3ea397d576f1b6753eaa906d4cdef1bf97589a6d9825a349b4729cc2/numpy-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:179a42101b845a816d464b6fe9a845dfaf308fdfc7925387195570789bb2c970", size = 18652520, upload-time = "2025-09-09T15:58:33.762Z" }, - { url = "https://files.pythonhosted.org/packages/fd/c2/e5ed830e08cd0196351db55db82f65bc0ab05da6ef2b72a836dcf1936d2f/numpy-2.3.3-cp314-cp314t-win32.whl", hash = "sha256:1250c5d3d2562ec4174bce2e3a1523041595f9b651065e4a4473f5f48a6bc8a5", size = 6515371, upload-time = "2025-09-09T15:58:36.04Z" }, - { url = "https://files.pythonhosted.org/packages/47/c7/b0f6b5b67f6788a0725f744496badbb604d226bf233ba716683ebb47b570/numpy-2.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b37a0b2e5935409daebe82c1e42274d30d9dd355852529eab91dab8dcca7419f", size = 13112576, upload-time = "2025-09-09T15:58:37.927Z" }, - { url = "https://files.pythonhosted.org/packages/06/b9/33bba5ff6fb679aa0b1f8a07e853f002a6b04b9394db3069a1270a7784ca/numpy-2.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:78c9f6560dc7e6b3990e32df7ea1a50bbd0e2a111e05209963f5ddcab7073b0b", size = 10545953, upload-time = "2025-09-09T15:58:40.576Z" }, +version = "2.1.3" +source = { registry = "https://pypi.org/simple" } +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/4d/0b/620591441457e25f3404c8057eb924d04f161244cb8a3680d529419aa86e/numpy-2.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f", size = 20836263, upload-time = "2024-11-02T17:40:39.528Z" }, + { url = "https://files.pythonhosted.org/packages/45/e1/210b2d8b31ce9119145433e6ea78046e30771de3fe353f313b2778142f34/numpy-2.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598", size = 13507771, upload-time = "2024-11-02T17:41:01.368Z" }, + { url = "https://files.pythonhosted.org/packages/55/44/aa9ee3caee02fa5a45f2c3b95cafe59c44e4b278fbbf895a93e88b308555/numpy-2.1.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57", size = 5075805, upload-time = "2024-11-02T17:41:11.213Z" }, + { url = "https://files.pythonhosted.org/packages/78/d6/61de6e7e31915ba4d87bbe1ae859e83e6582ea14c6add07c8f7eefd8488f/numpy-2.1.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe", size = 6608380, upload-time = "2024-11-02T17:41:22.19Z" }, + { url = "https://files.pythonhosted.org/packages/3e/46/48bdf9b7241e317e6cf94276fe11ba673c06d1fdf115d8b4ebf616affd1a/numpy-2.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43", size = 13602451, upload-time = "2024-11-02T17:41:43.094Z" }, + { url = "https://files.pythonhosted.org/packages/70/50/73f9a5aa0810cdccda9c1d20be3cbe4a4d6ea6bfd6931464a44c95eef731/numpy-2.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56", size = 16039822, upload-time = "2024-11-02T17:42:07.595Z" }, + { url = "https://files.pythonhosted.org/packages/ad/cd/098bc1d5a5bc5307cfc65ee9369d0ca658ed88fbd7307b0d49fab6ca5fa5/numpy-2.1.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea4dedd6e394a9c180b33c2c872b92f7ce0f8e7ad93e9585312b0c5a04777a4a", size = 16411822, upload-time = "2024-11-02T17:42:32.48Z" }, + { url = "https://files.pythonhosted.org/packages/83/a2/7d4467a2a6d984549053b37945620209e702cf96a8bc658bc04bba13c9e2/numpy-2.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0df3635b9c8ef48bd3be5f862cf71b0a4716fa0e702155c45067c6b711ddcef", size = 14079598, upload-time = "2024-11-02T17:42:53.773Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6a/d64514dcecb2ee70bfdfad10c42b76cab657e7ee31944ff7a600f141d9e9/numpy-2.1.3-cp313-cp313-win32.whl", hash = "sha256:50ca6aba6e163363f132b5c101ba078b8cbd3fa92c7865fd7d4d62d9779ac29f", size = 6236021, upload-time = "2024-11-02T17:46:19.171Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f9/12297ed8d8301a401e7d8eb6b418d32547f1d700ed3c038d325a605421a4/numpy-2.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed", size = 12560405, upload-time = "2024-11-02T17:46:38.177Z" }, + { url = "https://files.pythonhosted.org/packages/a7/45/7f9244cd792e163b334e3a7f02dff1239d2890b6f37ebf9e82cbe17debc0/numpy-2.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:996bb9399059c5b82f76b53ff8bb686069c05acc94656bb259b1d63d04a9506f", size = 20859062, upload-time = "2024-11-02T17:43:24.599Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b4/a084218e7e92b506d634105b13e27a3a6645312b93e1c699cc9025adb0e1/numpy-2.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:45966d859916ad02b779706bb43b954281db43e185015df6eb3323120188f9e4", size = 13515839, upload-time = "2024-11-02T17:43:45.498Z" }, + { url = "https://files.pythonhosted.org/packages/27/45/58ed3f88028dcf80e6ea580311dc3edefdd94248f5770deb980500ef85dd/numpy-2.1.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:baed7e8d7481bfe0874b566850cb0b85243e982388b7b23348c6db2ee2b2ae8e", size = 5116031, upload-time = "2024-11-02T17:43:54.585Z" }, + { url = "https://files.pythonhosted.org/packages/37/a8/eb689432eb977d83229094b58b0f53249d2209742f7de529c49d61a124a0/numpy-2.1.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f7f672a3388133335589cfca93ed468509cb7b93ba3105fce780d04a6576a0", size = 6629977, upload-time = "2024-11-02T17:44:05.31Z" }, + { url = "https://files.pythonhosted.org/packages/42/a3/5355ad51ac73c23334c7caaed01adadfda49544f646fcbfbb4331deb267b/numpy-2.1.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7aac50327da5d208db2eec22eb11e491e3fe13d22653dce51b0f4109101b408", size = 13575951, upload-time = "2024-11-02T17:44:25.881Z" }, + { url = "https://files.pythonhosted.org/packages/c4/70/ea9646d203104e647988cb7d7279f135257a6b7e3354ea6c56f8bafdb095/numpy-2.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4394bc0dbd074b7f9b52024832d16e019decebf86caf909d94f6b3f77a8ee3b6", size = 16022655, upload-time = "2024-11-02T17:44:50.115Z" }, + { url = "https://files.pythonhosted.org/packages/14/ce/7fc0612903e91ff9d0b3f2eda4e18ef9904814afcae5b0f08edb7f637883/numpy-2.1.3-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:50d18c4358a0a8a53f12a8ba9d772ab2d460321e6a93d6064fc22443d189853f", size = 16399902, upload-time = "2024-11-02T17:45:15.685Z" }, + { 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" }, ] [[package]] name = "openapi-python-client" -version = "0.26.0" +version = "0.26.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -908,9 +1093,9 @@ dependencies = [ { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/16/4a/7e8d29b76aa705fe571e75dcbc5ceb8a60560784e127213b3a1bebed6ca1/openapi_python_client-0.26.0.tar.gz", hash = "sha256:f4882d59238105edc49b6fe78baadff7f8b35b8c538af8fdaccd44d64bbe72e0", size = 125997, upload-time = "2025-08-26T03:01:26.597Z" } +sdist = { url = "https://files.pythonhosted.org/packages/69/31/264cf223b301186cfd2f3a78f962ee641e3b7794ec7b483380c56d59e824/openapi_python_client-0.26.1.tar.gz", hash = "sha256:e3832f0ef074a0ab591d1eeb5d3dab2ca820cd0349f7e79d9663b7b21206be5d", size = 126194, upload-time = "2025-09-13T05:49:34.514Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/27/4b8ac011c995d8d8571fe65bd45f3fa987af62877114cc16019001e4958d/openapi_python_client-0.26.0-py3-none-any.whl", hash = "sha256:40b759fd3bd19460bb7a8066ec4b24ebae9873245d2290862bc5704ad8ed776d", size = 183452, upload-time = "2025-08-26T03:01:25.138Z" }, + { url = "https://files.pythonhosted.org/packages/f3/56/d9304d6b7f8a173c868e10a506b8ccab79305d9e412b06cf89c654155f35/openapi_python_client-0.26.1-py3-none-any.whl", hash = "sha256:415cb8095b1a3f15cec45670c5075c5097f65390a351d21512e8f6ea5c1be644", size = 183638, upload-time = "2025-09-13T05:49:32.55Z" }, ] [[package]] @@ -1158,6 +1343,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, ] +[[package]] +name = "plotly" +version = "5.24.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "tenacity" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/4f/428f6d959818d7425a94c190a6b26fbc58035cbef40bf249be0b62a9aedd/plotly-5.24.1.tar.gz", hash = "sha256:dbc8ac8339d248a4bcc36e08a5659bacfe1b079390b8953533f4eb22169b4bae", size = 9479398, upload-time = "2024-09-12T15:36:31.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/ae/580600f441f6fc05218bd6c9d5794f4aef072a7d9093b291f1c50a9db8bc/plotly-5.24.1-py3-none-any.whl", hash = "sha256:f67073a1e637eb0dc3e46324d9d51e2fe76e9727c892dde64ddf1e1b51f29089", size = 19054220, upload-time = "2024-09-12T15:36:24.08Z" }, +] + [[package]] name = "pluggy" version = "1.6.0" @@ -1192,6 +1390,16 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/37/8e/458a1283f69b2ecb4c76e9acf288fff4d25f87b526bc2553ad65f68964cc/policyengine-3.0.0-py3-none-any.whl", hash = "sha256:55d7be28e9888329be84fc7e1a74d6d880c70f62852c1c3e80830464a3f9b88e", size = 54609, upload-time = "2025-09-23T09:36:56.872Z" }, ] +[package.optional-dependencies] +uk = [ + { name = "policyengine-core" }, + { name = "policyengine-uk" }, +] +us = [ + { name = "policyengine-core" }, + { name = "policyengine-us" }, +] + [[package]] name = "policyengine-api-full" version = "0.1.0" @@ -1199,7 +1407,7 @@ source = { editable = "." } dependencies = [ { name = "opentelemetry-instrumentation-fastapi" }, { name = "opentelemetry-instrumentation-sqlalchemy" }, - { name = "policyengine" }, + { name = "policyengine", extra = ["uk", "us"] }, { name = "policyengine-fastapi" }, { name = "psycopg2-binary" }, { name = "pydantic-settings" }, @@ -1223,7 +1431,7 @@ requires-dist = [ { name = "openapi-python-client", marker = "extra == 'build'", specifier = ">=0.21.6" }, { name = "opentelemetry-instrumentation-fastapi", specifier = ">=0.51b0,<0.52" }, { name = "opentelemetry-instrumentation-sqlalchemy", specifier = ">=0.51b0,<0.52" }, - { name = "policyengine", specifier = ">=3.0.0" }, + { name = "policyengine", extras = ["uk", "us"], specifier = ">=3.0.0" }, { name = "policyengine-fastapi", editable = "../../libs/policyengine-fastapi" }, { name = "psycopg2-binary", specifier = ">=2.9.0" }, { name = "pydantic-settings", specifier = ">=2.7.1,<3.0.0" }, @@ -1234,6 +1442,33 @@ requires-dist = [ ] provides-extras = ["test", "build"] +[[package]] +name = "policyengine-core" +version = "3.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dpath" }, + { name = "h5py" }, + { name = "huggingface-hub" }, + { name = "ipython" }, + { name = "microdf-python" }, + { name = "numexpr" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "plotly" }, + { name = "psutil" }, + { name = "pytest" }, + { name = "pyvis" }, + { name = "requests" }, + { name = "sortedcontainers" }, + { name = "standard-imghdr" }, + { name = "wheel" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d3/d7/cd4ae165221b3d5630a5c95e6df0a10be06d461b6545552a5f4a11c40907/policyengine_core-3.20.0.tar.gz", hash = "sha256:10c428467c8629861986f356f7f13ff8bf23ec907961779cf9f6add63f147fdf", size = 159655, upload-time = "2025-08-12T15:54:35.437Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/47/9cd4a2cfd675c5267dc905b2f23313b37df734f94f65490ca083422db39f/policyengine_core-3.20.0-py3-none-any.whl", hash = "sha256:c802edf10059242f7c03b54f7e8c78703ae053defcfe4ec75c677ed0714c07a6", size = 220871, upload-time = "2025-08-12T15:54:33.799Z" }, +] + [[package]] name = "policyengine-fastapi" version = "0.1.0" @@ -1273,6 +1508,35 @@ requires-dist = [ ] provides-extras = ["test", "build"] +[[package]] +name = "policyengine-uk" +version = "2.53.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "microdf-python" }, + { name = "policyengine-core" }, + { name = "pydantic" }, + { name = "tables" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/6a/3650b68e68dc79d56277097c80c7fa59f3d6c2bb8c9f4f48d8a2a857f3a7/policyengine_uk-2.53.0.tar.gz", hash = "sha256:c367e1e1c7a6ff11c1456b79b586cf44c619cb73f8c830237f21720c6a797a66", size = 1049649, upload-time = "2025-09-25T09:23:48.45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/72/f1942fa511a4fff1647d95793bf70437f9751533c919e1c78ca485f79aa7/policyengine_uk-2.53.0-py3-none-any.whl", hash = "sha256:c10a363ace92ee5c30a213a21b0796b996e873f3a29a523746842ab725d87471", size = 1606515, upload-time = "2025-09-25T09:23:46.692Z" }, +] + +[[package]] +name = "policyengine-us" +version = "1.405.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "microdf-python" }, + { name = "policyengine-core" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/11/5c/aed7f69ec6b65b1b0a8e647040cc7b6b168d1ca87f47c065db39678eb9f6/policyengine_us-1.405.0.tar.gz", hash = "sha256:72ed53a56458dfb274027ef4b47b0d20619458cfa49d7d47220e442267d4b96d", size = 8033705, upload-time = "2025-09-25T21:33:15.561Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/28/f12d9edb6c8d52bed5f9992db34fa8a1a72a05403eb8022f5bf9a4f4ad29/policyengine_us-1.405.0-py3-none-any.whl", hash = "sha256:a1a68efac75064214807d4dd884dfdbe548ab4fae2fc5ae54d384a5bfae18169", size = 5980868, upload-time = "2025-09-25T21:33:10.806Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -1299,16 +1563,31 @@ wheels = [ [[package]] name = "protobuf" -version = "6.32.0" +version = "6.32.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/a4/cc17347aa2897568beece2e674674359f911d6fe21b0b8d6268cd42727ac/protobuf-6.32.1.tar.gz", hash = "sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d", size = 440635, upload-time = "2025-09-11T21:38:42.935Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/98/645183ea03ab3995d29086b8bf4f7562ebd3d10c9a4b14ee3f20d47cfe50/protobuf-6.32.1-cp310-abi3-win32.whl", hash = "sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085", size = 424411, upload-time = "2025-09-11T21:38:27.427Z" }, + { url = "https://files.pythonhosted.org/packages/8c/f3/6f58f841f6ebafe076cebeae33fc336e900619d34b1c93e4b5c97a81fdfa/protobuf-6.32.1-cp310-abi3-win_amd64.whl", hash = "sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1", size = 435738, upload-time = "2025-09-11T21:38:30.959Z" }, + { url = "https://files.pythonhosted.org/packages/10/56/a8a3f4e7190837139e68c7002ec749190a163af3e330f65d90309145a210/protobuf-6.32.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281", size = 426454, upload-time = "2025-09-11T21:38:34.076Z" }, + { url = "https://files.pythonhosted.org/packages/3f/be/8dd0a927c559b37d7a6c8ab79034fd167dcc1f851595f2e641ad62be8643/protobuf-6.32.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4", size = 322874, upload-time = "2025-09-11T21:38:35.509Z" }, + { url = "https://files.pythonhosted.org/packages/5c/f6/88d77011b605ef979aace37b7703e4eefad066f7e84d935e5a696515c2dd/protobuf-6.32.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710", size = 322013, upload-time = "2025-09-11T21:38:37.017Z" }, + { url = "https://files.pythonhosted.org/packages/97/b7/15cc7d93443d6c6a84626ae3258a91f4c6ac8c0edd5df35ea7658f71b79c/protobuf-6.32.1-py3-none-any.whl", hash = "sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346", size = 169289, upload-time = "2025-09-11T21:38:41.234Z" }, +] + +[[package]] +name = "psutil" +version = "6.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c0/df/fb4a8eeea482eca989b51cffd274aac2ee24e825f0bf3cbce5281fa1567b/protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2", size = 440614, upload-time = "2025-08-14T21:21:25.015Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/5a/07871137bb752428aa4b659f910b399ba6f291156bdea939be3e96cae7cb/psutil-6.1.1.tar.gz", hash = "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5", size = 508502, upload-time = "2024-12-19T18:21:20.568Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/18/df8c87da2e47f4f1dcc5153a81cd6bca4e429803f4069a299e236e4dd510/protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741", size = 424409, upload-time = "2025-08-14T21:21:12.366Z" }, - { url = "https://files.pythonhosted.org/packages/e1/59/0a820b7310f8139bd8d5a9388e6a38e1786d179d6f33998448609296c229/protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e", size = 435735, upload-time = "2025-08-14T21:21:15.046Z" }, - { url = "https://files.pythonhosted.org/packages/cc/5b/0d421533c59c789e9c9894683efac582c06246bf24bb26b753b149bd88e4/protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0", size = 426449, upload-time = "2025-08-14T21:21:16.687Z" }, - { url = "https://files.pythonhosted.org/packages/ec/7b/607764ebe6c7a23dcee06e054fd1de3d5841b7648a90fd6def9a3bb58c5e/protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1", size = 322869, upload-time = "2025-08-14T21:21:18.282Z" }, - { url = "https://files.pythonhosted.org/packages/40/01/2e730bd1c25392fc32e3268e02446f0d77cb51a2c3a8486b1798e34d5805/protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c", size = 322009, upload-time = "2025-08-14T21:21:19.893Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f2/80ffc4677aac1bc3519b26bc7f7f5de7fce0ee2f7e36e59e27d8beb32dd1/protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783", size = 169287, upload-time = "2025-08-14T21:21:23.515Z" }, + { url = "https://files.pythonhosted.org/packages/61/99/ca79d302be46f7bdd8321089762dd4476ee725fce16fc2b2e1dbba8cac17/psutil-6.1.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8", size = 247511, upload-time = "2024-12-19T18:21:45.163Z" }, + { url = "https://files.pythonhosted.org/packages/0b/6b/73dbde0dd38f3782905d4587049b9be64d76671042fdcaf60e2430c6796d/psutil-6.1.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377", size = 248985, upload-time = "2024-12-19T18:21:49.254Z" }, + { url = "https://files.pythonhosted.org/packages/17/38/c319d31a1d3f88c5b79c68b3116c129e5133f1822157dd6da34043e32ed6/psutil-6.1.1-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003", size = 284488, upload-time = "2024-12-19T18:21:51.638Z" }, + { url = "https://files.pythonhosted.org/packages/9c/39/0f88a830a1c8a3aba27fededc642da37613c57cbff143412e3536f89784f/psutil-6.1.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160", size = 287477, upload-time = "2024-12-19T18:21:55.306Z" }, + { url = "https://files.pythonhosted.org/packages/47/da/99f4345d4ddf2845cb5b5bd0d93d554e84542d116934fde07a0c50bd4e9f/psutil-6.1.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3", size = 289017, upload-time = "2024-12-19T18:21:57.875Z" }, + { url = "https://files.pythonhosted.org/packages/38/53/bd755c2896f4461fd4f36fa6a6dcb66a88a9e4b9fd4e5b66a77cf9d4a584/psutil-6.1.1-cp37-abi3-win32.whl", hash = "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53", size = 250602, upload-time = "2024-12-19T18:22:08.808Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d7/7831438e6c3ebbfa6e01a927127a6cb42ad3ab844247f3c5b96bea25d73d/psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649", size = 254444, upload-time = "2024-12-19T18:22:11.335Z" }, ] [[package]] @@ -1348,6 +1627,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, ] +[[package]] +name = "py-cpuinfo" +version = "9.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/37/a8/d832f7293ebb21690860d2e01d8115e5ff6f2ae8bbdc953f0eb0fa4bd2c7/py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690", size = 104716, upload-time = "2022-10-25T20:38:06.303Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5", size = 22335, upload-time = "2022-10-25T20:38:27.636Z" }, +] + [[package]] name = "pyasn1" version = "0.6.1" @@ -1371,7 +1659,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.11.7" +version = "2.11.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -1379,9 +1667,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/5d/09a551ba512d7ca404d785072700d3f6727a02f6f3c24ecfd081c7cf0aa8/pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2", size = 788495, upload-time = "2025-09-13T11:26:39.325Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d3/108f2006987c58e76691d5ae5d200dd3e0f532cb4e5fa3560751c3a1feba/pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2", size = 444855, upload-time = "2025-09-13T11:26:36.909Z" }, ] [package.optional-dependencies] @@ -1419,16 +1707,16 @@ wheels = [ [[package]] name = "pydantic-settings" -version = "2.10.1" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583, upload-time = "2025-06-24T13:26:46.841Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/c5/dbbc27b814c71676593d1c3f718e6cd7d4f00652cefa24b75f7aa3efb25e/pydantic_settings-2.11.0.tar.gz", hash = "sha256:d0e87a1c7d33593beb7194adb8470fc426e95ba02af83a0f23474a04c9a08180", size = 188394, upload-time = "2025-09-24T14:19:11.764Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235, upload-time = "2025-06-24T13:26:45.485Z" }, + { url = "https://files.pythonhosted.org/packages/83/d6/887a1ff844e64aa823fb4905978d882a633cfe295c32eacad582b78a7d8b/pydantic_settings-2.11.0-py3-none-any.whl", hash = "sha256:fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c", size = 48608, upload-time = "2025-09-24T14:19:10.015Z" }, ] [[package]] @@ -1498,14 +1786,14 @@ wheels = [ [[package]] name = "pytest-asyncio" -version = "1.1.0" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -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" } +sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157, upload-time = "2025-07-16T04:29:24.929Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" }, ] [[package]] @@ -1561,6 +1849,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, ] +[[package]] +name = "pytokens" +version = "0.1.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/5f/e959a442435e24f6fb5a01aec6c657079ceaca1b3baf18561c3728d681da/pytokens-0.1.10.tar.gz", hash = "sha256:c9a4bfa0be1d26aebce03e6884ba454e842f186a59ea43a6d3b25af58223c044", size = 12171, upload-time = "2025-02-19T14:51:22.001Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/e5/63bed382f6a7a5ba70e7e132b8b7b8abbcf4888ffa6be4877698dcfbed7d/pytokens-0.1.10-py3-none-any.whl", hash = "sha256:db7b72284e480e69fb085d9f251f66b3d2df8b7166059261258ff35f50fb711b", size = 12046, upload-time = "2025-02-19T14:51:18.694Z" }, +] + [[package]] name = "pytz" version = "2025.2" @@ -1571,20 +1868,53 @@ wheels = [ ] [[package]] -name = "pyyaml" -version = "6.0.2" +name = "pyvis" +version = "0.3.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" } +dependencies = [ + { name = "ipython" }, + { name = "jinja2" }, + { name = "jsonpickle" }, + { name = "networkx" }, +] wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, - { 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/ab/4b/e37e4e5d5ee1179694917b445768bdbfb084f5a59ecd38089d3413d4c70f/pyvis-0.3.2-py3-none-any.whl", hash = "sha256:5720c4ca8161dc5d9ab352015723abb7a8bb8fb443edeb07f7a322db34a97555", size = 756038, upload-time = "2023-02-24T20:29:46.758Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] [[package]] @@ -1687,58 +2017,63 @@ wheels = [ [[package]] name = "ruamel-yaml-clib" -version = "0.2.12" +version = "0.2.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315, upload-time = "2024-10-20T10:10:56.22Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/e9/39ec4d4b3f91188fad1842748f67d4e749c77c37e353c4e545052ee8e893/ruamel.yaml.clib-0.2.14.tar.gz", hash = "sha256:803f5044b13602d58ea378576dd75aa759f52116a0232608e8fdada4da33752e", size = 225394, upload-time = "2025-09-22T19:51:23.753Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/00/4864119668d71a5fa45678f380b5923ff410701565821925c69780356ffa/ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a", size = 132011, upload-time = "2024-10-20T10:13:04.377Z" }, - { url = "https://files.pythonhosted.org/packages/7f/5e/212f473a93ae78c669ffa0cb051e3fee1139cb2d385d2ae1653d64281507/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475", size = 642488, upload-time = "2024-10-20T10:13:05.906Z" }, - { url = "https://files.pythonhosted.org/packages/1f/8f/ecfbe2123ade605c49ef769788f79c38ddb1c8fa81e01f4dbf5cf1a44b16/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef", size = 745066, upload-time = "2024-10-20T10:13:07.26Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a9/28f60726d29dfc01b8decdb385de4ced2ced9faeb37a847bd5cf26836815/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6", size = 701785, upload-time = "2024-10-20T10:13:08.504Z" }, - { url = "https://files.pythonhosted.org/packages/84/7e/8e7ec45920daa7f76046578e4f677a3215fe8f18ee30a9cb7627a19d9b4c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf", size = 693017, upload-time = "2024-10-21T11:26:48.866Z" }, - { url = "https://files.pythonhosted.org/packages/c5/b3/d650eaade4ca225f02a648321e1ab835b9d361c60d51150bac49063b83fa/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1", size = 741270, upload-time = "2024-10-21T11:26:50.213Z" }, - { url = "https://files.pythonhosted.org/packages/87/b8/01c29b924dcbbed75cc45b30c30d565d763b9c4d540545a0eeecffb8f09c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01", size = 709059, upload-time = "2024-12-11T19:58:18.846Z" }, - { url = "https://files.pythonhosted.org/packages/30/8c/ed73f047a73638257aa9377ad356bea4d96125b305c34a28766f4445cc0f/ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6", size = 98583, upload-time = "2024-10-20T10:13:09.658Z" }, - { url = "https://files.pythonhosted.org/packages/b0/85/e8e751d8791564dd333d5d9a4eab0a7a115f7e349595417fd50ecae3395c/ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3", size = 115190, upload-time = "2024-10-20T10:13:10.66Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ae/e3811f05415594025e96000349d3400978adaed88d8f98d494352d9761ee/ruamel.yaml.clib-0.2.14-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7e4f9da7e7549946e02a6122dcad00b7c1168513acb1f8a726b1aaf504a99d32", size = 269205, upload-time = "2025-09-23T14:24:15.06Z" }, + { url = "https://files.pythonhosted.org/packages/72/06/7d51f4688d6d72bb72fa74254e1593c4f5ebd0036be5b41fe39315b275e9/ruamel.yaml.clib-0.2.14-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:dd7546c851e59c06197a7c651335755e74aa383a835878ca86d2c650c07a2f85", size = 137417, upload-time = "2025-09-22T19:50:59.82Z" }, + { url = "https://files.pythonhosted.org/packages/5a/08/b4499234a420ef42960eeb05585df5cc7eb25ccb8c980490b079e6367050/ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:1c1acc3a0209ea9042cc3cfc0790edd2eddd431a2ec3f8283d081e4d5018571e", size = 642558, upload-time = "2025-09-22T19:51:03.388Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ba/1975a27dedf1c4c33306ee67c948121be8710b19387aada29e2f139c43ee/ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2070bf0ad1540d5c77a664de07ebcc45eebd1ddcab71a7a06f26936920692beb", size = 744087, upload-time = "2025-09-22T19:51:00.897Z" }, + { url = "https://files.pythonhosted.org/packages/20/15/8a19a13d27f3bd09fa18813add8380a29115a47b553845f08802959acbce/ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd8fe07f49c170e09d76773fb86ad9135e0beee44f36e1576a201b0676d3d1d", size = 699709, upload-time = "2025-09-22T19:51:02.075Z" }, + { url = "https://files.pythonhosted.org/packages/19/ee/8d6146a079ad21e534b5083c9ee4a4c8bec42f79cf87594b60978286b39a/ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ff86876889ea478b1381089e55cf9e345707b312beda4986f823e1d95e8c0f59", size = 708926, upload-time = "2025-09-23T18:42:51.707Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/426b714abdc222392e68f3b8ad323930d05a214a27c7e7a0f06c69126401/ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1f118b707eece8cf84ecbc3e3ec94d9db879d85ed608f95870d39b2d2efa5dca", size = 740202, upload-time = "2025-09-22T19:51:04.673Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ac/3c5c2b27a183f4fda8a57c82211721c016bcb689a4a175865f7646db9f94/ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b30110b29484adc597df6bd92a37b90e63a8c152ca8136aad100a02f8ba6d1b6", size = 765196, upload-time = "2025-09-22T19:51:05.916Z" }, + { url = "https://files.pythonhosted.org/packages/92/2e/06f56a71fd55021c993ed6e848c9b2e5e9cfce180a42179f0ddd28253f7c/ruamel.yaml.clib-0.2.14-cp313-cp313-win32.whl", hash = "sha256:f4e97a1cf0b7a30af9e1d9dad10a5671157b9acee790d9e26996391f49b965a2", size = 98635, upload-time = "2025-09-22T19:51:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/51/79/76aba16a1689b50528224b182f71097ece338e7a4ab55e84c2e73443b78a/ruamel.yaml.clib-0.2.14-cp313-cp313-win_amd64.whl", hash = "sha256:090782b5fb9d98df96509eecdbcaffd037d47389a89492320280d52f91330d78", size = 115238, upload-time = "2025-09-22T19:51:07.081Z" }, + { url = "https://files.pythonhosted.org/packages/21/e2/a59ff65c26aaf21a24eb38df777cb9af5d87ba8fc8107c163c2da9d1e85e/ruamel.yaml.clib-0.2.14-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:7df6f6e9d0e33c7b1d435defb185095386c469109de723d514142632a7b9d07f", size = 271441, upload-time = "2025-09-23T14:24:16.498Z" }, + { url = "https://files.pythonhosted.org/packages/6b/fa/3234f913fe9a6525a7b97c6dad1f51e72b917e6872e051a5e2ffd8b16fbb/ruamel.yaml.clib-0.2.14-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:70eda7703b8126f5e52fcf276e6c0f40b0d314674f896fc58c47b0aef2b9ae83", size = 137970, upload-time = "2025-09-22T19:51:09.472Z" }, + { url = "https://files.pythonhosted.org/packages/ef/ec/4edbf17ac2c87fa0845dd366ef8d5852b96eb58fcd65fc1ecf5fe27b4641/ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a0cb71ccc6ef9ce36eecb6272c81afdc2f565950cdcec33ae8e6cd8f7fc86f27", size = 739639, upload-time = "2025-09-22T19:51:10.566Z" }, + { url = "https://files.pythonhosted.org/packages/15/18/b0e1fafe59051de9e79cdd431863b03593ecfa8341c110affad7c8121efc/ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e7cb9ad1d525d40f7d87b6df7c0ff916a66bc52cb61b66ac1b2a16d0c1b07640", size = 764456, upload-time = "2025-09-22T19:51:11.736Z" }, ] [[package]] name = "ruff" -version = "0.12.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a8/f0/e0965dd709b8cabe6356811c0ee8c096806bb57d20b5019eb4e48a117410/ruff-0.12.12.tar.gz", hash = "sha256:b86cd3415dbe31b3b46a71c598f4c4b2f550346d1ccf6326b347cc0c8fd063d6", size = 5359915, upload-time = "2025-09-04T16:50:18.273Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/09/79/8d3d687224d88367b51c7974cec1040c4b015772bfbeffac95face14c04a/ruff-0.12.12-py3-none-linux_armv6l.whl", hash = "sha256:de1c4b916d98ab289818e55ce481e2cacfaad7710b01d1f990c497edf217dafc", size = 12116602, upload-time = "2025-09-04T16:49:18.892Z" }, - { url = "https://files.pythonhosted.org/packages/c3/c3/6e599657fe192462f94861a09aae935b869aea8a1da07f47d6eae471397c/ruff-0.12.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7acd6045e87fac75a0b0cdedacf9ab3e1ad9d929d149785903cff9bb69ad9727", size = 12868393, upload-time = "2025-09-04T16:49:23.043Z" }, - { url = "https://files.pythonhosted.org/packages/e8/d2/9e3e40d399abc95336b1843f52fc0daaceb672d0e3c9290a28ff1a96f79d/ruff-0.12.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:abf4073688d7d6da16611f2f126be86523a8ec4343d15d276c614bda8ec44edb", size = 12036967, upload-time = "2025-09-04T16:49:26.04Z" }, - { url = "https://files.pythonhosted.org/packages/e9/03/6816b2ed08836be272e87107d905f0908be5b4a40c14bfc91043e76631b8/ruff-0.12.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:968e77094b1d7a576992ac078557d1439df678a34c6fe02fd979f973af167577", size = 12276038, upload-time = "2025-09-04T16:49:29.056Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d5/707b92a61310edf358a389477eabd8af68f375c0ef858194be97ca5b6069/ruff-0.12.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42a67d16e5b1ffc6d21c5f67851e0e769517fb57a8ebad1d0781b30888aa704e", size = 11901110, upload-time = "2025-09-04T16:49:32.07Z" }, - { url = "https://files.pythonhosted.org/packages/9d/3d/f8b1038f4b9822e26ec3d5b49cf2bc313e3c1564cceb4c1a42820bf74853/ruff-0.12.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b216ec0a0674e4b1214dcc998a5088e54eaf39417327b19ffefba1c4a1e4971e", size = 13668352, upload-time = "2025-09-04T16:49:35.148Z" }, - { url = "https://files.pythonhosted.org/packages/98/0e/91421368ae6c4f3765dd41a150f760c5f725516028a6be30e58255e3c668/ruff-0.12.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:59f909c0fdd8f1dcdbfed0b9569b8bf428cf144bec87d9de298dcd4723f5bee8", size = 14638365, upload-time = "2025-09-04T16:49:38.892Z" }, - { url = "https://files.pythonhosted.org/packages/74/5d/88f3f06a142f58ecc8ecb0c2fe0b82343e2a2b04dcd098809f717cf74b6c/ruff-0.12.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ac93d87047e765336f0c18eacad51dad0c1c33c9df7484c40f98e1d773876f5", size = 14060812, upload-time = "2025-09-04T16:49:42.732Z" }, - { url = "https://files.pythonhosted.org/packages/13/fc/8962e7ddd2e81863d5c92400820f650b86f97ff919c59836fbc4c1a6d84c/ruff-0.12.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01543c137fd3650d322922e8b14cc133b8ea734617c4891c5a9fccf4bfc9aa92", size = 13050208, upload-time = "2025-09-04T16:49:46.434Z" }, - { url = "https://files.pythonhosted.org/packages/53/06/8deb52d48a9a624fd37390555d9589e719eac568c020b27e96eed671f25f/ruff-0.12.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afc2fa864197634e549d87fb1e7b6feb01df0a80fd510d6489e1ce8c0b1cc45", size = 13311444, upload-time = "2025-09-04T16:49:49.931Z" }, - { url = "https://files.pythonhosted.org/packages/2a/81/de5a29af7eb8f341f8140867ffb93f82e4fde7256dadee79016ac87c2716/ruff-0.12.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0c0945246f5ad776cb8925e36af2438e66188d2b57d9cf2eed2c382c58b371e5", size = 13279474, upload-time = "2025-09-04T16:49:53.465Z" }, - { url = "https://files.pythonhosted.org/packages/7f/14/d9577fdeaf791737ada1b4f5c6b59c21c3326f3f683229096cccd7674e0c/ruff-0.12.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a0fbafe8c58e37aae28b84a80ba1817f2ea552e9450156018a478bf1fa80f4e4", size = 12070204, upload-time = "2025-09-04T16:49:56.882Z" }, - { url = "https://files.pythonhosted.org/packages/77/04/a910078284b47fad54506dc0af13839c418ff704e341c176f64e1127e461/ruff-0.12.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b9c456fb2fc8e1282affa932c9e40f5ec31ec9cbb66751a316bd131273b57c23", size = 11880347, upload-time = "2025-09-04T16:49:59.729Z" }, - { url = "https://files.pythonhosted.org/packages/df/58/30185fcb0e89f05e7ea82e5817b47798f7fa7179863f9d9ba6fd4fe1b098/ruff-0.12.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5f12856123b0ad0147d90b3961f5c90e7427f9acd4b40050705499c98983f489", size = 12891844, upload-time = "2025-09-04T16:50:02.591Z" }, - { url = "https://files.pythonhosted.org/packages/21/9c/28a8dacce4855e6703dcb8cdf6c1705d0b23dd01d60150786cd55aa93b16/ruff-0.12.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:26a1b5a2bf7dd2c47e3b46d077cd9c0fc3b93e6c6cc9ed750bd312ae9dc302ee", size = 13360687, upload-time = "2025-09-04T16:50:05.8Z" }, - { url = "https://files.pythonhosted.org/packages/c8/fa/05b6428a008e60f79546c943e54068316f32ec8ab5c4f73e4563934fbdc7/ruff-0.12.12-py3-none-win32.whl", hash = "sha256:173be2bfc142af07a01e3a759aba6f7791aa47acf3604f610b1c36db888df7b1", size = 12052870, upload-time = "2025-09-04T16:50:09.121Z" }, - { url = "https://files.pythonhosted.org/packages/85/60/d1e335417804df452589271818749d061b22772b87efda88354cf35cdb7a/ruff-0.12.12-py3-none-win_amd64.whl", hash = "sha256:e99620bf01884e5f38611934c09dd194eb665b0109104acae3ba6102b600fd0d", size = 13178016, upload-time = "2025-09-04T16:50:12.559Z" }, - { url = "https://files.pythonhosted.org/packages/28/7e/61c42657f6e4614a4258f1c3b0c5b93adc4d1f8575f5229d1906b483099b/ruff-0.12.12-py3-none-win_arm64.whl", hash = "sha256:2a8199cab4ce4d72d158319b63370abf60991495fb733db96cd923a34c52d093", size = 12256762, upload-time = "2025-09-04T16:50:15.737Z" }, +version = "0.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/df/8d7d8c515d33adfc540e2edf6c6021ea1c5a58a678d8cfce9fae59aabcab/ruff-0.13.2.tar.gz", hash = "sha256:cb12fffd32fb16d32cef4ed16d8c7cdc27ed7c944eaa98d99d01ab7ab0b710ff", size = 5416417, upload-time = "2025-09-25T14:54:09.936Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/84/5716a7fa4758e41bf70e603e13637c42cfb9dbf7ceb07180211b9bbf75ef/ruff-0.13.2-py3-none-linux_armv6l.whl", hash = "sha256:3796345842b55f033a78285e4f1641078f902020d8450cade03aad01bffd81c3", size = 12343254, upload-time = "2025-09-25T14:53:27.784Z" }, + { url = "https://files.pythonhosted.org/packages/9b/77/c7042582401bb9ac8eff25360e9335e901d7a1c0749a2b28ba4ecb239991/ruff-0.13.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ff7e4dda12e683e9709ac89e2dd436abf31a4d8a8fc3d89656231ed808e231d2", size = 13040891, upload-time = "2025-09-25T14:53:31.38Z" }, + { url = "https://files.pythonhosted.org/packages/c6/15/125a7f76eb295cb34d19c6778e3a82ace33730ad4e6f28d3427e134a02e0/ruff-0.13.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c75e9d2a2fafd1fdd895d0e7e24b44355984affdde1c412a6f6d3f6e16b22d46", size = 12243588, upload-time = "2025-09-25T14:53:33.543Z" }, + { url = "https://files.pythonhosted.org/packages/9e/eb/0093ae04a70f81f8be7fd7ed6456e926b65d238fc122311293d033fdf91e/ruff-0.13.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cceac74e7bbc53ed7d15d1042ffe7b6577bf294611ad90393bf9b2a0f0ec7cb6", size = 12491359, upload-time = "2025-09-25T14:53:35.892Z" }, + { url = "https://files.pythonhosted.org/packages/43/fe/72b525948a6956f07dad4a6f122336b6a05f2e3fd27471cea612349fedb9/ruff-0.13.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6ae3f469b5465ba6d9721383ae9d49310c19b452a161b57507764d7ef15f4b07", size = 12162486, upload-time = "2025-09-25T14:53:38.171Z" }, + { url = "https://files.pythonhosted.org/packages/6a/e3/0fac422bbbfb2ea838023e0d9fcf1f30183d83ab2482800e2cb892d02dfe/ruff-0.13.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f8f9e3cd6714358238cd6626b9d43026ed19c0c018376ac1ef3c3a04ffb42d8", size = 13871203, upload-time = "2025-09-25T14:53:41.943Z" }, + { url = "https://files.pythonhosted.org/packages/6b/82/b721c8e3ec5df6d83ba0e45dcf00892c4f98b325256c42c38ef136496cbf/ruff-0.13.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c6ed79584a8f6cbe2e5d7dbacf7cc1ee29cbdb5df1172e77fbdadc8bb85a1f89", size = 14929635, upload-time = "2025-09-25T14:53:43.953Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a0/ad56faf6daa507b83079a1ad7a11694b87d61e6bf01c66bd82b466f21821/ruff-0.13.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aed130b2fde049cea2019f55deb939103123cdd191105f97a0599a3e753d61b0", size = 14338783, upload-time = "2025-09-25T14:53:46.205Z" }, + { url = "https://files.pythonhosted.org/packages/47/77/ad1d9156db8f99cd01ee7e29d74b34050e8075a8438e589121fcd25c4b08/ruff-0.13.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1887c230c2c9d65ed1b4e4cfe4d255577ea28b718ae226c348ae68df958191aa", size = 13355322, upload-time = "2025-09-25T14:53:48.164Z" }, + { url = "https://files.pythonhosted.org/packages/64/8b/e87cfca2be6f8b9f41f0bb12dc48c6455e2d66df46fe61bb441a226f1089/ruff-0.13.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5bcb10276b69b3cfea3a102ca119ffe5c6ba3901e20e60cf9efb53fa417633c3", size = 13354427, upload-time = "2025-09-25T14:53:50.486Z" }, + { url = "https://files.pythonhosted.org/packages/7f/df/bf382f3fbead082a575edb860897287f42b1b3c694bafa16bc9904c11ed3/ruff-0.13.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:afa721017aa55a555b2ff7944816587f1cb813c2c0a882d158f59b832da1660d", size = 13537637, upload-time = "2025-09-25T14:53:52.887Z" }, + { url = "https://files.pythonhosted.org/packages/51/70/1fb7a7c8a6fc8bd15636288a46e209e81913b87988f26e1913d0851e54f4/ruff-0.13.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1dbc875cf3720c64b3990fef8939334e74cb0ca65b8dbc61d1f439201a38101b", size = 12340025, upload-time = "2025-09-25T14:53:54.88Z" }, + { url = "https://files.pythonhosted.org/packages/4c/27/1e5b3f1c23ca5dd4106d9d580e5c13d9acb70288bff614b3d7b638378cc9/ruff-0.13.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939a1b2a960e9742e9a347e5bbc9b3c3d2c716f86c6ae273d9cbd64f193f22", size = 12133449, upload-time = "2025-09-25T14:53:57.089Z" }, + { url = "https://files.pythonhosted.org/packages/2d/09/b92a5ccee289f11ab128df57d5911224197d8d55ef3bd2043534ff72ca54/ruff-0.13.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:50e2d52acb8de3804fc5f6e2fa3ae9bdc6812410a9e46837e673ad1f90a18736", size = 13051369, upload-time = "2025-09-25T14:53:59.124Z" }, + { url = "https://files.pythonhosted.org/packages/89/99/26c9d1c7d8150f45e346dc045cc49f23e961efceb4a70c47dea0960dea9a/ruff-0.13.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3196bc13ab2110c176b9a4ae5ff7ab676faaa1964b330a1383ba20e1e19645f2", size = 13523644, upload-time = "2025-09-25T14:54:01.622Z" }, + { url = "https://files.pythonhosted.org/packages/f7/00/e7f1501e81e8ec290e79527827af1d88f541d8d26151751b46108978dade/ruff-0.13.2-py3-none-win32.whl", hash = "sha256:7c2a0b7c1e87795fec3404a485096bcd790216c7c146a922d121d8b9c8f1aaac", size = 12245990, upload-time = "2025-09-25T14:54:03.647Z" }, + { url = "https://files.pythonhosted.org/packages/ee/bd/d9f33a73de84fafd0146c6fba4f497c4565fe8fa8b46874b8e438869abc2/ruff-0.13.2-py3-none-win_amd64.whl", hash = "sha256:17d95fb32218357c89355f6f6f9a804133e404fc1f65694372e02a557edf8585", size = 13324004, upload-time = "2025-09-25T14:54:06.05Z" }, + { url = "https://files.pythonhosted.org/packages/c3/12/28fa2f597a605884deb0f65c1b1ae05111051b2a7030f5d8a4ff7f4599ba/ruff-0.13.2-py3-none-win_arm64.whl", hash = "sha256:da711b14c530412c827219312b7d7fbb4877fb31150083add7e8c5336549cea7", size = 12484437, upload-time = "2025-09-25T14:54:08.022Z" }, ] [[package]] name = "sentry-sdk" -version = "2.37.1" +version = "2.39.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/be/ffc232c32d0be18f8e4eff7a22dffc1f1fef2894703d64cc281a80e75da6/sentry_sdk-2.37.1.tar.gz", hash = "sha256:531751da91aa62a909b42a7be155b41f6bb0de9df6ae98441d23b95de2f98475", size = 346235, upload-time = "2025-09-09T13:48:27.137Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/72/43294fa4bdd75c51610b5104a3ff834459ba653abb415150aa7826a249dd/sentry_sdk-2.39.0.tar.gz", hash = "sha256:8c185854d111f47f329ab6bc35993f28f7a6b7114db64aa426b326998cfa14e9", size = 348556, upload-time = "2025-09-25T09:15:39.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/c3/cba447ab531331d165d9003c04473be944a308ad916ca2345b5ef1969ed9/sentry_sdk-2.37.1-py2.py3-none-any.whl", hash = "sha256:baaaea6608ed3a639766a69ded06b254b106d32ad9d180bdbe58f3db9364592b", size = 368307, upload-time = "2025-09-09T13:48:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/dd/44/4356cc64246ba7b2b920f7c97a85c3c52748e213e250b512ee8152eb559d/sentry_sdk-2.39.0-py2.py3-none-any.whl", hash = "sha256:ba655ca5e57b41569b18e2a5552cb3375209760a5d332cdd87c6c3f28f729602", size = 370851, upload-time = "2025-09-25T09:15:36.35Z" }, ] [[package]] @@ -1768,6 +2103,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] +[[package]] +name = "sortedcontainers" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, +] + [[package]] name = "sqlalchemy" version = "2.0.43" @@ -1816,6 +2160,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, ] +[[package]] +name = "standard-imghdr" +version = "3.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/8d/ab2620fbe2e348483c9cb776c3b7b3cc407899291a041d7fa026469b7cd1/standard_imghdr-3.13.0.tar.gz", hash = "sha256:8d9c68058d882f6fc3542a8d39ef9ff94d2187dc90bd0c851e0902776b7b7a42", size = 5511, upload-time = "2024-10-30T16:01:36.412Z" } +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 = "starlette" version = "0.46.2" @@ -1828,6 +2181,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037, upload-time = "2025-04-13T13:56:16.21Z" }, ] +[[package]] +name = "tables" +version = "3.10.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blosc2" }, + { name = "numexpr" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "py-cpuinfo" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/15/50/23ead25f60bb1babe7f2f061d8a2f8c2f6804c1a20b3058677beb9085b56/tables-3.10.2.tar.gz", hash = "sha256:2544812a7186fadba831d6dd34eb49ccd788d6a83f4e4c2b431b835b6796c910", size = 4779722, upload-time = "2025-01-04T20:44:13.034Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/02/8c7aeaa6c8aac8e0298d40dc5fc55477fddc30cb31e4dc7e5e473be4b464/tables-3.10.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7b8bc07c715bad3d447ed8f834388ef2e10265e2c4af6b1297fc61adb645948f", size = 6725764, upload-time = "2025-01-04T20:43:48.171Z" }, + { url = "https://files.pythonhosted.org/packages/91/f4/8683395d294b9e4576fd7d888aa6cf5583c013c2c0a2e47f862c2842407f/tables-3.10.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:28677ed8e1a371471495599078f48da0850f82457d6c852ca77959c974371140", size = 5442663, upload-time = "2025-01-04T20:43:53.722Z" }, + { url = "https://files.pythonhosted.org/packages/72/9b/ea43159eed8f81bfa1ead8fa8201a3c352e84c7220e046bb548736833951/tables-3.10.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaaea478dcf27dd54679ef2643c26d3b8b15676ad81e4d80a88fd1682d23deb1", size = 7078747, upload-time = "2025-01-04T20:43:59.596Z" }, + { url = "https://files.pythonhosted.org/packages/04/95/b3e88edc674e35d9011b168df0d7a9b1c3ab98733fa26e740ac7964edc2f/tables-3.10.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5e67a9f901842f9a4b1f3d2307f4bdd94047514fe0d0c558ed19c11f53c402a", size = 7479985, upload-time = "2025-01-04T20:44:04.13Z" }, + { url = "https://files.pythonhosted.org/packages/63/ca/eaa029a43d269bdda6985931d6cfd479e876cd8cf7c887d818bef05ef03b/tables-3.10.2-cp313-cp313-win_amd64.whl", hash = "sha256:5637fdcded5ba5426aa24e0e42d6f990926a4da7f193830df131dfcb7e842900", size = 6385562, upload-time = "2025-01-04T20:44:08.196Z" }, +] + +[[package]] +name = "tenacity" +version = "9.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, +] + [[package]] name = "tqdm" version = "4.67.1" @@ -1851,7 +2234,7 @@ wheels = [ [[package]] name = "typer" -version = "0.16.1" +version = "0.17.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -1859,9 +2242,9 @@ dependencies = [ { name = "shellingham" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/78/d90f616bf5f88f8710ad067c1f8705bf7618059836ca084e5bb2a0855d75/typer-0.16.1.tar.gz", hash = "sha256:d358c65a464a7a90f338e3bb7ff0c74ac081449e53884b12ba658cbd72990614", size = 102836, upload-time = "2025-08-18T19:18:22.898Z" } +sdist = { url = "https://files.pythonhosted.org/packages/92/e8/2a73ccf9874ec4c7638f172efc8972ceab13a0e3480b389d6ed822f7a822/typer-0.17.4.tar.gz", hash = "sha256:b77dc07d849312fd2bb5e7f20a7af8985c7ec360c45b051ed5412f64d8dc1580", size = 103734, upload-time = "2025-09-05T18:14:40.746Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/76/06dbe78f39b2203d2a47d5facc5df5102d0561e2807396471b5f7c5a30a1/typer-0.16.1-py3-none-any.whl", hash = "sha256:90ee01cb02d9b8395ae21ee3368421faf21fa138cb2a541ed369c08cec5237c9", size = 46397, upload-time = "2025-08-18T19:18:21.663Z" }, + { url = "https://files.pythonhosted.org/packages/93/72/6b3e70d32e89a5cbb6a4513726c1ae8762165b027af569289e19ec08edd8/typer-0.17.4-py3-none-any.whl", hash = "sha256:015534a6edaa450e7007eba705d5c18c3349dcea50a6ad79a5ed530967575824", size = 46643, upload-time = "2025-09-05T18:14:39.166Z" }, ] [[package]] @@ -1905,15 +2288,15 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.35.0" +version = "0.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } +sdist = { url = "https://files.pythonhosted.org/packages/71/57/1616c8274c3442d802621abf5deb230771c7a0fec9414cb6763900eb3868/uvicorn-0.37.0.tar.gz", hash = "sha256:4115c8add6d3fd536c8ee77f0e14a7fd2ebba939fed9b02583a97f80648f9e13", size = 80367, upload-time = "2025-09-23T13:33:47.486Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, + { url = "https://files.pythonhosted.org/packages/85/cd/584a2ceb5532af99dd09e50919e3615ba99aa127e9850eafe5f31ddfdb9a/uvicorn-0.37.0-py3-none-any.whl", hash = "sha256:913b2b88672343739927ce381ff9e2ad62541f9f8289664fa1d1d3803fa2ce6c", size = 67976, upload-time = "2025-09-23T13:33:45.842Z" }, ] [package.optional-dependencies] @@ -2024,6 +2407,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, ] +[[package]] +name = "wheel" +version = "0.45.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/98/2d9906746cdc6a6ef809ae6338005b3f21bb568bea3165cfc6a243fdc25c/wheel-0.45.1.tar.gz", hash = "sha256:661e1abd9198507b1409a20c02106d9670b2576e916d58f520316666abca6729", size = 107545, upload-time = "2024-11-23T00:18:23.513Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/2c/87f3254fd8ffd29e4c02732eee68a83a1d3c346ae39bc6822dcbcb697f2b/wheel-0.45.1-py3-none-any.whl", hash = "sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248", size = 72494, upload-time = "2024-11-23T00:18:21.207Z" }, +] + [[package]] name = "widgetsnbextension" version = "4.0.14" diff --git a/projects/policyengine-api-simulation/pyproject.toml b/projects/policyengine-api-simulation/pyproject.toml index 4f4fb8ba..55f38988 100644 --- a/projects/policyengine-api-simulation/pyproject.toml +++ b/projects/policyengine-api-simulation/pyproject.toml @@ -16,9 +16,7 @@ dependencies = [ "pydantic-settings (>=2.7.1,<3.0.0)", "opentelemetry-instrumentation-fastapi (>=0.51b0,<0.52)", "policyengine-fastapi", - "policyengine>=0.6.0", - "policyengine-uk>=2.22.8", - "policyengine-us>=1.370.2", + "policyengine[uk,us]>=0.6.0", "tables>=3.10.2", ] diff --git a/projects/policyengine-api-simulation/uv.lock b/projects/policyengine-api-simulation/uv.lock index 7a700386..3fefcbe3 100644 --- a/projects/policyengine-api-simulation/uv.lock +++ b/projects/policyengine-api-simulation/uv.lock @@ -2,6 +2,20 @@ version = 1 revision = 3 requires-python = ">=3.13" +[[package]] +name = "alembic" +version = "1.16.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mako" }, + { name = "sqlalchemy" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9a/ca/4dc52902cf3491892d464f5265a81e9dff094692c8a049a3ed6a05fe7ee8/alembic-1.16.5.tar.gz", hash = "sha256:a88bb7f6e513bd4301ecf4c7f2206fe93f9913f9b48dac3b78babde2d6fe765e", size = 1969868, upload-time = "2025-08-27T18:02:05.668Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/4a/4c61d4c84cfd9befb6fa08a702535b27b21fff08c946bc2f6139decbf7f7/alembic-1.16.5-py3-none-any.whl", hash = "sha256:e845dfe090c5ffa7b92593ae6687c5cb1a101e91fa53868497dbd79847f9dbe3", size = 247355, upload-time = "2025-08-27T18:02:07.37Z" }, +] + [[package]] name = "annotated-types" version = "0.7.0" @@ -13,24 +27,24 @@ wheels = [ [[package]] name = "anyio" -version = "4.10.0" +version = "4.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "sniffio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, ] [[package]] name = "asgiref" -version = "3.9.1" +version = "3.9.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/61/0aa957eec22ff70b830b22ff91f825e70e1ef732c06666a805730f28b36b/asgiref-3.9.1.tar.gz", hash = "sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142", size = 36870, upload-time = "2025-07-08T09:07:43.344Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/bf/0f3ecda32f1cb3bf1dca480aca08a7a8a3bdc4bed2343a103f30731565c9/asgiref-3.9.2.tar.gz", hash = "sha256:a0249afacb66688ef258ffe503528360443e2b9a8d8c4581b6ebefa58c841ef1", size = 36894, upload-time = "2025-09-23T15:00:55.136Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/3c/0464dcada90d5da0e71018c04a140ad6349558afb30b3051b4264cc5b965/asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c", size = 23790, upload-time = "2025-07-08T09:07:41.548Z" }, + { url = "https://files.pythonhosted.org/packages/c7/d1/69d02ce34caddb0a7ae088b84c356a625a93cd4ff57b2f97644c03fad905/asgiref-3.9.2-py3-none-any.whl", hash = "sha256:0b61526596219d70396548fc003635056856dba5d0d086f86476f10b33c75960", size = 23788, upload-time = "2025-09-23T15:00:53.627Z" }, ] [[package]] @@ -53,7 +67,7 @@ wheels = [ [[package]] name = "black" -version = "25.1.0" +version = "25.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -61,14 +75,31 @@ dependencies = [ { name = "packaging" }, { name = "pathspec" }, { name = "platformdirs" }, + { name = "pytokens" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload-time = "2025-01-29T04:15:40.373Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/43/20b5c90612d7bdb2bdbcceeb53d588acca3bb8f0e4c5d5c751a2c8fdd55a/black-25.9.0.tar.gz", hash = "sha256:0474bca9a0dd1b51791fcc507a4e02078a1c63f6d4e4ae5544b9848c7adfb619", size = 648393, upload-time = "2025-09-19T00:27:37.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/99/3acfea65f5e79f45472c45f87ec13037b506522719cd9d4ac86484ff51ac/black-25.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0172a012f725b792c358d57fe7b6b6e8e67375dd157f64fa7a3097b3ed3e2175", size = 1742165, upload-time = "2025-09-19T00:34:10.402Z" }, + { url = "https://files.pythonhosted.org/packages/3a/18/799285282c8236a79f25d590f0222dbd6850e14b060dfaa3e720241fd772/black-25.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3bec74ee60f8dfef564b573a96b8930f7b6a538e846123d5ad77ba14a8d7a64f", size = 1581259, upload-time = "2025-09-19T00:32:49.685Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ce/883ec4b6303acdeca93ee06b7622f1fa383c6b3765294824165d49b1a86b/black-25.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b756fc75871cb1bcac5499552d771822fd9db5a2bb8db2a7247936ca48f39831", size = 1655583, upload-time = "2025-09-19T00:30:44.505Z" }, + { url = "https://files.pythonhosted.org/packages/21/17/5c253aa80a0639ccc427a5c7144534b661505ae2b5a10b77ebe13fa25334/black-25.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:846d58e3ce7879ec1ffe816bb9df6d006cd9590515ed5d17db14e17666b2b357", size = 1343428, upload-time = "2025-09-19T00:32:13.839Z" }, + { url = "https://files.pythonhosted.org/packages/1b/46/863c90dcd3f9d41b109b7f19032ae0db021f0b2a81482ba0a1e28c84de86/black-25.9.0-py3-none-any.whl", hash = "sha256:474b34c1342cdc157d307b56c4c65bce916480c4a8f6551fdc6bf9b486a7c4ae", size = 203363, upload-time = "2025-09-19T00:27:35.724Z" }, +] + +[[package]] +name = "blosc" +version = "1.11.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/ca/3ec5a5d05e10ad200d887c8cfb9492d9a02e05f9f8f726aa178123b1711b/blosc-1.11.3.tar.gz", hash = "sha256:89ed658eba7814a92e89c44d8c524148d55921595bc133bd1a90f8888a9e088e", size = 1439627, upload-time = "2025-05-17T11:50:03.713Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673, upload-time = "2025-01-29T05:37:20.574Z" }, - { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190, upload-time = "2025-01-29T05:37:22.106Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926, upload-time = "2025-01-29T04:18:58.564Z" }, - { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613, upload-time = "2025-01-29T04:19:27.63Z" }, - { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" }, + { url = "https://files.pythonhosted.org/packages/a5/23/6ee0e7270ad6299e73483dfad31b17f8acf66f7768094316a35ee0534f1d/blosc-1.11.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9b474c70b9765587323dd1d7ff8e9fa9e9b35ccb3bee77e7658ce9faf2e05f7f", size = 2291576, upload-time = "2025-05-17T11:49:41.013Z" }, + { url = "https://files.pythonhosted.org/packages/51/8f/d8097dd6bf952d4bc1a31852f717d5a1157b32c1bea50dac723ed8e6bc8d/blosc-1.11.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:291d153864f53960861a48c2a5f6706adc2a84a2bdd9c3d1c5353d9c32748a03", size = 1801973, upload-time = "2025-05-17T11:49:42.259Z" }, + { url = "https://files.pythonhosted.org/packages/1e/cb/7fdf0756e6a38d6a28c5063bc8ba8a8c8b1a1ab6980d777c52ca7dd942b1/blosc-1.11.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece67bb34741a147e4120cff3ee3784121709a112d16795716b8f4239aaddfa4", size = 2485043, upload-time = "2025-05-17T11:49:44.034Z" }, + { url = "https://files.pythonhosted.org/packages/7c/b8/d21a1305356312ca0fc6bd54ad6fb91e7434f0efef545972eb72f040c815/blosc-1.11.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e70216dbddb85b69a8d0f62a4a5c09b7a1fce9ca2f329793e799f8b6f9fa3ab0", size = 2619988, upload-time = "2025-05-17T11:49:45.346Z" }, + { url = "https://files.pythonhosted.org/packages/a0/79/9ed273c9493e02f0bc5deacd3854ecabd6c6ba5371ed04b6c7702fd16f77/blosc-1.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:220865ffcac638f8f0f4b51259d4e4f3236165e5b43fffd1e836cd7cd29b9367", size = 2678176, upload-time = "2025-05-17T11:49:47.12Z" }, + { url = "https://files.pythonhosted.org/packages/79/0e/c50458a1e038c0f0da70c3223d2a34ad702b86a79d0921f23a8ffaae035f/blosc-1.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d57dde8c335378e8443757b69d0b29e90dfc53047d01311e952aecc815167dec", size = 2752740, upload-time = "2025-05-17T11:49:48.909Z" }, + { url = "https://files.pythonhosted.org/packages/f1/0e/3a5ed949e0e23eb576c08017bb39e8612607cf8f591d8149b0fb82469a03/blosc-1.11.3-cp313-cp313-win32.whl", hash = "sha256:d3d72046580a50177811916c78130d6ae7307420733de6e950cb567c896b1ca5", size = 1530991, upload-time = "2025-05-17T11:49:50.121Z" }, + { url = "https://files.pythonhosted.org/packages/06/d4/0c3cdaf34b3ef705fdab465ad8df4a3bce5bbdf2bca8f2515eae90ae28a0/blosc-1.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:73721c1949f2b8d2f4168cababbfe6280511f0da9a971ba7ec9c56eab9603824", size = 1815688, upload-time = "2025-05-17T11:49:51.434Z" }, ] [[package]] @@ -163,14 +194,14 @@ wheels = [ [[package]] name = "click" -version = "8.2.1" +version = "8.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, + { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, ] [[package]] @@ -191,57 +222,74 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "comm" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } +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.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/14/70/025b179c993f019105b79575ac6edb5e084fb0f0e63f15cdebef4e454fb5/coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90", size = 823736, upload-time = "2025-08-29T15:35:16.668Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/e7/917e5953ea29a28c1057729c1d5af9084ab6d9c66217523fd0e10f14d8f6/coverage-7.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6", size = 217351, upload-time = "2025-08-29T15:33:45.438Z" }, - { url = "https://files.pythonhosted.org/packages/eb/86/2e161b93a4f11d0ea93f9bebb6a53f113d5d6e416d7561ca41bb0a29996b/coverage-7.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80", size = 217600, upload-time = "2025-08-29T15:33:47.269Z" }, - { url = "https://files.pythonhosted.org/packages/0e/66/d03348fdd8df262b3a7fb4ee5727e6e4936e39e2f3a842e803196946f200/coverage-7.10.6-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003", size = 248600, upload-time = "2025-08-29T15:33:48.953Z" }, - { url = "https://files.pythonhosted.org/packages/73/dd/508420fb47d09d904d962f123221bc249f64b5e56aa93d5f5f7603be475f/coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27", size = 251206, upload-time = "2025-08-29T15:33:50.697Z" }, - { url = "https://files.pythonhosted.org/packages/e9/1f/9020135734184f439da85c70ea78194c2730e56c2d18aee6e8ff1719d50d/coverage-7.10.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4", size = 252478, upload-time = "2025-08-29T15:33:52.303Z" }, - { url = "https://files.pythonhosted.org/packages/a4/a4/3d228f3942bb5a2051fde28c136eea23a761177dc4ff4ef54533164ce255/coverage-7.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d", size = 250637, upload-time = "2025-08-29T15:33:53.67Z" }, - { url = "https://files.pythonhosted.org/packages/36/e3/293dce8cdb9a83de971637afc59b7190faad60603b40e32635cbd15fbf61/coverage-7.10.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc", size = 248529, upload-time = "2025-08-29T15:33:55.022Z" }, - { url = "https://files.pythonhosted.org/packages/90/26/64eecfa214e80dd1d101e420cab2901827de0e49631d666543d0e53cf597/coverage-7.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc", size = 250143, upload-time = "2025-08-29T15:33:56.386Z" }, - { url = "https://files.pythonhosted.org/packages/3e/70/bd80588338f65ea5b0d97e424b820fb4068b9cfb9597fbd91963086e004b/coverage-7.10.6-cp313-cp313-win32.whl", hash = "sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e", size = 219770, upload-time = "2025-08-29T15:33:58.063Z" }, - { url = "https://files.pythonhosted.org/packages/a7/14/0b831122305abcc1060c008f6c97bbdc0a913ab47d65070a01dc50293c2b/coverage-7.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32", size = 220566, upload-time = "2025-08-29T15:33:59.766Z" }, - { url = "https://files.pythonhosted.org/packages/83/c6/81a83778c1f83f1a4a168ed6673eeedc205afb562d8500175292ca64b94e/coverage-7.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2", size = 219195, upload-time = "2025-08-29T15:34:01.191Z" }, - { url = "https://files.pythonhosted.org/packages/d7/1c/ccccf4bf116f9517275fa85047495515add43e41dfe8e0bef6e333c6b344/coverage-7.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b", size = 218059, upload-time = "2025-08-29T15:34:02.91Z" }, - { url = "https://files.pythonhosted.org/packages/92/97/8a3ceff833d27c7492af4f39d5da6761e9ff624831db9e9f25b3886ddbca/coverage-7.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393", size = 218287, upload-time = "2025-08-29T15:34:05.106Z" }, - { url = "https://files.pythonhosted.org/packages/92/d8/50b4a32580cf41ff0423777a2791aaf3269ab60c840b62009aec12d3970d/coverage-7.10.6-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27", size = 259625, upload-time = "2025-08-29T15:34:06.575Z" }, - { url = "https://files.pythonhosted.org/packages/7e/7e/6a7df5a6fb440a0179d94a348eb6616ed4745e7df26bf2a02bc4db72c421/coverage-7.10.6-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df", size = 261801, upload-time = "2025-08-29T15:34:08.006Z" }, - { url = "https://files.pythonhosted.org/packages/3a/4c/a270a414f4ed5d196b9d3d67922968e768cd971d1b251e1b4f75e9362f75/coverage-7.10.6-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb", size = 264027, upload-time = "2025-08-29T15:34:09.806Z" }, - { url = "https://files.pythonhosted.org/packages/9c/8b/3210d663d594926c12f373c5370bf1e7c5c3a427519a8afa65b561b9a55c/coverage-7.10.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282", size = 261576, upload-time = "2025-08-29T15:34:11.585Z" }, - { url = "https://files.pythonhosted.org/packages/72/d0/e1961eff67e9e1dba3fc5eb7a4caf726b35a5b03776892da8d79ec895775/coverage-7.10.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4", size = 259341, upload-time = "2025-08-29T15:34:13.159Z" }, - { url = "https://files.pythonhosted.org/packages/3a/06/d6478d152cd189b33eac691cba27a40704990ba95de49771285f34a5861e/coverage-7.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21", size = 260468, upload-time = "2025-08-29T15:34:14.571Z" }, - { url = "https://files.pythonhosted.org/packages/ed/73/737440247c914a332f0b47f7598535b29965bf305e19bbc22d4c39615d2b/coverage-7.10.6-cp313-cp313t-win32.whl", hash = "sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0", size = 220429, upload-time = "2025-08-29T15:34:16.394Z" }, - { url = "https://files.pythonhosted.org/packages/bd/76/b92d3214740f2357ef4a27c75a526eb6c28f79c402e9f20a922c295c05e2/coverage-7.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5", size = 221493, upload-time = "2025-08-29T15:34:17.835Z" }, - { url = "https://files.pythonhosted.org/packages/fc/8e/6dcb29c599c8a1f654ec6cb68d76644fe635513af16e932d2d4ad1e5ac6e/coverage-7.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b", size = 219757, upload-time = "2025-08-29T15:34:19.248Z" }, - { url = "https://files.pythonhosted.org/packages/d3/aa/76cf0b5ec00619ef208da4689281d48b57f2c7fde883d14bf9441b74d59f/coverage-7.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6008a021907be8c4c02f37cdc3ffb258493bdebfeaf9a839f9e71dfdc47b018e", size = 217331, upload-time = "2025-08-29T15:34:20.846Z" }, - { url = "https://files.pythonhosted.org/packages/65/91/8e41b8c7c505d398d7730206f3cbb4a875a35ca1041efc518051bfce0f6b/coverage-7.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5e75e37f23eb144e78940b40395b42f2321951206a4f50e23cfd6e8a198d3ceb", size = 217607, upload-time = "2025-08-29T15:34:22.433Z" }, - { url = "https://files.pythonhosted.org/packages/87/7f/f718e732a423d442e6616580a951b8d1ec3575ea48bcd0e2228386805e79/coverage-7.10.6-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0f7cb359a448e043c576f0da00aa8bfd796a01b06aa610ca453d4dde09cc1034", size = 248663, upload-time = "2025-08-29T15:34:24.425Z" }, - { url = "https://files.pythonhosted.org/packages/e6/52/c1106120e6d801ac03e12b5285e971e758e925b6f82ee9b86db3aa10045d/coverage-7.10.6-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c68018e4fc4e14b5668f1353b41ccf4bc83ba355f0e1b3836861c6f042d89ac1", size = 251197, upload-time = "2025-08-29T15:34:25.906Z" }, - { url = "https://files.pythonhosted.org/packages/3d/ec/3a8645b1bb40e36acde9c0609f08942852a4af91a937fe2c129a38f2d3f5/coverage-7.10.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd4b2b0707fc55afa160cd5fc33b27ccbf75ca11d81f4ec9863d5793fc6df56a", size = 252551, upload-time = "2025-08-29T15:34:27.337Z" }, - { url = "https://files.pythonhosted.org/packages/a1/70/09ecb68eeb1155b28a1d16525fd3a9b65fbe75337311a99830df935d62b6/coverage-7.10.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cec13817a651f8804a86e4f79d815b3b28472c910e099e4d5a0e8a3b6a1d4cb", size = 250553, upload-time = "2025-08-29T15:34:29.065Z" }, - { url = "https://files.pythonhosted.org/packages/c6/80/47df374b893fa812e953b5bc93dcb1427a7b3d7a1a7d2db33043d17f74b9/coverage-7.10.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f2a6a8e06bbda06f78739f40bfb56c45d14eb8249d0f0ea6d4b3d48e1f7c695d", size = 248486, upload-time = "2025-08-29T15:34:30.897Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/9f98640979ecee1b0d1a7164b589de720ddf8100d1747d9bbdb84be0c0fb/coverage-7.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:081b98395ced0d9bcf60ada7661a0b75f36b78b9d7e39ea0790bb4ed8da14747", size = 249981, upload-time = "2025-08-29T15:34:32.365Z" }, - { url = "https://files.pythonhosted.org/packages/1f/55/eeb6603371e6629037f47bd25bef300387257ed53a3c5fdb159b7ac8c651/coverage-7.10.6-cp314-cp314-win32.whl", hash = "sha256:6937347c5d7d069ee776b2bf4e1212f912a9f1f141a429c475e6089462fcecc5", size = 220054, upload-time = "2025-08-29T15:34:34.124Z" }, - { url = "https://files.pythonhosted.org/packages/15/d1/a0912b7611bc35412e919a2cd59ae98e7ea3b475e562668040a43fb27897/coverage-7.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:adec1d980fa07e60b6ef865f9e5410ba760e4e1d26f60f7e5772c73b9a5b0713", size = 220851, upload-time = "2025-08-29T15:34:35.651Z" }, - { url = "https://files.pythonhosted.org/packages/ef/2d/11880bb8ef80a45338e0b3e0725e4c2d73ffbb4822c29d987078224fd6a5/coverage-7.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:a80f7aef9535442bdcf562e5a0d5a5538ce8abe6bb209cfbf170c462ac2c2a32", size = 219429, upload-time = "2025-08-29T15:34:37.16Z" }, - { url = "https://files.pythonhosted.org/packages/83/c0/1f00caad775c03a700146f55536ecd097a881ff08d310a58b353a1421be0/coverage-7.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0de434f4fbbe5af4fa7989521c655c8c779afb61c53ab561b64dcee6149e4c65", size = 218080, upload-time = "2025-08-29T15:34:38.919Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c4/b1c5d2bd7cc412cbeb035e257fd06ed4e3e139ac871d16a07434e145d18d/coverage-7.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6e31b8155150c57e5ac43ccd289d079eb3f825187d7c66e755a055d2c85794c6", size = 218293, upload-time = "2025-08-29T15:34:40.425Z" }, - { url = "https://files.pythonhosted.org/packages/3f/07/4468d37c94724bf6ec354e4ec2f205fda194343e3e85fd2e59cec57e6a54/coverage-7.10.6-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:98cede73eb83c31e2118ae8d379c12e3e42736903a8afcca92a7218e1f2903b0", size = 259800, upload-time = "2025-08-29T15:34:41.996Z" }, - { url = "https://files.pythonhosted.org/packages/82/d8/f8fb351be5fee31690cd8da768fd62f1cfab33c31d9f7baba6cd8960f6b8/coverage-7.10.6-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f863c08f4ff6b64fa8045b1e3da480f5374779ef187f07b82e0538c68cb4ff8e", size = 261965, upload-time = "2025-08-29T15:34:43.61Z" }, - { url = "https://files.pythonhosted.org/packages/e8/70/65d4d7cfc75c5c6eb2fed3ee5cdf420fd8ae09c4808723a89a81d5b1b9c3/coverage-7.10.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b38261034fda87be356f2c3f42221fdb4171c3ce7658066ae449241485390d5", size = 264220, upload-time = "2025-08-29T15:34:45.387Z" }, - { url = "https://files.pythonhosted.org/packages/98/3c/069df106d19024324cde10e4ec379fe2fb978017d25e97ebee23002fbadf/coverage-7.10.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e93b1476b79eae849dc3872faeb0bf7948fd9ea34869590bc16a2a00b9c82a7", size = 261660, upload-time = "2025-08-29T15:34:47.288Z" }, - { url = "https://files.pythonhosted.org/packages/fc/8a/2974d53904080c5dc91af798b3a54a4ccb99a45595cc0dcec6eb9616a57d/coverage-7.10.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ff8a991f70f4c0cf53088abf1e3886edcc87d53004c7bb94e78650b4d3dac3b5", size = 259417, upload-time = "2025-08-29T15:34:48.779Z" }, - { url = "https://files.pythonhosted.org/packages/30/38/9616a6b49c686394b318974d7f6e08f38b8af2270ce7488e879888d1e5db/coverage-7.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ac765b026c9f33044419cbba1da913cfb82cca1b60598ac1c7a5ed6aac4621a0", size = 260567, upload-time = "2025-08-29T15:34:50.718Z" }, - { url = "https://files.pythonhosted.org/packages/76/16/3ed2d6312b371a8cf804abf4e14895b70e4c3491c6e53536d63fd0958a8d/coverage-7.10.6-cp314-cp314t-win32.whl", hash = "sha256:441c357d55f4936875636ef2cfb3bee36e466dcf50df9afbd398ce79dba1ebb7", size = 220831, upload-time = "2025-08-29T15:34:52.653Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e5/d38d0cb830abede2adb8b147770d2a3d0e7fecc7228245b9b1ae6c24930a/coverage-7.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:073711de3181b2e204e4870ac83a7c4853115b42e9cd4d145f2231e12d670930", size = 221950, upload-time = "2025-08-29T15:34:54.212Z" }, - { url = "https://files.pythonhosted.org/packages/f4/51/e48e550f6279349895b0ffcd6d2a690e3131ba3a7f4eafccc141966d4dea/coverage-7.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:137921f2bac5559334ba66122b753db6dc5d1cf01eb7b64eb412bb0d064ef35b", size = 219969, upload-time = "2025-08-29T15:34:55.83Z" }, - { url = "https://files.pythonhosted.org/packages/44/0c/50db5379b615854b5cf89146f8f5bd1d5a9693d7f3a987e269693521c404/coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3", size = 208986, upload-time = "2025-08-29T15:35:14.506Z" }, +version = "7.10.7" +source = { registry = "https://pypi.org/simple" } +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/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/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]] @@ -265,15 +313,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998, upload-time = "2025-01-27T10:46:09.186Z" }, ] -[[package]] -name = "diskcache" -version = "5.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, -] - [[package]] name = "dnspython" version = "2.8.0" @@ -340,16 +379,16 @@ standard = [ [[package]] name = "fastapi-cli" -version = "0.0.11" +version = "0.0.13" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "rich-toolkit" }, { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/23/08/0af729f6231ebdc17a0356397f966838cbe2efa38529951e24017c7435d5/fastapi_cli-0.0.11.tar.gz", hash = "sha256:4f01d751c14d3d2760339cca0f45e81d816218cae8174d1dc757b5375868cde5", size = 17550, upload-time = "2025-09-09T12:50:38.917Z" } +sdist = { url = "https://files.pythonhosted.org/packages/32/4e/3f61850012473b097fc5297d681bd85788e186fadb8555b67baf4c7707f4/fastapi_cli-0.0.13.tar.gz", hash = "sha256:312addf3f57ba7139457cf0d345c03e2170cc5a034057488259c33cd7e494529", size = 17780, upload-time = "2025-09-20T16:37:31.089Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/8f/9e3ad391d1c4183de55c256b481899bbd7bbd06d389e4986741bb289fe94/fastapi_cli-0.0.11-py3-none-any.whl", hash = "sha256:bcdd1123c6077c7466452b9490ca47821f00eb784d58496674793003f9f8e33a", size = 11095, upload-time = "2025-09-09T12:50:37.658Z" }, + { url = "https://files.pythonhosted.org/packages/08/36/7432750f3638324b055496d2c952000bea824259fca70df5577a6a3c172f/fastapi_cli-0.0.13-py3-none-any.whl", hash = "sha256:219b73ccfde7622559cef1d43197da928516acb4f21f2ec69128c4b90057baba", size = 11142, upload-time = "2025-09-20T16:37:29.695Z" }, ] [package.optional-dependencies] @@ -360,7 +399,7 @@ standard = [ [[package]] name = "fastapi-cloud-cli" -version = "0.1.5" +version = "0.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -371,9 +410,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/2e/3b6e5016affc310e5109bc580f760586eabecea0c8a7ab067611cd849ac0/fastapi_cloud_cli-0.1.5.tar.gz", hash = "sha256:341ee585eb731a6d3c3656cb91ad38e5f39809bf1a16d41de1333e38635a7937", size = 22710, upload-time = "2025-07-28T13:30:48.216Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/38/1971f9dc8141e359d2435e6fae8bb228632adc55cff00cd00efed2a98456/fastapi_cloud_cli-0.2.1.tar.gz", hash = "sha256:aa22a4b867bf53165b6551d2f4eb21b079bad4aa74047cb889acf941e34699d9", size = 23676, upload-time = "2025-09-25T13:53:32.901Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/a6/5aa862489a2918a096166fd98d9fe86b7fd53c607678b3fa9d8c432d88d5/fastapi_cloud_cli-0.1.5-py3-none-any.whl", hash = "sha256:d80525fb9c0e8af122370891f9fa83cf5d496e4ad47a8dd26c0496a6c85a012a", size = 18992, upload-time = "2025-07-28T13:30:47.427Z" }, + { url = "https://files.pythonhosted.org/packages/3b/1f/5fa06afce6e4bb7fc7e54651236bad3b849340480967c54cbd7c13563c3f/fastapi_cloud_cli-0.2.1-py3-none-any.whl", hash = "sha256:245447bfb17b01ae5f7bc15dec0833bce85381ecf34532e8fa4bcf279ad1c361", size = 19894, upload-time = "2025-09-25T13:53:31.635Z" }, ] [[package]] @@ -475,7 +514,7 @@ wheels = [ [[package]] name = "google-cloud-storage" -version = "3.3.1" +version = "3.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core" }, @@ -485,9 +524,9 @@ dependencies = [ { name = "google-resumable-media" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ce/0d/6be1c7e10d1e186e22990fdc22e7ece79f7c622370793cfe88aa8c658316/google_cloud_storage-3.3.1.tar.gz", hash = "sha256:60f291b0881e5c72919b156d1ee276d1b69a2538fcdc35f4e87559ae11678f77", size = 17224623, upload-time = "2025-09-01T05:59:02.804Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/a6/6e0a318f70975a3c048c0e1a18aee4f7b6d7dac1e798fdc5353c5248d418/google_cloud_storage-3.4.0.tar.gz", hash = "sha256:4c77ec00c98ccc6428e4c39404926f41e2152f48809b02af29d5116645c3c317", size = 17226847, upload-time = "2025-09-15T10:40:05.045Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/67/68eee082fc77e718fa483893ac2463fe0ae8f28ccab334cea9dc5aba99b0/google_cloud_storage-3.3.1-py3-none-any.whl", hash = "sha256:8cace9359b85f315f21868cf771143d6dbb47dcc5a3a9317c8207accc4d10fd3", size = 275070, upload-time = "2025-09-01T05:59:00.633Z" }, + { url = "https://files.pythonhosted.org/packages/16/12/164a90e4692423ed5532274928b0e19c8cae345ae1aa413d78c6b688231b/google_cloud_storage-3.4.0-py3-none-any.whl", hash = "sha256:16eeca305e4747a6871f8f7627eef3b862fdd365b872ca74d4a89e9841d0f8e8", size = 278423, upload-time = "2025-09-15T10:40:03.349Z" }, ] [[package]] @@ -570,34 +609,37 @@ wheels = [ [[package]] name = "grpcio" -version = "1.74.0" +version = "1.75.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/38/b4/35feb8f7cab7239c5b94bd2db71abb3d6adb5f335ad8f131abb6060840b6/grpcio-1.74.0.tar.gz", hash = "sha256:80d1f4fbb35b0742d3e3d3bb654b7381cd5f015f8497279a1e9c21ba623e01b1", size = 12756048, upload-time = "2025-07-24T18:54:23.039Z" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/88/fe2844eefd3d2188bc0d7a2768c6375b46dfd96469ea52d8aeee8587d7e0/grpcio-1.75.0.tar.gz", hash = "sha256:b989e8b09489478c2d19fecc744a298930f40d8b27c3638afbfe84d22f36ce4e", size = 12722485, upload-time = "2025-09-16T09:20:21.731Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/d8/1004a5f468715221450e66b051c839c2ce9a985aa3ee427422061fcbb6aa/grpcio-1.74.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:2bc2d7d8d184e2362b53905cb1708c84cb16354771c04b490485fa07ce3a1d89", size = 5449488, upload-time = "2025-07-24T18:53:41.174Z" }, - { url = "https://files.pythonhosted.org/packages/94/0e/33731a03f63740d7743dced423846c831d8e6da808fcd02821a4416df7fa/grpcio-1.74.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:c14e803037e572c177ba54a3e090d6eb12efd795d49327c5ee2b3bddb836bf01", size = 10974059, upload-time = "2025-07-24T18:53:43.066Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c6/3d2c14d87771a421205bdca991467cfe473ee4c6a1231c1ede5248c62ab8/grpcio-1.74.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f6ec94f0e50eb8fa1744a731088b966427575e40c2944a980049798b127a687e", size = 5945647, upload-time = "2025-07-24T18:53:45.269Z" }, - { url = "https://files.pythonhosted.org/packages/c5/83/5a354c8aaff58594eef7fffebae41a0f8995a6258bbc6809b800c33d4c13/grpcio-1.74.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:566b9395b90cc3d0d0c6404bc8572c7c18786ede549cdb540ae27b58afe0fb91", size = 6626101, upload-time = "2025-07-24T18:53:47.015Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ca/4fdc7bf59bf6994aa45cbd4ef1055cd65e2884de6113dbd49f75498ddb08/grpcio-1.74.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1ea6176d7dfd5b941ea01c2ec34de9531ba494d541fe2057c904e601879f249", size = 6182562, upload-time = "2025-07-24T18:53:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/fd/48/2869e5b2c1922583686f7ae674937986807c2f676d08be70d0a541316270/grpcio-1.74.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:64229c1e9cea079420527fa8ac45d80fc1e8d3f94deaa35643c381fa8d98f362", size = 6303425, upload-time = "2025-07-24T18:53:50.847Z" }, - { url = "https://files.pythonhosted.org/packages/a6/0e/bac93147b9a164f759497bc6913e74af1cb632c733c7af62c0336782bd38/grpcio-1.74.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:0f87bddd6e27fc776aacf7ebfec367b6d49cad0455123951e4488ea99d9b9b8f", size = 6996533, upload-time = "2025-07-24T18:53:52.747Z" }, - { url = "https://files.pythonhosted.org/packages/84/35/9f6b2503c1fd86d068b46818bbd7329db26a87cdd8c01e0d1a9abea1104c/grpcio-1.74.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3b03d8f2a07f0fea8c8f74deb59f8352b770e3900d143b3d1475effcb08eec20", size = 6491489, upload-time = "2025-07-24T18:53:55.06Z" }, - { url = "https://files.pythonhosted.org/packages/75/33/a04e99be2a82c4cbc4039eb3a76f6c3632932b9d5d295221389d10ac9ca7/grpcio-1.74.0-cp313-cp313-win32.whl", hash = "sha256:b6a73b2ba83e663b2480a90b82fdae6a7aa6427f62bf43b29912c0cfd1aa2bfa", size = 3805811, upload-time = "2025-07-24T18:53:56.798Z" }, - { url = "https://files.pythonhosted.org/packages/34/80/de3eb55eb581815342d097214bed4c59e806b05f1b3110df03b2280d6dfd/grpcio-1.74.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd3c71aeee838299c5887230b8a1822795325ddfea635edd82954c1eaa831e24", size = 4489214, upload-time = "2025-07-24T18:53:59.771Z" }, + { url = "https://files.pythonhosted.org/packages/00/64/dbce0ffb6edaca2b292d90999dd32a3bd6bc24b5b77618ca28440525634d/grpcio-1.75.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:1bb78d052948d8272c820bb928753f16a614bb2c42fbf56ad56636991b427518", size = 5666860, upload-time = "2025-09-16T09:19:25.417Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e6/da02c8fa882ad3a7f868d380bb3da2c24d35dd983dd12afdc6975907a352/grpcio-1.75.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:9dc4a02796394dd04de0b9673cb79a78901b90bb16bf99ed8cb528c61ed9372e", size = 11455148, upload-time = "2025-09-16T09:19:28.615Z" }, + { url = "https://files.pythonhosted.org/packages/ba/a0/84f87f6c2cf2a533cfce43b2b620eb53a51428ec0c8fe63e5dd21d167a70/grpcio-1.75.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:437eeb16091d31498585d73b133b825dc80a8db43311e332c08facf820d36894", size = 6243865, upload-time = "2025-09-16T09:19:31.342Z" }, + { url = "https://files.pythonhosted.org/packages/be/12/53da07aa701a4839dd70d16e61ce21ecfcc9e929058acb2f56e9b2dd8165/grpcio-1.75.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:c2c39984e846bd5da45c5f7bcea8fafbe47c98e1ff2b6f40e57921b0c23a52d0", size = 6915102, upload-time = "2025-09-16T09:19:33.658Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c0/7eaceafd31f52ec4bf128bbcf36993b4bc71f64480f3687992ddd1a6e315/grpcio-1.75.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38d665f44b980acdbb2f0e1abf67605ba1899f4d2443908df9ec8a6f26d2ed88", size = 6432042, upload-time = "2025-09-16T09:19:36.583Z" }, + { url = "https://files.pythonhosted.org/packages/6b/12/a2ce89a9f4fc52a16ed92951f1b05f53c17c4028b3db6a4db7f08332bee8/grpcio-1.75.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e8e752ab5cc0a9c5b949808c000ca7586223be4f877b729f034b912364c3964", size = 7062984, upload-time = "2025-09-16T09:19:39.163Z" }, + { url = "https://files.pythonhosted.org/packages/55/a6/2642a9b491e24482d5685c0f45c658c495a5499b43394846677abed2c966/grpcio-1.75.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3a6788b30aa8e6f207c417874effe3f79c2aa154e91e78e477c4825e8b431ce0", size = 8001212, upload-time = "2025-09-16T09:19:41.726Z" }, + { url = "https://files.pythonhosted.org/packages/19/20/530d4428750e9ed6ad4254f652b869a20a40a276c1f6817b8c12d561f5ef/grpcio-1.75.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc33e67cab6141c54e75d85acd5dec616c5095a957ff997b4330a6395aa9b51", size = 7457207, upload-time = "2025-09-16T09:19:44.368Z" }, + { url = "https://files.pythonhosted.org/packages/e2/6f/843670007e0790af332a21468d10059ea9fdf97557485ae633b88bd70efc/grpcio-1.75.0-cp313-cp313-win32.whl", hash = "sha256:c8cfc780b7a15e06253aae5f228e1e84c0d3c4daa90faf5bc26b751174da4bf9", size = 3934235, upload-time = "2025-09-16T09:19:46.815Z" }, + { url = "https://files.pythonhosted.org/packages/4b/92/c846b01b38fdf9e2646a682b12e30a70dc7c87dfe68bd5e009ee1501c14b/grpcio-1.75.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c91d5b16eff3cbbe76b7a1eaaf3d91e7a954501e9d4f915554f87c470475c3d", size = 4637558, upload-time = "2025-09-16T09:19:49.698Z" }, ] [[package]] name = "grpcio-status" -version = "1.74.0" +version = "1.75.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, { name = "grpcio" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/93/22/238c5f01e6837df54494deb08d5c772bc3f5bf5fb80a15dce254892d1a81/grpcio_status-1.74.0.tar.gz", hash = "sha256:c58c1b24aa454e30f1fc6a7e0dbbc194c54a408143971a94b5f4e40bb5831432", size = 13662, upload-time = "2025-07-24T19:01:56.874Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/8a/2e45ec0512d4ce9afa136c6e4186d063721b5b4c192eec7536ce6b7ba615/grpcio_status-1.75.0.tar.gz", hash = "sha256:69d5b91be1b8b926f086c1c483519a968c14640773a0ccdd6c04282515dbedf7", size = 13646, upload-time = "2025-09-16T09:24:51.069Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/aa/1b1fe7d8ab699e1ec26d3a36b91d3df9f83a30abc07d4c881d0296b17b67/grpcio_status-1.74.0-py3-none-any.whl", hash = "sha256:52cdbd759a6760fc8f668098a03f208f493dd5c76bf8e02598bbbaf1f6fc2876", size = 14425, upload-time = "2025-07-24T19:01:19.963Z" }, + { url = "https://files.pythonhosted.org/packages/2b/24/d536f0a0fda3a3eeb334893e5fb9d567c2777de6a5384413f71b35cfd0e5/grpcio_status-1.75.0-py3-none-any.whl", hash = "sha256:de62557ef97b7e19c3ce6da19793a12c5f6c1fbbb918d233d9671aba9d9e1d78", size = 14424, upload-time = "2025-09-16T09:23:33.843Z" }, ] [[package]] @@ -627,17 +669,17 @@ wheels = [ [[package]] name = "hf-xet" -version = "1.1.9" +version = "1.1.10" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/0f/5b60fc28ee7f8cc17a5114a584fd6b86e11c3e0a6e142a7f97a161e9640a/hf_xet-1.1.9.tar.gz", hash = "sha256:c99073ce404462e909f1d5839b2d14a3827b8fe75ed8aed551ba6609c026c803", size = 484242, upload-time = "2025-08-27T23:05:19.441Z" } +sdist = { url = "https://files.pythonhosted.org/packages/74/31/feeddfce1748c4a233ec1aa5b7396161c07ae1aa9b7bdbc9a72c3c7dd768/hf_xet-1.1.10.tar.gz", hash = "sha256:408aef343800a2102374a883f283ff29068055c111f003ff840733d3b715bb97", size = 487910, upload-time = "2025-09-12T20:10:27.12Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/12/56e1abb9a44cdef59a411fe8a8673313195711b5ecce27880eb9c8fa90bd/hf_xet-1.1.9-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:a3b6215f88638dd7a6ff82cb4e738dcbf3d863bf667997c093a3c990337d1160", size = 2762553, upload-time = "2025-08-27T23:05:15.153Z" }, - { url = "https://files.pythonhosted.org/packages/3a/e6/2d0d16890c5f21b862f5df3146519c182e7f0ae49b4b4bf2bd8a40d0b05e/hf_xet-1.1.9-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:9b486de7a64a66f9a172f4b3e0dfe79c9f0a93257c501296a2521a13495a698a", size = 2623216, upload-time = "2025-08-27T23:05:13.778Z" }, - { url = "https://files.pythonhosted.org/packages/81/42/7e6955cf0621e87491a1fb8cad755d5c2517803cea174229b0ec00ff0166/hf_xet-1.1.9-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4c5a840c2c4e6ec875ed13703a60e3523bc7f48031dfd750923b2a4d1a5fc3c", size = 3186789, upload-time = "2025-08-27T23:05:12.368Z" }, - { url = "https://files.pythonhosted.org/packages/df/8b/759233bce05457f5f7ec062d63bbfd2d0c740b816279eaaa54be92aa452a/hf_xet-1.1.9-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:96a6139c9e44dad1c52c52520db0fffe948f6bce487cfb9d69c125f254bb3790", size = 3088747, upload-time = "2025-08-27T23:05:10.439Z" }, - { url = "https://files.pythonhosted.org/packages/6c/3c/28cc4db153a7601a996985bcb564f7b8f5b9e1a706c7537aad4b4809f358/hf_xet-1.1.9-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ad1022e9a998e784c97b2173965d07fe33ee26e4594770b7785a8cc8f922cd95", size = 3251429, upload-time = "2025-08-27T23:05:16.471Z" }, - { url = "https://files.pythonhosted.org/packages/84/17/7caf27a1d101bfcb05be85850d4aa0a265b2e1acc2d4d52a48026ef1d299/hf_xet-1.1.9-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:86754c2d6d5afb11b0a435e6e18911a4199262fe77553f8c50d75e21242193ea", size = 3354643, upload-time = "2025-08-27T23:05:17.828Z" }, - { url = "https://files.pythonhosted.org/packages/cd/50/0c39c9eed3411deadcc98749a6699d871b822473f55fe472fad7c01ec588/hf_xet-1.1.9-cp37-abi3-win_amd64.whl", hash = "sha256:5aad3933de6b725d61d51034e04174ed1dce7a57c63d530df0014dea15a40127", size = 2804797, upload-time = "2025-08-27T23:05:20.77Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a2/343e6d05de96908366bdc0081f2d8607d61200be2ac802769c4284cc65bd/hf_xet-1.1.10-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:686083aca1a6669bc85c21c0563551cbcdaa5cf7876a91f3d074a030b577231d", size = 2761466, upload-time = "2025-09-12T20:10:22.836Z" }, + { url = "https://files.pythonhosted.org/packages/31/f9/6215f948ac8f17566ee27af6430ea72045e0418ce757260248b483f4183b/hf_xet-1.1.10-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:71081925383b66b24eedff3013f8e6bbd41215c3338be4b94ba75fd75b21513b", size = 2623807, upload-time = "2025-09-12T20:10:21.118Z" }, + { url = "https://files.pythonhosted.org/packages/15/07/86397573efefff941e100367bbda0b21496ffcdb34db7ab51912994c32a2/hf_xet-1.1.10-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6bceb6361c80c1cc42b5a7b4e3efd90e64630bcf11224dcac50ef30a47e435", size = 3186960, upload-time = "2025-09-12T20:10:19.336Z" }, + { url = "https://files.pythonhosted.org/packages/01/a7/0b2e242b918cc30e1f91980f3c4b026ff2eedaf1e2ad96933bca164b2869/hf_xet-1.1.10-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eae7c1fc8a664e54753ffc235e11427ca61f4b0477d757cc4eb9ae374b69f09c", size = 3087167, upload-time = "2025-09-12T20:10:17.255Z" }, + { url = "https://files.pythonhosted.org/packages/4a/25/3e32ab61cc7145b11eee9d745988e2f0f4fafda81b25980eebf97d8cff15/hf_xet-1.1.10-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0a0005fd08f002180f7a12d4e13b22be277725bc23ed0529f8add5c7a6309c06", size = 3248612, upload-time = "2025-09-12T20:10:24.093Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3d/ab7109e607ed321afaa690f557a9ada6d6d164ec852fd6bf9979665dc3d6/hf_xet-1.1.10-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f900481cf6e362a6c549c61ff77468bd59d6dd082f3170a36acfef2eb6a6793f", size = 3353360, upload-time = "2025-09-12T20:10:25.563Z" }, + { url = "https://files.pythonhosted.org/packages/ee/0e/471f0a21db36e71a2f1752767ad77e92d8cde24e974e03d662931b1305ec/hf_xet-1.1.10-cp37-abi3-win_amd64.whl", hash = "sha256:5f54b19cc347c13235ae7ee98b330c26dd65ef1df47e5316ffb1e87713ca7045", size = 2804691, upload-time = "2025-09-12T20:10:28.433Z" }, ] [[package]] @@ -685,7 +727,7 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "0.34.4" +version = "0.35.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -697,9 +739,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/45/c9/bdbe19339f76d12985bc03572f330a01a93c04dffecaaea3061bdd7fb892/huggingface_hub-0.34.4.tar.gz", hash = "sha256:a4228daa6fb001be3f4f4bdaf9a0db00e1739235702848df00885c9b5742c85c", size = 459768, upload-time = "2025-08-08T09:14:52.365Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/42/0e7be334a6851cd7d51cc11717cb95e89333ebf0064431c0255c56957526/huggingface_hub-0.35.1.tar.gz", hash = "sha256:3585b88c5169c64b7e4214d0e88163d4a709de6d1a502e0cd0459e9ee2c9c572", size = 461374, upload-time = "2025-09-23T13:43:47.074Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/7b/bb06b061991107cd8783f300adff3e7b7f284e330fd82f507f2a1417b11d/huggingface_hub-0.34.4-py3-none-any.whl", hash = "sha256:9b365d781739c93ff90c359844221beef048403f1bc1f1c123c191257c3c890a", size = 561452, upload-time = "2025-08-08T09:14:50.159Z" }, + { url = "https://files.pythonhosted.org/packages/f1/60/4acf0c8a3925d9ff491dc08fe84d37e09cfca9c3b885e0db3d4dedb98cea/huggingface_hub-0.35.1-py3-none-any.whl", hash = "sha256:2f0e2709c711e3040e31d3e0418341f7092910f1462dd00350c4e97af47280a8", size = 563340, upload-time = "2025-09-23T13:43:45.343Z" }, ] [[package]] @@ -752,6 +794,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/91/d0/274fbf7b0b12643cbbc001ce13e6a5b1607ac4929d1b11c72460152c9fc3/ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2", size = 831864, upload-time = "2025-05-31T16:39:06.38Z" }, ] +[[package]] +name = "ipywidgets" +version = "8.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "comm" }, + { name = "ipython" }, + { name = "jupyterlab-widgets" }, + { name = "traitlets" }, + { name = "widgetsnbextension" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3e/48/d3dbac45c2814cb73812f98dd6b38bbcc957a4e7bb31d6ea9c03bf94ed87/ipywidgets-8.1.7.tar.gz", hash = "sha256:15f1ac050b9ccbefd45dccfbb2ef6bed0029d8278682d569d71b8dd96bee0376", size = 116721, upload-time = "2025-05-05T12:42:03.489Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/6a/9166369a2f092bd286d24e6307de555d63616e8ddb373ebad2b5635ca4cd/ipywidgets-8.1.7-py3-none-any.whl", hash = "sha256:764f2602d25471c213919b8a1997df04bef869251db4ca8efba1b76b1bd9f7bb", size = 139806, upload-time = "2025-05-05T12:41:56.833Z" }, +] + [[package]] name = "jedi" version = "0.19.2" @@ -785,6 +843,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c1/73/04df8a6fa66d43a9fd45c30f283cc4afff17da671886e451d52af60bdc7e/jsonpickle-4.1.1-py3-none-any.whl", hash = "sha256:bb141da6057898aa2438ff268362b126826c812a1721e31cf08a6e142910dc91", size = 47125, upload-time = "2025-06-02T20:36:08.647Z" }, ] +[[package]] +name = "jupyterlab-widgets" +version = "3.0.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/7d/160595ca88ee87ac6ba95d82177d29ec60aaa63821d3077babb22ce031a5/jupyterlab_widgets-3.0.15.tar.gz", hash = "sha256:2920888a0c2922351a9202817957a68c07d99673504d6cd37345299e971bb08b", size = 213149, upload-time = "2025-05-05T12:32:31.004Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/6a/ca128561b22b60bd5a0c4ea26649e68c8556b82bc70a0c396eebc977fe86/jupyterlab_widgets-3.0.15-py3-none-any.whl", hash = "sha256:d59023d7d7ef71400d51e6fee9a88867f6e65e10a4201605d2d7f3e8f012a31c", size = 216571, upload-time = "2025-05-05T12:32:29.534Z" }, +] + +[[package]] +name = "mako" +version = "1.3.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" }, +] + [[package]] name = "markdown-it-py" version = "4.0.0" @@ -928,45 +1007,45 @@ wheels = [ [[package]] name = "numexpr" -version = "2.12.1" +version = "2.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7c/08/211c9ae8a230f20976f3b0b9a3308264c62bd05caf92aba7c59beebf6049/numexpr-2.12.1.tar.gz", hash = "sha256:e239faed0af001d1f1ea02934f7b3bb2bb6711ddb98e7a7bef61be5f45ff54ab", size = 115053, upload-time = "2025-09-11T11:04:04.36Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/81/d5e66670aec3bf4c1d90766b7e885e9f7ecf4efaceedf5a17873dd19dc4a/numexpr-2.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:593bdffe14fdd2df05d46668cff75d5a6d899c47820e97335d08670b96fb3eef", size = 153918, upload-time = "2025-09-11T11:02:39.466Z" }, - { url = "https://files.pythonhosted.org/packages/4c/36/9418c31ddf65a8ef1d1f2defc5a97c0914ad6822617bb2005cd7db60a8bd/numexpr-2.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e04e657ad1c2c735a4d6966e89b1b151036c94a25e096cc74fd16465224c7d21", size = 144357, upload-time = "2025-09-11T11:02:40.413Z" }, - { url = "https://files.pythonhosted.org/packages/b7/fd/3c4617c2440be58b2ff8937b874f07e11e11c8a48ab5410f0f87165b7129/numexpr-2.12.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:149f194a9234675f4ae541ef56b7e45d596ad8f5d015ff252005d07a08235576", size = 427264, upload-time = "2025-09-11T11:02:41.425Z" }, - { url = "https://files.pythonhosted.org/packages/da/ea/c8387c867715d9035f160d95f8b93ad6adf5c089ff7a9cdbd737f2dd0fb6/numexpr-2.12.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0af94d9f13ff76101793b6e4a9f56681f09b11a37dc86ed38749f4cbefceaa64", size = 417042, upload-time = "2025-09-11T11:02:42.451Z" }, - { url = "https://files.pythonhosted.org/packages/6c/d3/98175c655d00dd59b7fa71a8b4a2ec25c6ffa6a449cefa6bdff179367718/numexpr-2.12.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:771a2475044ae0e7e919217c411eec6c1d44d443d00678ee3135266f738feee3", size = 1388846, upload-time = "2025-09-11T11:02:43.929Z" }, - { url = "https://files.pythonhosted.org/packages/bf/8d/b3eb251f72c926a40db2e2119150abe6f4093150be92cf9663813eec6253/numexpr-2.12.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c56bfbf29fd1212cd49fc41fc18314b18469e9e018fc7bf7cd372c4e9f22e92", size = 1435987, upload-time = "2025-09-11T11:02:45.502Z" }, - { url = "https://files.pythonhosted.org/packages/08/d5/79978f126209d7fff8bff2e54cecd3ee5dc660b77150c42510973c52f5fd/numexpr-2.12.1-cp313-cp313-win32.whl", hash = "sha256:20152d269e36888f5968ad7bc1cfc3ff6bf23279d2879f4c7d803041f4000f73", size = 156951, upload-time = "2025-09-11T11:02:46.757Z" }, - { url = "https://files.pythonhosted.org/packages/9a/ae/cc0e5a0d93210da1cad4d44ef690c60f673d134394c186a4a68f01e17c2e/numexpr-2.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:30e259e8bd192a0a1b9bb9952510a8146eb1a603c62b9d727ab50d6e562dcb36", size = 151371, upload-time = "2025-09-11T11:02:47.744Z" }, - { url = "https://files.pythonhosted.org/packages/f2/54/06a7ece842cb0f231e0560700ca569961ccaaeb56bc25d8100e36ae4affb/numexpr-2.12.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:adbeb49b6eb6a98a81053a07adb8f72de6829d48aca3004a5b3da696e18c6245", size = 154647, upload-time = "2025-09-11T11:02:48.786Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b7/6231b560b1a31385a5df98eb3bda354332f58ef6ef3a1121e550c47918f6/numexpr-2.12.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:00f9f238e624a9cf9b252a09e8e596577cc31d532d9405d338baa91c6f5f579a", size = 145111, upload-time = "2025-09-11T11:02:49.731Z" }, - { url = "https://files.pythonhosted.org/packages/f9/49/801aae47bf453951cf557c1954c6de39ce4b53c98e55d0c30dfa52b6aefd/numexpr-2.12.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:68ad1af4a7846dbf87ceff09304a7b4d58acbce4b36f250ec83cab8f48278322", size = 436641, upload-time = "2025-09-11T11:02:50.687Z" }, - { url = "https://files.pythonhosted.org/packages/7d/4c/3ed2a76e864acbd3aa8699423c5a2cc58665d4b1c016498c7c60f1388d5e/numexpr-2.12.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e59cf1570404b7d733c14f6a5601358990106864e6f4463ec061e6a82c1a3117", size = 426687, upload-time = "2025-09-11T11:02:51.888Z" }, - { url = "https://files.pythonhosted.org/packages/f7/df/927b57271c1bb39a826eb24f7a70ef511a7736fed060fb7ed944aadfb67f/numexpr-2.12.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1bf294d1d1ca8a35a9ea03d42559e1aa9d34c386fbc2b17bf0713313dacd32fe", size = 1397852, upload-time = "2025-09-11T11:02:53.054Z" }, - { url = "https://files.pythonhosted.org/packages/19/d1/ec5e2d25ce304818dd06d28649acf3aa1a4d324ea2bb30c35c52be7adea8/numexpr-2.12.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6fe868309608190234be1f4d7ca33f10806129061180c57a22cdce7b26f5a426", size = 1444705, upload-time = "2025-09-11T11:02:54.69Z" }, - { url = "https://files.pythonhosted.org/packages/53/e3/e2902b257088a4d227f594a756228d48c498950a28a71f60dc557d34e4bb/numexpr-2.12.1-cp313-cp313t-win32.whl", hash = "sha256:7c0acb6c13687998679ecffa8cef83c85ada82a53db6aac820267f51814395e9", size = 157618, upload-time = "2025-09-11T11:02:56.16Z" }, - { url = "https://files.pythonhosted.org/packages/8f/32/de4a48816f414557f0c40747ab049d757dc73fdc94624bbf28147959526d/numexpr-2.12.1-cp313-cp313t-win_amd64.whl", hash = "sha256:98705e8b70c5ab4a35df7ae8a4fb42b5e71f8fed92b5c46672c4324ce866eaee", size = 152282, upload-time = "2025-09-11T11:02:57.472Z" }, - { url = "https://files.pythonhosted.org/packages/32/a5/987991b483099951a9f6674211e600f51ac3ac6d001215052be38ab6be63/numexpr-2.12.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f0fb16c9e58728e89e6519bb8be94113b813141152bb210d43f7e55aae5004ef", size = 153958, upload-time = "2025-09-11T11:02:58.395Z" }, - { url = "https://files.pythonhosted.org/packages/88/d0/6517d2c9e1dba160727080a2dbc94d6daadb540322283eecf8b014b6e88a/numexpr-2.12.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38add87c9e683e5fafd1b331a64d39b8a5b8a08b893447ef363fc5c23b74f6cf", size = 144490, upload-time = "2025-09-11T11:02:59.345Z" }, - { url = "https://files.pythonhosted.org/packages/b1/9e/d0c66f503d87a9f9581c176bc538a1ca2b0f9a4bcc88da01937ed14ba13e/numexpr-2.12.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03b9cc6da21c821524c9348bc29e5c0376a794fb68d5be9c2648e63bce651340", size = 427358, upload-time = "2025-09-11T11:03:00.327Z" }, - { url = "https://files.pythonhosted.org/packages/5e/a4/240516dc9b9664d14ab9f0ea900a31939c379917526b35e06eedb84a179c/numexpr-2.12.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:61cf86fe377f30a34ffeeb6ab065a6586f682260f7ffd155a2c5072f9a8ce11d", size = 417067, upload-time = "2025-09-11T11:03:01.714Z" }, - { url = "https://files.pythonhosted.org/packages/ad/38/e64a94aa796d2dd44617b4b3643bf8f885965845aba679849fc8f86d71ca/numexpr-2.12.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:113c8dccd663e66986c36f248f050b9f640d4c015d8201d900f29dbcf2a7dd6d", size = 1388946, upload-time = "2025-09-11T11:03:02.974Z" }, - { url = "https://files.pythonhosted.org/packages/ad/cc/9a4ee92ef12d8fc95c4f727efd9f846f9b7d7e04c0f5bcb730f8c1c591a4/numexpr-2.12.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7dd53e96559896f7f09127f7c8ca7f179d6fe58a4a52799bb45a2bc8a2c8c9e9", size = 1436049, upload-time = "2025-09-11T11:03:04.31Z" }, - { url = "https://files.pythonhosted.org/packages/68/15/742392b89d224593c149017cb2062442ba3ab173dd28119e0a1f5740f015/numexpr-2.12.1-cp314-cp314-win32.whl", hash = "sha256:abf6e100bfe341724e626507e38fd6f6910f73ded7a9193447471abcd94ab380", size = 158995, upload-time = "2025-09-11T11:03:05.58Z" }, - { url = "https://files.pythonhosted.org/packages/d6/5c/62055866ea480b81d6e58896256a7ba2cffc6ef6ac6481ffc0b44a8ae8ef/numexpr-2.12.1-cp314-cp314-win_amd64.whl", hash = "sha256:c87188f9fa81b22c5f1133304151875bc36477f779f857286df2331ebd3680ef", size = 153802, upload-time = "2025-09-11T11:03:06.605Z" }, - { url = "https://files.pythonhosted.org/packages/bd/17/bcbb07dc49624c18a53a6c59e3e9f55c5f611f3888a536e9337144c96305/numexpr-2.12.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:5a5b3331fce3b90d8031c67c854b5306c01608334053b04e6de6bebe3d2393b5", size = 154645, upload-time = "2025-09-11T11:03:07.73Z" }, - { url = "https://files.pythonhosted.org/packages/92/e2/3308099dac61fdb6df7282fd4f0b5a95716bba04c1e9a35cd6991c017710/numexpr-2.12.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8d8ac7a9860d67054857616b0e679259653120fe790c9156c1032e902df39559", size = 145111, upload-time = "2025-09-11T11:03:08.754Z" }, - { url = "https://files.pythonhosted.org/packages/c4/13/fda532440c335b6ac190b24547339be59463b783f3ff0a93fcfa074289bd/numexpr-2.12.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4582114b782ad64527a528cdae1bf2a6e022f5afc0c59f35c135142fc5f0062b", size = 436880, upload-time = "2025-09-11T11:03:09.696Z" }, - { url = "https://files.pythonhosted.org/packages/6b/49/9023d70cda5000a44c363426cdcc7243afb35533d1ab9406746b147576cc/numexpr-2.12.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddd3213f323ce5a9c6fd2adb68e875e5e87d407e3f4d24c005ee2735b834142d", size = 426862, upload-time = "2025-09-11T11:03:10.772Z" }, - { url = "https://files.pythonhosted.org/packages/99/c0/cbd8c24b56af202703852a47ca832c19401391f60b8c998e66873e01d537/numexpr-2.12.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b3d40fc207e1941eb0f777e99729d8fb6322c42d40c1eec13d415aa67e400bd6", size = 1397956, upload-time = "2025-09-11T11:03:11.988Z" }, - { url = "https://files.pythonhosted.org/packages/b4/3e/eda6efca94b792fce3659f3110bbbfc46d160a6e4f131d8f782130171d6f/numexpr-2.12.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bcc9147402fcd182d64392bb63534fb05919476226289c778ef2e3703604337e", size = 1444829, upload-time = "2025-09-11T11:03:13.317Z" }, - { url = "https://files.pythonhosted.org/packages/6f/40/b0b6addcfd97bb7edbdf4c3cc139df307c08bb84dbb85e3dc2d517312d36/numexpr-2.12.1-cp314-cp314t-win32.whl", hash = "sha256:faa76167b6e91e6e9186bef821ca5814d396ab635075c07af548ac8aadbb6097", size = 159803, upload-time = "2025-09-11T11:03:14.494Z" }, - { url = "https://files.pythonhosted.org/packages/95/1f/6f0f22b18fc29c2ce790afad05f35212052364a2c4949ceb9d09f834fe70/numexpr-2.12.1-cp314-cp314t-win_amd64.whl", hash = "sha256:0d90b23af17719d9f6ca674fab90b3cd8d625dfbf994fd1c4c2ad2b1056584c5", size = 154851, upload-time = "2025-09-11T11:03:15.515Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/45/6e/8164bc96108991fcd74e9d3eda4a4bf5752c394dde5b2408ecbefeaa339f/numexpr-2.13.0.tar.gz", hash = "sha256:3363d804f202437586447a49b5c83b01322e8be72279d49e0bf524720edc01b6", size = 118688, upload-time = "2025-09-24T12:18:17.621Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b3/642ddcd4c497a988d419c87f6e19c852fa76237530d6fa3531dae5be57d7/numexpr-2.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:071e7dd7d3722c58736fb8713827582b0eb6e752dc54b7db7ad81375c5717409", size = 161905, upload-time = "2025-09-24T12:17:28.577Z" }, + { url = "https://files.pythonhosted.org/packages/b8/8e/ade15c0aad2057923b115a76ba5a96a8b5058daa5056962ec0b20efaa2ab/numexpr-2.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c0c9d82c1cc8a4fbaf98ca7184997d3d4a96d8f5950a7cac7b63c7c88f0d7364", size = 151359, upload-time = "2025-09-24T12:17:29.608Z" }, + { url = "https://files.pythonhosted.org/packages/1f/26/8e315939b2c27880ad87dc2db66bad3aef3789448b099d06496e492e0b70/numexpr-2.13.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22ee0c6bd4b9f591793a1fd8b823e693851ace86e4fddf4ab51b71cbae59a0fc", size = 454648, upload-time = "2025-09-24T12:17:30.604Z" }, + { url = "https://files.pythonhosted.org/packages/cf/be/c6bf7d74fb716cd8d25edee004e8f06918d4cb0f1471a10a2085c741a55d/numexpr-2.13.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:450206905604486c3841b1041e21a8126cadc34da73456aa3e10bf10c6549c6a", size = 445196, upload-time = "2025-09-24T12:17:32.723Z" }, + { url = "https://files.pythonhosted.org/packages/62/42/f947150b9b6281c656cdfef375534bcda0b79e0e799c2dfa6b436e68b5be/numexpr-2.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1d485687f016ce2385817a584fe5f9e007817dbe12ce6dcdeaefae91e8abc78e", size = 1416376, upload-time = "2025-09-24T12:17:34.426Z" }, + { url = "https://files.pythonhosted.org/packages/c5/9d/8824d24b500c52fef2143bfb5a78ede273dc9418ad4cb12a976584b47beb/numexpr-2.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a21ac5bac1752ffb22e71cf7b65087ccc4fd211a06f4a358ce78c4652d44ebc0", size = 1464991, upload-time = "2025-09-24T12:17:36.066Z" }, + { url = "https://files.pythonhosted.org/packages/8d/43/32af1d8c1c7b8bfe3d9067c7610e8bffddea2f352adc24190b8be214e7ab/numexpr-2.13.0-cp313-cp313-win32.whl", hash = "sha256:8ce807751cbaa0aa6eb2a82388096eee350e8d1b8f7ee8e0452be762bda6e929", size = 166254, upload-time = "2025-09-24T12:17:37.631Z" }, + { url = "https://files.pythonhosted.org/packages/6b/88/bcf6ada80b557e19ebf6ce154a62afcb0f594ce58b9393b4fb5b8c620529/numexpr-2.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9858c7cc4436c03bd2d305a6b7f0bb3db2782e18c4428068a9e0427e20f5e111", size = 159343, upload-time = "2025-09-24T12:17:38.812Z" }, + { url = "https://files.pythonhosted.org/packages/63/dc/21fb87fa099def2cab8d27935ad2f0f715f9877d73be34a76fd4e8a9dfa3/numexpr-2.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c00310a9b06f18ddd9b02993044a6b8cae354d0c416828bd620f48e428274e1f", size = 162668, upload-time = "2025-09-24T12:17:40.109Z" }, + { url = "https://files.pythonhosted.org/packages/8c/66/5201b25039302291123fdefdc9aa2d88ce5c0d16ede979d6a8745e6c777c/numexpr-2.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d7e1ff76e7452d58e780435bdb8f1940c0c350c9b49cb2b7cdb0b0cda00c9701", size = 152036, upload-time = "2025-09-24T12:17:41.345Z" }, + { url = "https://files.pythonhosted.org/packages/86/92/c6b0257d014bbf8c81237ebff3c8ca3fc7186cfd613f21a28b725e8e608c/numexpr-2.13.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd9c3ec61407abcd42c36a59bd9e107ec1f64de18072ffca5d6f3f6897b9e4ad", size = 464918, upload-time = "2025-09-24T12:17:42.75Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d9/34afe53caab8040a2a280d1348da88b0277cc555f45b17a60a5eca4624d6/numexpr-2.13.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e638c40fb68b5e6c396d719b769b75c05d49aa92aa81426d5e9e3e2be886819", size = 455538, upload-time = "2025-09-24T12:17:43.876Z" }, + { url = "https://files.pythonhosted.org/packages/72/ee/1fbf99f1b492ae9f500d0cde20152b877f796bab4491be495020e4aef5eb/numexpr-2.13.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d5d2b40d8de3579f5409707dc30dfaa49f3dcbea41b3c1ccbc902e0d4012a792", size = 1425090, upload-time = "2025-09-24T12:17:45.049Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d5/1bb752f3cb8e2cd6610dec637bb20877082765de23a5d805d52d4a279a42/numexpr-2.13.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8bbf064c3886eda9b2ef2d9090093bd92a45a4b9eaa7ed704952a84bb212de49", size = 1473044, upload-time = "2025-09-24T12:17:46.449Z" }, + { url = "https://files.pythonhosted.org/packages/57/db/408043accb22a7585a4c5353234b547c2f38125da8719516cce7679a465f/numexpr-2.13.0-cp313-cp313t-win32.whl", hash = "sha256:7ba92383aeac47a3c8f26d214b6ed11bd10e70b74efdd53e7defe9d1a67ca591", size = 166897, upload-time = "2025-09-24T12:17:47.779Z" }, + { url = "https://files.pythonhosted.org/packages/9a/6f/6483c3400d20a8522480480561b417dbe7b0856ba771b2efa8361f89c44b/numexpr-2.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:435336e014c81866da481e2a03417e30b4f3159260c1d2a6de87191ba2fb77d8", size = 160270, upload-time = "2025-09-24T12:17:48.769Z" }, + { url = "https://files.pythonhosted.org/packages/de/45/1e952d230a034584f563416cfda5fb28a496147c2108fe180f711542ea1c/numexpr-2.13.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fec119ba261b110beeac49b905ce0cde233a5d14ef27bc26b98f2a578c30c35b", size = 161949, upload-time = "2025-09-24T12:17:49.761Z" }, + { url = "https://files.pythonhosted.org/packages/04/b2/6e42abd6edb9e3260e2e09485adc9edd59267fbacd94f20621ea43727595/numexpr-2.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c1aa845393b812f104e821ba7837c2a205a143ec02b6b384434acb2dd57682f3", size = 151412, upload-time = "2025-09-24T12:17:50.704Z" }, + { url = "https://files.pythonhosted.org/packages/5c/30/929ae24625bc8af1bf63542b3525c256b27ec8aa56a08e73163841fe07e6/numexpr-2.13.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:801a25e1d1832dc6f4134ea89d47edae3298b9c994dac25c5a7f290ebac5e4a5", size = 454817, upload-time = "2025-09-24T12:17:51.712Z" }, + { url = "https://files.pythonhosted.org/packages/6a/01/55c51aa4aa7a9f69a349d23d6db0b1edc96506f39e6d4ab2ad7dd1b2a689/numexpr-2.13.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c95d1fbd7e45ef87f9d7f7a2d714dcae01fa65317bf50e63cf4c3f24f0d6c20", size = 445128, upload-time = "2025-09-24T12:17:52.882Z" }, + { url = "https://files.pythonhosted.org/packages/fe/fc/c49f76632c0fe6c9ae6b56ccf3ee3a43194c7ae0e9b7b2a2b9a62e904679/numexpr-2.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa462b876a6b678b788a28a3fa2841c23074f26bd3659af59244c3138fa0087b", size = 1416567, upload-time = "2025-09-24T12:17:54.076Z" }, + { url = "https://files.pythonhosted.org/packages/a3/81/8dc19e400d0e347997f83a820e33637d7378f2513414386012abd65c91a0/numexpr-2.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c5331e75881a0be7ec3097f61a0ad90ded288e8916b90545f1aa67f293079521", size = 1465018, upload-time = "2025-09-24T12:17:55.255Z" }, + { url = "https://files.pythonhosted.org/packages/39/5d/5b1ae53bb1d22f87fafb69b16f77b2cfe76c5f85127bc3b8981fd7c866b8/numexpr-2.13.0-cp314-cp314-win32.whl", hash = "sha256:89a288c4dd25c12394685b0b105e85a4beddb632b3ef52e3e383f25d46d7d86f", size = 168096, upload-time = "2025-09-24T12:17:56.441Z" }, + { url = "https://files.pythonhosted.org/packages/93/2f/07b5a3f0bf3ffa3314210abac1069560344e8cde0028c385a49e08a27f51/numexpr-2.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:ae6068310da1532072bccb6c87870d0f1b584bdae3de528d577e67319b78f56c", size = 161712, upload-time = "2025-09-24T12:17:57.779Z" }, + { url = "https://files.pythonhosted.org/packages/49/86/753a9634b22c7202d02d0c5e33792bc7f8daac1865de8239383732e96f5d/numexpr-2.13.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:08173e867548e0ad251d9024c0539cb121b2f2b3a09205014cdc11c99bad4517", size = 162666, upload-time = "2025-09-24T12:17:59.169Z" }, + { url = "https://files.pythonhosted.org/packages/90/68/f4f9fbf80fca5bcf656f353b899732a56fcf192e32483bbada201bac48c5/numexpr-2.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ffd42f1631d009f0b405e3fe195f2b2551d1ac293d3147bfd4d8835c1ee91a1e", size = 152028, upload-time = "2025-09-24T12:18:00.156Z" }, + { url = "https://files.pythonhosted.org/packages/a9/d3/1df5b69265162448af4e0688c55d4483e856ae2e98d338690a707900f718/numexpr-2.13.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b6160eda53c48e0aa1b3ee02f27dfbe375f28a21d1ace1e151ec5c09011bfa4a", size = 465112, upload-time = "2025-09-24T12:18:01.184Z" }, + { url = "https://files.pythonhosted.org/packages/75/ee/de8cac0ff21c795106d0ac84418035a611f5a4acd0318e83b8a0b9815271/numexpr-2.13.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce3dc0f3f3dcf05c372a624c0e048f159849b0689e4d91a655ab4f2c5ba5a7f6", size = 455809, upload-time = "2025-09-24T12:18:02.396Z" }, + { url = "https://files.pythonhosted.org/packages/04/5b/6e812264d08f440a429e6dd0d0e8fd3709d06c5089c4ead29f14a46ff4d3/numexpr-2.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d017db23f4be18e00d96053f16af93ff77f8b1027d47a2cee78a980393007bc0", size = 1425342, upload-time = "2025-09-24T12:18:03.707Z" }, + { url = "https://files.pythonhosted.org/packages/fd/75/73a7b7ed28550eada9b1aacded71a6941ad28b4026c75de93f87353b7d49/numexpr-2.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:15e51cc76ea2e7f2b3bfb08d2d01bc84f48ba6b2e2b27a9ce645edb06293b91b", size = 1473175, upload-time = "2025-09-24T12:18:04.977Z" }, + { url = "https://files.pythonhosted.org/packages/b8/67/914ab2261130ac0f210a26187d7c0bd9c0c4e3be5ba56f889aa7eec59c3f/numexpr-2.13.0-cp314-cp314t-win32.whl", hash = "sha256:3ae41249afc233e12f186149da1d80ac1ffcfb0ee65ebbd590bdc81f16a5fa77", size = 168768, upload-time = "2025-09-24T12:18:06.215Z" }, + { url = "https://files.pythonhosted.org/packages/29/62/dab5c5e41eef490b3ee759ee5aa6c36b1af4bb43733515bea32c87dd9deb/numexpr-2.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:43d0fe73398e4d3b9ff548d6bda3d37b3569539b1d8cabe3023727b690b4f118", size = 162765, upload-time = "2025-09-24T12:18:07.338Z" }, ] [[package]] @@ -999,7 +1078,7 @@ wheels = [ [[package]] name = "openapi-python-client" -version = "0.26.0" +version = "0.26.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -1014,9 +1093,9 @@ dependencies = [ { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/16/4a/7e8d29b76aa705fe571e75dcbc5ceb8a60560784e127213b3a1bebed6ca1/openapi_python_client-0.26.0.tar.gz", hash = "sha256:f4882d59238105edc49b6fe78baadff7f8b35b8c538af8fdaccd44d64bbe72e0", size = 125997, upload-time = "2025-08-26T03:01:26.597Z" } +sdist = { url = "https://files.pythonhosted.org/packages/69/31/264cf223b301186cfd2f3a78f962ee641e3b7794ec7b483380c56d59e824/openapi_python_client-0.26.1.tar.gz", hash = "sha256:e3832f0ef074a0ab591d1eeb5d3dab2ca820cd0349f7e79d9663b7b21206be5d", size = 126194, upload-time = "2025-09-13T05:49:34.514Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/27/4b8ac011c995d8d8571fe65bd45f3fa987af62877114cc16019001e4958d/openapi_python_client-0.26.0-py3-none-any.whl", hash = "sha256:40b759fd3bd19460bb7a8066ec4b24ebae9873245d2290862bc5704ad8ed776d", size = 183452, upload-time = "2025-08-26T03:01:25.138Z" }, + { url = "https://files.pythonhosted.org/packages/f3/56/d9304d6b7f8a173c868e10a506b8ccab79305d9e412b06cf89c654155f35/openapi_python_client-0.26.1-py3-none-any.whl", hash = "sha256:415cb8095b1a3f15cec45670c5075c5097f65390a351d21512e8f6ea5c1be644", size = 183638, upload-time = "2025-09-13T05:49:32.55Z" }, ] [[package]] @@ -1288,19 +1367,38 @@ wheels = [ [[package]] name = "policyengine" -version = "0.6.1" +version = "3.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "diskcache" }, + { name = "alembic" }, + { name = "blosc" }, { name = "getpass4" }, { name = "google-cloud-storage" }, + { name = "ipywidgets" }, { name = "microdf-python" }, + { name = "pandas" }, + { name = "psycopg2-binary" }, + { name = "pydantic" }, + { name = "pymysql" }, + { name = "rich" }, + { name = "sqlalchemy" }, + { name = "sqlmodel" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/39/5cf9b8135f38d994b7da48f13639d3f1b4853b1d6b95ca858eaecfb152c4/policyengine-3.0.0.tar.gz", hash = "sha256:ba7ce61b7a82f8835b6768e5f7aea90f335a18119bab516b72305fc950b38eaa", size = 149539, upload-time = "2025-09-23T09:36:58.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/8e/458a1283f69b2ecb4c76e9acf288fff4d25f87b526bc2553ad65f68964cc/policyengine-3.0.0-py3-none-any.whl", hash = "sha256:55d7be28e9888329be84fc7e1a74d6d880c70f62852c1c3e80830464a3f9b88e", size = 54609, upload-time = "2025-09-23T09:36:56.872Z" }, +] + +[package.optional-dependencies] +uk = [ { name = "policyengine-core" }, { name = "policyengine-uk" }, +] +us = [ + { name = "policyengine-core" }, { name = "policyengine-us" }, - { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b0/97/6e21ea2f22ae3ac667146fab2d5cff9fb4a481fa8abd53b394d1aef06cb2/policyengine-0.6.1.tar.gz", hash = "sha256:e647c09bb353b10bb4416267309df31dbac941b159e0615db7483249e276721b", size = 202869, upload-time = "2025-08-14T08:12:38.477Z" } [[package]] name = "policyengine-core" @@ -1375,10 +1473,8 @@ source = { editable = "." } dependencies = [ { name = "opentelemetry-instrumentation-fastapi" }, { name = "opentelemetry-instrumentation-sqlalchemy" }, - { name = "policyengine" }, + { name = "policyengine", extra = ["uk", "us"] }, { name = "policyengine-fastapi" }, - { name = "policyengine-uk" }, - { name = "policyengine-us" }, { name = "pydantic-settings" }, { name = "tables" }, ] @@ -1401,10 +1497,8 @@ requires-dist = [ { name = "openapi-python-client", marker = "extra == 'build'", specifier = ">=0.21.6" }, { name = "opentelemetry-instrumentation-fastapi", specifier = ">=0.51b0,<0.52" }, { name = "opentelemetry-instrumentation-sqlalchemy", specifier = ">=0.51b0,<0.52" }, - { name = "policyengine", specifier = ">=0.6.0" }, + { name = "policyengine", extras = ["uk", "us"], specifier = ">=0.6.0" }, { name = "policyengine-fastapi", editable = "../../libs/policyengine-fastapi" }, - { name = "policyengine-uk", specifier = ">=2.22.8" }, - { name = "policyengine-us", specifier = ">=1.370.2" }, { name = "pydantic-settings", specifier = ">=2.7.1,<3.0.0" }, { name = "pyright", marker = "extra == 'build'", specifier = ">=1.1.401" }, { name = "pytest", marker = "extra == 'test'", specifier = ">=8.3.4" }, @@ -1416,7 +1510,7 @@ provides-extras = ["test", "build"] [[package]] name = "policyengine-uk" -version = "2.51.0" +version = "2.53.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "microdf-python" }, @@ -1424,23 +1518,23 @@ dependencies = [ { name = "pydantic" }, { name = "tables" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/13/d7/424a0bae90fe7ffba66c7612d62117b76debdb45fd60fd7aa8eee4c4c2d0/policyengine_uk-2.51.0.tar.gz", hash = "sha256:dcacb4ba17cac123ea9411070a32c78c0efb799697ac1656858a3f21bdcf83b1", size = 1048567, upload-time = "2025-09-10T09:29:59.925Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6a/3650b68e68dc79d56277097c80c7fa59f3d6c2bb8c9f4f48d8a2a857f3a7/policyengine_uk-2.53.0.tar.gz", hash = "sha256:c367e1e1c7a6ff11c1456b79b586cf44c619cb73f8c830237f21720c6a797a66", size = 1049649, upload-time = "2025-09-25T09:23:48.45Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/4e/606b47899d9627cfadf33ca63357269031d24923435eb624e666f7a2f769/policyengine_uk-2.51.0-py3-none-any.whl", hash = "sha256:6388d0e54da215737476f2cbf65734354d12c4c977981202f2e28b2f6ecd37ab", size = 1605338, upload-time = "2025-09-10T09:29:58.294Z" }, + { url = "https://files.pythonhosted.org/packages/a1/72/f1942fa511a4fff1647d95793bf70437f9751533c919e1c78ca485f79aa7/policyengine_uk-2.53.0-py3-none-any.whl", hash = "sha256:c10a363ace92ee5c30a213a21b0796b996e873f3a29a523746842ab725d87471", size = 1606515, upload-time = "2025-09-25T09:23:46.692Z" }, ] [[package]] name = "policyengine-us" -version = "1.398.0" +version = "1.405.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "microdf-python" }, { name = "policyengine-core" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d1/4b/2e5de9b21a77d4cc9a289c75ce099e40596d6dc671a8f0053a191ee1e999/policyengine_us-1.398.0.tar.gz", hash = "sha256:5924969c2878766a9dc6580b5ef62e3810569861241ff424814d17101d5361be", size = 8009681, upload-time = "2025-09-11T11:14:04.021Z" } +sdist = { url = "https://files.pythonhosted.org/packages/11/5c/aed7f69ec6b65b1b0a8e647040cc7b6b168d1ca87f47c065db39678eb9f6/policyengine_us-1.405.0.tar.gz", hash = "sha256:72ed53a56458dfb274027ef4b47b0d20619458cfa49d7d47220e442267d4b96d", size = 8033705, upload-time = "2025-09-25T21:33:15.561Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/09/0b30a486f0dbd3ad4f7aa9d861ad2ea975b78eb6ea2d78249efe2ebb34de/policyengine_us-1.398.0-py3-none-any.whl", hash = "sha256:386444e9f89dca9d303b4561171c43f7d140eeac49136b5f1960c395a44edfee", size = 5918836, upload-time = "2025-09-11T11:13:59.438Z" }, + { url = "https://files.pythonhosted.org/packages/64/28/f12d9edb6c8d52bed5f9992db34fa8a1a72a05403eb8022f5bf9a4f4ad29/policyengine_us-1.405.0-py3-none-any.whl", hash = "sha256:a1a68efac75064214807d4dd884dfdbe548ab4fae2fc5ae54d384a5bfae18169", size = 5980868, upload-time = "2025-09-25T21:33:10.806Z" }, ] [[package]] @@ -1469,16 +1563,16 @@ wheels = [ [[package]] name = "protobuf" -version = "6.32.0" +version = "6.32.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c0/df/fb4a8eeea482eca989b51cffd274aac2ee24e825f0bf3cbce5281fa1567b/protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2", size = 440614, upload-time = "2025-08-14T21:21:25.015Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/a4/cc17347aa2897568beece2e674674359f911d6fe21b0b8d6268cd42727ac/protobuf-6.32.1.tar.gz", hash = "sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d", size = 440635, upload-time = "2025-09-11T21:38:42.935Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/18/df8c87da2e47f4f1dcc5153a81cd6bca4e429803f4069a299e236e4dd510/protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741", size = 424409, upload-time = "2025-08-14T21:21:12.366Z" }, - { url = "https://files.pythonhosted.org/packages/e1/59/0a820b7310f8139bd8d5a9388e6a38e1786d179d6f33998448609296c229/protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e", size = 435735, upload-time = "2025-08-14T21:21:15.046Z" }, - { url = "https://files.pythonhosted.org/packages/cc/5b/0d421533c59c789e9c9894683efac582c06246bf24bb26b753b149bd88e4/protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0", size = 426449, upload-time = "2025-08-14T21:21:16.687Z" }, - { url = "https://files.pythonhosted.org/packages/ec/7b/607764ebe6c7a23dcee06e054fd1de3d5841b7648a90fd6def9a3bb58c5e/protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1", size = 322869, upload-time = "2025-08-14T21:21:18.282Z" }, - { url = "https://files.pythonhosted.org/packages/40/01/2e730bd1c25392fc32e3268e02446f0d77cb51a2c3a8486b1798e34d5805/protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c", size = 322009, upload-time = "2025-08-14T21:21:19.893Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f2/80ffc4677aac1bc3519b26bc7f7f5de7fce0ee2f7e36e59e27d8beb32dd1/protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783", size = 169287, upload-time = "2025-08-14T21:21:23.515Z" }, + { url = "https://files.pythonhosted.org/packages/c0/98/645183ea03ab3995d29086b8bf4f7562ebd3d10c9a4b14ee3f20d47cfe50/protobuf-6.32.1-cp310-abi3-win32.whl", hash = "sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085", size = 424411, upload-time = "2025-09-11T21:38:27.427Z" }, + { url = "https://files.pythonhosted.org/packages/8c/f3/6f58f841f6ebafe076cebeae33fc336e900619d34b1c93e4b5c97a81fdfa/protobuf-6.32.1-cp310-abi3-win_amd64.whl", hash = "sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1", size = 435738, upload-time = "2025-09-11T21:38:30.959Z" }, + { url = "https://files.pythonhosted.org/packages/10/56/a8a3f4e7190837139e68c7002ec749190a163af3e330f65d90309145a210/protobuf-6.32.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281", size = 426454, upload-time = "2025-09-11T21:38:34.076Z" }, + { url = "https://files.pythonhosted.org/packages/3f/be/8dd0a927c559b37d7a6c8ab79034fd167dcc1f851595f2e641ad62be8643/protobuf-6.32.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4", size = 322874, upload-time = "2025-09-11T21:38:35.509Z" }, + { url = "https://files.pythonhosted.org/packages/5c/f6/88d77011b605ef979aace37b7703e4eefad066f7e84d935e5a696515c2dd/protobuf-6.32.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710", size = 322013, upload-time = "2025-09-11T21:38:37.017Z" }, + { url = "https://files.pythonhosted.org/packages/97/b7/15cc7d93443d6c6a84626ae3258a91f4c6ac8c0edd5df35ea7658f71b79c/protobuf-6.32.1-py3-none-any.whl", hash = "sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346", size = 169289, upload-time = "2025-09-11T21:38:41.234Z" }, ] [[package]] @@ -1496,6 +1590,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/d7/7831438e6c3ebbfa6e01a927127a6cb42ad3ab844247f3c5b96bea25d73d/psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649", size = 254444, upload-time = "2024-12-19T18:22:11.335Z" }, ] +[[package]] +name = "psycopg2-binary" +version = "2.9.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/bdc8274dc0585090b4e3432267d7be4dfbfd8971c0fa59167c711105a6bf/psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", size = 385764, upload-time = "2024-10-16T11:24:58.126Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/30/d41d3ba765609c0763505d565c4d12d8f3c79793f0d0f044ff5a28bf395b/psycopg2_binary-2.9.10-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", size = 3044699, upload-time = "2024-10-16T11:21:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/35/44/257ddadec7ef04536ba71af6bc6a75ec05c5343004a7ec93006bee66c0bc/psycopg2_binary-2.9.10-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", size = 3275245, upload-time = "2024-10-16T11:21:51.989Z" }, + { url = "https://files.pythonhosted.org/packages/1b/11/48ea1cd11de67f9efd7262085588790a95d9dfcd9b8a687d46caf7305c1a/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", size = 2851631, upload-time = "2024-10-16T11:21:57.584Z" }, + { url = "https://files.pythonhosted.org/packages/62/e0/62ce5ee650e6c86719d621a761fe4bc846ab9eff8c1f12b1ed5741bf1c9b/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", size = 3082140, upload-time = "2024-10-16T11:22:02.005Z" }, + { url = "https://files.pythonhosted.org/packages/27/ce/63f946c098611f7be234c0dd7cb1ad68b0b5744d34f68062bb3c5aa510c8/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", size = 3264762, upload-time = "2024-10-16T11:22:06.412Z" }, + { url = "https://files.pythonhosted.org/packages/43/25/c603cd81402e69edf7daa59b1602bd41eb9859e2824b8c0855d748366ac9/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", size = 3020967, upload-time = "2024-10-16T11:22:11.583Z" }, + { url = "https://files.pythonhosted.org/packages/5f/d6/8708d8c6fca531057fa170cdde8df870e8b6a9b136e82b361c65e42b841e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", size = 2872326, upload-time = "2024-10-16T11:22:16.406Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712, upload-time = "2024-10-16T11:22:21.366Z" }, + { url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155, upload-time = "2024-10-16T11:22:25.684Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356, upload-time = "2024-10-16T11:22:30.562Z" }, + { url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224, upload-time = "2025-01-04T20:09:19.234Z" }, +] + [[package]] name = "ptyprocess" version = "0.7.0" @@ -1546,7 +1659,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.11.7" +version = "2.11.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -1554,9 +1667,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/5d/09a551ba512d7ca404d785072700d3f6727a02f6f3c24ecfd081c7cf0aa8/pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2", size = 788495, upload-time = "2025-09-13T11:26:39.325Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d3/108f2006987c58e76691d5ae5d200dd3e0f532cb4e5fa3560751c3a1feba/pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2", size = 444855, upload-time = "2025-09-13T11:26:36.909Z" }, ] [package.optional-dependencies] @@ -1594,16 +1707,16 @@ wheels = [ [[package]] name = "pydantic-settings" -version = "2.10.1" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583, upload-time = "2025-06-24T13:26:46.841Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/c5/dbbc27b814c71676593d1c3f718e6cd7d4f00652cefa24b75f7aa3efb25e/pydantic_settings-2.11.0.tar.gz", hash = "sha256:d0e87a1c7d33593beb7194adb8470fc426e95ba02af83a0f23474a04c9a08180", size = 188394, upload-time = "2025-09-24T14:19:11.764Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235, upload-time = "2025-06-24T13:26:45.485Z" }, + { url = "https://files.pythonhosted.org/packages/83/d6/887a1ff844e64aa823fb4905978d882a633cfe295c32eacad582b78a7d8b/pydantic_settings-2.11.0-py3-none-any.whl", hash = "sha256:fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c", size = 48608, upload-time = "2025-09-24T14:19:10.015Z" }, ] [[package]] @@ -1624,11 +1737,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, ] +[[package]] +name = "pymysql" +version = "1.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/ae/1fe3fcd9f959efa0ebe200b8de88b5a5ce3e767e38c7ac32fb179f16a388/pymysql-1.1.2.tar.gz", hash = "sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03", size = 48258, upload-time = "2025-08-24T12:55:55.146Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/4c/ad33b92b9864cbde84f259d5df035a6447f91891f5be77788e2a3892bce3/pymysql-1.1.2-py3-none-any.whl", hash = "sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9", size = 45300, upload-time = "2025-08-24T12:55:53.394Z" }, +] + [[package]] name = "pyperclip" -version = "1.9.0" +version = "1.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/23/2f0a3efc4d6a32f3b63cdff36cd398d9701d26cda58e3ab97ac79fb5e60d/pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310", size = 20961, upload-time = "2024-06-18T20:38:48.401Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/99/25f4898cf420efb6f45f519de018f4faea5391114a8618b16736ef3029f1/pyperclip-1.10.0.tar.gz", hash = "sha256:180c8346b1186921c75dfd14d9048a6b5d46bfc499778811952c6dd6eb1ca6be", size = 12193, upload-time = "2025-09-18T00:54:00.384Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/bc/22540e73c5f5ae18f02924cd3954a6c9a4aa6b713c841a94c98335d333a1/pyperclip-1.10.0-py3-none-any.whl", hash = "sha256:596fbe55dc59263bff26e61d2afbe10223e2fccb5210c9c96a28d6887cfcc7ec", size = 11062, upload-time = "2025-09-18T00:53:59.252Z" }, +] [[package]] name = "pyright" @@ -1661,14 +1786,14 @@ wheels = [ [[package]] name = "pytest-asyncio" -version = "1.1.0" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -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" } +sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157, upload-time = "2025-07-16T04:29:24.929Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" }, ] [[package]] @@ -1724,6 +1849,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, ] +[[package]] +name = "pytokens" +version = "0.1.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/5f/e959a442435e24f6fb5a01aec6c657079ceaca1b3baf18561c3728d681da/pytokens-0.1.10.tar.gz", hash = "sha256:c9a4bfa0be1d26aebce03e6884ba454e842f186a59ea43a6d3b25af58223c044", size = 12171, upload-time = "2025-02-19T14:51:22.001Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/e5/63bed382f6a7a5ba70e7e132b8b7b8abbcf4888ffa6be4877698dcfbed7d/pytokens-0.1.10-py3-none-any.whl", hash = "sha256:db7b72284e480e69fb085d9f251f66b3d2df8b7166059261258ff35f50fb711b", size = 12046, upload-time = "2025-02-19T14:51:18.694Z" }, +] + [[package]] name = "pytz" version = "2025.2" @@ -1749,19 +1883,38 @@ wheels = [ [[package]] name = "pyyaml" -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/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, - { 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" }, +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] [[package]] @@ -1864,58 +2017,63 @@ wheels = [ [[package]] name = "ruamel-yaml-clib" -version = "0.2.12" +version = "0.2.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315, upload-time = "2024-10-20T10:10:56.22Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/e9/39ec4d4b3f91188fad1842748f67d4e749c77c37e353c4e545052ee8e893/ruamel.yaml.clib-0.2.14.tar.gz", hash = "sha256:803f5044b13602d58ea378576dd75aa759f52116a0232608e8fdada4da33752e", size = 225394, upload-time = "2025-09-22T19:51:23.753Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/00/4864119668d71a5fa45678f380b5923ff410701565821925c69780356ffa/ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a", size = 132011, upload-time = "2024-10-20T10:13:04.377Z" }, - { url = "https://files.pythonhosted.org/packages/7f/5e/212f473a93ae78c669ffa0cb051e3fee1139cb2d385d2ae1653d64281507/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475", size = 642488, upload-time = "2024-10-20T10:13:05.906Z" }, - { url = "https://files.pythonhosted.org/packages/1f/8f/ecfbe2123ade605c49ef769788f79c38ddb1c8fa81e01f4dbf5cf1a44b16/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef", size = 745066, upload-time = "2024-10-20T10:13:07.26Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a9/28f60726d29dfc01b8decdb385de4ced2ced9faeb37a847bd5cf26836815/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6", size = 701785, upload-time = "2024-10-20T10:13:08.504Z" }, - { url = "https://files.pythonhosted.org/packages/84/7e/8e7ec45920daa7f76046578e4f677a3215fe8f18ee30a9cb7627a19d9b4c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf", size = 693017, upload-time = "2024-10-21T11:26:48.866Z" }, - { url = "https://files.pythonhosted.org/packages/c5/b3/d650eaade4ca225f02a648321e1ab835b9d361c60d51150bac49063b83fa/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1", size = 741270, upload-time = "2024-10-21T11:26:50.213Z" }, - { url = "https://files.pythonhosted.org/packages/87/b8/01c29b924dcbbed75cc45b30c30d565d763b9c4d540545a0eeecffb8f09c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01", size = 709059, upload-time = "2024-12-11T19:58:18.846Z" }, - { url = "https://files.pythonhosted.org/packages/30/8c/ed73f047a73638257aa9377ad356bea4d96125b305c34a28766f4445cc0f/ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6", size = 98583, upload-time = "2024-10-20T10:13:09.658Z" }, - { url = "https://files.pythonhosted.org/packages/b0/85/e8e751d8791564dd333d5d9a4eab0a7a115f7e349595417fd50ecae3395c/ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3", size = 115190, upload-time = "2024-10-20T10:13:10.66Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ae/e3811f05415594025e96000349d3400978adaed88d8f98d494352d9761ee/ruamel.yaml.clib-0.2.14-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7e4f9da7e7549946e02a6122dcad00b7c1168513acb1f8a726b1aaf504a99d32", size = 269205, upload-time = "2025-09-23T14:24:15.06Z" }, + { url = "https://files.pythonhosted.org/packages/72/06/7d51f4688d6d72bb72fa74254e1593c4f5ebd0036be5b41fe39315b275e9/ruamel.yaml.clib-0.2.14-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:dd7546c851e59c06197a7c651335755e74aa383a835878ca86d2c650c07a2f85", size = 137417, upload-time = "2025-09-22T19:50:59.82Z" }, + { url = "https://files.pythonhosted.org/packages/5a/08/b4499234a420ef42960eeb05585df5cc7eb25ccb8c980490b079e6367050/ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:1c1acc3a0209ea9042cc3cfc0790edd2eddd431a2ec3f8283d081e4d5018571e", size = 642558, upload-time = "2025-09-22T19:51:03.388Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ba/1975a27dedf1c4c33306ee67c948121be8710b19387aada29e2f139c43ee/ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2070bf0ad1540d5c77a664de07ebcc45eebd1ddcab71a7a06f26936920692beb", size = 744087, upload-time = "2025-09-22T19:51:00.897Z" }, + { url = "https://files.pythonhosted.org/packages/20/15/8a19a13d27f3bd09fa18813add8380a29115a47b553845f08802959acbce/ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd8fe07f49c170e09d76773fb86ad9135e0beee44f36e1576a201b0676d3d1d", size = 699709, upload-time = "2025-09-22T19:51:02.075Z" }, + { url = "https://files.pythonhosted.org/packages/19/ee/8d6146a079ad21e534b5083c9ee4a4c8bec42f79cf87594b60978286b39a/ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ff86876889ea478b1381089e55cf9e345707b312beda4986f823e1d95e8c0f59", size = 708926, upload-time = "2025-09-23T18:42:51.707Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/426b714abdc222392e68f3b8ad323930d05a214a27c7e7a0f06c69126401/ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1f118b707eece8cf84ecbc3e3ec94d9db879d85ed608f95870d39b2d2efa5dca", size = 740202, upload-time = "2025-09-22T19:51:04.673Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ac/3c5c2b27a183f4fda8a57c82211721c016bcb689a4a175865f7646db9f94/ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b30110b29484adc597df6bd92a37b90e63a8c152ca8136aad100a02f8ba6d1b6", size = 765196, upload-time = "2025-09-22T19:51:05.916Z" }, + { url = "https://files.pythonhosted.org/packages/92/2e/06f56a71fd55021c993ed6e848c9b2e5e9cfce180a42179f0ddd28253f7c/ruamel.yaml.clib-0.2.14-cp313-cp313-win32.whl", hash = "sha256:f4e97a1cf0b7a30af9e1d9dad10a5671157b9acee790d9e26996391f49b965a2", size = 98635, upload-time = "2025-09-22T19:51:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/51/79/76aba16a1689b50528224b182f71097ece338e7a4ab55e84c2e73443b78a/ruamel.yaml.clib-0.2.14-cp313-cp313-win_amd64.whl", hash = "sha256:090782b5fb9d98df96509eecdbcaffd037d47389a89492320280d52f91330d78", size = 115238, upload-time = "2025-09-22T19:51:07.081Z" }, + { url = "https://files.pythonhosted.org/packages/21/e2/a59ff65c26aaf21a24eb38df777cb9af5d87ba8fc8107c163c2da9d1e85e/ruamel.yaml.clib-0.2.14-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:7df6f6e9d0e33c7b1d435defb185095386c469109de723d514142632a7b9d07f", size = 271441, upload-time = "2025-09-23T14:24:16.498Z" }, + { url = "https://files.pythonhosted.org/packages/6b/fa/3234f913fe9a6525a7b97c6dad1f51e72b917e6872e051a5e2ffd8b16fbb/ruamel.yaml.clib-0.2.14-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:70eda7703b8126f5e52fcf276e6c0f40b0d314674f896fc58c47b0aef2b9ae83", size = 137970, upload-time = "2025-09-22T19:51:09.472Z" }, + { url = "https://files.pythonhosted.org/packages/ef/ec/4edbf17ac2c87fa0845dd366ef8d5852b96eb58fcd65fc1ecf5fe27b4641/ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a0cb71ccc6ef9ce36eecb6272c81afdc2f565950cdcec33ae8e6cd8f7fc86f27", size = 739639, upload-time = "2025-09-22T19:51:10.566Z" }, + { url = "https://files.pythonhosted.org/packages/15/18/b0e1fafe59051de9e79cdd431863b03593ecfa8341c110affad7c8121efc/ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e7cb9ad1d525d40f7d87b6df7c0ff916a66bc52cb61b66ac1b2a16d0c1b07640", size = 764456, upload-time = "2025-09-22T19:51:11.736Z" }, ] [[package]] name = "ruff" -version = "0.12.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a8/f0/e0965dd709b8cabe6356811c0ee8c096806bb57d20b5019eb4e48a117410/ruff-0.12.12.tar.gz", hash = "sha256:b86cd3415dbe31b3b46a71c598f4c4b2f550346d1ccf6326b347cc0c8fd063d6", size = 5359915, upload-time = "2025-09-04T16:50:18.273Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/09/79/8d3d687224d88367b51c7974cec1040c4b015772bfbeffac95face14c04a/ruff-0.12.12-py3-none-linux_armv6l.whl", hash = "sha256:de1c4b916d98ab289818e55ce481e2cacfaad7710b01d1f990c497edf217dafc", size = 12116602, upload-time = "2025-09-04T16:49:18.892Z" }, - { url = "https://files.pythonhosted.org/packages/c3/c3/6e599657fe192462f94861a09aae935b869aea8a1da07f47d6eae471397c/ruff-0.12.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7acd6045e87fac75a0b0cdedacf9ab3e1ad9d929d149785903cff9bb69ad9727", size = 12868393, upload-time = "2025-09-04T16:49:23.043Z" }, - { url = "https://files.pythonhosted.org/packages/e8/d2/9e3e40d399abc95336b1843f52fc0daaceb672d0e3c9290a28ff1a96f79d/ruff-0.12.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:abf4073688d7d6da16611f2f126be86523a8ec4343d15d276c614bda8ec44edb", size = 12036967, upload-time = "2025-09-04T16:49:26.04Z" }, - { url = "https://files.pythonhosted.org/packages/e9/03/6816b2ed08836be272e87107d905f0908be5b4a40c14bfc91043e76631b8/ruff-0.12.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:968e77094b1d7a576992ac078557d1439df678a34c6fe02fd979f973af167577", size = 12276038, upload-time = "2025-09-04T16:49:29.056Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d5/707b92a61310edf358a389477eabd8af68f375c0ef858194be97ca5b6069/ruff-0.12.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42a67d16e5b1ffc6d21c5f67851e0e769517fb57a8ebad1d0781b30888aa704e", size = 11901110, upload-time = "2025-09-04T16:49:32.07Z" }, - { url = "https://files.pythonhosted.org/packages/9d/3d/f8b1038f4b9822e26ec3d5b49cf2bc313e3c1564cceb4c1a42820bf74853/ruff-0.12.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b216ec0a0674e4b1214dcc998a5088e54eaf39417327b19ffefba1c4a1e4971e", size = 13668352, upload-time = "2025-09-04T16:49:35.148Z" }, - { url = "https://files.pythonhosted.org/packages/98/0e/91421368ae6c4f3765dd41a150f760c5f725516028a6be30e58255e3c668/ruff-0.12.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:59f909c0fdd8f1dcdbfed0b9569b8bf428cf144bec87d9de298dcd4723f5bee8", size = 14638365, upload-time = "2025-09-04T16:49:38.892Z" }, - { url = "https://files.pythonhosted.org/packages/74/5d/88f3f06a142f58ecc8ecb0c2fe0b82343e2a2b04dcd098809f717cf74b6c/ruff-0.12.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ac93d87047e765336f0c18eacad51dad0c1c33c9df7484c40f98e1d773876f5", size = 14060812, upload-time = "2025-09-04T16:49:42.732Z" }, - { url = "https://files.pythonhosted.org/packages/13/fc/8962e7ddd2e81863d5c92400820f650b86f97ff919c59836fbc4c1a6d84c/ruff-0.12.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01543c137fd3650d322922e8b14cc133b8ea734617c4891c5a9fccf4bfc9aa92", size = 13050208, upload-time = "2025-09-04T16:49:46.434Z" }, - { url = "https://files.pythonhosted.org/packages/53/06/8deb52d48a9a624fd37390555d9589e719eac568c020b27e96eed671f25f/ruff-0.12.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afc2fa864197634e549d87fb1e7b6feb01df0a80fd510d6489e1ce8c0b1cc45", size = 13311444, upload-time = "2025-09-04T16:49:49.931Z" }, - { url = "https://files.pythonhosted.org/packages/2a/81/de5a29af7eb8f341f8140867ffb93f82e4fde7256dadee79016ac87c2716/ruff-0.12.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0c0945246f5ad776cb8925e36af2438e66188d2b57d9cf2eed2c382c58b371e5", size = 13279474, upload-time = "2025-09-04T16:49:53.465Z" }, - { url = "https://files.pythonhosted.org/packages/7f/14/d9577fdeaf791737ada1b4f5c6b59c21c3326f3f683229096cccd7674e0c/ruff-0.12.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a0fbafe8c58e37aae28b84a80ba1817f2ea552e9450156018a478bf1fa80f4e4", size = 12070204, upload-time = "2025-09-04T16:49:56.882Z" }, - { url = "https://files.pythonhosted.org/packages/77/04/a910078284b47fad54506dc0af13839c418ff704e341c176f64e1127e461/ruff-0.12.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b9c456fb2fc8e1282affa932c9e40f5ec31ec9cbb66751a316bd131273b57c23", size = 11880347, upload-time = "2025-09-04T16:49:59.729Z" }, - { url = "https://files.pythonhosted.org/packages/df/58/30185fcb0e89f05e7ea82e5817b47798f7fa7179863f9d9ba6fd4fe1b098/ruff-0.12.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5f12856123b0ad0147d90b3961f5c90e7427f9acd4b40050705499c98983f489", size = 12891844, upload-time = "2025-09-04T16:50:02.591Z" }, - { url = "https://files.pythonhosted.org/packages/21/9c/28a8dacce4855e6703dcb8cdf6c1705d0b23dd01d60150786cd55aa93b16/ruff-0.12.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:26a1b5a2bf7dd2c47e3b46d077cd9c0fc3b93e6c6cc9ed750bd312ae9dc302ee", size = 13360687, upload-time = "2025-09-04T16:50:05.8Z" }, - { url = "https://files.pythonhosted.org/packages/c8/fa/05b6428a008e60f79546c943e54068316f32ec8ab5c4f73e4563934fbdc7/ruff-0.12.12-py3-none-win32.whl", hash = "sha256:173be2bfc142af07a01e3a759aba6f7791aa47acf3604f610b1c36db888df7b1", size = 12052870, upload-time = "2025-09-04T16:50:09.121Z" }, - { url = "https://files.pythonhosted.org/packages/85/60/d1e335417804df452589271818749d061b22772b87efda88354cf35cdb7a/ruff-0.12.12-py3-none-win_amd64.whl", hash = "sha256:e99620bf01884e5f38611934c09dd194eb665b0109104acae3ba6102b600fd0d", size = 13178016, upload-time = "2025-09-04T16:50:12.559Z" }, - { url = "https://files.pythonhosted.org/packages/28/7e/61c42657f6e4614a4258f1c3b0c5b93adc4d1f8575f5229d1906b483099b/ruff-0.12.12-py3-none-win_arm64.whl", hash = "sha256:2a8199cab4ce4d72d158319b63370abf60991495fb733db96cd923a34c52d093", size = 12256762, upload-time = "2025-09-04T16:50:15.737Z" }, +version = "0.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/df/8d7d8c515d33adfc540e2edf6c6021ea1c5a58a678d8cfce9fae59aabcab/ruff-0.13.2.tar.gz", hash = "sha256:cb12fffd32fb16d32cef4ed16d8c7cdc27ed7c944eaa98d99d01ab7ab0b710ff", size = 5416417, upload-time = "2025-09-25T14:54:09.936Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/84/5716a7fa4758e41bf70e603e13637c42cfb9dbf7ceb07180211b9bbf75ef/ruff-0.13.2-py3-none-linux_armv6l.whl", hash = "sha256:3796345842b55f033a78285e4f1641078f902020d8450cade03aad01bffd81c3", size = 12343254, upload-time = "2025-09-25T14:53:27.784Z" }, + { url = "https://files.pythonhosted.org/packages/9b/77/c7042582401bb9ac8eff25360e9335e901d7a1c0749a2b28ba4ecb239991/ruff-0.13.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ff7e4dda12e683e9709ac89e2dd436abf31a4d8a8fc3d89656231ed808e231d2", size = 13040891, upload-time = "2025-09-25T14:53:31.38Z" }, + { url = "https://files.pythonhosted.org/packages/c6/15/125a7f76eb295cb34d19c6778e3a82ace33730ad4e6f28d3427e134a02e0/ruff-0.13.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c75e9d2a2fafd1fdd895d0e7e24b44355984affdde1c412a6f6d3f6e16b22d46", size = 12243588, upload-time = "2025-09-25T14:53:33.543Z" }, + { url = "https://files.pythonhosted.org/packages/9e/eb/0093ae04a70f81f8be7fd7ed6456e926b65d238fc122311293d033fdf91e/ruff-0.13.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cceac74e7bbc53ed7d15d1042ffe7b6577bf294611ad90393bf9b2a0f0ec7cb6", size = 12491359, upload-time = "2025-09-25T14:53:35.892Z" }, + { url = "https://files.pythonhosted.org/packages/43/fe/72b525948a6956f07dad4a6f122336b6a05f2e3fd27471cea612349fedb9/ruff-0.13.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6ae3f469b5465ba6d9721383ae9d49310c19b452a161b57507764d7ef15f4b07", size = 12162486, upload-time = "2025-09-25T14:53:38.171Z" }, + { url = "https://files.pythonhosted.org/packages/6a/e3/0fac422bbbfb2ea838023e0d9fcf1f30183d83ab2482800e2cb892d02dfe/ruff-0.13.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f8f9e3cd6714358238cd6626b9d43026ed19c0c018376ac1ef3c3a04ffb42d8", size = 13871203, upload-time = "2025-09-25T14:53:41.943Z" }, + { url = "https://files.pythonhosted.org/packages/6b/82/b721c8e3ec5df6d83ba0e45dcf00892c4f98b325256c42c38ef136496cbf/ruff-0.13.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c6ed79584a8f6cbe2e5d7dbacf7cc1ee29cbdb5df1172e77fbdadc8bb85a1f89", size = 14929635, upload-time = "2025-09-25T14:53:43.953Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a0/ad56faf6daa507b83079a1ad7a11694b87d61e6bf01c66bd82b466f21821/ruff-0.13.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aed130b2fde049cea2019f55deb939103123cdd191105f97a0599a3e753d61b0", size = 14338783, upload-time = "2025-09-25T14:53:46.205Z" }, + { url = "https://files.pythonhosted.org/packages/47/77/ad1d9156db8f99cd01ee7e29d74b34050e8075a8438e589121fcd25c4b08/ruff-0.13.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1887c230c2c9d65ed1b4e4cfe4d255577ea28b718ae226c348ae68df958191aa", size = 13355322, upload-time = "2025-09-25T14:53:48.164Z" }, + { url = "https://files.pythonhosted.org/packages/64/8b/e87cfca2be6f8b9f41f0bb12dc48c6455e2d66df46fe61bb441a226f1089/ruff-0.13.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5bcb10276b69b3cfea3a102ca119ffe5c6ba3901e20e60cf9efb53fa417633c3", size = 13354427, upload-time = "2025-09-25T14:53:50.486Z" }, + { url = "https://files.pythonhosted.org/packages/7f/df/bf382f3fbead082a575edb860897287f42b1b3c694bafa16bc9904c11ed3/ruff-0.13.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:afa721017aa55a555b2ff7944816587f1cb813c2c0a882d158f59b832da1660d", size = 13537637, upload-time = "2025-09-25T14:53:52.887Z" }, + { url = "https://files.pythonhosted.org/packages/51/70/1fb7a7c8a6fc8bd15636288a46e209e81913b87988f26e1913d0851e54f4/ruff-0.13.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1dbc875cf3720c64b3990fef8939334e74cb0ca65b8dbc61d1f439201a38101b", size = 12340025, upload-time = "2025-09-25T14:53:54.88Z" }, + { url = "https://files.pythonhosted.org/packages/4c/27/1e5b3f1c23ca5dd4106d9d580e5c13d9acb70288bff614b3d7b638378cc9/ruff-0.13.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939a1b2a960e9742e9a347e5bbc9b3c3d2c716f86c6ae273d9cbd64f193f22", size = 12133449, upload-time = "2025-09-25T14:53:57.089Z" }, + { url = "https://files.pythonhosted.org/packages/2d/09/b92a5ccee289f11ab128df57d5911224197d8d55ef3bd2043534ff72ca54/ruff-0.13.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:50e2d52acb8de3804fc5f6e2fa3ae9bdc6812410a9e46837e673ad1f90a18736", size = 13051369, upload-time = "2025-09-25T14:53:59.124Z" }, + { url = "https://files.pythonhosted.org/packages/89/99/26c9d1c7d8150f45e346dc045cc49f23e961efceb4a70c47dea0960dea9a/ruff-0.13.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3196bc13ab2110c176b9a4ae5ff7ab676faaa1964b330a1383ba20e1e19645f2", size = 13523644, upload-time = "2025-09-25T14:54:01.622Z" }, + { url = "https://files.pythonhosted.org/packages/f7/00/e7f1501e81e8ec290e79527827af1d88f541d8d26151751b46108978dade/ruff-0.13.2-py3-none-win32.whl", hash = "sha256:7c2a0b7c1e87795fec3404a485096bcd790216c7c146a922d121d8b9c8f1aaac", size = 12245990, upload-time = "2025-09-25T14:54:03.647Z" }, + { url = "https://files.pythonhosted.org/packages/ee/bd/d9f33a73de84fafd0146c6fba4f497c4565fe8fa8b46874b8e438869abc2/ruff-0.13.2-py3-none-win_amd64.whl", hash = "sha256:17d95fb32218357c89355f6f6f9a804133e404fc1f65694372e02a557edf8585", size = 13324004, upload-time = "2025-09-25T14:54:06.05Z" }, + { url = "https://files.pythonhosted.org/packages/c3/12/28fa2f597a605884deb0f65c1b1ae05111051b2a7030f5d8a4ff7f4599ba/ruff-0.13.2-py3-none-win_arm64.whl", hash = "sha256:da711b14c530412c827219312b7d7fbb4877fb31150083add7e8c5336549cea7", size = 12484437, upload-time = "2025-09-25T14:54:08.022Z" }, ] [[package]] name = "sentry-sdk" -version = "2.37.1" +version = "2.39.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/be/ffc232c32d0be18f8e4eff7a22dffc1f1fef2894703d64cc281a80e75da6/sentry_sdk-2.37.1.tar.gz", hash = "sha256:531751da91aa62a909b42a7be155b41f6bb0de9df6ae98441d23b95de2f98475", size = 346235, upload-time = "2025-09-09T13:48:27.137Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/72/43294fa4bdd75c51610b5104a3ff834459ba653abb415150aa7826a249dd/sentry_sdk-2.39.0.tar.gz", hash = "sha256:8c185854d111f47f329ab6bc35993f28f7a6b7114db64aa426b326998cfa14e9", size = 348556, upload-time = "2025-09-25T09:15:39.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/c3/cba447ab531331d165d9003c04473be944a308ad916ca2345b5ef1969ed9/sentry_sdk-2.37.1-py2.py3-none-any.whl", hash = "sha256:baaaea6608ed3a639766a69ded06b254b106d32ad9d180bdbe58f3db9364592b", size = 368307, upload-time = "2025-09-09T13:48:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/dd/44/4356cc64246ba7b2b920f7c97a85c3c52748e213e250b512ee8152eb559d/sentry_sdk-2.39.0-py2.py3-none-any.whl", hash = "sha256:ba655ca5e57b41569b18e2a5552cb3375209760a5d332cdd87c6c3f28f729602", size = 370851, upload-time = "2025-09-25T09:15:36.35Z" }, ] [[package]] @@ -2076,7 +2234,7 @@ wheels = [ [[package]] name = "typer" -version = "0.16.1" +version = "0.17.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -2084,9 +2242,9 @@ dependencies = [ { name = "shellingham" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/78/d90f616bf5f88f8710ad067c1f8705bf7618059836ca084e5bb2a0855d75/typer-0.16.1.tar.gz", hash = "sha256:d358c65a464a7a90f338e3bb7ff0c74ac081449e53884b12ba658cbd72990614", size = 102836, upload-time = "2025-08-18T19:18:22.898Z" } +sdist = { url = "https://files.pythonhosted.org/packages/92/e8/2a73ccf9874ec4c7638f172efc8972ceab13a0e3480b389d6ed822f7a822/typer-0.17.4.tar.gz", hash = "sha256:b77dc07d849312fd2bb5e7f20a7af8985c7ec360c45b051ed5412f64d8dc1580", size = 103734, upload-time = "2025-09-05T18:14:40.746Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/76/06dbe78f39b2203d2a47d5facc5df5102d0561e2807396471b5f7c5a30a1/typer-0.16.1-py3-none-any.whl", hash = "sha256:90ee01cb02d9b8395ae21ee3368421faf21fa138cb2a541ed369c08cec5237c9", size = 46397, upload-time = "2025-08-18T19:18:21.663Z" }, + { url = "https://files.pythonhosted.org/packages/93/72/6b3e70d32e89a5cbb6a4513726c1ae8762165b027af569289e19ec08edd8/typer-0.17.4-py3-none-any.whl", hash = "sha256:015534a6edaa450e7007eba705d5c18c3349dcea50a6ad79a5ed530967575824", size = 46643, upload-time = "2025-09-05T18:14:39.166Z" }, ] [[package]] @@ -2130,15 +2288,15 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.35.0" +version = "0.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } +sdist = { url = "https://files.pythonhosted.org/packages/71/57/1616c8274c3442d802621abf5deb230771c7a0fec9414cb6763900eb3868/uvicorn-0.37.0.tar.gz", hash = "sha256:4115c8add6d3fd536c8ee77f0e14a7fd2ebba939fed9b02583a97f80648f9e13", size = 80367, upload-time = "2025-09-23T13:33:47.486Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, + { url = "https://files.pythonhosted.org/packages/85/cd/584a2ceb5532af99dd09e50919e3615ba99aa127e9850eafe5f31ddfdb9a/uvicorn-0.37.0-py3-none-any.whl", hash = "sha256:913b2b88672343739927ce381ff9e2ad62541f9f8289664fa1d1d3803fa2ce6c", size = 67976, upload-time = "2025-09-23T13:33:45.842Z" }, ] [package.optional-dependencies] @@ -2222,11 +2380,11 @@ wheels = [ [[package]] name = "wcwidth" -version = "0.2.13" +version = "0.2.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload-time = "2024-01-06T02:10:57.829Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605", size = 102293, upload-time = "2025-09-22T16:29:53.023Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" }, + { url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", size = 37286, upload-time = "2025-09-22T16:29:51.641Z" }, ] [[package]] @@ -2258,6 +2416,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0b/2c/87f3254fd8ffd29e4c02732eee68a83a1d3c346ae39bc6822dcbcb697f2b/wheel-0.45.1-py3-none-any.whl", hash = "sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248", size = 72494, upload-time = "2024-11-23T00:18:21.207Z" }, ] +[[package]] +name = "widgetsnbextension" +version = "4.0.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/53/2e0253c5efd69c9656b1843892052a31c36d37ad42812b5da45c62191f7e/widgetsnbextension-4.0.14.tar.gz", hash = "sha256:a3629b04e3edb893212df862038c7232f62973373869db5084aed739b437b5af", size = 1097428, upload-time = "2025-04-10T13:01:25.628Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/51/5447876806d1088a0f8f71e16542bf350918128d0a69437df26047c8e46f/widgetsnbextension-4.0.14-py3-none-any.whl", hash = "sha256:4875a9eaf72fbf5079dc372a51a9f268fc38d46f767cbf85c43a36da5cb9b575", size = 2196503, upload-time = "2025-04-10T13:01:23.086Z" }, +] + [[package]] name = "wrapt" version = "1.17.3" diff --git a/projects/policyengine-api-tagger/uv.lock b/projects/policyengine-api-tagger/uv.lock index d8936f8f..75357678 100644 --- a/projects/policyengine-api-tagger/uv.lock +++ b/projects/policyengine-api-tagger/uv.lock @@ -13,29 +13,29 @@ wheels = [ [[package]] name = "anyio" -version = "4.10.0" +version = "4.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "sniffio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, ] [[package]] name = "asgiref" -version = "3.9.1" +version = "3.9.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/61/0aa957eec22ff70b830b22ff91f825e70e1ef732c06666a805730f28b36b/asgiref-3.9.1.tar.gz", hash = "sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142", size = 36870, upload-time = "2025-07-08T09:07:43.344Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/bf/0f3ecda32f1cb3bf1dca480aca08a7a8a3bdc4bed2343a103f30731565c9/asgiref-3.9.2.tar.gz", hash = "sha256:a0249afacb66688ef258ffe503528360443e2b9a8d8c4581b6ebefa58c841ef1", size = 36894, upload-time = "2025-09-23T15:00:55.136Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/3c/0464dcada90d5da0e71018c04a140ad6349558afb30b3051b4264cc5b965/asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c", size = 23790, upload-time = "2025-07-08T09:07:41.548Z" }, + { url = "https://files.pythonhosted.org/packages/c7/d1/69d02ce34caddb0a7ae088b84c356a625a93cd4ff57b2f97644c03fad905/asgiref-3.9.2-py3-none-any.whl", hash = "sha256:0b61526596219d70396548fc003635056856dba5d0d086f86476f10b33c75960", size = 23788, upload-time = "2025-09-23T15:00:53.627Z" }, ] [[package]] name = "black" -version = "25.1.0" +version = "25.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -43,14 +43,15 @@ dependencies = [ { name = "packaging" }, { name = "pathspec" }, { name = "platformdirs" }, + { name = "pytokens" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload-time = "2025-01-29T04:15:40.373Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/43/20b5c90612d7bdb2bdbcceeb53d588acca3bb8f0e4c5d5c751a2c8fdd55a/black-25.9.0.tar.gz", hash = "sha256:0474bca9a0dd1b51791fcc507a4e02078a1c63f6d4e4ae5544b9848c7adfb619", size = 648393, upload-time = "2025-09-19T00:27:37.758Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673, upload-time = "2025-01-29T05:37:20.574Z" }, - { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190, upload-time = "2025-01-29T05:37:22.106Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926, upload-time = "2025-01-29T04:18:58.564Z" }, - { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613, upload-time = "2025-01-29T04:19:27.63Z" }, - { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" }, + { url = "https://files.pythonhosted.org/packages/48/99/3acfea65f5e79f45472c45f87ec13037b506522719cd9d4ac86484ff51ac/black-25.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0172a012f725b792c358d57fe7b6b6e8e67375dd157f64fa7a3097b3ed3e2175", size = 1742165, upload-time = "2025-09-19T00:34:10.402Z" }, + { url = "https://files.pythonhosted.org/packages/3a/18/799285282c8236a79f25d590f0222dbd6850e14b060dfaa3e720241fd772/black-25.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3bec74ee60f8dfef564b573a96b8930f7b6a538e846123d5ad77ba14a8d7a64f", size = 1581259, upload-time = "2025-09-19T00:32:49.685Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ce/883ec4b6303acdeca93ee06b7622f1fa383c6b3765294824165d49b1a86b/black-25.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b756fc75871cb1bcac5499552d771822fd9db5a2bb8db2a7247936ca48f39831", size = 1655583, upload-time = "2025-09-19T00:30:44.505Z" }, + { url = "https://files.pythonhosted.org/packages/21/17/5c253aa80a0639ccc427a5c7144534b661505ae2b5a10b77ebe13fa25334/black-25.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:846d58e3ce7879ec1ffe816bb9df6d006cd9590515ed5d17db14e17666b2b357", size = 1343428, upload-time = "2025-09-19T00:32:13.839Z" }, + { url = "https://files.pythonhosted.org/packages/1b/46/863c90dcd3f9d41b109b7f19032ae0db021f0b2a81482ba0a1e28c84de86/black-25.9.0-py3-none-any.whl", hash = "sha256:474b34c1342cdc157d307b56c4c65bce916480c4a8f6551fdc6bf9b486a7c4ae", size = 203363, upload-time = "2025-09-19T00:27:35.724Z" }, ] [[package]] @@ -104,14 +105,14 @@ wheels = [ [[package]] name = "click" -version = "8.2.1" +version = "8.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, + { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, ] [[package]] @@ -125,55 +126,63 @@ wheels = [ [[package]] name = "coverage" -version = "7.10.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/14/70/025b179c993f019105b79575ac6edb5e084fb0f0e63f15cdebef4e454fb5/coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90", size = 823736, upload-time = "2025-08-29T15:35:16.668Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/e7/917e5953ea29a28c1057729c1d5af9084ab6d9c66217523fd0e10f14d8f6/coverage-7.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6", size = 217351, upload-time = "2025-08-29T15:33:45.438Z" }, - { url = "https://files.pythonhosted.org/packages/eb/86/2e161b93a4f11d0ea93f9bebb6a53f113d5d6e416d7561ca41bb0a29996b/coverage-7.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80", size = 217600, upload-time = "2025-08-29T15:33:47.269Z" }, - { url = "https://files.pythonhosted.org/packages/0e/66/d03348fdd8df262b3a7fb4ee5727e6e4936e39e2f3a842e803196946f200/coverage-7.10.6-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003", size = 248600, upload-time = "2025-08-29T15:33:48.953Z" }, - { url = "https://files.pythonhosted.org/packages/73/dd/508420fb47d09d904d962f123221bc249f64b5e56aa93d5f5f7603be475f/coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27", size = 251206, upload-time = "2025-08-29T15:33:50.697Z" }, - { url = "https://files.pythonhosted.org/packages/e9/1f/9020135734184f439da85c70ea78194c2730e56c2d18aee6e8ff1719d50d/coverage-7.10.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4", size = 252478, upload-time = "2025-08-29T15:33:52.303Z" }, - { url = "https://files.pythonhosted.org/packages/a4/a4/3d228f3942bb5a2051fde28c136eea23a761177dc4ff4ef54533164ce255/coverage-7.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d", size = 250637, upload-time = "2025-08-29T15:33:53.67Z" }, - { url = "https://files.pythonhosted.org/packages/36/e3/293dce8cdb9a83de971637afc59b7190faad60603b40e32635cbd15fbf61/coverage-7.10.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc", size = 248529, upload-time = "2025-08-29T15:33:55.022Z" }, - { url = "https://files.pythonhosted.org/packages/90/26/64eecfa214e80dd1d101e420cab2901827de0e49631d666543d0e53cf597/coverage-7.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc", size = 250143, upload-time = "2025-08-29T15:33:56.386Z" }, - { url = "https://files.pythonhosted.org/packages/3e/70/bd80588338f65ea5b0d97e424b820fb4068b9cfb9597fbd91963086e004b/coverage-7.10.6-cp313-cp313-win32.whl", hash = "sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e", size = 219770, upload-time = "2025-08-29T15:33:58.063Z" }, - { url = "https://files.pythonhosted.org/packages/a7/14/0b831122305abcc1060c008f6c97bbdc0a913ab47d65070a01dc50293c2b/coverage-7.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32", size = 220566, upload-time = "2025-08-29T15:33:59.766Z" }, - { url = "https://files.pythonhosted.org/packages/83/c6/81a83778c1f83f1a4a168ed6673eeedc205afb562d8500175292ca64b94e/coverage-7.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2", size = 219195, upload-time = "2025-08-29T15:34:01.191Z" }, - { url = "https://files.pythonhosted.org/packages/d7/1c/ccccf4bf116f9517275fa85047495515add43e41dfe8e0bef6e333c6b344/coverage-7.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b", size = 218059, upload-time = "2025-08-29T15:34:02.91Z" }, - { url = "https://files.pythonhosted.org/packages/92/97/8a3ceff833d27c7492af4f39d5da6761e9ff624831db9e9f25b3886ddbca/coverage-7.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393", size = 218287, upload-time = "2025-08-29T15:34:05.106Z" }, - { url = "https://files.pythonhosted.org/packages/92/d8/50b4a32580cf41ff0423777a2791aaf3269ab60c840b62009aec12d3970d/coverage-7.10.6-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27", size = 259625, upload-time = "2025-08-29T15:34:06.575Z" }, - { url = "https://files.pythonhosted.org/packages/7e/7e/6a7df5a6fb440a0179d94a348eb6616ed4745e7df26bf2a02bc4db72c421/coverage-7.10.6-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df", size = 261801, upload-time = "2025-08-29T15:34:08.006Z" }, - { url = "https://files.pythonhosted.org/packages/3a/4c/a270a414f4ed5d196b9d3d67922968e768cd971d1b251e1b4f75e9362f75/coverage-7.10.6-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb", size = 264027, upload-time = "2025-08-29T15:34:09.806Z" }, - { url = "https://files.pythonhosted.org/packages/9c/8b/3210d663d594926c12f373c5370bf1e7c5c3a427519a8afa65b561b9a55c/coverage-7.10.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282", size = 261576, upload-time = "2025-08-29T15:34:11.585Z" }, - { url = "https://files.pythonhosted.org/packages/72/d0/e1961eff67e9e1dba3fc5eb7a4caf726b35a5b03776892da8d79ec895775/coverage-7.10.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4", size = 259341, upload-time = "2025-08-29T15:34:13.159Z" }, - { url = "https://files.pythonhosted.org/packages/3a/06/d6478d152cd189b33eac691cba27a40704990ba95de49771285f34a5861e/coverage-7.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21", size = 260468, upload-time = "2025-08-29T15:34:14.571Z" }, - { url = "https://files.pythonhosted.org/packages/ed/73/737440247c914a332f0b47f7598535b29965bf305e19bbc22d4c39615d2b/coverage-7.10.6-cp313-cp313t-win32.whl", hash = "sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0", size = 220429, upload-time = "2025-08-29T15:34:16.394Z" }, - { url = "https://files.pythonhosted.org/packages/bd/76/b92d3214740f2357ef4a27c75a526eb6c28f79c402e9f20a922c295c05e2/coverage-7.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5", size = 221493, upload-time = "2025-08-29T15:34:17.835Z" }, - { url = "https://files.pythonhosted.org/packages/fc/8e/6dcb29c599c8a1f654ec6cb68d76644fe635513af16e932d2d4ad1e5ac6e/coverage-7.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b", size = 219757, upload-time = "2025-08-29T15:34:19.248Z" }, - { url = "https://files.pythonhosted.org/packages/d3/aa/76cf0b5ec00619ef208da4689281d48b57f2c7fde883d14bf9441b74d59f/coverage-7.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6008a021907be8c4c02f37cdc3ffb258493bdebfeaf9a839f9e71dfdc47b018e", size = 217331, upload-time = "2025-08-29T15:34:20.846Z" }, - { url = "https://files.pythonhosted.org/packages/65/91/8e41b8c7c505d398d7730206f3cbb4a875a35ca1041efc518051bfce0f6b/coverage-7.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5e75e37f23eb144e78940b40395b42f2321951206a4f50e23cfd6e8a198d3ceb", size = 217607, upload-time = "2025-08-29T15:34:22.433Z" }, - { url = "https://files.pythonhosted.org/packages/87/7f/f718e732a423d442e6616580a951b8d1ec3575ea48bcd0e2228386805e79/coverage-7.10.6-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0f7cb359a448e043c576f0da00aa8bfd796a01b06aa610ca453d4dde09cc1034", size = 248663, upload-time = "2025-08-29T15:34:24.425Z" }, - { url = "https://files.pythonhosted.org/packages/e6/52/c1106120e6d801ac03e12b5285e971e758e925b6f82ee9b86db3aa10045d/coverage-7.10.6-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c68018e4fc4e14b5668f1353b41ccf4bc83ba355f0e1b3836861c6f042d89ac1", size = 251197, upload-time = "2025-08-29T15:34:25.906Z" }, - { url = "https://files.pythonhosted.org/packages/3d/ec/3a8645b1bb40e36acde9c0609f08942852a4af91a937fe2c129a38f2d3f5/coverage-7.10.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd4b2b0707fc55afa160cd5fc33b27ccbf75ca11d81f4ec9863d5793fc6df56a", size = 252551, upload-time = "2025-08-29T15:34:27.337Z" }, - { url = "https://files.pythonhosted.org/packages/a1/70/09ecb68eeb1155b28a1d16525fd3a9b65fbe75337311a99830df935d62b6/coverage-7.10.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cec13817a651f8804a86e4f79d815b3b28472c910e099e4d5a0e8a3b6a1d4cb", size = 250553, upload-time = "2025-08-29T15:34:29.065Z" }, - { url = "https://files.pythonhosted.org/packages/c6/80/47df374b893fa812e953b5bc93dcb1427a7b3d7a1a7d2db33043d17f74b9/coverage-7.10.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f2a6a8e06bbda06f78739f40bfb56c45d14eb8249d0f0ea6d4b3d48e1f7c695d", size = 248486, upload-time = "2025-08-29T15:34:30.897Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/9f98640979ecee1b0d1a7164b589de720ddf8100d1747d9bbdb84be0c0fb/coverage-7.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:081b98395ced0d9bcf60ada7661a0b75f36b78b9d7e39ea0790bb4ed8da14747", size = 249981, upload-time = "2025-08-29T15:34:32.365Z" }, - { url = "https://files.pythonhosted.org/packages/1f/55/eeb6603371e6629037f47bd25bef300387257ed53a3c5fdb159b7ac8c651/coverage-7.10.6-cp314-cp314-win32.whl", hash = "sha256:6937347c5d7d069ee776b2bf4e1212f912a9f1f141a429c475e6089462fcecc5", size = 220054, upload-time = "2025-08-29T15:34:34.124Z" }, - { url = "https://files.pythonhosted.org/packages/15/d1/a0912b7611bc35412e919a2cd59ae98e7ea3b475e562668040a43fb27897/coverage-7.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:adec1d980fa07e60b6ef865f9e5410ba760e4e1d26f60f7e5772c73b9a5b0713", size = 220851, upload-time = "2025-08-29T15:34:35.651Z" }, - { url = "https://files.pythonhosted.org/packages/ef/2d/11880bb8ef80a45338e0b3e0725e4c2d73ffbb4822c29d987078224fd6a5/coverage-7.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:a80f7aef9535442bdcf562e5a0d5a5538ce8abe6bb209cfbf170c462ac2c2a32", size = 219429, upload-time = "2025-08-29T15:34:37.16Z" }, - { url = "https://files.pythonhosted.org/packages/83/c0/1f00caad775c03a700146f55536ecd097a881ff08d310a58b353a1421be0/coverage-7.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0de434f4fbbe5af4fa7989521c655c8c779afb61c53ab561b64dcee6149e4c65", size = 218080, upload-time = "2025-08-29T15:34:38.919Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c4/b1c5d2bd7cc412cbeb035e257fd06ed4e3e139ac871d16a07434e145d18d/coverage-7.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6e31b8155150c57e5ac43ccd289d079eb3f825187d7c66e755a055d2c85794c6", size = 218293, upload-time = "2025-08-29T15:34:40.425Z" }, - { url = "https://files.pythonhosted.org/packages/3f/07/4468d37c94724bf6ec354e4ec2f205fda194343e3e85fd2e59cec57e6a54/coverage-7.10.6-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:98cede73eb83c31e2118ae8d379c12e3e42736903a8afcca92a7218e1f2903b0", size = 259800, upload-time = "2025-08-29T15:34:41.996Z" }, - { url = "https://files.pythonhosted.org/packages/82/d8/f8fb351be5fee31690cd8da768fd62f1cfab33c31d9f7baba6cd8960f6b8/coverage-7.10.6-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f863c08f4ff6b64fa8045b1e3da480f5374779ef187f07b82e0538c68cb4ff8e", size = 261965, upload-time = "2025-08-29T15:34:43.61Z" }, - { url = "https://files.pythonhosted.org/packages/e8/70/65d4d7cfc75c5c6eb2fed3ee5cdf420fd8ae09c4808723a89a81d5b1b9c3/coverage-7.10.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b38261034fda87be356f2c3f42221fdb4171c3ce7658066ae449241485390d5", size = 264220, upload-time = "2025-08-29T15:34:45.387Z" }, - { url = "https://files.pythonhosted.org/packages/98/3c/069df106d19024324cde10e4ec379fe2fb978017d25e97ebee23002fbadf/coverage-7.10.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e93b1476b79eae849dc3872faeb0bf7948fd9ea34869590bc16a2a00b9c82a7", size = 261660, upload-time = "2025-08-29T15:34:47.288Z" }, - { url = "https://files.pythonhosted.org/packages/fc/8a/2974d53904080c5dc91af798b3a54a4ccb99a45595cc0dcec6eb9616a57d/coverage-7.10.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ff8a991f70f4c0cf53088abf1e3886edcc87d53004c7bb94e78650b4d3dac3b5", size = 259417, upload-time = "2025-08-29T15:34:48.779Z" }, - { url = "https://files.pythonhosted.org/packages/30/38/9616a6b49c686394b318974d7f6e08f38b8af2270ce7488e879888d1e5db/coverage-7.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ac765b026c9f33044419cbba1da913cfb82cca1b60598ac1c7a5ed6aac4621a0", size = 260567, upload-time = "2025-08-29T15:34:50.718Z" }, - { url = "https://files.pythonhosted.org/packages/76/16/3ed2d6312b371a8cf804abf4e14895b70e4c3491c6e53536d63fd0958a8d/coverage-7.10.6-cp314-cp314t-win32.whl", hash = "sha256:441c357d55f4936875636ef2cfb3bee36e466dcf50df9afbd398ce79dba1ebb7", size = 220831, upload-time = "2025-08-29T15:34:52.653Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e5/d38d0cb830abede2adb8b147770d2a3d0e7fecc7228245b9b1ae6c24930a/coverage-7.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:073711de3181b2e204e4870ac83a7c4853115b42e9cd4d145f2231e12d670930", size = 221950, upload-time = "2025-08-29T15:34:54.212Z" }, - { url = "https://files.pythonhosted.org/packages/f4/51/e48e550f6279349895b0ffcd6d2a690e3131ba3a7f4eafccc141966d4dea/coverage-7.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:137921f2bac5559334ba66122b753db6dc5d1cf01eb7b64eb412bb0d064ef35b", size = 219969, upload-time = "2025-08-29T15:34:55.83Z" }, - { url = "https://files.pythonhosted.org/packages/44/0c/50db5379b615854b5cf89146f8f5bd1d5a9693d7f3a987e269693521c404/coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3", size = 208986, upload-time = "2025-08-29T15:35:14.506Z" }, +version = "7.10.7" +source = { registry = "https://pypi.org/simple" } +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/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/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]] @@ -236,16 +245,16 @@ standard = [ [[package]] name = "fastapi-cli" -version = "0.0.11" +version = "0.0.13" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "rich-toolkit" }, { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/23/08/0af729f6231ebdc17a0356397f966838cbe2efa38529951e24017c7435d5/fastapi_cli-0.0.11.tar.gz", hash = "sha256:4f01d751c14d3d2760339cca0f45e81d816218cae8174d1dc757b5375868cde5", size = 17550, upload-time = "2025-09-09T12:50:38.917Z" } +sdist = { url = "https://files.pythonhosted.org/packages/32/4e/3f61850012473b097fc5297d681bd85788e186fadb8555b67baf4c7707f4/fastapi_cli-0.0.13.tar.gz", hash = "sha256:312addf3f57ba7139457cf0d345c03e2170cc5a034057488259c33cd7e494529", size = 17780, upload-time = "2025-09-20T16:37:31.089Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/8f/9e3ad391d1c4183de55c256b481899bbd7bbd06d389e4986741bb289fe94/fastapi_cli-0.0.11-py3-none-any.whl", hash = "sha256:bcdd1123c6077c7466452b9490ca47821f00eb784d58496674793003f9f8e33a", size = 11095, upload-time = "2025-09-09T12:50:37.658Z" }, + { url = "https://files.pythonhosted.org/packages/08/36/7432750f3638324b055496d2c952000bea824259fca70df5577a6a3c172f/fastapi_cli-0.0.13-py3-none-any.whl", hash = "sha256:219b73ccfde7622559cef1d43197da928516acb4f21f2ec69128c4b90057baba", size = 11142, upload-time = "2025-09-20T16:37:29.695Z" }, ] [package.optional-dependencies] @@ -256,7 +265,7 @@ standard = [ [[package]] name = "fastapi-cloud-cli" -version = "0.1.5" +version = "0.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -267,9 +276,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/2e/3b6e5016affc310e5109bc580f760586eabecea0c8a7ab067611cd849ac0/fastapi_cloud_cli-0.1.5.tar.gz", hash = "sha256:341ee585eb731a6d3c3656cb91ad38e5f39809bf1a16d41de1333e38635a7937", size = 22710, upload-time = "2025-07-28T13:30:48.216Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/38/1971f9dc8141e359d2435e6fae8bb228632adc55cff00cd00efed2a98456/fastapi_cloud_cli-0.2.1.tar.gz", hash = "sha256:aa22a4b867bf53165b6551d2f4eb21b079bad4aa74047cb889acf941e34699d9", size = 23676, upload-time = "2025-09-25T13:53:32.901Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/a6/5aa862489a2918a096166fd98d9fe86b7fd53c607678b3fa9d8c432d88d5/fastapi_cloud_cli-0.1.5-py3-none-any.whl", hash = "sha256:d80525fb9c0e8af122370891f9fa83cf5d496e4ad47a8dd26c0496a6c85a012a", size = 18992, upload-time = "2025-07-28T13:30:47.427Z" }, + { url = "https://files.pythonhosted.org/packages/3b/1f/5fa06afce6e4bb7fc7e54651236bad3b849340480967c54cbd7c13563c3f/fastapi_cloud_cli-0.2.1-py3-none-any.whl", hash = "sha256:245447bfb17b01ae5f7bc15dec0833bce85381ecf34532e8fa4bcf279ad1c361", size = 19894, upload-time = "2025-09-25T13:53:31.635Z" }, ] [[package]] @@ -354,7 +363,7 @@ wheels = [ [[package]] name = "google-cloud-storage" -version = "3.3.1" +version = "3.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core" }, @@ -364,9 +373,9 @@ dependencies = [ { name = "google-resumable-media" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ce/0d/6be1c7e10d1e186e22990fdc22e7ece79f7c622370793cfe88aa8c658316/google_cloud_storage-3.3.1.tar.gz", hash = "sha256:60f291b0881e5c72919b156d1ee276d1b69a2538fcdc35f4e87559ae11678f77", size = 17224623, upload-time = "2025-09-01T05:59:02.804Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/a6/6e0a318f70975a3c048c0e1a18aee4f7b6d7dac1e798fdc5353c5248d418/google_cloud_storage-3.4.0.tar.gz", hash = "sha256:4c77ec00c98ccc6428e4c39404926f41e2152f48809b02af29d5116645c3c317", size = 17226847, upload-time = "2025-09-15T10:40:05.045Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/67/68eee082fc77e718fa483893ac2463fe0ae8f28ccab334cea9dc5aba99b0/google_cloud_storage-3.3.1-py3-none-any.whl", hash = "sha256:8cace9359b85f315f21868cf771143d6dbb47dcc5a3a9317c8207accc4d10fd3", size = 275070, upload-time = "2025-09-01T05:59:00.633Z" }, + { url = "https://files.pythonhosted.org/packages/16/12/164a90e4692423ed5532274928b0e19c8cae345ae1aa413d78c6b688231b/google_cloud_storage-3.4.0-py3-none-any.whl", hash = "sha256:16eeca305e4747a6871f8f7627eef3b862fdd365b872ca74d4a89e9841d0f8e8", size = 278423, upload-time = "2025-09-15T10:40:03.349Z" }, ] [[package]] @@ -468,34 +477,37 @@ wheels = [ [[package]] name = "grpcio" -version = "1.74.0" +version = "1.75.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/38/b4/35feb8f7cab7239c5b94bd2db71abb3d6adb5f335ad8f131abb6060840b6/grpcio-1.74.0.tar.gz", hash = "sha256:80d1f4fbb35b0742d3e3d3bb654b7381cd5f015f8497279a1e9c21ba623e01b1", size = 12756048, upload-time = "2025-07-24T18:54:23.039Z" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/88/fe2844eefd3d2188bc0d7a2768c6375b46dfd96469ea52d8aeee8587d7e0/grpcio-1.75.0.tar.gz", hash = "sha256:b989e8b09489478c2d19fecc744a298930f40d8b27c3638afbfe84d22f36ce4e", size = 12722485, upload-time = "2025-09-16T09:20:21.731Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/d8/1004a5f468715221450e66b051c839c2ce9a985aa3ee427422061fcbb6aa/grpcio-1.74.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:2bc2d7d8d184e2362b53905cb1708c84cb16354771c04b490485fa07ce3a1d89", size = 5449488, upload-time = "2025-07-24T18:53:41.174Z" }, - { url = "https://files.pythonhosted.org/packages/94/0e/33731a03f63740d7743dced423846c831d8e6da808fcd02821a4416df7fa/grpcio-1.74.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:c14e803037e572c177ba54a3e090d6eb12efd795d49327c5ee2b3bddb836bf01", size = 10974059, upload-time = "2025-07-24T18:53:43.066Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c6/3d2c14d87771a421205bdca991467cfe473ee4c6a1231c1ede5248c62ab8/grpcio-1.74.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f6ec94f0e50eb8fa1744a731088b966427575e40c2944a980049798b127a687e", size = 5945647, upload-time = "2025-07-24T18:53:45.269Z" }, - { url = "https://files.pythonhosted.org/packages/c5/83/5a354c8aaff58594eef7fffebae41a0f8995a6258bbc6809b800c33d4c13/grpcio-1.74.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:566b9395b90cc3d0d0c6404bc8572c7c18786ede549cdb540ae27b58afe0fb91", size = 6626101, upload-time = "2025-07-24T18:53:47.015Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ca/4fdc7bf59bf6994aa45cbd4ef1055cd65e2884de6113dbd49f75498ddb08/grpcio-1.74.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1ea6176d7dfd5b941ea01c2ec34de9531ba494d541fe2057c904e601879f249", size = 6182562, upload-time = "2025-07-24T18:53:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/fd/48/2869e5b2c1922583686f7ae674937986807c2f676d08be70d0a541316270/grpcio-1.74.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:64229c1e9cea079420527fa8ac45d80fc1e8d3f94deaa35643c381fa8d98f362", size = 6303425, upload-time = "2025-07-24T18:53:50.847Z" }, - { url = "https://files.pythonhosted.org/packages/a6/0e/bac93147b9a164f759497bc6913e74af1cb632c733c7af62c0336782bd38/grpcio-1.74.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:0f87bddd6e27fc776aacf7ebfec367b6d49cad0455123951e4488ea99d9b9b8f", size = 6996533, upload-time = "2025-07-24T18:53:52.747Z" }, - { url = "https://files.pythonhosted.org/packages/84/35/9f6b2503c1fd86d068b46818bbd7329db26a87cdd8c01e0d1a9abea1104c/grpcio-1.74.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3b03d8f2a07f0fea8c8f74deb59f8352b770e3900d143b3d1475effcb08eec20", size = 6491489, upload-time = "2025-07-24T18:53:55.06Z" }, - { url = "https://files.pythonhosted.org/packages/75/33/a04e99be2a82c4cbc4039eb3a76f6c3632932b9d5d295221389d10ac9ca7/grpcio-1.74.0-cp313-cp313-win32.whl", hash = "sha256:b6a73b2ba83e663b2480a90b82fdae6a7aa6427f62bf43b29912c0cfd1aa2bfa", size = 3805811, upload-time = "2025-07-24T18:53:56.798Z" }, - { url = "https://files.pythonhosted.org/packages/34/80/de3eb55eb581815342d097214bed4c59e806b05f1b3110df03b2280d6dfd/grpcio-1.74.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd3c71aeee838299c5887230b8a1822795325ddfea635edd82954c1eaa831e24", size = 4489214, upload-time = "2025-07-24T18:53:59.771Z" }, + { url = "https://files.pythonhosted.org/packages/00/64/dbce0ffb6edaca2b292d90999dd32a3bd6bc24b5b77618ca28440525634d/grpcio-1.75.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:1bb78d052948d8272c820bb928753f16a614bb2c42fbf56ad56636991b427518", size = 5666860, upload-time = "2025-09-16T09:19:25.417Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e6/da02c8fa882ad3a7f868d380bb3da2c24d35dd983dd12afdc6975907a352/grpcio-1.75.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:9dc4a02796394dd04de0b9673cb79a78901b90bb16bf99ed8cb528c61ed9372e", size = 11455148, upload-time = "2025-09-16T09:19:28.615Z" }, + { url = "https://files.pythonhosted.org/packages/ba/a0/84f87f6c2cf2a533cfce43b2b620eb53a51428ec0c8fe63e5dd21d167a70/grpcio-1.75.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:437eeb16091d31498585d73b133b825dc80a8db43311e332c08facf820d36894", size = 6243865, upload-time = "2025-09-16T09:19:31.342Z" }, + { url = "https://files.pythonhosted.org/packages/be/12/53da07aa701a4839dd70d16e61ce21ecfcc9e929058acb2f56e9b2dd8165/grpcio-1.75.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:c2c39984e846bd5da45c5f7bcea8fafbe47c98e1ff2b6f40e57921b0c23a52d0", size = 6915102, upload-time = "2025-09-16T09:19:33.658Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c0/7eaceafd31f52ec4bf128bbcf36993b4bc71f64480f3687992ddd1a6e315/grpcio-1.75.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38d665f44b980acdbb2f0e1abf67605ba1899f4d2443908df9ec8a6f26d2ed88", size = 6432042, upload-time = "2025-09-16T09:19:36.583Z" }, + { url = "https://files.pythonhosted.org/packages/6b/12/a2ce89a9f4fc52a16ed92951f1b05f53c17c4028b3db6a4db7f08332bee8/grpcio-1.75.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e8e752ab5cc0a9c5b949808c000ca7586223be4f877b729f034b912364c3964", size = 7062984, upload-time = "2025-09-16T09:19:39.163Z" }, + { url = "https://files.pythonhosted.org/packages/55/a6/2642a9b491e24482d5685c0f45c658c495a5499b43394846677abed2c966/grpcio-1.75.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3a6788b30aa8e6f207c417874effe3f79c2aa154e91e78e477c4825e8b431ce0", size = 8001212, upload-time = "2025-09-16T09:19:41.726Z" }, + { url = "https://files.pythonhosted.org/packages/19/20/530d4428750e9ed6ad4254f652b869a20a40a276c1f6817b8c12d561f5ef/grpcio-1.75.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc33e67cab6141c54e75d85acd5dec616c5095a957ff997b4330a6395aa9b51", size = 7457207, upload-time = "2025-09-16T09:19:44.368Z" }, + { url = "https://files.pythonhosted.org/packages/e2/6f/843670007e0790af332a21468d10059ea9fdf97557485ae633b88bd70efc/grpcio-1.75.0-cp313-cp313-win32.whl", hash = "sha256:c8cfc780b7a15e06253aae5f228e1e84c0d3c4daa90faf5bc26b751174da4bf9", size = 3934235, upload-time = "2025-09-16T09:19:46.815Z" }, + { url = "https://files.pythonhosted.org/packages/4b/92/c846b01b38fdf9e2646a682b12e30a70dc7c87dfe68bd5e009ee1501c14b/grpcio-1.75.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c91d5b16eff3cbbe76b7a1eaaf3d91e7a954501e9d4f915554f87c470475c3d", size = 4637558, upload-time = "2025-09-16T09:19:49.698Z" }, ] [[package]] name = "grpcio-status" -version = "1.74.0" +version = "1.75.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, { name = "grpcio" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/93/22/238c5f01e6837df54494deb08d5c772bc3f5bf5fb80a15dce254892d1a81/grpcio_status-1.74.0.tar.gz", hash = "sha256:c58c1b24aa454e30f1fc6a7e0dbbc194c54a408143971a94b5f4e40bb5831432", size = 13662, upload-time = "2025-07-24T19:01:56.874Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/8a/2e45ec0512d4ce9afa136c6e4186d063721b5b4c192eec7536ce6b7ba615/grpcio_status-1.75.0.tar.gz", hash = "sha256:69d5b91be1b8b926f086c1c483519a968c14640773a0ccdd6c04282515dbedf7", size = 13646, upload-time = "2025-09-16T09:24:51.069Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/aa/1b1fe7d8ab699e1ec26d3a36b91d3df9f83a30abc07d4c881d0296b17b67/grpcio_status-1.74.0-py3-none-any.whl", hash = "sha256:52cdbd759a6760fc8f668098a03f208f493dd5c76bf8e02598bbbaf1f6fc2876", size = 14425, upload-time = "2025-07-24T19:01:19.963Z" }, + { url = "https://files.pythonhosted.org/packages/2b/24/d536f0a0fda3a3eeb334893e5fb9d567c2777de6a5384413f71b35cfd0e5/grpcio_status-1.75.0-py3-none-any.whl", hash = "sha256:de62557ef97b7e19c3ce6da19793a12c5f6c1fbbb918d233d9671aba9d9e1d78", size = 14424, upload-time = "2025-09-16T09:23:33.843Z" }, ] [[package]] @@ -954,16 +966,16 @@ wheels = [ [[package]] name = "protobuf" -version = "6.32.0" +version = "6.32.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c0/df/fb4a8eeea482eca989b51cffd274aac2ee24e825f0bf3cbce5281fa1567b/protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2", size = 440614, upload-time = "2025-08-14T21:21:25.015Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/a4/cc17347aa2897568beece2e674674359f911d6fe21b0b8d6268cd42727ac/protobuf-6.32.1.tar.gz", hash = "sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d", size = 440635, upload-time = "2025-09-11T21:38:42.935Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/18/df8c87da2e47f4f1dcc5153a81cd6bca4e429803f4069a299e236e4dd510/protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741", size = 424409, upload-time = "2025-08-14T21:21:12.366Z" }, - { url = "https://files.pythonhosted.org/packages/e1/59/0a820b7310f8139bd8d5a9388e6a38e1786d179d6f33998448609296c229/protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e", size = 435735, upload-time = "2025-08-14T21:21:15.046Z" }, - { url = "https://files.pythonhosted.org/packages/cc/5b/0d421533c59c789e9c9894683efac582c06246bf24bb26b753b149bd88e4/protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0", size = 426449, upload-time = "2025-08-14T21:21:16.687Z" }, - { url = "https://files.pythonhosted.org/packages/ec/7b/607764ebe6c7a23dcee06e054fd1de3d5841b7648a90fd6def9a3bb58c5e/protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1", size = 322869, upload-time = "2025-08-14T21:21:18.282Z" }, - { url = "https://files.pythonhosted.org/packages/40/01/2e730bd1c25392fc32e3268e02446f0d77cb51a2c3a8486b1798e34d5805/protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c", size = 322009, upload-time = "2025-08-14T21:21:19.893Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f2/80ffc4677aac1bc3519b26bc7f7f5de7fce0ee2f7e36e59e27d8beb32dd1/protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783", size = 169287, upload-time = "2025-08-14T21:21:23.515Z" }, + { url = "https://files.pythonhosted.org/packages/c0/98/645183ea03ab3995d29086b8bf4f7562ebd3d10c9a4b14ee3f20d47cfe50/protobuf-6.32.1-cp310-abi3-win32.whl", hash = "sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085", size = 424411, upload-time = "2025-09-11T21:38:27.427Z" }, + { url = "https://files.pythonhosted.org/packages/8c/f3/6f58f841f6ebafe076cebeae33fc336e900619d34b1c93e4b5c97a81fdfa/protobuf-6.32.1-cp310-abi3-win_amd64.whl", hash = "sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1", size = 435738, upload-time = "2025-09-11T21:38:30.959Z" }, + { url = "https://files.pythonhosted.org/packages/10/56/a8a3f4e7190837139e68c7002ec749190a163af3e330f65d90309145a210/protobuf-6.32.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281", size = 426454, upload-time = "2025-09-11T21:38:34.076Z" }, + { url = "https://files.pythonhosted.org/packages/3f/be/8dd0a927c559b37d7a6c8ab79034fd167dcc1f851595f2e641ad62be8643/protobuf-6.32.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4", size = 322874, upload-time = "2025-09-11T21:38:35.509Z" }, + { url = "https://files.pythonhosted.org/packages/5c/f6/88d77011b605ef979aace37b7703e4eefad066f7e84d935e5a696515c2dd/protobuf-6.32.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710", size = 322013, upload-time = "2025-09-11T21:38:37.017Z" }, + { url = "https://files.pythonhosted.org/packages/97/b7/15cc7d93443d6c6a84626ae3258a91f4c6ac8c0edd5df35ea7658f71b79c/protobuf-6.32.1-py3-none-any.whl", hash = "sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346", size = 169289, upload-time = "2025-09-11T21:38:41.234Z" }, ] [[package]] @@ -989,7 +1001,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.11.7" +version = "2.11.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -997,9 +1009,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/5d/09a551ba512d7ca404d785072700d3f6727a02f6f3c24ecfd081c7cf0aa8/pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2", size = 788495, upload-time = "2025-09-13T11:26:39.325Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d3/108f2006987c58e76691d5ae5d200dd3e0f532cb4e5fa3560751c3a1feba/pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2", size = 444855, upload-time = "2025-09-13T11:26:36.909Z" }, ] [package.optional-dependencies] @@ -1037,16 +1049,16 @@ wheels = [ [[package]] name = "pydantic-settings" -version = "2.10.1" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583, upload-time = "2025-06-24T13:26:46.841Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/c5/dbbc27b814c71676593d1c3f718e6cd7d4f00652cefa24b75f7aa3efb25e/pydantic_settings-2.11.0.tar.gz", hash = "sha256:d0e87a1c7d33593beb7194adb8470fc426e95ba02af83a0f23474a04c9a08180", size = 188394, upload-time = "2025-09-24T14:19:11.764Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235, upload-time = "2025-06-24T13:26:45.485Z" }, + { url = "https://files.pythonhosted.org/packages/83/d6/887a1ff844e64aa823fb4905978d882a633cfe295c32eacad582b78a7d8b/pydantic_settings-2.11.0-py3-none-any.whl", hash = "sha256:fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c", size = 48608, upload-time = "2025-09-24T14:19:10.015Z" }, ] [[package]] @@ -1098,14 +1110,14 @@ wheels = [ [[package]] name = "pytest-asyncio" -version = "1.1.0" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -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" } +sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157, upload-time = "2025-07-16T04:29:24.929Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" }, ] [[package]] @@ -1150,20 +1162,48 @@ wheels = [ ] [[package]] -name = "pyyaml" -version = "6.0.2" +name = "pytokens" +version = "0.1.10" 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" } +sdist = { url = "https://files.pythonhosted.org/packages/30/5f/e959a442435e24f6fb5a01aec6c657079ceaca1b3baf18561c3728d681da/pytokens-0.1.10.tar.gz", hash = "sha256:c9a4bfa0be1d26aebce03e6884ba454e842f186a59ea43a6d3b25af58223c044", size = 12171, upload-time = "2025-02-19T14:51:22.001Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, - { 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/60/e5/63bed382f6a7a5ba70e7e132b8b7b8abbcf4888ffa6be4877698dcfbed7d/pytokens-0.1.10-py3-none-any.whl", hash = "sha256:db7b72284e480e69fb085d9f251f66b3d2df8b7166059261258ff35f50fb711b", size = 12046, upload-time = "2025-02-19T14:51:18.694Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] [[package]] @@ -1254,15 +1294,15 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.37.1" +version = "2.39.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/be/ffc232c32d0be18f8e4eff7a22dffc1f1fef2894703d64cc281a80e75da6/sentry_sdk-2.37.1.tar.gz", hash = "sha256:531751da91aa62a909b42a7be155b41f6bb0de9df6ae98441d23b95de2f98475", size = 346235, upload-time = "2025-09-09T13:48:27.137Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/72/43294fa4bdd75c51610b5104a3ff834459ba653abb415150aa7826a249dd/sentry_sdk-2.39.0.tar.gz", hash = "sha256:8c185854d111f47f329ab6bc35993f28f7a6b7114db64aa426b326998cfa14e9", size = 348556, upload-time = "2025-09-25T09:15:39.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/c3/cba447ab531331d165d9003c04473be944a308ad916ca2345b5ef1969ed9/sentry_sdk-2.37.1-py2.py3-none-any.whl", hash = "sha256:baaaea6608ed3a639766a69ded06b254b106d32ad9d180bdbe58f3db9364592b", size = 368307, upload-time = "2025-09-09T13:48:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/dd/44/4356cc64246ba7b2b920f7c97a85c3c52748e213e250b512ee8152eb559d/sentry_sdk-2.39.0-py2.py3-none-any.whl", hash = "sha256:ba655ca5e57b41569b18e2a5552cb3375209760a5d332cdd87c6c3f28f729602", size = 370851, upload-time = "2025-09-25T09:15:36.35Z" }, ] [[package]] @@ -1331,7 +1371,7 @@ wheels = [ [[package]] name = "typer" -version = "0.17.4" +version = "0.19.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -1339,9 +1379,9 @@ dependencies = [ { name = "shellingham" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/92/e8/2a73ccf9874ec4c7638f172efc8972ceab13a0e3480b389d6ed822f7a822/typer-0.17.4.tar.gz", hash = "sha256:b77dc07d849312fd2bb5e7f20a7af8985c7ec360c45b051ed5412f64d8dc1580", size = 103734, upload-time = "2025-09-05T18:14:40.746Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/ca/950278884e2ca20547ff3eb109478c6baf6b8cf219318e6bc4f666fad8e8/typer-0.19.2.tar.gz", hash = "sha256:9ad824308ded0ad06cc716434705f691d4ee0bfd0fb081839d2e426860e7fdca", size = 104755, upload-time = "2025-09-23T09:47:48.256Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/72/6b3e70d32e89a5cbb6a4513726c1ae8762165b027af569289e19ec08edd8/typer-0.17.4-py3-none-any.whl", hash = "sha256:015534a6edaa450e7007eba705d5c18c3349dcea50a6ad79a5ed530967575824", size = 46643, upload-time = "2025-09-05T18:14:39.166Z" }, + { url = "https://files.pythonhosted.org/packages/00/22/35617eee79080a5d071d0f14ad698d325ee6b3bf824fc0467c03b30e7fa8/typer-0.19.2-py3-none-any.whl", hash = "sha256:755e7e19670ffad8283db353267cb81ef252f595aa6834a0d1ca9312d9326cb9", size = 46748, upload-time = "2025-09-23T09:47:46.777Z" }, ] [[package]] @@ -1376,15 +1416,15 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.35.0" +version = "0.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } +sdist = { url = "https://files.pythonhosted.org/packages/71/57/1616c8274c3442d802621abf5deb230771c7a0fec9414cb6763900eb3868/uvicorn-0.37.0.tar.gz", hash = "sha256:4115c8add6d3fd536c8ee77f0e14a7fd2ebba939fed9b02583a97f80648f9e13", size = 80367, upload-time = "2025-09-23T13:33:47.486Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, + { url = "https://files.pythonhosted.org/packages/85/cd/584a2ceb5532af99dd09e50919e3615ba99aa127e9850eafe5f31ddfdb9a/uvicorn-0.37.0-py3-none-any.whl", hash = "sha256:913b2b88672343739927ce381ff9e2ad62541f9f8289664fa1d1d3803fa2ce6c", size = 67976, upload-time = "2025-09-23T13:33:45.842Z" }, ] [package.optional-dependencies] diff --git a/projects/policyengine-apis-integ/uv.lock b/projects/policyengine-apis-integ/uv.lock index 3d688d6d..85f27631 100644 --- a/projects/policyengine-apis-integ/uv.lock +++ b/projects/policyengine-apis-integ/uv.lock @@ -13,15 +13,15 @@ wheels = [ [[package]] name = "anyio" -version = "4.10.0" +version = "4.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "sniffio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, ] [[package]] @@ -44,7 +44,7 @@ wheels = [ [[package]] name = "black" -version = "25.1.0" +version = "25.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -52,14 +52,15 @@ dependencies = [ { name = "packaging" }, { name = "pathspec" }, { name = "platformdirs" }, + { name = "pytokens" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload-time = "2025-01-29T04:15:40.373Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/43/20b5c90612d7bdb2bdbcceeb53d588acca3bb8f0e4c5d5c751a2c8fdd55a/black-25.9.0.tar.gz", hash = "sha256:0474bca9a0dd1b51791fcc507a4e02078a1c63f6d4e4ae5544b9848c7adfb619", size = 648393, upload-time = "2025-09-19T00:27:37.758Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673, upload-time = "2025-01-29T05:37:20.574Z" }, - { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190, upload-time = "2025-01-29T05:37:22.106Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926, upload-time = "2025-01-29T04:18:58.564Z" }, - { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613, upload-time = "2025-01-29T04:19:27.63Z" }, - { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" }, + { url = "https://files.pythonhosted.org/packages/48/99/3acfea65f5e79f45472c45f87ec13037b506522719cd9d4ac86484ff51ac/black-25.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0172a012f725b792c358d57fe7b6b6e8e67375dd157f64fa7a3097b3ed3e2175", size = 1742165, upload-time = "2025-09-19T00:34:10.402Z" }, + { url = "https://files.pythonhosted.org/packages/3a/18/799285282c8236a79f25d590f0222dbd6850e14b060dfaa3e720241fd772/black-25.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3bec74ee60f8dfef564b573a96b8930f7b6a538e846123d5ad77ba14a8d7a64f", size = 1581259, upload-time = "2025-09-19T00:32:49.685Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ce/883ec4b6303acdeca93ee06b7622f1fa383c6b3765294824165d49b1a86b/black-25.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b756fc75871cb1bcac5499552d771822fd9db5a2bb8db2a7247936ca48f39831", size = 1655583, upload-time = "2025-09-19T00:30:44.505Z" }, + { url = "https://files.pythonhosted.org/packages/21/17/5c253aa80a0639ccc427a5c7144534b661505ae2b5a10b77ebe13fa25334/black-25.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:846d58e3ce7879ec1ffe816bb9df6d006cd9590515ed5d17db14e17666b2b357", size = 1343428, upload-time = "2025-09-19T00:32:13.839Z" }, + { url = "https://files.pythonhosted.org/packages/1b/46/863c90dcd3f9d41b109b7f19032ae0db021f0b2a81482ba0a1e28c84de86/black-25.9.0-py3-none-any.whl", hash = "sha256:474b34c1342cdc157d307b56c4c65bce916480c4a8f6551fdc6bf9b486a7c4ae", size = 203363, upload-time = "2025-09-19T00:27:35.724Z" }, ] [[package]] @@ -113,14 +114,14 @@ wheels = [ [[package]] name = "click" -version = "8.2.1" +version = "8.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, + { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, ] [[package]] @@ -206,34 +207,37 @@ wheels = [ [[package]] name = "grpcio" -version = "1.74.0" +version = "1.75.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/38/b4/35feb8f7cab7239c5b94bd2db71abb3d6adb5f335ad8f131abb6060840b6/grpcio-1.74.0.tar.gz", hash = "sha256:80d1f4fbb35b0742d3e3d3bb654b7381cd5f015f8497279a1e9c21ba623e01b1", size = 12756048, upload-time = "2025-07-24T18:54:23.039Z" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/88/fe2844eefd3d2188bc0d7a2768c6375b46dfd96469ea52d8aeee8587d7e0/grpcio-1.75.0.tar.gz", hash = "sha256:b989e8b09489478c2d19fecc744a298930f40d8b27c3638afbfe84d22f36ce4e", size = 12722485, upload-time = "2025-09-16T09:20:21.731Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/d8/1004a5f468715221450e66b051c839c2ce9a985aa3ee427422061fcbb6aa/grpcio-1.74.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:2bc2d7d8d184e2362b53905cb1708c84cb16354771c04b490485fa07ce3a1d89", size = 5449488, upload-time = "2025-07-24T18:53:41.174Z" }, - { url = "https://files.pythonhosted.org/packages/94/0e/33731a03f63740d7743dced423846c831d8e6da808fcd02821a4416df7fa/grpcio-1.74.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:c14e803037e572c177ba54a3e090d6eb12efd795d49327c5ee2b3bddb836bf01", size = 10974059, upload-time = "2025-07-24T18:53:43.066Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c6/3d2c14d87771a421205bdca991467cfe473ee4c6a1231c1ede5248c62ab8/grpcio-1.74.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f6ec94f0e50eb8fa1744a731088b966427575e40c2944a980049798b127a687e", size = 5945647, upload-time = "2025-07-24T18:53:45.269Z" }, - { url = "https://files.pythonhosted.org/packages/c5/83/5a354c8aaff58594eef7fffebae41a0f8995a6258bbc6809b800c33d4c13/grpcio-1.74.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:566b9395b90cc3d0d0c6404bc8572c7c18786ede549cdb540ae27b58afe0fb91", size = 6626101, upload-time = "2025-07-24T18:53:47.015Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ca/4fdc7bf59bf6994aa45cbd4ef1055cd65e2884de6113dbd49f75498ddb08/grpcio-1.74.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1ea6176d7dfd5b941ea01c2ec34de9531ba494d541fe2057c904e601879f249", size = 6182562, upload-time = "2025-07-24T18:53:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/fd/48/2869e5b2c1922583686f7ae674937986807c2f676d08be70d0a541316270/grpcio-1.74.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:64229c1e9cea079420527fa8ac45d80fc1e8d3f94deaa35643c381fa8d98f362", size = 6303425, upload-time = "2025-07-24T18:53:50.847Z" }, - { url = "https://files.pythonhosted.org/packages/a6/0e/bac93147b9a164f759497bc6913e74af1cb632c733c7af62c0336782bd38/grpcio-1.74.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:0f87bddd6e27fc776aacf7ebfec367b6d49cad0455123951e4488ea99d9b9b8f", size = 6996533, upload-time = "2025-07-24T18:53:52.747Z" }, - { url = "https://files.pythonhosted.org/packages/84/35/9f6b2503c1fd86d068b46818bbd7329db26a87cdd8c01e0d1a9abea1104c/grpcio-1.74.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3b03d8f2a07f0fea8c8f74deb59f8352b770e3900d143b3d1475effcb08eec20", size = 6491489, upload-time = "2025-07-24T18:53:55.06Z" }, - { url = "https://files.pythonhosted.org/packages/75/33/a04e99be2a82c4cbc4039eb3a76f6c3632932b9d5d295221389d10ac9ca7/grpcio-1.74.0-cp313-cp313-win32.whl", hash = "sha256:b6a73b2ba83e663b2480a90b82fdae6a7aa6427f62bf43b29912c0cfd1aa2bfa", size = 3805811, upload-time = "2025-07-24T18:53:56.798Z" }, - { url = "https://files.pythonhosted.org/packages/34/80/de3eb55eb581815342d097214bed4c59e806b05f1b3110df03b2280d6dfd/grpcio-1.74.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd3c71aeee838299c5887230b8a1822795325ddfea635edd82954c1eaa831e24", size = 4489214, upload-time = "2025-07-24T18:53:59.771Z" }, + { url = "https://files.pythonhosted.org/packages/00/64/dbce0ffb6edaca2b292d90999dd32a3bd6bc24b5b77618ca28440525634d/grpcio-1.75.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:1bb78d052948d8272c820bb928753f16a614bb2c42fbf56ad56636991b427518", size = 5666860, upload-time = "2025-09-16T09:19:25.417Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e6/da02c8fa882ad3a7f868d380bb3da2c24d35dd983dd12afdc6975907a352/grpcio-1.75.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:9dc4a02796394dd04de0b9673cb79a78901b90bb16bf99ed8cb528c61ed9372e", size = 11455148, upload-time = "2025-09-16T09:19:28.615Z" }, + { url = "https://files.pythonhosted.org/packages/ba/a0/84f87f6c2cf2a533cfce43b2b620eb53a51428ec0c8fe63e5dd21d167a70/grpcio-1.75.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:437eeb16091d31498585d73b133b825dc80a8db43311e332c08facf820d36894", size = 6243865, upload-time = "2025-09-16T09:19:31.342Z" }, + { url = "https://files.pythonhosted.org/packages/be/12/53da07aa701a4839dd70d16e61ce21ecfcc9e929058acb2f56e9b2dd8165/grpcio-1.75.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:c2c39984e846bd5da45c5f7bcea8fafbe47c98e1ff2b6f40e57921b0c23a52d0", size = 6915102, upload-time = "2025-09-16T09:19:33.658Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c0/7eaceafd31f52ec4bf128bbcf36993b4bc71f64480f3687992ddd1a6e315/grpcio-1.75.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38d665f44b980acdbb2f0e1abf67605ba1899f4d2443908df9ec8a6f26d2ed88", size = 6432042, upload-time = "2025-09-16T09:19:36.583Z" }, + { url = "https://files.pythonhosted.org/packages/6b/12/a2ce89a9f4fc52a16ed92951f1b05f53c17c4028b3db6a4db7f08332bee8/grpcio-1.75.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e8e752ab5cc0a9c5b949808c000ca7586223be4f877b729f034b912364c3964", size = 7062984, upload-time = "2025-09-16T09:19:39.163Z" }, + { url = "https://files.pythonhosted.org/packages/55/a6/2642a9b491e24482d5685c0f45c658c495a5499b43394846677abed2c966/grpcio-1.75.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3a6788b30aa8e6f207c417874effe3f79c2aa154e91e78e477c4825e8b431ce0", size = 8001212, upload-time = "2025-09-16T09:19:41.726Z" }, + { url = "https://files.pythonhosted.org/packages/19/20/530d4428750e9ed6ad4254f652b869a20a40a276c1f6817b8c12d561f5ef/grpcio-1.75.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc33e67cab6141c54e75d85acd5dec616c5095a957ff997b4330a6395aa9b51", size = 7457207, upload-time = "2025-09-16T09:19:44.368Z" }, + { url = "https://files.pythonhosted.org/packages/e2/6f/843670007e0790af332a21468d10059ea9fdf97557485ae633b88bd70efc/grpcio-1.75.0-cp313-cp313-win32.whl", hash = "sha256:c8cfc780b7a15e06253aae5f228e1e84c0d3c4daa90faf5bc26b751174da4bf9", size = 3934235, upload-time = "2025-09-16T09:19:46.815Z" }, + { url = "https://files.pythonhosted.org/packages/4b/92/c846b01b38fdf9e2646a682b12e30a70dc7c87dfe68bd5e009ee1501c14b/grpcio-1.75.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c91d5b16eff3cbbe76b7a1eaaf3d91e7a954501e9d4f915554f87c470475c3d", size = 4637558, upload-time = "2025-09-16T09:19:49.698Z" }, ] [[package]] name = "grpcio-status" -version = "1.74.0" +version = "1.75.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, { name = "grpcio" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/93/22/238c5f01e6837df54494deb08d5c772bc3f5bf5fb80a15dce254892d1a81/grpcio_status-1.74.0.tar.gz", hash = "sha256:c58c1b24aa454e30f1fc6a7e0dbbc194c54a408143971a94b5f4e40bb5831432", size = 13662, upload-time = "2025-07-24T19:01:56.874Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/8a/2e45ec0512d4ce9afa136c6e4186d063721b5b4c192eec7536ce6b7ba615/grpcio_status-1.75.0.tar.gz", hash = "sha256:69d5b91be1b8b926f086c1c483519a968c14640773a0ccdd6c04282515dbedf7", size = 13646, upload-time = "2025-09-16T09:24:51.069Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/aa/1b1fe7d8ab699e1ec26d3a36b91d3df9f83a30abc07d4c881d0296b17b67/grpcio_status-1.74.0-py3-none-any.whl", hash = "sha256:52cdbd759a6760fc8f668098a03f208f493dd5c76bf8e02598bbbaf1f6fc2876", size = 14425, upload-time = "2025-07-24T19:01:19.963Z" }, + { url = "https://files.pythonhosted.org/packages/2b/24/d536f0a0fda3a3eeb334893e5fb9d567c2777de6a5384413f71b35cfd0e5/grpcio_status-1.75.0-py3-none-any.whl", hash = "sha256:de62557ef97b7e19c3ce6da19793a12c5f6c1fbbb918d233d9671aba9d9e1d78", size = 14424, upload-time = "2025-09-16T09:23:33.843Z" }, ] [[package]] @@ -441,16 +445,16 @@ wheels = [ [[package]] name = "protobuf" -version = "6.32.0" +version = "6.32.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c0/df/fb4a8eeea482eca989b51cffd274aac2ee24e825f0bf3cbce5281fa1567b/protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2", size = 440614, upload-time = "2025-08-14T21:21:25.015Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/a4/cc17347aa2897568beece2e674674359f911d6fe21b0b8d6268cd42727ac/protobuf-6.32.1.tar.gz", hash = "sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d", size = 440635, upload-time = "2025-09-11T21:38:42.935Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/18/df8c87da2e47f4f1dcc5153a81cd6bca4e429803f4069a299e236e4dd510/protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741", size = 424409, upload-time = "2025-08-14T21:21:12.366Z" }, - { url = "https://files.pythonhosted.org/packages/e1/59/0a820b7310f8139bd8d5a9388e6a38e1786d179d6f33998448609296c229/protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e", size = 435735, upload-time = "2025-08-14T21:21:15.046Z" }, - { url = "https://files.pythonhosted.org/packages/cc/5b/0d421533c59c789e9c9894683efac582c06246bf24bb26b753b149bd88e4/protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0", size = 426449, upload-time = "2025-08-14T21:21:16.687Z" }, - { url = "https://files.pythonhosted.org/packages/ec/7b/607764ebe6c7a23dcee06e054fd1de3d5841b7648a90fd6def9a3bb58c5e/protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1", size = 322869, upload-time = "2025-08-14T21:21:18.282Z" }, - { url = "https://files.pythonhosted.org/packages/40/01/2e730bd1c25392fc32e3268e02446f0d77cb51a2c3a8486b1798e34d5805/protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c", size = 322009, upload-time = "2025-08-14T21:21:19.893Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f2/80ffc4677aac1bc3519b26bc7f7f5de7fce0ee2f7e36e59e27d8beb32dd1/protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783", size = 169287, upload-time = "2025-08-14T21:21:23.515Z" }, + { url = "https://files.pythonhosted.org/packages/c0/98/645183ea03ab3995d29086b8bf4f7562ebd3d10c9a4b14ee3f20d47cfe50/protobuf-6.32.1-cp310-abi3-win32.whl", hash = "sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085", size = 424411, upload-time = "2025-09-11T21:38:27.427Z" }, + { url = "https://files.pythonhosted.org/packages/8c/f3/6f58f841f6ebafe076cebeae33fc336e900619d34b1c93e4b5c97a81fdfa/protobuf-6.32.1-cp310-abi3-win_amd64.whl", hash = "sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1", size = 435738, upload-time = "2025-09-11T21:38:30.959Z" }, + { url = "https://files.pythonhosted.org/packages/10/56/a8a3f4e7190837139e68c7002ec749190a163af3e330f65d90309145a210/protobuf-6.32.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281", size = 426454, upload-time = "2025-09-11T21:38:34.076Z" }, + { url = "https://files.pythonhosted.org/packages/3f/be/8dd0a927c559b37d7a6c8ab79034fd167dcc1f851595f2e641ad62be8643/protobuf-6.32.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4", size = 322874, upload-time = "2025-09-11T21:38:35.509Z" }, + { url = "https://files.pythonhosted.org/packages/5c/f6/88d77011b605ef979aace37b7703e4eefad066f7e84d935e5a696515c2dd/protobuf-6.32.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710", size = 322013, upload-time = "2025-09-11T21:38:37.017Z" }, + { url = "https://files.pythonhosted.org/packages/97/b7/15cc7d93443d6c6a84626ae3258a91f4c6ac8c0edd5df35ea7658f71b79c/protobuf-6.32.1-py3-none-any.whl", hash = "sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346", size = 169289, upload-time = "2025-09-11T21:38:41.234Z" }, ] [[package]] @@ -476,7 +480,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.11.7" +version = "2.11.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -484,9 +488,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/5d/09a551ba512d7ca404d785072700d3f6727a02f6f3c24ecfd081c7cf0aa8/pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2", size = 788495, upload-time = "2025-09-13T11:26:39.325Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d3/108f2006987c58e76691d5ae5d200dd3e0f532cb4e5fa3560751c3a1feba/pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2", size = 444855, upload-time = "2025-09-13T11:26:36.909Z" }, ] [[package]] @@ -519,16 +523,16 @@ wheels = [ [[package]] name = "pydantic-settings" -version = "2.10.1" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583, upload-time = "2025-06-24T13:26:46.841Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/c5/dbbc27b814c71676593d1c3f718e6cd7d4f00652cefa24b75f7aa3efb25e/pydantic_settings-2.11.0.tar.gz", hash = "sha256:d0e87a1c7d33593beb7194adb8470fc426e95ba02af83a0f23474a04c9a08180", size = 188394, upload-time = "2025-09-24T14:19:11.764Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235, upload-time = "2025-06-24T13:26:45.485Z" }, + { url = "https://files.pythonhosted.org/packages/83/d6/887a1ff844e64aa823fb4905978d882a633cfe295c32eacad582b78a7d8b/pydantic_settings-2.11.0-py3-none-any.whl", hash = "sha256:fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c", size = 48608, upload-time = "2025-09-24T14:19:10.015Z" }, ] [[package]] @@ -603,6 +607,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, ] +[[package]] +name = "pytokens" +version = "0.1.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/5f/e959a442435e24f6fb5a01aec6c657079ceaca1b3baf18561c3728d681da/pytokens-0.1.10.tar.gz", hash = "sha256:c9a4bfa0be1d26aebce03e6884ba454e842f186a59ea43a6d3b25af58223c044", size = 12171, upload-time = "2025-02-19T14:51:22.001Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/e5/63bed382f6a7a5ba70e7e132b8b7b8abbcf4888ffa6be4877698dcfbed7d/pytokens-0.1.10-py3-none-any.whl", hash = "sha256:db7b72284e480e69fb085d9f251f66b3d2df8b7166059261258ff35f50fb711b", size = 12046, upload-time = "2025-02-19T14:51:18.694Z" }, +] + [[package]] name = "requests" version = "2.32.5" From 5339af706e2ec68cf86e923be329df9bf5ebf1ee Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Fri, 26 Sep 2025 16:13:21 +0100 Subject: [PATCH 04/11] Add DB routes --- .../routers/simulations.py | 46 ++- .../src/policyengine_api_simulation/main.py | 28 +- .../policyengine_api_simulation/settings.py | 36 --- .../policyengine_api_simulation/simulation.py | 268 ++++++++++++++++-- .../test_simulation.py | 94 ++++++ test.ipynb | 100 ------- 6 files changed, 388 insertions(+), 184 deletions(-) delete mode 100644 projects/policyengine-api-simulation/src/policyengine_api_simulation/settings.py create mode 100644 projects/policyengine-api-simulation/test_simulation.py delete mode 100644 test.ipynb diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/simulations.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/simulations.py index 0f511201..8ccbf561 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/routers/simulations.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/simulations.py @@ -1,14 +1,30 @@ -from fastapi import APIRouter, HTTPException, Depends, Query +from fastapi import APIRouter, HTTPException, Depends, Query, BackgroundTasks from sqlmodel import Session, select -from policyengine.database import SimulationTable +from policyengine.database import SimulationTable, Database +from policyengine.models import Simulation from policyengine_api_full.database import get_session from policyengine_api_full.schemas import SimulationResponse, SimulationCreate, decode_bytes from typing import Optional from datetime import datetime from uuid import uuid4 +import os router = APIRouter(prefix="/simulations", tags=["simulations"]) +# Initialize database connection for running simulations +database = Database(url=os.environ.get("DATABASE_URL", "postgresql://postgres:postgres@127.0.0.1:54322/postgres")) + + +def run_simulation_task(simulation_id: str): + """Background task to run a simulation.""" + try: + simulation = database.get(Simulation, id=simulation_id) + if simulation: + simulation.run() + database.set(simulation) + except Exception as e: + print(f"Error running simulation {simulation_id}: {e}") + @router.get("/", response_model=list[SimulationResponse]) def list_simulations( @@ -37,9 +53,11 @@ def list_simulations( @router.post("/", response_model=SimulationResponse, status_code=201) def create_simulation( simulation: SimulationCreate, + background_tasks: BackgroundTasks, session: Session = Depends(get_session), + run_immediately: bool = Query(True, description="Whether to run the simulation immediately after creation"), ): - """Create a new simulation.""" + """Create a new simulation and optionally run it immediately.""" db_simulation = SimulationTable( id=str(uuid4()), created_at=datetime.utcnow(), @@ -49,6 +67,11 @@ def create_simulation( session.add(db_simulation) session.commit() session.refresh(db_simulation) + + # Run simulation in background if requested + if run_immediately: + background_tasks.add_task(run_simulation_task, db_simulation.id) + return SimulationResponse.from_orm(db_simulation) @@ -101,6 +124,23 @@ def update_simulation( return SimulationResponse.from_orm(db_simulation) +@router.post("/{simulation_id}/run", response_model=SimulationResponse) +def run_simulation( + simulation_id: str, + background_tasks: BackgroundTasks, + session: Session = Depends(get_session), +): + """Run an existing simulation.""" + simulation = session.get(SimulationTable, simulation_id) + if not simulation: + raise HTTPException(status_code=404, detail="Simulation not found") + + # Run simulation in background + background_tasks.add_task(run_simulation_task, simulation_id) + + return SimulationResponse.from_orm(simulation) + + @router.delete("/{simulation_id}", status_code=204) def delete_simulation( simulation_id: str, diff --git a/projects/policyengine-api-simulation/src/policyengine_api_simulation/main.py b/projects/policyengine-api-simulation/src/policyengine_api_simulation/main.py index 31a7eb82..4462e234 100644 --- a/projects/policyengine-api-simulation/src/policyengine_api_simulation/main.py +++ b/projects/policyengine-api-simulation/src/policyengine_api_simulation/main.py @@ -1,7 +1,6 @@ from contextlib import asynccontextmanager from typing import Any from fastapi import FastAPI -from .settings import get_settings, Environment from policyengine_fastapi.opentelemetry import ( GCPLoggingInstrumentor, FastAPIEnhancedInstrumenter, @@ -45,29 +44,4 @@ async def lifespan(app: FastAPI): ) # attach the api defined in the app package -initialize(app=app) - -# attach ping routes -health_registry = HealthRegistry() -health_registry.register(HealthSystemReporter("general", {})) -ping.include_all_routers(app, health_registry) - -# configure tracing and metrics -GCPLoggingInstrumentor().instrument() -FastAPIEnhancedInstrumenter().instrument(app) - -resource = Resource.create( - attributes={ - SERVICE_NAME: get_settings().ot_service_name, - SERVICE_INSTANCE_ID: get_settings().ot_service_instance_id, - } -) - -match get_settings().environment: - case Environment.DESKTOP: - pass # Don't print opentelemetry to console- this makes it impossible to read the logs. Alternatively, do by uncommenting this line. - # export_ot_to_console(resource) - case Environment.PRODUCTION: - export_ot_to_gcp(resource) - case value: - raise Exception(f"Forgot to handle environment value {value}") +initialize(app=app) \ No newline at end of file diff --git a/projects/policyengine-api-simulation/src/policyengine_api_simulation/settings.py b/projects/policyengine-api-simulation/src/policyengine_api_simulation/settings.py deleted file mode 100644 index 09c3407d..00000000 --- a/projects/policyengine-api-simulation/src/policyengine_api_simulation/settings.py +++ /dev/null @@ -1,36 +0,0 @@ -from enum import Enum -from functools import lru_cache -from pydantic_settings import BaseSettings, SettingsConfigDict -from pydantic import field_validator - - -class Environment(Enum): - DESKTOP = "desktop" - PRODUCTION = "production" - - -class AppSettings(BaseSettings): - environment: Environment = Environment.DESKTOP - - @field_validator("environment", mode="before") - @classmethod - def strip_environment(cls, v): - if isinstance(v, str): - return v.strip() - return v - - ot_service_name: str = "YOUR_OT_SERVICE_NAME" - """ - service name used by opentelemetry when reporting trace information - """ - ot_service_instance_id: str = "YOUR_OT_INSTANCE_ID" - """ - instance id used by opentelemetry when reporting trace information - """ - - model_config = SettingsConfigDict(env_file=".env") - - -@lru_cache -def get_settings(): - return AppSettings() diff --git a/projects/policyengine-api-simulation/src/policyengine_api_simulation/simulation.py b/projects/policyengine-api-simulation/src/policyengine_api_simulation/simulation.py index cc81f6be..5723ab92 100644 --- a/projects/policyengine-api-simulation/src/policyengine_api_simulation/simulation.py +++ b/projects/policyengine-api-simulation/src/policyengine_api_simulation/simulation.py @@ -1,31 +1,263 @@ -from typing import Annotated import os -from fastapi import APIRouter, Depends, HTTPException +from fastapi import APIRouter, HTTPException, BackgroundTasks from pydantic import BaseModel -from policyengine.simulation import SimulationOptions, Simulation -from policyengine.outputs.macro.comparison.calculate_economy_comparison import ( - EconomyComparison, -) -from pathlib import Path import logging +from policyengine.database import Database +from policyengine.models import Policy, Dynamic, Model, ModelVersion, Dataset, Simulation +from typing import Optional +from uuid import uuid4 +from datetime import datetime logger = logging.getLogger(__file__) +# Initialize database connection +database = Database(url=os.environ.get("DATABASE_URL", "postgresql://postgres:postgres@127.0.0.1:54322/postgres")) + + +class SimulationCreateRequest(BaseModel): + """Request model for creating a simulation.""" + policy_id: Optional[str] = None + dynamic_id: Optional[str] = None + dataset_id: str + model_id: str + model_version_id: Optional[str] = None + + +class SimulationCreateResponse(BaseModel): + """Response model for simulation creation.""" + simulation_id: str + status: str + message: str + + +class SimulationRunRequest(BaseModel): + """Request model for running a simulation.""" + simulation_id: str + + +class SimulationRunResponse(BaseModel): + """Response model for simulation run.""" + simulation_id: str + status: str + message: str + + +def run_simulation_task(simulation_id: str): + """Background task to run a simulation.""" + try: + logger.info(f"Fetching simulation {simulation_id} from database") + simulation = database.get(Simulation, id=simulation_id) + + if not simulation: + logger.error(f"Simulation {simulation_id} not found") + return + + logger.info(f"Running simulation {simulation_id}") + simulation.run() + + logger.info(f"Saving simulation results for {simulation_id}") + database.set(simulation) + + logger.info(f"Successfully completed simulation {simulation_id}") + except Exception as e: + logger.error(f"Error running simulation {simulation_id}: {e}") + def create_router(): router = APIRouter() - @router.post("/simulate/economy/comparison", response_model=EconomyComparison) - async def simulate(parameters: SimulationOptions) -> EconomyComparison: - model = SimulationOptions.model_validate(parameters) - logger.info("Initialising simulation from input") - simulation = Simulation(**model.model_dump()) - logger.info("Calculating comparison") - result = ( - simulation.calculate_economy_comparison() # pyright: ignore [reportAttributeAccessIssue] + @router.post("/create_simulation", response_model=SimulationCreateResponse) + async def create_simulation( + request: SimulationCreateRequest, + ) -> SimulationCreateResponse: + """ + Create a new simulation in the database. + """ + try: + # Generate a new simulation ID + simulation_id = str(uuid4()) + + # Fetch the required objects from database + policy = database.get(Policy, id=request.policy_id) if request.policy_id else None + dynamic = database.get(Dynamic, id=request.dynamic_id) if request.dynamic_id else None + dataset = database.get(Dataset, id=request.dataset_id) + model = database.get(Model, id=request.model_id) + model_version = database.get(ModelVersion, id=request.model_version_id) if request.model_version_id else None + + if not dataset: + raise HTTPException(status_code=404, detail=f"Dataset {request.dataset_id} not found") + if not model: + raise HTTPException(status_code=404, detail=f"Model {request.model_id} not found") + + # Create the simulation object + simulation = Simulation( + id=simulation_id, + created_at=datetime.utcnow(), + updated_at=datetime.utcnow(), + policy=policy, + dynamic=dynamic, + dataset=dataset, + model=model, + model_version=model_version, + result=None + ) + + # Save to database + logger.info(f"Creating simulation {simulation_id} in database") + database.set(simulation) + + return SimulationCreateResponse( + simulation_id=simulation_id, + status="created", + message=f"Simulation {simulation_id} created successfully" + ) + except HTTPException: + raise + except Exception as e: + logger.error(f"Error creating simulation: {e}") + raise HTTPException(status_code=500, detail=f"Error creating simulation: {str(e)}") + + @router.post("/create_and_run_simulation", response_model=SimulationCreateResponse) + async def create_and_run_simulation( + request: SimulationCreateRequest, + background_tasks: BackgroundTasks + ) -> SimulationCreateResponse: + """ + Create a new simulation and immediately run it. + This combines creation and execution in one step. + """ + try: + # Generate a new simulation ID + simulation_id = str(uuid4()) + + # Fetch the required objects from database + policy = database.get(Policy, id=request.policy_id) if request.policy_id else None + dynamic = database.get(Dynamic, id=request.dynamic_id) if request.dynamic_id else None + dataset = database.get(Dataset, id=request.dataset_id) + model = database.get(Model, id=request.model_id) + model_version = database.get(ModelVersion, id=request.model_version_id) if request.model_version_id else None + + if not dataset: + raise HTTPException(status_code=404, detail=f"Dataset {request.dataset_id} not found") + if not model: + raise HTTPException(status_code=404, detail=f"Model {request.model_id} not found") + + # Create the simulation object + simulation = Simulation( + id=simulation_id, + created_at=datetime.utcnow(), + updated_at=datetime.utcnow(), + policy=policy, + dynamic=dynamic, + dataset=dataset, + model=model, + model_version=model_version, + result=None + ) + + # Save to database + logger.info(f"Creating simulation {simulation_id} in database") + database.set(simulation) + + # Add background task to run the simulation + background_tasks.add_task(run_simulation_task, simulation_id) + + return SimulationCreateResponse( + simulation_id=simulation_id, + status="running", + message=f"Simulation {simulation_id} created and running in background" + ) + except HTTPException: + raise + except Exception as e: + logger.error(f"Error creating and running simulation: {e}") + raise HTTPException(status_code=500, detail=f"Error creating and running simulation: {str(e)}") + + @router.post("/run_simulation", response_model=SimulationRunResponse) + async def run_simulation( + request: SimulationRunRequest, + background_tasks: BackgroundTasks + ) -> SimulationRunResponse: + """ + Run an existing simulation by ID using the database pattern from test.py. + The simulation runs in the background. + """ + simulation_id = request.simulation_id + + # Verify simulation exists + try: + simulation = database.get(Simulation, id=simulation_id) + if not simulation: + raise HTTPException(status_code=404, detail=f"Simulation {simulation_id} not found") + except Exception as e: + logger.error(f"Error fetching simulation {simulation_id}: {e}") + raise HTTPException(status_code=500, detail=f"Error fetching simulation: {str(e)}") + + # Add background task to run the simulation + background_tasks.add_task(run_simulation_task, simulation_id) + + return SimulationRunResponse( + simulation_id=simulation_id, + status="running", + message=f"Simulation {simulation_id} is running in the background" ) - logger.info("Comparison complete") - return result + @router.post("/run_simulation_sync/{simulation_id}") + async def run_simulation_sync(simulation_id: str) -> dict: + """ + Run a simulation synchronously (blocking). + This follows the exact pattern from test.py. + """ + try: + # Fetch simulation from database + logger.info(f"Fetching simulation {simulation_id} from database") + simulation = database.get(Simulation, id=simulation_id) + + if not simulation: + raise HTTPException(status_code=404, detail=f"Simulation {simulation_id} not found") + + # Run the simulation + logger.info(f"Running simulation {simulation_id}") + simulation.run() + + # Save the results back to database + logger.info(f"Saving simulation results for {simulation_id}") + database.set(simulation) + + return { + "simulation_id": simulation_id, + "status": "completed", + "message": f"Simulation {simulation_id} completed successfully" + } + except HTTPException: + raise + except Exception as e: + logger.error(f"Error running simulation {simulation_id}: {e}") + raise HTTPException(status_code=500, detail=f"Error running simulation: {str(e)}") + + @router.get("/simulation/{simulation_id}/status") + async def get_simulation_status(simulation_id: str) -> dict: + """ + Get the status of a simulation. + """ + try: + simulation = database.get(Simulation, id=simulation_id) + + if not simulation: + raise HTTPException(status_code=404, detail=f"Simulation {simulation_id} not found") + + # Check if simulation has results + has_results = simulation.result is not None + + return { + "simulation_id": simulation_id, + "has_results": has_results, + "status": "completed" if has_results else "pending" + } + except HTTPException: + raise + except Exception as e: + logger.error(f"Error fetching simulation status for {simulation_id}: {e}") + raise HTTPException(status_code=500, detail=f"Error fetching simulation status: {str(e)}") - return router + return router \ No newline at end of file diff --git a/projects/policyengine-api-simulation/test_simulation.py b/projects/policyengine-api-simulation/test_simulation.py new file mode 100644 index 00000000..46f71aee --- /dev/null +++ b/projects/policyengine-api-simulation/test_simulation.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +""" +Test script for the simulation API. +This demonstrates how to use the API endpoints that follow the pattern from test.py. +""" + +import requests +import json +import time + +# API base URL - adjust port as needed +BASE_URL = "http://localhost:8000" + +def run_simulation_async(simulation_id: str): + """Run a simulation asynchronously.""" + print(f"Running simulation {simulation_id} asynchronously...") + + response = requests.post( + f"{BASE_URL}/run_simulation", + json={"simulation_id": simulation_id} + ) + + if response.status_code == 200: + result = response.json() + print(f"Response: {result}") + return result + else: + print(f"Error: {response.status_code} - {response.text}") + return None + + +def run_simulation_sync(simulation_id: str): + """Run a simulation synchronously.""" + print(f"Running simulation {simulation_id} synchronously...") + + response = requests.post( + f"{BASE_URL}/run_simulation_sync/{simulation_id}" + ) + + if response.status_code == 200: + result = response.json() + print(f"Response: {result}") + return result + else: + print(f"Error: {response.status_code} - {response.text}") + return None + + +def check_simulation_status(simulation_id: str): + """Check the status of a simulation.""" + print(f"Checking status of simulation {simulation_id}...") + + response = requests.get( + f"{BASE_URL}/simulation/{simulation_id}/status" + ) + + if response.status_code == 200: + result = response.json() + print(f"Status: {result}") + return result + else: + print(f"Error: {response.status_code} - {response.text}") + return None + + +def main(): + # Example simulation ID - replace with an actual ID from your database + simulation_id = "acc3eb26-fd1a-4477-9ce2-40e45855042f" + + print("=" * 50) + print("Testing Simulation API") + print("=" * 50) + + # Test 1: Run simulation synchronously (blocking) + print("\n1. Running simulation synchronously (blocking):") + run_simulation_sync(simulation_id) + + # Test 2: Check simulation status + print("\n2. Checking simulation status:") + check_simulation_status(simulation_id) + + # Test 3: Run simulation asynchronously + print("\n3. Running simulation asynchronously:") + result = run_simulation_async(simulation_id) + + if result: + # Wait a bit then check status + print("\nWaiting 5 seconds for async simulation to complete...") + time.sleep(5) + check_simulation_status(simulation_id) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/test.ipynb b/test.ipynb deleted file mode 100644 index 49f29451..00000000 --- a/test.ipynb +++ /dev/null @@ -1,100 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "fae368db", - "metadata": {}, - "outputs": [], - "source": [ - "from policyengine.database import Database" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "20ec3c48", - "metadata": {}, - "outputs": [], - "source": [ - "db = Database(url=\"postgresql://postgres:postgres@127.0.0.1:54322/postgres\")" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "ebf1d5bf", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - " " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Warning: Invalid date 2021-06-31, using 2021-06-30\n", - "Warning: Invalid date 2021-06-31, using 2021-06-30\n", - "Warning: Invalid date 2021-06-31, using 2021-06-30\n", - "Warning: Invalid date 2021-06-31, using 2021-06-30\n" - ] - } - ], - "source": [ - "from policyengine.models.policyengine_uk import policyengine_uk_latest_version\n", - "from policyengine.models.policyengine_us import policyengine_us_latest_version\n", - "\n", - "db.register_model_version(policyengine_uk_latest_version)\n", - "db.register_model_version(policyengine_us_latest_version)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "c468407b", - "metadata": {}, - "outputs": [], - "source": [ - "from policyengine.utils.datasets import create_uk_dataset, create_us_dataset\n", - "\n", - "uk_dataset = create_uk_dataset()\n", - "us_dataset = create_us_dataset()\n", - "\n", - "db.set(uk_dataset)\n", - "db.set(us_dataset)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "policyengine", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.5" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From 9d6f9cdf0664097f5af72ebcd6ebbb7eaecc4f02 Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Sun, 28 Sep 2025 13:27:19 +0100 Subject: [PATCH 05/11] Add updates --- .../policyengine_api_full/routers/datasets.py | 32 ++++--- .../routers/parameters.py | 32 ++++--- .../routers/simulations.py | 16 +++- .../src/policyengine_api_full/schemas.py | 95 ++++++++++++++++++- 4 files changed, 145 insertions(+), 30 deletions(-) diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/datasets.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/datasets.py index 74ca2b56..296e2b96 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/routers/datasets.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/datasets.py @@ -3,7 +3,7 @@ from policyengine.database import DatasetTable, VersionedDatasetTable from policyengine_api_full.database import get_session from policyengine_api_full.schemas import ( - DatasetResponse, DatasetCreate, + DatasetResponse, DatasetCreate, DatasetDetailResponse, VersionedDatasetResponse, VersionedDatasetCreate ) from typing import Optional @@ -24,7 +24,7 @@ def list_datasets( """List all datasets with pagination.""" statement = select(DatasetTable).offset(skip).limit(limit) datasets = session.exec(statement).all() - return datasets + return [DatasetResponse.from_orm(d) for d in datasets] @datasets_router.post("/", response_model=DatasetResponse, status_code=201) @@ -33,15 +33,11 @@ def create_dataset( session: Session = Depends(get_session), ): """Create a new dataset.""" - db_dataset = DatasetTable( - created_at=datetime.utcnow(), - updated_at=datetime.utcnow(), - **dataset.model_dump() - ) + db_dataset = DatasetTable(**dataset.to_orm_dict()) session.add(db_dataset) session.commit() session.refresh(db_dataset) - return db_dataset + return DatasetResponse.from_orm(db_dataset) @datasets_router.get("/{dataset_id}", response_model=DatasetResponse) @@ -49,11 +45,23 @@ def get_dataset( dataset_id: str, session: Session = Depends(get_session), ): - """Get a single dataset by ID.""" + """Get a single dataset by ID (without data field).""" + dataset = session.get(DatasetTable, dataset_id) + if not dataset: + raise HTTPException(status_code=404, detail="Dataset not found") + return DatasetResponse.from_orm(dataset) + + +@datasets_router.get("/{dataset_id}/detail", response_model=DatasetDetailResponse) +def get_dataset_detail( + dataset_id: str, + session: Session = Depends(get_session), +): + """Get a single dataset by ID with full data.""" dataset = session.get(DatasetTable, dataset_id) if not dataset: raise HTTPException(status_code=404, detail="Dataset not found") - return dataset + return DatasetDetailResponse.from_orm(dataset) @datasets_router.patch("/{dataset_id}", response_model=DatasetResponse) @@ -73,12 +81,10 @@ def update_dataset( if description is not None: db_dataset.description = description - db_dataset.updated_at = datetime.utcnow() - session.add(db_dataset) session.commit() session.refresh(db_dataset) - return db_dataset + return DatasetResponse.from_orm(db_dataset) @datasets_router.delete("/{dataset_id}", status_code=204) diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/parameters.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/parameters.py index 3bac4a92..a2f339ab 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/routers/parameters.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/parameters.py @@ -7,7 +7,7 @@ ) from policyengine_api_full.database import get_session from typing import Optional -from datetime import datetime +from datetime import datetime, timezone parameters_router = APIRouter(prefix="/parameters", tags=["parameters"]) parameter_values_router = APIRouter(prefix="/parameter-values", tags=["parameter values"]) @@ -19,10 +19,16 @@ def list_parameters( session: Session = Depends(get_session), skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), + limit: int = Query(100, ge=1, le=10_000), + model_id: Optional[str] = Query(None, description="Filter by model ID"), ): - """List all parameters with pagination.""" - statement = select(ParameterTable).offset(skip).limit(limit) + """List all parameters with pagination and optional filters.""" + statement = select(ParameterTable) + + if model_id: + statement = statement.where(ParameterTable.model_id == model_id) + + statement = statement.offset(skip).limit(limit) parameters = session.exec(statement).all() return parameters @@ -39,25 +45,27 @@ def create_parameter( return parameter -@parameters_router.get("/{parameter_id}", response_model=ParameterTable) +@parameters_router.get("/{parameter_id}/{model_id}", response_model=ParameterTable) def get_parameter( parameter_id: str, + model_id: str, session: Session = Depends(get_session), ): - """Get a single parameter by ID.""" - parameter = session.get(ParameterTable, parameter_id) + """Get a single parameter by ID and model ID (composite key).""" + parameter = session.get(ParameterTable, (parameter_id, model_id)) if not parameter: raise HTTPException(status_code=404, detail="Parameter not found") return parameter -@parameters_router.delete("/{parameter_id}", status_code=204) +@parameters_router.delete("/{parameter_id}/{model_id}", status_code=204) def delete_parameter( parameter_id: str, + model_id: str, session: Session = Depends(get_session), ): """Delete a parameter.""" - parameter = session.get(ParameterTable, parameter_id) + parameter = session.get(ParameterTable, (parameter_id, model_id)) if not parameter: raise HTTPException(status_code=404, detail="Parameter not found") @@ -71,7 +79,7 @@ def delete_parameter( def list_parameter_values( session: Session = Depends(get_session), skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), + limit: int = Query(100, ge=1, le=10_000), parameter_id: Optional[str] = Query(None, description="Filter by parameter ID"), policy_id: Optional[str] = Query(None, description="Filter by policy ID"), ): @@ -126,7 +134,7 @@ def update_parameter_value( if value is not None: db_parameter_value.value = value - db_parameter_value.updated_at = datetime.utcnow() + db_parameter_value.updated_at = datetime.now(timezone.utc) session.add(db_parameter_value) session.commit() @@ -154,7 +162,7 @@ def delete_parameter_value( def list_baseline_parameter_values( session: Session = Depends(get_session), skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), + limit: int = Query(100, ge=1, le=10_000), parameter_id: Optional[str] = Query(None, description="Filter by parameter ID"), model_id: Optional[str] = Query(None, description="Filter by model ID"), ): diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/simulations.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/simulations.py index 8ccbf561..f52766a1 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/routers/simulations.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/simulations.py @@ -3,7 +3,7 @@ from policyengine.database import SimulationTable, Database from policyengine.models import Simulation from policyengine_api_full.database import get_session -from policyengine_api_full.schemas import SimulationResponse, SimulationCreate, decode_bytes +from policyengine_api_full.schemas import SimulationResponse, SimulationCreate, SimulationDetailResponse, decode_bytes from typing import Optional from datetime import datetime from uuid import uuid4 @@ -80,13 +80,25 @@ def get_simulation( simulation_id: str, session: Session = Depends(get_session), ): - """Get a single simulation by ID.""" + """Get a single simulation by ID (without result data).""" simulation = session.get(SimulationTable, simulation_id) if not simulation: raise HTTPException(status_code=404, detail="Simulation not found") return SimulationResponse.from_orm(simulation) +@router.get("/{simulation_id}/result", response_model=SimulationDetailResponse) +def get_simulation_result( + simulation_id: str, + session: Session = Depends(get_session), +): + """Get a single simulation by ID with full result data.""" + simulation = session.get(SimulationTable, simulation_id) + if not simulation: + raise HTTPException(status_code=404, detail="Simulation not found") + return SimulationDetailResponse.from_orm(simulation) + + @router.patch("/{simulation_id}", response_model=SimulationResponse) def update_simulation( simulation_id: str, diff --git a/projects/policyengine-api-full/src/policyengine_api_full/schemas.py b/projects/policyengine-api-full/src/policyengine_api_full/schemas.py index 1f98b57e..c0c2ee58 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/schemas.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/schemas.py @@ -104,6 +104,7 @@ class PolicyUpdate(BaseModel): # Simulation schemas class SimulationResponse(BaseModel): + """Basic simulation response without large binary data""" model_config = ConfigDict(from_attributes=True) id: str @@ -114,11 +115,40 @@ class SimulationResponse(BaseModel): dataset_id: str model_id: str model_version_id: Optional[str] = None - result: Optional[str] = Field(None, description="Base64 encoded result") + has_result: bool = Field(description="Whether simulation has completed and has results") @staticmethod def from_orm(obj): return SimulationResponse( + id=obj.id, + created_at=obj.created_at, + updated_at=obj.updated_at, + policy_id=obj.policy_id, + dynamic_id=obj.dynamic_id, + dataset_id=obj.dataset_id, + model_id=obj.model_id, + model_version_id=obj.model_version_id, + has_result=obj.result is not None + ) + + +class SimulationDetailResponse(BaseModel): + """Detailed simulation response including result data""" + model_config = ConfigDict(from_attributes=True) + + id: str + created_at: datetime + updated_at: datetime + policy_id: Optional[str] = None + dynamic_id: Optional[str] = None + dataset_id: str + model_id: str + model_version_id: Optional[str] = None + result: Optional[str] = Field(None, description="Base64 encoded result") + + @staticmethod + def from_orm(obj): + return SimulationDetailResponse( id=obj.id, created_at=obj.created_at, updated_at=obj.updated_at, @@ -152,19 +182,78 @@ def to_orm_dict(self): # Dataset schemas class DatasetResponse(BaseModel): + """Basic dataset response without large binary data""" model_config = ConfigDict(from_attributes=True) id: str name: str description: Optional[str] = None - created_at: datetime - updated_at: datetime + version: Optional[str] = None + versioned_dataset_id: Optional[str] = None + year: Optional[int] = None + model_id: Optional[str] = None + has_data: bool = Field(description="Whether dataset has data") + + @staticmethod + def from_orm(obj): + return DatasetResponse( + id=obj.id, + name=obj.name, + description=obj.description, + version=obj.version, + versioned_dataset_id=obj.versioned_dataset_id, + year=obj.year, + model_id=obj.model_id, + has_data=obj.data is not None + ) + + +class DatasetDetailResponse(BaseModel): + """Detailed dataset response including data""" + model_config = ConfigDict(from_attributes=True) + + id: str + name: str + description: Optional[str] = None + version: Optional[str] = None + versioned_dataset_id: Optional[str] = None + year: Optional[int] = None + data: Optional[str] = Field(None, description="Base64 encoded data") + model_id: Optional[str] = None + + @staticmethod + def from_orm(obj): + return DatasetDetailResponse( + id=obj.id, + name=obj.name, + description=obj.description, + version=obj.version, + versioned_dataset_id=obj.versioned_dataset_id, + year=obj.year, + data=encode_bytes(obj.data) if obj.data else None, + model_id=obj.model_id + ) class DatasetCreate(BaseModel): id: str name: str description: Optional[str] = None + version: Optional[str] = None + year: Optional[int] = None + data: Optional[str] = None # Base64 encoded + model_id: Optional[str] = None + + def to_orm_dict(self): + return { + "id": self.id, + "name": self.name, + "description": self.description, + "version": self.version, + "year": self.year, + "data": decode_bytes(self.data) if self.data else None, + "model_id": self.model_id + } # Versioned Dataset schemas From 2af54f95b694b49ef1ed92186e29df6639afe32d Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Sun, 28 Sep 2025 20:04:17 +0100 Subject: [PATCH 06/11] Get aggregates working! --- .../src/policyengine_api_full/main.py | 13 +- .../routers/aggregates.py | 133 ++++++++ .../routers/report_elements.py | 318 ++++++++++++++++++ .../policyengine_api_full/routers/reports.py | 85 +++++ 4 files changed, 547 insertions(+), 2 deletions(-) create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/routers/aggregates.py create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/routers/reports.py diff --git a/projects/policyengine-api-full/src/policyengine_api_full/main.py b/projects/policyengine-api-full/src/policyengine_api_full/main.py index 8f502028..a8d0ca9e 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/main.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/main.py @@ -15,7 +15,10 @@ parameters, dynamics, model_versions, - baseline_variables + baseline_variables, + reports, + report_elements, + aggregates ) # Import database setup @@ -33,7 +36,10 @@ BaselineParameterValueTable, BaselineVariableTable, DynamicTable, - ModelVersionTable + ModelVersionTable, + ReportTable, + ReportElementTable, + AggregateTable ) """ @@ -80,6 +86,9 @@ async def lifespan(app: FastAPI): app.include_router(dynamics.router) app.include_router(model_versions.router) app.include_router(baseline_variables.router) +app.include_router(reports.reports_router) +app.include_router(report_elements.report_elements_router) +app.include_router(aggregates.aggregates_router) # Health check endpoint @app.get("/health") diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/aggregates.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/aggregates.py new file mode 100644 index 00000000..d16712d1 --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/aggregates.py @@ -0,0 +1,133 @@ +from fastapi import APIRouter, HTTPException, Depends, Query +from sqlmodel import Session, select +from policyengine.database import AggregateTable +from policyengine_api_full.database import database +from policyengine.models import Aggregate +from policyengine_api_full.database import get_session +from typing import Optional +from datetime import datetime, timezone + +aggregates_router = APIRouter(prefix="/aggregates", tags=["aggregates"]) + + +@aggregates_router.get("/", response_model=list[AggregateTable]) +def list_aggregates( + session: Session = Depends(get_session), + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=10_000), + simulation_id: Optional[str] = Query(None, description="Filter by simulation ID"), + reportelement_id: Optional[str] = Query(None, description="Filter by report element ID"), + entity: Optional[str] = Query(None, description="Filter by entity"), + variable_name: Optional[str] = Query(None, description="Filter by variable name"), +): + """List all aggregates with pagination and optional filters.""" + statement = select(AggregateTable) + + if simulation_id: + statement = statement.where(AggregateTable.simulation_id == simulation_id) + if reportelement_id: + statement = statement.where(AggregateTable.reportelement_id == reportelement_id) + if entity: + statement = statement.where(AggregateTable.entity == entity) + if variable_name: + statement = statement.where(AggregateTable.variable_name == variable_name) + + statement = statement.offset(skip).limit(limit) + aggregates = session.exec(statement).all() + return aggregates + + +@aggregates_router.post("/", response_model=AggregateTable, status_code=201) +def create_aggregate( + aggregate: AggregateTable, + session: Session = Depends(get_session), +): + """Create a new aggregate (typically with a pre-computed value).""" + if not aggregate.id: + import uuid + aggregate.id = str(uuid.uuid4()) + + session.add(aggregate) + session.commit() + session.refresh(aggregate) + return aggregate + + +@aggregates_router.get("/{aggregate_id}", response_model=AggregateTable) +def get_aggregate( + aggregate_id: str, + session: Session = Depends(get_session), +): + """Get a single aggregate by ID.""" + aggregate = session.get(AggregateTable, aggregate_id) + if not aggregate: + raise HTTPException(status_code=404, detail="Aggregate not found") + return aggregate + + +@aggregates_router.patch("/{aggregate_id}", response_model=AggregateTable) +def update_aggregate( + aggregate_id: str, + value: Optional[float] = None, + session: Session = Depends(get_session), +): + """Update an aggregate's value.""" + db_aggregate = session.get(AggregateTable, aggregate_id) + if not db_aggregate: + raise HTTPException(status_code=404, detail="Aggregate not found") + + if value is not None: + db_aggregate.value = value + + session.add(db_aggregate) + session.commit() + session.refresh(db_aggregate) + return db_aggregate + + +@aggregates_router.delete("/{aggregate_id}", status_code=204) +def delete_aggregate( + aggregate_id: str, + session: Session = Depends(get_session), +): + """Delete an aggregate.""" + aggregate = session.get(AggregateTable, aggregate_id) + if not aggregate: + raise HTTPException(status_code=404, detail="Aggregate not found") + + session.delete(aggregate) + session.commit() + return None + + +@aggregates_router.post("/bulk", response_model=list[AggregateTable], status_code=201) +def create_bulk_aggregates( + aggregates: list[AggregateTable], + session: Session = Depends(get_session), +): + """Create multiple aggregates at once.""" + import uuid + + created_aggregates = [] + for aggregate in aggregates: + if not aggregate.id: + aggregate.id = str(uuid.uuid4()) + created_aggregates.append(aggregate) + + for i in range(len(created_aggregates)): + created_aggregates[i] = AggregateTable.convert_to_model(created_aggregates[i], database=database) + print(created_aggregates[i].simulation) + + created_aggregates = Aggregate.run(created_aggregates) + + for i in range(len(created_aggregates)): + created_aggregates[i] = AggregateTable.convert_from_model(created_aggregates[i]) + session.add(created_aggregates[i]) + + session.commit() + + # Refresh all aggregates + for aggregate in created_aggregates: + session.refresh(aggregate) + + return created_aggregates \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py new file mode 100644 index 00000000..4412ed62 --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py @@ -0,0 +1,318 @@ +from fastapi import APIRouter, HTTPException, Depends, Query, Body +from sqlmodel import Session, select +from policyengine.database import ReportElementTable, AggregateTable +from policyengine.models import Aggregate +from policyengine_api_full.database import get_session +from typing import Optional, List, Dict, Any +from datetime import datetime, timezone +from pydantic import BaseModel + +report_elements_router = APIRouter(prefix="/report-elements", tags=["report elements"]) + + +class AggregateInput(BaseModel): + """Input model for aggregate data without the value field.""" + entity: str + variable_name: str + aggregate_function: str # "sum", "mean", or "count" + simulation_id: str + year: Optional[int] = None + filter_variable_name: Optional[str] = None + filter_variable_value: Optional[str] = None + filter_variable_leq: Optional[float] = None + filter_variable_geq: Optional[float] = None + + +class ReportElementCreateRequest(BaseModel): + """Request model for creating a report element with associated data.""" + label: str + type: str # "chart", "markdown", or "aggregates" + data_type: Optional[str] = None # "Aggregate" when creating aggregates + data: Optional[List[AggregateInput]] = None # List of aggregates to compute + chart_type: Optional[str] = None + x_axis_variable: Optional[str] = None + y_axis_variable: Optional[str] = None + group_by: Optional[str] = None + color_by: Optional[str] = None + size_by: Optional[str] = None + markdown_content: Optional[str] = None + report_id: Optional[str] = None + user_id: Optional[str] = None + position: Optional[int] = None + visible: Optional[bool] = True + + +@report_elements_router.get("/", response_model=list[ReportElementTable]) +def list_report_elements( + session: Session = Depends(get_session), + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=10_000), + report_id: Optional[str] = Query(None, description="Filter by report ID"), + user_id: Optional[str] = Query(None, description="Filter by user ID"), +): + """List all report elements with pagination and optional filters.""" + statement = select(ReportElementTable) + + if report_id: + statement = statement.where(ReportElementTable.report_id == report_id) + if user_id: + statement = statement.where(ReportElementTable.user_id == user_id) + + statement = statement.offset(skip).limit(limit) + + if report_id: + statement = statement.order_by(ReportElementTable.position) + + report_elements = session.exec(statement).all() + return report_elements + + +@report_elements_router.post("/", response_model=Dict[str, Any], status_code=201) +def create_report_element( + request: ReportElementCreateRequest, + session: Session = Depends(get_session), +): + """ + Create a new report element. + + If data_type is "Aggregate" and data is provided, this will: + 1. Create the report element + 2. Run Aggregate.run() on the provided aggregates + 3. Save the computed aggregates to the database + 4. Return the report element with the computed aggregate values + """ + import uuid + from policyengine.database import SimulationTable + + # Create the report element + report_element = ReportElementTable( + id=str(uuid.uuid4()), + label=request.label, + type=request.type, + data_table="aggregates" if request.data_type == "Aggregate" else None, + chart_type=request.chart_type, + x_axis_variable=request.x_axis_variable, + y_axis_variable=request.y_axis_variable, + group_by=request.group_by, + color_by=request.color_by, + size_by=request.size_by, + markdown_content=request.markdown_content, + report_id=request.report_id, + user_id=request.user_id, + position=request.position, + visible=request.visible, + created_at=datetime.now(timezone.utc), + updated_at=datetime.now(timezone.utc) + ) + + session.add(report_element) + session.flush() # Get the ID before processing aggregates + + computed_aggregates = [] + + # Process aggregates if provided + if request.data_type == "Aggregate" and request.data: + # Convert input aggregates to Aggregate model instances + aggregate_models = [] + + for agg_input in request.data: + # Get the simulation + simulation_table = session.get(SimulationTable, agg_input.simulation_id) + if not simulation_table: + raise HTTPException( + status_code=400, + detail=f"Simulation {agg_input.simulation_id} not found" + ) + + # Convert simulation table to model (we need the full simulation object) + from policyengine.database import Database + from policyengine_api_full.database import engine + + # Create a database instance to handle the conversion + db = Database.from_engine(engine) + simulation = simulation_table.convert_to_model(db) + + # Create the Aggregate model instance + agg = Aggregate( + simulation=simulation, + entity=agg_input.entity, + variable_name=agg_input.variable_name, + year=agg_input.year, + filter_variable_name=agg_input.filter_variable_name, + filter_variable_value=agg_input.filter_variable_value, + filter_variable_leq=agg_input.filter_variable_leq, + filter_variable_geq=agg_input.filter_variable_geq, + aggregate_function=agg_input.aggregate_function, + reportelement_id=report_element.id + ) + aggregate_models.append(agg) + + # Run Aggregate.run to compute values + computed_models = Aggregate.run(aggregate_models) + + # Save the computed aggregates to the database + for agg_model in computed_models: + agg_table = AggregateTable( + id=agg_model.id, + simulation_id=agg_model.simulation.id if agg_model.simulation else None, + entity=agg_model.entity, + variable_name=agg_model.variable_name, + year=agg_model.year, + filter_variable_name=agg_model.filter_variable_name, + filter_variable_value=agg_model.filter_variable_value, + filter_variable_leq=agg_model.filter_variable_leq, + filter_variable_geq=agg_model.filter_variable_geq, + aggregate_function=agg_model.aggregate_function, + reportelement_id=report_element.id, + value=agg_model.value + ) + session.add(agg_table) + computed_aggregates.append({ + "id": agg_table.id, + "entity": agg_table.entity, + "variable_name": agg_table.variable_name, + "aggregate_function": agg_table.aggregate_function, + "value": agg_table.value, + "year": agg_table.year, + "filter_variable_name": agg_table.filter_variable_name, + "filter_variable_value": agg_table.filter_variable_value, + "filter_variable_leq": agg_table.filter_variable_leq, + "filter_variable_geq": agg_table.filter_variable_geq, + }) + + session.commit() + session.refresh(report_element) + + # Return the report element with computed aggregates + response = { + "report_element": { + "id": report_element.id, + "label": report_element.label, + "type": report_element.type, + "data_table": report_element.data_table, + "chart_type": report_element.chart_type, + "x_axis_variable": report_element.x_axis_variable, + "y_axis_variable": report_element.y_axis_variable, + "group_by": report_element.group_by, + "color_by": report_element.color_by, + "size_by": report_element.size_by, + "markdown_content": report_element.markdown_content, + "report_id": report_element.report_id, + "user_id": report_element.user_id, + "position": report_element.position, + "visible": report_element.visible, + "created_at": report_element.created_at.isoformat(), + "updated_at": report_element.updated_at.isoformat(), + } + } + + if computed_aggregates: + response["aggregates"] = computed_aggregates + + return response + + +@report_elements_router.get("/{report_element_id}", response_model=ReportElementTable) +def get_report_element( + report_element_id: str, + session: Session = Depends(get_session), +): + """Get a single report element by ID.""" + report_element = session.get(ReportElementTable, report_element_id) + if not report_element: + raise HTTPException(status_code=404, detail="Report element not found") + return report_element + + +@report_elements_router.get("/{report_element_id}/aggregates", response_model=list[AggregateTable]) +def get_report_element_aggregates( + report_element_id: str, + session: Session = Depends(get_session), +): + """Get all aggregates associated with a report element.""" + # First check if the report element exists + report_element = session.get(ReportElementTable, report_element_id) + if not report_element: + raise HTTPException(status_code=404, detail="Report element not found") + + # Get all aggregates for this report element + statement = select(AggregateTable).where( + AggregateTable.reportelement_id == report_element_id + ) + aggregates = session.exec(statement).all() + return aggregates + + +@report_elements_router.patch("/{report_element_id}", response_model=ReportElementTable) +def update_report_element( + report_element_id: str, + label: Optional[str] = None, + type: Optional[str] = None, + chart_type: Optional[str] = None, + x_axis_variable: Optional[str] = None, + y_axis_variable: Optional[str] = None, + group_by: Optional[str] = None, + color_by: Optional[str] = None, + size_by: Optional[str] = None, + markdown_content: Optional[str] = None, + position: Optional[int] = None, + visible: Optional[bool] = None, + session: Session = Depends(get_session), +): + """Update a report element.""" + db_report_element = session.get(ReportElementTable, report_element_id) + if not db_report_element: + raise HTTPException(status_code=404, detail="Report element not found") + + if label is not None: + db_report_element.label = label + if type is not None: + db_report_element.type = type + if chart_type is not None: + db_report_element.chart_type = chart_type + if x_axis_variable is not None: + db_report_element.x_axis_variable = x_axis_variable + if y_axis_variable is not None: + db_report_element.y_axis_variable = y_axis_variable + if group_by is not None: + db_report_element.group_by = group_by + if color_by is not None: + db_report_element.color_by = color_by + if size_by is not None: + db_report_element.size_by = size_by + if markdown_content is not None: + db_report_element.markdown_content = markdown_content + if position is not None: + db_report_element.position = position + if visible is not None: + db_report_element.visible = visible + + db_report_element.updated_at = datetime.now(timezone.utc) + + session.add(db_report_element) + session.commit() + session.refresh(db_report_element) + return db_report_element + + +@report_elements_router.delete("/{report_element_id}", status_code=204) +def delete_report_element( + report_element_id: str, + session: Session = Depends(get_session), +): + """Delete a report element and its associated aggregates.""" + report_element = session.get(ReportElementTable, report_element_id) + if not report_element: + raise HTTPException(status_code=404, detail="Report element not found") + + # Delete associated aggregates (cascade should handle this, but being explicit) + statement = select(AggregateTable).where( + AggregateTable.reportelement_id == report_element_id + ) + aggregates = session.exec(statement).all() + for aggregate in aggregates: + session.delete(aggregate) + + session.delete(report_element) + session.commit() + return None \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/reports.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/reports.py new file mode 100644 index 00000000..5cf82b89 --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/reports.py @@ -0,0 +1,85 @@ +from fastapi import APIRouter, HTTPException, Depends, Query +from sqlmodel import Session, select +from policyengine.database import ReportTable +from policyengine_api_full.database import get_session +from typing import Optional +from datetime import datetime, timezone + +reports_router = APIRouter(prefix="/reports", tags=["reports"]) + + +@reports_router.get("/", response_model=list[ReportTable]) +def list_reports( + session: Session = Depends(get_session), + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=10_000), +): + """List all reports with pagination.""" + statement = select(ReportTable).offset(skip).limit(limit) + reports = session.exec(statement).all() + return reports + + +@reports_router.post("/", response_model=ReportTable, status_code=201) +def create_report( + report: ReportTable, + session: Session = Depends(get_session), +): + """Create a new report.""" + if not report.id: + import uuid + report.id = str(uuid.uuid4()) + + report.created_at = datetime.now(timezone.utc) + + session.add(report) + session.commit() + session.refresh(report) + return report + + +@reports_router.get("/{report_id}", response_model=ReportTable) +def get_report( + report_id: str, + session: Session = Depends(get_session), +): + """Get a single report by ID.""" + report = session.get(ReportTable, report_id) + if not report: + raise HTTPException(status_code=404, detail="Report not found") + return report + + +@reports_router.patch("/{report_id}", response_model=ReportTable) +def update_report( + report_id: str, + label: Optional[str] = None, + session: Session = Depends(get_session), +): + """Update a report.""" + db_report = session.get(ReportTable, report_id) + if not db_report: + raise HTTPException(status_code=404, detail="Report not found") + + if label is not None: + db_report.label = label + + session.add(db_report) + session.commit() + session.refresh(db_report) + return db_report + + +@reports_router.delete("/{report_id}", status_code=204) +def delete_report( + report_id: str, + session: Session = Depends(get_session), +): + """Delete a report and its associated report elements.""" + report = session.get(ReportTable, report_id) + if not report: + raise HTTPException(status_code=404, detail="Report not found") + + session.delete(report) + session.commit() + return None \ No newline at end of file From cc7326a1f52910e0a531cb594ce633fc80a8831c Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Mon, 29 Sep 2025 09:19:08 +0100 Subject: [PATCH 07/11] Add metadata fields --- .../routers/aggregates.py | 13 +- .../routers/baseline_variables.py | 115 ++++++++++++++---- .../routers/report_elements.py | 87 +++++++------ .../policyengine_api_full/routers/reports.py | 53 +++++++- 4 files changed, 204 insertions(+), 64 deletions(-) diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/aggregates.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/aggregates.py index d16712d1..06658e75 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/routers/aggregates.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/aggregates.py @@ -6,10 +6,16 @@ from policyengine_api_full.database import get_session from typing import Optional from datetime import datetime, timezone +from pydantic import BaseModel aggregates_router = APIRouter(prefix="/aggregates", tags=["aggregates"]) +class AggregateUpdateRequest(BaseModel): + """Request model for updating an aggregate.""" + value: Optional[float] = None + + @aggregates_router.get("/", response_model=list[AggregateTable]) def list_aggregates( session: Session = Depends(get_session), @@ -68,7 +74,7 @@ def get_aggregate( @aggregates_router.patch("/{aggregate_id}", response_model=AggregateTable) def update_aggregate( aggregate_id: str, - value: Optional[float] = None, + update_request: AggregateUpdateRequest, session: Session = Depends(get_session), ): """Update an aggregate's value.""" @@ -76,8 +82,8 @@ def update_aggregate( if not db_aggregate: raise HTTPException(status_code=404, detail="Aggregate not found") - if value is not None: - db_aggregate.value = value + if update_request.value is not None: + db_aggregate.value = update_request.value session.add(db_aggregate) session.commit() @@ -116,7 +122,6 @@ def create_bulk_aggregates( for i in range(len(created_aggregates)): created_aggregates[i] = AggregateTable.convert_to_model(created_aggregates[i], database=database) - print(created_aggregates[i].simulation) created_aggregates = Aggregate.run(created_aggregates) diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/baseline_variables.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/baseline_variables.py index c584b919..06f32d7f 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/routers/baseline_variables.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/baseline_variables.py @@ -2,45 +2,101 @@ from sqlmodel import Session, select from policyengine.database import BaselineVariableTable from policyengine_api_full.database import get_session -from typing import Optional +from typing import Optional, List +from pydantic import BaseModel router = APIRouter(prefix="/baseline-variables", tags=["baseline variables"]) -@router.get("/", response_model=list[BaselineVariableTable]) +class BaselineVariableResponse(BaseModel): + """Response model for baseline variable.""" + id: str + label: str + description: Optional[str] = None + entity: Optional[str] = None + model_id: str + model_version_id: Optional[str] = None + + +class BaselineVariableCreateRequest(BaseModel): + """Request model for creating a baseline variable.""" + label: str + model_id: str + description: Optional[str] = None + entity: Optional[str] = None + model_version_id: Optional[str] = None + + +class BaselineVariableUpdateRequest(BaseModel): + """Request model for updating a baseline variable.""" + label: Optional[str] = None + description: Optional[str] = None + entity: Optional[str] = None + + +@router.get("/", response_model=List[BaselineVariableResponse]) def list_baseline_variables( session: Session = Depends(get_session), skip: int = Query(0, ge=0), limit: int = Query(100, ge=1, le=1000), - variable_name: Optional[str] = Query(None, description="Filter by variable name"), + label: Optional[str] = Query(None, description="Filter by label"), model_id: Optional[str] = Query(None, description="Filter by model ID"), ): """List all baseline variables with pagination and optional filters.""" statement = select(BaselineVariableTable) - if variable_name: - statement = statement.where(BaselineVariableTable.variable_name == variable_name) + if label: + statement = statement.where(BaselineVariableTable.label == label) if model_id: statement = statement.where(BaselineVariableTable.model_id == model_id) statement = statement.offset(skip).limit(limit) baseline_variables = session.exec(statement).all() - return baseline_variables - - -@router.post("/", response_model=BaselineVariableTable, status_code=201) + return [ + BaselineVariableResponse( + id=bv.id, + label=bv.label, + description=bv.description, + entity=bv.entity, + model_id=bv.model_id, + model_version_id=bv.model_version_id + ) + for bv in baseline_variables + ] + + +@router.post("/", response_model=BaselineVariableResponse, status_code=201) def create_baseline_variable( - baseline_variable: BaselineVariableTable, + request: BaselineVariableCreateRequest, session: Session = Depends(get_session), ): """Create a new baseline variable.""" + import uuid + + baseline_variable = BaselineVariableTable( + id=str(uuid.uuid4()), + label=request.label, + description=request.description, + entity=request.entity, + model_id=request.model_id, + model_version_id=request.model_version_id + ) + session.add(baseline_variable) session.commit() session.refresh(baseline_variable) - return baseline_variable + return BaselineVariableResponse( + id=baseline_variable.id, + label=baseline_variable.label, + description=baseline_variable.description, + entity=baseline_variable.entity, + model_id=baseline_variable.model_id, + model_version_id=baseline_variable.model_version_id + ) -@router.get("/{baseline_variable_id}", response_model=BaselineVariableTable) + +@router.get("/{baseline_variable_id}", response_model=BaselineVariableResponse) def get_baseline_variable( baseline_variable_id: str, session: Session = Depends(get_session), @@ -49,14 +105,21 @@ def get_baseline_variable( baseline_variable = session.get(BaselineVariableTable, baseline_variable_id) if not baseline_variable: raise HTTPException(status_code=404, detail="Baseline variable not found") - return baseline_variable + + return BaselineVariableResponse( + id=baseline_variable.id, + label=baseline_variable.label, + description=baseline_variable.description, + entity=baseline_variable.entity, + model_id=baseline_variable.model_id, + model_version_id=baseline_variable.model_version_id + ) -@router.patch("/{baseline_variable_id}", response_model=BaselineVariableTable) +@router.patch("/{baseline_variable_id}", response_model=BaselineVariableResponse) def update_baseline_variable( baseline_variable_id: str, - variable_name: Optional[str] = None, - value: Optional[bytes] = None, + request: BaselineVariableUpdateRequest, session: Session = Depends(get_session), ): """Update a baseline variable.""" @@ -64,15 +127,25 @@ def update_baseline_variable( if not db_baseline_variable: raise HTTPException(status_code=404, detail="Baseline variable not found") - if variable_name is not None: - db_baseline_variable.variable_name = variable_name - if value is not None: - db_baseline_variable.value = value + if request.label is not None: + db_baseline_variable.label = request.label + if request.description is not None: + db_baseline_variable.description = request.description + if request.entity is not None: + db_baseline_variable.entity = request.entity session.add(db_baseline_variable) session.commit() session.refresh(db_baseline_variable) - return db_baseline_variable + + return BaselineVariableResponse( + id=db_baseline_variable.id, + label=db_baseline_variable.label, + description=db_baseline_variable.description, + entity=db_baseline_variable.entity, + model_id=db_baseline_variable.model_id, + model_version_id=db_baseline_variable.model_version_id + ) @router.delete("/{baseline_variable_id}", status_code=204) diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py index 4412ed62..2d86fb31 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py @@ -2,7 +2,7 @@ from sqlmodel import Session, select from policyengine.database import ReportElementTable, AggregateTable from policyengine.models import Aggregate -from policyengine_api_full.database import get_session +from policyengine_api_full.database import get_session, database from typing import Optional, List, Dict, Any from datetime import datetime, timezone from pydantic import BaseModel @@ -40,6 +40,25 @@ class ReportElementCreateRequest(BaseModel): user_id: Optional[str] = None position: Optional[int] = None visible: Optional[bool] = True + model_version_id: Optional[str] = None + report_element_metadata: Optional[Dict[str, Any]] = None + + +class ReportElementUpdateRequest(BaseModel): + """Request model for updating a report element.""" + label: Optional[str] = None + type: Optional[str] = None + chart_type: Optional[str] = None + x_axis_variable: Optional[str] = None + y_axis_variable: Optional[str] = None + group_by: Optional[str] = None + color_by: Optional[str] = None + size_by: Optional[str] = None + markdown_content: Optional[str] = None + position: Optional[int] = None + visible: Optional[bool] = None + model_version_id: Optional[str] = None + report_element_metadata: Optional[Dict[str, Any]] = None @report_elements_router.get("/", response_model=list[ReportElementTable]) @@ -101,6 +120,8 @@ def create_report_element( user_id=request.user_id, position=request.position, visible=request.visible, + model_version_id=request.model_version_id, + report_element_metadata=request.report_element_metadata, created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc) ) @@ -129,7 +150,7 @@ def create_report_element( from policyengine_api_full.database import engine # Create a database instance to handle the conversion - db = Database.from_engine(engine) + db = database simulation = simulation_table.convert_to_model(db) # Create the Aggregate model instance @@ -201,6 +222,8 @@ def create_report_element( "user_id": report_element.user_id, "position": report_element.position, "visible": report_element.visible, + "model_version_id": report_element.model_version_id, + "report_element_metadata": report_element.report_element_metadata, "created_at": report_element.created_at.isoformat(), "updated_at": report_element.updated_at.isoformat(), } @@ -246,17 +269,7 @@ def get_report_element_aggregates( @report_elements_router.patch("/{report_element_id}", response_model=ReportElementTable) def update_report_element( report_element_id: str, - label: Optional[str] = None, - type: Optional[str] = None, - chart_type: Optional[str] = None, - x_axis_variable: Optional[str] = None, - y_axis_variable: Optional[str] = None, - group_by: Optional[str] = None, - color_by: Optional[str] = None, - size_by: Optional[str] = None, - markdown_content: Optional[str] = None, - position: Optional[int] = None, - visible: Optional[bool] = None, + update_request: ReportElementUpdateRequest, session: Session = Depends(get_session), ): """Update a report element.""" @@ -264,28 +277,32 @@ def update_report_element( if not db_report_element: raise HTTPException(status_code=404, detail="Report element not found") - if label is not None: - db_report_element.label = label - if type is not None: - db_report_element.type = type - if chart_type is not None: - db_report_element.chart_type = chart_type - if x_axis_variable is not None: - db_report_element.x_axis_variable = x_axis_variable - if y_axis_variable is not None: - db_report_element.y_axis_variable = y_axis_variable - if group_by is not None: - db_report_element.group_by = group_by - if color_by is not None: - db_report_element.color_by = color_by - if size_by is not None: - db_report_element.size_by = size_by - if markdown_content is not None: - db_report_element.markdown_content = markdown_content - if position is not None: - db_report_element.position = position - if visible is not None: - db_report_element.visible = visible + if update_request.label is not None: + db_report_element.label = update_request.label + if update_request.type is not None: + db_report_element.type = update_request.type + if update_request.chart_type is not None: + db_report_element.chart_type = update_request.chart_type + if update_request.x_axis_variable is not None: + db_report_element.x_axis_variable = update_request.x_axis_variable + if update_request.y_axis_variable is not None: + db_report_element.y_axis_variable = update_request.y_axis_variable + if update_request.group_by is not None: + db_report_element.group_by = update_request.group_by + if update_request.color_by is not None: + db_report_element.color_by = update_request.color_by + if update_request.size_by is not None: + db_report_element.size_by = update_request.size_by + if update_request.markdown_content is not None: + db_report_element.markdown_content = update_request.markdown_content + if update_request.position is not None: + db_report_element.position = update_request.position + if update_request.visible is not None: + db_report_element.visible = update_request.visible + if update_request.model_version_id is not None: + db_report_element.model_version_id = update_request.model_version_id + if update_request.report_element_metadata is not None: + db_report_element.report_element_metadata = update_request.report_element_metadata db_report_element.updated_at = datetime.now(timezone.utc) diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/reports.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/reports.py index 5cf82b89..2a0df81d 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/routers/reports.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/reports.py @@ -1,13 +1,19 @@ from fastapi import APIRouter, HTTPException, Depends, Query from sqlmodel import Session, select -from policyengine.database import ReportTable +from policyengine.database import ReportTable, ReportElementTable, AggregateTable from policyengine_api_full.database import get_session from typing import Optional from datetime import datetime, timezone +from pydantic import BaseModel reports_router = APIRouter(prefix="/reports", tags=["reports"]) +class ReportUpdateRequest(BaseModel): + """Request model for updating a report.""" + label: Optional[str] = None + + @reports_router.get("/", response_model=list[ReportTable]) def list_reports( session: Session = Depends(get_session), @@ -50,10 +56,30 @@ def get_report( return report +@reports_router.get("/{report_id}/elements", response_model=list[ReportElementTable]) +def get_report_elements( + report_id: str, + session: Session = Depends(get_session), +): + """Get all report elements for a specific report.""" + # First check if the report exists + report = session.get(ReportTable, report_id) + if not report: + raise HTTPException(status_code=404, detail="Report not found") + + # Get all report elements for this report, ordered by position + statement = select(ReportElementTable).where( + ReportElementTable.report_id == report_id + ).order_by(ReportElementTable.position) + + report_elements = session.exec(statement).all() + return report_elements + + @reports_router.patch("/{report_id}", response_model=ReportTable) def update_report( report_id: str, - label: Optional[str] = None, + update_request: ReportUpdateRequest, session: Session = Depends(get_session), ): """Update a report.""" @@ -61,8 +87,8 @@ def update_report( if not db_report: raise HTTPException(status_code=404, detail="Report not found") - if label is not None: - db_report.label = label + if update_request.label is not None: + db_report.label = update_request.label session.add(db_report) session.commit() @@ -80,6 +106,25 @@ def delete_report( if not report: raise HTTPException(status_code=404, detail="Report not found") + # First delete all associated report elements + statement = select(ReportElementTable).where( + ReportElementTable.report_id == report_id + ) + report_elements = session.exec(statement).all() + + # Delete each report element (this will also handle their aggregates) + for element in report_elements: + # Delete associated aggregates first + agg_statement = select(AggregateTable).where( + AggregateTable.reportelement_id == element.id + ) + aggregates = session.exec(agg_statement).all() + for aggregate in aggregates: + session.delete(aggregate) + + session.delete(element) + + # Now delete the report itself session.delete(report) session.commit() return None \ No newline at end of file From 3f085f1b63a6516a268ca7cbb32699da494fe374 Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Mon, 29 Sep 2025 15:17:48 +0100 Subject: [PATCH 08/11] Update --- projects/policyengine-api-full/pyproject.toml | 3 +- .../src/policyengine_api_full/main.py | 6 +- .../routers/data_requests.py | 228 ++++++++++++++++++ .../routers/report_elements.py | 9 +- projects/policyengine-api-full/uv.lock | 75 ++++++ 5 files changed, 317 insertions(+), 4 deletions(-) create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/routers/data_requests.py diff --git a/projects/policyengine-api-full/pyproject.toml b/projects/policyengine-api-full/pyproject.toml index b7b78706..fd61c650 100644 --- a/projects/policyengine-api-full/pyproject.toml +++ b/projects/policyengine-api-full/pyproject.toml @@ -18,7 +18,8 @@ dependencies = [ "opentelemetry-instrumentation-fastapi (>=0.51b0,<0.52)", "policyengine-fastapi", "policyengine[uk,us]>=3.0.0", - "psycopg2-binary>=2.9.0" + "psycopg2-binary>=2.9.0", + "anthropic>=0.68.1", ] [tool.hatch.build.targets.wheel] diff --git a/projects/policyengine-api-full/src/policyengine_api_full/main.py b/projects/policyengine-api-full/src/policyengine_api_full/main.py index a8d0ca9e..9d8ed2f1 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/main.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/main.py @@ -5,6 +5,8 @@ from policyengine_fastapi.exit import exit from .settings import get_settings import logging +import dotenv +dotenv.load_dotenv() # Import routers from .routers import ( @@ -18,7 +20,8 @@ baseline_variables, reports, report_elements, - aggregates + aggregates, + data_requests ) # Import database setup @@ -89,6 +92,7 @@ async def lifespan(app: FastAPI): app.include_router(reports.reports_router) app.include_router(report_elements.report_elements_router) app.include_router(aggregates.aggregates_router) +app.include_router(data_requests.router) # Health check endpoint @app.get("/health") diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/data_requests.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/data_requests.py new file mode 100644 index 00000000..aff6050f --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/data_requests.py @@ -0,0 +1,228 @@ +""" +Data request parsing endpoint using LLM +""" +from fastapi import APIRouter, HTTPException, Depends +from sqlmodel import Session, select +from typing import List, Optional, Dict, Any +from pydantic import BaseModel +import anthropic +import json +import os +from policyengine_api_full.database import get_session +from policyengine.database import BaselineVariableTable, SimulationTable, PolicyTable, DatasetTable + +router = APIRouter(prefix="/data-requests", tags=["data requests"]) + + +class DataRequest(BaseModel): + description: str + report_id: str + + +class ParsedAggregate(BaseModel): + simulation_id: str + entity: str + variable_name: str + aggregate_function: str + year: Optional[int] = None + filter_variable_name: Optional[str] = None + filter_variable_value: Optional[str] = None + filter_variable_leq: Optional[float] = None + filter_variable_geq: Optional[float] = None + + +class DataRequestResponse(BaseModel): + aggregates: List[ParsedAggregate] + chart_type: str + x_axis_variable: Optional[str] = None + y_axis_variable: Optional[str] = None + explanation: str + + +@router.post("/parse", response_model=DataRequestResponse) +async def parse_data_request( + request: DataRequest, + db: Session = Depends(get_session) +): + """Parse a natural language data request into aggregates configuration""" + + # Get API key from environment + api_key = os.getenv("ANTHROPIC_API_KEY") + if not api_key: + raise HTTPException(status_code=500, detail="Anthropic API key not configured") + + # Fetch context data + variables = db.exec(select(BaselineVariableTable).limit(1000)).all() + simulations = db.exec(select(SimulationTable).limit(100)).all() + policies = db.exec(select(PolicyTable).limit(100)).all() + datasets = db.exec(select(DatasetTable).limit(100)).all() + + # Create lookup maps + policy_map = {p.id: p for p in policies} + dataset_map = {d.id: d for d in datasets} + + # Build context for LLM + variables_context = [ + { + "name": v.id, + "label": v.label, + "entity": v.entity, + "description": v.description + } + for v in variables[:100] # Limit to avoid token limits + ] + + simulations_context = [ + { + "id": s.id, + "policy_label": policy_map.get(s.policy_id).name if s.policy_id and s.policy_id in policy_map else "Baseline", + "dataset_label": dataset_map.get(s.dataset_id).name if s.dataset_id and s.dataset_id in dataset_map else "Default" + } + for s in simulations + ] + + # Create prompt for Claude + system_prompt = """You are a JSON API that converts natural language requests into structured aggregate queries. + +CRITICAL RULES: +1. Return ONLY raw JSON - no markdown, no code blocks, no explanations outside the JSON +2. The JSON must be valid and parseable by Python's json.loads() +3. Never use trailing commas in JSON +4. All string values must use double quotes +5. null values must be lowercase "null" +6. Boolean values must be lowercase "true" or "false" + +You have access to: +- Variables (metrics that can be aggregated) +- Simulations (different policy scenarios to compare) +- Aggregation functions: mean, sum, median, count, min, max + +Create ONE aggregate object for EACH combination of simulation and variable. +Example: comparing 2 simulations for 1 variable = 2 aggregate objects. + +Required JSON structure (return EXACTLY this format): +{ + "aggregates": [ + { + "simulation_id": "string", + "entity": "string", + "variable_name": "string", + "aggregate_function": "string", + "year": null, + "filter_variable_name": null, + "filter_variable_leq": null, + "filter_variable_geq": null + } + ], + "chart_type": "table", + "x_axis_variable": null, + "y_axis_variable": null, + "explanation": "string" +} + +Remember: Return ONLY the JSON object. No text before or after.""" + + user_prompt = f"""User request: {request.description} + +Available variables (first 100): +{json.dumps(variables_context, indent=2)} + +Available simulations: +{json.dumps(simulations_context, indent=2)} + +Parse this request into aggregate queries. If the user mentions comparing policies or simulations, include multiple simulation_ids. If they mention income bands or other ranges, use filter variables. + +REMINDER: Return ONLY valid JSON. No text before or after. Ensure all JSON syntax is correct: +- No trailing commas +- All strings in double quotes +- Proper null values (not None or undefined) +- Valid array/object structure""" + + # Call Claude + client = anthropic.Anthropic(api_key=api_key) + + try: + message = client.messages.create( + model="claude-3-haiku-20240307", + max_tokens=2000, + temperature=0, + system=system_prompt, + messages=[ + {"role": "user", "content": user_prompt} + ] + ) + + # Parse the response - should be pure JSON + response_text = message.content[0].text.strip() + print(f"LLM Response: {response_text[:1000]}...") # Debug logging + + try: + # Clean up common issues before parsing + # Remove any potential Unicode BOM + if response_text.startswith('\ufeff'): + response_text = response_text[1:] + + # Remove any leading/trailing whitespace + response_text = response_text.strip() + + # Try to parse as pure JSON + parsed_response = json.loads(response_text) + except json.JSONDecodeError as e: + print(f"JSON decode error: {e}") + print(f"Failed text: {response_text[:500]}") + + # Try to extract JSON from the response + import re + # Look for JSON object pattern + json_match = re.search(r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}', response_text, re.DOTALL) + if json_match: + try: + parsed_response = json.loads(json_match.group()) + except json.JSONDecodeError as e2: + print(f"Failed to parse extracted JSON: {e2}") + raise ValueError(f"Failed to parse LLM response: {str(e)}") + else: + raise ValueError(f"Failed to parse LLM response: {str(e)}") + + print(f"Parsed JSON: {json.dumps(parsed_response, indent=2)[:500]}...") # Debug logging + + # Convert to response model - validate each aggregate + aggregates = [] + for agg_data in parsed_response.get("aggregates", []): + # Handle cases where LLM returns lists instead of individual items + sim_ids = agg_data.get("simulation_id", []) + if not isinstance(sim_ids, list): + sim_ids = [sim_ids] + + var_names = agg_data.get("variable_name", []) + if not isinstance(var_names, list): + var_names = [var_names] + + # Create one aggregate for each combination + for sim_id in sim_ids: + for var_name in var_names: + agg_copy = agg_data.copy() + agg_copy["simulation_id"] = sim_id + agg_copy["variable_name"] = var_name + # Ensure entity has a default + if not agg_copy.get("entity"): + agg_copy["entity"] = "person" + # Ensure aggregate_function has a default + if not agg_copy.get("aggregate_function"): + agg_copy["aggregate_function"] = "mean" + aggregates.append(ParsedAggregate(**agg_copy)) + + return DataRequestResponse( + aggregates=aggregates, + chart_type=parsed_response.get("chart_type", "table"), + x_axis_variable=parsed_response.get("x_axis_variable"), + y_axis_variable=parsed_response.get("y_axis_variable"), + explanation=parsed_response.get("explanation", "Data visualization based on your request") + ) + + except anthropic.APIError as e: + raise HTTPException(status_code=500, detail=f"Anthropic API error: {str(e)}") + except json.JSONDecodeError as e: + raise HTTPException(status_code=500, detail=f"Failed to parse LLM response: {str(e)}") + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error processing request: {str(e)}") \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py index 2d86fb31..c3691ce7 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py @@ -133,10 +133,12 @@ def create_report_element( # Process aggregates if provided if request.data_type == "Aggregate" and request.data: + print(f"Processing {len(request.data)} aggregates for report element {report_element.id}") # Convert input aggregates to Aggregate model instances aggregate_models = [] - for agg_input in request.data: + for i, agg_input in enumerate(request.data): + print(f"Processing aggregate {i+1}/{len(request.data)}: {agg_input.variable_name} for simulation {agg_input.simulation_id}") # Get the simulation simulation_table = session.get(SimulationTable, agg_input.simulation_id) if not simulation_table: @@ -169,10 +171,13 @@ def create_report_element( aggregate_models.append(agg) # Run Aggregate.run to compute values + print(f"Running Aggregate.run on {len(aggregate_models)} models") computed_models = Aggregate.run(aggregate_models) + print(f"Aggregate.run returned {len(computed_models)} computed models") # Save the computed aggregates to the database - for agg_model in computed_models: + for j, agg_model in enumerate(computed_models): + print(f"Saving aggregate {j+1}/{len(computed_models)}: {agg_model.variable_name} = {agg_model.value}") agg_table = AggregateTable( id=agg_model.id, simulation_id=agg_model.simulation.id if agg_model.simulation else None, diff --git a/projects/policyengine-api-full/uv.lock b/projects/policyengine-api-full/uv.lock index a0c1c20b..c0f12e77 100644 --- a/projects/policyengine-api-full/uv.lock +++ b/projects/policyengine-api-full/uv.lock @@ -25,6 +25,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] +[[package]] +name = "anthropic" +version = "0.68.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "docstring-parser" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/b9/5e736a924dabe0473b212597ae83a124e09818fac672998bb5960a64e9b1/anthropic-0.68.1.tar.gz", hash = "sha256:5fc01019dfffbc39e87a24f4d7cfe34e83dda4fff7b5371f33639f982f0c8dba", size = 471965, upload-time = "2025-09-26T16:27:30.547Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/5c/e871e40ba11338502569a3f5cb176650f99f7b4ecf915f4d38fe2b8cff37/anthropic-0.68.1-py3-none-any.whl", hash = "sha256:40fc545c4781ebc45d8e7849240c4001f40d61d18a17e5363c0acb10afefc33b", size = 325214, upload-time = "2025-09-26T16:27:29.049Z" }, +] + [[package]] name = "anyio" version = "4.11.0" @@ -313,6 +332,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998, upload-time = "2025-01-27T10:46:09.186Z" }, ] +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, +] + [[package]] name = "dnspython" version = "2.8.0" @@ -322,6 +350,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, ] +[[package]] +name = "docstring-parser" +version = "0.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, +] + [[package]] name = "dpath" version = "2.2.0" @@ -834,6 +871,42 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] +[[package]] +name = "jiter" +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/c0/a3bb4cc13aced219dd18191ea66e874266bd8aa7b96744e495e1c733aa2d/jiter-0.11.0.tar.gz", hash = "sha256:1d9637eaf8c1d6a63d6562f2a6e5ab3af946c66037eb1b894e8fad75422266e4", size = 167094, upload-time = "2025-09-15T09:20:38.212Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/c4/d530e514d0f4f29b2b68145e7b389cbc7cac7f9c8c23df43b04d3d10fa3e/jiter-0.11.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:4441a91b80a80249f9a6452c14b2c24708f139f64de959943dfeaa6cb915e8eb", size = 305021, upload-time = "2025-09-15T09:19:43.523Z" }, + { url = "https://files.pythonhosted.org/packages/7a/77/796a19c567c5734cbfc736a6f987affc0d5f240af8e12063c0fb93990ffa/jiter-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ff85fc6d2a431251ad82dbd1ea953affb5a60376b62e7d6809c5cd058bb39471", size = 314384, upload-time = "2025-09-15T09:19:44.849Z" }, + { url = "https://files.pythonhosted.org/packages/14/9c/824334de0b037b91b6f3fa9fe5a191c83977c7ec4abe17795d3cb6d174cf/jiter-0.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5e86126d64706fd28dfc46f910d496923c6f95b395138c02d0e252947f452bd", size = 337389, upload-time = "2025-09-15T09:19:46.094Z" }, + { url = "https://files.pythonhosted.org/packages/a2/95/ed4feab69e6cf9b2176ea29d4ef9d01a01db210a3a2c8a31a44ecdc68c38/jiter-0.11.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ad8bd82165961867a10f52010590ce0b7a8c53da5ddd8bbb62fef68c181b921", size = 360519, upload-time = "2025-09-15T09:19:47.494Z" }, + { url = "https://files.pythonhosted.org/packages/b5/0c/2ad00f38d3e583caba3909d95b7da1c3a7cd82c0aa81ff4317a8016fb581/jiter-0.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b42c2cd74273455ce439fd9528db0c6e84b5623cb74572305bdd9f2f2961d3df", size = 487198, upload-time = "2025-09-15T09:19:49.116Z" }, + { url = "https://files.pythonhosted.org/packages/ea/8b/919b64cf3499b79bdfba6036da7b0cac5d62d5c75a28fb45bad7819e22f0/jiter-0.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0062dab98172dd0599fcdbf90214d0dcde070b1ff38a00cc1b90e111f071982", size = 377835, upload-time = "2025-09-15T09:19:50.468Z" }, + { url = "https://files.pythonhosted.org/packages/29/7f/8ebe15b6e0a8026b0d286c083b553779b4dd63db35b43a3f171b544de91d/jiter-0.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb948402821bc76d1f6ef0f9e19b816f9b09f8577844ba7140f0b6afe994bc64", size = 347655, upload-time = "2025-09-15T09:19:51.726Z" }, + { url = "https://files.pythonhosted.org/packages/8e/64/332127cef7e94ac75719dda07b9a472af6158ba819088d87f17f3226a769/jiter-0.11.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:25a5b1110cca7329fd0daf5060faa1234be5c11e988948e4f1a1923b6a457fe1", size = 386135, upload-time = "2025-09-15T09:19:53.075Z" }, + { url = "https://files.pythonhosted.org/packages/20/c8/557b63527442f84c14774159948262a9d4fabb0d61166f11568f22fc60d2/jiter-0.11.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:bf11807e802a214daf6c485037778843fadd3e2ec29377ae17e0706ec1a25758", size = 516063, upload-time = "2025-09-15T09:19:54.447Z" }, + { url = "https://files.pythonhosted.org/packages/86/13/4164c819df4a43cdc8047f9a42880f0ceef5afeb22e8b9675c0528ebdccd/jiter-0.11.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:dbb57da40631c267861dd0090461222060960012d70fd6e4c799b0f62d0ba166", size = 508139, upload-time = "2025-09-15T09:19:55.764Z" }, + { url = "https://files.pythonhosted.org/packages/fa/70/6e06929b401b331d41ddb4afb9f91cd1168218e3371972f0afa51c9f3c31/jiter-0.11.0-cp313-cp313-win32.whl", hash = "sha256:8e36924dad32c48d3c5e188d169e71dc6e84d6cb8dedefea089de5739d1d2f80", size = 206369, upload-time = "2025-09-15T09:19:57.048Z" }, + { url = "https://files.pythonhosted.org/packages/f4/0d/8185b8e15de6dce24f6afae63380e16377dd75686d56007baa4f29723ea1/jiter-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:452d13e4fd59698408087235259cebe67d9d49173b4dacb3e8d35ce4acf385d6", size = 202538, upload-time = "2025-09-15T09:19:58.35Z" }, + { url = "https://files.pythonhosted.org/packages/13/3a/d61707803260d59520721fa326babfae25e9573a88d8b7b9cb54c5423a59/jiter-0.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:089f9df9f69532d1339e83142438668f52c97cd22ee2d1195551c2b1a9e6cf33", size = 313737, upload-time = "2025-09-15T09:19:59.638Z" }, + { url = "https://files.pythonhosted.org/packages/cd/cc/c9f0eec5d00f2a1da89f6bdfac12b8afdf8d5ad974184863c75060026457/jiter-0.11.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29ed1fe69a8c69bf0f2a962d8d706c7b89b50f1332cd6b9fbda014f60bd03a03", size = 346183, upload-time = "2025-09-15T09:20:01.442Z" }, + { url = "https://files.pythonhosted.org/packages/a6/87/fc632776344e7aabbab05a95a0075476f418c5d29ab0f2eec672b7a1f0ac/jiter-0.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a4d71d7ea6ea8786291423fe209acf6f8d398a0759d03e7f24094acb8ab686ba", size = 204225, upload-time = "2025-09-15T09:20:03.102Z" }, + { url = "https://files.pythonhosted.org/packages/ee/3b/e7f45be7d3969bdf2e3cd4b816a7a1d272507cd0edd2d6dc4b07514f2d9a/jiter-0.11.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9a6dff27eca70930bdbe4cbb7c1a4ba8526e13b63dc808c0670083d2d51a4a72", size = 304414, upload-time = "2025-09-15T09:20:04.357Z" }, + { url = "https://files.pythonhosted.org/packages/06/32/13e8e0d152631fcc1907ceb4943711471be70496d14888ec6e92034e2caf/jiter-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ae2a7593a62132c7d4c2abbee80bbbb94fdc6d157e2c6cc966250c564ef774", size = 314223, upload-time = "2025-09-15T09:20:05.631Z" }, + { url = "https://files.pythonhosted.org/packages/0c/7e/abedd5b5a20ca083f778d96bba0d2366567fcecb0e6e34ff42640d5d7a18/jiter-0.11.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b13a431dba4b059e9e43019d3022346d009baf5066c24dcdea321a303cde9f0", size = 337306, upload-time = "2025-09-15T09:20:06.917Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e2/30d59bdc1204c86aa975ec72c48c482fee6633120ee9c3ab755e4dfefea8/jiter-0.11.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:af62e84ca3889604ebb645df3b0a3f3bcf6b92babbff642bd214616f57abb93a", size = 360565, upload-time = "2025-09-15T09:20:08.283Z" }, + { url = "https://files.pythonhosted.org/packages/fe/88/567288e0d2ed9fa8f7a3b425fdaf2cb82b998633c24fe0d98f5417321aa8/jiter-0.11.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6f3b32bb723246e6b351aecace52aba78adb8eeb4b2391630322dc30ff6c773", size = 486465, upload-time = "2025-09-15T09:20:09.613Z" }, + { url = "https://files.pythonhosted.org/packages/18/6e/7b72d09273214cadd15970e91dd5ed9634bee605176107db21e1e4205eb1/jiter-0.11.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:adcab442f4a099a358a7f562eaa54ed6456fb866e922c6545a717be51dbed7d7", size = 377581, upload-time = "2025-09-15T09:20:10.884Z" }, + { url = "https://files.pythonhosted.org/packages/58/52/4db456319f9d14deed325f70102577492e9d7e87cf7097bda9769a1fcacb/jiter-0.11.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9967c2ab338ee2b2c0102fd379ec2693c496abf71ffd47e4d791d1f593b68e2", size = 347102, upload-time = "2025-09-15T09:20:12.175Z" }, + { url = "https://files.pythonhosted.org/packages/ce/b4/433d5703c38b26083aec7a733eb5be96f9c6085d0e270a87ca6482cbf049/jiter-0.11.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e7d0bed3b187af8b47a981d9742ddfc1d9b252a7235471ad6078e7e4e5fe75c2", size = 386477, upload-time = "2025-09-15T09:20:13.428Z" }, + { url = "https://files.pythonhosted.org/packages/c8/7a/a60bfd9c55b55b07c5c441c5085f06420b6d493ce9db28d069cc5b45d9f3/jiter-0.11.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:f6fe0283e903ebc55f1a6cc569b8c1f3bf4abd026fed85e3ff8598a9e6f982f0", size = 516004, upload-time = "2025-09-15T09:20:14.848Z" }, + { url = "https://files.pythonhosted.org/packages/2e/46/f8363e5ecc179b4ed0ca6cb0a6d3bfc266078578c71ff30642ea2ce2f203/jiter-0.11.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:4ee5821e3d66606b29ae5b497230b304f1376f38137d69e35f8d2bd5f310ff73", size = 507855, upload-time = "2025-09-15T09:20:16.176Z" }, + { url = "https://files.pythonhosted.org/packages/90/33/396083357d51d7ff0f9805852c288af47480d30dd31d8abc74909b020761/jiter-0.11.0-cp314-cp314-win32.whl", hash = "sha256:c2d13ba7567ca8799f17c76ed56b1d49be30df996eb7fa33e46b62800562a5e2", size = 205802, upload-time = "2025-09-15T09:20:17.661Z" }, + { url = "https://files.pythonhosted.org/packages/e7/ab/eb06ca556b2551d41de7d03bf2ee24285fa3d0c58c5f8d95c64c9c3281b1/jiter-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:fb4790497369d134a07fc763cc88888c46f734abdd66f9fdf7865038bf3a8f40", size = 313405, upload-time = "2025-09-15T09:20:18.918Z" }, + { url = "https://files.pythonhosted.org/packages/af/22/7ab7b4ec3a1c1f03aef376af11d23b05abcca3fb31fbca1e7557053b1ba2/jiter-0.11.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e2bbf24f16ba5ad4441a9845e40e4ea0cb9eed00e76ba94050664ef53ef4406", size = 347102, upload-time = "2025-09-15T09:20:20.16Z" }, +] + [[package]] name = "jsonpickle" version = "4.1.1" @@ -1405,6 +1478,7 @@ name = "policyengine-api-full" version = "0.1.0" source = { editable = "." } dependencies = [ + { name = "anthropic" }, { name = "opentelemetry-instrumentation-fastapi" }, { name = "opentelemetry-instrumentation-sqlalchemy" }, { name = "policyengine", extra = ["uk", "us"] }, @@ -1427,6 +1501,7 @@ test = [ [package.metadata] requires-dist = [ + { name = "anthropic", specifier = ">=0.68.1" }, { name = "black", marker = "extra == 'build'", specifier = ">=25.1.0" }, { name = "openapi-python-client", marker = "extra == 'build'", specifier = ">=0.21.6" }, { name = "opentelemetry-instrumentation-fastapi", specifier = ">=0.51b0,<0.52" }, From 8dd3edf283296a5ea2868cd46f02f4e189fe605e Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Mon, 29 Sep 2025 18:24:17 +0100 Subject: [PATCH 09/11] Update --- .../scripts/add_quantile_fields.py | 73 +++++ .../scripts/create_tables.py | 46 ++++ .../scripts/init_database.py | 244 +++++++++++++++++ .../scripts/init_db_simple.py | 87 ++++++ .../src/policyengine_api_full/main.py | 5 +- .../routers/aggregate_changes.py | 166 ++++++++++++ .../routers/aggregates.py | 15 +- .../routers/data_requests.py | 123 +++++++-- .../routers/report_elements.py | 251 ++++++++++++++---- 9 files changed, 936 insertions(+), 74 deletions(-) create mode 100644 projects/policyengine-api-full/scripts/add_quantile_fields.py create mode 100644 projects/policyengine-api-full/scripts/create_tables.py create mode 100644 projects/policyengine-api-full/scripts/init_database.py create mode 100644 projects/policyengine-api-full/scripts/init_db_simple.py create mode 100644 projects/policyengine-api-full/src/policyengine_api_full/routers/aggregate_changes.py diff --git a/projects/policyengine-api-full/scripts/add_quantile_fields.py b/projects/policyengine-api-full/scripts/add_quantile_fields.py new file mode 100644 index 00000000..b762d2c9 --- /dev/null +++ b/projects/policyengine-api-full/scripts/add_quantile_fields.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +""" +Add quantile filter fields to aggregates and aggregate_changes tables. +""" + +import os +from sqlmodel import Session, text +from policyengine.database import Database + +def add_quantile_fields(database_url: str = None): + """ + Add quantile filter fields to existing tables. + + Args: + database_url: PostgreSQL connection string. If None, uses DATABASE_URL env var + """ + # Get database URL + if database_url is None: + database_url = os.environ.get( + "DATABASE_URL", + "postgresql://postgres:postgres@127.0.0.1:54322/postgres" + ) + + print(f"Connecting to database: {database_url}") + database = Database(database_url) + + with Session(database.engine) as session: + # Add quantile fields to aggregates table + print("Adding quantile fields to aggregates table...") + try: + session.exec(text(""" + ALTER TABLE aggregates + ADD COLUMN IF NOT EXISTS filter_variable_quantile_leq DOUBLE PRECISION, + ADD COLUMN IF NOT EXISTS filter_variable_quantile_geq DOUBLE PRECISION, + ADD COLUMN IF NOT EXISTS filter_variable_quantile_value VARCHAR; + """)) + session.commit() + print("✓ Added quantile fields to aggregates table") + except Exception as e: + print(f"Error adding fields to aggregates: {e}") + session.rollback() + + # Add quantile fields to aggregate_changes table + print("Adding quantile fields to aggregate_changes table...") + try: + session.exec(text(""" + ALTER TABLE aggregate_changes + ADD COLUMN IF NOT EXISTS filter_variable_quantile_leq DOUBLE PRECISION, + ADD COLUMN IF NOT EXISTS filter_variable_quantile_geq DOUBLE PRECISION, + ADD COLUMN IF NOT EXISTS filter_variable_quantile_value VARCHAR; + """)) + session.commit() + print("✓ Added quantile fields to aggregate_changes table") + except Exception as e: + print(f"Error adding fields to aggregate_changes: {e}") + session.rollback() + + print("\nMigration complete!") + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="Add quantile fields to PolicyEngine database") + parser.add_argument( + "--database-url", + help="PostgreSQL connection string", + default="postgresql://postgres:postgres@127.0.0.1:54322/postgres" + ) + + args = parser.parse_args() + + add_quantile_fields(database_url=args.database_url) \ No newline at end of file diff --git a/projects/policyengine-api-full/scripts/create_tables.py b/projects/policyengine-api-full/scripts/create_tables.py new file mode 100644 index 00000000..a7ded239 --- /dev/null +++ b/projects/policyengine-api-full/scripts/create_tables.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +""" +Create database tables for PolicyEngine API. +""" + +import os +from policyengine.database import Database +from sqlmodel import SQLModel + +def create_tables(database_url: str = None): + """ + Create all database tables. + + Args: + database_url: PostgreSQL connection string. If None, uses DATABASE_URL env var + """ + # Get database URL + if database_url is None: + database_url = os.environ.get( + "DATABASE_URL", + "postgresql://postgres:postgres@127.0.0.1:54322/postgres" + ) + + print(f"Connecting to database: {database_url}") + database = Database(database_url) + + print("Creating all tables...") + # This will create all tables defined in SQLModel + SQLModel.metadata.create_all(database.engine) + + print("Tables created successfully!") + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="Create PolicyEngine database tables") + parser.add_argument( + "--database-url", + help="PostgreSQL connection string", + default="postgresql://postgres:postgres@127.0.0.1:54322/postgres" + ) + + args = parser.parse_args() + + create_tables(database_url=args.database_url) \ No newline at end of file diff --git a/projects/policyengine-api-full/scripts/init_database.py b/projects/policyengine-api-full/scripts/init_database.py new file mode 100644 index 00000000..ba019bbf --- /dev/null +++ b/projects/policyengine-api-full/scripts/init_database.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python3 +""" +Database initialization script for PolicyEngine API. +Creates initial data including models, simulations, and sample aggregates. +""" + +import os +from datetime import datetime +from policyengine.database import Database +from policyengine.models import ( + Aggregate, + Simulation, + Parameter, + ParameterValue, + Policy, + policyengine_uk_latest_version, + policyengine_uk_model, + policyengine_us_latest_version, + policyengine_us_model, +) +from policyengine.utils.datasets import create_uk_dataset, create_us_dataset + + +def init_database(database_url: str = None, reset: bool = False): + """ + Initialize the database with models and sample data. + + Args: + database_url: PostgreSQL connection string. If None, uses DATABASE_URL env var + reset: Whether to reset (drop and recreate) all tables + """ + # Get database URL + if database_url is None: + database_url = os.environ.get( + "DATABASE_URL", + "postgresql://postgres:postgres@127.0.0.1:5432/postgres" + ) + + print(f"Connecting to database: {database_url}") + database = Database(database_url) + + if reset: + print("Resetting database tables...") + database.reset() + + # Register model versions + print("Registering UK model version...") + database.register_model_version(policyengine_uk_latest_version) + + print("Registering US model version...") + database.register_model_version(policyengine_us_latest_version) + + # Create datasets + print("Creating UK dataset...") + uk_dataset = create_uk_dataset() + database.set(uk_dataset) + + print("Creating US dataset...") + us_dataset = create_us_dataset() + database.set(us_dataset) + + # Create baseline simulations + print("Creating UK baseline simulation...") + uk_baseline = Simulation( + dataset=uk_dataset, + model=policyengine_uk_model, + model_version=policyengine_uk_latest_version, + label="UK Baseline 2024" + ) + uk_baseline.run() + database.set(uk_baseline) + + print("Creating US baseline simulation...") + us_baseline = Simulation( + dataset=us_dataset, + model=policyengine_us_model, + model_version=policyengine_us_latest_version, + label="US Baseline 2024" + ) + us_baseline.run() + database.set(us_baseline) + + # Create sample UK policy reform + print("Creating UK sample policy...") + personal_allowance = Parameter( + id="gov.hmrc.income_tax.allowances.personal_allowance.amount", + model=policyengine_uk_model, + ) + + personal_allowance_value = ParameterValue( + parameter=personal_allowance, + start_date=datetime(2024, 4, 1), + value=20000, + ) + + uk_policy = Policy( + name="Increase personal allowance to £20,000", + description="A policy to increase the personal allowance for income tax to £20,000.", + parameter_values=[personal_allowance_value], + ) + database.set(uk_policy) + for pv in uk_policy.parameter_values: + database.set(pv) + + # Create UK reform simulation + print("Creating UK reform simulation...") + uk_reform = Simulation( + dataset=uk_dataset, + model=policyengine_uk_model, + model_version=policyengine_uk_latest_version, + policy=uk_policy, + label="UK Reform: £20k Personal Allowance" + ) + uk_reform.run() + database.set(uk_reform) + + # Create sample aggregates for UK + print("Creating UK sample aggregates...") + income_ranges = [0, 20000, 40000, 60000, 80000, 100000] + + uk_aggregates = [] + for i in range(len(income_ranges) - 1): + # Baseline aggregate + agg = Aggregate( + entity="household", + variable_name="household_net_income", + aggregate_function="count", + filter_variable_name="household_net_income", + filter_variable_geq=income_ranges[i], + filter_variable_leq=income_ranges[i + 1], + simulation=uk_baseline, + ) + uk_aggregates.append(agg) + + # Reform aggregate + agg_reform = Aggregate( + entity="household", + variable_name="household_net_income", + aggregate_function="count", + filter_variable_name="household_net_income", + filter_variable_geq=income_ranges[i], + filter_variable_leq=income_ranges[i + 1], + simulation=uk_reform, + ) + uk_aggregates.append(agg_reform) + + # Run aggregates + uk_aggregates = Aggregate.run(uk_aggregates) + for agg in uk_aggregates: + database.set(agg) + + # Create sample US policy reform + print("Creating US sample policy...") + standard_deduction = Parameter( + id="gov.irs.deductions.standard.amount.single", + model=policyengine_us_model, + ) + + standard_deduction_value = ParameterValue( + parameter=standard_deduction, + start_date=datetime(2024, 1, 1), + value=15000, + ) + + us_policy = Policy( + name="Increase standard deduction to $15,000", + description="A policy to increase the standard deduction for single filers to $15,000.", + parameter_values=[standard_deduction_value], + ) + database.set(us_policy) + for pv in us_policy.parameter_values: + database.set(pv) + + # Create US reform simulation + print("Creating US reform simulation...") + us_reform = Simulation( + dataset=us_dataset, + model=policyengine_us_model, + model_version=policyengine_us_latest_version, + policy=us_policy, + label="US Reform: $15k Standard Deduction" + ) + us_reform.run() + database.set(us_reform) + + # Create sample aggregates for US + print("Creating US sample aggregates...") + us_income_ranges = [0, 25000, 50000, 75000, 100000, 150000] + + us_aggregates = [] + for i in range(len(us_income_ranges) - 1): + # Baseline aggregate + agg = Aggregate( + entity="tax_unit", + variable_name="household_net_income", + aggregate_function="count", + filter_variable_name="household_net_income", + filter_variable_geq=us_income_ranges[i], + filter_variable_leq=us_income_ranges[i + 1], + simulation=us_baseline, + ) + us_aggregates.append(agg) + + # Reform aggregate + agg_reform = Aggregate( + entity="tax_unit", + variable_name="household_net_income", + aggregate_function="count", + filter_variable_name="household_net_income", + filter_variable_geq=us_income_ranges[i], + filter_variable_leq=us_income_ranges[i + 1], + simulation=us_reform, + ) + us_aggregates.append(agg_reform) + + # Run aggregates + us_aggregates = Aggregate.run(us_aggregates) + for agg in us_aggregates: + database.set(agg) + + print("\nDatabase initialization complete!") + print(f"Created {len(uk_aggregates) + len(us_aggregates)} aggregates") + print(f"Created 4 simulations (2 UK, 2 US)") + print(f"Created 2 policies (1 UK, 1 US)") + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="Initialize PolicyEngine database") + parser.add_argument( + "--database-url", + help="PostgreSQL connection string", + default=None + ) + parser.add_argument( + "--reset", + action="store_true", + help="Reset (drop and recreate) all tables" + ) + + args = parser.parse_args() + + init_database(database_url=args.database_url, reset=args.reset) \ No newline at end of file diff --git a/projects/policyengine-api-full/scripts/init_db_simple.py b/projects/policyengine-api-full/scripts/init_db_simple.py new file mode 100644 index 00000000..d316be54 --- /dev/null +++ b/projects/policyengine-api-full/scripts/init_db_simple.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +""" +Simple database initialization script for PolicyEngine API. +Creates initial data without running full simulations. +""" + +import os +from datetime import datetime +from policyengine.database import Database +from policyengine.models import ( + Parameter, + ParameterValue, + Policy, + policyengine_uk_latest_version, + policyengine_uk_model, +) + + +def init_database_simple(database_url: str = None): + """ + Initialize the database with models and sample data. + + Args: + database_url: PostgreSQL connection string. If None, uses DATABASE_URL env var + """ + # Get database URL + if database_url is None: + database_url = os.environ.get( + "DATABASE_URL", + "postgresql://postgres:postgres@127.0.0.1:54322/postgres" + ) + + print(f"Connecting to database: {database_url}") + database = Database(database_url) + + try: + # Register model versions (this is fast) + print("Registering UK model version...") + database.register_model_version(policyengine_uk_latest_version) + print("✓ UK model registered") + + # Create a sample UK policy + print("Creating sample UK policy...") + personal_allowance = Parameter( + id="gov.hmrc.income_tax.allowances.personal_allowance.amount", + model=policyengine_uk_model, + ) + + personal_allowance_value = ParameterValue( + parameter=personal_allowance, + start_date=datetime(2024, 4, 1), + value=20000, + ) + + uk_policy = Policy( + name="Increase personal allowance to £20,000", + description="A policy to increase the personal allowance for income tax to £20,000.", + parameter_values=[personal_allowance_value], + ) + + database.set(uk_policy) + for pv in uk_policy.parameter_values: + database.set(pv) + print("✓ Sample policy created") + + print("\n✓ Database initialization complete!") + print("Note: To create simulations and aggregates, use the API endpoints.") + + except Exception as e: + print(f"\n✗ Error during initialization: {e}") + import traceback + traceback.print_exc() + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="Initialize PolicyEngine database (simple)") + parser.add_argument( + "--database-url", + help="PostgreSQL connection string", + default="postgresql://postgres:postgres@127.0.0.1:54322/postgres" + ) + + args = parser.parse_args() + + init_database_simple(database_url=args.database_url) \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/main.py b/projects/policyengine-api-full/src/policyengine_api_full/main.py index 9d8ed2f1..add076fc 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/main.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/main.py @@ -21,6 +21,7 @@ reports, report_elements, aggregates, + aggregate_changes, data_requests ) @@ -42,7 +43,8 @@ ModelVersionTable, ReportTable, ReportElementTable, - AggregateTable + AggregateTable, + AggregateChangeTable ) """ @@ -92,6 +94,7 @@ async def lifespan(app: FastAPI): app.include_router(reports.reports_router) app.include_router(report_elements.report_elements_router) app.include_router(aggregates.aggregates_router) +app.include_router(aggregate_changes.aggregate_changes_router) app.include_router(data_requests.router) # Health check endpoint diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/aggregate_changes.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/aggregate_changes.py new file mode 100644 index 00000000..bdf87788 --- /dev/null +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/aggregate_changes.py @@ -0,0 +1,166 @@ +from fastapi import APIRouter, HTTPException, Depends, Query +from sqlmodel import Session, select +from policyengine.database import AggregateChangeTable +from policyengine_api_full.database import database +from policyengine.models import AggregateChange +from policyengine_api_full.database import get_session +from typing import Optional +from datetime import datetime, timezone +from pydantic import BaseModel + +aggregate_changes_router = APIRouter(prefix="/aggregate-changes", tags=["aggregate_changes"]) + + +class AggregateChangeUpdateRequest(BaseModel): + """Request model for updating an aggregate change.""" + baseline_value: Optional[float] = None + comparison_value: Optional[float] = None + change: Optional[float] = None + relative_change: Optional[float] = None + + +@aggregate_changes_router.get("/", response_model=list[AggregateChangeTable]) +def list_aggregate_changes( + session: Session = Depends(get_session), + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=10_000), + baseline_simulation_id: Optional[str] = Query(None, description="Filter by baseline simulation ID"), + comparison_simulation_id: Optional[str] = Query(None, description="Filter by comparison simulation ID"), + reportelement_id: Optional[str] = Query(None, description="Filter by report element ID"), + entity: Optional[str] = Query(None, description="Filter by entity"), + variable_name: Optional[str] = Query(None, description="Filter by variable name"), +): + """List all aggregate changes with pagination and optional filters.""" + statement = select(AggregateChangeTable) + + if baseline_simulation_id: + statement = statement.where(AggregateChangeTable.baseline_simulation_id == baseline_simulation_id) + if comparison_simulation_id: + statement = statement.where(AggregateChangeTable.comparison_simulation_id == comparison_simulation_id) + if reportelement_id: + statement = statement.where(AggregateChangeTable.reportelement_id == reportelement_id) + if entity: + statement = statement.where(AggregateChangeTable.entity == entity) + if variable_name: + statement = statement.where(AggregateChangeTable.variable_name == variable_name) + + statement = statement.offset(skip).limit(limit) + aggregate_changes = session.exec(statement).all() + return aggregate_changes + + +@aggregate_changes_router.post("/", response_model=AggregateChangeTable, status_code=201) +def create_aggregate_change( + aggregate_change: AggregateChangeTable, + session: Session = Depends(get_session), +): + """Create a new aggregate change (typically with pre-computed values).""" + if not aggregate_change.id: + import uuid + aggregate_change.id = str(uuid.uuid4()) + + session.add(aggregate_change) + session.commit() + session.refresh(aggregate_change) + return aggregate_change + + +@aggregate_changes_router.get("/{aggregate_change_id}", response_model=AggregateChangeTable) +def get_aggregate_change( + aggregate_change_id: str, + session: Session = Depends(get_session), +): + """Get a single aggregate change by ID.""" + aggregate_change = session.get(AggregateChangeTable, aggregate_change_id) + if not aggregate_change: + raise HTTPException(status_code=404, detail="Aggregate change not found") + return aggregate_change + + +@aggregate_changes_router.patch("/{aggregate_change_id}", response_model=AggregateChangeTable) +def update_aggregate_change( + aggregate_change_id: str, + update_request: AggregateChangeUpdateRequest, + session: Session = Depends(get_session), +): + """Update an aggregate change's values.""" + db_aggregate_change = session.get(AggregateChangeTable, aggregate_change_id) + if not db_aggregate_change: + raise HTTPException(status_code=404, detail="Aggregate change not found") + + if update_request.baseline_value is not None: + db_aggregate_change.baseline_value = update_request.baseline_value + if update_request.comparison_value is not None: + db_aggregate_change.comparison_value = update_request.comparison_value + if update_request.change is not None: + db_aggregate_change.change = update_request.change + if update_request.relative_change is not None: + db_aggregate_change.relative_change = update_request.relative_change + + session.add(db_aggregate_change) + session.commit() + session.refresh(db_aggregate_change) + return db_aggregate_change + + +@aggregate_changes_router.delete("/{aggregate_change_id}", status_code=204) +def delete_aggregate_change( + aggregate_change_id: str, + session: Session = Depends(get_session), +): + """Delete an aggregate change.""" + aggregate_change = session.get(AggregateChangeTable, aggregate_change_id) + if not aggregate_change: + raise HTTPException(status_code=404, detail="Aggregate change not found") + + session.delete(aggregate_change) + session.commit() + return None + + +@aggregate_changes_router.post("/bulk", response_model=list[AggregateChangeTable], status_code=201) +def create_bulk_aggregate_changes( + aggregate_changes: list[AggregateChangeTable], + session: Session = Depends(get_session), +): + """Create multiple aggregate changes at once, computing their values.""" + import uuid + + created_aggregate_changes = [] + for aggregate_change in aggregate_changes: + if not aggregate_change.id: + aggregate_change.id = str(uuid.uuid4()) + created_aggregate_changes.append(aggregate_change) + + # Convert to models for computation + for i in range(len(created_aggregate_changes)): + created_aggregate_changes[i] = AggregateChangeTable.convert_to_model(created_aggregate_changes[i], database=database) + + # Run computation + created_aggregate_changes = AggregateChange.run(created_aggregate_changes) + + # Convert back to tables and save + for i in range(len(created_aggregate_changes)): + created_aggregate_changes[i] = AggregateChangeTable.convert_from_model(created_aggregate_changes[i]) + session.add(created_aggregate_changes[i]) + + session.commit() + + # Refresh all aggregate changes + for aggregate_change in created_aggregate_changes: + session.refresh(aggregate_change) + + return created_aggregate_changes + + +@aggregate_changes_router.get("/by-report-element/{reportelement_id}", response_model=list[AggregateChangeTable]) +def get_aggregate_changes_by_report_element( + reportelement_id: str, + session: Session = Depends(get_session), +): + """Get all aggregate changes for a specific report element.""" + statement = select(AggregateChangeTable).where( + AggregateChangeTable.reportelement_id == reportelement_id + ) + aggregate_changes = session.exec(statement).all() + return aggregate_changes \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/aggregates.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/aggregates.py index 06658e75..7b54e769 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/routers/aggregates.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/aggregates.py @@ -135,4 +135,17 @@ def create_bulk_aggregates( for aggregate in created_aggregates: session.refresh(aggregate) - return created_aggregates \ No newline at end of file + return created_aggregates + + +@aggregates_router.get("/by-report-element/{reportelement_id}", response_model=list[AggregateTable]) +def get_aggregates_by_report_element( + reportelement_id: str, + session: Session = Depends(get_session), +): + """Get all aggregates for a specific report element.""" + statement = select(AggregateTable).where( + AggregateTable.reportelement_id == reportelement_id + ) + aggregates = session.exec(statement).all() + return aggregates \ No newline at end of file diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/data_requests.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/data_requests.py index aff6050f..fe601c5c 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/routers/data_requests.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/data_requests.py @@ -17,10 +17,14 @@ class DataRequest(BaseModel): description: str report_id: str + simulation_ids: Optional[List[str]] = None + is_comparison: Optional[bool] = False class ParsedAggregate(BaseModel): - simulation_id: str + simulation_id: Optional[str] = None + baseline_simulation_id: Optional[str] = None + comparison_simulation_id: Optional[str] = None entity: str variable_name: str aggregate_function: str @@ -29,6 +33,9 @@ class ParsedAggregate(BaseModel): filter_variable_value: Optional[str] = None filter_variable_leq: Optional[float] = None filter_variable_geq: Optional[float] = None + filter_variable_quantile_leq: Optional[float] = None + filter_variable_quantile_geq: Optional[float] = None + filter_variable_quantile_value: Optional[str] = None class DataRequestResponse(BaseModel): @@ -95,23 +102,56 @@ async def parse_data_request( You have access to: - Variables (metrics that can be aggregated) - Simulations (different policy scenarios to compare) -- Aggregation functions: mean, sum, median, count, min, max +- Aggregation functions: mean, sum, median, count -Create ONE aggregate object for EACH combination of simulation and variable. -Example: comparing 2 simulations for 1 variable = 2 aggregate objects. +QUANTILE FILTERING RULES: +When users ask for deciles, quintiles, vigintiles, or percentiles: + +1. DECILES (10 groups): Create 10 separate aggregates with quantile filters + - 1st decile: filter_variable_quantile_geq: 0.0, filter_variable_quantile_leq: 0.1 + - 2nd decile: filter_variable_quantile_geq: 0.1, filter_variable_quantile_leq: 0.2 + - 3rd decile: filter_variable_quantile_geq: 0.2, filter_variable_quantile_leq: 0.3 + - ... and so on up to 10th decile: filter_variable_quantile_geq: 0.9, filter_variable_quantile_leq: 1.0 + +2. QUINTILES (5 groups): Create 5 separate aggregates + - 1st quintile: filter_variable_quantile_geq: 0.0, filter_variable_quantile_leq: 0.2 + - 2nd quintile: filter_variable_quantile_geq: 0.2, filter_variable_quantile_leq: 0.4 + - 3rd quintile: filter_variable_quantile_geq: 0.4, filter_variable_quantile_leq: 0.6 + - 4th quintile: filter_variable_quantile_geq: 0.6, filter_variable_quantile_leq: 0.8 + - 5th quintile: filter_variable_quantile_geq: 0.8, filter_variable_quantile_leq: 1.0 + +3. VIGINTILES (20 groups): Create 20 separate aggregates + - 1st vigintile: filter_variable_quantile_geq: 0.00, filter_variable_quantile_leq: 0.05 + - 2nd vigintile: filter_variable_quantile_geq: 0.05, filter_variable_quantile_leq: 0.10 + - ... and so on with 0.05 increments + +4. TOP/BOTTOM percentages: Use filter_variable_quantile_value + - "top 10%": filter_variable_quantile_value: "top_10%" + - "bottom 20%": filter_variable_quantile_value: "bottom_20%" + +IMPORTANT: When creating quantile groups, you must: +- Set filter_variable_name to the variable you want to group by (usually income) +- Create multiple aggregate objects, one for each quantile group +- Use the quantile filter fields (filter_variable_quantile_leq, filter_variable_quantile_geq) + +Create ONE aggregate object for EACH combination of simulation, variable, and quantile group. +Example: 2 simulations × 1 variable × 10 deciles = 20 aggregate objects. Required JSON structure (return EXACTLY this format): { "aggregates": [ { - "simulation_id": "string", "entity": "string", "variable_name": "string", "aggregate_function": "string", "year": null, "filter_variable_name": null, + "filter_variable_value": null, "filter_variable_leq": null, - "filter_variable_geq": null + "filter_variable_geq": null, + "filter_variable_quantile_leq": null, + "filter_variable_quantile_geq": null, + "filter_variable_quantile_value": null } ], "chart_type": "table", @@ -120,17 +160,33 @@ async def parse_data_request( "explanation": "string" } +Note: Do NOT include simulation_id in the aggregates - simulations are handled separately. + Remember: Return ONLY the JSON object. No text before or after.""" + # Customize prompt based on whether simulations are provided + if request.simulation_ids: + sim_context = f"Using simulation IDs: {request.simulation_ids}" + if request.is_comparison and len(request.simulation_ids) == 2: + sim_context = f"Comparing baseline simulation {request.simulation_ids[0]} with reform simulation {request.simulation_ids[1]}" + else: + sim_context = f"Available simulations:\n{json.dumps(simulations_context, indent=2)}" + user_prompt = f"""User request: {request.description} Available variables (first 100): {json.dumps(variables_context, indent=2)} -Available simulations: -{json.dumps(simulations_context, indent=2)} +{sim_context} + +Parse this request into aggregate queries. {"The simulations have already been selected, so just focus on parsing what variables and aggregations are needed." if request.simulation_ids else "If the user mentions comparing policies or simulations, include multiple simulation_ids."} -Parse this request into aggregate queries. If the user mentions comparing policies or simulations, include multiple simulation_ids. If they mention income bands or other ranges, use filter variables. +If they mention: +- Income bands or ranges: use filter_variable_leq and filter_variable_geq +- Deciles: create 10 aggregates with filter_variable_quantile_leq and filter_variable_quantile_geq (0.0-0.1, 0.1-0.2, etc) +- Quintiles: create 5 aggregates with quantile filters (0.0-0.2, 0.2-0.4, etc) +- Vigintiles: create 20 aggregates with quantile filters (0.0-0.05, 0.05-0.10, etc) +- Top/bottom X%: use filter_variable_quantile_value (e.g., "top_10%" or "bottom_20%") REMINDER: Return ONLY valid JSON. No text before or after. Ensure all JSON syntax is correct: - No trailing commas @@ -188,29 +244,42 @@ async def parse_data_request( # Convert to response model - validate each aggregate aggregates = [] - for agg_data in parsed_response.get("aggregates", []): - # Handle cases where LLM returns lists instead of individual items - sim_ids = agg_data.get("simulation_id", []) - if not isinstance(sim_ids, list): - sim_ids = [sim_ids] + # If this is a comparison and we have simulation_ids from the request, use them + is_comparison = request.is_comparison and request.simulation_ids and len(request.simulation_ids) == 2 + + for agg_data in parsed_response.get("aggregates", []): var_names = agg_data.get("variable_name", []) if not isinstance(var_names, list): var_names = [var_names] - # Create one aggregate for each combination - for sim_id in sim_ids: - for var_name in var_names: - agg_copy = agg_data.copy() - agg_copy["simulation_id"] = sim_id - agg_copy["variable_name"] = var_name - # Ensure entity has a default - if not agg_copy.get("entity"): - agg_copy["entity"] = "person" - # Ensure aggregate_function has a default - if not agg_copy.get("aggregate_function"): - agg_copy["aggregate_function"] = "mean" - aggregates.append(ParsedAggregate(**agg_copy)) + # Create one aggregate for each variable + for var_name in var_names: + agg_copy = agg_data.copy() + agg_copy["variable_name"] = var_name + + # If it's a comparison, set baseline and comparison IDs + if is_comparison: + # Use the provided simulation_ids for comparison + agg_copy["baseline_simulation_id"] = request.simulation_ids[0] + agg_copy["comparison_simulation_id"] = request.simulation_ids[1] + # Don't set simulation_id for comparisons + agg_copy.pop("simulation_id", None) + else: + # For single simulation, use the first simulation_id + if request.simulation_ids and len(request.simulation_ids) > 0: + agg_copy["simulation_id"] = request.simulation_ids[0] + else: + # Fallback if no simulation_ids provided (shouldn't happen in new flow) + agg_copy["simulation_id"] = agg_data.get("simulation_id", "default") + + # Ensure entity has a default + if not agg_copy.get("entity"): + agg_copy["entity"] = "person" + # Ensure aggregate_function has a default + if not agg_copy.get("aggregate_function"): + agg_copy["aggregate_function"] = "mean" + aggregates.append(ParsedAggregate(**agg_copy)) return DataRequestResponse( aggregates=aggregates, diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py index c3691ce7..6128dbda 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py @@ -1,7 +1,7 @@ from fastapi import APIRouter, HTTPException, Depends, Query, Body from sqlmodel import Session, select -from policyengine.database import ReportElementTable, AggregateTable -from policyengine.models import Aggregate +from policyengine.database import ReportElementTable, AggregateTable, AggregateChangeTable +from policyengine.models import Aggregate, AggregateChange from policyengine_api_full.database import get_session, database from typing import Optional, List, Dict, Any from datetime import datetime, timezone @@ -23,12 +23,27 @@ class AggregateInput(BaseModel): filter_variable_geq: Optional[float] = None +class AggregateChangeInput(BaseModel): + """Input model for aggregate change data.""" + entity: str + variable_name: str + aggregate_function: str # "sum", "mean", or "count" + baseline_simulation_id: str + comparison_simulation_id: str + year: Optional[int] = None + filter_variable_name: Optional[str] = None + filter_variable_value: Optional[str] = None + filter_variable_leq: Optional[float] = None + filter_variable_geq: Optional[float] = None + + class ReportElementCreateRequest(BaseModel): """Request model for creating a report element with associated data.""" label: str type: str # "chart", "markdown", or "aggregates" - data_type: Optional[str] = None # "Aggregate" when creating aggregates - data: Optional[List[AggregateInput]] = None # List of aggregates to compute + data_type: Optional[str] = None # "Aggregate" or "AggregateChange" + data: Optional[List[Any]] = None # List of aggregates or aggregate changes to compute + data_table: Optional[str] = None # "aggregates" or "aggregate_changes" chart_type: Optional[str] = None x_axis_variable: Optional[str] = None y_axis_variable: Optional[str] = None @@ -108,7 +123,7 @@ def create_report_element( id=str(uuid.uuid4()), label=request.label, type=request.type, - data_table="aggregates" if request.data_type == "Aggregate" else None, + data_table=request.data_table or ("aggregates" if request.data_type == "Aggregate" else "aggregate_changes" if request.data_type == "AggregateChange" else None), chart_type=request.chart_type, x_axis_variable=request.x_axis_variable, y_axis_variable=request.y_axis_variable, @@ -138,13 +153,36 @@ def create_report_element( aggregate_models = [] for i, agg_input in enumerate(request.data): - print(f"Processing aggregate {i+1}/{len(request.data)}: {agg_input.variable_name} for simulation {agg_input.simulation_id}") + # Handle dict or object input + if isinstance(agg_input, dict): + simulation_id = agg_input.get('simulation_id') + entity = agg_input.get('entity') + variable_name = agg_input.get('variable_name') + year = agg_input.get('year') + filter_variable_name = agg_input.get('filter_variable_name') + filter_variable_value = agg_input.get('filter_variable_value') + filter_variable_leq = agg_input.get('filter_variable_leq') + filter_variable_geq = agg_input.get('filter_variable_geq') + aggregate_function = agg_input.get('aggregate_function') + else: + simulation_id = agg_input.simulation_id + entity = agg_input.entity + variable_name = agg_input.variable_name + year = agg_input.year + filter_variable_name = agg_input.filter_variable_name + filter_variable_value = agg_input.filter_variable_value + filter_variable_leq = agg_input.filter_variable_leq + filter_variable_geq = agg_input.filter_variable_geq + aggregate_function = agg_input.aggregate_function + + print(f"Processing aggregate {i+1}/{len(request.data)}: {variable_name} for simulation {simulation_id}") + # Get the simulation - simulation_table = session.get(SimulationTable, agg_input.simulation_id) + simulation_table = session.get(SimulationTable, simulation_id) if not simulation_table: raise HTTPException( status_code=400, - detail=f"Simulation {agg_input.simulation_id} not found" + detail=f"Simulation {simulation_id} not found" ) # Convert simulation table to model (we need the full simulation object) @@ -158,14 +196,14 @@ def create_report_element( # Create the Aggregate model instance agg = Aggregate( simulation=simulation, - entity=agg_input.entity, - variable_name=agg_input.variable_name, - year=agg_input.year, - filter_variable_name=agg_input.filter_variable_name, - filter_variable_value=agg_input.filter_variable_value, - filter_variable_leq=agg_input.filter_variable_leq, - filter_variable_geq=agg_input.filter_variable_geq, - aggregate_function=agg_input.aggregate_function, + entity=entity, + variable_name=variable_name, + year=year, + filter_variable_name=filter_variable_name, + filter_variable_value=filter_variable_value, + filter_variable_leq=filter_variable_leq, + filter_variable_geq=filter_variable_geq, + aggregate_function=aggregate_function, reportelement_id=report_element.id ) aggregate_models.append(agg) @@ -206,39 +244,154 @@ def create_report_element( "filter_variable_geq": agg_table.filter_variable_geq, }) + # Process aggregate changes if provided + elif request.data_type == "AggregateChange" and request.data: + print(f"Processing {len(request.data)} aggregate changes for report element {report_element.id}") + # Convert input aggregate changes to AggregateChange model instances + aggregate_change_models = [] + + for i, agg_change_input in enumerate(request.data): + # Parse the input data - it could be dict or AggregateChangeInput + if isinstance(agg_change_input, dict): + baseline_simulation_id = agg_change_input.get('baseline_simulation_id') + comparison_simulation_id = agg_change_input.get('comparison_simulation_id') + entity = agg_change_input.get('entity') + variable_name = agg_change_input.get('variable_name') + year = agg_change_input.get('year') + filter_variable_name = agg_change_input.get('filter_variable_name') + filter_variable_value = agg_change_input.get('filter_variable_value') + filter_variable_leq = agg_change_input.get('filter_variable_leq') + filter_variable_geq = agg_change_input.get('filter_variable_geq') + aggregate_function = agg_change_input.get('aggregate_function') + else: + baseline_simulation_id = agg_change_input.baseline_simulation_id + comparison_simulation_id = agg_change_input.comparison_simulation_id + entity = agg_change_input.entity + variable_name = agg_change_input.variable_name + year = agg_change_input.year + filter_variable_name = agg_change_input.filter_variable_name + filter_variable_value = agg_change_input.filter_variable_value + filter_variable_leq = agg_change_input.filter_variable_leq + filter_variable_geq = agg_change_input.filter_variable_geq + aggregate_function = agg_change_input.aggregate_function + + print(f"Processing aggregate change {i+1}/{len(request.data)}: {variable_name} for simulations {baseline_simulation_id} vs {comparison_simulation_id}") + + # Get the baseline simulation + baseline_simulation_table = session.get(SimulationTable, baseline_simulation_id) + if not baseline_simulation_table: + raise HTTPException( + status_code=400, + detail=f"Baseline simulation {baseline_simulation_id} not found" + ) + + # Get the comparison simulation + comparison_simulation_table = session.get(SimulationTable, comparison_simulation_id) + if not comparison_simulation_table: + raise HTTPException( + status_code=400, + detail=f"Comparison simulation {comparison_simulation_id} not found" + ) + + # Convert simulation tables to models + from policyengine.database import Database + from policyengine_api_full.database import engine + + # Create a database instance to handle the conversion + db = database + baseline_simulation = baseline_simulation_table.convert_to_model(db) + comparison_simulation = comparison_simulation_table.convert_to_model(db) + + # Create the AggregateChange model instance + agg_change = AggregateChange( + baseline_simulation=baseline_simulation, + comparison_simulation=comparison_simulation, + entity=entity, + variable_name=variable_name, + year=year, + filter_variable_name=filter_variable_name, + filter_variable_value=filter_variable_value, + filter_variable_leq=filter_variable_leq, + filter_variable_geq=filter_variable_geq, + aggregate_function=aggregate_function, + reportelement_id=report_element.id + ) + aggregate_change_models.append(agg_change) + + # Run AggregateChange.run to compute values + print(f"Running AggregateChange.run on {len(aggregate_change_models)} models") + computed_change_models = AggregateChange.run(aggregate_change_models) + print(f"AggregateChange.run returned {len(computed_change_models)} computed models") + + # Save the computed aggregate changes to the database + for j, agg_change_model in enumerate(computed_change_models): + print(f"Saving aggregate change {j+1}/{len(computed_change_models)}: {agg_change_model.variable_name}") + print(f" Baseline value: {agg_change_model.baseline_value}") + print(f" Comparison value: {agg_change_model.comparison_value}") + print(f" Change: {agg_change_model.change}") + print(f" Relative change: {agg_change_model.relative_change}") + + agg_change_table = AggregateChangeTable( + id=agg_change_model.id, + baseline_simulation_id=agg_change_model.baseline_simulation.id if agg_change_model.baseline_simulation else None, + comparison_simulation_id=agg_change_model.comparison_simulation.id if agg_change_model.comparison_simulation else None, + entity=agg_change_model.entity, + variable_name=agg_change_model.variable_name, + year=agg_change_model.year, + filter_variable_name=agg_change_model.filter_variable_name, + filter_variable_value=agg_change_model.filter_variable_value, + filter_variable_leq=agg_change_model.filter_variable_leq, + filter_variable_geq=agg_change_model.filter_variable_geq, + aggregate_function=agg_change_model.aggregate_function, + reportelement_id=report_element.id, + baseline_value=agg_change_model.baseline_value, + comparison_value=agg_change_model.comparison_value, + change=agg_change_model.change, + relative_change=agg_change_model.relative_change + ) + session.add(agg_change_table) + computed_aggregates.append({ + "id": agg_change_table.id, + "entity": agg_change_table.entity, + "variable_name": agg_change_table.variable_name, + "aggregate_function": agg_change_table.aggregate_function, + "baseline_value": agg_change_table.baseline_value, + "comparison_value": agg_change_table.comparison_value, + "change": agg_change_table.change, + "relative_change": agg_change_table.relative_change, + "year": agg_change_table.year, + "filter_variable_name": agg_change_table.filter_variable_name, + "filter_variable_value": agg_change_table.filter_variable_value, + "filter_variable_leq": agg_change_table.filter_variable_leq, + "filter_variable_geq": agg_change_table.filter_variable_geq, + }) + session.commit() session.refresh(report_element) - # Return the report element with computed aggregates - response = { - "report_element": { - "id": report_element.id, - "label": report_element.label, - "type": report_element.type, - "data_table": report_element.data_table, - "chart_type": report_element.chart_type, - "x_axis_variable": report_element.x_axis_variable, - "y_axis_variable": report_element.y_axis_variable, - "group_by": report_element.group_by, - "color_by": report_element.color_by, - "size_by": report_element.size_by, - "markdown_content": report_element.markdown_content, - "report_id": report_element.report_id, - "user_id": report_element.user_id, - "position": report_element.position, - "visible": report_element.visible, - "model_version_id": report_element.model_version_id, - "report_element_metadata": report_element.report_element_metadata, - "created_at": report_element.created_at.isoformat(), - "updated_at": report_element.updated_at.isoformat(), - } + # Convert to dict for JSON serialization + return { + "id": report_element.id, + "label": report_element.label, + "type": report_element.type, + "data_table": report_element.data_table, + "chart_type": report_element.chart_type, + "x_axis_variable": report_element.x_axis_variable, + "y_axis_variable": report_element.y_axis_variable, + "group_by": report_element.group_by, + "color_by": report_element.color_by, + "size_by": report_element.size_by, + "markdown_content": report_element.markdown_content, + "report_id": report_element.report_id, + "user_id": report_element.user_id, + "position": report_element.position, + "visible": report_element.visible, + "model_version_id": report_element.model_version_id, + "report_element_metadata": report_element.report_element_metadata, + "created_at": report_element.created_at.isoformat() if report_element.created_at else None, + "updated_at": report_element.updated_at.isoformat() if report_element.updated_at else None, } - if computed_aggregates: - response["aggregates"] = computed_aggregates - - return response - @report_elements_router.get("/{report_element_id}", response_model=ReportElementTable) def get_report_element( @@ -322,7 +475,7 @@ def delete_report_element( report_element_id: str, session: Session = Depends(get_session), ): - """Delete a report element and its associated aggregates.""" + """Delete a report element and its associated aggregates or aggregate changes.""" report_element = session.get(ReportElementTable, report_element_id) if not report_element: raise HTTPException(status_code=404, detail="Report element not found") @@ -335,6 +488,14 @@ def delete_report_element( for aggregate in aggregates: session.delete(aggregate) + # Delete associated aggregate changes + statement_changes = select(AggregateChangeTable).where( + AggregateChangeTable.reportelement_id == report_element_id + ) + aggregate_changes = session.exec(statement_changes).all() + for aggregate_change in aggregate_changes: + session.delete(aggregate_change) + session.delete(report_element) session.commit() return None \ No newline at end of file From 685c76783ece89ec4577e097803387a1d2786bfe Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Mon, 29 Sep 2025 23:22:50 +0100 Subject: [PATCH 10/11] Update --- .../routers/data_requests.py | 209 +++++++----------- .../routers/report_elements.py | 125 ++++++++--- 2 files changed, 165 insertions(+), 169 deletions(-) diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/data_requests.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/data_requests.py index fe601c5c..56adaf8a 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/routers/data_requests.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/data_requests.py @@ -8,6 +8,7 @@ import anthropic import json import os +import time from policyengine_api_full.database import get_session from policyengine.database import BaselineVariableTable, SimulationTable, PolicyTable, DatasetTable @@ -52,157 +53,79 @@ async def parse_data_request( db: Session = Depends(get_session) ): """Parse a natural language data request into aggregates configuration""" + import time + start_time = time.time() # Get API key from environment api_key = os.getenv("ANTHROPIC_API_KEY") if not api_key: raise HTTPException(status_code=500, detail="Anthropic API key not configured") - # Fetch context data - variables = db.exec(select(BaselineVariableTable).limit(1000)).all() - simulations = db.exec(select(SimulationTable).limit(100)).all() - policies = db.exec(select(PolicyTable).limit(100)).all() - datasets = db.exec(select(DatasetTable).limit(100)).all() - - # Create lookup maps - policy_map = {p.id: p for p in policies} - dataset_map = {d.id: d for d in datasets} - - # Build context for LLM - variables_context = [ - { - "name": v.id, - "label": v.label, - "entity": v.entity, - "description": v.description - } - for v in variables[:100] # Limit to avoid token limits + # Only fetch minimal context - common variables that are likely to be used + common_variable_names = [ + "household_net_income", "hbai_household_net_income", "equiv_hbai_household_net_income", + "household_benefits", "household_tax", "employment_income", "person_weight" ] - simulations_context = [ - { - "id": s.id, - "policy_label": policy_map.get(s.policy_id).name if s.policy_id and s.policy_id in policy_map else "Baseline", - "dataset_label": dataset_map.get(s.dataset_id).name if s.dataset_id and s.dataset_id in dataset_map else "Default" - } - for s in simulations - ] + variables = db.exec( + select(BaselineVariableTable).where(BaselineVariableTable.id.in_(common_variable_names)) + ).all() + + # Build minimal context + variables_context = [{"name": v.id, "entity": v.entity} for v in variables] + + # Consolidated system prompt with examples + system_prompt = """Convert natural language to JSON aggregate queries. Return ONLY valid JSON. - # Create prompt for Claude - system_prompt = """You are a JSON API that converts natural language requests into structured aggregate queries. - -CRITICAL RULES: -1. Return ONLY raw JSON - no markdown, no code blocks, no explanations outside the JSON -2. The JSON must be valid and parseable by Python's json.loads() -3. Never use trailing commas in JSON -4. All string values must use double quotes -5. null values must be lowercase "null" -6. Boolean values must be lowercase "true" or "false" - -You have access to: -- Variables (metrics that can be aggregated) -- Simulations (different policy scenarios to compare) -- Aggregation functions: mean, sum, median, count - -QUANTILE FILTERING RULES: -When users ask for deciles, quintiles, vigintiles, or percentiles: - -1. DECILES (10 groups): Create 10 separate aggregates with quantile filters - - 1st decile: filter_variable_quantile_geq: 0.0, filter_variable_quantile_leq: 0.1 - - 2nd decile: filter_variable_quantile_geq: 0.1, filter_variable_quantile_leq: 0.2 - - 3rd decile: filter_variable_quantile_geq: 0.2, filter_variable_quantile_leq: 0.3 - - ... and so on up to 10th decile: filter_variable_quantile_geq: 0.9, filter_variable_quantile_leq: 1.0 - -2. QUINTILES (5 groups): Create 5 separate aggregates - - 1st quintile: filter_variable_quantile_geq: 0.0, filter_variable_quantile_leq: 0.2 - - 2nd quintile: filter_variable_quantile_geq: 0.2, filter_variable_quantile_leq: 0.4 - - 3rd quintile: filter_variable_quantile_geq: 0.4, filter_variable_quantile_leq: 0.6 - - 4th quintile: filter_variable_quantile_geq: 0.6, filter_variable_quantile_leq: 0.8 - - 5th quintile: filter_variable_quantile_geq: 0.8, filter_variable_quantile_leq: 1.0 - -3. VIGINTILES (20 groups): Create 20 separate aggregates - - 1st vigintile: filter_variable_quantile_geq: 0.00, filter_variable_quantile_leq: 0.05 - - 2nd vigintile: filter_variable_quantile_geq: 0.05, filter_variable_quantile_leq: 0.10 - - ... and so on with 0.05 increments - -4. TOP/BOTTOM percentages: Use filter_variable_quantile_value - - "top 10%": filter_variable_quantile_value: "top_10%" - - "bottom 20%": filter_variable_quantile_value: "bottom_20%" - -IMPORTANT: When creating quantile groups, you must: -- Set filter_variable_name to the variable you want to group by (usually income) -- Create multiple aggregate objects, one for each quantile group -- Use the quantile filter fields (filter_variable_quantile_leq, filter_variable_quantile_geq) - -Create ONE aggregate object for EACH combination of simulation, variable, and quantile group. -Example: 2 simulations × 1 variable × 10 deciles = 20 aggregate objects. - -Required JSON structure (return EXACTLY this format): +RULES: +- For "by deciles": Create 10 aggregates with filter_variable_quantile_geq/leq from 0.0-0.1, 0.1-0.2, ... 0.9-1.0 +- For "by quintiles": Create 5 aggregates with ranges 0.0-0.2, 0.2-0.4, 0.4-0.6, 0.6-0.8, 0.8-1.0 +- entity is optional (auto-inferred from variable), default function: "mean" +- Common income variable for filtering: "equiv_hbai_household_net_income" + +JSON schema: { - "aggregates": [ - { - "entity": "string", - "variable_name": "string", - "aggregate_function": "string", - "year": null, - "filter_variable_name": null, - "filter_variable_value": null, - "filter_variable_leq": null, - "filter_variable_geq": null, - "filter_variable_quantile_leq": null, - "filter_variable_quantile_geq": null, - "filter_variable_quantile_value": null - } - ], - "chart_type": "table", - "x_axis_variable": null, - "y_axis_variable": null, - "explanation": "string" -} - -Note: Do NOT include simulation_id in the aggregates - simulations are handled separately. - -Remember: Return ONLY the JSON object. No text before or after.""" - - # Customize prompt based on whether simulations are provided - if request.simulation_ids: - sim_context = f"Using simulation IDs: {request.simulation_ids}" - if request.is_comparison and len(request.simulation_ids) == 2: - sim_context = f"Comparing baseline simulation {request.simulation_ids[0]} with reform simulation {request.simulation_ids[1]}" - else: - sim_context = f"Available simulations:\n{json.dumps(simulations_context, indent=2)}" - - user_prompt = f"""User request: {request.description} - -Available variables (first 100): -{json.dumps(variables_context, indent=2)} - -{sim_context} - -Parse this request into aggregate queries. {"The simulations have already been selected, so just focus on parsing what variables and aggregations are needed." if request.simulation_ids else "If the user mentions comparing policies or simulations, include multiple simulation_ids."} - -If they mention: -- Income bands or ranges: use filter_variable_leq and filter_variable_geq -- Deciles: create 10 aggregates with filter_variable_quantile_leq and filter_variable_quantile_geq (0.0-0.1, 0.1-0.2, etc) -- Quintiles: create 5 aggregates with quantile filters (0.0-0.2, 0.2-0.4, etc) -- Vigintiles: create 20 aggregates with quantile filters (0.0-0.05, 0.05-0.10, etc) -- Top/bottom X%: use filter_variable_quantile_value (e.g., "top_10%" or "bottom_20%") - -REMINDER: Return ONLY valid JSON. No text before or after. Ensure all JSON syntax is correct: -- No trailing commas -- All strings in double quotes -- Proper null values (not None or undefined) -- Valid array/object structure""" - - # Call Claude + "aggregates": [{ + "entity": null, + "variable_name": "hbai_household_net_income", + "aggregate_function": "mean", + "year": null, + "filter_variable_name": null, + "filter_variable_value": null, + "filter_variable_leq": null, + "filter_variable_geq": null, + "filter_variable_quantile_leq": null, + "filter_variable_quantile_geq": null, + "filter_variable_quantile_value": null + }], + "chart_type": "table", + "x_axis_variable": null, + "y_axis_variable": null, + "explanation": "Brief description" +}""" + + user_prompt = f"""Request: {request.description} + +Variables: {json.dumps(variables_context)} +{"Simulations already selected." if request.simulation_ids else ""} + +Return JSON:""" + + # Call Claude with Haiku for speed client = anthropic.Anthropic(api_key=api_key) try: message = client.messages.create( - model="claude-3-haiku-20240307", + model="claude-3-5-haiku-20241022", max_tokens=2000, temperature=0, - system=system_prompt, + system=[ + { + "type": "text", + "text": system_prompt, + "cache_control": {"type": "ephemeral"} + } + ], messages=[ {"role": "user", "content": user_prompt} ] @@ -212,6 +135,7 @@ async def parse_data_request( response_text = message.content[0].text.strip() print(f"LLM Response: {response_text[:1000]}...") # Debug logging + parse_start = time.time() try: # Clean up common issues before parsing # Remove any potential Unicode BOM @@ -242,6 +166,15 @@ async def parse_data_request( print(f"Parsed JSON: {json.dumps(parsed_response, indent=2)[:500]}...") # Debug logging + # Check if quantile fields are present + if parsed_response.get("aggregates"): + first_agg = parsed_response["aggregates"][0] + print(f"First aggregate quantile fields:") + print(f" filter_variable_quantile_leq: {first_agg.get('filter_variable_quantile_leq')}") + print(f" filter_variable_quantile_geq: {first_agg.get('filter_variable_quantile_geq')}") + print(f" filter_variable_name: {first_agg.get('filter_variable_name')}") + print(f" Total aggregates: {len(parsed_response['aggregates'])}") + # Convert to response model - validate each aggregate aggregates = [] @@ -281,6 +214,12 @@ async def parse_data_request( agg_copy["aggregate_function"] = "mean" aggregates.append(ParsedAggregate(**agg_copy)) + parse_time = time.time() + print(f"[PERFORMANCE] Response parsing took {parse_time - parse_start:.2f} seconds") + total_time = time.time() + print(f"[PERFORMANCE] Total data request parsing took {total_time - start_time:.2f} seconds") + print(f"[PERFORMANCE] Generated {len(aggregates)} aggregates") + return DataRequestResponse( aggregates=aggregates, chart_type=parsed_response.get("chart_type", "table"), diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py index 6128dbda..1e1fe3b1 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/report_elements.py @@ -6,13 +6,14 @@ from typing import Optional, List, Dict, Any from datetime import datetime, timezone from pydantic import BaseModel +import time report_elements_router = APIRouter(prefix="/report-elements", tags=["report elements"]) class AggregateInput(BaseModel): """Input model for aggregate data without the value field.""" - entity: str + entity: Optional[str] = None variable_name: str aggregate_function: str # "sum", "mean", or "count" simulation_id: str @@ -25,7 +26,7 @@ class AggregateInput(BaseModel): class AggregateChangeInput(BaseModel): """Input model for aggregate change data.""" - entity: str + entity: Optional[str] = None variable_name: str aggregate_function: str # "sum", "mean", or "count" baseline_simulation_id: str @@ -118,6 +119,9 @@ def create_report_element( import uuid from policyengine.database import SimulationTable + start_time = time.time() + print(f"[PERFORMANCE] Starting report element creation at {start_time}") + # Create the report element report_element = ReportElementTable( id=str(uuid.uuid4()), @@ -144,11 +148,15 @@ def create_report_element( session.add(report_element) session.flush() # Get the ID before processing aggregates + element_creation_time = time.time() + print(f"[PERFORMANCE] Report element created in {element_creation_time - start_time:.2f} seconds") + computed_aggregates = [] # Process aggregates if provided if request.data_type == "Aggregate" and request.data: - print(f"Processing {len(request.data)} aggregates for report element {report_element.id}") + print(f"[PERFORMANCE] Processing {len(request.data)} aggregates for report element {report_element.id}") + aggregate_prep_start = time.time() # Convert input aggregates to Aggregate model instances aggregate_models = [] @@ -209,11 +217,17 @@ def create_report_element( aggregate_models.append(agg) # Run Aggregate.run to compute values - print(f"Running Aggregate.run on {len(aggregate_models)} models") + aggregate_prep_time = time.time() + print(f"[PERFORMANCE] Aggregate preparation took {aggregate_prep_time - aggregate_prep_start:.2f} seconds") + print(f"[PERFORMANCE] Running Aggregate.run on {len(aggregate_models)} models") + aggregate_run_start = time.time() computed_models = Aggregate.run(aggregate_models) - print(f"Aggregate.run returned {len(computed_models)} computed models") + aggregate_run_time = time.time() + print(f"[PERFORMANCE] Aggregate.run took {aggregate_run_time - aggregate_run_start:.2f} seconds") + print(f"[PERFORMANCE] Aggregate.run returned {len(computed_models)} computed models") # Save the computed aggregates to the database + save_start = time.time() for j, agg_model in enumerate(computed_models): print(f"Saving aggregate {j+1}/{len(computed_models)}: {agg_model.variable_name} = {agg_model.value}") agg_table = AggregateTable( @@ -246,7 +260,42 @@ def create_report_element( # Process aggregate changes if provided elif request.data_type == "AggregateChange" and request.data: - print(f"Processing {len(request.data)} aggregate changes for report element {report_element.id}") + print(f"[PERFORMANCE] Processing {len(request.data)} aggregate changes for report element {report_element.id}") + aggregate_change_prep_start = time.time() + + # Collect unique simulation IDs and fetch them once + sim_ids = set() + for agg_change_input in request.data: + if isinstance(agg_change_input, dict): + sim_ids.add(agg_change_input.get('baseline_simulation_id')) + sim_ids.add(agg_change_input.get('comparison_simulation_id')) + else: + sim_ids.add(agg_change_input.baseline_simulation_id) + sim_ids.add(agg_change_input.comparison_simulation_id) + + # Fetch all simulations at once + sim_fetch_start = time.time() + sim_tables = {} + for sim_id in sim_ids: + sim_table = session.get(SimulationTable, sim_id) + if not sim_table: + raise HTTPException( + status_code=400, + detail=f"Simulation {sim_id} not found" + ) + sim_tables[sim_id] = sim_table + sim_fetch_time = time.time() + print(f"[PERFORMANCE] Fetched {len(sim_ids)} unique simulations in {sim_fetch_time - sim_fetch_start:.2f} seconds") + + # Convert all simulations to models + sim_convert_start = time.time() + db = database + sim_models = {} + for sim_id, sim_table in sim_tables.items(): + sim_models[sim_id] = sim_table.convert_to_model(db) + sim_convert_time = time.time() + print(f"[PERFORMANCE] Converted {len(sim_models)} simulations in {sim_convert_time - sim_convert_start:.2f} seconds") + # Convert input aggregate changes to AggregateChange model instances aggregate_change_models = [] @@ -262,6 +311,9 @@ def create_report_element( filter_variable_value = agg_change_input.get('filter_variable_value') filter_variable_leq = agg_change_input.get('filter_variable_leq') filter_variable_geq = agg_change_input.get('filter_variable_geq') + filter_variable_quantile_leq = agg_change_input.get('filter_variable_quantile_leq') + filter_variable_quantile_geq = agg_change_input.get('filter_variable_quantile_geq') + filter_variable_quantile_value = agg_change_input.get('filter_variable_quantile_value') aggregate_function = agg_change_input.get('aggregate_function') else: baseline_simulation_id = agg_change_input.baseline_simulation_id @@ -273,34 +325,14 @@ def create_report_element( filter_variable_value = agg_change_input.filter_variable_value filter_variable_leq = agg_change_input.filter_variable_leq filter_variable_geq = agg_change_input.filter_variable_geq + filter_variable_quantile_leq = getattr(agg_change_input, 'filter_variable_quantile_leq', None) + filter_variable_quantile_geq = getattr(agg_change_input, 'filter_variable_quantile_geq', None) + filter_variable_quantile_value = getattr(agg_change_input, 'filter_variable_quantile_value', None) aggregate_function = agg_change_input.aggregate_function - print(f"Processing aggregate change {i+1}/{len(request.data)}: {variable_name} for simulations {baseline_simulation_id} vs {comparison_simulation_id}") - - # Get the baseline simulation - baseline_simulation_table = session.get(SimulationTable, baseline_simulation_id) - if not baseline_simulation_table: - raise HTTPException( - status_code=400, - detail=f"Baseline simulation {baseline_simulation_id} not found" - ) - - # Get the comparison simulation - comparison_simulation_table = session.get(SimulationTable, comparison_simulation_id) - if not comparison_simulation_table: - raise HTTPException( - status_code=400, - detail=f"Comparison simulation {comparison_simulation_id} not found" - ) - - # Convert simulation tables to models - from policyengine.database import Database - from policyengine_api_full.database import engine - - # Create a database instance to handle the conversion - db = database - baseline_simulation = baseline_simulation_table.convert_to_model(db) - comparison_simulation = comparison_simulation_table.convert_to_model(db) + # Get pre-fetched and converted simulations + baseline_simulation = sim_models[baseline_simulation_id] + comparison_simulation = sim_models[comparison_simulation_id] # Create the AggregateChange model instance agg_change = AggregateChange( @@ -313,17 +345,26 @@ def create_report_element( filter_variable_value=filter_variable_value, filter_variable_leq=filter_variable_leq, filter_variable_geq=filter_variable_geq, + filter_variable_quantile_leq=filter_variable_quantile_leq, + filter_variable_quantile_geq=filter_variable_quantile_geq, + filter_variable_quantile_value=filter_variable_quantile_value, aggregate_function=aggregate_function, reportelement_id=report_element.id ) aggregate_change_models.append(agg_change) # Run AggregateChange.run to compute values - print(f"Running AggregateChange.run on {len(aggregate_change_models)} models") + aggregate_change_prep_time = time.time() + print(f"[PERFORMANCE] AggregateChange preparation took {aggregate_change_prep_time - aggregate_change_prep_start:.2f} seconds") + print(f"[PERFORMANCE] Running AggregateChange.run on {len(aggregate_change_models)} models") + aggregate_change_run_start = time.time() computed_change_models = AggregateChange.run(aggregate_change_models) - print(f"AggregateChange.run returned {len(computed_change_models)} computed models") + aggregate_change_run_time = time.time() + print(f"[PERFORMANCE] AggregateChange.run took {aggregate_change_run_time - aggregate_change_run_start:.2f} seconds") + print(f"[PERFORMANCE] AggregateChange.run returned {len(computed_change_models)} computed models") # Save the computed aggregate changes to the database + save_start = time.time() for j, agg_change_model in enumerate(computed_change_models): print(f"Saving aggregate change {j+1}/{len(computed_change_models)}: {agg_change_model.variable_name}") print(f" Baseline value: {agg_change_model.baseline_value}") @@ -342,6 +383,9 @@ def create_report_element( filter_variable_value=agg_change_model.filter_variable_value, filter_variable_leq=agg_change_model.filter_variable_leq, filter_variable_geq=agg_change_model.filter_variable_geq, + filter_variable_quantile_leq=agg_change_model.filter_variable_quantile_leq, + filter_variable_quantile_geq=agg_change_model.filter_variable_quantile_geq, + filter_variable_quantile_value=agg_change_model.filter_variable_quantile_value, aggregate_function=agg_change_model.aggregate_function, reportelement_id=report_element.id, baseline_value=agg_change_model.baseline_value, @@ -364,10 +408,23 @@ def create_report_element( "filter_variable_value": agg_change_table.filter_variable_value, "filter_variable_leq": agg_change_table.filter_variable_leq, "filter_variable_geq": agg_change_table.filter_variable_geq, + "filter_variable_quantile_leq": agg_change_table.filter_variable_quantile_leq, + "filter_variable_quantile_geq": agg_change_table.filter_variable_quantile_geq, + "filter_variable_quantile_value": agg_change_table.filter_variable_quantile_value, }) + if request.data_type in ["Aggregate", "AggregateChange"] and request.data: + save_time = time.time() + print(f"[PERFORMANCE] Database save took {save_time - save_start:.2f} seconds") + + commit_start = time.time() session.commit() session.refresh(report_element) + commit_time = time.time() + print(f"[PERFORMANCE] Database commit took {commit_time - commit_start:.2f} seconds") + + total_time = time.time() + print(f"[PERFORMANCE] Total report element creation took {total_time - start_time:.2f} seconds") # Convert to dict for JSON serialization return { From 63974b6cc08c0b434de3e035a931c9576aefeb77 Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Mon, 29 Sep 2025 23:45:43 +0100 Subject: [PATCH 11/11] Update --- .../src/policyengine_api_full/routers/data_requests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/projects/policyengine-api-full/src/policyengine_api_full/routers/data_requests.py b/projects/policyengine-api-full/src/policyengine_api_full/routers/data_requests.py index 56adaf8a..026ede91 100644 --- a/projects/policyengine-api-full/src/policyengine_api_full/routers/data_requests.py +++ b/projects/policyengine-api-full/src/policyengine_api_full/routers/data_requests.py @@ -107,7 +107,6 @@ async def parse_data_request( user_prompt = f"""Request: {request.description} Variables: {json.dumps(variables_context)} -{"Simulations already selected." if request.simulation_ids else ""} Return JSON:"""