From 7ff18bc6b68cdb3a5eaf593a264032dfe759dca7 Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:24:12 -0700 Subject: [PATCH 01/38] Start v3 --- pyproject.toml | 4 +- src/contiguity/__init__.py | 44 +-- src/contiguity/_client.py | 4 +- src/contiguity/_common.py | 8 - src/contiguity/_response.py | 19 ++ src/contiguity/analytics.py | 28 -- src/contiguity/base/async_base.py | 24 +- src/contiguity/base/base.py | 24 +- src/contiguity/base/common.py | 12 +- src/contiguity/domains.py | 38 +++ src/contiguity/{send.py => email.py} | 66 +--- src/contiguity/imessage.py | 0 src/contiguity/indentity.py | 0 src/contiguity/lease.py | 0 src/contiguity/otp.py | 38 +-- src/contiguity/quota.py | 27 -- src/contiguity/template.py | 19 -- src/contiguity/text.py | 59 ++++ src/contiguity/whatsapp.py | 0 uv.lock | 489 ++++++++++++--------------- 20 files changed, 397 insertions(+), 506 deletions(-) delete mode 100644 src/contiguity/_common.py create mode 100644 src/contiguity/_response.py delete mode 100644 src/contiguity/analytics.py create mode 100644 src/contiguity/domains.py rename src/contiguity/{send.py => email.py} (52%) create mode 100644 src/contiguity/imessage.py create mode 100644 src/contiguity/indentity.py create mode 100644 src/contiguity/lease.py delete mode 100644 src/contiguity/quota.py delete mode 100644 src/contiguity/template.py create mode 100644 src/contiguity/text.py create mode 100644 src/contiguity/whatsapp.py diff --git a/pyproject.toml b/pyproject.toml index d67ec4c..fec33ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,8 +35,8 @@ classifiers = [ ] dependencies = [ "httpx>=0.27.2", + "msgspec>=0.19.0", "phonenumbers>=8.13.47,<9.0.0", - "pydantic>=2.9.0,<3.0.0", "typing-extensions>=4.12.2,<5.0.0", ] @@ -44,9 +44,9 @@ dependencies = [ dev = [ "pre-commit~=3.8.0", "pytest~=8.3.3", + "pytest-asyncio~=0.24.0", "pytest-cov~=5.0.0", "python-dotenv~=1.0.1", - "pytest-asyncio~=0.24.0", ] [project.urls] diff --git a/src/contiguity/__init__.py b/src/contiguity/__init__.py index b6658d7..e31ad19 100644 --- a/src/contiguity/__init__.py +++ b/src/contiguity/__init__.py @@ -1,16 +1,13 @@ from ._client import ApiClient -from .analytics import EmailAnalytics -from .base import AsyncBase, Base, BaseItem, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse +from .email import Email from .otp import OTP -from .quota import Quota -from .send import Send -from .template import Template -from .verify import Verify +from .text import Text class Contiguity: """ Create a new instance of the Contiguity class. + Args: token (str): The authentication token. debug (bool, optional): A flag indicating whether to enable debug mode. Default is False. @@ -20,8 +17,7 @@ def __init__( self, *, token: str, - base_url: str = "https://api.contiguity.co", - orwell_base_url: str = "https://orwell.contiguity.co", + base_url: str = "https://api.contiguity.com", debug: bool = False, ) -> None: if not token: @@ -29,38 +25,18 @@ def __init__( raise ValueError(msg) self.token = token self.base_url = base_url - self.orwell_base_url = orwell_base_url self.debug = debug self.client = ApiClient(base_url=self.base_url, api_key=token.strip()) - self.orwell_client = ApiClient(base_url=self.orwell_base_url, api_key=token.strip()) - self.send = Send(client=self.client, debug=self.debug) - self.verify = Verify() - self.email_analytics = EmailAnalytics(client=self.orwell_client, debug=self.debug) - self.quota = Quota(client=self.client, debug=self.debug) + self.text = Text(client=self.client, debug=self.debug) + self.email = Email(client=self.client, debug=self.debug) self.otp = OTP(client=self.client, debug=self.debug) - self.template = Template() - - -def login(token: str, /, *, debug: bool = False) -> Contiguity: - return Contiguity(token=token, debug=debug) __all__ = ( - "AsyncBase", - "Contiguity", - "Send", - "Verify", - "EmailAnalytics", - "Quota", "OTP", - "Template", - "Base", - "BaseItem", - "InvalidKeyError", - "ItemConflictError", - "ItemNotFoundError", - "QueryResponse", - "login", + "Contiguity", + "Email", + "Text", ) -__version__ = "2.0.0" +__version__ = "3.0.0" diff --git a/src/contiguity/_client.py b/src/contiguity/_client.py index 5ffbbdb..fa8ca55 100644 --- a/src/contiguity/_client.py +++ b/src/contiguity/_client.py @@ -14,7 +14,7 @@ class ApiClient(HttpxClient): def __init__( self: ApiClient, *, - base_url: str = "https://api.contiguity.co", + base_url: str = "https://api.contiguity.com", api_key: str | None = None, timeout: int = 5, ) -> None: @@ -35,7 +35,7 @@ class AsyncApiClient(HttpxAsyncClient): def __init__( self: AsyncApiClient, *, - base_url: str = "https://api.contiguity.co", + base_url: str = "https://api.contiguity.com", api_key: str | None = None, timeout: int = 5, ) -> None: diff --git a/src/contiguity/_common.py b/src/contiguity/_common.py deleted file mode 100644 index f0d3392..0000000 --- a/src/contiguity/_common.py +++ /dev/null @@ -1,8 +0,0 @@ -from pydantic import BaseModel - - -class Crumbs(BaseModel): - plan: str - quota: int - type: str - ad: bool diff --git a/src/contiguity/_response.py b/src/contiguity/_response.py new file mode 100644 index 0000000..d90bf1b --- /dev/null +++ b/src/contiguity/_response.py @@ -0,0 +1,19 @@ +from http import HTTPStatus + +from msgspec import Struct + + +class ResponseMetadata(Struct): + id: str + timestamp: int + api_version: str + object: str + + +class BaseResponse(Struct): + metadata: ResponseMetadata + + +class ErrorResponse(BaseResponse): + error: str + status: HTTPStatus diff --git a/src/contiguity/analytics.py b/src/contiguity/analytics.py deleted file mode 100644 index 1408d2c..0000000 --- a/src/contiguity/analytics.py +++ /dev/null @@ -1,28 +0,0 @@ -from http import HTTPStatus - -from typing_extensions import deprecated - -from ._client import ApiClient - - -class EmailAnalytics: - def __init__(self, *, client: ApiClient, debug: bool = False) -> None: - self._client = client - self.debug = debug - - @deprecated("email analytics will be removed in a future release") - def retrieve(self, id: str) -> dict: - if not id: - msg = "Contiguity Analytics requires an email ID." - raise ValueError(msg) - - response = self._client.get(f"/email/status/{id}") - data = response.json() - - if response.status_code != HTTPStatus.OK: - msg = f"Contiguity Analytics couldn't find an email with ID {id}" - raise ValueError(msg) - if self.debug: - print(f"Contiguity successfully found your email. Data:\n{data}") - - return data diff --git a/src/contiguity/base/async_base.py b/src/contiguity/base/async_base.py index 3a0c3c1..39e80c5 100644 --- a/src/contiguity/base/async_base.py +++ b/src/contiguity/base/async_base.py @@ -8,9 +8,8 @@ from typing import TYPE_CHECKING, Generic, Literal, overload from warnings import warn +import msgspec from httpx import HTTPStatusError -from pydantic import BaseModel, TypeAdapter -from pydantic import JsonValue as DataType from typing_extensions import deprecated from contiguity._auth import get_data_key, get_project_id @@ -18,6 +17,7 @@ from .common import ( UNSET, + DataType, DefaultItemT, ItemT, QueryResponse, @@ -129,9 +129,7 @@ def _response_as_item_type( except HTTPStatusError as exc: raise ApiError(exc.response.text) from exc if self.item_type: - if sequence: - return TypeAdapter(Sequence[self.item_type]).validate_json(response.content) - return TypeAdapter(self.item_type).validate_json(response.content) + return msgspec.json.decode(response.content, type=Sequence[self.item_type] if sequence else self.item_type) return response.json(cls=self.json_decoder) def _insert_expires_attr( @@ -144,7 +142,7 @@ def _insert_expires_attr( msg = "cannot use both expire_in and expire_at" raise ValueError(msg) - item_dict = item.model_dump() if isinstance(item, BaseModel) else dict(item) + item_dict = msgspec.to_builtins(item) if isinstance(item, msgspec.Struct) else dict(item) if not expire_in and not expire_at: return item_dict @@ -273,7 +271,7 @@ async def update( expire_at=expire_at, ) - response = await self._client.patch(f"/items/{key}", json={"updates": payload.model_dump()}) + response = await self._client.patch(f"/items/{key}", json={"updates": msgspec.to_builtins(payload)}) if response.status_code == HTTPStatus.NOT_FOUND: raise ItemNotFoundError(key) @@ -302,11 +300,13 @@ async def query( response.raise_for_status() except HTTPStatusError as exc: raise ApiError(exc.response.text) from exc - query_response = QueryResponse[ItemT].model_validate_json(response.content) - if self.item_type: - # HACK: Pydantic model_validate_json doesn't validate Sequence[ItemT] properly. # noqa: FIX004 - query_response.items = TypeAdapter(Sequence[self.item_type]).validate_python(query_response.items) - return query_response + return msgspec.json.decode(response.content, type=QueryResponse[ItemT]) + + # query_response = QueryResponse[ItemT].model_validate_json(response.content) + # if self.item_type: + # # HACK: Pydantic model_validate_json doesn't validate Sequence[ItemT] properly. # noqa: FIX004 + # query_response.items = msgspec.json.decode(query_response.items, type=Sequence[self.item_type]) + # return query_response @deprecated("This method has been renamed to `query` and will be removed in a future release.") async def fetch( diff --git a/src/contiguity/base/base.py b/src/contiguity/base/base.py index 819899d..490e11f 100644 --- a/src/contiguity/base/base.py +++ b/src/contiguity/base/base.py @@ -17,9 +17,8 @@ from typing import TYPE_CHECKING, Generic, Literal, overload from warnings import warn +import msgspec from httpx import HTTPStatusError -from pydantic import BaseModel, TypeAdapter -from pydantic import JsonValue as DataType from typing_extensions import deprecated from contiguity._auth import get_data_key, get_project_id @@ -27,6 +26,7 @@ from .common import ( UNSET, + DataType, DefaultItemT, ItemT, QueryResponse, @@ -138,9 +138,7 @@ def _response_as_item_type( except HTTPStatusError as exc: raise ApiError(exc.response.text) from exc if self.item_type: - if sequence: - return TypeAdapter(Sequence[self.item_type]).validate_json(response.content) - return TypeAdapter(self.item_type).validate_json(response.content) + return msgspec.json.decode(response.content, type=Sequence[self.item_type] if sequence else self.item_type) return response.json(cls=self.json_decoder) def _insert_expires_attr( @@ -153,7 +151,7 @@ def _insert_expires_attr( msg = "cannot use both expire_in and expire_at" raise ValueError(msg) - item_dict = item.model_dump() if isinstance(item, BaseModel) else dict(item) + item_dict = msgspec.to_builtins(item) if isinstance(item, msgspec.Struct) else dict(item) if not expire_in and not expire_at: return item_dict @@ -282,7 +280,7 @@ def update( expire_at=expire_at, ) - response = self._client.patch(f"/items/{key}", json={"updates": payload.model_dump()}) + response = self._client.patch(f"/items/{key}", json={"updates": msgspec.to_builtins(payload)}) if response.status_code == HTTPStatus.NOT_FOUND: raise ItemNotFoundError(key) @@ -311,11 +309,13 @@ def query( response.raise_for_status() except HTTPStatusError as exc: raise ApiError(exc.response.text) from exc - query_response = QueryResponse[ItemT].model_validate_json(response.content) - if self.item_type: - # HACK: Pydantic model_validate_json doesn't validate Sequence[ItemT] properly. # noqa: FIX004 - query_response.items = TypeAdapter(Sequence[self.item_type]).validate_python(query_response.items) - return query_response + return msgspec.json.decode(response.content, type=QueryResponse[ItemT]) + + # query_response = QueryResponse[ItemT].model_validate_json(response.content) + # if self.item_type: + # # HACK: Pydantic model_validate_json doesn't validate Sequence[ItemT] properly. # noqa: FIX004 + # query_response.items = TypeAdapter(Sequence[self.item_type]).validate_python(query_response.items) + # return query_response @deprecated("This method has been renamed to `query` and will be removed in a future release.") def fetch( diff --git a/src/contiguity/base/common.py b/src/contiguity/base/common.py index dc0b61e..e95ff50 100644 --- a/src/contiguity/base/common.py +++ b/src/contiguity/base/common.py @@ -5,16 +5,16 @@ from typing import Any, Generic, TypeVar, Union from urllib.parse import quote -from pydantic import BaseModel -from pydantic import JsonValue as DataType +from msgspec import Struct from typing_extensions import Self from .exceptions import InvalidKeyError +DataType = Union[str, int, float, bool, None, Sequence["DataType"], Mapping[str, "DataType"]] TimestampType = Union[int, datetime] QueryType = Mapping[str, DataType] -ItemType = Union[Mapping[str, Any], BaseModel] +ItemType = Union[Mapping[str, Any], Struct] ItemT = TypeVar("ItemT", bound=ItemType) DefaultItemT = TypeVar("DefaultItemT") @@ -26,11 +26,11 @@ class Unset: UNSET = Unset() -class BaseItem(BaseModel): +class BaseItem(Struct): key: str -class QueryResponse(BaseModel, Generic[ItemT]): +class QueryResponse(Struct, Generic[ItemT]): count: int = 0 last_key: Union[str, None] = None # noqa: UP007 Pydantic doesn't support `X | Y` syntax in Python 3.9. items: Sequence[ItemT] = [] @@ -79,7 +79,7 @@ def prepend(value: DataType, /) -> Prepend: return Prepend(value) -class UpdatePayload(BaseModel): +class UpdatePayload(Struct): set: dict[str, DataType] = {} increment: dict[str, int] = {} append: dict[str, Sequence[DataType]] = {} diff --git a/src/contiguity/domains.py b/src/contiguity/domains.py new file mode 100644 index 0000000..0e59c2f --- /dev/null +++ b/src/contiguity/domains.py @@ -0,0 +1,38 @@ +import msgspec +from contiguity._response import BaseResponse, ErrorResponse + + +class DnsRecord(msgspec.Struct): + ... + + +class DomainVerifications(msgspec.Struct): + # TODO: change to bools? + dkim: str + mail_from: str + domain: str + + +class Domain(msgspec.Struct): + domain: str + status: str + id: str + created_at: int + records: list[DnsRecord] + region: str + sending_allowed: bool + verifications: DomainVerifications + + +class Domains: + def register(self, domain: str, /, region: str = "us-east-1", custom_return_path: str = "contiguity"): + raise NotImplementedError + + def list(self): + raise NotImplementedError + + def get(self, domain: str, /): + raise NotImplementedError + + def delete(self, domain: str, /): + raise NotImplementedError diff --git a/src/contiguity/send.py b/src/contiguity/email.py similarity index 52% rename from src/contiguity/send.py rename to src/contiguity/email.py index d5f9c63..1b16227 100644 --- a/src/contiguity/send.py +++ b/src/contiguity/email.py @@ -3,70 +3,23 @@ from http import HTTPStatus from typing import TYPE_CHECKING, overload -import phonenumbers -from pydantic import BaseModel +import msgspec -from ._common import Crumbs # noqa: TCH001 Pydantic needs this to be outside of the TYPE_CHECKING block. +from ._response import BaseResponse, ErrorResponse if TYPE_CHECKING: from ._client import ApiClient -class TextResponse(BaseModel): +class EmailResponse(BaseResponse): message: str - crumbs: Crumbs -class EmailResponse(BaseModel): - message: str - crumbs: Crumbs - - -class Send: +class Email: def __init__(self, *, client: ApiClient, debug: bool = False) -> None: self._client = client self.debug = debug - def text(self, to: str, message: str) -> TextResponse: - """ - Send a text message. - Args: - to (str): The recipient's phone number. - message (str): The message to send. - Returns: - dict: The response object. - Raises: - ValueError: Raises an error if required fields are missing or sending the message fails. - """ - try: - parsed_number = phonenumbers.parse(to, None) - if not phonenumbers.is_valid_number(parsed_number): - msg = "Contiguity requires phone numbers to follow the E.164 format. Formatting failed." - raise ValueError(msg) - except phonenumbers.NumberParseException as exc: - msg = "Contiguity requires phone numbers to follow the E.164 format. Parsing failed." - raise ValueError(msg) from exc - - response = self._client.post( - "/send/text", - json={ - "to": phonenumbers.format_number(parsed_number, phonenumbers.PhoneNumberFormat.E164), - "message": message, - }, - ) - data = TextResponse.model_validate_json(response.content) - - if response.status_code != HTTPStatus.OK: - msg = ( - "Contiguity couldn't send your message." - f" Received: {response.status_code} with reason: '{data.message}'" - ) - raise ValueError(msg) - if self.debug: - print(f"Contiguity successfully sent your text to {to}. Crumbs:\n{data.crumbs}") - - return data - @overload def email( self, @@ -134,15 +87,14 @@ def email( # noqa: PLR0913 email_payload["cc"] = cc response = self._client.post("/send/email", json=email_payload) - data = EmailResponse.model_validate_json(response.content) if response.status_code != HTTPStatus.OK: - msg = ( - "Contiguity couldn't send your email." - f" Received: {response.status_code} with reason: '{data.message}'" - ) + data = msgspec.json.decode(response.content, type=ErrorResponse) + msg = f"Contiguity couldn't send your email. Received: {response.status_code} with reason: '{data.error}'" raise ValueError(msg) + + data = msgspec.json.decode(response.content, type=EmailResponse) if self.debug: - print(f"Contiguity successfully sent your email to {to}. Crumbs:\n{data.crumbs}") + print(f"Contiguity successfully sent your email to {to}") return data diff --git a/src/contiguity/imessage.py b/src/contiguity/imessage.py new file mode 100644 index 0000000..e69de29 diff --git a/src/contiguity/indentity.py b/src/contiguity/indentity.py new file mode 100644 index 0000000..e69de29 diff --git a/src/contiguity/lease.py b/src/contiguity/lease.py new file mode 100644 index 0000000..e69de29 diff --git a/src/contiguity/otp.py b/src/contiguity/otp.py index 7e00cfd..6685231 100644 --- a/src/contiguity/otp.py +++ b/src/contiguity/otp.py @@ -4,10 +4,10 @@ from http import HTTPStatus from typing import TYPE_CHECKING +import msgspec import phonenumbers -from pydantic import BaseModel -from ._common import Crumbs # noqa: TCH001 Pydantic needs this to be outside of the TYPE_CHECKING block. +from ._response import BaseResponse, ErrorResponse if TYPE_CHECKING: from ._client import ApiClient @@ -52,19 +52,15 @@ class OTPLanguage(str, Enum): VIETNAMESE = "vi" -class OTPSendResponse(BaseModel): - message: str - crumbs: Crumbs +class OTPSendResponse(BaseResponse): otp_id: str -class OTPResendResponse(BaseModel): - message: str +class OTPResendResponse(BaseResponse): resent: bool -class OTPVerifyResponse(BaseModel): - message: str +class OTPVerifyResponse(BaseResponse): verified: bool @@ -91,11 +87,13 @@ def send( "name": name, }, ) - data = OTPSendResponse.model_validate_json(response.content) if response.status_code != HTTPStatus.OK: - msg = f"Contiguity couldn't send your OTP. Received: {response.status_code} with reason: '{data.message}'" + data = msgspec.json.decode(response.content, type=ErrorResponse) + msg = f"Contiguity couldn't send your OTP. Received: {response.status_code} with reason: '{data.error}'" raise ValueError(msg) + + data = msgspec.json.decode(response.content, type=OTPSendResponse) if self.debug: print(f"Contiguity successfully sent your OTP to {to} with OTP ID {data.otp_id}") @@ -108,14 +106,13 @@ def resend(self, otp_id: str, /) -> OTPResendResponse: "otp_id": otp_id, }, ) - data = OTPResendResponse.model_validate_json(response.content) if response.status_code != HTTPStatus.OK: - msg = ( - "Contiguity couldn't resend your OTP." - f" Received: {response.status_code} with reason: '{data.message}'" - ) + data = msgspec.json.decode(response.content, type=ErrorResponse) + msg = f"Contiguity couldn't resend your OTP. Received: {response.status_code} with reason: '{data.error}'" raise ValueError(msg) + + data = msgspec.json.decode(response.content, type=OTPResendResponse) if self.debug: print(f"Contiguity resent your OTP ({id}) with status: {data.resent}") @@ -129,14 +126,13 @@ def verify(self, otp: int | str, /, *, otp_id: str) -> OTPVerifyResponse: "otp_id": otp_id, }, ) - data = OTPVerifyResponse.model_validate_json(response.content) if response.status_code != HTTPStatus.OK: - msg = ( - "Contiguity couldn't verify your OTP." - f" Received: {response.status_code} with reason: '{data.message}'" - ) + data = msgspec.json.decode(response.content, type=ErrorResponse) + msg = f"Contiguity couldn't verify your OTP. Received: {response.status_code} with reason: '{data.error}'" raise ValueError(msg) + + data = msgspec.json.decode(response.content, type=OTPVerifyResponse) if self.debug: print(f"Contiguity verified your OTP ({otp}) with status: {data.verified}") diff --git a/src/contiguity/quota.py b/src/contiguity/quota.py deleted file mode 100644 index 53f0602..0000000 --- a/src/contiguity/quota.py +++ /dev/null @@ -1,27 +0,0 @@ -from http import HTTPStatus - -from typing_extensions import deprecated - -from ._client import ApiClient - - -class Quota: - def __init__(self, *, client: ApiClient, debug: bool = False) -> None: - self._client = client - self.debug = debug - - @deprecated("quota functionality will be removed in a future release") - def retrieve(self) -> dict: - response = self._client.get("/user/get/quota") - data = response.json() - - if response.status_code != HTTPStatus.OK: - msg = ( - "Contiguity had an issue finding your quota." - f" Received {response.status_code} with reason: '{data['message']}'" - ) - raise ValueError(msg) - if self.debug: - print(f"Contiguity successfully found your quota. Data:\n{data}") - - return data diff --git a/src/contiguity/template.py b/src/contiguity/template.py deleted file mode 100644 index b15b052..0000000 --- a/src/contiguity/template.py +++ /dev/null @@ -1,19 +0,0 @@ -from __future__ import annotations - -from pathlib import Path - -from typing_extensions import Never - - -class Template: - def local(self, file_path: Path | str) -> str: - try: - file_path = Path(file_path) - return file_path.read_text() - except OSError as exc: - msg = "reading files is not supported in the this environment" - raise ValueError(msg) from exc - - async def online(self, file_path: str) -> Never: - # Coming soon - raise NotImplementedError diff --git a/src/contiguity/text.py b/src/contiguity/text.py new file mode 100644 index 0000000..39d26e1 --- /dev/null +++ b/src/contiguity/text.py @@ -0,0 +1,59 @@ +from http import HTTPStatus + +import msgspec +import phonenumbers + +from contiguity._client import ApiClient + +from ._response import BaseResponse, ErrorResponse + + +class TextResponse(BaseResponse): + message_id: str + + +class Text: + def __init__(self, *, client: ApiClient, debug: bool = False) -> None: + self._client = client + self.debug = debug + + def send(self, to: str, message: str) -> TextResponse: + """ + Send a text message. + Args: + to (str): The recipient's phone number. + message (str): The message to send. + Returns: + dict: The response object. + Raises: + ValueError: Raises an error if required fields are missing or sending the message fails. + """ + try: + parsed_number = phonenumbers.parse(to, None) + if not phonenumbers.is_valid_number(parsed_number): + msg = "Contiguity requires phone numbers to follow the E.164 format. Formatting failed." + raise ValueError(msg) + except phonenumbers.NumberParseException as exc: + msg = "Contiguity requires phone numbers to follow the E.164 format. Parsing failed." + raise ValueError(msg) from exc + + response = self._client.post( + "/send/text", + json={ + "to": phonenumbers.format_number(parsed_number, phonenumbers.PhoneNumberFormat.E164), + "message": message, + }, + ) + + if response.status_code != HTTPStatus.OK: + data = msgspec.json.decode(response.content, type=ErrorResponse) + msg = ( + f"Contiguity couldn't send your message. Received: {response.status_code} with reason: '{data.error}'" + ) + raise ValueError(msg) + + data = msgspec.json.decode(response.content, type=TextResponse) + if self.debug: + print(f"Contiguity successfully sent your text to {to}") + + return data diff --git a/src/contiguity/whatsapp.py b/src/contiguity/whatsapp.py new file mode 100644 index 0000000..e69de29 diff --git a/uv.lock b/uv.lock index 28e7970..b38aec5 100644 --- a/uv.lock +++ b/uv.lock @@ -1,20 +1,11 @@ version = 1 -revision = 1 +revision = 3 requires-python = ">=3.9" resolution-markers = [ "python_full_version < '3.13'", "python_full_version >= '3.13'", ] -[[package]] -name = "annotated-types" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, -] - [[package]] name = "anyio" version = "4.6.0" @@ -25,36 +16,36 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/49/f3f17ec11c4a91fe79275c426658e509b07547f874b14c1a526d86a83fc8/anyio-4.6.0.tar.gz", hash = "sha256:137b4559cbb034c477165047febb6ff83f390fc3b20bf181c1fc0a728cb8beeb", size = 170983 } +sdist = { url = "https://files.pythonhosted.org/packages/78/49/f3f17ec11c4a91fe79275c426658e509b07547f874b14c1a526d86a83fc8/anyio-4.6.0.tar.gz", hash = "sha256:137b4559cbb034c477165047febb6ff83f390fc3b20bf181c1fc0a728cb8beeb", size = 170983, upload-time = "2024-09-21T10:33:28.479Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/ef/7a4f225581a0d7886ea28359179cb861d7fbcdefad29663fc1167b86f69f/anyio-4.6.0-py3-none-any.whl", hash = "sha256:c7d2e9d63e31599eeb636c8c5c03a7e108d73b345f064f1c19fdc87b79036a9a", size = 89631 }, + { url = "https://files.pythonhosted.org/packages/9e/ef/7a4f225581a0d7886ea28359179cb861d7fbcdefad29663fc1167b86f69f/anyio-4.6.0-py3-none-any.whl", hash = "sha256:c7d2e9d63e31599eeb636c8c5c03a7e108d73b345f064f1c19fdc87b79036a9a", size = 89631, upload-time = "2024-09-21T10:33:27.05Z" }, ] [[package]] name = "certifi" version = "2024.8.30" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } +sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507, upload-time = "2024-08-30T01:55:04.365Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, + { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321, upload-time = "2024-08-30T01:55:02.591Z" }, ] [[package]] name = "cfgv" version = "3.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, + { 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]] @@ -62,8 +53,8 @@ name = "contiguity" source = { editable = "." } dependencies = [ { name = "httpx" }, + { name = "msgspec" }, { name = "phonenumbers" }, - { name = "pydantic" }, { name = "typing-extensions" }, ] @@ -79,8 +70,8 @@ dev = [ [package.metadata] requires-dist = [ { name = "httpx", specifier = ">=0.27.2" }, + { name = "msgspec", specifier = ">=0.19.0" }, { name = "phonenumbers", specifier = ">=8.13.47,<9.0.0" }, - { name = "pydantic", specifier = ">=2.9.0,<3.0.0" }, { name = "typing-extensions", specifier = ">=4.12.2,<5.0.0" }, ] @@ -97,69 +88,69 @@ dev = [ name = "coverage" version = "7.6.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9a/60/e781e8302e7b28f21ce06e30af077f856aa2cb4cf2253287dae9a593d509/coverage-7.6.2.tar.gz", hash = "sha256:a5f81e68aa62bc0cfca04f7b19eaa8f9c826b53fc82ab9e2121976dc74f131f3", size = 797872 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/16/14/fb75c01b8427fb567c90ce920c90ed2bd314ad6960d54e8b377928607fd1/coverage-7.6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c9df1950fb92d49970cce38100d7e7293c84ed3606eaa16ea0b6bc27175bb667", size = 206561 }, - { url = "https://files.pythonhosted.org/packages/93/b4/dcbf15f5583507415d0a78ce206e19d76699f1161e8b1ff6e1a21e9f9743/coverage-7.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:24500f4b0e03aab60ce575c85365beab64b44d4db837021e08339f61d1fbfe52", size = 206994 }, - { url = "https://files.pythonhosted.org/packages/47/ee/57d607e14479fb760721ea1784608ade532665934bd75f260b250dc6c877/coverage-7.6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a663b180b6669c400b4630a24cc776f23a992d38ce7ae72ede2a397ce6b0f170", size = 235429 }, - { url = "https://files.pythonhosted.org/packages/76/e1/cd263fd750fdb115aab11a086e3584d99d46fca1f201b5493cc3972aea28/coverage-7.6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfde025e2793a22efe8c21f807d276bd1d6a4bcc5ba6f19dbdfc4e7a12160909", size = 233329 }, - { url = "https://files.pythonhosted.org/packages/30/3b/a1623d50fcd6ba532cef0c3c1059eec2a08a311676ffa84dbe4beb2b8a33/coverage-7.6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:087932079c065d7b8ebadd3a0160656c55954144af6439886c8bcf78bbbcde7f", size = 234491 }, - { url = "https://files.pythonhosted.org/packages/b1/a6/8f3b3fd1f9b9400f3df38a7159362622546e2d951cc4984cf4617d0fd4d7/coverage-7.6.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9c6b0c1cafd96213a0327cf680acb39f70e452caf8e9a25aeb05316db9c07f89", size = 233589 }, - { url = "https://files.pythonhosted.org/packages/e3/40/37d64093f57b372435d87679956607ecab066d2aede76c6d215815a35fa3/coverage-7.6.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6e85830eed5b5263ffa0c62428e43cb844296f3b4461f09e4bdb0d44ec190bc2", size = 232050 }, - { url = "https://files.pythonhosted.org/packages/80/63/cbb76298b4f42bffe0030f1bc129a26a26255857c6beaa20419259ac07cc/coverage-7.6.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:62ab4231c01e156ece1b3a187c87173f31cbeee83a5e1f6dff17f288dca93345", size = 233180 }, - { url = "https://files.pythonhosted.org/packages/7a/6a/eafa81503e905d473b799920927b06aa6ffba12db035fc98735b55bc1741/coverage-7.6.2-cp310-cp310-win32.whl", hash = "sha256:7b80fbb0da3aebde102a37ef0138aeedff45997e22f8962e5f16ae1742852676", size = 209281 }, - { url = "https://files.pythonhosted.org/packages/19/d1/6b354c2cd52e0244944c097aaa71896869878df999f5f8e75fcd37eaf0f3/coverage-7.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:d20c3d1f31f14d6962a4e2f549c21d31e670b90f777ef4171be540fb7fb70f02", size = 210092 }, - { url = "https://files.pythonhosted.org/packages/a5/29/72da824da4182f518b054c21552b7ed2473a4e4c6ac616298209808a1a5c/coverage-7.6.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bb21bac7783c1bf6f4bbe68b1e0ff0d20e7e7732cfb7995bc8d96e23aa90fc7b", size = 206667 }, - { url = "https://files.pythonhosted.org/packages/23/52/c15dcf3cf575256c7c0992e441cd41092a6c519d65abe1eb5567aab3d8e8/coverage-7.6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7b2e437fbd8fae5bc7716b9c7ff97aecc95f0b4d56e4ca08b3c8d8adcaadb84", size = 207111 }, - { url = "https://files.pythonhosted.org/packages/92/61/0d46dc26cf9f711b7b6078a54680665a5c2d62ec15991adb51e79236c699/coverage-7.6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:536f77f2bf5797983652d1d55f1a7272a29afcc89e3ae51caa99b2db4e89d658", size = 239050 }, - { url = "https://files.pythonhosted.org/packages/3b/cb/9de71bade0343a0793f645f78a0e409248d85a2e5b4c4a9a1697c3b2e3d2/coverage-7.6.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f361296ca7054f0936b02525646b2731b32c8074ba6defab524b79b2b7eeac72", size = 236454 }, - { url = "https://files.pythonhosted.org/packages/f2/81/b0dc02487447c4a56cf2eed5c57735097f77aeff582277a35f1f70713a8d/coverage-7.6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7926d8d034e06b479797c199747dd774d5e86179f2ce44294423327a88d66ca7", size = 238320 }, - { url = "https://files.pythonhosted.org/packages/60/90/76815a76234050a87d0d1438a34820c1b857dd17353855c02bddabbedea8/coverage-7.6.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0bbae11c138585c89fb4e991faefb174a80112e1a7557d507aaa07675c62e66b", size = 237250 }, - { url = "https://files.pythonhosted.org/packages/f6/bd/760a599c08c882d97382855264586bba2604901029c3f6bec5710477ae81/coverage-7.6.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fcad7d5d2bbfeae1026b395036a8aa5abf67e8038ae7e6a25c7d0f88b10a8e6a", size = 235880 }, - { url = "https://files.pythonhosted.org/packages/83/de/41c3b90a779e473ae1ca325542aa5fa5464b7d2061288e9c22ba5f1deaa3/coverage-7.6.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f01e53575f27097d75d42de33b1b289c74b16891ce576d767ad8c48d17aeb5e0", size = 236653 }, - { url = "https://files.pythonhosted.org/packages/f4/90/61fe2721b9a9d9446e6c3ca33b6569e81d2a9a795ddfe786a66bf54035b7/coverage-7.6.2-cp311-cp311-win32.whl", hash = "sha256:7781f4f70c9b0b39e1b129b10c7d43a4e0c91f90c60435e6da8288efc2b73438", size = 209251 }, - { url = "https://files.pythonhosted.org/packages/96/87/d586f2b12b98288fc874d366cd8d5601f5a374cb75853647a3e4d02e4eb0/coverage-7.6.2-cp311-cp311-win_amd64.whl", hash = "sha256:9bcd51eeca35a80e76dc5794a9dd7cb04b97f0e8af620d54711793bfc1fbba4b", size = 210083 }, - { url = "https://files.pythonhosted.org/packages/3f/ac/1cca5ed5cf512a71cdd6e3afb75a5ef196f7ef9772be9192dadaaa5cfc1c/coverage-7.6.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ebc94fadbd4a3f4215993326a6a00e47d79889391f5659bf310f55fe5d9f581c", size = 206856 }, - { url = "https://files.pythonhosted.org/packages/e4/58/030354d250f107a95e7aca24c7fd238709a3c7df3083cb206368798e637a/coverage-7.6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9681516288e3dcf0aa7c26231178cc0be6cac9705cac06709f2353c5b406cfea", size = 207098 }, - { url = "https://files.pythonhosted.org/packages/03/df/5f2cd6048d44a54bb5f58f8ece4efbc5b686ed49f8bd8dbf41eb2a6a687f/coverage-7.6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d9c5d13927d77af4fbe453953810db766f75401e764727e73a6ee4f82527b3e", size = 240109 }, - { url = "https://files.pythonhosted.org/packages/d3/18/7c53887643d921faa95529643b1b33e60ebba30ab835c8b5abd4e54d946b/coverage-7.6.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b92f9ca04b3e719d69b02dc4a69debb795af84cb7afd09c5eb5d54b4a1ae2191", size = 237141 }, - { url = "https://files.pythonhosted.org/packages/d2/79/339bdf597d128374e6150c089b37436ba694585d769cabf6d5abd73a1365/coverage-7.6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ff2ef83d6d0b527b5c9dad73819b24a2f76fdddcfd6c4e7a4d7e73ecb0656b4", size = 239210 }, - { url = "https://files.pythonhosted.org/packages/a9/62/7310c6de2bcb8a42f91094d41f0d4793ccda5a54621be3db76a156556cf2/coverage-7.6.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:47ccb6e99a3031ffbbd6e7cc041e70770b4fe405370c66a54dbf26a500ded80b", size = 238698 }, - { url = "https://files.pythonhosted.org/packages/f2/cb/ccb23c084d7f581f770dc7ed547dc5b50763334ad6ce26087a9ad0b5b26d/coverage-7.6.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a867d26f06bcd047ef716175b2696b315cb7571ccb951006d61ca80bbc356e9e", size = 237000 }, - { url = "https://files.pythonhosted.org/packages/e7/ab/58de9e2f94e4dc91b84d6e2705aa1e9d5447a2669fe113b4bbce6d2224a1/coverage-7.6.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cdfcf2e914e2ba653101157458afd0ad92a16731eeba9a611b5cbb3e7124e74b", size = 238666 }, - { url = "https://files.pythonhosted.org/packages/6c/dc/8be87b9ed5dbd4892b603f41088b41982768e928734e5bdce67d2ddd460a/coverage-7.6.2-cp312-cp312-win32.whl", hash = "sha256:f9035695dadfb397bee9eeaf1dc7fbeda483bf7664a7397a629846800ce6e276", size = 209489 }, - { url = "https://files.pythonhosted.org/packages/64/3a/3f44e55273a58bfb39b87ad76541bbb81d14de916b034fdb39971cc99ffe/coverage-7.6.2-cp312-cp312-win_amd64.whl", hash = "sha256:5ed69befa9a9fc796fe015a7040c9398722d6b97df73a6b608e9e275fa0932b0", size = 210270 }, - { url = "https://files.pythonhosted.org/packages/ae/99/c9676a75b57438a19c5174dfcf39798b42728ad56650497286379dc0c2c3/coverage-7.6.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4eea60c79d36a8f39475b1af887663bc3ae4f31289cd216f514ce18d5938df40", size = 206888 }, - { url = "https://files.pythonhosted.org/packages/e0/de/820ecb42e892049c5f384430e98b35b899da3451dd0cdb2f867baf26abfa/coverage-7.6.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa68a6cdbe1bc6793a9dbfc38302c11599bbe1837392ae9b1d238b9ef3dafcf1", size = 207142 }, - { url = "https://files.pythonhosted.org/packages/dd/59/81fc7ad855d65eeb68fe9e7809cbb339946adb07be7ac32d3fc24dc17bd7/coverage-7.6.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ec528ae69f0a139690fad6deac8a7d33629fa61ccce693fdd07ddf7e9931fba", size = 239658 }, - { url = "https://files.pythonhosted.org/packages/cd/a7/865de3eb9e78ffbf7afd92f86d2580b18edfb6f0481bd3c39b205e05a762/coverage-7.6.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed5ac02126f74d190fa2cc14a9eb2a5d9837d5863920fa472b02eb1595cdc925", size = 236802 }, - { url = "https://files.pythonhosted.org/packages/36/94/3b8f3abf88b7c451f97fd14c98f536bcee364e74250d928d57cc97c38ddd/coverage-7.6.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21c0ea0d4db8a36b275cb6fb2437a3715697a4ba3cb7b918d3525cc75f726304", size = 238793 }, - { url = "https://files.pythonhosted.org/packages/d5/4b/57f95e41a10525002f524f3dbd577a3a9871d67998f8a8eb192fe697dc7b/coverage-7.6.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:35a51598f29b2a19e26d0908bd196f771a9b1c5d9a07bf20be0adf28f1ad4f77", size = 238455 }, - { url = "https://files.pythonhosted.org/packages/99/c9/9fbe5b841628e1d9030c8044844afef4f4735586289eb9237eeb5b97f0d7/coverage-7.6.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c9192925acc33e146864b8cf037e2ed32a91fdf7644ae875f5d46cd2ef086a5f", size = 236538 }, - { url = "https://files.pythonhosted.org/packages/43/0d/2200a0d447e30de94d48e4851c04d8dce37340815e7eda27457a7043c037/coverage-7.6.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf4eeecc9e10f5403ec06138978235af79c9a79af494eb6b1d60a50b49ed2869", size = 238383 }, - { url = "https://files.pythonhosted.org/packages/ec/8a/106c66faafb4a87002b698769d6de3c4db0b6c29a7aeb72de13b893c333e/coverage-7.6.2-cp313-cp313-win32.whl", hash = "sha256:e4ee15b267d2dad3e8759ca441ad450c334f3733304c55210c2a44516e8d5530", size = 209551 }, - { url = "https://files.pythonhosted.org/packages/c4/f5/1b39e2faaf5b9cc7eed568c444df5991ce7ff7138e2e735a6801be1bdadb/coverage-7.6.2-cp313-cp313-win_amd64.whl", hash = "sha256:c71965d1ced48bf97aab79fad56df82c566b4c498ffc09c2094605727c4b7e36", size = 210282 }, - { url = "https://files.pythonhosted.org/packages/79/a3/8dd4e6c09f5286094cd6c7edb115b3fbf06ad8304d45431722a4e3bc2508/coverage-7.6.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7571e8bbecc6ac066256f9de40365ff833553e2e0c0c004f4482facb131820ef", size = 207629 }, - { url = "https://files.pythonhosted.org/packages/8e/db/a9aa7009bbdc570a235e1ac781c0a83aa323cac6db8f8f13c2127b110978/coverage-7.6.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:078a87519057dacb5d77e333f740708ec2a8f768655f1db07f8dfd28d7a005f0", size = 207902 }, - { url = "https://files.pythonhosted.org/packages/54/08/d0962be62d4335599ca2ff3a48bb68c9bfb80df74e28ca689ff5f392087b/coverage-7.6.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e5e92e3e84a8718d2de36cd8387459cba9a4508337b8c5f450ce42b87a9e760", size = 250617 }, - { url = "https://files.pythonhosted.org/packages/a5/a2/158570aff1dd88b661a6c11281cbb190e8696e77798b4b2e47c74bfb2f39/coverage-7.6.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ebabdf1c76593a09ee18c1a06cd3022919861365219ea3aca0247ededf6facd6", size = 246334 }, - { url = "https://files.pythonhosted.org/packages/aa/fe/b00428cca325b6585ca77422e4f64d7d86a225b14664b98682ea501efb57/coverage-7.6.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12179eb0575b8900912711688e45474f04ab3934aaa7b624dea7b3c511ecc90f", size = 248692 }, - { url = "https://files.pythonhosted.org/packages/30/21/0a15fefc13039450bc45e7159f3add92489f004555eb7dab9c7ad4365dd0/coverage-7.6.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:39d3b964abfe1519b9d313ab28abf1d02faea26cd14b27f5283849bf59479ff5", size = 248188 }, - { url = "https://files.pythonhosted.org/packages/de/b8/5c093526046a8450a7a3d62ad09517cf38e638f6b3ee9433dd6a73360501/coverage-7.6.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:84c4315577f7cd511d6250ffd0f695c825efe729f4205c0340f7004eda51191f", size = 246072 }, - { url = "https://files.pythonhosted.org/packages/1e/8b/542b607d2cff56e5a90a6948f5a9040b693761d2be2d3c3bf88957b02361/coverage-7.6.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ff797320dcbff57caa6b2301c3913784a010e13b1f6cf4ab3f563f3c5e7919db", size = 247354 }, - { url = "https://files.pythonhosted.org/packages/95/82/2e9111aa5e59f42b332d387f64e3205c2263518d1e660154d0c9fc54390e/coverage-7.6.2-cp313-cp313t-win32.whl", hash = "sha256:2b636a301e53964550e2f3094484fa5a96e699db318d65398cfba438c5c92171", size = 210194 }, - { url = "https://files.pythonhosted.org/packages/9d/46/aabe4305cfc57cab4865f788ceceef746c422469720c32ed7a5b44e20f5e/coverage-7.6.2-cp313-cp313t-win_amd64.whl", hash = "sha256:d03a060ac1a08e10589c27d509bbdb35b65f2d7f3f8d81cf2fa199877c7bc58a", size = 211346 }, - { url = "https://files.pythonhosted.org/packages/6a/a9/85d14426f2449252f302f12c1c2a957a0a7ae7f35317ca3eaa365e1d6453/coverage-7.6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c37faddc8acd826cfc5e2392531aba734b229741d3daec7f4c777a8f0d4993e5", size = 206555 }, - { url = "https://files.pythonhosted.org/packages/71/ff/bc4d5697a55edf1ff077c47df5637ff4518ba2760ada82c142aca79ea3fe/coverage-7.6.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab31fdd643f162c467cfe6a86e9cb5f1965b632e5e65c072d90854ff486d02cf", size = 206990 }, - { url = "https://files.pythonhosted.org/packages/34/65/1301721d09f5b58da9decfd62eb42eaef07fdb854dae904c3482e59cc309/coverage-7.6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97df87e1a20deb75ac7d920c812e9326096aa00a9a4b6d07679b4f1f14b06c90", size = 235022 }, - { url = "https://files.pythonhosted.org/packages/9f/ec/7a2f361485226e6934a8f5d1f6eef7e8b7faf228fb6107476fa584700a32/coverage-7.6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:343056c5e0737487a5291f5691f4dfeb25b3e3c8699b4d36b92bb0e586219d14", size = 232943 }, - { url = "https://files.pythonhosted.org/packages/2d/60/b23e61a372bef93c9d13d87efa2ea3a870130be498e5b81740616b6e6200/coverage-7.6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad4ef1c56b47b6b9024b939d503ab487231df1f722065a48f4fc61832130b90e", size = 234074 }, - { url = "https://files.pythonhosted.org/packages/89/ec/4a56d9b310b2413987682ae3a858e30ea11d6f6d05366ecab4d73385fbef/coverage-7.6.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fca4a92c8a7a73dee6946471bce6d1443d94155694b893b79e19ca2a540d86e", size = 233226 }, - { url = "https://files.pythonhosted.org/packages/8c/77/31ecc00c525dea216d59090b807e9d1268a07d289f9dbe0cfc6795e33b68/coverage-7.6.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69f251804e052fc46d29d0e7348cdc5fcbfc4861dc4a1ebedef7e78d241ad39e", size = 231706 }, - { url = "https://files.pythonhosted.org/packages/7b/02/3f84bdd286a9db9b816cb5ca0adfa001575f8e496ba39da26f0ded2f0849/coverage-7.6.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e8ea055b3ea046c0f66217af65bc193bbbeca1c8661dc5fd42698db5795d2627", size = 232697 }, - { url = "https://files.pythonhosted.org/packages/7c/34/158b73026cbc2d2b3a56fbc71d955c0eea52953e49de97f820b3060f62b9/coverage-7.6.2-cp39-cp39-win32.whl", hash = "sha256:6c2ba1e0c24d8fae8f2cf0aeb2fc0a2a7f69b6d20bd8d3749fd6b36ecef5edf0", size = 209278 }, - { url = "https://files.pythonhosted.org/packages/d1/05/4326e4ea071176f0bddc30b5a3555b48fa96c45a8f6a09b6c2e4041dfcc0/coverage-7.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:2186369a654a15628e9c1c9921409a6b3eda833e4b91f3ca2a7d9f77abb4987c", size = 210057 }, - { url = "https://files.pythonhosted.org/packages/9d/5c/88f15b7614ba9ed1dbb1c0bd2c9073184b96c2bead0b93199487b44d04b3/coverage-7.6.2-pp39.pp310-none-any.whl", hash = "sha256:667952739daafe9616db19fbedbdb87917eee253ac4f31d70c7587f7ab531b4e", size = 198799 }, +sdist = { url = "https://files.pythonhosted.org/packages/9a/60/e781e8302e7b28f21ce06e30af077f856aa2cb4cf2253287dae9a593d509/coverage-7.6.2.tar.gz", hash = "sha256:a5f81e68aa62bc0cfca04f7b19eaa8f9c826b53fc82ab9e2121976dc74f131f3", size = 797872, upload-time = "2024-10-09T11:33:30.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/14/fb75c01b8427fb567c90ce920c90ed2bd314ad6960d54e8b377928607fd1/coverage-7.6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c9df1950fb92d49970cce38100d7e7293c84ed3606eaa16ea0b6bc27175bb667", size = 206561, upload-time = "2024-10-09T11:31:56.354Z" }, + { url = "https://files.pythonhosted.org/packages/93/b4/dcbf15f5583507415d0a78ce206e19d76699f1161e8b1ff6e1a21e9f9743/coverage-7.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:24500f4b0e03aab60ce575c85365beab64b44d4db837021e08339f61d1fbfe52", size = 206994, upload-time = "2024-10-09T11:31:59.205Z" }, + { url = "https://files.pythonhosted.org/packages/47/ee/57d607e14479fb760721ea1784608ade532665934bd75f260b250dc6c877/coverage-7.6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a663b180b6669c400b4630a24cc776f23a992d38ce7ae72ede2a397ce6b0f170", size = 235429, upload-time = "2024-10-09T11:32:00.967Z" }, + { url = "https://files.pythonhosted.org/packages/76/e1/cd263fd750fdb115aab11a086e3584d99d46fca1f201b5493cc3972aea28/coverage-7.6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfde025e2793a22efe8c21f807d276bd1d6a4bcc5ba6f19dbdfc4e7a12160909", size = 233329, upload-time = "2024-10-09T11:32:02.741Z" }, + { url = "https://files.pythonhosted.org/packages/30/3b/a1623d50fcd6ba532cef0c3c1059eec2a08a311676ffa84dbe4beb2b8a33/coverage-7.6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:087932079c065d7b8ebadd3a0160656c55954144af6439886c8bcf78bbbcde7f", size = 234491, upload-time = "2024-10-09T11:32:03.804Z" }, + { url = "https://files.pythonhosted.org/packages/b1/a6/8f3b3fd1f9b9400f3df38a7159362622546e2d951cc4984cf4617d0fd4d7/coverage-7.6.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9c6b0c1cafd96213a0327cf680acb39f70e452caf8e9a25aeb05316db9c07f89", size = 233589, upload-time = "2024-10-09T11:32:04.877Z" }, + { url = "https://files.pythonhosted.org/packages/e3/40/37d64093f57b372435d87679956607ecab066d2aede76c6d215815a35fa3/coverage-7.6.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6e85830eed5b5263ffa0c62428e43cb844296f3b4461f09e4bdb0d44ec190bc2", size = 232050, upload-time = "2024-10-09T11:32:07.647Z" }, + { url = "https://files.pythonhosted.org/packages/80/63/cbb76298b4f42bffe0030f1bc129a26a26255857c6beaa20419259ac07cc/coverage-7.6.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:62ab4231c01e156ece1b3a187c87173f31cbeee83a5e1f6dff17f288dca93345", size = 233180, upload-time = "2024-10-09T11:32:09.321Z" }, + { url = "https://files.pythonhosted.org/packages/7a/6a/eafa81503e905d473b799920927b06aa6ffba12db035fc98735b55bc1741/coverage-7.6.2-cp310-cp310-win32.whl", hash = "sha256:7b80fbb0da3aebde102a37ef0138aeedff45997e22f8962e5f16ae1742852676", size = 209281, upload-time = "2024-10-09T11:32:10.503Z" }, + { url = "https://files.pythonhosted.org/packages/19/d1/6b354c2cd52e0244944c097aaa71896869878df999f5f8e75fcd37eaf0f3/coverage-7.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:d20c3d1f31f14d6962a4e2f549c21d31e670b90f777ef4171be540fb7fb70f02", size = 210092, upload-time = "2024-10-09T11:32:12.213Z" }, + { url = "https://files.pythonhosted.org/packages/a5/29/72da824da4182f518b054c21552b7ed2473a4e4c6ac616298209808a1a5c/coverage-7.6.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bb21bac7783c1bf6f4bbe68b1e0ff0d20e7e7732cfb7995bc8d96e23aa90fc7b", size = 206667, upload-time = "2024-10-09T11:32:13.409Z" }, + { url = "https://files.pythonhosted.org/packages/23/52/c15dcf3cf575256c7c0992e441cd41092a6c519d65abe1eb5567aab3d8e8/coverage-7.6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7b2e437fbd8fae5bc7716b9c7ff97aecc95f0b4d56e4ca08b3c8d8adcaadb84", size = 207111, upload-time = "2024-10-09T11:32:14.469Z" }, + { url = "https://files.pythonhosted.org/packages/92/61/0d46dc26cf9f711b7b6078a54680665a5c2d62ec15991adb51e79236c699/coverage-7.6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:536f77f2bf5797983652d1d55f1a7272a29afcc89e3ae51caa99b2db4e89d658", size = 239050, upload-time = "2024-10-09T11:32:15.578Z" }, + { url = "https://files.pythonhosted.org/packages/3b/cb/9de71bade0343a0793f645f78a0e409248d85a2e5b4c4a9a1697c3b2e3d2/coverage-7.6.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f361296ca7054f0936b02525646b2731b32c8074ba6defab524b79b2b7eeac72", size = 236454, upload-time = "2024-10-09T11:32:17.158Z" }, + { url = "https://files.pythonhosted.org/packages/f2/81/b0dc02487447c4a56cf2eed5c57735097f77aeff582277a35f1f70713a8d/coverage-7.6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7926d8d034e06b479797c199747dd774d5e86179f2ce44294423327a88d66ca7", size = 238320, upload-time = "2024-10-09T11:32:18.282Z" }, + { url = "https://files.pythonhosted.org/packages/60/90/76815a76234050a87d0d1438a34820c1b857dd17353855c02bddabbedea8/coverage-7.6.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0bbae11c138585c89fb4e991faefb174a80112e1a7557d507aaa07675c62e66b", size = 237250, upload-time = "2024-10-09T11:32:19.342Z" }, + { url = "https://files.pythonhosted.org/packages/f6/bd/760a599c08c882d97382855264586bba2604901029c3f6bec5710477ae81/coverage-7.6.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fcad7d5d2bbfeae1026b395036a8aa5abf67e8038ae7e6a25c7d0f88b10a8e6a", size = 235880, upload-time = "2024-10-09T11:32:20.509Z" }, + { url = "https://files.pythonhosted.org/packages/83/de/41c3b90a779e473ae1ca325542aa5fa5464b7d2061288e9c22ba5f1deaa3/coverage-7.6.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f01e53575f27097d75d42de33b1b289c74b16891ce576d767ad8c48d17aeb5e0", size = 236653, upload-time = "2024-10-09T11:32:22.371Z" }, + { url = "https://files.pythonhosted.org/packages/f4/90/61fe2721b9a9d9446e6c3ca33b6569e81d2a9a795ddfe786a66bf54035b7/coverage-7.6.2-cp311-cp311-win32.whl", hash = "sha256:7781f4f70c9b0b39e1b129b10c7d43a4e0c91f90c60435e6da8288efc2b73438", size = 209251, upload-time = "2024-10-09T11:32:23.454Z" }, + { url = "https://files.pythonhosted.org/packages/96/87/d586f2b12b98288fc874d366cd8d5601f5a374cb75853647a3e4d02e4eb0/coverage-7.6.2-cp311-cp311-win_amd64.whl", hash = "sha256:9bcd51eeca35a80e76dc5794a9dd7cb04b97f0e8af620d54711793bfc1fbba4b", size = 210083, upload-time = "2024-10-09T11:32:24.557Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ac/1cca5ed5cf512a71cdd6e3afb75a5ef196f7ef9772be9192dadaaa5cfc1c/coverage-7.6.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ebc94fadbd4a3f4215993326a6a00e47d79889391f5659bf310f55fe5d9f581c", size = 206856, upload-time = "2024-10-09T11:32:26.083Z" }, + { url = "https://files.pythonhosted.org/packages/e4/58/030354d250f107a95e7aca24c7fd238709a3c7df3083cb206368798e637a/coverage-7.6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9681516288e3dcf0aa7c26231178cc0be6cac9705cac06709f2353c5b406cfea", size = 207098, upload-time = "2024-10-09T11:32:28.303Z" }, + { url = "https://files.pythonhosted.org/packages/03/df/5f2cd6048d44a54bb5f58f8ece4efbc5b686ed49f8bd8dbf41eb2a6a687f/coverage-7.6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d9c5d13927d77af4fbe453953810db766f75401e764727e73a6ee4f82527b3e", size = 240109, upload-time = "2024-10-09T11:32:29.451Z" }, + { url = "https://files.pythonhosted.org/packages/d3/18/7c53887643d921faa95529643b1b33e60ebba30ab835c8b5abd4e54d946b/coverage-7.6.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b92f9ca04b3e719d69b02dc4a69debb795af84cb7afd09c5eb5d54b4a1ae2191", size = 237141, upload-time = "2024-10-09T11:32:31.276Z" }, + { url = "https://files.pythonhosted.org/packages/d2/79/339bdf597d128374e6150c089b37436ba694585d769cabf6d5abd73a1365/coverage-7.6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ff2ef83d6d0b527b5c9dad73819b24a2f76fdddcfd6c4e7a4d7e73ecb0656b4", size = 239210, upload-time = "2024-10-09T11:32:33.187Z" }, + { url = "https://files.pythonhosted.org/packages/a9/62/7310c6de2bcb8a42f91094d41f0d4793ccda5a54621be3db76a156556cf2/coverage-7.6.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:47ccb6e99a3031ffbbd6e7cc041e70770b4fe405370c66a54dbf26a500ded80b", size = 238698, upload-time = "2024-10-09T11:32:34.292Z" }, + { url = "https://files.pythonhosted.org/packages/f2/cb/ccb23c084d7f581f770dc7ed547dc5b50763334ad6ce26087a9ad0b5b26d/coverage-7.6.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a867d26f06bcd047ef716175b2696b315cb7571ccb951006d61ca80bbc356e9e", size = 237000, upload-time = "2024-10-09T11:32:36.737Z" }, + { url = "https://files.pythonhosted.org/packages/e7/ab/58de9e2f94e4dc91b84d6e2705aa1e9d5447a2669fe113b4bbce6d2224a1/coverage-7.6.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cdfcf2e914e2ba653101157458afd0ad92a16731eeba9a611b5cbb3e7124e74b", size = 238666, upload-time = "2024-10-09T11:32:38.027Z" }, + { url = "https://files.pythonhosted.org/packages/6c/dc/8be87b9ed5dbd4892b603f41088b41982768e928734e5bdce67d2ddd460a/coverage-7.6.2-cp312-cp312-win32.whl", hash = "sha256:f9035695dadfb397bee9eeaf1dc7fbeda483bf7664a7397a629846800ce6e276", size = 209489, upload-time = "2024-10-09T11:32:39.345Z" }, + { url = "https://files.pythonhosted.org/packages/64/3a/3f44e55273a58bfb39b87ad76541bbb81d14de916b034fdb39971cc99ffe/coverage-7.6.2-cp312-cp312-win_amd64.whl", hash = "sha256:5ed69befa9a9fc796fe015a7040c9398722d6b97df73a6b608e9e275fa0932b0", size = 210270, upload-time = "2024-10-09T11:32:40.569Z" }, + { url = "https://files.pythonhosted.org/packages/ae/99/c9676a75b57438a19c5174dfcf39798b42728ad56650497286379dc0c2c3/coverage-7.6.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4eea60c79d36a8f39475b1af887663bc3ae4f31289cd216f514ce18d5938df40", size = 206888, upload-time = "2024-10-09T11:32:42.417Z" }, + { url = "https://files.pythonhosted.org/packages/e0/de/820ecb42e892049c5f384430e98b35b899da3451dd0cdb2f867baf26abfa/coverage-7.6.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa68a6cdbe1bc6793a9dbfc38302c11599bbe1837392ae9b1d238b9ef3dafcf1", size = 207142, upload-time = "2024-10-09T11:32:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/dd/59/81fc7ad855d65eeb68fe9e7809cbb339946adb07be7ac32d3fc24dc17bd7/coverage-7.6.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ec528ae69f0a139690fad6deac8a7d33629fa61ccce693fdd07ddf7e9931fba", size = 239658, upload-time = "2024-10-09T11:32:45.479Z" }, + { url = "https://files.pythonhosted.org/packages/cd/a7/865de3eb9e78ffbf7afd92f86d2580b18edfb6f0481bd3c39b205e05a762/coverage-7.6.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed5ac02126f74d190fa2cc14a9eb2a5d9837d5863920fa472b02eb1595cdc925", size = 236802, upload-time = "2024-10-09T11:32:46.868Z" }, + { url = "https://files.pythonhosted.org/packages/36/94/3b8f3abf88b7c451f97fd14c98f536bcee364e74250d928d57cc97c38ddd/coverage-7.6.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21c0ea0d4db8a36b275cb6fb2437a3715697a4ba3cb7b918d3525cc75f726304", size = 238793, upload-time = "2024-10-09T11:32:48.095Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4b/57f95e41a10525002f524f3dbd577a3a9871d67998f8a8eb192fe697dc7b/coverage-7.6.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:35a51598f29b2a19e26d0908bd196f771a9b1c5d9a07bf20be0adf28f1ad4f77", size = 238455, upload-time = "2024-10-09T11:32:50.784Z" }, + { url = "https://files.pythonhosted.org/packages/99/c9/9fbe5b841628e1d9030c8044844afef4f4735586289eb9237eeb5b97f0d7/coverage-7.6.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c9192925acc33e146864b8cf037e2ed32a91fdf7644ae875f5d46cd2ef086a5f", size = 236538, upload-time = "2024-10-09T11:32:52.072Z" }, + { url = "https://files.pythonhosted.org/packages/43/0d/2200a0d447e30de94d48e4851c04d8dce37340815e7eda27457a7043c037/coverage-7.6.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf4eeecc9e10f5403ec06138978235af79c9a79af494eb6b1d60a50b49ed2869", size = 238383, upload-time = "2024-10-09T11:32:53.25Z" }, + { url = "https://files.pythonhosted.org/packages/ec/8a/106c66faafb4a87002b698769d6de3c4db0b6c29a7aeb72de13b893c333e/coverage-7.6.2-cp313-cp313-win32.whl", hash = "sha256:e4ee15b267d2dad3e8759ca441ad450c334f3733304c55210c2a44516e8d5530", size = 209551, upload-time = "2024-10-09T11:32:54.455Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f5/1b39e2faaf5b9cc7eed568c444df5991ce7ff7138e2e735a6801be1bdadb/coverage-7.6.2-cp313-cp313-win_amd64.whl", hash = "sha256:c71965d1ced48bf97aab79fad56df82c566b4c498ffc09c2094605727c4b7e36", size = 210282, upload-time = "2024-10-09T11:32:55.661Z" }, + { url = "https://files.pythonhosted.org/packages/79/a3/8dd4e6c09f5286094cd6c7edb115b3fbf06ad8304d45431722a4e3bc2508/coverage-7.6.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7571e8bbecc6ac066256f9de40365ff833553e2e0c0c004f4482facb131820ef", size = 207629, upload-time = "2024-10-09T11:32:57.543Z" }, + { url = "https://files.pythonhosted.org/packages/8e/db/a9aa7009bbdc570a235e1ac781c0a83aa323cac6db8f8f13c2127b110978/coverage-7.6.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:078a87519057dacb5d77e333f740708ec2a8f768655f1db07f8dfd28d7a005f0", size = 207902, upload-time = "2024-10-09T11:32:58.787Z" }, + { url = "https://files.pythonhosted.org/packages/54/08/d0962be62d4335599ca2ff3a48bb68c9bfb80df74e28ca689ff5f392087b/coverage-7.6.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e5e92e3e84a8718d2de36cd8387459cba9a4508337b8c5f450ce42b87a9e760", size = 250617, upload-time = "2024-10-09T11:33:02.145Z" }, + { url = "https://files.pythonhosted.org/packages/a5/a2/158570aff1dd88b661a6c11281cbb190e8696e77798b4b2e47c74bfb2f39/coverage-7.6.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ebabdf1c76593a09ee18c1a06cd3022919861365219ea3aca0247ededf6facd6", size = 246334, upload-time = "2024-10-09T11:33:03.943Z" }, + { url = "https://files.pythonhosted.org/packages/aa/fe/b00428cca325b6585ca77422e4f64d7d86a225b14664b98682ea501efb57/coverage-7.6.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12179eb0575b8900912711688e45474f04ab3934aaa7b624dea7b3c511ecc90f", size = 248692, upload-time = "2024-10-09T11:33:05.216Z" }, + { url = "https://files.pythonhosted.org/packages/30/21/0a15fefc13039450bc45e7159f3add92489f004555eb7dab9c7ad4365dd0/coverage-7.6.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:39d3b964abfe1519b9d313ab28abf1d02faea26cd14b27f5283849bf59479ff5", size = 248188, upload-time = "2024-10-09T11:33:07.139Z" }, + { url = "https://files.pythonhosted.org/packages/de/b8/5c093526046a8450a7a3d62ad09517cf38e638f6b3ee9433dd6a73360501/coverage-7.6.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:84c4315577f7cd511d6250ffd0f695c825efe729f4205c0340f7004eda51191f", size = 246072, upload-time = "2024-10-09T11:33:08.425Z" }, + { url = "https://files.pythonhosted.org/packages/1e/8b/542b607d2cff56e5a90a6948f5a9040b693761d2be2d3c3bf88957b02361/coverage-7.6.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ff797320dcbff57caa6b2301c3913784a010e13b1f6cf4ab3f563f3c5e7919db", size = 247354, upload-time = "2024-10-09T11:33:09.719Z" }, + { url = "https://files.pythonhosted.org/packages/95/82/2e9111aa5e59f42b332d387f64e3205c2263518d1e660154d0c9fc54390e/coverage-7.6.2-cp313-cp313t-win32.whl", hash = "sha256:2b636a301e53964550e2f3094484fa5a96e699db318d65398cfba438c5c92171", size = 210194, upload-time = "2024-10-09T11:33:10.935Z" }, + { url = "https://files.pythonhosted.org/packages/9d/46/aabe4305cfc57cab4865f788ceceef746c422469720c32ed7a5b44e20f5e/coverage-7.6.2-cp313-cp313t-win_amd64.whl", hash = "sha256:d03a060ac1a08e10589c27d509bbdb35b65f2d7f3f8d81cf2fa199877c7bc58a", size = 211346, upload-time = "2024-10-09T11:33:12.889Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a9/85d14426f2449252f302f12c1c2a957a0a7ae7f35317ca3eaa365e1d6453/coverage-7.6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c37faddc8acd826cfc5e2392531aba734b229741d3daec7f4c777a8f0d4993e5", size = 206555, upload-time = "2024-10-09T11:33:14.181Z" }, + { url = "https://files.pythonhosted.org/packages/71/ff/bc4d5697a55edf1ff077c47df5637ff4518ba2760ada82c142aca79ea3fe/coverage-7.6.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab31fdd643f162c467cfe6a86e9cb5f1965b632e5e65c072d90854ff486d02cf", size = 206990, upload-time = "2024-10-09T11:33:15.48Z" }, + { url = "https://files.pythonhosted.org/packages/34/65/1301721d09f5b58da9decfd62eb42eaef07fdb854dae904c3482e59cc309/coverage-7.6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97df87e1a20deb75ac7d920c812e9326096aa00a9a4b6d07679b4f1f14b06c90", size = 235022, upload-time = "2024-10-09T11:33:16.813Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ec/7a2f361485226e6934a8f5d1f6eef7e8b7faf228fb6107476fa584700a32/coverage-7.6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:343056c5e0737487a5291f5691f4dfeb25b3e3c8699b4d36b92bb0e586219d14", size = 232943, upload-time = "2024-10-09T11:33:18.319Z" }, + { url = "https://files.pythonhosted.org/packages/2d/60/b23e61a372bef93c9d13d87efa2ea3a870130be498e5b81740616b6e6200/coverage-7.6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad4ef1c56b47b6b9024b939d503ab487231df1f722065a48f4fc61832130b90e", size = 234074, upload-time = "2024-10-09T11:33:19.655Z" }, + { url = "https://files.pythonhosted.org/packages/89/ec/4a56d9b310b2413987682ae3a858e30ea11d6f6d05366ecab4d73385fbef/coverage-7.6.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fca4a92c8a7a73dee6946471bce6d1443d94155694b893b79e19ca2a540d86e", size = 233226, upload-time = "2024-10-09T11:33:20.986Z" }, + { url = "https://files.pythonhosted.org/packages/8c/77/31ecc00c525dea216d59090b807e9d1268a07d289f9dbe0cfc6795e33b68/coverage-7.6.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69f251804e052fc46d29d0e7348cdc5fcbfc4861dc4a1ebedef7e78d241ad39e", size = 231706, upload-time = "2024-10-09T11:33:22.369Z" }, + { url = "https://files.pythonhosted.org/packages/7b/02/3f84bdd286a9db9b816cb5ca0adfa001575f8e496ba39da26f0ded2f0849/coverage-7.6.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e8ea055b3ea046c0f66217af65bc193bbbeca1c8661dc5fd42698db5795d2627", size = 232697, upload-time = "2024-10-09T11:33:23.697Z" }, + { url = "https://files.pythonhosted.org/packages/7c/34/158b73026cbc2d2b3a56fbc71d955c0eea52953e49de97f820b3060f62b9/coverage-7.6.2-cp39-cp39-win32.whl", hash = "sha256:6c2ba1e0c24d8fae8f2cf0aeb2fc0a2a7f69b6d20bd8d3749fd6b36ecef5edf0", size = 209278, upload-time = "2024-10-09T11:33:25.075Z" }, + { url = "https://files.pythonhosted.org/packages/d1/05/4326e4ea071176f0bddc30b5a3555b48fa96c45a8f6a09b6c2e4041dfcc0/coverage-7.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:2186369a654a15628e9c1c9921409a6b3eda833e4b91f3ca2a7d9f77abb4987c", size = 210057, upload-time = "2024-10-09T11:33:26.772Z" }, + { url = "https://files.pythonhosted.org/packages/9d/5c/88f15b7614ba9ed1dbb1c0bd2c9073184b96c2bead0b93199487b44d04b3/coverage-7.6.2-pp39.pp310-none-any.whl", hash = "sha256:667952739daafe9616db19fbedbdb87917eee253ac4f31d70c7587f7ab531b4e", size = 198799, upload-time = "2024-10-09T11:33:28.796Z" }, ] [package.optional-dependencies] @@ -171,36 +162,36 @@ toml = [ name = "distlib" version = "0.3.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923 } +sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923, upload-time = "2024-10-09T18:35:47.551Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 }, + { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973, upload-time = "2024-10-09T18:35:44.272Z" }, ] [[package]] name = "exceptiongroup" version = "1.2.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } +sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883, upload-time = "2024-07-12T22:26:00.161Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, + { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453, upload-time = "2024-07-12T22:25:58.476Z" }, ] [[package]] name = "filelock" version = "3.16.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/db/3ef5bb276dae18d6ec2124224403d1d67bccdbefc17af4cc8f553e341ab1/filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435", size = 18037 } +sdist = { url = "https://files.pythonhosted.org/packages/9d/db/3ef5bb276dae18d6ec2124224403d1d67bccdbefc17af4cc8f553e341ab1/filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435", size = 18037, upload-time = "2024-09-17T19:02:01.779Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163 }, + { url = "https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163, upload-time = "2024-09-17T19:02:00.268Z" }, ] [[package]] name = "h11" version = "0.14.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418, upload-time = "2022-09-25T15:40:01.519Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259, upload-time = "2022-09-25T15:39:59.68Z" }, ] [[package]] @@ -211,9 +202,9 @@ dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b6/44/ed0fa6a17845fb033bd885c03e842f08c1b9406c86a2e60ac1ae1b9206a6/httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f", size = 85180 } +sdist = { url = "https://files.pythonhosted.org/packages/b6/44/ed0fa6a17845fb033bd885c03e842f08c1b9406c86a2e60ac1ae1b9206a6/httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f", size = 85180, upload-time = "2024-10-01T17:02:00.094Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/89/b161908e2f51be56568184aeb4a880fd287178d176fd1c860d2217f41106/httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f", size = 78011 }, + { url = "https://files.pythonhosted.org/packages/06/89/b161908e2f51be56568184aeb4a880fd287178d176fd1c860d2217f41106/httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f", size = 78011, upload-time = "2024-10-01T17:01:58.811Z" }, ] [[package]] @@ -227,81 +218,124 @@ dependencies = [ { name = "idna" }, { name = "sniffio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/82/08f8c936781f67d9e6b9eeb8a0c8b4e406136ea4c3d1f89a5db71d42e0e6/httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2", size = 144189 } +sdist = { url = "https://files.pythonhosted.org/packages/78/82/08f8c936781f67d9e6b9eeb8a0c8b4e406136ea4c3d1f89a5db71d42e0e6/httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2", size = 144189, upload-time = "2024-08-27T12:54:01.334Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/95/9377bcb415797e44274b51d46e3249eba641711cf3348050f76ee7b15ffc/httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0", size = 76395 }, + { url = "https://files.pythonhosted.org/packages/56/95/9377bcb415797e44274b51d46e3249eba641711cf3348050f76ee7b15ffc/httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0", size = 76395, upload-time = "2024-08-27T12:53:59.653Z" }, ] [[package]] name = "identify" version = "2.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bb/25024dbcc93516c492b75919e76f389bac754a3e4248682fba32b250c880/identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98", size = 99097 } +sdist = { url = "https://files.pythonhosted.org/packages/29/bb/25024dbcc93516c492b75919e76f389bac754a3e4248682fba32b250c880/identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98", size = 99097, upload-time = "2024-09-14T23:50:32.513Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/0c/4ef72754c050979fdcc06c744715ae70ea37e734816bb6514f79df77a42f/identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0", size = 98972 }, + { url = "https://files.pythonhosted.org/packages/7d/0c/4ef72754c050979fdcc06c744715ae70ea37e734816bb6514f79df77a42f/identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0", size = 98972, upload-time = "2024-09-14T23:50:30.747Z" }, ] [[package]] name = "idna" version = "3.10" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, ] [[package]] name = "iniconfig" version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646, upload-time = "2023-01-07T11:08:11.254Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892, upload-time = "2023-01-07T11:08:09.864Z" }, +] + +[[package]] +name = "msgspec" +version = "0.19.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/9b/95d8ce458462b8b71b8a70fa94563b2498b89933689f3a7b8911edfae3d7/msgspec-0.19.0.tar.gz", hash = "sha256:604037e7cd475345848116e89c553aa9a233259733ab51986ac924ab1b976f8e", size = 216934, upload-time = "2024-12-27T17:40:28.597Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/40/817282b42f58399762267b30deb8ac011d8db373f8da0c212c85fbe62b8f/msgspec-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d8dd848ee7ca7c8153462557655570156c2be94e79acec3561cf379581343259", size = 190019, upload-time = "2024-12-27T17:39:13.803Z" }, + { url = "https://files.pythonhosted.org/packages/92/99/bd7ed738c00f223a8119928661167a89124140792af18af513e6519b0d54/msgspec-0.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0553bbc77662e5708fe66aa75e7bd3e4b0f209709c48b299afd791d711a93c36", size = 183680, upload-time = "2024-12-27T17:39:17.847Z" }, + { url = "https://files.pythonhosted.org/packages/e5/27/322badde18eb234e36d4a14122b89edd4e2973cdbc3da61ca7edf40a1ccd/msgspec-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe2c4bf29bf4e89790b3117470dea2c20b59932772483082c468b990d45fb947", size = 209334, upload-time = "2024-12-27T17:39:19.065Z" }, + { url = "https://files.pythonhosted.org/packages/c6/65/080509c5774a1592b2779d902a70b5fe008532759927e011f068145a16cb/msgspec-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e87ecfa9795ee5214861eab8326b0e75475c2e68a384002aa135ea2a27d909", size = 211551, upload-time = "2024-12-27T17:39:21.767Z" }, + { url = "https://files.pythonhosted.org/packages/6f/2e/1c23c6b4ca6f4285c30a39def1054e2bee281389e4b681b5e3711bd5a8c9/msgspec-0.19.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3c4ec642689da44618f68c90855a10edbc6ac3ff7c1d94395446c65a776e712a", size = 215099, upload-time = "2024-12-27T17:39:24.71Z" }, + { url = "https://files.pythonhosted.org/packages/83/fe/95f9654518879f3359d1e76bc41189113aa9102452170ab7c9a9a4ee52f6/msgspec-0.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2719647625320b60e2d8af06b35f5b12d4f4d281db30a15a1df22adb2295f633", size = 218211, upload-time = "2024-12-27T17:39:27.396Z" }, + { url = "https://files.pythonhosted.org/packages/79/f6/71ca7e87a1fb34dfe5efea8156c9ef59dd55613aeda2ca562f122cd22012/msgspec-0.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:695b832d0091edd86eeb535cd39e45f3919f48d997685f7ac31acb15e0a2ed90", size = 186174, upload-time = "2024-12-27T17:39:29.647Z" }, + { url = "https://files.pythonhosted.org/packages/24/d4/2ec2567ac30dab072cce3e91fb17803c52f0a37aab6b0c24375d2b20a581/msgspec-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa77046904db764b0462036bc63ef71f02b75b8f72e9c9dd4c447d6da1ed8f8e", size = 187939, upload-time = "2024-12-27T17:39:32.347Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/18226e4328897f4f19875cb62bb9259fe47e901eade9d9376ab5f251a929/msgspec-0.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:047cfa8675eb3bad68722cfe95c60e7afabf84d1bd8938979dd2b92e9e4a9551", size = 182202, upload-time = "2024-12-27T17:39:33.633Z" }, + { url = "https://files.pythonhosted.org/packages/81/25/3a4b24d468203d8af90d1d351b77ea3cffb96b29492855cf83078f16bfe4/msgspec-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e78f46ff39a427e10b4a61614a2777ad69559cc8d603a7c05681f5a595ea98f7", size = 209029, upload-time = "2024-12-27T17:39:35.023Z" }, + { url = "https://files.pythonhosted.org/packages/85/2e/db7e189b57901955239f7689b5dcd6ae9458637a9c66747326726c650523/msgspec-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c7adf191e4bd3be0e9231c3b6dc20cf1199ada2af523885efc2ed218eafd011", size = 210682, upload-time = "2024-12-27T17:39:36.384Z" }, + { url = "https://files.pythonhosted.org/packages/03/97/7c8895c9074a97052d7e4a1cc1230b7b6e2ca2486714eb12c3f08bb9d284/msgspec-0.19.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f04cad4385e20be7c7176bb8ae3dca54a08e9756cfc97bcdb4f18560c3042063", size = 214003, upload-time = "2024-12-27T17:39:39.097Z" }, + { url = "https://files.pythonhosted.org/packages/61/61/e892997bcaa289559b4d5869f066a8021b79f4bf8e955f831b095f47a4cd/msgspec-0.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45c8fb410670b3b7eb884d44a75589377c341ec1392b778311acdbfa55187716", size = 216833, upload-time = "2024-12-27T17:39:41.203Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3d/71b2dffd3a1c743ffe13296ff701ee503feaebc3f04d0e75613b6563c374/msgspec-0.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:70eaef4934b87193a27d802534dc466778ad8d536e296ae2f9334e182ac27b6c", size = 186184, upload-time = "2024-12-27T17:39:43.702Z" }, + { url = "https://files.pythonhosted.org/packages/b2/5f/a70c24f075e3e7af2fae5414c7048b0e11389685b7f717bb55ba282a34a7/msgspec-0.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f98bd8962ad549c27d63845b50af3f53ec468b6318400c9f1adfe8b092d7b62f", size = 190485, upload-time = "2024-12-27T17:39:44.974Z" }, + { url = "https://files.pythonhosted.org/packages/89/b0/1b9763938cfae12acf14b682fcf05c92855974d921a5a985ecc197d1c672/msgspec-0.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:43bbb237feab761b815ed9df43b266114203f53596f9b6e6f00ebd79d178cdf2", size = 183910, upload-time = "2024-12-27T17:39:46.401Z" }, + { url = "https://files.pythonhosted.org/packages/87/81/0c8c93f0b92c97e326b279795f9c5b956c5a97af28ca0fbb9fd86c83737a/msgspec-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cfc033c02c3e0aec52b71710d7f84cb3ca5eb407ab2ad23d75631153fdb1f12", size = 210633, upload-time = "2024-12-27T17:39:49.099Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ef/c5422ce8af73928d194a6606f8ae36e93a52fd5e8df5abd366903a5ca8da/msgspec-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d911c442571605e17658ca2b416fd8579c5050ac9adc5e00c2cb3126c97f73bc", size = 213594, upload-time = "2024-12-27T17:39:51.204Z" }, + { url = "https://files.pythonhosted.org/packages/19/2b/4137bc2ed45660444842d042be2cf5b18aa06efd2cda107cff18253b9653/msgspec-0.19.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:757b501fa57e24896cf40a831442b19a864f56d253679f34f260dcb002524a6c", size = 214053, upload-time = "2024-12-27T17:39:52.866Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e6/8ad51bdc806aac1dc501e8fe43f759f9ed7284043d722b53323ea421c360/msgspec-0.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5f0f65f29b45e2816d8bded36e6b837a4bf5fb60ec4bc3c625fa2c6da4124537", size = 219081, upload-time = "2024-12-27T17:39:55.142Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ef/27dd35a7049c9a4f4211c6cd6a8c9db0a50647546f003a5867827ec45391/msgspec-0.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:067f0de1c33cfa0b6a8206562efdf6be5985b988b53dd244a8e06f993f27c8c0", size = 187467, upload-time = "2024-12-27T17:39:56.531Z" }, + { url = "https://files.pythonhosted.org/packages/3c/cb/2842c312bbe618d8fefc8b9cedce37f773cdc8fa453306546dba2c21fd98/msgspec-0.19.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f12d30dd6266557aaaf0aa0f9580a9a8fbeadfa83699c487713e355ec5f0bd86", size = 190498, upload-time = "2024-12-27T17:40:00.427Z" }, + { url = "https://files.pythonhosted.org/packages/58/95/c40b01b93465e1a5f3b6c7d91b10fb574818163740cc3acbe722d1e0e7e4/msgspec-0.19.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82b2c42c1b9ebc89e822e7e13bbe9d17ede0c23c187469fdd9505afd5a481314", size = 183950, upload-time = "2024-12-27T17:40:04.219Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f0/5b764e066ce9aba4b70d1db8b087ea66098c7c27d59b9dd8a3532774d48f/msgspec-0.19.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19746b50be214a54239aab822964f2ac81e38b0055cca94808359d779338c10e", size = 210647, upload-time = "2024-12-27T17:40:05.606Z" }, + { url = "https://files.pythonhosted.org/packages/9d/87/bc14f49bc95c4cb0dd0a8c56028a67c014ee7e6818ccdce74a4862af259b/msgspec-0.19.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60ef4bdb0ec8e4ad62e5a1f95230c08efb1f64f32e6e8dd2ced685bcc73858b5", size = 213563, upload-time = "2024-12-27T17:40:10.516Z" }, + { url = "https://files.pythonhosted.org/packages/53/2f/2b1c2b056894fbaa975f68f81e3014bb447516a8b010f1bed3fb0e016ed7/msgspec-0.19.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac7f7c377c122b649f7545810c6cd1b47586e3aa3059126ce3516ac7ccc6a6a9", size = 213996, upload-time = "2024-12-27T17:40:12.244Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5a/4cd408d90d1417e8d2ce6a22b98a6853c1b4d7cb7669153e4424d60087f6/msgspec-0.19.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5bc1472223a643f5ffb5bf46ccdede7f9795078194f14edd69e3aab7020d327", size = 219087, upload-time = "2024-12-27T17:40:14.881Z" }, + { url = "https://files.pythonhosted.org/packages/23/d8/f15b40611c2d5753d1abb0ca0da0c75348daf1252220e5dda2867bd81062/msgspec-0.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:317050bc0f7739cb30d257ff09152ca309bf5a369854bbf1e57dffc310c1f20f", size = 187432, upload-time = "2024-12-27T17:40:16.256Z" }, + { url = "https://files.pythonhosted.org/packages/ea/d0/323f867eaec1f2236ba30adf613777b1c97a7e8698e2e881656b21871fa4/msgspec-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15c1e86fff77184c20a2932cd9742bf33fe23125fa3fcf332df9ad2f7d483044", size = 189926, upload-time = "2024-12-27T17:40:18.939Z" }, + { url = "https://files.pythonhosted.org/packages/a8/37/c3e1b39bdae90a7258d77959f5f5e36ad44b40e2be91cff83eea33c54d43/msgspec-0.19.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3b5541b2b3294e5ffabe31a09d604e23a88533ace36ac288fa32a420aa38d229", size = 183873, upload-time = "2024-12-27T17:40:20.214Z" }, + { url = "https://files.pythonhosted.org/packages/cb/a2/48f2c15c7644668e51f4dce99d5f709bd55314e47acb02e90682f5880f35/msgspec-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f5c043ace7962ef188746e83b99faaa9e3e699ab857ca3f367b309c8e2c6b12", size = 209272, upload-time = "2024-12-27T17:40:21.534Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/aa339cf08b990c3f07e67b229a3a8aa31bf129ed974b35e5daa0df7d9d56/msgspec-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca06aa08e39bf57e39a258e1996474f84d0dd8130d486c00bec26d797b8c5446", size = 211396, upload-time = "2024-12-27T17:40:22.897Z" }, + { url = "https://files.pythonhosted.org/packages/c7/00/c7fb9d524327c558b2803973cc3f988c5100a1708879970a9e377bdf6f4f/msgspec-0.19.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e695dad6897896e9384cf5e2687d9ae9feaef50e802f93602d35458e20d1fb19", size = 215002, upload-time = "2024-12-27T17:40:24.341Z" }, + { url = "https://files.pythonhosted.org/packages/3f/bf/d9f9fff026c1248cde84a5ce62b3742e8a63a3c4e811f99f00c8babf7615/msgspec-0.19.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3be5c02e1fee57b54130316a08fe40cca53af92999a302a6054cd451700ea7db", size = 218132, upload-time = "2024-12-27T17:40:25.744Z" }, + { url = "https://files.pythonhosted.org/packages/00/03/b92011210f79794958167a3a3ea64a71135d9a2034cfb7597b545a42606d/msgspec-0.19.0-cp39-cp39-win_amd64.whl", hash = "sha256:0684573a821be3c749912acf5848cce78af4298345cb2d7a8b8948a0a5a27cfe", size = 186301, upload-time = "2024-12-27T17:40:27.076Z" }, ] [[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 } +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 }, + { 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 = "packaging" version = "24.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 } +sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788, upload-time = "2024-06-09T23:19:24.956Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 }, + { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985, upload-time = "2024-06-09T23:19:21.909Z" }, ] [[package]] name = "phonenumbers" version = "8.13.47" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/0c/8f315d5e6ddea2e45ae13ada6936df6240858929881daf20cb3133fdb729/phonenumbers-8.13.47.tar.gz", hash = "sha256:53c5e7c6d431cafe4efdd44956078404ae9bc8b0eacc47be3105d3ccc88aaffa", size = 2297081 } +sdist = { url = "https://files.pythonhosted.org/packages/ae/0c/8f315d5e6ddea2e45ae13ada6936df6240858929881daf20cb3133fdb729/phonenumbers-8.13.47.tar.gz", hash = "sha256:53c5e7c6d431cafe4efdd44956078404ae9bc8b0eacc47be3105d3ccc88aaffa", size = 2297081, upload-time = "2024-10-04T06:00:28.756Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/0b/5cde445764ac72460748107e999b026b7245e3fcc5fd5551cc5aff45e469/phonenumbers-8.13.47-py2.py3-none-any.whl", hash = "sha256:5d3c0142ef7055ca5551884352e3b6b93bfe002a0bc95b8eaba39b0e2184541b", size = 2582530 }, + { url = "https://files.pythonhosted.org/packages/b6/0b/5cde445764ac72460748107e999b026b7245e3fcc5fd5551cc5aff45e469/phonenumbers-8.13.47-py2.py3-none-any.whl", hash = "sha256:5d3c0142ef7055ca5551884352e3b6b93bfe002a0bc95b8eaba39b0e2184541b", size = 2582530, upload-time = "2024-10-04T06:00:24.918Z" }, ] [[package]] name = "platformdirs" version = "4.3.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302, upload-time = "2024-09-17T19:06:50.688Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439, upload-time = "2024-09-17T19:06:49.212Z" }, ] [[package]] name = "pluggy" version = "1.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload-time = "2024-04-20T21:34:42.531Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" }, ] [[package]] @@ -315,110 +349,9 @@ dependencies = [ { name = "pyyaml" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/64/10/97ee2fa54dff1e9da9badbc5e35d0bbaef0776271ea5907eccf64140f72f/pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af", size = 177815 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/92/caae8c86e94681b42c246f0bca35c059a2f0529e5b92619f6aba4cf7e7b6/pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f", size = 204643 }, -] - -[[package]] -name = "pydantic" -version = "2.9.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-types" }, - { name = "pydantic-core" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a9/b7/d9e3f12af310e1120c21603644a1cd86f59060e040ec5c3a80b8f05fae30/pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f", size = 769917 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/e4/ba44652d562cbf0bf320e0f3810206149c8a4e99cdbf66da82e97ab53a15/pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12", size = 434928 }, -] - -[[package]] -name = "pydantic-core" -version = "2.23.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e2/aa/6b6a9b9f8537b872f552ddd46dd3da230367754b6f707b8e1e963f515ea3/pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863", size = 402156 } +sdist = { url = "https://files.pythonhosted.org/packages/64/10/97ee2fa54dff1e9da9badbc5e35d0bbaef0776271ea5907eccf64140f72f/pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af", size = 177815, upload-time = "2024-07-28T19:59:01.538Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/8b/d3ae387f66277bd8104096d6ec0a145f4baa2966ebb2cad746c0920c9526/pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b", size = 1867835 }, - { url = "https://files.pythonhosted.org/packages/46/76/f68272e4c3a7df8777798282c5e47d508274917f29992d84e1898f8908c7/pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166", size = 1776689 }, - { url = "https://files.pythonhosted.org/packages/cc/69/5f945b4416f42ea3f3bc9d2aaec66c76084a6ff4ff27555bf9415ab43189/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb", size = 1800748 }, - { url = "https://files.pythonhosted.org/packages/50/ab/891a7b0054bcc297fb02d44d05c50e68154e31788f2d9d41d0b72c89fdf7/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916", size = 1806469 }, - { url = "https://files.pythonhosted.org/packages/31/7c/6e3fa122075d78f277a8431c4c608f061881b76c2b7faca01d317ee39b5d/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07", size = 2002246 }, - { url = "https://files.pythonhosted.org/packages/ad/6f/22d5692b7ab63fc4acbc74de6ff61d185804a83160adba5e6cc6068e1128/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232", size = 2659404 }, - { url = "https://files.pythonhosted.org/packages/11/ac/1e647dc1121c028b691028fa61a4e7477e6aeb5132628fde41dd34c1671f/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2", size = 2053940 }, - { url = "https://files.pythonhosted.org/packages/91/75/984740c17f12c3ce18b5a2fcc4bdceb785cce7df1511a4ce89bca17c7e2d/pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f", size = 1921437 }, - { url = "https://files.pythonhosted.org/packages/a0/74/13c5f606b64d93f0721e7768cd3e8b2102164866c207b8cd6f90bb15d24f/pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3", size = 1966129 }, - { url = "https://files.pythonhosted.org/packages/18/03/9c4aa5919457c7b57a016c1ab513b1a926ed9b2bb7915bf8e506bf65c34b/pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071", size = 2110908 }, - { url = "https://files.pythonhosted.org/packages/92/2c/053d33f029c5dc65e5cf44ff03ceeefb7cce908f8f3cca9265e7f9b540c8/pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119", size = 1735278 }, - { url = "https://files.pythonhosted.org/packages/de/81/7dfe464eca78d76d31dd661b04b5f2036ec72ea8848dd87ab7375e185c23/pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f", size = 1917453 }, - { url = "https://files.pythonhosted.org/packages/5d/30/890a583cd3f2be27ecf32b479d5d615710bb926d92da03e3f7838ff3e58b/pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8", size = 1865160 }, - { url = "https://files.pythonhosted.org/packages/1d/9a/b634442e1253bc6889c87afe8bb59447f106ee042140bd57680b3b113ec7/pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d", size = 1776777 }, - { url = "https://files.pythonhosted.org/packages/75/9a/7816295124a6b08c24c96f9ce73085032d8bcbaf7e5a781cd41aa910c891/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e", size = 1799244 }, - { url = "https://files.pythonhosted.org/packages/a9/8f/89c1405176903e567c5f99ec53387449e62f1121894aa9fc2c4fdc51a59b/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607", size = 1805307 }, - { url = "https://files.pythonhosted.org/packages/d5/a5/1a194447d0da1ef492e3470680c66048fef56fc1f1a25cafbea4bc1d1c48/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd", size = 2000663 }, - { url = "https://files.pythonhosted.org/packages/13/a5/1df8541651de4455e7d587cf556201b4f7997191e110bca3b589218745a5/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea", size = 2655941 }, - { url = "https://files.pythonhosted.org/packages/44/31/a3899b5ce02c4316865e390107f145089876dff7e1dfc770a231d836aed8/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e", size = 2052105 }, - { url = "https://files.pythonhosted.org/packages/1b/aa/98e190f8745d5ec831f6d5449344c48c0627ac5fed4e5340a44b74878f8e/pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b", size = 1919967 }, - { url = "https://files.pythonhosted.org/packages/ae/35/b6e00b6abb2acfee3e8f85558c02a0822e9a8b2f2d812ea8b9079b118ba0/pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0", size = 1964291 }, - { url = "https://files.pythonhosted.org/packages/13/46/7bee6d32b69191cd649bbbd2361af79c472d72cb29bb2024f0b6e350ba06/pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64", size = 2109666 }, - { url = "https://files.pythonhosted.org/packages/39/ef/7b34f1b122a81b68ed0a7d0e564da9ccdc9a2924c8d6c6b5b11fa3a56970/pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f", size = 1732940 }, - { url = "https://files.pythonhosted.org/packages/2f/76/37b7e76c645843ff46c1d73e046207311ef298d3f7b2f7d8f6ac60113071/pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3", size = 1916804 }, - { url = "https://files.pythonhosted.org/packages/74/7b/8e315f80666194b354966ec84b7d567da77ad927ed6323db4006cf915f3f/pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231", size = 1856459 }, - { url = "https://files.pythonhosted.org/packages/14/de/866bdce10ed808323d437612aca1ec9971b981e1c52e5e42ad9b8e17a6f6/pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee", size = 1770007 }, - { url = "https://files.pythonhosted.org/packages/dc/69/8edd5c3cd48bb833a3f7ef9b81d7666ccddd3c9a635225214e044b6e8281/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87", size = 1790245 }, - { url = "https://files.pythonhosted.org/packages/80/33/9c24334e3af796ce80d2274940aae38dd4e5676298b4398eff103a79e02d/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8", size = 1801260 }, - { url = "https://files.pythonhosted.org/packages/a5/6f/e9567fd90104b79b101ca9d120219644d3314962caa7948dd8b965e9f83e/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327", size = 1996872 }, - { url = "https://files.pythonhosted.org/packages/2d/ad/b5f0fe9e6cfee915dd144edbd10b6e9c9c9c9d7a56b69256d124b8ac682e/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2", size = 2661617 }, - { url = "https://files.pythonhosted.org/packages/06/c8/7d4b708f8d05a5cbfda3243aad468052c6e99de7d0937c9146c24d9f12e9/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36", size = 2071831 }, - { url = "https://files.pythonhosted.org/packages/89/4d/3079d00c47f22c9a9a8220db088b309ad6e600a73d7a69473e3a8e5e3ea3/pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126", size = 1917453 }, - { url = "https://files.pythonhosted.org/packages/e9/88/9df5b7ce880a4703fcc2d76c8c2d8eb9f861f79d0c56f4b8f5f2607ccec8/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e", size = 1968793 }, - { url = "https://files.pythonhosted.org/packages/e3/b9/41f7efe80f6ce2ed3ee3c2dcfe10ab7adc1172f778cc9659509a79518c43/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24", size = 2116872 }, - { url = "https://files.pythonhosted.org/packages/63/08/b59b7a92e03dd25554b0436554bf23e7c29abae7cce4b1c459cd92746811/pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84", size = 1738535 }, - { url = "https://files.pythonhosted.org/packages/88/8d/479293e4d39ab409747926eec4329de5b7129beaedc3786eca070605d07f/pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9", size = 1917992 }, - { url = "https://files.pythonhosted.org/packages/ad/ef/16ee2df472bf0e419b6bc68c05bf0145c49247a1095e85cee1463c6a44a1/pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc", size = 1856143 }, - { url = "https://files.pythonhosted.org/packages/da/fa/bc3dbb83605669a34a93308e297ab22be82dfb9dcf88c6cf4b4f264e0a42/pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd", size = 1770063 }, - { url = "https://files.pythonhosted.org/packages/4e/48/e813f3bbd257a712303ebdf55c8dc46f9589ec74b384c9f652597df3288d/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05", size = 1790013 }, - { url = "https://files.pythonhosted.org/packages/b4/e0/56eda3a37929a1d297fcab1966db8c339023bcca0b64c5a84896db3fcc5c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d", size = 1801077 }, - { url = "https://files.pythonhosted.org/packages/04/be/5e49376769bfbf82486da6c5c1683b891809365c20d7c7e52792ce4c71f3/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510", size = 1996782 }, - { url = "https://files.pythonhosted.org/packages/bc/24/e3ee6c04f1d58cc15f37bcc62f32c7478ff55142b7b3e6d42ea374ea427c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6", size = 2661375 }, - { url = "https://files.pythonhosted.org/packages/c1/f8/11a9006de4e89d016b8de74ebb1db727dc100608bb1e6bbe9d56a3cbbcce/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b", size = 2071635 }, - { url = "https://files.pythonhosted.org/packages/7c/45/bdce5779b59f468bdf262a5bc9eecbae87f271c51aef628d8c073b4b4b4c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327", size = 1916994 }, - { url = "https://files.pythonhosted.org/packages/d8/fa/c648308fe711ee1f88192cad6026ab4f925396d1293e8356de7e55be89b5/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6", size = 1968877 }, - { url = "https://files.pythonhosted.org/packages/16/16/b805c74b35607d24d37103007f899abc4880923b04929547ae68d478b7f4/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f", size = 2116814 }, - { url = "https://files.pythonhosted.org/packages/d1/58/5305e723d9fcdf1c5a655e6a4cc2a07128bf644ff4b1d98daf7a9dbf57da/pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769", size = 1738360 }, - { url = "https://files.pythonhosted.org/packages/a5/ae/e14b0ff8b3f48e02394d8acd911376b7b66e164535687ef7dc24ea03072f/pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5", size = 1919411 }, - { url = "https://files.pythonhosted.org/packages/7a/04/2580b2deaae37b3e30fc30c54298be938b973990b23612d6b61c7bdd01c7/pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a", size = 1868200 }, - { url = "https://files.pythonhosted.org/packages/39/6e/e311bd0751505350f0cdcee3077841eb1f9253c5a1ddbad048cd9fbf7c6e/pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36", size = 1749316 }, - { url = "https://files.pythonhosted.org/packages/d0/b4/95b5eb47c6dc8692508c3ca04a1f8d6f0884c9dacb34cf3357595cbe73be/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b", size = 1800880 }, - { url = "https://files.pythonhosted.org/packages/da/79/41c4f817acd7f42d94cd1e16526c062a7b089f66faed4bd30852314d9a66/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323", size = 1807077 }, - { url = "https://files.pythonhosted.org/packages/fb/53/d13d1eb0a97d5c06cf7a225935d471e9c241afd389a333f40c703f214973/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3", size = 2002859 }, - { url = "https://files.pythonhosted.org/packages/53/7d/6b8a1eff453774b46cac8c849e99455b27167971a003212f668e94bc4c9c/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df", size = 2661437 }, - { url = "https://files.pythonhosted.org/packages/6c/ea/8820f57f0b46e6148ee42d8216b15e8fe3b360944284bbc705bf34fac888/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c", size = 2054404 }, - { url = "https://files.pythonhosted.org/packages/0f/36/d4ae869e473c3c7868e1cd1e2a1b9e13bce5cd1a7d287f6ac755a0b1575e/pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55", size = 1921680 }, - { url = "https://files.pythonhosted.org/packages/0d/f8/eed5c65b80c4ac4494117e2101973b45fc655774ef647d17dde40a70f7d2/pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040", size = 1966093 }, - { url = "https://files.pythonhosted.org/packages/e8/c8/1d42ce51d65e571ab53d466cae83434325a126811df7ce4861d9d97bee4b/pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605", size = 2111437 }, - { url = "https://files.pythonhosted.org/packages/aa/c9/7fea9d13383c2ec6865919e09cffe44ab77e911eb281b53a4deaafd4c8e8/pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6", size = 1735049 }, - { url = "https://files.pythonhosted.org/packages/98/95/dd7045c4caa2b73d0bf3b989d66b23cfbb7a0ef14ce99db15677a000a953/pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29", size = 1920180 }, - { url = "https://files.pythonhosted.org/packages/13/a9/5d582eb3204464284611f636b55c0a7410d748ff338756323cb1ce721b96/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5", size = 1857135 }, - { url = "https://files.pythonhosted.org/packages/2c/57/faf36290933fe16717f97829eabfb1868182ac495f99cf0eda9f59687c9d/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec", size = 1740583 }, - { url = "https://files.pythonhosted.org/packages/91/7c/d99e3513dc191c4fec363aef1bf4c8af9125d8fa53af7cb97e8babef4e40/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480", size = 1793637 }, - { url = "https://files.pythonhosted.org/packages/29/18/812222b6d18c2d13eebbb0f7cdc170a408d9ced65794fdb86147c77e1982/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068", size = 1941963 }, - { url = "https://files.pythonhosted.org/packages/0f/36/c1f3642ac3f05e6bb4aec3ffc399fa3f84895d259cf5f0ce3054b7735c29/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801", size = 1915332 }, - { url = "https://files.pythonhosted.org/packages/f7/ca/9c0854829311fb446020ebb540ee22509731abad886d2859c855dd29b904/pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728", size = 1957926 }, - { url = "https://files.pythonhosted.org/packages/c0/1c/7836b67c42d0cd4441fcd9fafbf6a027ad4b79b6559f80cf11f89fd83648/pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433", size = 2100342 }, - { url = "https://files.pythonhosted.org/packages/a9/f9/b6bcaf874f410564a78908739c80861a171788ef4d4f76f5009656672dfe/pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753", size = 1920344 }, - { url = "https://files.pythonhosted.org/packages/32/fd/ac9cdfaaa7cf2d32590b807d900612b39acb25e5527c3c7e482f0553025b/pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21", size = 1857850 }, - { url = "https://files.pythonhosted.org/packages/08/fe/038f4b2bcae325ea643c8ad353191187a4c92a9c3b913b139289a6f2ef04/pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb", size = 1740265 }, - { url = "https://files.pythonhosted.org/packages/51/14/b215c9c3cbd1edaaea23014d4b3304260823f712d3fdee52549b19b25d62/pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59", size = 1793912 }, - { url = "https://files.pythonhosted.org/packages/62/de/2c3ad79b63ba564878cbce325be725929ba50089cd5156f89ea5155cb9b3/pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577", size = 1942870 }, - { url = "https://files.pythonhosted.org/packages/cb/55/c222af19e4644c741b3f3fe4fd8bbb6b4cdca87d8a49258b61cf7826b19e/pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744", size = 1915610 }, - { url = "https://files.pythonhosted.org/packages/c4/7a/9a8760692a6f76bb54bcd43f245ff3d8b603db695899bbc624099c00af80/pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef", size = 1958403 }, - { url = "https://files.pythonhosted.org/packages/4c/91/9b03166feb914bb5698e2f6499e07c2617e2eebf69f9374d0358d7eb2009/pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8", size = 2101154 }, - { url = "https://files.pythonhosted.org/packages/1d/d9/1d7ecb98318da4cb96986daaf0e20d66f1651d0aeb9e2d4435b916ce031d/pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e", size = 1920855 }, + { url = "https://files.pythonhosted.org/packages/07/92/caae8c86e94681b42c246f0bca35c059a2f0529e5b92619f6aba4cf7e7b6/pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f", size = 204643, upload-time = "2024-07-28T19:58:59.335Z" }, ] [[package]] @@ -433,9 +366,9 @@ dependencies = [ { name = "pluggy" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } +sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487, upload-time = "2024-09-10T10:52:15.003Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 }, + { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341, upload-time = "2024-09-10T10:52:12.54Z" }, ] [[package]] @@ -445,9 +378,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/6d/c6cf50ce320cf8611df7a1254d86233b3df7cc07f9b5f5cbcb82e08aa534/pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276", size = 49855 } +sdist = { url = "https://files.pythonhosted.org/packages/52/6d/c6cf50ce320cf8611df7a1254d86233b3df7cc07f9b5f5cbcb82e08aa534/pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276", size = 49855, upload-time = "2024-08-22T08:03:18.145Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/31/6607dab48616902f76885dfcf62c08d929796fc3b2d2318faf9fd54dbed9/pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b", size = 18024 }, + { url = "https://files.pythonhosted.org/packages/96/31/6607dab48616902f76885dfcf62c08d929796fc3b2d2318faf9fd54dbed9/pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b", size = 18024, upload-time = "2024-08-22T08:03:15.536Z" }, ] [[package]] @@ -458,98 +391,98 @@ dependencies = [ { name = "coverage", extra = ["toml"] }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/67/00efc8d11b630c56f15f4ad9c7f9223f1e5ec275aaae3fa9118c6a223ad2/pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857", size = 63042 } +sdist = { url = "https://files.pythonhosted.org/packages/74/67/00efc8d11b630c56f15f4ad9c7f9223f1e5ec275aaae3fa9118c6a223ad2/pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857", size = 63042, upload-time = "2024-03-24T20:16:34.856Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/3a/af5b4fa5961d9a1e6237b530eb87dd04aea6eb83da09d2a4073d81b54ccf/pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652", size = 21990 }, + { url = "https://files.pythonhosted.org/packages/78/3a/af5b4fa5961d9a1e6237b530eb87dd04aea6eb83da09d2a4073d81b54ccf/pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652", size = 21990, upload-time = "2024-03-24T20:16:32.444Z" }, ] [[package]] name = "python-dotenv" version = "1.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 } +sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115, upload-time = "2024-01-23T06:33:00.505Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, + { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863, upload-time = "2024-01-23T06:32:58.246Z" }, ] [[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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, - { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, - { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, - { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, - { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, - { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, - { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, - { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, - { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, - { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, - { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, - { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, - { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, - { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, - { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, - { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, - { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, - { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, - { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 }, - { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 }, - { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 }, - { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 }, - { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 }, - { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 }, - { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 }, - { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 }, - { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 }, +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, + { 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/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777, upload-time = "2024-08-06T20:33:25.896Z" }, + { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318, upload-time = "2024-08-06T20:33:27.212Z" }, + { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891, upload-time = "2024-08-06T20:33:28.974Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614, upload-time = "2024-08-06T20:33:34.157Z" }, + { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360, upload-time = "2024-08-06T20:33:35.84Z" }, + { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006, upload-time = "2024-08-06T20:33:37.501Z" }, + { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577, upload-time = "2024-08-06T20:33:39.389Z" }, + { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593, upload-time = "2024-08-06T20:33:46.63Z" }, + { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312, upload-time = "2024-08-06T20:33:49.073Z" }, ] [[package]] name = "sniffio" version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, + { 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 = "tomli" version = "2.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/b9/de2a5c0144d7d75a57ff355c0c24054f965b2dc3036456ae03a51ea6264b/tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed", size = 16096 } +sdist = { url = "https://files.pythonhosted.org/packages/35/b9/de2a5c0144d7d75a57ff355c0c24054f965b2dc3036456ae03a51ea6264b/tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed", size = 16096, upload-time = "2024-10-02T10:46:13.208Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/db/ce8eda256fa131af12e0a76d481711abe4681b6923c27efb9a255c9e4594/tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38", size = 13237 }, + { url = "https://files.pythonhosted.org/packages/cf/db/ce8eda256fa131af12e0a76d481711abe4681b6923c27efb9a255c9e4594/tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38", size = 13237, upload-time = "2024-10-02T10:46:11.806Z" }, ] [[package]] name = "typing-extensions" version = "4.12.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321, upload-time = "2024-06-07T18:52:15.995Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438, upload-time = "2024-06-07T18:52:13.582Z" }, ] [[package]] @@ -561,7 +494,7 @@ dependencies = [ { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/40/abc5a766da6b0b2457f819feab8e9203cbeae29327bd241359f866a3da9d/virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48", size = 9372482 } +sdist = { url = "https://files.pythonhosted.org/packages/3f/40/abc5a766da6b0b2457f819feab8e9203cbeae29327bd241359f866a3da9d/virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48", size = 9372482, upload-time = "2024-09-27T16:28:57.502Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/90/57b8ac0c8a231545adc7698c64c5a36fa7cd8e376c691b9bde877269f2eb/virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2", size = 5999862 }, + { url = "https://files.pythonhosted.org/packages/59/90/57b8ac0c8a231545adc7698c64c5a36fa7cd8e376c691b9bde877269f2eb/virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2", size = 5999862, upload-time = "2024-09-27T16:28:54.798Z" }, ] From d4ae1b97249fb6a3ff21034492667e0cd85c9131 Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:38:38 -0700 Subject: [PATCH 02/38] Remove x-api-key header --- src/contiguity/_client.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/contiguity/_client.py b/src/contiguity/_client.py index fa8ca55..a511861 100644 --- a/src/contiguity/_client.py +++ b/src/contiguity/_client.py @@ -23,7 +23,6 @@ def __init__( super().__init__( headers={ "Content-Type": "application/json", - "X-API-Key": api_key, "Authorization": f"Token {api_key}", }, timeout=timeout, @@ -44,7 +43,6 @@ def __init__( super().__init__( headers={ "Content-Type": "application/json", - "X-API-Key": api_key, "Authorization": f"Token {api_key}", }, timeout=timeout, From 9f928eec2c386429618208cade2819a10270af43 Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:38:51 -0700 Subject: [PATCH 03/38] Create base product class --- src/contiguity/_product.py | 7 +++++++ src/contiguity/email.py | 12 +++--------- src/contiguity/otp.py | 11 ++--------- src/contiguity/text.py | 11 +++-------- 4 files changed, 15 insertions(+), 26 deletions(-) create mode 100644 src/contiguity/_product.py diff --git a/src/contiguity/_product.py b/src/contiguity/_product.py new file mode 100644 index 0000000..beb30a6 --- /dev/null +++ b/src/contiguity/_product.py @@ -0,0 +1,7 @@ +from ._client import ApiClient + + +class BaseProduct: + def __init__(self, *, client: ApiClient, debug: bool = False) -> None: + self._client = client + self.debug = debug diff --git a/src/contiguity/email.py b/src/contiguity/email.py index 1b16227..bf81da1 100644 --- a/src/contiguity/email.py +++ b/src/contiguity/email.py @@ -1,25 +1,19 @@ from __future__ import annotations from http import HTTPStatus -from typing import TYPE_CHECKING, overload +from typing import overload import msgspec +from ._product import BaseProduct from ._response import BaseResponse, ErrorResponse -if TYPE_CHECKING: - from ._client import ApiClient - class EmailResponse(BaseResponse): message: str -class Email: - def __init__(self, *, client: ApiClient, debug: bool = False) -> None: - self._client = client - self.debug = debug - +class Email(BaseProduct): @overload def email( self, diff --git a/src/contiguity/otp.py b/src/contiguity/otp.py index 6685231..91ec1e3 100644 --- a/src/contiguity/otp.py +++ b/src/contiguity/otp.py @@ -2,16 +2,13 @@ from enum import Enum from http import HTTPStatus -from typing import TYPE_CHECKING import msgspec import phonenumbers +from ._product import BaseProduct from ._response import BaseResponse, ErrorResponse -if TYPE_CHECKING: - from ._client import ApiClient - class OTPLanguage(str, Enum): ENGLISH = "en" @@ -64,11 +61,7 @@ class OTPVerifyResponse(BaseResponse): verified: bool -class OTP: - def __init__(self, *, client: ApiClient, debug: bool = False) -> None: - self._client = client - self.debug = debug - +class OTP(BaseProduct): def send( self, to: str, diff --git a/src/contiguity/text.py b/src/contiguity/text.py index 39d26e1..62d953c 100644 --- a/src/contiguity/text.py +++ b/src/contiguity/text.py @@ -3,8 +3,7 @@ import msgspec import phonenumbers -from contiguity._client import ApiClient - +from ._product import BaseProduct from ._response import BaseResponse, ErrorResponse @@ -12,12 +11,8 @@ class TextResponse(BaseResponse): message_id: str -class Text: - def __init__(self, *, client: ApiClient, debug: bool = False) -> None: - self._client = client - self.debug = debug - - def send(self, to: str, message: str) -> TextResponse: +class Text(BaseProduct): + def send(self, *, to: str, message: str) -> TextResponse: """ Send a text message. Args: From 3bc627ae2f381863d535594c8c59fda338590317 Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Thu, 21 Aug 2025 01:26:24 -0700 Subject: [PATCH 04/38] Add IM impl --- src/contiguity/_instant_messaging.py | 135 +++++++++++++++++++++++++++ src/contiguity/imessage.py | 11 +++ src/contiguity/whatsapp.py | 11 +++ 3 files changed, 157 insertions(+) create mode 100644 src/contiguity/_instant_messaging.py diff --git a/src/contiguity/_instant_messaging.py b/src/contiguity/_instant_messaging.py new file mode 100644 index 0000000..3452271 --- /dev/null +++ b/src/contiguity/_instant_messaging.py @@ -0,0 +1,135 @@ +from __future__ import annotations + +import logging +from abc import ABC, abstractmethod +from http import HTTPStatus +from typing import TYPE_CHECKING, Generic, Literal, TypeVar + +from ._product import BaseProduct +from ._response import BaseResponse, ErrorResponse, decode_response + +if TYPE_CHECKING: + from collections.abc import Sequence + +FallbackCauseT = TypeVar("FallbackCauseT", bound=str) + +logger = logging.getLogger(__name__) + + +class IMSendResponse(BaseResponse): + message_id: str + + +class IMTypingResponse(BaseResponse): + status: str + + +class IMReactionResponse(IMTypingResponse): + message: str + + +class InstantMessagingClient(ABC, BaseProduct, Generic[FallbackCauseT]): + @property + @abstractmethod + def _api_path(self) -> str: ... + + def send( # noqa: PLR0913 + self, + *, + to: str, + message: str, + from_: str | None = None, + attachments: Sequence[str] | None = None, + fallback_when: Sequence[FallbackCauseT] | None = None, + fallback_number: str | None = None, + ) -> IMSendResponse: + payload = { + "to": to, + "message": message, + "from": from_, + "attachments": attachments, + "fallback_when": fallback_when, + "fallback_number": fallback_number, + } + + response = self._client.post( + self._api_path, + json={k: v for k, v in payload.items() if v is not None}, + ) + + if response.status_code != HTTPStatus.OK: + data = decode_response(response.content, type=ErrorResponse) + msg = f"failed to send instant message. {response.status_code} {data.error}" + raise ValueError(msg) + + data = decode_response(response.content, type=IMSendResponse) + if self.debug: + logger.debug(f"successfully sent {self._api_path[1:]} message to {to}") + + return data + + def _typing(self, *, to: str, action: Literal["start", "stop"], from_: str | None = None) -> IMTypingResponse: + payload = { + "to": to, + "action": action, + "from": from_, + } + + response = self._client.post( + f"{self._api_path}/typing", + json={k: v for k, v in payload.items() if v is not None}, + ) + + if response.status_code != HTTPStatus.OK: + data = decode_response(response.content, type=ErrorResponse) + msg = f"failed to {action} {self._api_path[1:]} typing indicator. {response.status_code} {data.error}" + raise ValueError(msg) + + data = decode_response(response.content, type=IMTypingResponse) + if self.debug: + logger.debug(f"successfully {action} {self._api_path[1:]} typing indicator for {to}") + + return data + + def start_typing(self, *, to: str, from_: str | None = None) -> IMTypingResponse: + return self._typing(to=to, action="start", from_=from_) + + def stop_typing(self, *, to: str, from_: str | None = None) -> IMTypingResponse: + return self._typing(to=to, action="stop", from_=from_) + + def _reactions( + self, + *, + to: str, + action: Literal["add", "remove"], + reaction: str, + message: str, + ) -> IMReactionResponse: + payload = { + "to": to, + "action": action, + "reaction": reaction, + "message": message, + } + + response = self._client.post( + f"{self._api_path}/reactions", + json={k: v for k, v in payload.items() if v is not None}, + ) + + if response.status_code != HTTPStatus.OK: + data = decode_response(response.content, type=ErrorResponse) + msg = f"failed to {action} {self._api_path[1:]} reaction. {response.status_code} {data.error}" + raise ValueError(msg) + + data = decode_response(response.content, type=IMReactionResponse) + if self.debug: + logger.debug(f"successfully {action} {self._api_path[1:]} reaction for {to}") + + return data + + def add_reaction(self, *, to: str, reaction: str, message: str) -> IMReactionResponse: + return self._reactions(to=to, action="add", reaction=reaction, message=message) + + def remove_reaction(self, *, to: str, reaction: str, message: str) -> IMReactionResponse: + return self._reactions(to=to, action="remove", reaction=reaction, message=message) diff --git a/src/contiguity/imessage.py b/src/contiguity/imessage.py index e69de29..b041835 100644 --- a/src/contiguity/imessage.py +++ b/src/contiguity/imessage.py @@ -0,0 +1,11 @@ +from typing import Literal + +from ._instant_messaging import InstantMessagingClient + +FallbackCause = Literal["imessage_unsupported", "imessage_fails"] + + +class IMessage(InstantMessagingClient[FallbackCause]): + @property + def _api_path(self) -> str: + return "/imessage" diff --git a/src/contiguity/whatsapp.py b/src/contiguity/whatsapp.py index e69de29..7ea58e7 100644 --- a/src/contiguity/whatsapp.py +++ b/src/contiguity/whatsapp.py @@ -0,0 +1,11 @@ +from typing import Literal + +from ._instant_messaging import InstantMessagingClient + +FallbackCause = Literal["whatsapp_unsupported", "whatsapp_fails"] + + +class WhatsApp(InstantMessagingClient[FallbackCause]): + @property + def _api_path(self) -> str: + return "/whatsapp" From e0b55638bb70b20f484537249c7b8ae226a91f34 Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Thu, 21 Aug 2025 01:28:46 -0700 Subject: [PATCH 05/38] Simplify error and debug messages --- src/contiguity/__init__.py | 2 +- src/contiguity/email.py | 4 ++-- src/contiguity/otp.py | 12 ++++++------ src/contiguity/text.py | 11 ++++------- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/contiguity/__init__.py b/src/contiguity/__init__.py index e31ad19..306a6c9 100644 --- a/src/contiguity/__init__.py +++ b/src/contiguity/__init__.py @@ -21,7 +21,7 @@ def __init__( debug: bool = False, ) -> None: if not token: - msg = "Contiguity requires a token/API key to be provided via contiguity.login('token')" + msg = "token cannot be empty" raise ValueError(msg) self.token = token self.base_url = base_url diff --git a/src/contiguity/email.py b/src/contiguity/email.py index bf81da1..4110f2d 100644 --- a/src/contiguity/email.py +++ b/src/contiguity/email.py @@ -84,11 +84,11 @@ def email( # noqa: PLR0913 if response.status_code != HTTPStatus.OK: data = msgspec.json.decode(response.content, type=ErrorResponse) - msg = f"Contiguity couldn't send your email. Received: {response.status_code} with reason: '{data.error}'" + msg = f"failed to send email. {response.status_code} {data.error}" raise ValueError(msg) data = msgspec.json.decode(response.content, type=EmailResponse) if self.debug: - print(f"Contiguity successfully sent your email to {to}") + print(f"successfully sent email to {to}") return data diff --git a/src/contiguity/otp.py b/src/contiguity/otp.py index 91ec1e3..1539fc4 100644 --- a/src/contiguity/otp.py +++ b/src/contiguity/otp.py @@ -83,12 +83,12 @@ def send( if response.status_code != HTTPStatus.OK: data = msgspec.json.decode(response.content, type=ErrorResponse) - msg = f"Contiguity couldn't send your OTP. Received: {response.status_code} with reason: '{data.error}'" + msg = f"failed to send OTP. {response.status_code} {data.error}" raise ValueError(msg) data = msgspec.json.decode(response.content, type=OTPSendResponse) if self.debug: - print(f"Contiguity successfully sent your OTP to {to} with OTP ID {data.otp_id}") + print(f"successfully sent OTP {data.otp_id} to {to}") return data @@ -102,12 +102,12 @@ def resend(self, otp_id: str, /) -> OTPResendResponse: if response.status_code != HTTPStatus.OK: data = msgspec.json.decode(response.content, type=ErrorResponse) - msg = f"Contiguity couldn't resend your OTP. Received: {response.status_code} with reason: '{data.error}'" + msg = f"failed to resend OTP. {response.status_code} {data.error}" raise ValueError(msg) data = msgspec.json.decode(response.content, type=OTPResendResponse) if self.debug: - print(f"Contiguity resent your OTP ({id}) with status: {data.resent}") + print(f"successfully resent OTP {otp_id} with status: {data.resent}") return data @@ -122,11 +122,11 @@ def verify(self, otp: int | str, /, *, otp_id: str) -> OTPVerifyResponse: if response.status_code != HTTPStatus.OK: data = msgspec.json.decode(response.content, type=ErrorResponse) - msg = f"Contiguity couldn't verify your OTP. Received: {response.status_code} with reason: '{data.error}'" + msg = f"failed to verify OTP. {response.status_code} {data.error}" raise ValueError(msg) data = msgspec.json.decode(response.content, type=OTPVerifyResponse) if self.debug: - print(f"Contiguity verified your OTP ({otp}) with status: {data.verified}") + print(f"successfully verified OTP ({otp}) with status: {data.verified}") return data diff --git a/src/contiguity/text.py b/src/contiguity/text.py index 62d953c..c201316 100644 --- a/src/contiguity/text.py +++ b/src/contiguity/text.py @@ -26,10 +26,10 @@ def send(self, *, to: str, message: str) -> TextResponse: try: parsed_number = phonenumbers.parse(to, None) if not phonenumbers.is_valid_number(parsed_number): - msg = "Contiguity requires phone numbers to follow the E.164 format. Formatting failed." + msg = "formatting failed. Phone number must follow the E.164 format." raise ValueError(msg) except phonenumbers.NumberParseException as exc: - msg = "Contiguity requires phone numbers to follow the E.164 format. Parsing failed." + msg = "parsing failed. Phone number must follow the E.164 format." raise ValueError(msg) from exc response = self._client.post( @@ -41,14 +41,11 @@ def send(self, *, to: str, message: str) -> TextResponse: ) if response.status_code != HTTPStatus.OK: - data = msgspec.json.decode(response.content, type=ErrorResponse) - msg = ( - f"Contiguity couldn't send your message. Received: {response.status_code} with reason: '{data.error}'" - ) + msg = f"failed to send message. {response.status_code} {data.error}" raise ValueError(msg) data = msgspec.json.decode(response.content, type=TextResponse) if self.debug: - print(f"Contiguity successfully sent your text to {to}") + print(f"successfully sent text to {to}") return data From 2b2cd0e71269633e0cb5cf20d5be57f389e98a5c Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Thu, 21 Aug 2025 01:30:01 -0700 Subject: [PATCH 06/38] Properly handle API response format --- src/contiguity/_response.py | 24 +++++++++++++++++++++--- src/contiguity/email.py | 8 +++----- src/contiguity/otp.py | 15 +++++++-------- src/contiguity/text.py | 6 +++--- 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/contiguity/_response.py b/src/contiguity/_response.py index d90bf1b..d84847e 100644 --- a/src/contiguity/_response.py +++ b/src/contiguity/_response.py @@ -1,19 +1,37 @@ from http import HTTPStatus +from typing import Generic, TypeVar -from msgspec import Struct +import msgspec +T = TypeVar("T", bound=msgspec.Struct) -class ResponseMetadata(Struct): + +class ResponseMetadata(msgspec.Struct): id: str timestamp: int api_version: str object: str -class BaseResponse(Struct): +class RawResponse(ResponseMetadata, Generic[T]): + data: T + + +class BaseResponse(msgspec.Struct): metadata: ResponseMetadata class ErrorResponse(BaseResponse): error: str status: HTTPStatus + + +def decode_response(content: bytes, /, *, type: type[T]) -> T: + raw = msgspec.json.decode(content, type=RawResponse[type]) + metadata = ResponseMetadata( + id=raw.id, + timestamp=raw.timestamp, + api_version=raw.api_version, + object=raw.object, + ) + return msgspec.convert({**msgspec.to_builtins(raw.data), "metadata": metadata}, type=type) diff --git a/src/contiguity/email.py b/src/contiguity/email.py index 4110f2d..7b93dff 100644 --- a/src/contiguity/email.py +++ b/src/contiguity/email.py @@ -3,10 +3,8 @@ from http import HTTPStatus from typing import overload -import msgspec - from ._product import BaseProduct -from ._response import BaseResponse, ErrorResponse +from ._response import BaseResponse, ErrorResponse, decode_response class EmailResponse(BaseResponse): @@ -83,11 +81,11 @@ def email( # noqa: PLR0913 response = self._client.post("/send/email", json=email_payload) if response.status_code != HTTPStatus.OK: - data = msgspec.json.decode(response.content, type=ErrorResponse) + data = decode_response(response.content, type=ErrorResponse) msg = f"failed to send email. {response.status_code} {data.error}" raise ValueError(msg) - data = msgspec.json.decode(response.content, type=EmailResponse) + data = decode_response(response.content, type=EmailResponse) if self.debug: print(f"successfully sent email to {to}") diff --git a/src/contiguity/otp.py b/src/contiguity/otp.py index 1539fc4..24945e4 100644 --- a/src/contiguity/otp.py +++ b/src/contiguity/otp.py @@ -3,11 +3,10 @@ from enum import Enum from http import HTTPStatus -import msgspec import phonenumbers from ._product import BaseProduct -from ._response import BaseResponse, ErrorResponse +from ._response import BaseResponse, ErrorResponse, decode_response class OTPLanguage(str, Enum): @@ -82,11 +81,11 @@ def send( ) if response.status_code != HTTPStatus.OK: - data = msgspec.json.decode(response.content, type=ErrorResponse) + data = decode_response(response.content, type=ErrorResponse) msg = f"failed to send OTP. {response.status_code} {data.error}" raise ValueError(msg) - data = msgspec.json.decode(response.content, type=OTPSendResponse) + data = decode_response(response.content, type=OTPSendResponse) if self.debug: print(f"successfully sent OTP {data.otp_id} to {to}") @@ -101,11 +100,11 @@ def resend(self, otp_id: str, /) -> OTPResendResponse: ) if response.status_code != HTTPStatus.OK: - data = msgspec.json.decode(response.content, type=ErrorResponse) + data = decode_response(response.content, type=ErrorResponse) msg = f"failed to resend OTP. {response.status_code} {data.error}" raise ValueError(msg) - data = msgspec.json.decode(response.content, type=OTPResendResponse) + data = decode_response(response.content, type=OTPResendResponse) if self.debug: print(f"successfully resent OTP {otp_id} with status: {data.resent}") @@ -121,11 +120,11 @@ def verify(self, otp: int | str, /, *, otp_id: str) -> OTPVerifyResponse: ) if response.status_code != HTTPStatus.OK: - data = msgspec.json.decode(response.content, type=ErrorResponse) + data = decode_response(response.content, type=ErrorResponse) msg = f"failed to verify OTP. {response.status_code} {data.error}" raise ValueError(msg) - data = msgspec.json.decode(response.content, type=OTPVerifyResponse) + data = decode_response(response.content, type=OTPVerifyResponse) if self.debug: print(f"successfully verified OTP ({otp}) with status: {data.verified}") diff --git a/src/contiguity/text.py b/src/contiguity/text.py index c201316..b5ec403 100644 --- a/src/contiguity/text.py +++ b/src/contiguity/text.py @@ -1,10 +1,9 @@ from http import HTTPStatus -import msgspec import phonenumbers from ._product import BaseProduct -from ._response import BaseResponse, ErrorResponse +from ._response import BaseResponse, ErrorResponse, decode_response class TextResponse(BaseResponse): @@ -41,10 +40,11 @@ def send(self, *, to: str, message: str) -> TextResponse: ) if response.status_code != HTTPStatus.OK: + data = decode_response(response.content, type=ErrorResponse) msg = f"failed to send message. {response.status_code} {data.error}" raise ValueError(msg) - data = msgspec.json.decode(response.content, type=TextResponse) + data = decode_response(response.content, type=TextResponse) if self.debug: print(f"successfully sent text to {to}") From 035c3c8733dbd37d8216aedc3bac8d06f9767cc8 Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Thu, 21 Aug 2025 01:38:39 -0700 Subject: [PATCH 07/38] Use logger instead of print --- README.md | 6 ------ src/contiguity/__init__.py | 15 +++++++++------ src/contiguity/_instant_messaging.py | 12 +++--------- src/contiguity/_product.py | 3 +-- src/contiguity/email.py | 7 ++++--- src/contiguity/otp.py | 15 ++++++--------- src/contiguity/text.py | 7 ++++--- 7 files changed, 27 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 1e8cfdb..d9c8cd4 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,6 @@ import contiguity client = contiguity.login("your_token_here") ``` -You can also initialize it with the optional 'debug' flag: - -```js -client = contiguity.login("your_token_here", True) -``` - You can get your token from the Contiguity [dashboard](https://contiguity.co/dashboard). ## Sending your first email 📤 diff --git a/src/contiguity/__init__.py b/src/contiguity/__init__.py index 306a6c9..fe18f57 100644 --- a/src/contiguity/__init__.py +++ b/src/contiguity/__init__.py @@ -1,7 +1,9 @@ from ._client import ApiClient from .email import Email +from .imessage import IMessage from .otp import OTP from .text import Text +from .whatsapp import WhatsApp class Contiguity: @@ -10,7 +12,6 @@ class Contiguity: Args: token (str): The authentication token. - debug (bool, optional): A flag indicating whether to enable debug mode. Default is False. """ def __init__( @@ -18,25 +19,27 @@ def __init__( *, token: str, base_url: str = "https://api.contiguity.com", - debug: bool = False, ) -> None: if not token: msg = "token cannot be empty" raise ValueError(msg) self.token = token self.base_url = base_url - self.debug = debug self.client = ApiClient(base_url=self.base_url, api_key=token.strip()) - self.text = Text(client=self.client, debug=self.debug) - self.email = Email(client=self.client, debug=self.debug) - self.otp = OTP(client=self.client, debug=self.debug) + self.text = Text(client=self.client) + self.email = Email(client=self.client) + self.otp = OTP(client=self.client) + self.imessage = IMessage(client=self.client) + self.whatsapp = WhatsApp(client=self.client) __all__ = ( "OTP", "Contiguity", "Email", + "IMessage", "Text", + "WhatsApp", ) __version__ = "3.0.0" diff --git a/src/contiguity/_instant_messaging.py b/src/contiguity/_instant_messaging.py index 3452271..c6b604f 100644 --- a/src/contiguity/_instant_messaging.py +++ b/src/contiguity/_instant_messaging.py @@ -63,9 +63,7 @@ def send( # noqa: PLR0913 raise ValueError(msg) data = decode_response(response.content, type=IMSendResponse) - if self.debug: - logger.debug(f"successfully sent {self._api_path[1:]} message to {to}") - + logger.debug("successfully sent %s message to %r", self._api_path[1:], to) return data def _typing(self, *, to: str, action: Literal["start", "stop"], from_: str | None = None) -> IMTypingResponse: @@ -86,9 +84,7 @@ def _typing(self, *, to: str, action: Literal["start", "stop"], from_: str | Non raise ValueError(msg) data = decode_response(response.content, type=IMTypingResponse) - if self.debug: - logger.debug(f"successfully {action} {self._api_path[1:]} typing indicator for {to}") - + logger.debug("successfully %s %s typing indicator for %r", action, self._api_path[1:], to) return data def start_typing(self, *, to: str, from_: str | None = None) -> IMTypingResponse: @@ -123,9 +119,7 @@ def _reactions( raise ValueError(msg) data = decode_response(response.content, type=IMReactionResponse) - if self.debug: - logger.debug(f"successfully {action} {self._api_path[1:]} reaction for {to}") - + logger.debug("successfully %s %s reaction for %r", action, self._api_path[1:], to) return data def add_reaction(self, *, to: str, reaction: str, message: str) -> IMReactionResponse: diff --git a/src/contiguity/_product.py b/src/contiguity/_product.py index beb30a6..d043b3a 100644 --- a/src/contiguity/_product.py +++ b/src/contiguity/_product.py @@ -2,6 +2,5 @@ class BaseProduct: - def __init__(self, *, client: ApiClient, debug: bool = False) -> None: + def __init__(self, *, client: ApiClient) -> None: self._client = client - self.debug = debug diff --git a/src/contiguity/email.py b/src/contiguity/email.py index 7b93dff..5b284d2 100644 --- a/src/contiguity/email.py +++ b/src/contiguity/email.py @@ -1,11 +1,14 @@ from __future__ import annotations +import logging from http import HTTPStatus from typing import overload from ._product import BaseProduct from ._response import BaseResponse, ErrorResponse, decode_response +logger = logging.getLogger(__name__) + class EmailResponse(BaseResponse): message: str @@ -86,7 +89,5 @@ def email( # noqa: PLR0913 raise ValueError(msg) data = decode_response(response.content, type=EmailResponse) - if self.debug: - print(f"successfully sent email to {to}") - + logger.debug("successfully sent email to %r", to) return data diff --git a/src/contiguity/otp.py b/src/contiguity/otp.py index 24945e4..d8937b2 100644 --- a/src/contiguity/otp.py +++ b/src/contiguity/otp.py @@ -1,5 +1,6 @@ from __future__ import annotations +import logging from enum import Enum from http import HTTPStatus @@ -8,6 +9,8 @@ from ._product import BaseProduct from ._response import BaseResponse, ErrorResponse, decode_response +logger = logging.getLogger(__name__) + class OTPLanguage(str, Enum): ENGLISH = "en" @@ -86,9 +89,7 @@ def send( raise ValueError(msg) data = decode_response(response.content, type=OTPSendResponse) - if self.debug: - print(f"successfully sent OTP {data.otp_id} to {to}") - + logger.debug("successfully sent OTP %r to %r", data.otp_id, to) return data def resend(self, otp_id: str, /) -> OTPResendResponse: @@ -105,9 +106,7 @@ def resend(self, otp_id: str, /) -> OTPResendResponse: raise ValueError(msg) data = decode_response(response.content, type=OTPResendResponse) - if self.debug: - print(f"successfully resent OTP {otp_id} with status: {data.resent}") - + logger.debug("successfully resent OTP %r with status: %r", otp_id, data.resent) return data def verify(self, otp: int | str, /, *, otp_id: str) -> OTPVerifyResponse: @@ -125,7 +124,5 @@ def verify(self, otp: int | str, /, *, otp_id: str) -> OTPVerifyResponse: raise ValueError(msg) data = decode_response(response.content, type=OTPVerifyResponse) - if self.debug: - print(f"successfully verified OTP ({otp}) with status: {data.verified}") - + logger.debug("successfully verified OTP %r with status: %r", otp_id, data.verified) return data diff --git a/src/contiguity/text.py b/src/contiguity/text.py index b5ec403..6a38ca5 100644 --- a/src/contiguity/text.py +++ b/src/contiguity/text.py @@ -1,3 +1,4 @@ +import logging from http import HTTPStatus import phonenumbers @@ -5,6 +6,8 @@ from ._product import BaseProduct from ._response import BaseResponse, ErrorResponse, decode_response +logger = logging.getLogger(__name__) + class TextResponse(BaseResponse): message_id: str @@ -45,7 +48,5 @@ def send(self, *, to: str, message: str) -> TextResponse: raise ValueError(msg) data = decode_response(response.content, type=TextResponse) - if self.debug: - print(f"successfully sent text to {to}") - + logger.debug("successfully sent text to %r", to) return data From fd84c1e6114d1b29258e55f5895c02eec8c58fc3 Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Thu, 21 Aug 2025 01:39:32 -0700 Subject: [PATCH 08/38] Fix filename typo --- src/contiguity/{indentity.py => identity.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/contiguity/{indentity.py => identity.py} (100%) diff --git a/src/contiguity/indentity.py b/src/contiguity/identity.py similarity index 100% rename from src/contiguity/indentity.py rename to src/contiguity/identity.py From b97ac5992fe69224e8d25d7a58c2177e13e031ef Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Sat, 23 Aug 2025 00:43:34 -0700 Subject: [PATCH 09/38] Add domains impl --- src/contiguity/domains.py | 103 ++++++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 20 deletions(-) diff --git a/src/contiguity/domains.py b/src/contiguity/domains.py index 0e59c2f..ddcea51 100644 --- a/src/contiguity/domains.py +++ b/src/contiguity/domains.py @@ -1,38 +1,101 @@ +import logging +from collections.abc import Sequence +from http import HTTPStatus + import msgspec -from contiguity._response import BaseResponse, ErrorResponse + +from ._product import BaseProduct +from ._response import BaseResponse, ErrorResponse, decode_response + +logger = logging.getLogger(__name__) + + +class PartialDomain(msgspec.Struct): + domain: str + status: str + id: str + created_at: int + region: str + sending_allowed: bool class DnsRecord(msgspec.Struct): - ... + type: str + name: str + value: str + purpose: str class DomainVerifications(msgspec.Struct): - # TODO: change to bools? dkim: str mail_from: str domain: str -class Domain(msgspec.Struct): - domain: str - status: str - id: str - created_at: int - records: list[DnsRecord] - region: str - sending_allowed: bool +class Domain(PartialDomain): + records: Sequence[DnsRecord] verifications: DomainVerifications -class Domains: - def register(self, domain: str, /, region: str = "us-east-1", custom_return_path: str = "contiguity"): - raise NotImplementedError +class DeleteDomainResponse(BaseResponse): + success: bool + message: str + + +class Domains(BaseProduct): + def register( + self, + domain: str, + /, + *, + region: str = "us-east-1", + custom_return_path: str = "contiguity", + ) -> PartialDomain: + response = self._client.post( + f"/domains/{domain}", + json={ + "region": region, + "custom_return_path": custom_return_path, + }, + ) + + if response.status_code != HTTPStatus.OK: + data = decode_response(response.content, type=ErrorResponse) + msg = f"failed to register domain. {response.status_code} {data.error}" + raise ValueError(msg) + + data = decode_response(response.content, type=PartialDomain) + logger.debug("successfully registered domain %r", domain) + return data + + def list(self) -> list[PartialDomain]: + response = self._client.get("/domains") + + if response.status_code != HTTPStatus.OK: + data = decode_response(response.content, type=ErrorResponse) + msg = f"failed to list domains. {response.status_code} {data.error}" + raise ValueError(msg) + + return decode_response(response.content, type=list[PartialDomain]) + + def get(self, domain: str, /) -> Domain: + response = self._client.get(f"/domains/{domain}") + + if response.status_code != HTTPStatus.OK: + data = decode_response(response.content, type=ErrorResponse) + msg = f"failed to get domain. {response.status_code} {data.error}" + raise ValueError(msg) + + return decode_response(response.content, type=Domain) - def list(self): - raise NotImplementedError + def delete(self, domain: str, /) -> DeleteDomainResponse: + response = self._client.delete(f"/domains/{domain}") - def get(self, domain: str, /): - raise NotImplementedError + if response.status_code != HTTPStatus.OK: + data = decode_response(response.content, type=ErrorResponse) + msg = f"failed to delete domain. {response.status_code} {data.error}" + raise ValueError(msg) - def delete(self, domain: str, /): - raise NotImplementedError + data = decode_response(response.content, type=DeleteDomainResponse) + logger.debug("successfully deleted domain %r", domain) + return data From dd54232c174c0f7dfa3320038b4918a94a17643e Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Sat, 23 Aug 2025 00:56:15 -0700 Subject: [PATCH 10/38] re-use api error handling --- src/contiguity/_client.py | 18 ++++++++-- src/contiguity/_instant_messaging.py | 21 +++--------- src/contiguity/_response.py | 5 +-- src/contiguity/domains.py | 30 +++------------- src/contiguity/email.py | 51 ++++++++++++++++------------ src/contiguity/otp.py | 21 +++--------- src/contiguity/text.py | 9 ++--- 7 files changed, 62 insertions(+), 93 deletions(-) diff --git a/src/contiguity/_client.py b/src/contiguity/_client.py index a511861..229c468 100644 --- a/src/contiguity/_client.py +++ b/src/contiguity/_client.py @@ -1,16 +1,28 @@ from __future__ import annotations +from http import HTTPStatus + from httpx import AsyncClient as HttpxAsyncClient from httpx import Client as HttpxClient +from httpx import Response from ._auth import get_contiguity_token +from ._response import ErrorResponse, decode_response -class ApiError(Exception): +class ContiguityApiError(Exception): pass -class ApiClient(HttpxClient): +class BaseApiClient: + def handle_error(self, response: Response, /, *, fail_message: str = "api request failed") -> None: + if not HTTPStatus.OK <= response.status_code <= HTTPStatus.IM_USED: + data = decode_response(response.content, type=ErrorResponse) + msg = f"{fail_message}. {response.status_code} {data.error}" + raise ContiguityApiError(msg) + + +class ApiClient(HttpxClient, BaseApiClient): def __init__( self: ApiClient, *, @@ -30,7 +42,7 @@ def __init__( ) -class AsyncApiClient(HttpxAsyncClient): +class AsyncApiClient(HttpxAsyncClient, BaseApiClient): def __init__( self: AsyncApiClient, *, diff --git a/src/contiguity/_instant_messaging.py b/src/contiguity/_instant_messaging.py index c6b604f..5bd7c64 100644 --- a/src/contiguity/_instant_messaging.py +++ b/src/contiguity/_instant_messaging.py @@ -2,11 +2,10 @@ import logging from abc import ABC, abstractmethod -from http import HTTPStatus from typing import TYPE_CHECKING, Generic, Literal, TypeVar from ._product import BaseProduct -from ._response import BaseResponse, ErrorResponse, decode_response +from ._response import BaseResponse, decode_response if TYPE_CHECKING: from collections.abc import Sequence @@ -57,11 +56,7 @@ def send( # noqa: PLR0913 json={k: v for k, v in payload.items() if v is not None}, ) - if response.status_code != HTTPStatus.OK: - data = decode_response(response.content, type=ErrorResponse) - msg = f"failed to send instant message. {response.status_code} {data.error}" - raise ValueError(msg) - + self._client.handle_error(response, fail_message="failed to send instant message") data = decode_response(response.content, type=IMSendResponse) logger.debug("successfully sent %s message to %r", self._api_path[1:], to) return data @@ -78,11 +73,7 @@ def _typing(self, *, to: str, action: Literal["start", "stop"], from_: str | Non json={k: v for k, v in payload.items() if v is not None}, ) - if response.status_code != HTTPStatus.OK: - data = decode_response(response.content, type=ErrorResponse) - msg = f"failed to {action} {self._api_path[1:]} typing indicator. {response.status_code} {data.error}" - raise ValueError(msg) - + self._client.handle_error(response, fail_message=f"failed to {action} {self._api_path[1:]} typing indicator") data = decode_response(response.content, type=IMTypingResponse) logger.debug("successfully %s %s typing indicator for %r", action, self._api_path[1:], to) return data @@ -113,11 +104,7 @@ def _reactions( json={k: v for k, v in payload.items() if v is not None}, ) - if response.status_code != HTTPStatus.OK: - data = decode_response(response.content, type=ErrorResponse) - msg = f"failed to {action} {self._api_path[1:]} reaction. {response.status_code} {data.error}" - raise ValueError(msg) - + self._client.handle_error(response, fail_message=f"failed to {action} {self._api_path[1:]} reaction") data = decode_response(response.content, type=IMReactionResponse) logger.debug("successfully %s %s reaction for %r", action, self._api_path[1:], to) return data diff --git a/src/contiguity/_response.py b/src/contiguity/_response.py index d84847e..30d82a5 100644 --- a/src/contiguity/_response.py +++ b/src/contiguity/_response.py @@ -1,9 +1,10 @@ +from collections.abc import Sequence from http import HTTPStatus -from typing import Generic, TypeVar +from typing import Generic, TypeVar, Union import msgspec -T = TypeVar("T", bound=msgspec.Struct) +T = TypeVar("T", bound=Union[msgspec.Struct, Sequence[msgspec.Struct]]) class ResponseMetadata(msgspec.Struct): diff --git a/src/contiguity/domains.py b/src/contiguity/domains.py index ddcea51..c403559 100644 --- a/src/contiguity/domains.py +++ b/src/contiguity/domains.py @@ -1,11 +1,10 @@ import logging from collections.abc import Sequence -from http import HTTPStatus import msgspec from ._product import BaseProduct -from ._response import BaseResponse, ErrorResponse, decode_response +from ._response import BaseResponse, decode_response logger = logging.getLogger(__name__) @@ -59,43 +58,24 @@ def register( }, ) - if response.status_code != HTTPStatus.OK: - data = decode_response(response.content, type=ErrorResponse) - msg = f"failed to register domain. {response.status_code} {data.error}" - raise ValueError(msg) - + self._client.handle_error(response, fail_message="failed to register domain") data = decode_response(response.content, type=PartialDomain) logger.debug("successfully registered domain %r", domain) return data def list(self) -> list[PartialDomain]: response = self._client.get("/domains") - - if response.status_code != HTTPStatus.OK: - data = decode_response(response.content, type=ErrorResponse) - msg = f"failed to list domains. {response.status_code} {data.error}" - raise ValueError(msg) - + self._client.handle_error(response, fail_message="failed to list domains") return decode_response(response.content, type=list[PartialDomain]) def get(self, domain: str, /) -> Domain: response = self._client.get(f"/domains/{domain}") - - if response.status_code != HTTPStatus.OK: - data = decode_response(response.content, type=ErrorResponse) - msg = f"failed to get domain. {response.status_code} {data.error}" - raise ValueError(msg) - + self._client.handle_error(response, fail_message="failed to get domain") return decode_response(response.content, type=Domain) def delete(self, domain: str, /) -> DeleteDomainResponse: response = self._client.delete(f"/domains/{domain}") - - if response.status_code != HTTPStatus.OK: - data = decode_response(response.content, type=ErrorResponse) - msg = f"failed to delete domain. {response.status_code} {data.error}" - raise ValueError(msg) - + self._client.handle_error(response, fail_message="failed to delete domain") data = decode_response(response.content, type=DeleteDomainResponse) logger.debug("successfully deleted domain %r", domain) return data diff --git a/src/contiguity/email.py b/src/contiguity/email.py index 5b284d2..8d9ec25 100644 --- a/src/contiguity/email.py +++ b/src/contiguity/email.py @@ -1,17 +1,19 @@ from __future__ import annotations import logging -from http import HTTPStatus -from typing import overload +from typing import TYPE_CHECKING, overload from ._product import BaseProduct -from ._response import BaseResponse, ErrorResponse, decode_response +from ._response import BaseResponse, decode_response + +if TYPE_CHECKING: + from collections.abc import Mapping, Sequence logger = logging.getLogger(__name__) class EmailResponse(BaseResponse): - message: str + email_id: str class Email(BaseProduct): @@ -45,10 +47,12 @@ def email( # noqa: PLR0913 to: str, from_: str, subject: str, - reply_to: str = "", - cc: str = "", - text: str | None = None, - html: str | None = None, + body_text: str | None = None, + body_html: str | None = None, + reply_to: str | None = None, + cc: str | Sequence[str] | None = None, + bcc: str | Sequence[str] | None = None, + headers: Mapping[str, str] | None = None, ) -> EmailResponse: """ Send an email. @@ -67,27 +71,30 @@ def email( # noqa: PLR0913 Raises: ValueError: Raises an error if required fields are missing or sending the email fails. """ + if not body_text and not body_html: + msg = "either text or html body must be provided" + raise ValueError(msg) + email_payload = { "to": to, "from": from_, "subject": subject, - "body": html or text, - "contentType": "html" if html else "text", + "body": { + "text": body_text, + "html": body_html, + }, + "reply_to": reply_to, + "cc": cc, + "bcc": bcc, + "headers": headers, } - if reply_to: - email_payload["replyTo"] = reply_to - - if cc: - email_payload["cc"] = cc - - response = self._client.post("/send/email", json=email_payload) - - if response.status_code != HTTPStatus.OK: - data = decode_response(response.content, type=ErrorResponse) - msg = f"failed to send email. {response.status_code} {data.error}" - raise ValueError(msg) + response = self._client.post( + "/send/email", + json={k: v for k, v in email_payload.items() if v}, + ) + self._client.handle_error(response, fail_message="failed to send email") data = decode_response(response.content, type=EmailResponse) logger.debug("successfully sent email to %r", to) return data diff --git a/src/contiguity/otp.py b/src/contiguity/otp.py index d8937b2..6afac3b 100644 --- a/src/contiguity/otp.py +++ b/src/contiguity/otp.py @@ -2,12 +2,11 @@ import logging from enum import Enum -from http import HTTPStatus import phonenumbers from ._product import BaseProduct -from ._response import BaseResponse, ErrorResponse, decode_response +from ._response import BaseResponse, decode_response logger = logging.getLogger(__name__) @@ -83,11 +82,7 @@ def send( }, ) - if response.status_code != HTTPStatus.OK: - data = decode_response(response.content, type=ErrorResponse) - msg = f"failed to send OTP. {response.status_code} {data.error}" - raise ValueError(msg) - + self._client.handle_error(response, fail_message="failed to send OTP") data = decode_response(response.content, type=OTPSendResponse) logger.debug("successfully sent OTP %r to %r", data.otp_id, to) return data @@ -100,11 +95,7 @@ def resend(self, otp_id: str, /) -> OTPResendResponse: }, ) - if response.status_code != HTTPStatus.OK: - data = decode_response(response.content, type=ErrorResponse) - msg = f"failed to resend OTP. {response.status_code} {data.error}" - raise ValueError(msg) - + self._client.handle_error(response, fail_message="failed to resend OTP") data = decode_response(response.content, type=OTPResendResponse) logger.debug("successfully resent OTP %r with status: %r", otp_id, data.resent) return data @@ -118,11 +109,7 @@ def verify(self, otp: int | str, /, *, otp_id: str) -> OTPVerifyResponse: }, ) - if response.status_code != HTTPStatus.OK: - data = decode_response(response.content, type=ErrorResponse) - msg = f"failed to verify OTP. {response.status_code} {data.error}" - raise ValueError(msg) - + self._client.handle_error(response, fail_message="failed to verify OTP") data = decode_response(response.content, type=OTPVerifyResponse) logger.debug("successfully verified OTP %r with status: %r", otp_id, data.verified) return data diff --git a/src/contiguity/text.py b/src/contiguity/text.py index 6a38ca5..c6747de 100644 --- a/src/contiguity/text.py +++ b/src/contiguity/text.py @@ -1,10 +1,9 @@ import logging -from http import HTTPStatus import phonenumbers from ._product import BaseProduct -from ._response import BaseResponse, ErrorResponse, decode_response +from ._response import BaseResponse, decode_response logger = logging.getLogger(__name__) @@ -42,11 +41,7 @@ def send(self, *, to: str, message: str) -> TextResponse: }, ) - if response.status_code != HTTPStatus.OK: - data = decode_response(response.content, type=ErrorResponse) - msg = f"failed to send message. {response.status_code} {data.error}" - raise ValueError(msg) - + self._client.handle_error(response, fail_message="failed to send text message") data = decode_response(response.content, type=TextResponse) logger.debug("successfully sent text to %r", to) return data From af40d810d7c0129e98a0847dbe1cc03e5c08ac9f Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Sat, 23 Aug 2025 00:58:56 -0700 Subject: [PATCH 11/38] update email overloads --- src/contiguity/email.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/contiguity/email.py b/src/contiguity/email.py index 8d9ec25..4f1d0ea 100644 --- a/src/contiguity/email.py +++ b/src/contiguity/email.py @@ -24,9 +24,11 @@ def email( to: str, from_: str, subject: str, - text: str, - reply_to: str = "", - cc: str = "", + body_text: str, + reply_to: str | None = None, + cc: str | Sequence[str] | None = None, + bcc: str | Sequence[str] | None = None, + headers: Mapping[str, str] | None = None, ) -> EmailResponse: ... @overload @@ -36,9 +38,11 @@ def email( to: str, from_: str, subject: str, - html: str, - reply_to: str = "", - cc: str = "", + body_html: str, + reply_to: str | None = None, + cc: str | Sequence[str] | None = None, + bcc: str | Sequence[str] | None = None, + headers: Mapping[str, str] | None = None, ) -> EmailResponse: ... def email( # noqa: PLR0913 From 0af2417fc84197ad502dcfc481790c7e7db616db Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Thu, 18 Sep 2025 22:25:01 +0200 Subject: [PATCH 12/38] Forgot these changes --- src/contiguity/base/async_base.py | 10 +++++----- src/contiguity/base/base.py | 10 +++++----- src/contiguity/base/exceptions.py | 6 +++--- src/contiguity/domains.py | 8 ++++---- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/contiguity/base/async_base.py b/src/contiguity/base/async_base.py index 39e80c5..ee62c3b 100644 --- a/src/contiguity/base/async_base.py +++ b/src/contiguity/base/async_base.py @@ -13,7 +13,7 @@ from typing_extensions import deprecated from contiguity._auth import get_data_key, get_project_id -from contiguity._client import ApiError, AsyncApiClient +from contiguity._client import ContiguityApiError, AsyncApiClient from .common import ( UNSET, @@ -127,7 +127,7 @@ def _response_as_item_type( try: response.raise_for_status() except HTTPStatusError as exc: - raise ApiError(exc.response.text) from exc + raise ContiguityApiError(exc.response.text) from exc if self.item_type: return msgspec.json.decode(response.content, type=Sequence[self.item_type] if sequence else self.item_type) return response.json(cls=self.json_decoder) @@ -194,7 +194,7 @@ async def delete(self: Self, key: str, /) -> None: try: response.raise_for_status() except HTTPStatusError as exc: - raise ApiError(exc.response.text) from exc + raise ContiguityApiError(exc.response.text) from exc async def insert( self: Self, @@ -212,7 +212,7 @@ async def insert( if not (returned_item := self._response_as_item_type(response, sequence=True)): msg = "expected a single item, got an empty response" - raise ApiError(msg) + raise ContiguityApiError(msg) return returned_item[0] async def put( @@ -299,7 +299,7 @@ async def query( try: response.raise_for_status() except HTTPStatusError as exc: - raise ApiError(exc.response.text) from exc + raise ContiguityApiError(exc.response.text) from exc return msgspec.json.decode(response.content, type=QueryResponse[ItemT]) # query_response = QueryResponse[ItemT].model_validate_json(response.content) diff --git a/src/contiguity/base/base.py b/src/contiguity/base/base.py index 490e11f..913547a 100644 --- a/src/contiguity/base/base.py +++ b/src/contiguity/base/base.py @@ -22,7 +22,7 @@ from typing_extensions import deprecated from contiguity._auth import get_data_key, get_project_id -from contiguity._client import ApiClient, ApiError +from contiguity._client import ApiClient, ContiguityApiError from .common import ( UNSET, @@ -136,7 +136,7 @@ def _response_as_item_type( try: response.raise_for_status() except HTTPStatusError as exc: - raise ApiError(exc.response.text) from exc + raise ContiguityApiError(exc.response.text) from exc if self.item_type: return msgspec.json.decode(response.content, type=Sequence[self.item_type] if sequence else self.item_type) return response.json(cls=self.json_decoder) @@ -203,7 +203,7 @@ def delete(self: Self, key: str, /) -> None: try: response.raise_for_status() except HTTPStatusError as exc: - raise ApiError(exc.response.text) from exc + raise ContiguityApiError(exc.response.text) from exc def insert( self: Self, @@ -221,7 +221,7 @@ def insert( if not (returned_item := self._response_as_item_type(response, sequence=True)): msg = "expected a single item, got an empty response" - raise ApiError(msg) + raise ContiguityApiError(msg) return returned_item[0] def put( @@ -308,7 +308,7 @@ def query( try: response.raise_for_status() except HTTPStatusError as exc: - raise ApiError(exc.response.text) from exc + raise ContiguityApiError(exc.response.text) from exc return msgspec.json.decode(response.content, type=QueryResponse[ItemT]) # query_response = QueryResponse[ItemT].model_validate_json(response.content) diff --git a/src/contiguity/base/exceptions.py b/src/contiguity/base/exceptions.py index 9e6cc86..2e21e14 100644 --- a/src/contiguity/base/exceptions.py +++ b/src/contiguity/base/exceptions.py @@ -1,12 +1,12 @@ -from contiguity._client import ApiError +from contiguity._client import ContiguityApiError -class ItemConflictError(ApiError): +class ItemConflictError(ContiguityApiError): def __init__(self, key: str, *args: object) -> None: super().__init__(f"item with key '{key}' already exists", *args) -class ItemNotFoundError(ApiError): +class ItemNotFoundError(ContiguityApiError): def __init__(self, key: str, *args: object) -> None: super().__init__(f"key '{key}' not found", *args) diff --git a/src/contiguity/domains.py b/src/contiguity/domains.py index c403559..75f7624 100644 --- a/src/contiguity/domains.py +++ b/src/contiguity/domains.py @@ -1,7 +1,7 @@ import logging from collections.abc import Sequence -import msgspec +from msgspec import Struct from ._product import BaseProduct from ._response import BaseResponse, decode_response @@ -9,7 +9,7 @@ logger = logging.getLogger(__name__) -class PartialDomain(msgspec.Struct): +class PartialDomain(Struct): domain: str status: str id: str @@ -18,14 +18,14 @@ class PartialDomain(msgspec.Struct): sending_allowed: bool -class DnsRecord(msgspec.Struct): +class DnsRecord(Struct): type: str name: str value: str purpose: str -class DomainVerifications(msgspec.Struct): +class DomainVerifications(Struct): dkim: str mail_from: str domain: str From 7312269bcc414043602165a86bde2f631e8786c1 Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Thu, 18 Sep 2025 23:41:24 +0200 Subject: [PATCH 13/38] Add lease impl --- src/contiguity/lease.py | 117 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/src/contiguity/lease.py b/src/contiguity/lease.py index e69de29..4f3be25 100644 --- a/src/contiguity/lease.py +++ b/src/contiguity/lease.py @@ -0,0 +1,117 @@ +from __future__ import annotations + +from typing import Literal + +from msgspec import Struct + +from ._product import BaseProduct +from ._response import decode_response + +Carrier = Literal["T-Mobile", "AT&T", "Verizon", "Twilio", "Contiguity", "International Partner"] +LeaseStatus = Literal["active", "expired", "terminated"] + + +class NumberFormats(Struct): + e164: str + formatted: str + + +class NumberLocation(Struct): + country: str + region: str + city: str + + +class NumberCapabilities(Struct): + intl_sms: bool + channels: list[Literal["sms", "mms", "rcs", "imessage", "whatsapp"]] + + +class NumberHealth(Struct): + reputation: float + previous_owners: int + + +class AdditionalNumberData(Struct): + requirements: list[ + Literal["imessage_entitlement_required", "whatsapp_entitlement_required", "enterprise_plan_required"] + ] + e911_capable: bool + + +class NumberPricing(Struct): + currency: str + upfront_fee: float + monthly_rate: float + + +class NumberBillingPeriod(Struct): + start: int + end: int | None + + +class NumberBilling(Struct): + method: Literal["monthly", "service_contract", "goodwill"] + period: NumberBillingPeriod + + +class NumberDetails(Struct): + id: str + """Phone number in E.164 format""" + status: Literal["available", "g-available", "leased", "unavailable"] + number: NumberFormats + location: NumberLocation + carrier: Carrier + capabilities: NumberCapabilities + health: NumberHealth + data: AdditionalNumberData + created_at: int + pricing: NumberPricing + lease_id: str | None + lease_status: LeaseStatus | None + billing: NumberBilling | None + + +class TerminateLeaseResponse(Struct): + lease_id: str + number_id: str + status: LeaseStatus + terminated_at: int + + +class Lease(BaseProduct): + def get_available_numbers(self) -> list[NumberDetails]: + response = self._client.get("/leases") + self._client.handle_error(response, fail_message="failed to get available numbers") + return decode_response(response.content, type=list[NumberDetails]) + + def get_leased_numbers(self) -> list[NumberDetails]: + response = self._client.get("/leased") + self._client.handle_error(response, fail_message="failed to get leased numbers") + return decode_response(response.content, type=list[NumberDetails]) + + def get_number_details(self, number: str, /) -> NumberDetails: + response = self._client.get(f"/lease/{number}") + self._client.handle_error(response, fail_message="failed to get number details") + return decode_response(response.content, type=NumberDetails) + + def lease_number( + self, + number: NumberDetails | str, + /, + *, + billing_method: Literal["monthly", "service_contract"], + ) -> NumberDetails: + number = number.id if isinstance(number, NumberDetails) else number + response = self._client.post( + f"/lease/{number}", + json={"billing_method": billing_method}, + ) + self._client.handle_error(response, fail_message="failed to lease number") + return decode_response(response.content, type=NumberDetails) + + def terminate_lease(self, number: NumberDetails | str, /) -> TerminateLeaseResponse: + number = number.id if isinstance(number, NumberDetails) else number + response = self._client.delete(f"/leased/{number}") + self._client.handle_error(response, fail_message="failed to terminate lease") + return decode_response(response.content, type=TerminateLeaseResponse) From 3b27446ab1ed6330498154b57ecca053061048a8 Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Thu, 18 Sep 2025 23:45:02 +0200 Subject: [PATCH 14/38] Add leases and domains to contiguity class --- src/contiguity/__init__.py | 6 ++++++ src/contiguity/{lease.py => leases.py} | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) rename src/contiguity/{lease.py => leases.py} (99%) diff --git a/src/contiguity/__init__.py b/src/contiguity/__init__.py index fe18f57..afee28e 100644 --- a/src/contiguity/__init__.py +++ b/src/contiguity/__init__.py @@ -1,6 +1,8 @@ from ._client import ApiClient +from .domains import Domains from .email import Email from .imessage import IMessage +from .leases import Leases from .otp import OTP from .text import Text from .whatsapp import WhatsApp @@ -32,13 +34,17 @@ def __init__( self.otp = OTP(client=self.client) self.imessage = IMessage(client=self.client) self.whatsapp = WhatsApp(client=self.client) + self.leases = Leases(client=self.client) + self.domains = Domains(client=self.client) __all__ = ( "OTP", "Contiguity", + "Domains", "Email", "IMessage", + "Leases", "Text", "WhatsApp", ) diff --git a/src/contiguity/lease.py b/src/contiguity/leases.py similarity index 99% rename from src/contiguity/lease.py rename to src/contiguity/leases.py index 4f3be25..282e983 100644 --- a/src/contiguity/lease.py +++ b/src/contiguity/leases.py @@ -79,7 +79,7 @@ class TerminateLeaseResponse(Struct): terminated_at: int -class Lease(BaseProduct): +class Leases(BaseProduct): def get_available_numbers(self) -> list[NumberDetails]: response = self._client.get("/leases") self._client.handle_error(response, fail_message="failed to get available numbers") From 9041278301e76c7cf6f8b77e996b40e268c65311 Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Thu, 18 Sep 2025 23:47:13 +0200 Subject: [PATCH 15/38] fix lint --- src/contiguity/base/async_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contiguity/base/async_base.py b/src/contiguity/base/async_base.py index ee62c3b..15a359d 100644 --- a/src/contiguity/base/async_base.py +++ b/src/contiguity/base/async_base.py @@ -13,7 +13,7 @@ from typing_extensions import deprecated from contiguity._auth import get_data_key, get_project_id -from contiguity._client import ContiguityApiError, AsyncApiClient +from contiguity._client import AsyncApiClient, ContiguityApiError from .common import ( UNSET, From 9465bc2b845a07dd14a4dbbabc0e64339fbb1d77 Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Fri, 19 Sep 2025 00:09:09 +0200 Subject: [PATCH 16/38] Update base examples and tests for msgspec --- examples/base/basic_usage.py | 3 +-- .../{pydantic_usage.py => msgspec_usage.py} | 7 +++---- tests/__init__.py | 4 ++++ tests/base/test_async_base_dict_typed.py | 16 +++++++-------- tests/base/test_async_base_dict_untyped.py | 14 ++++++------- tests/base/test_async_base_model.py | 20 +++++++++---------- tests/base/test_async_base_typeddict.py | 10 +++++----- tests/base/test_base_dict_typed.py | 16 +++++++-------- tests/base/test_base_dict_untyped.py | 14 ++++++------- tests/base/test_base_model.py | 20 +++++++++---------- tests/base/test_base_typeddict.py | 10 +++++----- 11 files changed, 68 insertions(+), 66 deletions(-) rename examples/base/{pydantic_usage.py => msgspec_usage.py} (93%) diff --git a/examples/base/basic_usage.py b/examples/base/basic_usage.py index c673c22..a840112 100644 --- a/examples/base/basic_usage.py +++ b/examples/base/basic_usage.py @@ -1,5 +1,4 @@ -# ruff: noqa: T201 -from contiguity import Base +from contiguity.base import Base # Create a Base instance. db = Base("my-base") diff --git a/examples/base/pydantic_usage.py b/examples/base/msgspec_usage.py similarity index 93% rename from examples/base/pydantic_usage.py rename to examples/base/msgspec_usage.py index 305c750..d9bca50 100644 --- a/examples/base/pydantic_usage.py +++ b/examples/base/msgspec_usage.py @@ -1,11 +1,10 @@ -# ruff: noqa: T201 -from pydantic import BaseModel +from msgspec import Struct -from contiguity import Base +from contiguity.base import Base # Create a Pydantic model for the item. -class MyItem(BaseModel): +class MyItem(Struct): key: str # Make sure to include the key field. name: str age: int diff --git a/tests/__init__.py b/tests/__init__.py index 97f1811..9d2ee39 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,6 +1,10 @@ import random import string +NON_EXISTENT_ITEM_WARNING = ( + "ItemNotFoundError will be raised in the future. To receive None for non-existent keys, set default=None." +) + def random_string(length: int = 10) -> str: return "".join(random.choices(string.ascii_lowercase, k=length)) # noqa: S311 diff --git a/tests/base/test_async_base_dict_typed.py b/tests/base/test_async_base_dict_typed.py index fd65184..39f3d83 100644 --- a/tests/base/test_async_base_dict_typed.py +++ b/tests/base/test_async_base_dict_typed.py @@ -1,21 +1,21 @@ -# ruff: noqa: S101, S311, PLR2004 +# ruff: noqa: S101, S311 import random from collections.abc import AsyncGenerator, Mapping from typing import Any import pytest from dotenv import load_dotenv -from pydantic import JsonValue -from contiguity import AsyncBase, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse -from tests import random_string +from contiguity.base import AsyncBase, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse +from contiguity.base.common import DataType +from tests import NON_EXISTENT_ITEM_WARNING, random_string load_dotenv() -DictItemType = Mapping[str, JsonValue] +DictItemType = Mapping[str, DataType] -def create_test_item(**kwargs: JsonValue) -> DictItemType: +def create_test_item(**kwargs: DataType) -> DictItemType: kwargs.setdefault("key", "test_key") kwargs.setdefault("field1", random.randint(1, 1000)) kwargs.setdefault("field2", random_string()) @@ -59,7 +59,7 @@ async def test_get(base: AsyncBase[DictItemType]) -> None: async def test_get_nonexistent(base: AsyncBase[DictItemType]) -> None: - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning, match=NON_EXISTENT_ITEM_WARNING): assert await base.get("nonexistent_key") is None @@ -73,7 +73,7 @@ async def test_delete(base: AsyncBase[DictItemType]) -> None: item = create_test_item() await base.insert(item) await base.delete("test_key") - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning, match=NON_EXISTENT_ITEM_WARNING): assert await base.get("test_key") is None diff --git a/tests/base/test_async_base_dict_untyped.py b/tests/base/test_async_base_dict_untyped.py index 1de7a6f..6ee3a68 100644 --- a/tests/base/test_async_base_dict_untyped.py +++ b/tests/base/test_async_base_dict_untyped.py @@ -1,19 +1,19 @@ -# ruff: noqa: S101, S311, PLR2004 +# ruff: noqa: S101, S311 import random from collections.abc import AsyncGenerator from typing import Any import pytest from dotenv import load_dotenv -from pydantic import JsonValue -from contiguity import AsyncBase, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse -from tests import random_string +from contiguity.base import AsyncBase, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse +from contiguity.base.common import DataType +from tests import NON_EXISTENT_ITEM_WARNING, random_string load_dotenv() -def create_test_item(**kwargs: JsonValue) -> dict: +def create_test_item(**kwargs: DataType) -> dict: kwargs.setdefault("key", "test_key") kwargs.setdefault("field1", random.randint(1, 1000)) kwargs.setdefault("field2", random_string()) @@ -57,7 +57,7 @@ async def test_get(base: AsyncBase) -> None: async def test_get_nonexistent(base: AsyncBase) -> None: - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning, match=NON_EXISTENT_ITEM_WARNING): assert await base.get("nonexistent_key") is None @@ -71,7 +71,7 @@ async def test_delete(base: AsyncBase) -> None: item = create_test_item() await base.insert(item) await base.delete("test_key") - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning, match=NON_EXISTENT_ITEM_WARNING): assert await base.get("test_key") is None diff --git a/tests/base/test_async_base_model.py b/tests/base/test_async_base_model.py index e0704de..5861738 100644 --- a/tests/base/test_async_base_model.py +++ b/tests/base/test_async_base_model.py @@ -1,27 +1,27 @@ -# ruff: noqa: S101, S311, PLR2004 +# ruff: noqa: S101, S311 import random from collections.abc import AsyncGenerator from typing import Any import pytest from dotenv import load_dotenv -from pydantic import BaseModel +from msgspec import Struct, field -from contiguity import AsyncBase, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse -from tests import random_string +from contiguity.base import AsyncBase, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse +from tests import NON_EXISTENT_ITEM_WARNING, random_string load_dotenv() -class TestItemModel(BaseModel): +class TestItemModel(Struct): key: str = "test_key" field1: int = random.randint(1, 1000) field2: str = random_string() field3: int = 1 field4: int = 0 - field5: list[str] = ["foo", "bar"] - field6: list[int] = [1, 2] - field7: dict[str, str] = {"foo": "bar"} + field5: list[str] = field(default_factory=lambda: ["foo", "bar"]) + field6: list[int] = field(default_factory=lambda: [1, 2]) + field7: dict[str, str] = field(default_factory=lambda: {"foo": "bar"}) @pytest.fixture @@ -56,7 +56,7 @@ async def test_get(base: AsyncBase[TestItemModel]) -> None: async def test_get_nonexistent(base: AsyncBase[TestItemModel]) -> None: - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning, match=NON_EXISTENT_ITEM_WARNING): assert await base.get("nonexistent_key") is None @@ -70,7 +70,7 @@ async def test_delete(base: AsyncBase[TestItemModel]) -> None: item = TestItemModel() await base.insert(item) await base.delete("test_key") - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning, match=NON_EXISTENT_ITEM_WARNING): assert await base.get("test_key") is None diff --git a/tests/base/test_async_base_typeddict.py b/tests/base/test_async_base_typeddict.py index 627ccf1..39629e4 100644 --- a/tests/base/test_async_base_typeddict.py +++ b/tests/base/test_async_base_typeddict.py @@ -1,4 +1,4 @@ -# ruff: noqa: S101, S311, PLR2004 +# ruff: noqa: S101, S311 from __future__ import annotations import random @@ -8,8 +8,8 @@ from dotenv import load_dotenv from typing_extensions import TypedDict -from contiguity import AsyncBase, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse -from tests import random_string +from contiguity.base import AsyncBase, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse +from tests import NON_EXISTENT_ITEM_WARNING, random_string if TYPE_CHECKING: from collections.abc import AsyncGenerator @@ -82,7 +82,7 @@ async def test_get(base: AsyncBase[TestItemDict]) -> None: async def test_get_nonexistent(base: AsyncBase[TestItemDict]) -> None: - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning, match=NON_EXISTENT_ITEM_WARNING): assert await base.get("nonexistent_key") is None @@ -96,7 +96,7 @@ async def test_delete(base: AsyncBase[TestItemDict]) -> None: item = create_test_item() await base.insert(item) await base.delete("test_key") - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning, match=NON_EXISTENT_ITEM_WARNING): assert await base.get("test_key") is None diff --git a/tests/base/test_base_dict_typed.py b/tests/base/test_base_dict_typed.py index 1c3553a..7017125 100644 --- a/tests/base/test_base_dict_typed.py +++ b/tests/base/test_base_dict_typed.py @@ -1,21 +1,21 @@ -# ruff: noqa: S101, S311, PLR2004 +# ruff: noqa: S101, S311 import random from collections.abc import Generator, Mapping from typing import Any import pytest from dotenv import load_dotenv -from pydantic import JsonValue -from contiguity import Base, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse -from tests import random_string +from contiguity.base import Base, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse +from contiguity.base.common import DataType +from tests import NON_EXISTENT_ITEM_WARNING, random_string load_dotenv() -DictItemType = Mapping[str, JsonValue] +DictItemType = Mapping[str, DataType] -def create_test_item(**kwargs: JsonValue) -> DictItemType: +def create_test_item(**kwargs: DataType) -> DictItemType: kwargs.setdefault("key", "test_key") kwargs.setdefault("field1", random.randint(1, 1000)) kwargs.setdefault("field2", random_string()) @@ -59,7 +59,7 @@ def test_get(base: Base[DictItemType]) -> None: def test_get_nonexistent(base: Base[DictItemType]) -> None: - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning, match=NON_EXISTENT_ITEM_WARNING): assert base.get("nonexistent_key") is None @@ -73,7 +73,7 @@ def test_delete(base: Base[DictItemType]) -> None: item = create_test_item() base.insert(item) base.delete("test_key") - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning, match=NON_EXISTENT_ITEM_WARNING): assert base.get("test_key") is None diff --git a/tests/base/test_base_dict_untyped.py b/tests/base/test_base_dict_untyped.py index 37bce0c..cd5a4f5 100644 --- a/tests/base/test_base_dict_untyped.py +++ b/tests/base/test_base_dict_untyped.py @@ -1,19 +1,19 @@ -# ruff: noqa: S101, S311, PLR2004 +# ruff: noqa: S101, S311 import random from collections.abc import Generator from typing import Any import pytest from dotenv import load_dotenv -from pydantic import JsonValue -from contiguity import Base, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse -from tests import random_string +from contiguity.base import Base, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse +from contiguity.base.common import DataType +from tests import NON_EXISTENT_ITEM_WARNING, random_string load_dotenv() -def create_test_item(**kwargs: JsonValue) -> dict: +def create_test_item(**kwargs: DataType) -> dict: kwargs.setdefault("key", "test_key") kwargs.setdefault("field1", random.randint(1, 1000)) kwargs.setdefault("field2", random_string()) @@ -57,7 +57,7 @@ def test_get(base: Base) -> None: def test_get_nonexistent(base: Base) -> None: - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning, match=NON_EXISTENT_ITEM_WARNING): assert base.get("nonexistent_key") is None @@ -71,7 +71,7 @@ def test_delete(base: Base) -> None: item = create_test_item() base.insert(item) base.delete("test_key") - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning, match=NON_EXISTENT_ITEM_WARNING): assert base.get("test_key") is None diff --git a/tests/base/test_base_model.py b/tests/base/test_base_model.py index d299318..759e71c 100644 --- a/tests/base/test_base_model.py +++ b/tests/base/test_base_model.py @@ -1,27 +1,27 @@ -# ruff: noqa: S101, S311, PLR2004 +# ruff: noqa: S101, S311 import random from collections.abc import Generator from typing import Any import pytest from dotenv import load_dotenv -from pydantic import BaseModel +from msgspec import Struct, field -from contiguity import Base, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse -from tests import random_string +from contiguity.base import Base, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse +from tests import NON_EXISTENT_ITEM_WARNING, random_string load_dotenv() -class TestItemModel(BaseModel): +class TestItemModel(Struct): key: str = "test_key" field1: int = random.randint(1, 1000) field2: str = random_string() field3: int = 1 field4: int = 0 - field5: list[str] = ["foo", "bar"] - field6: list[int] = [1, 2] - field7: dict[str, str] = {"foo": "bar"} + field5: list[str] = field(default_factory=lambda: ["foo", "bar"]) + field6: list[int] = field(default_factory=lambda: [1, 2]) + field7: dict[str, str] = field(default_factory=lambda: {"foo": "bar"}) @pytest.fixture @@ -56,7 +56,7 @@ def test_get(base: Base[TestItemModel]) -> None: def test_get_nonexistent(base: Base[TestItemModel]) -> None: - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning, match=NON_EXISTENT_ITEM_WARNING): assert base.get("nonexistent_key") is None @@ -70,7 +70,7 @@ def test_delete(base: Base[TestItemModel]) -> None: item = TestItemModel() base.insert(item) base.delete("test_key") - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning, match=NON_EXISTENT_ITEM_WARNING): assert base.get("test_key") is None diff --git a/tests/base/test_base_typeddict.py b/tests/base/test_base_typeddict.py index 39e4003..86c970a 100644 --- a/tests/base/test_base_typeddict.py +++ b/tests/base/test_base_typeddict.py @@ -1,4 +1,4 @@ -# ruff: noqa: S101, S311, PLR2004 +# ruff: noqa: S101, S311 from __future__ import annotations import random @@ -8,8 +8,8 @@ from dotenv import load_dotenv from typing_extensions import TypedDict -from contiguity import Base, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse -from tests import random_string +from contiguity.base import Base, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse +from tests import NON_EXISTENT_ITEM_WARNING, random_string if TYPE_CHECKING: from collections.abc import Generator @@ -82,7 +82,7 @@ def test_get(base: Base[TestItemDict]) -> None: def test_get_nonexistent(base: Base[TestItemDict]) -> None: - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning, match=NON_EXISTENT_ITEM_WARNING): assert base.get("nonexistent_key") is None @@ -96,7 +96,7 @@ def test_delete(base: Base[TestItemDict]) -> None: item = create_test_item() base.insert(item) base.delete("test_key") - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning, match=NON_EXISTENT_ITEM_WARNING): assert base.get("test_key") is None From 62e606eb6689e91fd38c8ebbb120b27347d34115 Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Mon, 6 Oct 2025 22:07:43 +0200 Subject: [PATCH 17/38] Update dev deps --- pyproject.toml | 10 +- uv.lock | 495 ++++++++++++++++++++++++++++++------------------- 2 files changed, 309 insertions(+), 196 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fec33ca..cdbe471 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,11 +42,11 @@ dependencies = [ [dependency-groups] dev = [ - "pre-commit~=3.8.0", - "pytest~=8.3.3", - "pytest-asyncio~=0.24.0", - "pytest-cov~=5.0.0", - "python-dotenv~=1.0.1", + "pre-commit~=4.3.0", + "pytest~=8.4.2", + "pytest-asyncio~=1.2.0", + "pytest-cov~=7.0.0", + "python-dotenv~=1.1.1", ] [project.urls] diff --git a/uv.lock b/uv.lock index b38aec5..e5237e1 100644 --- a/uv.lock +++ b/uv.lock @@ -1,33 +1,38 @@ version = 1 revision = 3 requires-python = ">=3.9" -resolution-markers = [ - "python_full_version < '3.13'", - "python_full_version >= '3.13'", -] [[package]] name = "anyio" -version = "4.6.0" +version = "4.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "idna" }, { name = "sniffio" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/49/f3f17ec11c4a91fe79275c426658e509b07547f874b14c1a526d86a83fc8/anyio-4.6.0.tar.gz", hash = "sha256:137b4559cbb034c477165047febb6ff83f390fc3b20bf181c1fc0a728cb8beeb", size = 170983, upload-time = "2024-09-21T10:33:28.479Z" } +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/9e/ef/7a4f225581a0d7886ea28359179cb861d7fbcdefad29663fc1167b86f69f/anyio-4.6.0-py3-none-any.whl", hash = "sha256:c7d2e9d63e31599eeb636c8c5c03a7e108d73b345f064f1c19fdc87b79036a9a", size = 89631, upload-time = "2024-09-21T10:33:27.05Z" }, + { 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 = "backports-asyncio-runner" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, ] [[package]] name = "certifi" -version = "2024.8.30" +version = "2025.10.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507, upload-time = "2024-08-30T01:55:04.365Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", size = 164519, upload-time = "2025-10-05T04:12:15.808Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321, upload-time = "2024-08-30T01:55:02.591Z" }, + { url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", size = 163286, upload-time = "2025-10-05T04:12:14.03Z" }, ] [[package]] @@ -77,80 +82,122 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ - { name = "pre-commit", specifier = "~=3.8.0" }, - { name = "pytest", specifier = "~=8.3.3" }, - { name = "pytest-asyncio", specifier = "~=0.24.0" }, - { name = "pytest-cov", specifier = "~=5.0.0" }, - { name = "python-dotenv", specifier = "~=1.0.1" }, + { name = "pre-commit", specifier = "~=4.3.0" }, + { name = "pytest", specifier = "~=8.4.2" }, + { name = "pytest-asyncio", specifier = "~=1.2.0" }, + { name = "pytest-cov", specifier = "~=7.0.0" }, + { name = "python-dotenv", specifier = "~=1.1.1" }, ] [[package]] name = "coverage" -version = "7.6.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9a/60/e781e8302e7b28f21ce06e30af077f856aa2cb4cf2253287dae9a593d509/coverage-7.6.2.tar.gz", hash = "sha256:a5f81e68aa62bc0cfca04f7b19eaa8f9c826b53fc82ab9e2121976dc74f131f3", size = 797872, upload-time = "2024-10-09T11:33:30.113Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/16/14/fb75c01b8427fb567c90ce920c90ed2bd314ad6960d54e8b377928607fd1/coverage-7.6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c9df1950fb92d49970cce38100d7e7293c84ed3606eaa16ea0b6bc27175bb667", size = 206561, upload-time = "2024-10-09T11:31:56.354Z" }, - { url = "https://files.pythonhosted.org/packages/93/b4/dcbf15f5583507415d0a78ce206e19d76699f1161e8b1ff6e1a21e9f9743/coverage-7.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:24500f4b0e03aab60ce575c85365beab64b44d4db837021e08339f61d1fbfe52", size = 206994, upload-time = "2024-10-09T11:31:59.205Z" }, - { url = "https://files.pythonhosted.org/packages/47/ee/57d607e14479fb760721ea1784608ade532665934bd75f260b250dc6c877/coverage-7.6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a663b180b6669c400b4630a24cc776f23a992d38ce7ae72ede2a397ce6b0f170", size = 235429, upload-time = "2024-10-09T11:32:00.967Z" }, - { url = "https://files.pythonhosted.org/packages/76/e1/cd263fd750fdb115aab11a086e3584d99d46fca1f201b5493cc3972aea28/coverage-7.6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfde025e2793a22efe8c21f807d276bd1d6a4bcc5ba6f19dbdfc4e7a12160909", size = 233329, upload-time = "2024-10-09T11:32:02.741Z" }, - { url = "https://files.pythonhosted.org/packages/30/3b/a1623d50fcd6ba532cef0c3c1059eec2a08a311676ffa84dbe4beb2b8a33/coverage-7.6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:087932079c065d7b8ebadd3a0160656c55954144af6439886c8bcf78bbbcde7f", size = 234491, upload-time = "2024-10-09T11:32:03.804Z" }, - { url = "https://files.pythonhosted.org/packages/b1/a6/8f3b3fd1f9b9400f3df38a7159362622546e2d951cc4984cf4617d0fd4d7/coverage-7.6.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9c6b0c1cafd96213a0327cf680acb39f70e452caf8e9a25aeb05316db9c07f89", size = 233589, upload-time = "2024-10-09T11:32:04.877Z" }, - { url = "https://files.pythonhosted.org/packages/e3/40/37d64093f57b372435d87679956607ecab066d2aede76c6d215815a35fa3/coverage-7.6.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6e85830eed5b5263ffa0c62428e43cb844296f3b4461f09e4bdb0d44ec190bc2", size = 232050, upload-time = "2024-10-09T11:32:07.647Z" }, - { url = "https://files.pythonhosted.org/packages/80/63/cbb76298b4f42bffe0030f1bc129a26a26255857c6beaa20419259ac07cc/coverage-7.6.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:62ab4231c01e156ece1b3a187c87173f31cbeee83a5e1f6dff17f288dca93345", size = 233180, upload-time = "2024-10-09T11:32:09.321Z" }, - { url = "https://files.pythonhosted.org/packages/7a/6a/eafa81503e905d473b799920927b06aa6ffba12db035fc98735b55bc1741/coverage-7.6.2-cp310-cp310-win32.whl", hash = "sha256:7b80fbb0da3aebde102a37ef0138aeedff45997e22f8962e5f16ae1742852676", size = 209281, upload-time = "2024-10-09T11:32:10.503Z" }, - { url = "https://files.pythonhosted.org/packages/19/d1/6b354c2cd52e0244944c097aaa71896869878df999f5f8e75fcd37eaf0f3/coverage-7.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:d20c3d1f31f14d6962a4e2f549c21d31e670b90f777ef4171be540fb7fb70f02", size = 210092, upload-time = "2024-10-09T11:32:12.213Z" }, - { url = "https://files.pythonhosted.org/packages/a5/29/72da824da4182f518b054c21552b7ed2473a4e4c6ac616298209808a1a5c/coverage-7.6.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bb21bac7783c1bf6f4bbe68b1e0ff0d20e7e7732cfb7995bc8d96e23aa90fc7b", size = 206667, upload-time = "2024-10-09T11:32:13.409Z" }, - { url = "https://files.pythonhosted.org/packages/23/52/c15dcf3cf575256c7c0992e441cd41092a6c519d65abe1eb5567aab3d8e8/coverage-7.6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7b2e437fbd8fae5bc7716b9c7ff97aecc95f0b4d56e4ca08b3c8d8adcaadb84", size = 207111, upload-time = "2024-10-09T11:32:14.469Z" }, - { url = "https://files.pythonhosted.org/packages/92/61/0d46dc26cf9f711b7b6078a54680665a5c2d62ec15991adb51e79236c699/coverage-7.6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:536f77f2bf5797983652d1d55f1a7272a29afcc89e3ae51caa99b2db4e89d658", size = 239050, upload-time = "2024-10-09T11:32:15.578Z" }, - { url = "https://files.pythonhosted.org/packages/3b/cb/9de71bade0343a0793f645f78a0e409248d85a2e5b4c4a9a1697c3b2e3d2/coverage-7.6.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f361296ca7054f0936b02525646b2731b32c8074ba6defab524b79b2b7eeac72", size = 236454, upload-time = "2024-10-09T11:32:17.158Z" }, - { url = "https://files.pythonhosted.org/packages/f2/81/b0dc02487447c4a56cf2eed5c57735097f77aeff582277a35f1f70713a8d/coverage-7.6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7926d8d034e06b479797c199747dd774d5e86179f2ce44294423327a88d66ca7", size = 238320, upload-time = "2024-10-09T11:32:18.282Z" }, - { url = "https://files.pythonhosted.org/packages/60/90/76815a76234050a87d0d1438a34820c1b857dd17353855c02bddabbedea8/coverage-7.6.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0bbae11c138585c89fb4e991faefb174a80112e1a7557d507aaa07675c62e66b", size = 237250, upload-time = "2024-10-09T11:32:19.342Z" }, - { url = "https://files.pythonhosted.org/packages/f6/bd/760a599c08c882d97382855264586bba2604901029c3f6bec5710477ae81/coverage-7.6.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fcad7d5d2bbfeae1026b395036a8aa5abf67e8038ae7e6a25c7d0f88b10a8e6a", size = 235880, upload-time = "2024-10-09T11:32:20.509Z" }, - { url = "https://files.pythonhosted.org/packages/83/de/41c3b90a779e473ae1ca325542aa5fa5464b7d2061288e9c22ba5f1deaa3/coverage-7.6.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f01e53575f27097d75d42de33b1b289c74b16891ce576d767ad8c48d17aeb5e0", size = 236653, upload-time = "2024-10-09T11:32:22.371Z" }, - { url = "https://files.pythonhosted.org/packages/f4/90/61fe2721b9a9d9446e6c3ca33b6569e81d2a9a795ddfe786a66bf54035b7/coverage-7.6.2-cp311-cp311-win32.whl", hash = "sha256:7781f4f70c9b0b39e1b129b10c7d43a4e0c91f90c60435e6da8288efc2b73438", size = 209251, upload-time = "2024-10-09T11:32:23.454Z" }, - { url = "https://files.pythonhosted.org/packages/96/87/d586f2b12b98288fc874d366cd8d5601f5a374cb75853647a3e4d02e4eb0/coverage-7.6.2-cp311-cp311-win_amd64.whl", hash = "sha256:9bcd51eeca35a80e76dc5794a9dd7cb04b97f0e8af620d54711793bfc1fbba4b", size = 210083, upload-time = "2024-10-09T11:32:24.557Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ac/1cca5ed5cf512a71cdd6e3afb75a5ef196f7ef9772be9192dadaaa5cfc1c/coverage-7.6.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ebc94fadbd4a3f4215993326a6a00e47d79889391f5659bf310f55fe5d9f581c", size = 206856, upload-time = "2024-10-09T11:32:26.083Z" }, - { url = "https://files.pythonhosted.org/packages/e4/58/030354d250f107a95e7aca24c7fd238709a3c7df3083cb206368798e637a/coverage-7.6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9681516288e3dcf0aa7c26231178cc0be6cac9705cac06709f2353c5b406cfea", size = 207098, upload-time = "2024-10-09T11:32:28.303Z" }, - { url = "https://files.pythonhosted.org/packages/03/df/5f2cd6048d44a54bb5f58f8ece4efbc5b686ed49f8bd8dbf41eb2a6a687f/coverage-7.6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d9c5d13927d77af4fbe453953810db766f75401e764727e73a6ee4f82527b3e", size = 240109, upload-time = "2024-10-09T11:32:29.451Z" }, - { url = "https://files.pythonhosted.org/packages/d3/18/7c53887643d921faa95529643b1b33e60ebba30ab835c8b5abd4e54d946b/coverage-7.6.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b92f9ca04b3e719d69b02dc4a69debb795af84cb7afd09c5eb5d54b4a1ae2191", size = 237141, upload-time = "2024-10-09T11:32:31.276Z" }, - { url = "https://files.pythonhosted.org/packages/d2/79/339bdf597d128374e6150c089b37436ba694585d769cabf6d5abd73a1365/coverage-7.6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ff2ef83d6d0b527b5c9dad73819b24a2f76fdddcfd6c4e7a4d7e73ecb0656b4", size = 239210, upload-time = "2024-10-09T11:32:33.187Z" }, - { url = "https://files.pythonhosted.org/packages/a9/62/7310c6de2bcb8a42f91094d41f0d4793ccda5a54621be3db76a156556cf2/coverage-7.6.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:47ccb6e99a3031ffbbd6e7cc041e70770b4fe405370c66a54dbf26a500ded80b", size = 238698, upload-time = "2024-10-09T11:32:34.292Z" }, - { url = "https://files.pythonhosted.org/packages/f2/cb/ccb23c084d7f581f770dc7ed547dc5b50763334ad6ce26087a9ad0b5b26d/coverage-7.6.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a867d26f06bcd047ef716175b2696b315cb7571ccb951006d61ca80bbc356e9e", size = 237000, upload-time = "2024-10-09T11:32:36.737Z" }, - { url = "https://files.pythonhosted.org/packages/e7/ab/58de9e2f94e4dc91b84d6e2705aa1e9d5447a2669fe113b4bbce6d2224a1/coverage-7.6.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cdfcf2e914e2ba653101157458afd0ad92a16731eeba9a611b5cbb3e7124e74b", size = 238666, upload-time = "2024-10-09T11:32:38.027Z" }, - { url = "https://files.pythonhosted.org/packages/6c/dc/8be87b9ed5dbd4892b603f41088b41982768e928734e5bdce67d2ddd460a/coverage-7.6.2-cp312-cp312-win32.whl", hash = "sha256:f9035695dadfb397bee9eeaf1dc7fbeda483bf7664a7397a629846800ce6e276", size = 209489, upload-time = "2024-10-09T11:32:39.345Z" }, - { url = "https://files.pythonhosted.org/packages/64/3a/3f44e55273a58bfb39b87ad76541bbb81d14de916b034fdb39971cc99ffe/coverage-7.6.2-cp312-cp312-win_amd64.whl", hash = "sha256:5ed69befa9a9fc796fe015a7040c9398722d6b97df73a6b608e9e275fa0932b0", size = 210270, upload-time = "2024-10-09T11:32:40.569Z" }, - { url = "https://files.pythonhosted.org/packages/ae/99/c9676a75b57438a19c5174dfcf39798b42728ad56650497286379dc0c2c3/coverage-7.6.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4eea60c79d36a8f39475b1af887663bc3ae4f31289cd216f514ce18d5938df40", size = 206888, upload-time = "2024-10-09T11:32:42.417Z" }, - { url = "https://files.pythonhosted.org/packages/e0/de/820ecb42e892049c5f384430e98b35b899da3451dd0cdb2f867baf26abfa/coverage-7.6.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa68a6cdbe1bc6793a9dbfc38302c11599bbe1837392ae9b1d238b9ef3dafcf1", size = 207142, upload-time = "2024-10-09T11:32:43.623Z" }, - { url = "https://files.pythonhosted.org/packages/dd/59/81fc7ad855d65eeb68fe9e7809cbb339946adb07be7ac32d3fc24dc17bd7/coverage-7.6.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ec528ae69f0a139690fad6deac8a7d33629fa61ccce693fdd07ddf7e9931fba", size = 239658, upload-time = "2024-10-09T11:32:45.479Z" }, - { url = "https://files.pythonhosted.org/packages/cd/a7/865de3eb9e78ffbf7afd92f86d2580b18edfb6f0481bd3c39b205e05a762/coverage-7.6.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed5ac02126f74d190fa2cc14a9eb2a5d9837d5863920fa472b02eb1595cdc925", size = 236802, upload-time = "2024-10-09T11:32:46.868Z" }, - { url = "https://files.pythonhosted.org/packages/36/94/3b8f3abf88b7c451f97fd14c98f536bcee364e74250d928d57cc97c38ddd/coverage-7.6.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21c0ea0d4db8a36b275cb6fb2437a3715697a4ba3cb7b918d3525cc75f726304", size = 238793, upload-time = "2024-10-09T11:32:48.095Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4b/57f95e41a10525002f524f3dbd577a3a9871d67998f8a8eb192fe697dc7b/coverage-7.6.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:35a51598f29b2a19e26d0908bd196f771a9b1c5d9a07bf20be0adf28f1ad4f77", size = 238455, upload-time = "2024-10-09T11:32:50.784Z" }, - { url = "https://files.pythonhosted.org/packages/99/c9/9fbe5b841628e1d9030c8044844afef4f4735586289eb9237eeb5b97f0d7/coverage-7.6.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c9192925acc33e146864b8cf037e2ed32a91fdf7644ae875f5d46cd2ef086a5f", size = 236538, upload-time = "2024-10-09T11:32:52.072Z" }, - { url = "https://files.pythonhosted.org/packages/43/0d/2200a0d447e30de94d48e4851c04d8dce37340815e7eda27457a7043c037/coverage-7.6.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf4eeecc9e10f5403ec06138978235af79c9a79af494eb6b1d60a50b49ed2869", size = 238383, upload-time = "2024-10-09T11:32:53.25Z" }, - { url = "https://files.pythonhosted.org/packages/ec/8a/106c66faafb4a87002b698769d6de3c4db0b6c29a7aeb72de13b893c333e/coverage-7.6.2-cp313-cp313-win32.whl", hash = "sha256:e4ee15b267d2dad3e8759ca441ad450c334f3733304c55210c2a44516e8d5530", size = 209551, upload-time = "2024-10-09T11:32:54.455Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f5/1b39e2faaf5b9cc7eed568c444df5991ce7ff7138e2e735a6801be1bdadb/coverage-7.6.2-cp313-cp313-win_amd64.whl", hash = "sha256:c71965d1ced48bf97aab79fad56df82c566b4c498ffc09c2094605727c4b7e36", size = 210282, upload-time = "2024-10-09T11:32:55.661Z" }, - { url = "https://files.pythonhosted.org/packages/79/a3/8dd4e6c09f5286094cd6c7edb115b3fbf06ad8304d45431722a4e3bc2508/coverage-7.6.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7571e8bbecc6ac066256f9de40365ff833553e2e0c0c004f4482facb131820ef", size = 207629, upload-time = "2024-10-09T11:32:57.543Z" }, - { url = "https://files.pythonhosted.org/packages/8e/db/a9aa7009bbdc570a235e1ac781c0a83aa323cac6db8f8f13c2127b110978/coverage-7.6.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:078a87519057dacb5d77e333f740708ec2a8f768655f1db07f8dfd28d7a005f0", size = 207902, upload-time = "2024-10-09T11:32:58.787Z" }, - { url = "https://files.pythonhosted.org/packages/54/08/d0962be62d4335599ca2ff3a48bb68c9bfb80df74e28ca689ff5f392087b/coverage-7.6.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e5e92e3e84a8718d2de36cd8387459cba9a4508337b8c5f450ce42b87a9e760", size = 250617, upload-time = "2024-10-09T11:33:02.145Z" }, - { url = "https://files.pythonhosted.org/packages/a5/a2/158570aff1dd88b661a6c11281cbb190e8696e77798b4b2e47c74bfb2f39/coverage-7.6.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ebabdf1c76593a09ee18c1a06cd3022919861365219ea3aca0247ededf6facd6", size = 246334, upload-time = "2024-10-09T11:33:03.943Z" }, - { url = "https://files.pythonhosted.org/packages/aa/fe/b00428cca325b6585ca77422e4f64d7d86a225b14664b98682ea501efb57/coverage-7.6.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12179eb0575b8900912711688e45474f04ab3934aaa7b624dea7b3c511ecc90f", size = 248692, upload-time = "2024-10-09T11:33:05.216Z" }, - { url = "https://files.pythonhosted.org/packages/30/21/0a15fefc13039450bc45e7159f3add92489f004555eb7dab9c7ad4365dd0/coverage-7.6.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:39d3b964abfe1519b9d313ab28abf1d02faea26cd14b27f5283849bf59479ff5", size = 248188, upload-time = "2024-10-09T11:33:07.139Z" }, - { url = "https://files.pythonhosted.org/packages/de/b8/5c093526046a8450a7a3d62ad09517cf38e638f6b3ee9433dd6a73360501/coverage-7.6.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:84c4315577f7cd511d6250ffd0f695c825efe729f4205c0340f7004eda51191f", size = 246072, upload-time = "2024-10-09T11:33:08.425Z" }, - { url = "https://files.pythonhosted.org/packages/1e/8b/542b607d2cff56e5a90a6948f5a9040b693761d2be2d3c3bf88957b02361/coverage-7.6.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ff797320dcbff57caa6b2301c3913784a010e13b1f6cf4ab3f563f3c5e7919db", size = 247354, upload-time = "2024-10-09T11:33:09.719Z" }, - { url = "https://files.pythonhosted.org/packages/95/82/2e9111aa5e59f42b332d387f64e3205c2263518d1e660154d0c9fc54390e/coverage-7.6.2-cp313-cp313t-win32.whl", hash = "sha256:2b636a301e53964550e2f3094484fa5a96e699db318d65398cfba438c5c92171", size = 210194, upload-time = "2024-10-09T11:33:10.935Z" }, - { url = "https://files.pythonhosted.org/packages/9d/46/aabe4305cfc57cab4865f788ceceef746c422469720c32ed7a5b44e20f5e/coverage-7.6.2-cp313-cp313t-win_amd64.whl", hash = "sha256:d03a060ac1a08e10589c27d509bbdb35b65f2d7f3f8d81cf2fa199877c7bc58a", size = 211346, upload-time = "2024-10-09T11:33:12.889Z" }, - { url = "https://files.pythonhosted.org/packages/6a/a9/85d14426f2449252f302f12c1c2a957a0a7ae7f35317ca3eaa365e1d6453/coverage-7.6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c37faddc8acd826cfc5e2392531aba734b229741d3daec7f4c777a8f0d4993e5", size = 206555, upload-time = "2024-10-09T11:33:14.181Z" }, - { url = "https://files.pythonhosted.org/packages/71/ff/bc4d5697a55edf1ff077c47df5637ff4518ba2760ada82c142aca79ea3fe/coverage-7.6.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab31fdd643f162c467cfe6a86e9cb5f1965b632e5e65c072d90854ff486d02cf", size = 206990, upload-time = "2024-10-09T11:33:15.48Z" }, - { url = "https://files.pythonhosted.org/packages/34/65/1301721d09f5b58da9decfd62eb42eaef07fdb854dae904c3482e59cc309/coverage-7.6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97df87e1a20deb75ac7d920c812e9326096aa00a9a4b6d07679b4f1f14b06c90", size = 235022, upload-time = "2024-10-09T11:33:16.813Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ec/7a2f361485226e6934a8f5d1f6eef7e8b7faf228fb6107476fa584700a32/coverage-7.6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:343056c5e0737487a5291f5691f4dfeb25b3e3c8699b4d36b92bb0e586219d14", size = 232943, upload-time = "2024-10-09T11:33:18.319Z" }, - { url = "https://files.pythonhosted.org/packages/2d/60/b23e61a372bef93c9d13d87efa2ea3a870130be498e5b81740616b6e6200/coverage-7.6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad4ef1c56b47b6b9024b939d503ab487231df1f722065a48f4fc61832130b90e", size = 234074, upload-time = "2024-10-09T11:33:19.655Z" }, - { url = "https://files.pythonhosted.org/packages/89/ec/4a56d9b310b2413987682ae3a858e30ea11d6f6d05366ecab4d73385fbef/coverage-7.6.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fca4a92c8a7a73dee6946471bce6d1443d94155694b893b79e19ca2a540d86e", size = 233226, upload-time = "2024-10-09T11:33:20.986Z" }, - { url = "https://files.pythonhosted.org/packages/8c/77/31ecc00c525dea216d59090b807e9d1268a07d289f9dbe0cfc6795e33b68/coverage-7.6.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69f251804e052fc46d29d0e7348cdc5fcbfc4861dc4a1ebedef7e78d241ad39e", size = 231706, upload-time = "2024-10-09T11:33:22.369Z" }, - { url = "https://files.pythonhosted.org/packages/7b/02/3f84bdd286a9db9b816cb5ca0adfa001575f8e496ba39da26f0ded2f0849/coverage-7.6.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e8ea055b3ea046c0f66217af65bc193bbbeca1c8661dc5fd42698db5795d2627", size = 232697, upload-time = "2024-10-09T11:33:23.697Z" }, - { url = "https://files.pythonhosted.org/packages/7c/34/158b73026cbc2d2b3a56fbc71d955c0eea52953e49de97f820b3060f62b9/coverage-7.6.2-cp39-cp39-win32.whl", hash = "sha256:6c2ba1e0c24d8fae8f2cf0aeb2fc0a2a7f69b6d20bd8d3749fd6b36ecef5edf0", size = 209278, upload-time = "2024-10-09T11:33:25.075Z" }, - { url = "https://files.pythonhosted.org/packages/d1/05/4326e4ea071176f0bddc30b5a3555b48fa96c45a8f6a09b6c2e4041dfcc0/coverage-7.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:2186369a654a15628e9c1c9921409a6b3eda833e4b91f3ca2a7d9f77abb4987c", size = 210057, upload-time = "2024-10-09T11:33:26.772Z" }, - { url = "https://files.pythonhosted.org/packages/9d/5c/88f15b7614ba9ed1dbb1c0bd2c9073184b96c2bead0b93199487b44d04b3/coverage-7.6.2-pp39.pp310-none-any.whl", hash = "sha256:667952739daafe9616db19fbedbdb87917eee253ac4f31d70c7587f7ab531b4e", size = 198799, upload-time = "2024-10-09T11:33:28.796Z" }, +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/e5/6c/3a3f7a46888e69d18abe3ccc6fe4cb16cccb1e6a2f99698931dafca489e6/coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a", size = 217987, upload-time = "2025-09-21T20:00:57.218Z" }, + { url = "https://files.pythonhosted.org/packages/03/94/952d30f180b1a916c11a56f5c22d3535e943aa22430e9e3322447e520e1c/coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5", size = 218388, upload-time = "2025-09-21T20:01:00.081Z" }, + { url = "https://files.pythonhosted.org/packages/50/2b/9e0cf8ded1e114bcd8b2fd42792b57f1c4e9e4ea1824cde2af93a67305be/coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17", size = 245148, upload-time = "2025-09-21T20:01:01.768Z" }, + { url = "https://files.pythonhosted.org/packages/19/20/d0384ac06a6f908783d9b6aa6135e41b093971499ec488e47279f5b846e6/coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b", size = 246958, upload-time = "2025-09-21T20:01:03.355Z" }, + { url = "https://files.pythonhosted.org/packages/60/83/5c283cff3d41285f8eab897651585db908a909c572bdc014bcfaf8a8b6ae/coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87", size = 248819, upload-time = "2025-09-21T20:01:04.968Z" }, + { url = "https://files.pythonhosted.org/packages/60/22/02eb98fdc5ff79f423e990d877693e5310ae1eab6cb20ae0b0b9ac45b23b/coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e", size = 245754, upload-time = "2025-09-21T20:01:06.321Z" }, + { url = "https://files.pythonhosted.org/packages/b4/bc/25c83bcf3ad141b32cd7dc45485ef3c01a776ca3aa8ef0a93e77e8b5bc43/coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e", size = 246860, upload-time = "2025-09-21T20:01:07.605Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b7/95574702888b58c0928a6e982038c596f9c34d52c5e5107f1eef729399b5/coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df", size = 244877, upload-time = "2025-09-21T20:01:08.829Z" }, + { url = "https://files.pythonhosted.org/packages/47/b6/40095c185f235e085df0e0b158f6bd68cc6e1d80ba6c7721dc81d97ec318/coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0", size = 245108, upload-time = "2025-09-21T20:01:10.527Z" }, + { url = "https://files.pythonhosted.org/packages/c8/50/4aea0556da7a4b93ec9168420d170b55e2eb50ae21b25062513d020c6861/coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13", size = 245752, upload-time = "2025-09-21T20:01:11.857Z" }, + { url = "https://files.pythonhosted.org/packages/6a/28/ea1a84a60828177ae3b100cb6723838523369a44ec5742313ed7db3da160/coverage-7.10.7-cp310-cp310-win32.whl", hash = "sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b", size = 220497, upload-time = "2025-09-21T20:01:13.459Z" }, + { url = "https://files.pythonhosted.org/packages/fc/1a/a81d46bbeb3c3fd97b9602ebaa411e076219a150489bcc2c025f151bd52d/coverage-7.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807", size = 221392, upload-time = "2025-09-21T20:01:14.722Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5d/c1a17867b0456f2e9ce2d8d4708a4c3a089947d0bec9c66cdf60c9e7739f/coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59", size = 218102, upload-time = "2025-09-21T20:01:16.089Z" }, + { url = "https://files.pythonhosted.org/packages/54/f0/514dcf4b4e3698b9a9077f084429681bf3aad2b4a72578f89d7f643eb506/coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a", size = 218505, upload-time = "2025-09-21T20:01:17.788Z" }, + { url = "https://files.pythonhosted.org/packages/20/f6/9626b81d17e2a4b25c63ac1b425ff307ecdeef03d67c9a147673ae40dc36/coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699", size = 248898, upload-time = "2025-09-21T20:01:19.488Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ef/bd8e719c2f7417ba03239052e099b76ea1130ac0cbb183ee1fcaa58aaff3/coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d", size = 250831, upload-time = "2025-09-21T20:01:20.817Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b6/bf054de41ec948b151ae2b79a55c107f5760979538f5fb80c195f2517718/coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e", size = 252937, upload-time = "2025-09-21T20:01:22.171Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e5/3860756aa6f9318227443c6ce4ed7bf9e70bb7f1447a0353f45ac5c7974b/coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23", size = 249021, upload-time = "2025-09-21T20:01:23.907Z" }, + { url = "https://files.pythonhosted.org/packages/26/0f/bd08bd042854f7fd07b45808927ebcce99a7ed0f2f412d11629883517ac2/coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab", size = 250626, upload-time = "2025-09-21T20:01:25.721Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a7/4777b14de4abcc2e80c6b1d430f5d51eb18ed1d75fca56cbce5f2db9b36e/coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82", size = 248682, upload-time = "2025-09-21T20:01:27.105Z" }, + { url = "https://files.pythonhosted.org/packages/34/72/17d082b00b53cd45679bad682fac058b87f011fd8b9fe31d77f5f8d3a4e4/coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2", size = 248402, upload-time = "2025-09-21T20:01:28.629Z" }, + { url = "https://files.pythonhosted.org/packages/81/7a/92367572eb5bdd6a84bfa278cc7e97db192f9f45b28c94a9ca1a921c3577/coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61", size = 249320, upload-time = "2025-09-21T20:01:30.004Z" }, + { url = "https://files.pythonhosted.org/packages/2f/88/a23cc185f6a805dfc4fdf14a94016835eeb85e22ac3a0e66d5e89acd6462/coverage-7.10.7-cp311-cp311-win32.whl", hash = "sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14", size = 220536, upload-time = "2025-09-21T20:01:32.184Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ef/0b510a399dfca17cec7bc2f05ad8bd78cf55f15c8bc9a73ab20c5c913c2e/coverage-7.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2", size = 221425, upload-time = "2025-09-21T20:01:33.557Z" }, + { url = "https://files.pythonhosted.org/packages/51/7f/023657f301a276e4ba1850f82749bc136f5a7e8768060c2e5d9744a22951/coverage-7.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a", size = 220103, upload-time = "2025-09-21T20:01:34.929Z" }, + { url = "https://files.pythonhosted.org/packages/13/e4/eb12450f71b542a53972d19117ea5a5cea1cab3ac9e31b0b5d498df1bd5a/coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417", size = 218290, upload-time = "2025-09-21T20:01:36.455Z" }, + { url = "https://files.pythonhosted.org/packages/37/66/593f9be12fc19fb36711f19a5371af79a718537204d16ea1d36f16bd78d2/coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973", size = 218515, upload-time = "2025-09-21T20:01:37.982Z" }, + { url = "https://files.pythonhosted.org/packages/66/80/4c49f7ae09cafdacc73fbc30949ffe77359635c168f4e9ff33c9ebb07838/coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c", size = 250020, upload-time = "2025-09-21T20:01:39.617Z" }, + { url = "https://files.pythonhosted.org/packages/a6/90/a64aaacab3b37a17aaedd83e8000142561a29eb262cede42d94a67f7556b/coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7", size = 252769, upload-time = "2025-09-21T20:01:41.341Z" }, + { url = "https://files.pythonhosted.org/packages/98/2e/2dda59afd6103b342e096f246ebc5f87a3363b5412609946c120f4e7750d/coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6", size = 253901, upload-time = "2025-09-21T20:01:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/dc/8d8119c9051d50f3119bb4a75f29f1e4a6ab9415cd1fa8bf22fcc3fb3b5f/coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59", size = 250413, upload-time = "2025-09-21T20:01:44.469Z" }, + { url = "https://files.pythonhosted.org/packages/98/b3/edaff9c5d79ee4d4b6d3fe046f2b1d799850425695b789d491a64225d493/coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b", size = 251820, upload-time = "2025-09-21T20:01:45.915Z" }, + { url = "https://files.pythonhosted.org/packages/11/25/9a0728564bb05863f7e513e5a594fe5ffef091b325437f5430e8cfb0d530/coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a", size = 249941, upload-time = "2025-09-21T20:01:47.296Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fd/ca2650443bfbef5b0e74373aac4df67b08180d2f184b482c41499668e258/coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb", size = 249519, upload-time = "2025-09-21T20:01:48.73Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/f692f125fb4299b6f963b0745124998ebb8e73ecdfce4ceceb06a8c6bec5/coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1", size = 251375, upload-time = "2025-09-21T20:01:50.529Z" }, + { url = "https://files.pythonhosted.org/packages/5e/75/61b9bbd6c7d24d896bfeec57acba78e0f8deac68e6baf2d4804f7aae1f88/coverage-7.10.7-cp312-cp312-win32.whl", hash = "sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256", size = 220699, upload-time = "2025-09-21T20:01:51.941Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f3/3bf7905288b45b075918d372498f1cf845b5b579b723c8fd17168018d5f5/coverage-7.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba", size = 221512, upload-time = "2025-09-21T20:01:53.481Z" }, + { url = "https://files.pythonhosted.org/packages/5c/44/3e32dbe933979d05cf2dac5e697c8599cfe038aaf51223ab901e208d5a62/coverage-7.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf", size = 220147, upload-time = "2025-09-21T20:01:55.2Z" }, + { url = "https://files.pythonhosted.org/packages/9a/94/b765c1abcb613d103b64fcf10395f54d69b0ef8be6a0dd9c524384892cc7/coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d", size = 218320, upload-time = "2025-09-21T20:01:56.629Z" }, + { url = "https://files.pythonhosted.org/packages/72/4f/732fff31c119bb73b35236dd333030f32c4bfe909f445b423e6c7594f9a2/coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b", size = 218575, upload-time = "2025-09-21T20:01:58.203Z" }, + { url = "https://files.pythonhosted.org/packages/87/02/ae7e0af4b674be47566707777db1aa375474f02a1d64b9323e5813a6cdd5/coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e", size = 249568, upload-time = "2025-09-21T20:01:59.748Z" }, + { url = "https://files.pythonhosted.org/packages/a2/77/8c6d22bf61921a59bce5471c2f1f7ac30cd4ac50aadde72b8c48d5727902/coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b", size = 252174, upload-time = "2025-09-21T20:02:01.192Z" }, + { url = "https://files.pythonhosted.org/packages/b1/20/b6ea4f69bbb52dac0aebd62157ba6a9dddbfe664f5af8122dac296c3ee15/coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49", size = 253447, upload-time = "2025-09-21T20:02:02.701Z" }, + { url = "https://files.pythonhosted.org/packages/f9/28/4831523ba483a7f90f7b259d2018fef02cb4d5b90bc7c1505d6e5a84883c/coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911", size = 249779, upload-time = "2025-09-21T20:02:04.185Z" }, + { url = "https://files.pythonhosted.org/packages/a7/9f/4331142bc98c10ca6436d2d620c3e165f31e6c58d43479985afce6f3191c/coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0", size = 251604, upload-time = "2025-09-21T20:02:06.034Z" }, + { url = "https://files.pythonhosted.org/packages/ce/60/bda83b96602036b77ecf34e6393a3836365481b69f7ed7079ab85048202b/coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f", size = 249497, upload-time = "2025-09-21T20:02:07.619Z" }, + { url = "https://files.pythonhosted.org/packages/5f/af/152633ff35b2af63977edd835d8e6430f0caef27d171edf2fc76c270ef31/coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c", size = 249350, upload-time = "2025-09-21T20:02:10.34Z" }, + { url = "https://files.pythonhosted.org/packages/9d/71/d92105d122bd21cebba877228990e1646d862e34a98bb3374d3fece5a794/coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f", size = 251111, upload-time = "2025-09-21T20:02:12.122Z" }, + { url = "https://files.pythonhosted.org/packages/a2/9e/9fdb08f4bf476c912f0c3ca292e019aab6712c93c9344a1653986c3fd305/coverage-7.10.7-cp313-cp313-win32.whl", hash = "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698", size = 220746, upload-time = "2025-09-21T20:02:13.919Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b1/a75fd25df44eab52d1931e89980d1ada46824c7a3210be0d3c88a44aaa99/coverage-7.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843", size = 221541, upload-time = "2025-09-21T20:02:15.57Z" }, + { url = "https://files.pythonhosted.org/packages/14/3a/d720d7c989562a6e9a14b2c9f5f2876bdb38e9367126d118495b89c99c37/coverage-7.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546", size = 220170, upload-time = "2025-09-21T20:02:17.395Z" }, + { url = "https://files.pythonhosted.org/packages/bb/22/e04514bf2a735d8b0add31d2b4ab636fc02370730787c576bb995390d2d5/coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c", size = 219029, upload-time = "2025-09-21T20:02:18.936Z" }, + { url = "https://files.pythonhosted.org/packages/11/0b/91128e099035ece15da3445d9015e4b4153a6059403452d324cbb0a575fa/coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15", size = 219259, upload-time = "2025-09-21T20:02:20.44Z" }, + { url = "https://files.pythonhosted.org/packages/8b/51/66420081e72801536a091a0c8f8c1f88a5c4bf7b9b1bdc6222c7afe6dc9b/coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4", size = 260592, upload-time = "2025-09-21T20:02:22.313Z" }, + { url = "https://files.pythonhosted.org/packages/5d/22/9b8d458c2881b22df3db5bb3e7369e63d527d986decb6c11a591ba2364f7/coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0", size = 262768, upload-time = "2025-09-21T20:02:24.287Z" }, + { url = "https://files.pythonhosted.org/packages/f7/08/16bee2c433e60913c610ea200b276e8eeef084b0d200bdcff69920bd5828/coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0", size = 264995, upload-time = "2025-09-21T20:02:26.133Z" }, + { url = "https://files.pythonhosted.org/packages/20/9d/e53eb9771d154859b084b90201e5221bca7674ba449a17c101a5031d4054/coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65", size = 259546, upload-time = "2025-09-21T20:02:27.716Z" }, + { url = "https://files.pythonhosted.org/packages/ad/b0/69bc7050f8d4e56a89fb550a1577d5d0d1db2278106f6f626464067b3817/coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541", size = 262544, upload-time = "2025-09-21T20:02:29.216Z" }, + { url = "https://files.pythonhosted.org/packages/ef/4b/2514b060dbd1bc0aaf23b852c14bb5818f244c664cb16517feff6bb3a5ab/coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6", size = 260308, upload-time = "2025-09-21T20:02:31.226Z" }, + { url = "https://files.pythonhosted.org/packages/54/78/7ba2175007c246d75e496f64c06e94122bdb914790a1285d627a918bd271/coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999", size = 258920, upload-time = "2025-09-21T20:02:32.823Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b3/fac9f7abbc841409b9a410309d73bfa6cfb2e51c3fada738cb607ce174f8/coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2", size = 261434, upload-time = "2025-09-21T20:02:34.86Z" }, + { url = "https://files.pythonhosted.org/packages/ee/51/a03bec00d37faaa891b3ff7387192cef20f01604e5283a5fabc95346befa/coverage-7.10.7-cp313-cp313t-win32.whl", hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a", size = 221403, upload-time = "2025-09-21T20:02:37.034Z" }, + { url = "https://files.pythonhosted.org/packages/53/22/3cf25d614e64bf6d8e59c7c669b20d6d940bb337bdee5900b9ca41c820bb/coverage-7.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb", size = 222469, upload-time = "2025-09-21T20:02:39.011Z" }, + { url = "https://files.pythonhosted.org/packages/49/a1/00164f6d30d8a01c3c9c48418a7a5be394de5349b421b9ee019f380df2a0/coverage-7.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb", size = 220731, upload-time = "2025-09-21T20:02:40.939Z" }, + { url = "https://files.pythonhosted.org/packages/23/9c/5844ab4ca6a4dd97a1850e030a15ec7d292b5c5cb93082979225126e35dd/coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520", size = 218302, upload-time = "2025-09-21T20:02:42.527Z" }, + { url = "https://files.pythonhosted.org/packages/f0/89/673f6514b0961d1f0e20ddc242e9342f6da21eaba3489901b565c0689f34/coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32", size = 218578, upload-time = "2025-09-21T20:02:44.468Z" }, + { url = "https://files.pythonhosted.org/packages/05/e8/261cae479e85232828fb17ad536765c88dd818c8470aca690b0ac6feeaa3/coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f", size = 249629, upload-time = "2025-09-21T20:02:46.503Z" }, + { url = "https://files.pythonhosted.org/packages/82/62/14ed6546d0207e6eda876434e3e8475a3e9adbe32110ce896c9e0c06bb9a/coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a", size = 252162, upload-time = "2025-09-21T20:02:48.689Z" }, + { url = "https://files.pythonhosted.org/packages/ff/49/07f00db9ac6478e4358165a08fb41b469a1b053212e8a00cb02f0d27a05f/coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360", size = 253517, upload-time = "2025-09-21T20:02:50.31Z" }, + { url = "https://files.pythonhosted.org/packages/a2/59/c5201c62dbf165dfbc91460f6dbbaa85a8b82cfa6131ac45d6c1bfb52deb/coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69", size = 249632, upload-time = "2025-09-21T20:02:51.971Z" }, + { url = "https://files.pythonhosted.org/packages/07/ae/5920097195291a51fb00b3a70b9bbd2edbfe3c84876a1762bd1ef1565ebc/coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14", size = 251520, upload-time = "2025-09-21T20:02:53.858Z" }, + { url = "https://files.pythonhosted.org/packages/b9/3c/a815dde77a2981f5743a60b63df31cb322c944843e57dbd579326625a413/coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe", size = 249455, upload-time = "2025-09-21T20:02:55.807Z" }, + { url = "https://files.pythonhosted.org/packages/aa/99/f5cdd8421ea656abefb6c0ce92556709db2265c41e8f9fc6c8ae0f7824c9/coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e", size = 249287, upload-time = "2025-09-21T20:02:57.784Z" }, + { url = "https://files.pythonhosted.org/packages/c3/7a/e9a2da6a1fc5d007dd51fca083a663ab930a8c4d149c087732a5dbaa0029/coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd", size = 250946, upload-time = "2025-09-21T20:02:59.431Z" }, + { url = "https://files.pythonhosted.org/packages/ef/5b/0b5799aa30380a949005a353715095d6d1da81927d6dbed5def2200a4e25/coverage-7.10.7-cp314-cp314-win32.whl", hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2", size = 221009, upload-time = "2025-09-21T20:03:01.324Z" }, + { url = "https://files.pythonhosted.org/packages/da/b0/e802fbb6eb746de006490abc9bb554b708918b6774b722bb3a0e6aa1b7de/coverage-7.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681", size = 221804, upload-time = "2025-09-21T20:03:03.4Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e8/71d0c8e374e31f39e3389bb0bd19e527d46f00ea8571ec7ec8fd261d8b44/coverage-7.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880", size = 220384, upload-time = "2025-09-21T20:03:05.111Z" }, + { url = "https://files.pythonhosted.org/packages/62/09/9a5608d319fa3eba7a2019addeacb8c746fb50872b57a724c9f79f146969/coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63", size = 219047, upload-time = "2025-09-21T20:03:06.795Z" }, + { url = "https://files.pythonhosted.org/packages/f5/6f/f58d46f33db9f2e3647b2d0764704548c184e6f5e014bef528b7f979ef84/coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2", size = 219266, upload-time = "2025-09-21T20:03:08.495Z" }, + { url = "https://files.pythonhosted.org/packages/74/5c/183ffc817ba68e0b443b8c934c8795553eb0c14573813415bd59941ee165/coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d", size = 260767, upload-time = "2025-09-21T20:03:10.172Z" }, + { url = "https://files.pythonhosted.org/packages/0f/48/71a8abe9c1ad7e97548835e3cc1adbf361e743e9d60310c5f75c9e7bf847/coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0", size = 262931, upload-time = "2025-09-21T20:03:11.861Z" }, + { url = "https://files.pythonhosted.org/packages/84/fd/193a8fb132acfc0a901f72020e54be5e48021e1575bb327d8ee1097a28fd/coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699", size = 265186, upload-time = "2025-09-21T20:03:13.539Z" }, + { url = "https://files.pythonhosted.org/packages/b1/8f/74ecc30607dd95ad50e3034221113ccb1c6d4e8085cc761134782995daae/coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9", size = 259470, upload-time = "2025-09-21T20:03:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/0f/55/79ff53a769f20d71b07023ea115c9167c0bb56f281320520cf64c5298a96/coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f", size = 262626, upload-time = "2025-09-21T20:03:17.673Z" }, + { url = "https://files.pythonhosted.org/packages/88/e2/dac66c140009b61ac3fc13af673a574b00c16efdf04f9b5c740703e953c0/coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1", size = 260386, upload-time = "2025-09-21T20:03:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/a2/f1/f48f645e3f33bb9ca8a496bc4a9671b52f2f353146233ebd7c1df6160440/coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0", size = 258852, upload-time = "2025-09-21T20:03:21.007Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3b/8442618972c51a7affeead957995cfa8323c0c9bcf8fa5a027421f720ff4/coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399", size = 261534, upload-time = "2025-09-21T20:03:23.12Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dc/101f3fa3a45146db0cb03f5b4376e24c0aac818309da23e2de0c75295a91/coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235", size = 221784, upload-time = "2025-09-21T20:03:24.769Z" }, + { url = "https://files.pythonhosted.org/packages/4c/a1/74c51803fc70a8a40d7346660379e144be772bab4ac7bb6e6b905152345c/coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d", size = 222905, upload-time = "2025-09-21T20:03:26.93Z" }, + { url = "https://files.pythonhosted.org/packages/12/65/f116a6d2127df30bcafbceef0302d8a64ba87488bf6f73a6d8eebf060873/coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a", size = 220922, upload-time = "2025-09-21T20:03:28.672Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ad/d1c25053764b4c42eb294aae92ab617d2e4f803397f9c7c8295caa77a260/coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3", size = 217978, upload-time = "2025-09-21T20:03:30.362Z" }, + { url = "https://files.pythonhosted.org/packages/52/2f/b9f9daa39b80ece0b9548bbb723381e29bc664822d9a12c2135f8922c22b/coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c", size = 218370, upload-time = "2025-09-21T20:03:32.147Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6e/30d006c3b469e58449650642383dddf1c8fb63d44fdf92994bfd46570695/coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396", size = 244802, upload-time = "2025-09-21T20:03:33.919Z" }, + { url = "https://files.pythonhosted.org/packages/b0/49/8a070782ce7e6b94ff6a0b6d7c65ba6bc3091d92a92cef4cd4eb0767965c/coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40", size = 246625, upload-time = "2025-09-21T20:03:36.09Z" }, + { url = "https://files.pythonhosted.org/packages/6a/92/1c1c5a9e8677ce56d42b97bdaca337b2d4d9ebe703d8c174ede52dbabd5f/coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594", size = 248399, upload-time = "2025-09-21T20:03:38.342Z" }, + { url = "https://files.pythonhosted.org/packages/c0/54/b140edee7257e815de7426d5d9846b58505dffc29795fff2dfb7f8a1c5a0/coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a", size = 245142, upload-time = "2025-09-21T20:03:40.591Z" }, + { url = "https://files.pythonhosted.org/packages/e4/9e/6d6b8295940b118e8b7083b29226c71f6154f7ff41e9ca431f03de2eac0d/coverage-7.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b", size = 246284, upload-time = "2025-09-21T20:03:42.355Z" }, + { url = "https://files.pythonhosted.org/packages/db/e5/5e957ca747d43dbe4d9714358375c7546cb3cb533007b6813fc20fce37ad/coverage-7.10.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3", size = 244353, upload-time = "2025-09-21T20:03:44.218Z" }, + { url = "https://files.pythonhosted.org/packages/9a/45/540fc5cc92536a1b783b7ef99450bd55a4b3af234aae35a18a339973ce30/coverage-7.10.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0", size = 244430, upload-time = "2025-09-21T20:03:46.065Z" }, + { url = "https://files.pythonhosted.org/packages/75/0b/8287b2e5b38c8fe15d7e3398849bb58d382aedc0864ea0fa1820e8630491/coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f", size = 245311, upload-time = "2025-09-21T20:03:48.19Z" }, + { url = "https://files.pythonhosted.org/packages/0c/1d/29724999984740f0c86d03e6420b942439bf5bd7f54d4382cae386a9d1e9/coverage-7.10.7-cp39-cp39-win32.whl", hash = "sha256:5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431", size = 220500, upload-time = "2025-09-21T20:03:50.024Z" }, + { url = "https://files.pythonhosted.org/packages/43/11/4b1e6b129943f905ca54c339f343877b55b365ae2558806c1be4f7476ed5/coverage-7.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07", size = 221408, upload-time = "2025-09-21T20:03:51.803Z" }, + { url = "https://files.pythonhosted.org/packages/ec/16/114df1c291c22cac3b0c127a73e0af5c12ed7bbb6558d310429a0ae24023/coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260", size = 209952, upload-time = "2025-09-21T20:03:53.918Z" }, ] [package.optional-dependencies] @@ -160,76 +207,78 @@ toml = [ [[package]] name = "distlib" -version = "0.3.9" +version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923, upload-time = "2024-10-09T18:35:47.551Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973, upload-time = "2024-10-09T18:35:44.272Z" }, + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, ] [[package]] name = "exceptiongroup" -version = "1.2.2" +version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883, upload-time = "2024-07-12T22:26:00.161Z" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453, upload-time = "2024-07-12T22:25:58.476Z" }, + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, ] [[package]] name = "filelock" -version = "3.16.1" +version = "3.19.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/db/3ef5bb276dae18d6ec2124224403d1d67bccdbefc17af4cc8f553e341ab1/filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435", size = 18037, upload-time = "2024-09-17T19:02:01.779Z" } +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/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163, upload-time = "2024-09-17T19:02:00.268Z" }, + { 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 = "h11" -version = "0.14.0" +version = "0.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418, upload-time = "2022-09-25T15:40:01.519Z" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259, upload-time = "2022-09-25T15:39:59.68Z" }, + { 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 = "httpcore" -version = "1.0.6" +version = "1.0.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b6/44/ed0fa6a17845fb033bd885c03e842f08c1b9406c86a2e60ac1ae1b9206a6/httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f", size = 85180, upload-time = "2024-10-01T17:02:00.094Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/89/b161908e2f51be56568184aeb4a880fd287178d176fd1c860d2217f41106/httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f", size = 78011, upload-time = "2024-10-01T17:01:58.811Z" }, + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, ] [[package]] name = "httpx" -version = "0.27.2" +version = "0.28.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "certifi" }, { name = "httpcore" }, { name = "idna" }, - { name = "sniffio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/82/08f8c936781f67d9e6b9eeb8a0c8b4e406136ea4c3d1f89a5db71d42e0e6/httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2", size = 144189, upload-time = "2024-08-27T12:54:01.334Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/95/9377bcb415797e44274b51d46e3249eba641711cf3348050f76ee7b15ffc/httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0", size = 76395, upload-time = "2024-08-27T12:53:59.653Z" }, + { 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 = "identify" -version = "2.6.1" +version = "2.6.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/bb/25024dbcc93516c492b75919e76f389bac754a3e4248682fba32b250c880/identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98", size = 99097, upload-time = "2024-09-14T23:50:32.513Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311, upload-time = "2025-10-02T17:43:40.631Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/0c/4ef72754c050979fdcc06c744715ae70ea37e734816bb6514f79df77a42f/identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0", size = 98972, upload-time = "2024-09-14T23:50:30.747Z" }, + { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183, upload-time = "2025-10-02T17:43:39.137Z" }, ] [[package]] @@ -243,11 +292,11 @@ wheels = [ [[package]] name = "iniconfig" -version = "2.0.0" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646, upload-time = "2023-01-07T11:08:11.254Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892, upload-time = "2023-01-07T11:08:09.864Z" }, + { 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]] @@ -304,43 +353,43 @@ wheels = [ [[package]] name = "packaging" -version = "24.1" +version = "25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788, upload-time = "2024-06-09T23:19:24.956Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985, upload-time = "2024-06-09T23:19:21.909Z" }, + { 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 = "phonenumbers" -version = "8.13.47" +version = "8.13.55" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/0c/8f315d5e6ddea2e45ae13ada6936df6240858929881daf20cb3133fdb729/phonenumbers-8.13.47.tar.gz", hash = "sha256:53c5e7c6d431cafe4efdd44956078404ae9bc8b0eacc47be3105d3ccc88aaffa", size = 2297081, upload-time = "2024-10-04T06:00:28.756Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/23/b4c886487ca212ca87768433a43e2b3099c1c2fa5d9e21d2fbce187cc3c2/phonenumbers-8.13.55.tar.gz", hash = "sha256:57c989dda3eabab1b5a9e3d24438a39ebd032fa0172bf68bfd90ab70b3d5e08b", size = 2296624, upload-time = "2025-02-15T08:06:03.465Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/0b/5cde445764ac72460748107e999b026b7245e3fcc5fd5551cc5aff45e469/phonenumbers-8.13.47-py2.py3-none-any.whl", hash = "sha256:5d3c0142ef7055ca5551884352e3b6b93bfe002a0bc95b8eaba39b0e2184541b", size = 2582530, upload-time = "2024-10-04T06:00:24.918Z" }, + { url = "https://files.pythonhosted.org/packages/50/dc/a7f0a9d5ad8b98bc5406deb00207b268d6d2edd215c21642e8f2ecc6f0ce/phonenumbers-8.13.55-py2.py3-none-any.whl", hash = "sha256:25feaf46135f0fb1e61b69513dc97c477285ba98a69204bf5a8cf241a844a718", size = 2582306, upload-time = "2025-02-15T08:05:56.746Z" }, ] [[package]] name = "platformdirs" -version = "4.3.6" +version = "4.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302, upload-time = "2024-09-17T19:06:50.688Z" } +sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439, upload-time = "2024-09-17T19:06:49.212Z" }, + { 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 = "pluggy" -version = "1.5.0" +version = "1.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload-time = "2024-04-20T21:34:42.531Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" }, + { 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 = "pre-commit" -version = "3.8.0" +version = "4.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cfgv" }, @@ -349,14 +398,23 @@ dependencies = [ { name = "pyyaml" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/64/10/97ee2fa54dff1e9da9badbc5e35d0bbaef0776271ea5907eccf64140f72f/pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af", size = 177815, upload-time = "2024-07-28T19:59:01.538Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792, upload-time = "2025-08-09T18:56:14.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965, upload-time = "2025-08-09T18:56:13.192Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/92/caae8c86e94681b42c246f0bca35c059a2f0529e5b92619f6aba4cf7e7b6/pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f", size = 204643, upload-time = "2024-07-28T19:58:59.335Z" }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] [[package]] name = "pytest" -version = "8.3.3" +version = "8.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -364,98 +422,122 @@ dependencies = [ { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, + { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487, upload-time = "2024-09-10T10:52:15.003Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341, upload-time = "2024-09-10T10:52:12.54Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, ] [[package]] name = "pytest-asyncio" -version = "0.24.0" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/6d/c6cf50ce320cf8611df7a1254d86233b3df7cc07f9b5f5cbcb82e08aa534/pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276", size = 49855, upload-time = "2024-08-22T08:03:18.145Z" } +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/96/31/6607dab48616902f76885dfcf62c08d929796fc3b2d2318faf9fd54dbed9/pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b", size = 18024, upload-time = "2024-08-22T08:03:15.536Z" }, + { 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]] name = "pytest-cov" -version = "5.0.0" +version = "7.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/67/00efc8d11b630c56f15f4ad9c7f9223f1e5ec275aaae3fa9118c6a223ad2/pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857", size = 63042, upload-time = "2024-03-24T20:16:34.856Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/3a/af5b4fa5961d9a1e6237b530eb87dd04aea6eb83da09d2a4073d81b54ccf/pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652", size = 21990, upload-time = "2024-03-24T20:16:32.444Z" }, + { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, ] [[package]] name = "python-dotenv" -version = "1.0.1" +version = "1.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115, upload-time = "2024-01-23T06:33:00.505Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863, upload-time = "2024-01-23T06:32:58.246Z" }, + { 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 = "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/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, - { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, - { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, - { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, - { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, - { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, - { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, - { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, - { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, - { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, - { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, - { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, - { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, - { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, - { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, - { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, - { 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/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777, upload-time = "2024-08-06T20:33:25.896Z" }, - { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318, upload-time = "2024-08-06T20:33:27.212Z" }, - { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891, upload-time = "2024-08-06T20:33:28.974Z" }, - { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614, upload-time = "2024-08-06T20:33:34.157Z" }, - { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360, upload-time = "2024-08-06T20:33:35.84Z" }, - { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006, upload-time = "2024-08-06T20:33:37.501Z" }, - { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577, upload-time = "2024-08-06T20:33:39.389Z" }, - { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593, upload-time = "2024-08-06T20:33:46.63Z" }, - { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312, upload-time = "2024-08-06T20:33:49.073Z" }, +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/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { 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" }, + { url = "https://files.pythonhosted.org/packages/9f/62/67fc8e68a75f738c9200422bf65693fb79a4cd0dc5b23310e5202e978090/pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da", size = 184450, upload-time = "2025-09-25T21:33:00.618Z" }, + { url = "https://files.pythonhosted.org/packages/ae/92/861f152ce87c452b11b9d0977952259aa7df792d71c1053365cc7b09cc08/pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917", size = 174319, upload-time = "2025-09-25T21:33:02.086Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cd/f0cfc8c74f8a030017a2b9c771b7f47e5dd702c3e28e5b2071374bda2948/pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9", size = 737631, upload-time = "2025-09-25T21:33:03.25Z" }, + { url = "https://files.pythonhosted.org/packages/ef/b2/18f2bd28cd2055a79a46c9b0895c0b3d987ce40ee471cecf58a1a0199805/pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5", size = 836795, upload-time = "2025-09-25T21:33:05.014Z" }, + { url = "https://files.pythonhosted.org/packages/73/b9/793686b2d54b531203c160ef12bec60228a0109c79bae6c1277961026770/pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a", size = 750767, upload-time = "2025-09-25T21:33:06.398Z" }, + { url = "https://files.pythonhosted.org/packages/a9/86/a137b39a611def2ed78b0e66ce2fe13ee701a07c07aebe55c340ed2a050e/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926", size = 727982, upload-time = "2025-09-25T21:33:08.708Z" }, + { url = "https://files.pythonhosted.org/packages/dd/62/71c27c94f457cf4418ef8ccc71735324c549f7e3ea9d34aba50874563561/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7", size = 755677, upload-time = "2025-09-25T21:33:09.876Z" }, + { url = "https://files.pythonhosted.org/packages/29/3d/6f5e0d58bd924fb0d06c3a6bad00effbdae2de5adb5cda5648006ffbd8d3/pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0", size = 142592, upload-time = "2025-09-25T21:33:10.983Z" }, + { url = "https://files.pythonhosted.org/packages/f0/0c/25113e0b5e103d7f1490c0e947e303fe4a696c10b501dea7a9f49d4e876c/pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007", size = 158777, upload-time = "2025-09-25T21:33:15.55Z" }, ] [[package]] @@ -469,32 +551,63 @@ wheels = [ [[package]] name = "tomli" -version = "2.0.2" +version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/b9/de2a5c0144d7d75a57ff355c0c24054f965b2dc3036456ae03a51ea6264b/tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed", size = 16096, upload-time = "2024-10-02T10:46:13.208Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/db/ce8eda256fa131af12e0a76d481711abe4681b6923c27efb9a255c9e4594/tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38", size = 13237, upload-time = "2024-10-02T10:46:11.806Z" }, + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, ] [[package]] name = "typing-extensions" -version = "4.12.2" +version = "4.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321, upload-time = "2024-06-07T18:52:15.995Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438, upload-time = "2024-06-07T18:52:13.582Z" }, + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] [[package]] name = "virtualenv" -version = "20.26.6" +version = "20.34.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/40/abc5a766da6b0b2457f819feab8e9203cbeae29327bd241359f866a3da9d/virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48", size = 9372482, upload-time = "2024-09-27T16:28:57.502Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/14/37fcdba2808a6c615681cd216fecae00413c9dab44fb2e57805ecf3eaee3/virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a", size = 6003808, upload-time = "2025-08-13T14:24:07.464Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/90/57b8ac0c8a231545adc7698c64c5a36fa7cd8e376c691b9bde877269f2eb/virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2", size = 5999862, upload-time = "2024-09-27T16:28:54.798Z" }, + { url = "https://files.pythonhosted.org/packages/76/06/04c8e804f813cf972e3262f3f8584c232de64f0cde9f703b46cf53a45090/virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026", size = 5983279, upload-time = "2025-08-13T14:24:05.111Z" }, ] From 5b7241f27c48e2a3eef7af91c9b426a5bd42a10f Mon Sep 17 00:00:00 2001 From: Lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Mon, 6 Oct 2025 22:11:11 +0200 Subject: [PATCH 18/38] Bump phonenumbers to v9 --- pyproject.toml | 2 +- uv.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cdbe471..66af88e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ classifiers = [ dependencies = [ "httpx>=0.27.2", "msgspec>=0.19.0", - "phonenumbers>=8.13.47,<9.0.0", + "phonenumbers>=9.0.15,<10.0.0", "typing-extensions>=4.12.2,<5.0.0", ] diff --git a/uv.lock b/uv.lock index e5237e1..ba86cc5 100644 --- a/uv.lock +++ b/uv.lock @@ -76,7 +76,7 @@ dev = [ requires-dist = [ { name = "httpx", specifier = ">=0.27.2" }, { name = "msgspec", specifier = ">=0.19.0" }, - { name = "phonenumbers", specifier = ">=8.13.47,<9.0.0" }, + { name = "phonenumbers", specifier = ">=9.0.15,<10.0.0" }, { name = "typing-extensions", specifier = ">=4.12.2,<5.0.0" }, ] @@ -362,11 +362,11 @@ wheels = [ [[package]] name = "phonenumbers" -version = "8.13.55" +version = "9.0.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6e/23/b4c886487ca212ca87768433a43e2b3099c1c2fa5d9e21d2fbce187cc3c2/phonenumbers-8.13.55.tar.gz", hash = "sha256:57c989dda3eabab1b5a9e3d24438a39ebd032fa0172bf68bfd90ab70b3d5e08b", size = 2296624, upload-time = "2025-02-15T08:06:03.465Z" } +sdist = { url = "https://files.pythonhosted.org/packages/14/8e/755749f61b93c39493730428cbfe14897198c0151e8b6d832f0a3c71ff16/phonenumbers-9.0.15.tar.gz", hash = "sha256:345ff7f23768332d866f37732f815cdf1d33c7f0961246562a5c5b78c12c3ff3", size = 2298105, upload-time = "2025-09-26T05:32:39.052Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/dc/a7f0a9d5ad8b98bc5406deb00207b268d6d2edd215c21642e8f2ecc6f0ce/phonenumbers-8.13.55-py2.py3-none-any.whl", hash = "sha256:25feaf46135f0fb1e61b69513dc97c477285ba98a69204bf5a8cf241a844a718", size = 2582306, upload-time = "2025-02-15T08:05:56.746Z" }, + { url = "https://files.pythonhosted.org/packages/f1/52/e9bbc1b092bb17b86f56da84a36ca98320330a1904d9b0ca3d685155db2c/phonenumbers-9.0.15-py2.py3-none-any.whl", hash = "sha256:269b73bc05258e8fd57582770b9559307099ea677c8f1dc5272476f661344776", size = 2583916, upload-time = "2025-09-26T05:32:35.736Z" }, ] [[package]] From 9458053074777fb8273157180425f5afd650c87f Mon Sep 17 00:00:00 2001 From: Lemonyte Date: Mon, 13 Oct 2025 10:18:58 +0200 Subject: [PATCH 19/38] add imessage read and history endpoints --- src/contiguity/imessage.py | 94 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/src/contiguity/imessage.py b/src/contiguity/imessage.py index b041835..b6af7c9 100644 --- a/src/contiguity/imessage.py +++ b/src/contiguity/imessage.py @@ -1,11 +1,105 @@ +import logging from typing import Literal +from msgspec import Struct + from ._instant_messaging import InstantMessagingClient +from ._response import decode_response FallbackCause = Literal["imessage_unsupported", "imessage_fails"] +logger = logging.getLogger(__name__) + + +class ReadResponse(Struct): + status: str # possible values? + """Status of the read receipt.""" + message: str + """Confirmation message.""" + + +class MessageDelivery(Struct): + status: Literal["delivered", "failed", "delayed"] + delayed: bool + timestamp: int + """When the message was delivered.""" + + +class MessageRisk(Struct): + auto_reported_as_spam: bool + """Whether the message was automatically labeled as spam by Apple.""" + marked_as_spam: bool + """Whether the message was marked as spam by the user.""" + + +class HistoryMessage(Struct): + to: str + from_: int + message: str + """Message content.""" + timestamp: int + """When the message was sent.""" + read: int + """When the message was read.""" + delivery: MessageDelivery + risk: MessageRisk + attachments: list[str] + """URLs of the attachments.""" + + +class HistoryReaction(Struct): + reaction: str + on: str + """Message content.""" + timestamp: int + action: str # possible values? + from_: str + + +class HistoryChat(Struct): + filtered: bool + """Whether the conversation was filtered by Apple.""" + chat_marked_as_spam: bool + """Whether the chat was automatically labeled as spam by Apple.""" + limit: int + """Message return limit.""" + total: int + """Total number of messages in the conversation.""" + count: int + """Number of messages returned.""" + + +class History(Struct): + conversation: list[HistoryMessage] + reactions: list[HistoryReaction] + chat: HistoryChat + class IMessage(InstantMessagingClient[FallbackCause]): @property def _api_path(self) -> str: return "/imessage" + + def mark_read(self, *, to: str, from_: str) -> ReadResponse: + payload = { + "to": to, + "from": from_, + } + + response = self._client.post( + f"{self._api_path}/read", + json={k: v for k, v in payload.items() if v is not None}, + ) + + self._client.handle_error(response, fail_message="failed to send instant message") + data = decode_response(response.content, type=ReadResponse) + logger.debug("successfully sent %s read receipt to %r", self._api_path[1:], to) + return data + + def get_history(self, *, to: str, from_: str, limit: int = 20) -> History: + response = self._client.post( + f"/history/{self._api_path}/{to}/{from_}/{limit}", + ) + + self._client.handle_error(response, fail_message="failed to get message history") + return decode_response(response.content, type=History) From e91b586dd9ed3448d89a52bd2dbac5527d42a00f Mon Sep 17 00:00:00 2001 From: Lemonyte Date: Mon, 13 Oct 2025 10:32:48 +0200 Subject: [PATCH 20/38] Bump minimum python version to 3.10 --- pyproject.toml | 2 +- uv.lock | 140 +++++++++++++++++++++---------------------------- 2 files changed, 62 insertions(+), 80 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 66af88e..a172b88 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ dynamic = ["version"] description = "Contiguity's official Python SDK" readme = "README.md" license = {file = "LICENSE.txt"} -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ {name = "Contiguity", email = "help@contiguity.support"}, ] diff --git a/uv.lock b/uv.lock index ba86cc5..d63f079 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,6 @@ version = 1 revision = 3 -requires-python = ">=3.9" +requires-python = ">=3.10" [[package]] name = "anyio" @@ -185,18 +185,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b2/dc/101f3fa3a45146db0cb03f5b4376e24c0aac818309da23e2de0c75295a91/coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235", size = 221784, upload-time = "2025-09-21T20:03:24.769Z" }, { url = "https://files.pythonhosted.org/packages/4c/a1/74c51803fc70a8a40d7346660379e144be772bab4ac7bb6e6b905152345c/coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d", size = 222905, upload-time = "2025-09-21T20:03:26.93Z" }, { url = "https://files.pythonhosted.org/packages/12/65/f116a6d2127df30bcafbceef0302d8a64ba87488bf6f73a6d8eebf060873/coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a", size = 220922, upload-time = "2025-09-21T20:03:28.672Z" }, - { url = "https://files.pythonhosted.org/packages/a3/ad/d1c25053764b4c42eb294aae92ab617d2e4f803397f9c7c8295caa77a260/coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3", size = 217978, upload-time = "2025-09-21T20:03:30.362Z" }, - { url = "https://files.pythonhosted.org/packages/52/2f/b9f9daa39b80ece0b9548bbb723381e29bc664822d9a12c2135f8922c22b/coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c", size = 218370, upload-time = "2025-09-21T20:03:32.147Z" }, - { url = "https://files.pythonhosted.org/packages/dd/6e/30d006c3b469e58449650642383dddf1c8fb63d44fdf92994bfd46570695/coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396", size = 244802, upload-time = "2025-09-21T20:03:33.919Z" }, - { url = "https://files.pythonhosted.org/packages/b0/49/8a070782ce7e6b94ff6a0b6d7c65ba6bc3091d92a92cef4cd4eb0767965c/coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40", size = 246625, upload-time = "2025-09-21T20:03:36.09Z" }, - { url = "https://files.pythonhosted.org/packages/6a/92/1c1c5a9e8677ce56d42b97bdaca337b2d4d9ebe703d8c174ede52dbabd5f/coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594", size = 248399, upload-time = "2025-09-21T20:03:38.342Z" }, - { url = "https://files.pythonhosted.org/packages/c0/54/b140edee7257e815de7426d5d9846b58505dffc29795fff2dfb7f8a1c5a0/coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a", size = 245142, upload-time = "2025-09-21T20:03:40.591Z" }, - { url = "https://files.pythonhosted.org/packages/e4/9e/6d6b8295940b118e8b7083b29226c71f6154f7ff41e9ca431f03de2eac0d/coverage-7.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b", size = 246284, upload-time = "2025-09-21T20:03:42.355Z" }, - { url = "https://files.pythonhosted.org/packages/db/e5/5e957ca747d43dbe4d9714358375c7546cb3cb533007b6813fc20fce37ad/coverage-7.10.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3", size = 244353, upload-time = "2025-09-21T20:03:44.218Z" }, - { url = "https://files.pythonhosted.org/packages/9a/45/540fc5cc92536a1b783b7ef99450bd55a4b3af234aae35a18a339973ce30/coverage-7.10.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0", size = 244430, upload-time = "2025-09-21T20:03:46.065Z" }, - { url = "https://files.pythonhosted.org/packages/75/0b/8287b2e5b38c8fe15d7e3398849bb58d382aedc0864ea0fa1820e8630491/coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f", size = 245311, upload-time = "2025-09-21T20:03:48.19Z" }, - { url = "https://files.pythonhosted.org/packages/0c/1d/29724999984740f0c86d03e6420b942439bf5bd7f54d4382cae386a9d1e9/coverage-7.10.7-cp39-cp39-win32.whl", hash = "sha256:5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431", size = 220500, upload-time = "2025-09-21T20:03:50.024Z" }, - { url = "https://files.pythonhosted.org/packages/43/11/4b1e6b129943f905ca54c339f343877b55b365ae2558806c1be4f7476ed5/coverage-7.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07", size = 221408, upload-time = "2025-09-21T20:03:51.803Z" }, { url = "https://files.pythonhosted.org/packages/ec/16/114df1c291c22cac3b0c127a73e0af5c12ed7bbb6558d310429a0ae24023/coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260", size = 209952, upload-time = "2025-09-21T20:03:53.918Z" }, ] @@ -228,11 +216,11 @@ wheels = [ [[package]] name = "filelock" -version = "3.19.1" +version = "3.20.0" 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" } +sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" }, ] [[package]] @@ -283,11 +271,11 @@ wheels = [ [[package]] name = "idna" -version = "3.10" +version = "3.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] [[package]] @@ -333,13 +321,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/53/2f/2b1c2b056894fbaa975f68f81e3014bb447516a8b010f1bed3fb0e016ed7/msgspec-0.19.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac7f7c377c122b649f7545810c6cd1b47586e3aa3059126ce3516ac7ccc6a6a9", size = 213996, upload-time = "2024-12-27T17:40:12.244Z" }, { url = "https://files.pythonhosted.org/packages/aa/5a/4cd408d90d1417e8d2ce6a22b98a6853c1b4d7cb7669153e4424d60087f6/msgspec-0.19.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5bc1472223a643f5ffb5bf46ccdede7f9795078194f14edd69e3aab7020d327", size = 219087, upload-time = "2024-12-27T17:40:14.881Z" }, { url = "https://files.pythonhosted.org/packages/23/d8/f15b40611c2d5753d1abb0ca0da0c75348daf1252220e5dda2867bd81062/msgspec-0.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:317050bc0f7739cb30d257ff09152ca309bf5a369854bbf1e57dffc310c1f20f", size = 187432, upload-time = "2024-12-27T17:40:16.256Z" }, - { url = "https://files.pythonhosted.org/packages/ea/d0/323f867eaec1f2236ba30adf613777b1c97a7e8698e2e881656b21871fa4/msgspec-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15c1e86fff77184c20a2932cd9742bf33fe23125fa3fcf332df9ad2f7d483044", size = 189926, upload-time = "2024-12-27T17:40:18.939Z" }, - { url = "https://files.pythonhosted.org/packages/a8/37/c3e1b39bdae90a7258d77959f5f5e36ad44b40e2be91cff83eea33c54d43/msgspec-0.19.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3b5541b2b3294e5ffabe31a09d604e23a88533ace36ac288fa32a420aa38d229", size = 183873, upload-time = "2024-12-27T17:40:20.214Z" }, - { url = "https://files.pythonhosted.org/packages/cb/a2/48f2c15c7644668e51f4dce99d5f709bd55314e47acb02e90682f5880f35/msgspec-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f5c043ace7962ef188746e83b99faaa9e3e699ab857ca3f367b309c8e2c6b12", size = 209272, upload-time = "2024-12-27T17:40:21.534Z" }, - { url = "https://files.pythonhosted.org/packages/25/3c/aa339cf08b990c3f07e67b229a3a8aa31bf129ed974b35e5daa0df7d9d56/msgspec-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca06aa08e39bf57e39a258e1996474f84d0dd8130d486c00bec26d797b8c5446", size = 211396, upload-time = "2024-12-27T17:40:22.897Z" }, - { url = "https://files.pythonhosted.org/packages/c7/00/c7fb9d524327c558b2803973cc3f988c5100a1708879970a9e377bdf6f4f/msgspec-0.19.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e695dad6897896e9384cf5e2687d9ae9feaef50e802f93602d35458e20d1fb19", size = 215002, upload-time = "2024-12-27T17:40:24.341Z" }, - { url = "https://files.pythonhosted.org/packages/3f/bf/d9f9fff026c1248cde84a5ce62b3742e8a63a3c4e811f99f00c8babf7615/msgspec-0.19.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3be5c02e1fee57b54130316a08fe40cca53af92999a302a6054cd451700ea7db", size = 218132, upload-time = "2024-12-27T17:40:25.744Z" }, - { url = "https://files.pythonhosted.org/packages/00/03/b92011210f79794958167a3a3ea64a71135d9a2034cfb7597b545a42606d/msgspec-0.19.0-cp39-cp39-win_amd64.whl", hash = "sha256:0684573a821be3c749912acf5848cce78af4298345cb2d7a8b8948a0a5a27cfe", size = 186301, upload-time = "2024-12-27T17:40:27.076Z" }, ] [[package]] @@ -362,20 +343,20 @@ wheels = [ [[package]] name = "phonenumbers" -version = "9.0.15" +version = "9.0.16" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/14/8e/755749f61b93c39493730428cbfe14897198c0151e8b6d832f0a3c71ff16/phonenumbers-9.0.15.tar.gz", hash = "sha256:345ff7f23768332d866f37732f815cdf1d33c7f0961246562a5c5b78c12c3ff3", size = 2298105, upload-time = "2025-09-26T05:32:39.052Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/cd/b40e0fef007e969e72005fe38598f28fa9c8b3ab61515bac426d039a197d/phonenumbers-9.0.16.tar.gz", hash = "sha256:4002542d987c453b333b54450a9f60a280ed7ec932afd7d5fa8dbe6c2379f10a", size = 2298224, upload-time = "2025-10-10T12:04:24.498Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/52/e9bbc1b092bb17b86f56da84a36ca98320330a1904d9b0ca3d685155db2c/phonenumbers-9.0.15-py2.py3-none-any.whl", hash = "sha256:269b73bc05258e8fd57582770b9559307099ea677c8f1dc5272476f661344776", size = 2583916, upload-time = "2025-09-26T05:32:35.736Z" }, + { url = "https://files.pythonhosted.org/packages/52/99/506c181b050a7da77a0ee9558a66693f7c4c8efd13716468d6440fb04154/phonenumbers-9.0.16-py2.py3-none-any.whl", hash = "sha256:3c019f10305c311005672f3a0cea4139fef0b949319a60635ee803cfaef4a2e2", size = 2584075, upload-time = "2025-10-10T12:04:20.436Z" }, ] [[package]] name = "platformdirs" -version = "4.4.0" +version = "4.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" }, ] [[package]] @@ -529,15 +510,6 @@ wheels = [ { 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" }, - { url = "https://files.pythonhosted.org/packages/9f/62/67fc8e68a75f738c9200422bf65693fb79a4cd0dc5b23310e5202e978090/pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da", size = 184450, upload-time = "2025-09-25T21:33:00.618Z" }, - { url = "https://files.pythonhosted.org/packages/ae/92/861f152ce87c452b11b9d0977952259aa7df792d71c1053365cc7b09cc08/pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917", size = 174319, upload-time = "2025-09-25T21:33:02.086Z" }, - { url = "https://files.pythonhosted.org/packages/d0/cd/f0cfc8c74f8a030017a2b9c771b7f47e5dd702c3e28e5b2071374bda2948/pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9", size = 737631, upload-time = "2025-09-25T21:33:03.25Z" }, - { url = "https://files.pythonhosted.org/packages/ef/b2/18f2bd28cd2055a79a46c9b0895c0b3d987ce40ee471cecf58a1a0199805/pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5", size = 836795, upload-time = "2025-09-25T21:33:05.014Z" }, - { url = "https://files.pythonhosted.org/packages/73/b9/793686b2d54b531203c160ef12bec60228a0109c79bae6c1277961026770/pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a", size = 750767, upload-time = "2025-09-25T21:33:06.398Z" }, - { url = "https://files.pythonhosted.org/packages/a9/86/a137b39a611def2ed78b0e66ce2fe13ee701a07c07aebe55c340ed2a050e/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926", size = 727982, upload-time = "2025-09-25T21:33:08.708Z" }, - { url = "https://files.pythonhosted.org/packages/dd/62/71c27c94f457cf4418ef8ccc71735324c549f7e3ea9d34aba50874563561/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7", size = 755677, upload-time = "2025-09-25T21:33:09.876Z" }, - { url = "https://files.pythonhosted.org/packages/29/3d/6f5e0d58bd924fb0d06c3a6bad00effbdae2de5adb5cda5648006ffbd8d3/pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0", size = 142592, upload-time = "2025-09-25T21:33:10.983Z" }, - { url = "https://files.pythonhosted.org/packages/f0/0c/25113e0b5e103d7f1490c0e947e303fe4a696c10b501dea7a9f49d4e876c/pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007", size = 158777, upload-time = "2025-09-25T21:33:15.55Z" }, ] [[package]] @@ -551,41 +523,51 @@ wheels = [ [[package]] name = "tomli" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, - { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, - { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, - { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, - { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, - { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, - { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, - { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, - { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, - { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, - { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, - { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, - { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, - { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, - { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, - { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, - { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, - { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, - { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, - { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, - { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, - { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, - { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, - { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, - { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" }, + { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" }, + { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" }, + { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" }, + { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" }, + { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" }, + { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" }, + { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" }, + { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" }, + { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" }, + { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" }, + { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" }, + { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" }, + { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" }, + { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" }, + { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" }, + { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" }, + { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" }, + { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" }, + { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" }, + { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" }, + { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" }, + { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" }, + { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" }, + { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" }, + { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" }, + { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" }, + { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" }, + { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" }, + { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" }, + { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" }, + { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" }, + { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" }, + { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" }, + { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" }, + { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" }, + { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" }, + { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, ] [[package]] @@ -599,7 +581,7 @@ wheels = [ [[package]] name = "virtualenv" -version = "20.34.0" +version = "20.35.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, @@ -607,7 +589,7 @@ dependencies = [ { name = "platformdirs" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1c/14/37fcdba2808a6c615681cd216fecae00413c9dab44fb2e57805ecf3eaee3/virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a", size = 6003808, upload-time = "2025-08-13T14:24:07.464Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a4/d5/b0ccd381d55c8f45d46f77df6ae59fbc23d19e901e2d523395598e5f4c93/virtualenv-20.35.3.tar.gz", hash = "sha256:4f1a845d131133bdff10590489610c98c168ff99dc75d6c96853801f7f67af44", size = 6002907, upload-time = "2025-10-10T21:23:33.178Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/06/04c8e804f813cf972e3262f3f8584c232de64f0cde9f703b46cf53a45090/virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026", size = 5983279, upload-time = "2025-08-13T14:24:05.111Z" }, + { url = "https://files.pythonhosted.org/packages/27/73/d9a94da0e9d470a543c1b9d3ccbceb0f59455983088e727b8a1824ed90fb/virtualenv-20.35.3-py3-none-any.whl", hash = "sha256:63d106565078d8c8d0b206d48080f938a8b25361e19432d2c9db40d2899c810a", size = 5981061, upload-time = "2025-10-10T21:23:30.433Z" }, ] From 8bdd44542e2973189a32c1001f910a98c3b88cd2 Mon Sep 17 00:00:00 2001 From: Lemonyte Date: Mon, 13 Oct 2025 14:04:32 +0200 Subject: [PATCH 21/38] Remove usage of future annotations --- pyproject.toml | 2 +- src/contiguity/_auth.py | 2 -- src/contiguity/_client.py | 6 ++-- src/contiguity/_instant_messaging.py | 8 ++--- src/contiguity/_response.py | 4 +-- src/contiguity/base/async_base.py | 45 +++++++++++-------------- src/contiguity/base/base.py | 45 +++++++++++-------------- src/contiguity/base/common.py | 17 ++++------ src/contiguity/email.py | 8 ++--- src/contiguity/leases.py | 2 -- src/contiguity/otp.py | 2 -- tests/base/test_async_base_typeddict.py | 9 ++--- tests/base/test_base_typeddict.py | 9 ++--- 13 files changed, 60 insertions(+), 99 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a172b88..ae60123 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,7 +61,7 @@ version = {attr = "contiguity.__version__"} [tool.ruff] src = ["src"] line-length = 119 -target-version = "py39" +target-version = "py310" [tool.ruff.lint] select = ["ALL"] diff --git a/src/contiguity/_auth.py b/src/contiguity/_auth.py index 8e533e4..7c51727 100644 --- a/src/contiguity/_auth.py +++ b/src/contiguity/_auth.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import os diff --git a/src/contiguity/_client.py b/src/contiguity/_client.py index 229c468..202a346 100644 --- a/src/contiguity/_client.py +++ b/src/contiguity/_client.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from http import HTTPStatus from httpx import AsyncClient as HttpxAsyncClient @@ -24,7 +22,7 @@ def handle_error(self, response: Response, /, *, fail_message: str = "api reques class ApiClient(HttpxClient, BaseApiClient): def __init__( - self: ApiClient, + self: "ApiClient", *, base_url: str = "https://api.contiguity.com", api_key: str | None = None, @@ -44,7 +42,7 @@ def __init__( class AsyncApiClient(HttpxAsyncClient, BaseApiClient): def __init__( - self: AsyncApiClient, + self: "AsyncApiClient", *, base_url: str = "https://api.contiguity.com", api_key: str | None = None, diff --git a/src/contiguity/_instant_messaging.py b/src/contiguity/_instant_messaging.py index 5bd7c64..a56b6c7 100644 --- a/src/contiguity/_instant_messaging.py +++ b/src/contiguity/_instant_messaging.py @@ -1,15 +1,11 @@ -from __future__ import annotations - import logging from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Generic, Literal, TypeVar +from collections.abc import Sequence +from typing import Generic, Literal, TypeVar from ._product import BaseProduct from ._response import BaseResponse, decode_response -if TYPE_CHECKING: - from collections.abc import Sequence - FallbackCauseT = TypeVar("FallbackCauseT", bound=str) logger = logging.getLogger(__name__) diff --git a/src/contiguity/_response.py b/src/contiguity/_response.py index 30d82a5..05a8023 100644 --- a/src/contiguity/_response.py +++ b/src/contiguity/_response.py @@ -1,10 +1,10 @@ from collections.abc import Sequence from http import HTTPStatus -from typing import Generic, TypeVar, Union +from typing import Generic, TypeVar import msgspec -T = TypeVar("T", bound=Union[msgspec.Struct, Sequence[msgspec.Struct]]) +T = TypeVar("T", bound=msgspec.Struct | Sequence[msgspec.Struct]) class ResponseMetadata(msgspec.Struct): diff --git a/src/contiguity/base/async_base.py b/src/contiguity/base/async_base.py index 15a359d..3bcc2bb 100644 --- a/src/contiguity/base/async_base.py +++ b/src/contiguity/base/async_base.py @@ -1,15 +1,14 @@ -from __future__ import annotations - import json import os from collections.abc import Mapping, Sequence from datetime import datetime, timedelta, timezone from http import HTTPStatus -from typing import TYPE_CHECKING, Generic, Literal, overload +from typing import Generic, Literal, overload from warnings import warn import msgspec from httpx import HTTPStatusError +from httpx import Response as HttpxResponse from typing_extensions import deprecated from contiguity._auth import get_data_key, get_project_id @@ -31,10 +30,6 @@ ) from .exceptions import ItemConflictError, ItemNotFoundError -if TYPE_CHECKING: - from httpx import Response as HttpxResponse - from typing_extensions import Self - class AsyncBase(Generic[ItemT]): EXPIRES_ATTRIBUTE = "__expires" @@ -42,7 +37,7 @@ class AsyncBase(Generic[ItemT]): @overload def __init__( - self: Self, + self, name: str, /, *, @@ -57,7 +52,7 @@ def __init__( @overload @deprecated("The `project_key` parameter has been renamed to `data_key`.") def __init__( - self: Self, + self, name: str, /, *, @@ -70,7 +65,7 @@ def __init__( ) -> None: ... def __init__( # noqa: PLR0913 - self: Self, + self, name: str, /, *, @@ -102,7 +97,7 @@ def __init__( # noqa: PLR0913 @overload def _response_as_item_type( - self: Self, + self, response: HttpxResponse, /, *, @@ -110,7 +105,7 @@ def _response_as_item_type( ) -> ItemT: ... @overload def _response_as_item_type( - self: Self, + self, response: HttpxResponse, /, *, @@ -118,7 +113,7 @@ def _response_as_item_type( ) -> Sequence[ItemT]: ... def _response_as_item_type( - self: Self, + self, response: HttpxResponse, /, *, @@ -133,7 +128,7 @@ def _response_as_item_type( return response.json(cls=self.json_decoder) def _insert_expires_attr( - self: Self, + self, item: ItemT | Mapping[str, DataType], expire_in: int | None = None, expire_at: TimestampType | None = None, @@ -158,16 +153,16 @@ def _insert_expires_attr( return item_dict @overload - async def get(self: Self, key: str, /) -> ItemT | None: ... + async def get(self, key: str, /) -> ItemT | None: ... @overload - async def get(self: Self, key: str, /, *, default: ItemT) -> ItemT: ... + async def get(self, key: str, /, *, default: ItemT) -> ItemT: ... @overload - async def get(self: Self, key: str, /, *, default: DefaultItemT) -> ItemT | DefaultItemT: ... + async def get(self, key: str, /, *, default: DefaultItemT) -> ItemT | DefaultItemT: ... async def get( - self: Self, + self, key: str, /, *, @@ -187,7 +182,7 @@ async def get( return self._response_as_item_type(response, sequence=False) - async def delete(self: Self, key: str, /) -> None: + async def delete(self, key: str, /) -> None: """Delete an item from the Base.""" key = check_key(key) response = await self._client.delete(f"/items/{key}") @@ -197,7 +192,7 @@ async def delete(self: Self, key: str, /) -> None: raise ContiguityApiError(exc.response.text) from exc async def insert( - self: Self, + self, item: ItemT, /, *, @@ -216,7 +211,7 @@ async def insert( return returned_item[0] async def put( - self: Self, + self, *items: ItemT, expire_in: int | None = None, expire_at: TimestampType | None = None, @@ -237,7 +232,7 @@ async def put( @deprecated("This method will be removed in a future release. You can pass multiple items to `put`.") async def put_many( - self: Self, + self, items: Sequence[ItemT], /, *, @@ -247,7 +242,7 @@ async def put_many( return await self.put(*items, expire_in=expire_in, expire_at=expire_at) async def update( - self: Self, + self, updates: Mapping[str, DataType | UpdateOperation], /, *, @@ -278,7 +273,7 @@ async def update( return self._response_as_item_type(response, sequence=False) async def query( - self: Self, + self, *queries: QueryType, limit: int = 1000, last: str | None = None, @@ -310,7 +305,7 @@ async def query( @deprecated("This method has been renamed to `query` and will be removed in a future release.") async def fetch( - self: Self, + self, *queries: QueryType, limit: int = 1000, last: str | None = None, diff --git a/src/contiguity/base/base.py b/src/contiguity/base/base.py index 913547a..41d96ad 100644 --- a/src/contiguity/base/base.py +++ b/src/contiguity/base/base.py @@ -7,18 +7,17 @@ # - [ ] add async # - [ ] add drive support -from __future__ import annotations - import json import os from collections.abc import Mapping, Sequence from datetime import datetime, timedelta, timezone from http import HTTPStatus -from typing import TYPE_CHECKING, Generic, Literal, overload +from typing import Generic, Literal, overload from warnings import warn import msgspec from httpx import HTTPStatusError +from httpx import Response as HttpxResponse from typing_extensions import deprecated from contiguity._auth import get_data_key, get_project_id @@ -40,10 +39,6 @@ ) from .exceptions import ItemConflictError, ItemNotFoundError -if TYPE_CHECKING: - from httpx import Response as HttpxResponse - from typing_extensions import Self - class Base(Generic[ItemT]): EXPIRES_ATTRIBUTE = "__expires" @@ -51,7 +46,7 @@ class Base(Generic[ItemT]): @overload def __init__( - self: Self, + self, name: str, /, *, @@ -66,7 +61,7 @@ def __init__( @overload @deprecated("The `project_key` parameter has been renamed to `data_key`.") def __init__( - self: Self, + self, name: str, /, *, @@ -79,7 +74,7 @@ def __init__( ) -> None: ... def __init__( # noqa: PLR0913 - self: Self, + self, name: str, /, *, @@ -111,7 +106,7 @@ def __init__( # noqa: PLR0913 @overload def _response_as_item_type( - self: Self, + self, response: HttpxResponse, /, *, @@ -119,7 +114,7 @@ def _response_as_item_type( ) -> ItemT: ... @overload def _response_as_item_type( - self: Self, + self, response: HttpxResponse, /, *, @@ -127,7 +122,7 @@ def _response_as_item_type( ) -> Sequence[ItemT]: ... def _response_as_item_type( - self: Self, + self, response: HttpxResponse, /, *, @@ -142,7 +137,7 @@ def _response_as_item_type( return response.json(cls=self.json_decoder) def _insert_expires_attr( - self: Self, + self, item: ItemT | Mapping[str, DataType], expire_in: int | None = None, expire_at: TimestampType | None = None, @@ -167,16 +162,16 @@ def _insert_expires_attr( return item_dict @overload - def get(self: Self, key: str, /) -> ItemT | None: ... + def get(self, key: str, /) -> ItemT | None: ... @overload - def get(self: Self, key: str, /, *, default: ItemT) -> ItemT: ... + def get(self, key: str, /, *, default: ItemT) -> ItemT: ... @overload - def get(self: Self, key: str, /, *, default: DefaultItemT) -> ItemT | DefaultItemT: ... + def get(self, key: str, /, *, default: DefaultItemT) -> ItemT | DefaultItemT: ... def get( - self: Self, + self, key: str, /, *, @@ -196,7 +191,7 @@ def get( return self._response_as_item_type(response, sequence=False) - def delete(self: Self, key: str, /) -> None: + def delete(self, key: str, /) -> None: """Delete an item from the Base.""" key = check_key(key) response = self._client.delete(f"/items/{key}") @@ -206,7 +201,7 @@ def delete(self: Self, key: str, /) -> None: raise ContiguityApiError(exc.response.text) from exc def insert( - self: Self, + self, item: ItemT, /, *, @@ -225,7 +220,7 @@ def insert( return returned_item[0] def put( - self: Self, + self, *items: ItemT, expire_in: int | None = None, expire_at: TimestampType | None = None, @@ -246,7 +241,7 @@ def put( @deprecated("This method will be removed in a future release. You can pass multiple items to `put`.") def put_many( - self: Self, + self, items: Sequence[ItemT], /, *, @@ -256,7 +251,7 @@ def put_many( return self.put(*items, expire_in=expire_in, expire_at=expire_at) def update( - self: Self, + self, updates: Mapping[str, DataType | UpdateOperation], /, *, @@ -287,7 +282,7 @@ def update( return self._response_as_item_type(response, sequence=False) def query( - self: Self, + self, *queries: QueryType, limit: int = 1000, last: str | None = None, @@ -319,7 +314,7 @@ def query( @deprecated("This method has been renamed to `query` and will be removed in a future release.") def fetch( - self: Self, + self, *queries: QueryType, limit: int = 1000, last: str | None = None, diff --git a/src/contiguity/base/common.py b/src/contiguity/base/common.py index e95ff50..43d44f5 100644 --- a/src/contiguity/base/common.py +++ b/src/contiguity/base/common.py @@ -1,20 +1,17 @@ -from __future__ import annotations - from collections.abc import Mapping, Sequence from datetime import datetime from typing import Any, Generic, TypeVar, Union from urllib.parse import quote from msgspec import Struct -from typing_extensions import Self from .exceptions import InvalidKeyError -DataType = Union[str, int, float, bool, None, Sequence["DataType"], Mapping[str, "DataType"]] -TimestampType = Union[int, datetime] +DataType = str | int | float | bool | None | Sequence["DataType"] | Mapping[str, "DataType"] +TimestampType = int | datetime QueryType = Mapping[str, DataType] -ItemType = Union[Mapping[str, Any], Struct] +ItemType = Mapping[str, Any] | Struct ItemT = TypeVar("ItemT", bound=ItemType) DefaultItemT = TypeVar("DefaultItemT") @@ -45,13 +42,13 @@ class Trim(UpdateOperation): class Increment(UpdateOperation): - def __init__(self: Increment, value: int = 1, /) -> None: + def __init__(self, value: int = 1, /) -> None: self.value = value class Append(UpdateOperation): - def __init__(self: Append, value: DataType, /) -> None: - if isinstance(value, (list, tuple)): + def __init__(self, value: DataType, /) -> None: + if isinstance(value, list | tuple): self.value = value else: self.value = [value] @@ -87,7 +84,7 @@ class UpdatePayload(Struct): delete: list[str] = [] @classmethod - def from_updates_mapping(cls: type[Self], updates: Mapping[str, DataType | UpdateOperation], /) -> Self: + def from_updates_mapping(cls, updates: Mapping[str, DataType | UpdateOperation], /) -> "UpdatePayload": set = {} increment = {} append = {} diff --git a/src/contiguity/email.py b/src/contiguity/email.py index 4f1d0ea..917d352 100644 --- a/src/contiguity/email.py +++ b/src/contiguity/email.py @@ -1,14 +1,10 @@ -from __future__ import annotations - import logging -from typing import TYPE_CHECKING, overload +from collections.abc import Mapping, Sequence +from typing import overload from ._product import BaseProduct from ._response import BaseResponse, decode_response -if TYPE_CHECKING: - from collections.abc import Mapping, Sequence - logger = logging.getLogger(__name__) diff --git a/src/contiguity/leases.py b/src/contiguity/leases.py index 282e983..be6f264 100644 --- a/src/contiguity/leases.py +++ b/src/contiguity/leases.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from typing import Literal from msgspec import Struct diff --git a/src/contiguity/otp.py b/src/contiguity/otp.py index 6afac3b..d098c7e 100644 --- a/src/contiguity/otp.py +++ b/src/contiguity/otp.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import logging from enum import Enum diff --git a/tests/base/test_async_base_typeddict.py b/tests/base/test_async_base_typeddict.py index 39629e4..209b0ab 100644 --- a/tests/base/test_async_base_typeddict.py +++ b/tests/base/test_async_base_typeddict.py @@ -1,19 +1,14 @@ # ruff: noqa: S101, S311 -from __future__ import annotations - import random -from typing import TYPE_CHECKING, Any +from collections.abc import AsyncGenerator +from typing import Any, TypedDict import pytest from dotenv import load_dotenv -from typing_extensions import TypedDict from contiguity.base import AsyncBase, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse from tests import NON_EXISTENT_ITEM_WARNING, random_string -if TYPE_CHECKING: - from collections.abc import AsyncGenerator - load_dotenv() diff --git a/tests/base/test_base_typeddict.py b/tests/base/test_base_typeddict.py index 86c970a..1e273ef 100644 --- a/tests/base/test_base_typeddict.py +++ b/tests/base/test_base_typeddict.py @@ -1,19 +1,14 @@ # ruff: noqa: S101, S311 -from __future__ import annotations - import random -from typing import TYPE_CHECKING, Any +from collections.abc import Generator +from typing import Any, TypedDict import pytest from dotenv import load_dotenv -from typing_extensions import TypedDict from contiguity.base import Base, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse from tests import NON_EXISTENT_ITEM_WARNING, random_string -if TYPE_CHECKING: - from collections.abc import Generator - load_dotenv() From cb4fedba3bbc2f4e5ebe087380da3dc87f5e2375 Mon Sep 17 00:00:00 2001 From: Lemonyte Date: Mon, 13 Oct 2025 14:06:36 +0200 Subject: [PATCH 22/38] Add from and attachments params to text.send --- src/contiguity/text.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/contiguity/text.py b/src/contiguity/text.py index c6747de..58cdde5 100644 --- a/src/contiguity/text.py +++ b/src/contiguity/text.py @@ -1,4 +1,5 @@ import logging +from collections.abc import Sequence import phonenumbers @@ -13,17 +14,14 @@ class TextResponse(BaseResponse): class Text(BaseProduct): - def send(self, *, to: str, message: str) -> TextResponse: - """ - Send a text message. - Args: - to (str): The recipient's phone number. - message (str): The message to send. - Returns: - dict: The response object. - Raises: - ValueError: Raises an error if required fields are missing or sending the message fails. - """ + def send( + self, + *, + to: str, + message: str, + from_: str | None = None, + attachments: Sequence[str] | None = None, + ) -> TextResponse: try: parsed_number = phonenumbers.parse(to, None) if not phonenumbers.is_valid_number(parsed_number): @@ -33,12 +31,16 @@ def send(self, *, to: str, message: str) -> TextResponse: msg = "parsing failed. Phone number must follow the E.164 format." raise ValueError(msg) from exc + payload = { + "to": phonenumbers.format_number(parsed_number, phonenumbers.PhoneNumberFormat.E164), + "message": message, + "from": from_, + "attachments": attachments, + } + response = self._client.post( "/send/text", - json={ - "to": phonenumbers.format_number(parsed_number, phonenumbers.PhoneNumberFormat.E164), - "message": message, - }, + json={k: v for k, v in payload.items() if v is not None}, ) self._client.handle_error(response, fail_message="failed to send text message") From efd9a042ecbd6208b6c914802bb84e98b45e2d6e Mon Sep 17 00:00:00 2001 From: lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Tue, 14 Oct 2025 14:43:47 +0200 Subject: [PATCH 23/38] fix status code check --- src/contiguity/_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contiguity/_client.py b/src/contiguity/_client.py index 202a346..885f6cd 100644 --- a/src/contiguity/_client.py +++ b/src/contiguity/_client.py @@ -14,7 +14,7 @@ class ContiguityApiError(Exception): class BaseApiClient: def handle_error(self, response: Response, /, *, fail_message: str = "api request failed") -> None: - if not HTTPStatus.OK <= response.status_code <= HTTPStatus.IM_USED: + if not HTTPStatus.OK <= response.status_code < HTTPStatus.MULTIPLE_CHOICES: data = decode_response(response.content, type=ErrorResponse) msg = f"{fail_message}. {response.status_code} {data.error}" raise ContiguityApiError(msg) From dc45307254c7fb0c30b40c8e27b44ee6a9ae7f6f Mon Sep 17 00:00:00 2001 From: lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Tue, 14 Oct 2025 14:44:44 +0200 Subject: [PATCH 24/38] handle unexpected objects in response data --- src/contiguity/_response.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/contiguity/_response.py b/src/contiguity/_response.py index 05a8023..aca2478 100644 --- a/src/contiguity/_response.py +++ b/src/contiguity/_response.py @@ -1,10 +1,11 @@ -from collections.abc import Sequence +from collections.abc import Mapping, Sequence from http import HTTPStatus from typing import Generic, TypeVar import msgspec T = TypeVar("T", bound=msgspec.Struct | Sequence[msgspec.Struct]) +_type = type class ResponseMetadata(msgspec.Struct): @@ -35,4 +36,8 @@ def decode_response(content: bytes, /, *, type: type[T]) -> T: api_version=raw.api_version, object=raw.object, ) - return msgspec.convert({**msgspec.to_builtins(raw.data), "metadata": metadata}, type=type) + data = msgspec.to_builtins(raw.data) + if not isinstance(data, Mapping): + msg = f"expected Mapping instance for 'data' field, got {_type(data)}" + raise TypeError(msg) + return msgspec.convert({**data, "metadata": metadata}, type=type) From 34d280e3a6ac6fb8663c3163c359d1c51302d16c Mon Sep 17 00:00:00 2001 From: lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Tue, 14 Oct 2025 15:13:53 +0200 Subject: [PATCH 25/38] fix overload typing --- src/contiguity/base/async_base.py | 2 +- src/contiguity/base/base.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/contiguity/base/async_base.py b/src/contiguity/base/async_base.py index 3bcc2bb..6289bc2 100644 --- a/src/contiguity/base/async_base.py +++ b/src/contiguity/base/async_base.py @@ -109,7 +109,7 @@ def _response_as_item_type( response: HttpxResponse, /, *, - sequence: Literal[True] = True, + sequence: Literal[True], ) -> Sequence[ItemT]: ... def _response_as_item_type( diff --git a/src/contiguity/base/base.py b/src/contiguity/base/base.py index 41d96ad..c784d90 100644 --- a/src/contiguity/base/base.py +++ b/src/contiguity/base/base.py @@ -118,7 +118,7 @@ def _response_as_item_type( response: HttpxResponse, /, *, - sequence: Literal[True] = True, + sequence: Literal[True], ) -> Sequence[ItemT]: ... def _response_as_item_type( From 47dafdaf8d6c8ae962ad356102ebc3ee77bbe8a6 Mon Sep 17 00:00:00 2001 From: lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Wed, 22 Oct 2025 00:55:45 +0200 Subject: [PATCH 26/38] Remove pydantic mentions --- examples/base/msgspec_usage.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/base/msgspec_usage.py b/examples/base/msgspec_usage.py index d9bca50..e276786 100644 --- a/examples/base/msgspec_usage.py +++ b/examples/base/msgspec_usage.py @@ -3,7 +3,7 @@ from contiguity.base import Base -# Create a Pydantic model for the item. +# Create a msgspec model for the item. class MyItem(Struct): key: str # Make sure to include the key field. name: str @@ -12,7 +12,7 @@ class MyItem(Struct): # Create a Base instance. -# Static type checking will work with the Pydantic model. +# Static type checking will work with the msgspec model. db = Base("members", item_type=MyItem) # Put an item with a specific key. From d6b50785e22c8377d3af3c8cda2eaeb7378066b0 Mon Sep 17 00:00:00 2001 From: lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Wed, 22 Oct 2025 00:55:56 +0200 Subject: [PATCH 27/38] Fix test discovery --- tests/base/test_async_base_model.py | 1 + tests/base/test_base_model.py | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/base/test_async_base_model.py b/tests/base/test_async_base_model.py index 5861738..f340c48 100644 --- a/tests/base/test_async_base_model.py +++ b/tests/base/test_async_base_model.py @@ -14,6 +14,7 @@ class TestItemModel(Struct): + __test__ = False key: str = "test_key" field1: int = random.randint(1, 1000) field2: str = random_string() diff --git a/tests/base/test_base_model.py b/tests/base/test_base_model.py index 759e71c..18c1386 100644 --- a/tests/base/test_base_model.py +++ b/tests/base/test_base_model.py @@ -14,6 +14,7 @@ class TestItemModel(Struct): + __test__ = False key: str = "test_key" field1: int = random.randint(1, 1000) field2: str = random_string() From fe952483fbce8d052ae8a245ade1b5907f10facc Mon Sep 17 00:00:00 2001 From: lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Wed, 22 Oct 2025 00:57:00 +0200 Subject: [PATCH 28/38] Fix item typing in Base - set default item type to Mapping[str, Any] - remove custom json_decoder param --- src/contiguity/base/async_base.py | 25 ++++++------------------- src/contiguity/base/base.py | 25 ++++++------------------- src/contiguity/base/common.py | 8 +++++--- 3 files changed, 17 insertions(+), 41 deletions(-) diff --git a/src/contiguity/base/async_base.py b/src/contiguity/base/async_base.py index 6289bc2..242b9e7 100644 --- a/src/contiguity/base/async_base.py +++ b/src/contiguity/base/async_base.py @@ -1,9 +1,8 @@ -import json import os from collections.abc import Mapping, Sequence from datetime import datetime, timedelta, timezone from http import HTTPStatus -from typing import Generic, Literal, overload +from typing import Any, Generic, Literal, overload from warnings import warn import msgspec @@ -41,12 +40,11 @@ def __init__( name: str, /, *, - item_type: type[ItemT] | None = None, + item_type: type[ItemT] = Mapping[str, Any], data_key: str | None = None, project_id: str | None = None, host: str | None = None, api_version: str = "v1", - json_decoder: type[json.JSONDecoder] = json.JSONDecoder, ) -> None: ... @overload @@ -56,12 +54,11 @@ def __init__( name: str, /, *, - item_type: type[ItemT] | None = None, + item_type: type[ItemT] = Mapping[str, Any], project_key: str | None = None, project_id: str | None = None, host: str | None = None, api_version: str = "v1", - json_decoder: type[json.JSONDecoder] = json.JSONDecoder, ) -> None: ... def __init__( # noqa: PLR0913 @@ -69,13 +66,12 @@ def __init__( # noqa: PLR0913 name: str, /, *, - item_type: type[ItemT] | None = None, + item_type: type[ItemT] = Mapping[str, Any], data_key: str | None = None, project_key: str | None = None, # Deprecated. project_id: str | None = None, host: str | None = None, api_version: str = "v1", - json_decoder: type[json.JSONDecoder] = json.JSONDecoder, # Only used when item_type is not a Pydantic model. ) -> None: if not name: msg = f"invalid Base name '{name}'" @@ -87,7 +83,6 @@ def __init__( # noqa: PLR0913 self.project_id = project_id or get_project_id() self.host = host or os.getenv("CONTIGUITY_BASE_HOST") or "api.base.contiguity.co" self.api_version = api_version - self.json_decoder = json_decoder self.util = Updates() self._client = AsyncApiClient( base_url=f"https://{self.host}/{api_version}/{self.project_id}/{self.name}", @@ -123,9 +118,7 @@ def _response_as_item_type( response.raise_for_status() except HTTPStatusError as exc: raise ContiguityApiError(exc.response.text) from exc - if self.item_type: - return msgspec.json.decode(response.content, type=Sequence[self.item_type] if sequence else self.item_type) - return response.json(cls=self.json_decoder) + return msgspec.json.decode(response.content, type=Sequence[self.item_type] if sequence else self.item_type) def _insert_expires_attr( self, @@ -295,13 +288,7 @@ async def query( response.raise_for_status() except HTTPStatusError as exc: raise ContiguityApiError(exc.response.text) from exc - return msgspec.json.decode(response.content, type=QueryResponse[ItemT]) - - # query_response = QueryResponse[ItemT].model_validate_json(response.content) - # if self.item_type: - # # HACK: Pydantic model_validate_json doesn't validate Sequence[ItemT] properly. # noqa: FIX004 - # query_response.items = msgspec.json.decode(query_response.items, type=Sequence[self.item_type]) - # return query_response + return msgspec.json.decode(response.content, type=QueryResponse[self.item_type]) @deprecated("This method has been renamed to `query` and will be removed in a future release.") async def fetch( diff --git a/src/contiguity/base/base.py b/src/contiguity/base/base.py index c784d90..d7d9644 100644 --- a/src/contiguity/base/base.py +++ b/src/contiguity/base/base.py @@ -7,12 +7,11 @@ # - [ ] add async # - [ ] add drive support -import json import os from collections.abc import Mapping, Sequence from datetime import datetime, timedelta, timezone from http import HTTPStatus -from typing import Generic, Literal, overload +from typing import Any, Generic, Literal, overload from warnings import warn import msgspec @@ -50,12 +49,11 @@ def __init__( name: str, /, *, - item_type: type[ItemT] | None = None, + item_type: type[ItemT] = Mapping[str, Any], data_key: str | None = None, project_id: str | None = None, host: str | None = None, api_version: str = "v1", - json_decoder: type[json.JSONDecoder] = json.JSONDecoder, ) -> None: ... @overload @@ -65,12 +63,11 @@ def __init__( name: str, /, *, - item_type: type[ItemT] | None = None, + item_type: type[ItemT] = Mapping[str, Any], project_key: str | None = None, project_id: str | None = None, host: str | None = None, api_version: str = "v1", - json_decoder: type[json.JSONDecoder] = json.JSONDecoder, ) -> None: ... def __init__( # noqa: PLR0913 @@ -78,13 +75,12 @@ def __init__( # noqa: PLR0913 name: str, /, *, - item_type: type[ItemT] | None = None, + item_type: type[ItemT] = Mapping[str, Any], data_key: str | None = None, project_key: str | None = None, # Deprecated. project_id: str | None = None, host: str | None = None, api_version: str = "v1", - json_decoder: type[json.JSONDecoder] = json.JSONDecoder, # Only used when item_type is not a Pydantic model. ) -> None: if not name: msg = f"invalid Base name '{name}'" @@ -96,7 +92,6 @@ def __init__( # noqa: PLR0913 self.project_id = project_id or get_project_id() self.host = host or os.getenv("CONTIGUITY_BASE_HOST") or "api.base.contiguity.co" self.api_version = api_version - self.json_decoder = json_decoder self.util = Updates() self._client = ApiClient( base_url=f"https://{self.host}/{api_version}/{self.project_id}/{self.name}", @@ -132,9 +127,7 @@ def _response_as_item_type( response.raise_for_status() except HTTPStatusError as exc: raise ContiguityApiError(exc.response.text) from exc - if self.item_type: - return msgspec.json.decode(response.content, type=Sequence[self.item_type] if sequence else self.item_type) - return response.json(cls=self.json_decoder) + return msgspec.json.decode(response.content, type=Sequence[self.item_type] if sequence else self.item_type) def _insert_expires_attr( self, @@ -304,13 +297,7 @@ def query( response.raise_for_status() except HTTPStatusError as exc: raise ContiguityApiError(exc.response.text) from exc - return msgspec.json.decode(response.content, type=QueryResponse[ItemT]) - - # query_response = QueryResponse[ItemT].model_validate_json(response.content) - # if self.item_type: - # # HACK: Pydantic model_validate_json doesn't validate Sequence[ItemT] properly. # noqa: FIX004 - # query_response.items = TypeAdapter(Sequence[self.item_type]).validate_python(query_response.items) - # return query_response + return msgspec.json.decode(response.content, type=QueryResponse[self.item_type]) @deprecated("This method has been renamed to `query` and will be removed in a future release.") def fetch( diff --git a/src/contiguity/base/common.py b/src/contiguity/base/common.py index 43d44f5..b8ee136 100644 --- a/src/contiguity/base/common.py +++ b/src/contiguity/base/common.py @@ -1,13 +1,15 @@ from collections.abc import Mapping, Sequence from datetime import datetime -from typing import Any, Generic, TypeVar, Union +from typing import Any, Generic, TypeAlias, TypeVar from urllib.parse import quote from msgspec import Struct from .exceptions import InvalidKeyError -DataType = str | int | float | bool | None | Sequence["DataType"] | Mapping[str, "DataType"] +# Defining DataType in one line causes issues with msgspec. +DataType: TypeAlias = str | int | float | bool | None # type: ignore[reportRedeclaration] +DataType: TypeAlias = DataType | Sequence[DataType] | Mapping[str, DataType] # type: ignore[reportRedeclaration] TimestampType = int | datetime QueryType = Mapping[str, DataType] @@ -29,7 +31,7 @@ class BaseItem(Struct): class QueryResponse(Struct, Generic[ItemT]): count: int = 0 - last_key: Union[str, None] = None # noqa: UP007 Pydantic doesn't support `X | Y` syntax in Python 3.9. + last_key: str | None = None items: Sequence[ItemT] = [] From 665efefece273b2ed8efc9adc12560d88c236177 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 22 Oct 2025 01:57:51 +0000 Subject: [PATCH 29/38] Refactor: Use response_type in decode_response Co-authored-by: hello --- src/contiguity/_client.py | 2 +- src/contiguity/_instant_messaging.py | 6 +++--- src/contiguity/_response.py | 14 ++++++++++---- src/contiguity/domains.py | 8 ++++---- src/contiguity/email.py | 2 +- src/contiguity/imessage.py | 4 ++-- src/contiguity/leases.py | 10 +++++----- src/contiguity/otp.py | 6 +++--- src/contiguity/text.py | 2 +- 9 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/contiguity/_client.py b/src/contiguity/_client.py index 885f6cd..31f9232 100644 --- a/src/contiguity/_client.py +++ b/src/contiguity/_client.py @@ -15,7 +15,7 @@ class ContiguityApiError(Exception): class BaseApiClient: def handle_error(self, response: Response, /, *, fail_message: str = "api request failed") -> None: if not HTTPStatus.OK <= response.status_code < HTTPStatus.MULTIPLE_CHOICES: - data = decode_response(response.content, type=ErrorResponse) + data = decode_response(response.content, response_type=ErrorResponse) msg = f"{fail_message}. {response.status_code} {data.error}" raise ContiguityApiError(msg) diff --git a/src/contiguity/_instant_messaging.py b/src/contiguity/_instant_messaging.py index a56b6c7..ee11243 100644 --- a/src/contiguity/_instant_messaging.py +++ b/src/contiguity/_instant_messaging.py @@ -53,7 +53,7 @@ def send( # noqa: PLR0913 ) self._client.handle_error(response, fail_message="failed to send instant message") - data = decode_response(response.content, type=IMSendResponse) + data = decode_response(response.content, response_type=IMSendResponse) logger.debug("successfully sent %s message to %r", self._api_path[1:], to) return data @@ -70,7 +70,7 @@ def _typing(self, *, to: str, action: Literal["start", "stop"], from_: str | Non ) self._client.handle_error(response, fail_message=f"failed to {action} {self._api_path[1:]} typing indicator") - data = decode_response(response.content, type=IMTypingResponse) + data = decode_response(response.content, response_type=IMTypingResponse) logger.debug("successfully %s %s typing indicator for %r", action, self._api_path[1:], to) return data @@ -101,7 +101,7 @@ def _reactions( ) self._client.handle_error(response, fail_message=f"failed to {action} {self._api_path[1:]} reaction") - data = decode_response(response.content, type=IMReactionResponse) + data = decode_response(response.content, response_type=IMReactionResponse) logger.debug("successfully %s %s reaction for %r", action, self._api_path[1:], to) return data diff --git a/src/contiguity/_response.py b/src/contiguity/_response.py index aca2478..3eac9dd 100644 --- a/src/contiguity/_response.py +++ b/src/contiguity/_response.py @@ -28,8 +28,8 @@ class ErrorResponse(BaseResponse): status: HTTPStatus -def decode_response(content: bytes, /, *, type: type[T]) -> T: - raw = msgspec.json.decode(content, type=RawResponse[type]) +def decode_response(content: bytes, /, *, response_type: type[T]) -> T: + raw = msgspec.json.decode(content, type=RawResponse[response_type]) metadata = ResponseMetadata( id=raw.id, timestamp=raw.timestamp, @@ -37,7 +37,13 @@ def decode_response(content: bytes, /, *, type: type[T]) -> T: object=raw.object, ) data = msgspec.to_builtins(raw.data) + + # Handle sequence types (list of structs) + if isinstance(data, Sequence) and not isinstance(data, str): + return msgspec.convert(data, type=response_type) + + # Handle mapping types (single struct with metadata) if not isinstance(data, Mapping): - msg = f"expected Mapping instance for 'data' field, got {_type(data)}" + msg = f"expected Mapping or Sequence instance for 'data' field, got {_type(data)}" raise TypeError(msg) - return msgspec.convert({**data, "metadata": metadata}, type=type) + return msgspec.convert({**data, "metadata": metadata}, type=response_type) diff --git a/src/contiguity/domains.py b/src/contiguity/domains.py index 75f7624..50fd8c7 100644 --- a/src/contiguity/domains.py +++ b/src/contiguity/domains.py @@ -59,23 +59,23 @@ def register( ) self._client.handle_error(response, fail_message="failed to register domain") - data = decode_response(response.content, type=PartialDomain) + data = decode_response(response.content, response_type=PartialDomain) logger.debug("successfully registered domain %r", domain) return data def list(self) -> list[PartialDomain]: response = self._client.get("/domains") self._client.handle_error(response, fail_message="failed to list domains") - return decode_response(response.content, type=list[PartialDomain]) + return decode_response(response.content, response_type=list[PartialDomain]) def get(self, domain: str, /) -> Domain: response = self._client.get(f"/domains/{domain}") self._client.handle_error(response, fail_message="failed to get domain") - return decode_response(response.content, type=Domain) + return decode_response(response.content, response_type=Domain) def delete(self, domain: str, /) -> DeleteDomainResponse: response = self._client.delete(f"/domains/{domain}") self._client.handle_error(response, fail_message="failed to delete domain") - data = decode_response(response.content, type=DeleteDomainResponse) + data = decode_response(response.content, response_type=DeleteDomainResponse) logger.debug("successfully deleted domain %r", domain) return data diff --git a/src/contiguity/email.py b/src/contiguity/email.py index 917d352..9b480b0 100644 --- a/src/contiguity/email.py +++ b/src/contiguity/email.py @@ -95,6 +95,6 @@ def email( # noqa: PLR0913 ) self._client.handle_error(response, fail_message="failed to send email") - data = decode_response(response.content, type=EmailResponse) + data = decode_response(response.content, response_type=EmailResponse) logger.debug("successfully sent email to %r", to) return data diff --git a/src/contiguity/imessage.py b/src/contiguity/imessage.py index b6af7c9..7d3863e 100644 --- a/src/contiguity/imessage.py +++ b/src/contiguity/imessage.py @@ -92,7 +92,7 @@ def mark_read(self, *, to: str, from_: str) -> ReadResponse: ) self._client.handle_error(response, fail_message="failed to send instant message") - data = decode_response(response.content, type=ReadResponse) + data = decode_response(response.content, response_type=ReadResponse) logger.debug("successfully sent %s read receipt to %r", self._api_path[1:], to) return data @@ -102,4 +102,4 @@ def get_history(self, *, to: str, from_: str, limit: int = 20) -> History: ) self._client.handle_error(response, fail_message="failed to get message history") - return decode_response(response.content, type=History) + return decode_response(response.content, response_type=History) diff --git a/src/contiguity/leases.py b/src/contiguity/leases.py index be6f264..940aa02 100644 --- a/src/contiguity/leases.py +++ b/src/contiguity/leases.py @@ -81,17 +81,17 @@ class Leases(BaseProduct): def get_available_numbers(self) -> list[NumberDetails]: response = self._client.get("/leases") self._client.handle_error(response, fail_message="failed to get available numbers") - return decode_response(response.content, type=list[NumberDetails]) + return decode_response(response.content, response_type=list[NumberDetails]) def get_leased_numbers(self) -> list[NumberDetails]: response = self._client.get("/leased") self._client.handle_error(response, fail_message="failed to get leased numbers") - return decode_response(response.content, type=list[NumberDetails]) + return decode_response(response.content, response_type=list[NumberDetails]) def get_number_details(self, number: str, /) -> NumberDetails: response = self._client.get(f"/lease/{number}") self._client.handle_error(response, fail_message="failed to get number details") - return decode_response(response.content, type=NumberDetails) + return decode_response(response.content, response_type=NumberDetails) def lease_number( self, @@ -106,10 +106,10 @@ def lease_number( json={"billing_method": billing_method}, ) self._client.handle_error(response, fail_message="failed to lease number") - return decode_response(response.content, type=NumberDetails) + return decode_response(response.content, response_type=NumberDetails) def terminate_lease(self, number: NumberDetails | str, /) -> TerminateLeaseResponse: number = number.id if isinstance(number, NumberDetails) else number response = self._client.delete(f"/leased/{number}") self._client.handle_error(response, fail_message="failed to terminate lease") - return decode_response(response.content, type=TerminateLeaseResponse) + return decode_response(response.content, response_type=TerminateLeaseResponse) diff --git a/src/contiguity/otp.py b/src/contiguity/otp.py index d098c7e..47345cc 100644 --- a/src/contiguity/otp.py +++ b/src/contiguity/otp.py @@ -81,7 +81,7 @@ def send( ) self._client.handle_error(response, fail_message="failed to send OTP") - data = decode_response(response.content, type=OTPSendResponse) + data = decode_response(response.content, response_type=OTPSendResponse) logger.debug("successfully sent OTP %r to %r", data.otp_id, to) return data @@ -94,7 +94,7 @@ def resend(self, otp_id: str, /) -> OTPResendResponse: ) self._client.handle_error(response, fail_message="failed to resend OTP") - data = decode_response(response.content, type=OTPResendResponse) + data = decode_response(response.content, response_type=OTPResendResponse) logger.debug("successfully resent OTP %r with status: %r", otp_id, data.resent) return data @@ -108,6 +108,6 @@ def verify(self, otp: int | str, /, *, otp_id: str) -> OTPVerifyResponse: ) self._client.handle_error(response, fail_message="failed to verify OTP") - data = decode_response(response.content, type=OTPVerifyResponse) + data = decode_response(response.content, response_type=OTPVerifyResponse) logger.debug("successfully verified OTP %r with status: %r", otp_id, data.verified) return data diff --git a/src/contiguity/text.py b/src/contiguity/text.py index 58cdde5..871b051 100644 --- a/src/contiguity/text.py +++ b/src/contiguity/text.py @@ -44,6 +44,6 @@ def send( ) self._client.handle_error(response, fail_message="failed to send text message") - data = decode_response(response.content, type=TextResponse) + data = decode_response(response.content, response_type=TextResponse) logger.debug("successfully sent text to %r", to) return data From 93483e48befd9620f98d45a3ee8648f9f05ed2c1 Mon Sep 17 00:00:00 2001 From: lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Wed, 22 Oct 2025 13:52:49 +0200 Subject: [PATCH 30/38] Revert "AI nonsense" This reverts commit 665efefece273b2ed8efc9adc12560d88c236177. get your slimy AI hands off my codebase --- src/contiguity/_client.py | 2 +- src/contiguity/_instant_messaging.py | 6 +++--- src/contiguity/_response.py | 14 ++++---------- src/contiguity/domains.py | 8 ++++---- src/contiguity/email.py | 2 +- src/contiguity/imessage.py | 4 ++-- src/contiguity/leases.py | 10 +++++----- src/contiguity/otp.py | 6 +++--- src/contiguity/text.py | 2 +- 9 files changed, 24 insertions(+), 30 deletions(-) diff --git a/src/contiguity/_client.py b/src/contiguity/_client.py index 31f9232..885f6cd 100644 --- a/src/contiguity/_client.py +++ b/src/contiguity/_client.py @@ -15,7 +15,7 @@ class ContiguityApiError(Exception): class BaseApiClient: def handle_error(self, response: Response, /, *, fail_message: str = "api request failed") -> None: if not HTTPStatus.OK <= response.status_code < HTTPStatus.MULTIPLE_CHOICES: - data = decode_response(response.content, response_type=ErrorResponse) + data = decode_response(response.content, type=ErrorResponse) msg = f"{fail_message}. {response.status_code} {data.error}" raise ContiguityApiError(msg) diff --git a/src/contiguity/_instant_messaging.py b/src/contiguity/_instant_messaging.py index ee11243..a56b6c7 100644 --- a/src/contiguity/_instant_messaging.py +++ b/src/contiguity/_instant_messaging.py @@ -53,7 +53,7 @@ def send( # noqa: PLR0913 ) self._client.handle_error(response, fail_message="failed to send instant message") - data = decode_response(response.content, response_type=IMSendResponse) + data = decode_response(response.content, type=IMSendResponse) logger.debug("successfully sent %s message to %r", self._api_path[1:], to) return data @@ -70,7 +70,7 @@ def _typing(self, *, to: str, action: Literal["start", "stop"], from_: str | Non ) self._client.handle_error(response, fail_message=f"failed to {action} {self._api_path[1:]} typing indicator") - data = decode_response(response.content, response_type=IMTypingResponse) + data = decode_response(response.content, type=IMTypingResponse) logger.debug("successfully %s %s typing indicator for %r", action, self._api_path[1:], to) return data @@ -101,7 +101,7 @@ def _reactions( ) self._client.handle_error(response, fail_message=f"failed to {action} {self._api_path[1:]} reaction") - data = decode_response(response.content, response_type=IMReactionResponse) + data = decode_response(response.content, type=IMReactionResponse) logger.debug("successfully %s %s reaction for %r", action, self._api_path[1:], to) return data diff --git a/src/contiguity/_response.py b/src/contiguity/_response.py index 3eac9dd..aca2478 100644 --- a/src/contiguity/_response.py +++ b/src/contiguity/_response.py @@ -28,8 +28,8 @@ class ErrorResponse(BaseResponse): status: HTTPStatus -def decode_response(content: bytes, /, *, response_type: type[T]) -> T: - raw = msgspec.json.decode(content, type=RawResponse[response_type]) +def decode_response(content: bytes, /, *, type: type[T]) -> T: + raw = msgspec.json.decode(content, type=RawResponse[type]) metadata = ResponseMetadata( id=raw.id, timestamp=raw.timestamp, @@ -37,13 +37,7 @@ def decode_response(content: bytes, /, *, response_type: type[T]) -> T: object=raw.object, ) data = msgspec.to_builtins(raw.data) - - # Handle sequence types (list of structs) - if isinstance(data, Sequence) and not isinstance(data, str): - return msgspec.convert(data, type=response_type) - - # Handle mapping types (single struct with metadata) if not isinstance(data, Mapping): - msg = f"expected Mapping or Sequence instance for 'data' field, got {_type(data)}" + msg = f"expected Mapping instance for 'data' field, got {_type(data)}" raise TypeError(msg) - return msgspec.convert({**data, "metadata": metadata}, type=response_type) + return msgspec.convert({**data, "metadata": metadata}, type=type) diff --git a/src/contiguity/domains.py b/src/contiguity/domains.py index 50fd8c7..75f7624 100644 --- a/src/contiguity/domains.py +++ b/src/contiguity/domains.py @@ -59,23 +59,23 @@ def register( ) self._client.handle_error(response, fail_message="failed to register domain") - data = decode_response(response.content, response_type=PartialDomain) + data = decode_response(response.content, type=PartialDomain) logger.debug("successfully registered domain %r", domain) return data def list(self) -> list[PartialDomain]: response = self._client.get("/domains") self._client.handle_error(response, fail_message="failed to list domains") - return decode_response(response.content, response_type=list[PartialDomain]) + return decode_response(response.content, type=list[PartialDomain]) def get(self, domain: str, /) -> Domain: response = self._client.get(f"/domains/{domain}") self._client.handle_error(response, fail_message="failed to get domain") - return decode_response(response.content, response_type=Domain) + return decode_response(response.content, type=Domain) def delete(self, domain: str, /) -> DeleteDomainResponse: response = self._client.delete(f"/domains/{domain}") self._client.handle_error(response, fail_message="failed to delete domain") - data = decode_response(response.content, response_type=DeleteDomainResponse) + data = decode_response(response.content, type=DeleteDomainResponse) logger.debug("successfully deleted domain %r", domain) return data diff --git a/src/contiguity/email.py b/src/contiguity/email.py index 9b480b0..917d352 100644 --- a/src/contiguity/email.py +++ b/src/contiguity/email.py @@ -95,6 +95,6 @@ def email( # noqa: PLR0913 ) self._client.handle_error(response, fail_message="failed to send email") - data = decode_response(response.content, response_type=EmailResponse) + data = decode_response(response.content, type=EmailResponse) logger.debug("successfully sent email to %r", to) return data diff --git a/src/contiguity/imessage.py b/src/contiguity/imessage.py index 7d3863e..b6af7c9 100644 --- a/src/contiguity/imessage.py +++ b/src/contiguity/imessage.py @@ -92,7 +92,7 @@ def mark_read(self, *, to: str, from_: str) -> ReadResponse: ) self._client.handle_error(response, fail_message="failed to send instant message") - data = decode_response(response.content, response_type=ReadResponse) + data = decode_response(response.content, type=ReadResponse) logger.debug("successfully sent %s read receipt to %r", self._api_path[1:], to) return data @@ -102,4 +102,4 @@ def get_history(self, *, to: str, from_: str, limit: int = 20) -> History: ) self._client.handle_error(response, fail_message="failed to get message history") - return decode_response(response.content, response_type=History) + return decode_response(response.content, type=History) diff --git a/src/contiguity/leases.py b/src/contiguity/leases.py index 940aa02..be6f264 100644 --- a/src/contiguity/leases.py +++ b/src/contiguity/leases.py @@ -81,17 +81,17 @@ class Leases(BaseProduct): def get_available_numbers(self) -> list[NumberDetails]: response = self._client.get("/leases") self._client.handle_error(response, fail_message="failed to get available numbers") - return decode_response(response.content, response_type=list[NumberDetails]) + return decode_response(response.content, type=list[NumberDetails]) def get_leased_numbers(self) -> list[NumberDetails]: response = self._client.get("/leased") self._client.handle_error(response, fail_message="failed to get leased numbers") - return decode_response(response.content, response_type=list[NumberDetails]) + return decode_response(response.content, type=list[NumberDetails]) def get_number_details(self, number: str, /) -> NumberDetails: response = self._client.get(f"/lease/{number}") self._client.handle_error(response, fail_message="failed to get number details") - return decode_response(response.content, response_type=NumberDetails) + return decode_response(response.content, type=NumberDetails) def lease_number( self, @@ -106,10 +106,10 @@ def lease_number( json={"billing_method": billing_method}, ) self._client.handle_error(response, fail_message="failed to lease number") - return decode_response(response.content, response_type=NumberDetails) + return decode_response(response.content, type=NumberDetails) def terminate_lease(self, number: NumberDetails | str, /) -> TerminateLeaseResponse: number = number.id if isinstance(number, NumberDetails) else number response = self._client.delete(f"/leased/{number}") self._client.handle_error(response, fail_message="failed to terminate lease") - return decode_response(response.content, response_type=TerminateLeaseResponse) + return decode_response(response.content, type=TerminateLeaseResponse) diff --git a/src/contiguity/otp.py b/src/contiguity/otp.py index 47345cc..d098c7e 100644 --- a/src/contiguity/otp.py +++ b/src/contiguity/otp.py @@ -81,7 +81,7 @@ def send( ) self._client.handle_error(response, fail_message="failed to send OTP") - data = decode_response(response.content, response_type=OTPSendResponse) + data = decode_response(response.content, type=OTPSendResponse) logger.debug("successfully sent OTP %r to %r", data.otp_id, to) return data @@ -94,7 +94,7 @@ def resend(self, otp_id: str, /) -> OTPResendResponse: ) self._client.handle_error(response, fail_message="failed to resend OTP") - data = decode_response(response.content, response_type=OTPResendResponse) + data = decode_response(response.content, type=OTPResendResponse) logger.debug("successfully resent OTP %r with status: %r", otp_id, data.resent) return data @@ -108,6 +108,6 @@ def verify(self, otp: int | str, /, *, otp_id: str) -> OTPVerifyResponse: ) self._client.handle_error(response, fail_message="failed to verify OTP") - data = decode_response(response.content, response_type=OTPVerifyResponse) + data = decode_response(response.content, type=OTPVerifyResponse) logger.debug("successfully verified OTP %r with status: %r", otp_id, data.verified) return data diff --git a/src/contiguity/text.py b/src/contiguity/text.py index 871b051..58cdde5 100644 --- a/src/contiguity/text.py +++ b/src/contiguity/text.py @@ -44,6 +44,6 @@ def send( ) self._client.handle_error(response, fail_message="failed to send text message") - data = decode_response(response.content, response_type=TextResponse) + data = decode_response(response.content, type=TextResponse) logger.debug("successfully sent text to %r", to) return data From 06a4719d4080fc517dd612e6d564180b9bfebf8c Mon Sep 17 00:00:00 2001 From: lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:01:48 +0200 Subject: [PATCH 31/38] pre-commit autoupdate --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9b8fb29..2ab4b6c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -11,12 +11,12 @@ repos: args: [--markdown-linebreak-ext=md] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.9 + rev: v0.14.1 hooks: - id: ruff - id: ruff-format - repo: https://github.com/RobertCraigie/pyright-python - rev: v1.1.384 + rev: v1.1.406 hooks: - id: pyright From 67da89fc657167ff3c43250a5310c7057ac13692 Mon Sep 17 00:00:00 2001 From: lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:04:52 +0200 Subject: [PATCH 32/38] Bump python version in workflows --- .github/workflows/lint.yaml | 2 +- .github/workflows/test.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index fb5eb8a..8da9983 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -12,7 +12,7 @@ jobs: lint: runs-on: ubuntu-latest env: - PYTHON_VERSION: "3.9" + PYTHON_VERSION: "3.10" steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index ec288e1..65fc255 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -12,7 +12,7 @@ jobs: test: runs-on: ubuntu-latest env: - PYTHON_VERSION: "3.9" + PYTHON_VERSION: "3.10" CONTIGUITY_PROJECT_ID: ${{ secrets.CONTIGUITY_PROJECT_ID }} CONTIGUITY_TOKEN: ${{ secrets.CONTIGUITY_TOKEN }} CONTIGUITY_DATA_KEY: ${{ secrets.CONTIGUITY_DATA_KEY }} From cb269169fecd8f45dc9ba7fddcde7e193aecb9c3 Mon Sep 17 00:00:00 2001 From: lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:56:00 +0200 Subject: [PATCH 33/38] Indicate identity is not implemented --- src/contiguity/identity.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/contiguity/identity.py b/src/contiguity/identity.py index e69de29..5f043f8 100644 --- a/src/contiguity/identity.py +++ b/src/contiguity/identity.py @@ -0,0 +1 @@ +# Not implemented yet. From 36a5d893fba1bf7cda432c37e1fdf7e7cd2fc9d9 Mon Sep 17 00:00:00 2001 From: lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:56:14 +0200 Subject: [PATCH 34/38] Rename email.email to email.send --- src/contiguity/email.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/contiguity/email.py b/src/contiguity/email.py index 917d352..52cdeed 100644 --- a/src/contiguity/email.py +++ b/src/contiguity/email.py @@ -14,7 +14,7 @@ class EmailResponse(BaseResponse): class Email(BaseProduct): @overload - def email( + def send( self, *, to: str, @@ -28,7 +28,7 @@ def email( ) -> EmailResponse: ... @overload - def email( + def send( self, *, to: str, @@ -41,7 +41,7 @@ def email( headers: Mapping[str, str] | None = None, ) -> EmailResponse: ... - def email( # noqa: PLR0913 + def send( # noqa: PLR0913 self, *, to: str, From 4baa451a580acc737b5e1dfde7a81444df55e306 Mon Sep 17 00:00:00 2001 From: lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:56:34 +0200 Subject: [PATCH 35/38] Make token param optional --- src/contiguity/__init__.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/contiguity/__init__.py b/src/contiguity/__init__.py index afee28e..79e38a5 100644 --- a/src/contiguity/__init__.py +++ b/src/contiguity/__init__.py @@ -1,3 +1,4 @@ +from ._auth import get_contiguity_token from ._client import ApiClient from .domains import Domains from .email import Email @@ -9,25 +10,17 @@ class Contiguity: - """ - Create a new instance of the Contiguity class. - - Args: - token (str): The authentication token. - """ + """The Contiguity client.""" def __init__( self, *, - token: str, + token: str | None = None, base_url: str = "https://api.contiguity.com", ) -> None: - if not token: - msg = "token cannot be empty" - raise ValueError(msg) - self.token = token + self.token = token or get_contiguity_token() self.base_url = base_url - self.client = ApiClient(base_url=self.base_url, api_key=token.strip()) + self.client = ApiClient(base_url=self.base_url, api_key=self.token.strip()) self.text = Text(client=self.client) self.email = Email(client=self.client) From 6cb9102a847523fad93f31dd3fc0304180420edb Mon Sep 17 00:00:00 2001 From: lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:56:43 +0200 Subject: [PATCH 36/38] Ignore assert lint in tests --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index ae60123..7b6f13e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,6 +67,9 @@ target-version = "py310" select = ["ALL"] ignore = ["A", "D", "T201"] +[tool.ruff.lint.per-file-ignores] +"**/tests/*" = ["S101"] + [tool.pyright] venvPath = "." venv = ".venv" From 0350b6a3804877308e17a08b46fde9d728ccda96 Mon Sep 17 00:00:00 2001 From: lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Wed, 22 Oct 2025 15:25:14 +0200 Subject: [PATCH 37/38] Clean up tests --- pyproject.toml | 2 +- tests/__init__.py | 16 +++++++++++++++- tests/base/test_async_base_dict_typed.py | 4 ---- tests/base/test_async_base_dict_untyped.py | 4 ---- tests/base/test_async_base_model.py | 4 ---- tests/base/test_async_base_typeddict.py | 4 ---- tests/base/test_base_dict_typed.py | 4 ---- tests/base/test_base_dict_untyped.py | 4 ---- tests/base/test_base_model.py | 4 ---- tests/base/test_base_typeddict.py | 4 ---- 10 files changed, 16 insertions(+), 34 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7b6f13e..bf40719 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,7 @@ select = ["ALL"] ignore = ["A", "D", "T201"] [tool.ruff.lint.per-file-ignores] -"**/tests/*" = ["S101"] +"**/tests/*" = ["S101", "S311"] [tool.pyright] venvPath = "." diff --git a/tests/__init__.py b/tests/__init__.py index 9d2ee39..8ac15da 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,10 +1,24 @@ import random import string +from dotenv import load_dotenv + +from contiguity._auth import _get_env_var + +load_dotenv() + NON_EXISTENT_ITEM_WARNING = ( "ItemNotFoundError will be raised in the future. To receive None for non-existent keys, set default=None." ) def random_string(length: int = 10) -> str: - return "".join(random.choices(string.ascii_lowercase, k=length)) # noqa: S311 + return "".join(random.choices(string.ascii_lowercase, k=length)) + + +def get_test_email() -> str: + return _get_env_var("TEST_EMAIL", "test email address") + + +def get_test_phone() -> str: + return _get_env_var("TEST_PHONE_NUMBER", "test phone number") diff --git a/tests/base/test_async_base_dict_typed.py b/tests/base/test_async_base_dict_typed.py index 39f3d83..e0ff609 100644 --- a/tests/base/test_async_base_dict_typed.py +++ b/tests/base/test_async_base_dict_typed.py @@ -1,17 +1,13 @@ -# ruff: noqa: S101, S311 import random from collections.abc import AsyncGenerator, Mapping from typing import Any import pytest -from dotenv import load_dotenv from contiguity.base import AsyncBase, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse from contiguity.base.common import DataType from tests import NON_EXISTENT_ITEM_WARNING, random_string -load_dotenv() - DictItemType = Mapping[str, DataType] diff --git a/tests/base/test_async_base_dict_untyped.py b/tests/base/test_async_base_dict_untyped.py index 6ee3a68..4764723 100644 --- a/tests/base/test_async_base_dict_untyped.py +++ b/tests/base/test_async_base_dict_untyped.py @@ -1,17 +1,13 @@ -# ruff: noqa: S101, S311 import random from collections.abc import AsyncGenerator from typing import Any import pytest -from dotenv import load_dotenv from contiguity.base import AsyncBase, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse from contiguity.base.common import DataType from tests import NON_EXISTENT_ITEM_WARNING, random_string -load_dotenv() - def create_test_item(**kwargs: DataType) -> dict: kwargs.setdefault("key", "test_key") diff --git a/tests/base/test_async_base_model.py b/tests/base/test_async_base_model.py index f340c48..036eeaf 100644 --- a/tests/base/test_async_base_model.py +++ b/tests/base/test_async_base_model.py @@ -1,17 +1,13 @@ -# ruff: noqa: S101, S311 import random from collections.abc import AsyncGenerator from typing import Any import pytest -from dotenv import load_dotenv from msgspec import Struct, field from contiguity.base import AsyncBase, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse from tests import NON_EXISTENT_ITEM_WARNING, random_string -load_dotenv() - class TestItemModel(Struct): __test__ = False diff --git a/tests/base/test_async_base_typeddict.py b/tests/base/test_async_base_typeddict.py index 209b0ab..a95ab27 100644 --- a/tests/base/test_async_base_typeddict.py +++ b/tests/base/test_async_base_typeddict.py @@ -1,16 +1,12 @@ -# ruff: noqa: S101, S311 import random from collections.abc import AsyncGenerator from typing import Any, TypedDict import pytest -from dotenv import load_dotenv from contiguity.base import AsyncBase, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse from tests import NON_EXISTENT_ITEM_WARNING, random_string -load_dotenv() - class TestItemDict(TypedDict): key: str diff --git a/tests/base/test_base_dict_typed.py b/tests/base/test_base_dict_typed.py index 7017125..866684a 100644 --- a/tests/base/test_base_dict_typed.py +++ b/tests/base/test_base_dict_typed.py @@ -1,17 +1,13 @@ -# ruff: noqa: S101, S311 import random from collections.abc import Generator, Mapping from typing import Any import pytest -from dotenv import load_dotenv from contiguity.base import Base, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse from contiguity.base.common import DataType from tests import NON_EXISTENT_ITEM_WARNING, random_string -load_dotenv() - DictItemType = Mapping[str, DataType] diff --git a/tests/base/test_base_dict_untyped.py b/tests/base/test_base_dict_untyped.py index cd5a4f5..45a98b4 100644 --- a/tests/base/test_base_dict_untyped.py +++ b/tests/base/test_base_dict_untyped.py @@ -1,17 +1,13 @@ -# ruff: noqa: S101, S311 import random from collections.abc import Generator from typing import Any import pytest -from dotenv import load_dotenv from contiguity.base import Base, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse from contiguity.base.common import DataType from tests import NON_EXISTENT_ITEM_WARNING, random_string -load_dotenv() - def create_test_item(**kwargs: DataType) -> dict: kwargs.setdefault("key", "test_key") diff --git a/tests/base/test_base_model.py b/tests/base/test_base_model.py index 18c1386..38792d6 100644 --- a/tests/base/test_base_model.py +++ b/tests/base/test_base_model.py @@ -1,17 +1,13 @@ -# ruff: noqa: S101, S311 import random from collections.abc import Generator from typing import Any import pytest -from dotenv import load_dotenv from msgspec import Struct, field from contiguity.base import Base, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse from tests import NON_EXISTENT_ITEM_WARNING, random_string -load_dotenv() - class TestItemModel(Struct): __test__ = False diff --git a/tests/base/test_base_typeddict.py b/tests/base/test_base_typeddict.py index 1e273ef..14819e8 100644 --- a/tests/base/test_base_typeddict.py +++ b/tests/base/test_base_typeddict.py @@ -1,16 +1,12 @@ -# ruff: noqa: S101, S311 import random from collections.abc import Generator from typing import Any, TypedDict import pytest -from dotenv import load_dotenv from contiguity.base import Base, InvalidKeyError, ItemConflictError, ItemNotFoundError, QueryResponse from tests import NON_EXISTENT_ITEM_WARNING, random_string -load_dotenv() - class TestItemDict(TypedDict): key: str From d9856206b84d19207a91b65e9d78f7bba5aa2876 Mon Sep 17 00:00:00 2001 From: lemonyte <49930425+lemonyte@users.noreply.github.com> Date: Wed, 22 Oct 2025 15:25:28 +0200 Subject: [PATCH 38/38] Add preliminary sdk tests --- tests/test_domains.py | 1 + tests/test_email.py | 52 +++++++++++++++++++++++++++ tests/test_identity.py | 1 + tests/test_imessage.py | 81 ++++++++++++++++++++++++++++++++++++++++++ tests/test_leases.py | 1 + tests/test_otp.py | 34 ++++++++++++++++++ tests/test_text.py | 63 ++++++++++++++++++++++++++++++++ tests/test_verify.py | 54 ++++++++++++++++++++++++++++ tests/test_whatsapp.py | 55 ++++++++++++++++++++++++++++ 9 files changed, 342 insertions(+) create mode 100644 tests/test_domains.py create mode 100644 tests/test_email.py create mode 100644 tests/test_identity.py create mode 100644 tests/test_imessage.py create mode 100644 tests/test_leases.py create mode 100644 tests/test_otp.py create mode 100644 tests/test_text.py create mode 100644 tests/test_verify.py create mode 100644 tests/test_whatsapp.py diff --git a/tests/test_domains.py b/tests/test_domains.py new file mode 100644 index 0000000..0020cca --- /dev/null +++ b/tests/test_domains.py @@ -0,0 +1 @@ +# Not implmemented yet. diff --git a/tests/test_email.py b/tests/test_email.py new file mode 100644 index 0000000..32f1a50 --- /dev/null +++ b/tests/test_email.py @@ -0,0 +1,52 @@ +import pytest + +from contiguity import Contiguity +from contiguity.email import Email +from tests import get_test_email + + +@pytest.fixture +def email_client() -> Email: + return Contiguity().email + + +def test_email_with_text_body(email: Email) -> None: + """Test sending an email with plain text body.""" + result = email.send( + to=get_test_email(), + from_="Test Sender", + subject="Test Email with Text Body", + body_text="This is a test email body sent from pytest.", + ) + + assert result.email_id + assert result.metadata + + +def test_email_with_html_body(email: Email) -> None: + """Test sending an email with HTML body.""" + result = email.send( + to=get_test_email(), + from_="Test Sender", + subject="Test Email with HTML Body", + body_html="

This is a test email

Sent from pytest.

", + ) + + assert result.email_id + assert result.metadata + + +def test_email_with_optional_fields(email: Email) -> None: + """Test sending an email with optional fields.""" + result = email.send( + to=get_test_email(), + from_="Test Sender", + subject="Test Email with Optional Fields", + body_text="This is a test email with optional fields.", + reply_to="reply@contiguity.co", + cc=["cc@contiguity.co"], + headers={"X-Test-Header": "pytest"}, + ) + + assert result.email_id + assert result.metadata diff --git a/tests/test_identity.py b/tests/test_identity.py new file mode 100644 index 0000000..5f043f8 --- /dev/null +++ b/tests/test_identity.py @@ -0,0 +1 @@ +# Not implemented yet. diff --git a/tests/test_imessage.py b/tests/test_imessage.py new file mode 100644 index 0000000..49ed577 --- /dev/null +++ b/tests/test_imessage.py @@ -0,0 +1,81 @@ +import pytest + +from contiguity import Contiguity +from contiguity.imessage import IMessage +from tests import get_test_phone + + +@pytest.fixture +def imessage_client() -> IMessage: + return Contiguity().imessage + + +def test_send_imessage_basic(imessage: IMessage) -> None: + """Test sending a basic iMessage.""" + result = imessage.send( + to=get_test_phone(), + message="Hello via iMessage from pytest!", + ) + + assert result.message_id + assert result.metadata + + +def test_send_imessage_with_attachments(imessage: IMessage) -> None: + """Test sending an iMessage with attachments.""" + result = imessage.send( + to=get_test_phone(), + message="Check out this attachment!", + attachments=["https://example.com/image1.jpg"], + ) + + assert result.message_id + assert result.metadata + + +def test_start_typing(imessage: IMessage) -> None: + """Test starting typing indicator.""" + result = imessage.start_typing(to=get_test_phone()) + + assert result.status + + +def test_stop_typing(imessage: IMessage) -> None: + """Test stopping typing indicator.""" + result = imessage.stop_typing(to=get_test_phone()) + + assert result.status + + +def test_add_reaction(imessage: IMessage) -> None: + """Test adding a reaction to a message.""" + result = imessage.add_reaction( + to=get_test_phone(), + reaction="❤️", + message="test_message_id", + ) + + assert result.status + assert result.message + + +def test_remove_reaction(imessage: IMessage) -> None: + """Test removing a reaction from a message.""" + result = imessage.remove_reaction( + to=get_test_phone(), + reaction="❤️", + message="test_message_id", + ) + + assert result.status + + +def test_mark_read(imessage: IMessage) -> None: + """Test marking a message as read.""" + result = imessage.mark_read( + to=get_test_phone(), + from_="+14155552670", + ) + + assert result.status + assert result.message diff --git a/tests/test_leases.py b/tests/test_leases.py new file mode 100644 index 0000000..5f043f8 --- /dev/null +++ b/tests/test_leases.py @@ -0,0 +1 @@ +# Not implemented yet. diff --git a/tests/test_otp.py b/tests/test_otp.py new file mode 100644 index 0000000..8dfe015 --- /dev/null +++ b/tests/test_otp.py @@ -0,0 +1,34 @@ +import pytest + +from contiguity import Contiguity +from contiguity.otp import OTP, OTPLanguage +from tests import get_test_phone + + +@pytest.fixture +def otp_client() -> OTP: + return Contiguity().otp + + +def test_send_otp_basic(otp: OTP) -> None: + """Test sending a basic OTP.""" + result = otp.send(get_test_phone()) + + assert result.otp_id + assert result.metadata + + +def test_send_otp_with_name(otp: OTP) -> None: + """Test sending an OTP with a custom name.""" + result = otp.send(get_test_phone(), name="PyTest App") + + assert result.otp_id + assert result.metadata + + +def test_send_otp_with_language(otp: OTP) -> None: + """Test sending an OTP with a different language.""" + result = otp.send(get_test_phone(), language=OTPLanguage.SPANISH) + + assert result.otp_id + assert result.metadata diff --git a/tests/test_text.py b/tests/test_text.py new file mode 100644 index 0000000..50e549e --- /dev/null +++ b/tests/test_text.py @@ -0,0 +1,63 @@ +import pytest + +from contiguity import Contiguity +from contiguity.text import Text +from tests import get_test_phone + + +@pytest.fixture +def text_client() -> Text: + return Contiguity().text + + +def test_send_text_basic(text: Text) -> None: + """Test sending a basic text message.""" + result = text.send( + to=get_test_phone(), + message="Hello, this is a test message from pytest!", + ) + + assert result.message_id + assert result.metadata + + +def test_send_text_with_from(text: Text) -> None: + """Test sending a text message with from field.""" + result = text.send( + to=get_test_phone(), + message="Hello from a specific sender!", + from_="+14155552670", + ) + + assert result.message_id + assert result.metadata + + +def test_send_text_with_attachments(text: Text) -> None: + """Test sending a text message with attachments.""" + result = text.send( + to=get_test_phone(), + message="Check out these attachments!", + attachments=["https://example.com/image1.jpg"], + ) + + assert result.message_id + assert result.metadata + + +def test_send_text_invalid_number_format(text: Text) -> None: + """Test that invalid phone number format raises ValueError.""" + with pytest.raises(ValueError, match="parsing failed"): + text.send( + to="invalid_number", + message="This should fail", + ) + + +def test_send_text_invalid_number_validation(text: Text) -> None: + """Test that invalid phone number validation raises ValueError.""" + with pytest.raises(ValueError, match="formatting failed"): + text.send( + to="+1234", + message="This should fail", + ) diff --git a/tests/test_verify.py b/tests/test_verify.py new file mode 100644 index 0000000..a5ee342 --- /dev/null +++ b/tests/test_verify.py @@ -0,0 +1,54 @@ +import pytest + +from contiguity.verify import Verify + + +@pytest.fixture +def verify() -> Verify: + return Verify() + + +def test_verify_valid_number(verify: Verify) -> None: + """Test verifying a valid phone number.""" + assert verify.number("+14155552671") is True + assert verify.number("+44 20 7946 0958") is True + assert verify.number("+49 30 901820") is True + + +def test_verify_invalid_number(verify: Verify) -> None: + """Test verifying invalid phone numbers.""" + assert verify.number("invalid") is False + assert verify.number("1234") is False + assert verify.number("") is False + assert verify.number("abc123") is False + + +def test_verify_malformed_number(verify: Verify) -> None: + """Test verifying malformed phone numbers.""" + assert verify.number("+1-415-555-2671-extra") is False + + +def test_verify_valid_email(verify: Verify) -> None: + """Test verifying valid email addresses.""" + assert verify.email("test@example.com") is True + assert verify.email("user.name@example.co.uk") is True + assert verify.email("user+tag@example.com") is True + assert verify.email("123@domain.org") is True + + +def test_verify_invalid_email(verify: Verify) -> None: + """Test verifying invalid email addresses.""" + assert verify.email("invalid") is False + assert verify.email("@example.com") is False + assert verify.email("user@") is False + assert verify.email("user @example.com") is False + assert verify.email("user@domain") is False + assert verify.email("") is False + assert verify.email("user@.com") is False + + +def test_verify_edge_case_emails(verify: Verify) -> None: + """Test verifying edge case email addresses.""" + assert verify.email("user@@example.com") is False + assert verify.email("user@.") is False + assert verify.email("@") is False diff --git a/tests/test_whatsapp.py b/tests/test_whatsapp.py new file mode 100644 index 0000000..8970718 --- /dev/null +++ b/tests/test_whatsapp.py @@ -0,0 +1,55 @@ +import pytest + +from contiguity import Contiguity +from contiguity.whatsapp import WhatsApp +from tests import get_test_phone + + +@pytest.fixture +def whatsapp_client() -> WhatsApp: + return Contiguity().whatsapp + + +def test_send_whatsapp_basic(whatsapp: WhatsApp) -> None: + """Test sending a basic WhatsApp message.""" + whatsapp.send( + to=get_test_phone(), + message="Hello via WhatsApp!", + ) + + +def test_send_whatsapp_with_attachments(whatsapp: WhatsApp) -> None: + """Test sending a WhatsApp message with attachments.""" + whatsapp.send( + to=get_test_phone(), + message="Check out these attachments!", + attachments=["https://example.com/image1.jpg"], + ) + + +def test_start_typing(whatsapp: WhatsApp) -> None: + """Test starting typing indicator.""" + whatsapp.start_typing(to=get_test_phone()) + + +def test_stop_typing(whatsapp: WhatsApp) -> None: + """Test stopping typing indicator.""" + whatsapp.stop_typing(to=get_test_phone()) + + +def test_add_reaction(whatsapp: WhatsApp) -> None: + """Test adding a reaction to a message.""" + whatsapp.add_reaction( + to=get_test_phone(), + reaction="❤️", + message="msg_123", + ) + + +def test_remove_reaction(whatsapp: WhatsApp) -> None: + """Test removing a reaction from a message.""" + whatsapp.remove_reaction( + to=get_test_phone(), + reaction="❤️", + message="msg_123", + )