test: switch from pyright to pyrefly for typing

perf: minimize HTML while templating
This commit is contained in:
Markbeep
2025-12-29 19:06:20 +00:00
parent 23aaf169d7
commit 63c9d147b5
20 changed files with 163 additions and 189 deletions
+2 -1
View File
@@ -13,7 +13,8 @@
"tamasfe.even-better-toml",
"bradlc.vscode-tailwindcss",
"samuelcolvin.jinjahtml",
"yzhang.markdown-all-in-one"
"yzhang.markdown-all-in-one",
"meta.pyrefly"
]
}
}
+3 -4
View File
@@ -48,11 +48,10 @@ jobs:
- run: uv sync --all-extras
- run: echo "$PWD/.venv/bin" >> $GITHUB_PATH
- name: Run Type Checks
run: uv run pyrefly check
- uses: jakebailey/pyright-action@v2
with:
extra-args: app # only check apps dir
- run: echo "$PWD/.venv/bin" >> $GITHUB_PATH
- name: Check SQLite Alembic Migrations
run: |
-77
View File
@@ -1,77 +0,0 @@
name: Build Nix Docker Image
on:
push:
paths:
- "**.py"
- "**.html"
- "**.css"
- "**.yaml"
- "**.nix"
branches:
- main
env:
DOCKER_TAG: ${{ secrets.DOCKER_HUB_USERNAME }}/audiobookrequest
jobs:
website:
strategy:
matrix:
include:
- os: ubuntu-24.04
arch: linux/amd64
- os: ubuntu-24.04-arm
arch: linux/arm64
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
- name: Enable magic Nix cache
uses: DeterminateSystems/flakehub-cache-action@main
- name: Set version
run: |
# if [ "${{ github.event_name }}" == "release" ]; then
# VERSION="${{ github.event.release.tag_name }}"
# echo $VERSION > static/version
# else
github_sha_hash=${{ github.sha }}
echo nix-test:${github_sha_hash:0:7} > static/version
# fi
- name: Build Docker image using Nix
run: nix build ".#docker"
- name: Load image
run: |
export IMAGE_TAG=$(docker load < result | grep -Po 'Loaded image: \K.*')
echo "Loaded image ${IMAGE_TAG}"
echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_ENV
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Push image
run: |
# if [ "${{ github.event_name }}" == "release" ]; then
# VERSION="${{ github.event.release.tag_name }}"
# MAJOR=$(echo $VERSION | cut -d. -f1)
# MINOR=$(echo $VERSION | cut -d. -f2)
# PATCH=$(echo $VERSION | cut -d. -f3)
# docker tag $IMAGE_TAG "${DOCKER_TAG}:$VERSION"
# docker tag $IMAGE_TAG "${DOCKER_TAG}:$MAJOR.$MINOR"
# docker tag $IMAGE_TAG "${DOCKER_TAG}:$MAJOR"
# docker tag $IMAGE_TAG "${DOCKER_TAG}:latest"
# docker push "${DOCKER_TAG}:$VERSION"
# docker push "${DOCKER_TAG}:$MAJOR.$MINOR"
# docker push "${DOCKER_TAG}:$MAJOR"
# docker push "${DOCKER_TAG}:latest"
# else
docker tag $IMAGE_TAG "${DOCKER_TAG}:nix-test"
docker push "${DOCKER_TAG}:nix-test"
# fi
+1 -1
View File
@@ -19,7 +19,7 @@ if config.config_file_name is not None:
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = models.BaseModel.metadata
target_metadata = models.BaseSQLModel.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
+3 -2
View File
@@ -1,3 +1,4 @@
from typing import cast
import asyncio
import time
from datetime import datetime
@@ -49,7 +50,7 @@ def clear_old_book_caches(session: Session):
col(BookRequest.updated_at) < datetime.fromtimestamp(time.time() - REFETCH_TTL),
col(BookRequest.user_username).is_(None),
)
result: CursorResult = session.execute(delete_query) # type: ignore[reportDeprecated]
result = cast(CursorResult, session.execute(delete_query))
session.commit()
logger.debug("Cleared old book caches", rowcount=result.rowcount)
@@ -58,7 +59,7 @@ def get_region_from_settings() -> audible_region_type:
region = Settings().app.default_region
if region not in audible_regions:
return "us"
return region
return cast(audible_region_type, region)
async def _get_audnexus_book(
+1 -1
View File
@@ -82,7 +82,7 @@ def create_valued_configuration(
if not isinstance(_value, IndexerConfiguration):
logger.debug("Skipping key", key=key)
continue
value: IndexerConfiguration[Any] = _value # pyright: ignore[reportUnknownVariableType]
value: IndexerConfiguration[Any] = _value
config_value = indexer_configuration_cache.get(session, key)
if config_value is None:
+13 -16
View File
@@ -1,15 +1,14 @@
# pyright: reportUnknownVariableType=false
from pydantic import ConfigDict, BaseModel
import json
import uuid
from datetime import datetime
from enum import Enum
from typing import Annotated, Literal, Optional, Union
import pydantic
from sqlmodel import JSON, Column, DateTime, Field, SQLModel, UniqueConstraint, func
class BaseModel(SQLModel):
class BaseSQLModel(SQLModel):
pass
@@ -19,7 +18,7 @@ class GroupEnum(str, Enum):
admin = "admin"
class User(BaseModel, table=True):
class User(BaseSQLModel, table=True):
username: str = Field(primary_key=True)
password: str
group: GroupEnum = Field(
@@ -60,7 +59,7 @@ class User(BaseModel, table=True):
return self.username == username
class BaseBook(BaseModel):
class BaseBook(BaseSQLModel):
asin: str
title: str
subtitle: Optional[str]
@@ -114,11 +113,10 @@ class BookRequest(BaseBook, table=True):
UniqueConstraint("asin", "user_username", name="unique_asin_user"),
)
class Config: # pyright: ignore[reportIncompatibleVariableOverride]
arbitrary_types_allowed = True
model_config = ConfigDict(arbitrary_types_allowed=True)
class ManualBookRequest(BaseModel, table=True):
class ManualBookRequest(BaseSQLModel, table=True):
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
user_username: str = Field(foreign_key="user.username", ondelete="CASCADE")
title: str
@@ -138,11 +136,10 @@ class ManualBookRequest(BaseModel, table=True):
)
downloaded: bool = False
class Config: # pyright: ignore[reportIncompatibleVariableOverride]
arbitrary_types_allowed = True
model_config = ConfigDict(arbitrary_types_allowed=True)
class BookMetadata(BaseModel):
class BookMetadata(BaseSQLModel):
"""extra metadata that can be added to sources to better rank them"""
title: Optional[str] = None
@@ -152,7 +149,7 @@ class BookMetadata(BaseModel):
filetype: Optional[str] = None
class BaseSource(BaseModel):
class BaseSource(BaseSQLModel):
guid: str
indexer_id: int
indexer: str
@@ -187,14 +184,14 @@ ProwlarrSource = Annotated[
]
class Indexer(pydantic.BaseModel, frozen=True):
class Indexer(BaseModel, frozen=True):
id: int
name: str
enable: bool
privacy: str
class Config(BaseModel, table=True):
class Config(BaseSQLModel, table=True):
key: str = Field(primary_key=True)
value: str
@@ -210,7 +207,7 @@ class NotificationBodyTypeEnum(str, Enum):
json = "json"
class Notification(BaseModel, table=True):
class Notification(BaseSQLModel, table=True):
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
name: str
url: str
@@ -225,7 +222,7 @@ class Notification(BaseModel, table=True):
return json.dumps(self.headers)
class APIKey(BaseModel, table=True):
class APIKey(BaseSQLModel, table=True):
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
user_username: str = Field(foreign_key="user.username", ondelete="CASCADE")
name: str
+1 -3
View File
@@ -1,6 +1,4 @@
import asyncio
from types import CoroutineType
from typing import Any
from app.internal.indexers.abstract import SessionContainer
from app.internal.indexers.indexer_util import get_indexer_contexts
@@ -24,7 +22,7 @@ async def edit_source_metadata(
if exc:
logger.error("Failed to setup indexer", error=str(exc))
coros: list[CoroutineType[Any, Any, None]] = []
coros = []
for source in sources:
for context in contexts:
if await context.indexer.is_matching_source(source, container):
+2 -2
View File
@@ -81,7 +81,7 @@ class CompareSource:
quality_range = quality_config.get_range(
self.session, "quality_unknown_audio"
)
case "unknown":
case "unknown" | _:
quality_range = quality_config.get_range(
self.session, "quality_unknown"
)
@@ -262,7 +262,7 @@ def fuzzy_author_narrator_match(
return 0
score = 0
for book_person in book_people:
best_match = 0
best_match = 0.0
for source_person in source_people:
match_score = fuzz.token_set_ratio(
book_person, source_person, processor=utils.default_process
-2
View File
@@ -1,5 +1,3 @@
# pyright: basic
import os
from collections import defaultdict
+2 -2
View File
@@ -7,7 +7,7 @@ from sqlmodel import Session
from app.internal.auth.authentication import APIKeyAuth, DetailedUser
from app.internal.indexers.abstract import SessionContainer
from app.internal.indexers.indexer_util import get_indexer_contexts
from app.internal.models import BaseModel, GroupEnum
from app.internal.models import BaseSQLModel, GroupEnum
from app.routers.settings.indexers import update_single_indexer
from app.util.connection import get_connection
from app.util.db import get_session
@@ -48,7 +48,7 @@ async def update_indexer(
return Response(status_code=204)
class StringConfigurationResponse(BaseModel):
class StringConfigurationResponse(BaseSQLModel):
name: str
description: Optional[str] = None
default: Optional[str] = None
+2 -2
View File
@@ -89,7 +89,7 @@ async def login(
redirect_uri=auth_redirect_uri,
)
state = jwt.encode( # pyright: ignore[reportUnknownMemberType]
state = jwt.encode(
{"redirect_uri": redirect_uri},
auth_config.get_auth_secret(session),
algorithm="HS256",
@@ -247,7 +247,7 @@ async def login_oidc(
request.session["exp"] = expires
if state:
decoded = jwt.decode( # pyright: ignore[reportUnknownMemberType]
decoded = jwt.decode(
state,
auth_config.get_auth_secret(session),
algorithms=["HS256"],
+2 -1
View File
@@ -34,7 +34,8 @@ etag_cache: dict[PathLike[str] | str, str] = {}
def add_cache_headers(func: Callable[..., FileResponse]):
def wrapper(v: str):
file = func()
if not (etag := etag_cache.get(file.path)) or Settings().app.debug:
etag = etag_cache.get(file.path)
if not etag or Settings().app.debug:
with open(file.path, "rb") as f:
etag = hashlib.sha1(f.read(), usedforsecurity=False).hexdigest()
etag_cache[file.path] = etag
+4 -4
View File
@@ -4,7 +4,7 @@ from contextlib import asynccontextmanager
from typing import Annotated, Any, Literal, Mapping, Optional, cast
from aiohttp import ClientSession
from apscheduler.schedulers.asyncio import AsyncIOScheduler # pyright: ignore[reportMissingTypeStubs]
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from fastapi import APIRouter, Depends, FastAPI, Form, Request, Security
from sqlmodel import Session
@@ -39,10 +39,10 @@ async def check_indexer_file_changes():
@asynccontextmanager
async def lifespan(app: FastAPI):
scheduler = AsyncIOScheduler()
scheduler.add_job(check_indexer_file_changes, "interval", seconds=15) # pyright: ignore[reportUnknownMemberType]
scheduler.start() # pyright: ignore[reportUnknownMemberType]
scheduler.add_job(check_indexer_file_changes, "interval", seconds=15)
scheduler.start()
yield
scheduler.shutdown() # pyright: ignore[reportUnknownMemberType]
scheduler.shutdown()
router = APIRouter(prefix="/indexers", lifespan=lifespan)
+4 -8
View File
@@ -39,12 +39,10 @@ class StringConfigCache[L: str](ABC):
_cache: dict[L, str] = {}
@overload
def get(self, session: Session, key: L) -> Optional[str]:
pass
def get(self, session: Session, key: L) -> Optional[str]: ...
@overload
def get(self, session: Session, key: L, default: str) -> str:
pass
def get(self, session: Session, key: L, default: str) -> str: ...
def get(
self, session: Session, key: L, default: Optional[str] = None
@@ -75,12 +73,10 @@ class StringConfigCache[L: str](ABC):
del self._cache[key]
@overload
def get_int(self, session: Session, key: L) -> Optional[int]:
pass
def get_int(self, session: Session, key: L) -> Optional[int]: ...
@overload
def get_int(self, session: Session, key: L, default: int) -> int:
pass
def get_int(self, session: Session, key: L, default: int) -> int: ...
def get_int(
self, session: Session, key: L, default: Optional[int] = None
+2 -2
View File
@@ -18,7 +18,7 @@ else:
def get_session():
with Session(engine) as session:
if not Settings().db.use_postgres:
session.execute(text("PRAGMA foreign_keys=ON")) # pyright: ignore[reportDeprecated]
session.execute(text("PRAGMA foreign_keys=ON"))
yield session
@@ -27,5 +27,5 @@ def get_session():
def open_session():
with Session(engine) as session:
if not Settings().db.use_postgres:
session.execute(text("PRAGMA foreign_keys=ON")) # pyright: ignore[reportDeprecated]
session.execute(text("PRAGMA foreign_keys=ON"))
yield session
+23 -10
View File
@@ -1,3 +1,5 @@
from jinja2_htmlmin import minify_loader
from jinja2 import Environment, FileSystemLoader
from typing import Any, Mapping, overload
import markdown
@@ -8,22 +10,33 @@ from starlette.background import BackgroundTask
from app.internal.auth.authentication import DetailedUser
from app.internal.env_settings import Settings
templates = Jinja2Blocks(directory="templates")
templates.env.filters["zfill"] = lambda val, num: str(val).zfill(num) # pyright: ignore[reportUnknownLambdaType,reportUnknownMemberType,reportUnknownArgumentType]
templates.env.filters["toJSstring"] = ( # pyright: ignore[reportUnknownLambdaType,reportUnknownMemberType,reportUnknownArgumentType]
lambda val: f"'{str(val).replace("'", "\\'").replace('\n', '\\n')}'" # pyright: ignore[reportUnknownLambdaType,reportUnknownMemberType,reportUnknownArgumentType]
templates = Jinja2Blocks(
env=Environment(
loader=minify_loader(
FileSystemLoader("templates"),
remove_comments=True, # pyrefly: ignore[bad-argument-type]
remove_empty_space=True, # pyrefly: ignore[bad-argument-type]
remove_all_empty_space=True, # pyrefly: ignore[bad-argument-type]
reduce_boolean_attributes=True, # pyrefly: ignore[bad-argument-type]
)
)
)
templates.env.globals["vars"] = vars # pyright: ignore[reportUnknownMemberType]
templates.env.globals["getattr"] = getattr # pyright: ignore[reportUnknownMemberType]
templates.env.globals["version"] = Settings().app.version # pyright: ignore[reportUnknownMemberType]
templates.env.globals["json_regexp"] = ( # pyright: ignore[reportUnknownMemberType]
templates.env.filters["zfill"] = lambda val, num: str(val).zfill(num)
templates.env.filters["toJSstring"] = (
lambda val: f"'{str(val).replace("'", "\\'").replace('\n', '\\n')}'"
)
templates.env.globals["vars"] = vars
templates.env.globals["getattr"] = getattr
templates.env.globals["version"] = Settings().app.version
templates.env.globals["json_regexp"] = (
r'^\{\s*(?:"[^"\\]*(?:\\.[^"\\]*)*"\s*:\s*"[^"\\]*(?:\\.[^"\\]*)*"\s*(?:,\s*"[^"\\]*(?:\\.[^"\\]*)*"\s*:\s*"[^"\\]*(?:\\.[^"\\]*)*"\s*)*)?\}$'
)
templates.env.globals["base_url"] = Settings().app.base_url.rstrip("/") # pyright: ignore[reportUnknownMemberType]
templates.env.globals["base_url"] = Settings().app.base_url.rstrip("/")
with open("CHANGELOG.md", "r") as file:
changelog_content = file.read()
templates.env.globals["changelog"] = markdown.markdown(changelog_content) # pyright: ignore[reportUnknownMemberType]
templates.env.globals["changelog"] = markdown.markdown(changelog_content)
@overload
+1 -1
View File
@@ -21,7 +21,7 @@ tailwind: node_modules
tailwindcss -i static/tw.css -o static/globals.css --watch
types:
uv run pyright app
uv run pyrefly check
uv run djlint templates
uv run ruff format --check app
uv run alembic check
+13 -10
View File
@@ -30,6 +30,7 @@ dependencies = [
"markdown>=3.8.2",
"apscheduler>=3.11.0",
"psycopg2-binary>=2.9.10",
"jinja2-htmlmin>=1.0.1",
]
# setuptools by default expects a "src" folder structure
@@ -39,18 +40,20 @@ where = ["."]
[tool.uv]
package = false
[tool.pyright]
include = ["**/*.py"]
exclude = ["**/__pycache__", "**/.venv", "**/.direnv"]
ignore = []
typeCheckingMode = "strict"
reportUnknownParameterType = true
reportMissingParameterType = true
[tool.djlint]
profile = "jinja"
ignore = "H021"
[tool.pyrefly]
project-includes = ["app"]
[tool.pyrefly.errors]
deprecated = false
[dependency-groups]
dev = ["djlint", "pyright", "ruff"]
dev = [
"djlint",
"pyrefly>=0.46.2",
"ruff>=0.14.10",
"ty>=0.0.8",
]
Generated
+84 -40
View File
@@ -223,6 +223,7 @@ dependencies = [
{ name = "itsdangerous" },
{ name = "jinja2" },
{ name = "jinja2-fragments" },
{ name = "jinja2-htmlmin" },
{ name = "markdown" },
{ name = "psycopg2-binary" },
{ name = "pydantic" },
@@ -244,8 +245,9 @@ dependencies = [
[package.dev-dependencies]
dev = [
{ name = "djlint" },
{ name = "pyright" },
{ name = "pyrefly" },
{ name = "ruff" },
{ name = "ty" },
]
[package.metadata]
@@ -259,6 +261,7 @@ requires-dist = [
{ name = "itsdangerous" },
{ name = "jinja2" },
{ name = "jinja2-fragments" },
{ name = "jinja2-htmlmin", specifier = ">=1.0.1" },
{ name = "markdown", specifier = ">=3.8.2" },
{ name = "psycopg2-binary", specifier = ">=2.9.10" },
{ name = "pydantic" },
@@ -280,8 +283,9 @@ requires-dist = [
[package.metadata.requires-dev]
dev = [
{ name = "djlint" },
{ name = "pyright" },
{ name = "ruff" },
{ name = "pyrefly", specifier = ">=0.46.2" },
{ name = "ruff", specifier = ">=0.14.10" },
{ name = "ty", specifier = ">=0.0.8" },
]
[[package]]
@@ -638,6 +642,14 @@ 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 = "htmlmin2"
version = "0.1.13"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/be/31/a76f4bfa885f93b8167cb4c85cf32b54d1f64384d0b897d45bc6d19b7b45/htmlmin2-0.1.13-py3-none-any.whl", hash = "sha256:75609f2a42e64f7ce57dbff28a39890363bde9e7e5885db633317efbdf8c79a2", size = 34486, upload-time = "2023-03-14T21:28:30.388Z" },
]
[[package]]
name = "httpcore"
version = "1.0.9"
@@ -730,6 +742,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e2/05/2a29edac68484f1e1171d257773f73b436a240bd2640ab66d27bfadb69ae/jinja2_fragments-1.9.0-py3-none-any.whl", hash = "sha256:69b91e7e2f325ea7e391e36a9abcc572db967e2bf3afd35f74fcb78fc9f8c6c5", size = 14433, upload-time = "2025-04-20T22:46:48.778Z" },
]
[[package]]
name = "jinja2-htmlmin"
version = "1.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "htmlmin2" },
{ name = "jinja2" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1a/ef/d642475f0cc66c89f3347924a23a173751dc160ba44269e76db48a89f2a7/jinja2_htmlmin-1.0.1.tar.gz", hash = "sha256:c53398ff12799a79ab75e471e9c26be9c837d1c10103da308280bd96c45588af", size = 21088, upload-time = "2025-07-29T17:08:43.698Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/13/6f/89b2029eb311bcab200d875599ab6594e186d865c181d08b686f60d3d26e/jinja2_htmlmin-1.0.1-py3-none-any.whl", hash = "sha256:b9c6c43e88443a238e3796054c4a54cd5bd755dfc3c65b775910813988a89da3", size = 4247, upload-time = "2025-07-29T17:08:42.595Z" },
]
[[package]]
name = "jsbeautifier"
version = "1.15.4"
@@ -895,15 +920,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fd/69/b547032297c7e63ba2af494edba695d781af8a0c6e89e4d06cf848b21d80/multidict-6.6.4-py3-none-any.whl", hash = "sha256:27d8f8e125c07cb954e54d75d04905a9bba8a439c1d84aca94949d4d03d8601c", size = 12313, upload-time = "2025-08-11T12:08:46.891Z" },
]
[[package]]
name = "nodeenv"
version = "1.9.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" }
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 = "pathspec"
version = "0.12.1"
@@ -1105,16 +1121,19 @@ wheels = [
]
[[package]]
name = "pyright"
version = "1.1.403"
name = "pyrefly"
version = "0.46.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nodeenv" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fe/f6/35f885264ff08c960b23d1542038d8da86971c5d8c955cfab195a4f672d7/pyright-1.1.403.tar.gz", hash = "sha256:3ab69b9f41c67fb5bbb4d7a36243256f0d549ed3608678d381d5f51863921104", size = 3913526, upload-time = "2025-07-09T07:15:52.882Z" }
sdist = { url = "https://files.pythonhosted.org/packages/a5/60/40ba98bed6cdc375d80d1be92b0980fb9425102993246dddd7a5cd9db9e2/pyrefly-0.46.2.tar.gz", hash = "sha256:d680c411948e41fcfc1d37f7c7a3d566c52e226d48eef0b1d4c922ddb0465abb", size = 4762179, upload-time = "2025-12-29T16:44:52.683Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/49/b6/b04e5c2f41a5ccad74a1a4759da41adb20b4bc9d59a5e08d29ba60084d07/pyright-1.1.403-py3-none-any.whl", hash = "sha256:c0eeca5aa76cbef3fcc271259bbd785753c7ad7bcac99a9162b4c4c7daed23b3", size = 5684504, upload-time = "2025-07-09T07:15:50.958Z" },
{ url = "https://files.pythonhosted.org/packages/dc/05/5fa9cd08d9ed6f791158707cd7e860f283e6a44fddea1d5abb9dcb3a153c/pyrefly-0.46.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:94090428807796247b42f2c5840cff4d3262e1a64dcc23ba9d56cd0708e328b0", size = 11642476, upload-time = "2025-12-29T16:44:33.386Z" },
{ url = "https://files.pythonhosted.org/packages/57/fd/d4c73518cbf8629c335e2ef6fe54d75d12782cb882d883c1fc964679f0c8/pyrefly-0.46.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1dbe54b8ef0a3697614d3a622cd15eb642415bed4c72540372cbcbfb5ff0119a", size = 11262304, upload-time = "2025-12-29T16:44:35.823Z" },
{ url = "https://files.pythonhosted.org/packages/01/ff/9f1056e2437b7067d950198312c3c26ead6c6bae57d14a2ba075bae82b90/pyrefly-0.46.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f225303ff76ad6b581abb566c160fbc64bcb9cf4260151c4897853485fab619b", size = 31476614, upload-time = "2025-12-29T16:44:38.4Z" },
{ url = "https://files.pythonhosted.org/packages/8d/d4/302d853a3b34f8eec57ab6417c6b03d3f496fcb965fcf92893d5c6ff9179/pyrefly-0.46.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81903be4bcdca9e1f24b7ad0a9768cd8094f74847c5f67ceec731bd2e933e3be", size = 33695293, upload-time = "2025-12-29T16:44:40.994Z" },
{ url = "https://files.pythonhosted.org/packages/af/2c/458e271a63f5ddeb407b16277d1520425691f87627bce5cafac926339e3f/pyrefly-0.46.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4bce05c26954fea7aa43ce51675ade8ea8770fe187443833e8751637864ebed", size = 34734729, upload-time = "2025-12-29T16:44:43.628Z" },
{ url = "https://files.pythonhosted.org/packages/1d/e6/ac334eb16962f3da390859e70461be9bc8ebb1f7bdb6d11a99e846263d1e/pyrefly-0.46.2-py3-none-win32.whl", hash = "sha256:a95a95a53e453f381e4cf4da5cdc67b21852e9b13fafd71f97dcdc008e01b277", size = 10725298, upload-time = "2025-12-29T16:44:46.254Z" },
{ url = "https://files.pythonhosted.org/packages/c2/e0/96ca0587e76c08eb8c917b44369d953bf22e255f43568bf612811676ff69/pyrefly-0.46.2-py3-none-win_amd64.whl", hash = "sha256:e75d43f7149bc5d472edfa9071a95f51b2cd3024ff6d4808cddbcfbdfdd32d7a", size = 11417266, upload-time = "2025-12-29T16:44:48.212Z" },
{ url = "https://files.pythonhosted.org/packages/a1/ef/11687e55e5b3d92f9695c713d9f90af181c2a4376c032ec34650ea8a035d/pyrefly-0.46.2-py3-none-win_arm64.whl", hash = "sha256:1a467d24f33500d0bfc8fe8f8fe3d1da5d9e4ad9637fc49d4f83b3f2cef7d8d6", size = 10962676, upload-time = "2025-12-29T16:44:50.374Z" },
]
[[package]]
@@ -1339,28 +1358,28 @@ wheels = [
[[package]]
name = "ruff"
version = "0.12.9"
version = "0.14.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/4a/45/2e403fa7007816b5fbb324cb4f8ed3c7402a927a0a0cb2b6279879a8bfdc/ruff-0.12.9.tar.gz", hash = "sha256:fbd94b2e3c623f659962934e52c2bea6fc6da11f667a427a368adaf3af2c866a", size = 5254702, upload-time = "2025-08-14T16:08:55.2Z" }
sdist = { url = "https://files.pythonhosted.org/packages/57/08/52232a877978dd8f9cf2aeddce3e611b40a63287dfca29b6b8da791f5e8d/ruff-0.14.10.tar.gz", hash = "sha256:9a2e830f075d1a42cd28420d7809ace390832a490ed0966fe373ba288e77aaf4", size = 5859763, upload-time = "2025-12-18T19:28:57.98Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ad/20/53bf098537adb7b6a97d98fcdebf6e916fcd11b2e21d15f8c171507909cc/ruff-0.12.9-py3-none-linux_armv6l.whl", hash = "sha256:fcebc6c79fcae3f220d05585229463621f5dbf24d79fdc4936d9302e177cfa3e", size = 11759705, upload-time = "2025-08-14T16:08:12.968Z" },
{ url = "https://files.pythonhosted.org/packages/20/4d/c764ee423002aac1ec66b9d541285dd29d2c0640a8086c87de59ebbe80d5/ruff-0.12.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aed9d15f8c5755c0e74467731a007fcad41f19bcce41cd75f768bbd687f8535f", size = 12527042, upload-time = "2025-08-14T16:08:16.54Z" },
{ url = "https://files.pythonhosted.org/packages/8b/45/cfcdf6d3eb5fc78a5b419e7e616d6ccba0013dc5b180522920af2897e1be/ruff-0.12.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5b15ea354c6ff0d7423814ba6d44be2807644d0c05e9ed60caca87e963e93f70", size = 11724457, upload-time = "2025-08-14T16:08:18.686Z" },
{ url = "https://files.pythonhosted.org/packages/72/e6/44615c754b55662200c48bebb02196dbb14111b6e266ab071b7e7297b4ec/ruff-0.12.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d596c2d0393c2502eaabfef723bd74ca35348a8dac4267d18a94910087807c53", size = 11949446, upload-time = "2025-08-14T16:08:21.059Z" },
{ url = "https://files.pythonhosted.org/packages/fd/d1/9b7d46625d617c7df520d40d5ac6cdcdf20cbccb88fad4b5ecd476a6bb8d/ruff-0.12.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1b15599931a1a7a03c388b9c5df1bfa62be7ede6eb7ef753b272381f39c3d0ff", size = 11566350, upload-time = "2025-08-14T16:08:23.433Z" },
{ url = "https://files.pythonhosted.org/packages/59/20/b73132f66f2856bc29d2d263c6ca457f8476b0bbbe064dac3ac3337a270f/ruff-0.12.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d02faa2977fb6f3f32ddb7828e212b7dd499c59eb896ae6c03ea5c303575756", size = 13270430, upload-time = "2025-08-14T16:08:25.837Z" },
{ url = "https://files.pythonhosted.org/packages/a2/21/eaf3806f0a3d4c6be0a69d435646fba775b65f3f2097d54898b0fd4bb12e/ruff-0.12.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:17d5b6b0b3a25259b69ebcba87908496e6830e03acfb929ef9fd4c58675fa2ea", size = 14264717, upload-time = "2025-08-14T16:08:27.907Z" },
{ url = "https://files.pythonhosted.org/packages/d2/82/1d0c53bd37dcb582b2c521d352fbf4876b1e28bc0d8894344198f6c9950d/ruff-0.12.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72db7521860e246adbb43f6ef464dd2a532ef2ef1f5dd0d470455b8d9f1773e0", size = 13684331, upload-time = "2025-08-14T16:08:30.352Z" },
{ url = "https://files.pythonhosted.org/packages/3b/2f/1c5cf6d8f656306d42a686f1e207f71d7cebdcbe7b2aa18e4e8a0cb74da3/ruff-0.12.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a03242c1522b4e0885af63320ad754d53983c9599157ee33e77d748363c561ce", size = 12739151, upload-time = "2025-08-14T16:08:32.55Z" },
{ url = "https://files.pythonhosted.org/packages/47/09/25033198bff89b24d734e6479e39b1968e4c992e82262d61cdccaf11afb9/ruff-0.12.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fc83e4e9751e6c13b5046d7162f205d0a7bac5840183c5beebf824b08a27340", size = 12954992, upload-time = "2025-08-14T16:08:34.816Z" },
{ url = "https://files.pythonhosted.org/packages/52/8e/d0dbf2f9dca66c2d7131feefc386523404014968cd6d22f057763935ab32/ruff-0.12.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:881465ed56ba4dd26a691954650de6ad389a2d1fdb130fe51ff18a25639fe4bb", size = 12899569, upload-time = "2025-08-14T16:08:36.852Z" },
{ url = "https://files.pythonhosted.org/packages/a0/bd/b614d7c08515b1428ed4d3f1d4e3d687deffb2479703b90237682586fa66/ruff-0.12.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:43f07a3ccfc62cdb4d3a3348bf0588358a66da756aa113e071b8ca8c3b9826af", size = 11751983, upload-time = "2025-08-14T16:08:39.314Z" },
{ url = "https://files.pythonhosted.org/packages/58/d6/383e9f818a2441b1a0ed898d7875f11273f10882f997388b2b51cb2ae8b5/ruff-0.12.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:07adb221c54b6bba24387911e5734357f042e5669fa5718920ee728aba3cbadc", size = 11538635, upload-time = "2025-08-14T16:08:41.297Z" },
{ url = "https://files.pythonhosted.org/packages/20/9c/56f869d314edaa9fc1f491706d1d8a47747b9d714130368fbd69ce9024e9/ruff-0.12.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f5cd34fabfdea3933ab85d72359f118035882a01bff15bd1d2b15261d85d5f66", size = 12534346, upload-time = "2025-08-14T16:08:43.39Z" },
{ url = "https://files.pythonhosted.org/packages/bd/4b/d8b95c6795a6c93b439bc913ee7a94fda42bb30a79285d47b80074003ee7/ruff-0.12.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f6be1d2ca0686c54564da8e7ee9e25f93bdd6868263805f8c0b8fc6a449db6d7", size = 13017021, upload-time = "2025-08-14T16:08:45.889Z" },
{ url = "https://files.pythonhosted.org/packages/c7/c1/5f9a839a697ce1acd7af44836f7c2181cdae5accd17a5cb85fcbd694075e/ruff-0.12.9-py3-none-win32.whl", hash = "sha256:cc7a37bd2509974379d0115cc5608a1a4a6c4bff1b452ea69db83c8855d53f93", size = 11734785, upload-time = "2025-08-14T16:08:48.062Z" },
{ url = "https://files.pythonhosted.org/packages/fa/66/cdddc2d1d9a9f677520b7cfc490d234336f523d4b429c1298de359a3be08/ruff-0.12.9-py3-none-win_amd64.whl", hash = "sha256:6fb15b1977309741d7d098c8a3cb7a30bc112760a00fb6efb7abc85f00ba5908", size = 12840654, upload-time = "2025-08-14T16:08:50.158Z" },
{ url = "https://files.pythonhosted.org/packages/ac/fd/669816bc6b5b93b9586f3c1d87cd6bc05028470b3ecfebb5938252c47a35/ruff-0.12.9-py3-none-win_arm64.whl", hash = "sha256:63c8c819739d86b96d500cce885956a1a48ab056bbcbc61b747ad494b2485089", size = 11949623, upload-time = "2025-08-14T16:08:52.233Z" },
{ url = "https://files.pythonhosted.org/packages/60/01/933704d69f3f05ee16ef11406b78881733c186fe14b6a46b05cfcaf6d3b2/ruff-0.14.10-py3-none-linux_armv6l.whl", hash = "sha256:7a3ce585f2ade3e1f29ec1b92df13e3da262178df8c8bdf876f48fa0e8316c49", size = 13527080, upload-time = "2025-12-18T19:29:25.642Z" },
{ url = "https://files.pythonhosted.org/packages/df/58/a0349197a7dfa603ffb7f5b0470391efa79ddc327c1e29c4851e85b09cc5/ruff-0.14.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:674f9be9372907f7257c51f1d4fc902cb7cf014b9980152b802794317941f08f", size = 13797320, upload-time = "2025-12-18T19:29:02.571Z" },
{ url = "https://files.pythonhosted.org/packages/7b/82/36be59f00a6082e38c23536df4e71cdbc6af8d7c707eade97fcad5c98235/ruff-0.14.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d85713d522348837ef9df8efca33ccb8bd6fcfc86a2cde3ccb4bc9d28a18003d", size = 12918434, upload-time = "2025-12-18T19:28:51.202Z" },
{ url = "https://files.pythonhosted.org/packages/a6/00/45c62a7f7e34da92a25804f813ebe05c88aa9e0c25e5cb5a7d23dd7450e3/ruff-0.14.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6987ebe0501ae4f4308d7d24e2d0fe3d7a98430f5adfd0f1fead050a740a3a77", size = 13371961, upload-time = "2025-12-18T19:29:04.991Z" },
{ url = "https://files.pythonhosted.org/packages/40/31/a5906d60f0405f7e57045a70f2d57084a93ca7425f22e1d66904769d1628/ruff-0.14.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16a01dfb7b9e4eee556fbfd5392806b1b8550c9b4a9f6acd3dbe6812b193c70a", size = 13275629, upload-time = "2025-12-18T19:29:21.381Z" },
{ url = "https://files.pythonhosted.org/packages/3e/60/61c0087df21894cf9d928dc04bcd4fb10e8b2e8dca7b1a276ba2155b2002/ruff-0.14.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7165d31a925b7a294465fa81be8c12a0e9b60fb02bf177e79067c867e71f8b1f", size = 14029234, upload-time = "2025-12-18T19:29:00.132Z" },
{ url = "https://files.pythonhosted.org/packages/44/84/77d911bee3b92348b6e5dab5a0c898d87084ea03ac5dc708f46d88407def/ruff-0.14.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c561695675b972effb0c0a45db233f2c816ff3da8dcfbe7dfc7eed625f218935", size = 15449890, upload-time = "2025-12-18T19:28:53.573Z" },
{ url = "https://files.pythonhosted.org/packages/e9/36/480206eaefa24a7ec321582dda580443a8f0671fdbf6b1c80e9c3e93a16a/ruff-0.14.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bb98fcbbc61725968893682fd4df8966a34611239c9fd07a1f6a07e7103d08e", size = 15123172, upload-time = "2025-12-18T19:29:23.453Z" },
{ url = "https://files.pythonhosted.org/packages/5c/38/68e414156015ba80cef5473d57919d27dfb62ec804b96180bafdeaf0e090/ruff-0.14.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f24b47993a9d8cb858429e97bdf8544c78029f09b520af615c1d261bf827001d", size = 14460260, upload-time = "2025-12-18T19:29:27.808Z" },
{ url = "https://files.pythonhosted.org/packages/b3/19/9e050c0dca8aba824d67cc0db69fb459c28d8cd3f6855b1405b3f29cc91d/ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59aabd2e2c4fd614d2862e7939c34a532c04f1084476d6833dddef4afab87e9f", size = 14229978, upload-time = "2025-12-18T19:29:11.32Z" },
{ url = "https://files.pythonhosted.org/packages/51/eb/e8dd1dd6e05b9e695aa9dd420f4577debdd0f87a5ff2fedda33c09e9be8c/ruff-0.14.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:213db2b2e44be8625002dbea33bb9c60c66ea2c07c084a00d55732689d697a7f", size = 14338036, upload-time = "2025-12-18T19:29:09.184Z" },
{ url = "https://files.pythonhosted.org/packages/6a/12/f3e3a505db7c19303b70af370d137795fcfec136d670d5de5391e295c134/ruff-0.14.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b914c40ab64865a17a9a5b67911d14df72346a634527240039eb3bd650e5979d", size = 13264051, upload-time = "2025-12-18T19:29:13.431Z" },
{ url = "https://files.pythonhosted.org/packages/08/64/8c3a47eaccfef8ac20e0484e68e0772013eb85802f8a9f7603ca751eb166/ruff-0.14.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1484983559f026788e3a5c07c81ef7d1e97c1c78ed03041a18f75df104c45405", size = 13283998, upload-time = "2025-12-18T19:29:06.994Z" },
{ url = "https://files.pythonhosted.org/packages/12/84/534a5506f4074e5cc0529e5cd96cfc01bb480e460c7edf5af70d2bcae55e/ruff-0.14.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c70427132db492d25f982fffc8d6c7535cc2fd2c83fc8888f05caaa248521e60", size = 13601891, upload-time = "2025-12-18T19:28:55.811Z" },
{ url = "https://files.pythonhosted.org/packages/0d/1e/14c916087d8598917dbad9b2921d340f7884824ad6e9c55de948a93b106d/ruff-0.14.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5bcf45b681e9f1ee6445d317ce1fa9d6cba9a6049542d1c3d5b5958986be8830", size = 14336660, upload-time = "2025-12-18T19:29:16.531Z" },
{ url = "https://files.pythonhosted.org/packages/f2/1c/d7b67ab43f30013b47c12b42d1acd354c195351a3f7a1d67f59e54227ede/ruff-0.14.10-py3-none-win32.whl", hash = "sha256:104c49fc7ab73f3f3a758039adea978869a918f31b73280db175b43a2d9b51d6", size = 13196187, upload-time = "2025-12-18T19:29:19.006Z" },
{ url = "https://files.pythonhosted.org/packages/fb/9c/896c862e13886fae2af961bef3e6312db9ebc6adc2b156fe95e615dee8c1/ruff-0.14.10-py3-none-win_amd64.whl", hash = "sha256:466297bd73638c6bdf06485683e812db1c00c7ac96d4ddd0294a338c62fdc154", size = 14661283, upload-time = "2025-12-18T19:29:30.16Z" },
{ url = "https://files.pythonhosted.org/packages/74/31/b0e29d572670dca3674eeee78e418f20bdf97fa8aa9ea71380885e175ca0/ruff-0.14.10-py3-none-win_arm64.whl", hash = "sha256:e51d046cf6dda98a4633b8a8a771451107413b0f07183b2bef03f075599e44e6", size = 13729839, upload-time = "2025-12-18T19:28:48.636Z" },
]
[[package]]
@@ -1509,6 +1528,31 @@ 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 = "ty"
version = "0.0.8"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/9d/59e955cc39206a0d58df5374808785c45ec2a8a2a230eb1638fbb4fe5c5d/ty-0.0.8.tar.gz", hash = "sha256:352ac93d6e0050763be57ad1e02087f454a842887e618ec14ac2103feac48676", size = 4828477, upload-time = "2025-12-29T13:50:07.193Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/69/2b/dd61f7e50a69c72f72c625d026e9ab64a0db62b2dd32e7426b520e2429c6/ty-0.0.8-py3-none-linux_armv6l.whl", hash = "sha256:a289d033c5576fa3b4a582b37d63395edf971cdbf70d2d2e6b8c95638d1a4fcd", size = 9853417, upload-time = "2025-12-29T13:50:08.979Z" },
{ url = "https://files.pythonhosted.org/packages/90/72/3f1d3c64a049a388e199de4493689a51fc6aa5ff9884c03dea52b4966657/ty-0.0.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:788ea97dc8153a94e476c4d57b2551a9458f79c187c4aba48fcb81f05372924a", size = 9657890, upload-time = "2025-12-29T13:50:27.867Z" },
{ url = "https://files.pythonhosted.org/packages/71/d1/08ac676bd536de3c2baba0deb60e67b3196683a2fabebfd35659d794b5e9/ty-0.0.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1b5f1f3d3e230f35a29e520be7c3d90194a5229f755b721e9092879c00842d31", size = 9180129, upload-time = "2025-12-29T13:50:22.842Z" },
{ url = "https://files.pythonhosted.org/packages/af/93/610000e2cfeea1875900f73a375ba917624b0a008d4b8a6c18c894c8dbbc/ty-0.0.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6da9ed377fbbcec0a3b60b2ca5fd30496e15068f47cef2344ba87923e78ba996", size = 9683517, upload-time = "2025-12-29T13:50:18.658Z" },
{ url = "https://files.pythonhosted.org/packages/05/04/bef50ba7d8580b0140be597de5cc0ba9a63abe50d3f65560235f23658762/ty-0.0.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7d0a2bdce5e701d19eb8d46d9da0fe31340f079cecb7c438f5ac6897c73fc5ba", size = 9676279, upload-time = "2025-12-29T13:50:25.207Z" },
{ url = "https://files.pythonhosted.org/packages/aa/b9/2aff1ef1f41b25898bc963173ae67fc8f04ca666ac9439a9c4e78d5cc0ff/ty-0.0.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef9078799d26d3cc65366e02392e2b78f64f72911b599e80a8497d2ec3117ddb", size = 10073015, upload-time = "2025-12-29T13:50:35.422Z" },
{ url = "https://files.pythonhosted.org/packages/df/0e/9feb6794b6ff0a157c3e6a8eb6365cbfa3adb9c0f7976e2abdc48615dd72/ty-0.0.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:54814ac39b4ab67cf111fc0a236818155cf49828976152378347a7678d30ee89", size = 10961649, upload-time = "2025-12-29T13:49:58.717Z" },
{ url = "https://files.pythonhosted.org/packages/f4/3b/faf7328b14f00408f4f65c9d01efe52e11b9bcc4a79e06187b370457b004/ty-0.0.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4baf0a80398e8b6c68fa36ff85045a50ede1906cd4edb41fb4fab46d471f1d4", size = 10676190, upload-time = "2025-12-29T13:50:01.11Z" },
{ url = "https://files.pythonhosted.org/packages/64/a5/cfeca780de7eeab7852c911c06a84615a174d23e9ae08aae42a645771094/ty-0.0.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ac8e23c3faefc579686799ef1649af8d158653169ad5c3a7df56b152781eeb67", size = 10438641, upload-time = "2025-12-29T13:50:29.664Z" },
{ url = "https://files.pythonhosted.org/packages/0e/8d/8667c7e0ac9f13c461ded487c8d7350f440cd39ba866d0160a8e1b1efd6c/ty-0.0.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b558a647a073d0c25540aaa10f8947de826cb8757d034dd61ecf50ab8dbd77bf", size = 10214082, upload-time = "2025-12-29T13:50:31.531Z" },
{ url = "https://files.pythonhosted.org/packages/f8/11/e563229870e2c1d089e7e715c6c3b7605a34436dddf6f58e9205823020c2/ty-0.0.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:8c0104327bf480508bd81f320e22074477df159d9eff85207df39e9c62ad5e96", size = 9664364, upload-time = "2025-12-29T13:50:05.443Z" },
{ url = "https://files.pythonhosted.org/packages/b1/ad/05b79b778bf5237bcd7ee08763b226130aa8da872cbb151c8cfa2e886203/ty-0.0.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:496f1cb87261dd1a036a5609da80ee13de2e6ee4718a661bfa2afb91352fe528", size = 9679440, upload-time = "2025-12-29T13:50:11.289Z" },
{ url = "https://files.pythonhosted.org/packages/12/b5/23ba887769c4a7b8abfd1b6395947dc3dcc87533fbf86379d3a57f87ae8f/ty-0.0.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2c488031f92a075ae39d13ac6295fdce2141164ec38c5d47aa8dc24ee3afa37e", size = 9808201, upload-time = "2025-12-29T13:50:21.003Z" },
{ url = "https://files.pythonhosted.org/packages/f8/90/5a82ac0a0707db55376922aed80cd5fca6b2e6d6e9bcd8c286e6b43b4084/ty-0.0.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:90d6f08c5982fa3e802b8918a32e326153519077b827f91c66eea4913a86756a", size = 10313262, upload-time = "2025-12-29T13:50:03.306Z" },
{ url = "https://files.pythonhosted.org/packages/14/f7/ff97f37f0a75db9495ddbc47738ec4339837867c4bfa145bdcfbd0d1eb2f/ty-0.0.8-py3-none-win32.whl", hash = "sha256:d7f460ad6fc9325e9cc8ea898949bbd88141b4609d1088d7ede02ce2ef06e776", size = 9254675, upload-time = "2025-12-29T13:50:33.35Z" },
{ url = "https://files.pythonhosted.org/packages/af/51/eba5d83015e04630002209e3590c310a0ff1d26e1815af204a322617a42e/ty-0.0.8-py3-none-win_amd64.whl", hash = "sha256:1641fb8dedc3d2da43279d21c3c7c1f80d84eae5c264a1e8daa544458e433c19", size = 10131382, upload-time = "2025-12-29T13:50:13.719Z" },
{ url = "https://files.pythonhosted.org/packages/38/1c/0d8454ff0f0f258737ecfe84f6e508729191d29663b404832f98fa5626b7/ty-0.0.8-py3-none-win_arm64.whl", hash = "sha256:ec74f022f315bede478ecae1277a01ab618e6500c1d68450d7883f5cd6ed554a", size = 9636374, upload-time = "2025-12-29T13:50:16.344Z" },
]
[[package]]
name = "typer"
version = "0.16.0"