diff --git a/docs/changelog.rst b/docs/changelog.rst index b97337f2f..42a2f77fc 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -9,6 +9,38 @@ important operational fixes. Recent Updates ============== +v0.60.0 - Queue limits +------------------------------------------------------------------------------ + +**Added:** + +* The new ``listener_queue_capacity`` setting caps each PostgreSQL listener + queue. It works with asyncpg, psqlpy, and sync or async psycopg listeners. + The default has no limit. Bad values raise + :class:`~sqlspec.exceptions.ImproperConfigurationError`. +* :class:`~sqlspec.extensions.litestar.channels.SQLSpecChannelsBackend` accepts + ``output_queue_capacity``. It caps decoded messages from any async event + transport, including PostgreSQL, Oracle, and polling channels. The default + has no limit. Bad values raise ``ValueError``. +* ``output_queue_depth`` shows the current Litestar backlog. + ``dropped_message_count`` shows the total number of overflow drops. +* PostgreSQL listener metrics now track ``events.listener.queue.depth`` and the + total ``events.listener.queue.dropped`` count for the hub. + +**Changed:** + +* A full capped queue drops its oldest item before it adds the new one. Each + PostgreSQL consumer has its own queue. Shutdown clears queued items and sets + depth to zero. Drop counts stay in place after a restart. +* An overflow in PostgreSQL ``notify`` drops transient data. With + ``notify_queue``, it drops only a wake-up marker. The durable row stays in the + table and can be found by the next scan. +* Oracle AQ and TxEventQ still read from their native queues. They do not add a + listener queue. Table-backed ``poll_queue`` stores check + ``listener_queue_capacity``, but the setting does not change how they poll. +* Bad Litestar channel payloads are logged and acknowledged. They do not increase + ``dropped_message_count``. + v0.59.0 - Data dictionary and loader access ------------------------------------------------------------------------------ @@ -49,6 +81,25 @@ v0.59.0 - Data dictionary and loader access * ADBC adapters for PostgreSQL now keep ``None`` in arrays. Each value binds as SQL ``NULL``. This keeps null values in place. +v0.58.3 - Data and store fixes +------------------------------------------------------------------------------ + +**Fixed:** + +* Nested msgspec structs now use their own encoded field names. This works for + optional fields, annotations, lists, tuples, and maps. Mixed rename rules no + longer reuse the outer struct's rule. Keys in plain maps stay unchanged. +* ``ensure_async_()`` now has a + :class:`collections.abc.Coroutine` return type. This matches the value it has + always returned at run time and removes the need for a cast. +* SQLSpec Litestar session stores now inherit + :class:`litestar.stores.base.Store`. They keep its async context manager and + work with ``StoreRegistry``. +* Each extension now loads its SQL migration query names on its own. A file + such as ``0001_create_table.sql`` can use ``migrate-0001-up`` and + ``migrate-0001-down`` with no clash across extensions. SQLSpec still stores + the prefixed tracker version. + v0.58.2 - SQL file parameter diagnostics ------------------------------------------------------------------------------ diff --git a/docs/reference/extensions/events.rst b/docs/reference/extensions/events.rst index 031c1dd09..821d5edbd 100644 --- a/docs/reference/extensions/events.rst +++ b/docs/reference/extensions/events.rst @@ -55,6 +55,7 @@ Configure the transport and durable reconciliation cadence independently: "events": { "backend": "notify_queue", "event_poll_interval": 1.0, + "listener_queue_capacity": 256, } }, ) @@ -64,6 +65,15 @@ queue when no native wakeup arrives. The older ``poll_interval`` setting is a compatibility input; ``event_poll_interval`` takes precedence when both are provided. +``listener_queue_capacity`` bounds pending PostgreSQL notifications separately +for each consumer. It is unbounded when omitted. At capacity, the oldest pending +notification is discarded so the newest notification can be retained. For +``notify`` this intentionally loses the transient notification. For +``notify_queue`` only the wake-up marker is lost; the durable row remains in the +table and reconciliation recovers it. The setting is accepted by every event +store so applications can share configuration across adapters, but it does not +change ``poll_queue`` polling or Oracle AQ and TxEventQ native dequeue behavior. + ``polling`` is not a SQLSpec backend name. Litestar Queues uses it for the fallback worker mode where no push wakeup transport is available and the worker waits for its configured polling interval. @@ -83,6 +93,12 @@ backend owns its own listener hub that: * Serializes subscribe / unsubscribe under a lock so concurrent callers cannot race on driver-level statements that share the connection. +Listener hubs report aggregate pending depth as +``events.listener.queue.depth`` and increment the cumulative +``events.listener.queue.dropped`` metric whenever overflow evicts an item. +Shutdown clears pending payloads and records depth zero; a restarted hub keeps +the runtime's cumulative dropped count. + The listener lease is held for the backend lifetime. Publishers use separate, short-lived pooled sessions, so a shared PostgreSQL pool must configure at least two connections: ``max_size >= 2`` for asyncpg/psycopg and @@ -93,6 +109,8 @@ the listener. The Oracle native backends (``aq`` and ``txeventq``) use an analogous pattern: a per-channel queue-handle cache backed by a single dedicated session per backend instance. +They dequeue directly and do not add the PostgreSQL listener buffer described +above. ``dequeue`` honors ``min(poll_interval, aq_wait_seconds)`` as its wait bound so the caller's polling cadence is respected. diff --git a/docs/reference/extensions/litestar.rst b/docs/reference/extensions/litestar.rst index e6135b93f..d039a7c06 100644 --- a/docs/reference/extensions/litestar.rst +++ b/docs/reference/extensions/litestar.rst @@ -22,6 +22,16 @@ Configuration Channels Backend ================ +``SQLSpecChannelsBackend`` buffers decoded output from every asynchronous +``EventChannel`` transport. Pass ``output_queue_capacity`` to bound that buffer; +the default ``None`` remains unbounded. When full, the backend discards the +oldest decoded message before acknowledging and retaining the newest one. +``output_queue_depth`` reports the current pending count and +``dropped_message_count`` reports cumulative overflow drops for the backend +instance. Malformed payloads are acknowledged and logged without increasing the +overflow count. Shutdown clears pending output while preserving the cumulative +drop diagnostic for lifecycle reuse. + .. autoclass:: sqlspec.extensions.litestar.SQLSpecChannelsBackend :members: :show-inheritance: diff --git a/pyproject.toml b/pyproject.toml index 73d8675fe..cfb360c94 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ maintainers = [{ name = "Litestar Developers", email = "hello@litestar.dev" }] name = "sqlspec" readme = "README.md" requires-python = ">=3.10, <4.0" -version = "0.59.0" +version = "0.60.0" [project.urls] Discord = "https://discord.gg/litestar" @@ -331,7 +331,7 @@ opt_level = "3" # Maximum optimization (0-3) allow_dirty = true commit = false commit_args = "--no-verify" -current_version = "0.59.0" +current_version = "0.60.0" ignore_missing_files = false ignore_missing_version = false message = "chore(release): bump to v{new_version}" diff --git a/sqlspec/adapters/asyncpg/events/_hub.py b/sqlspec/adapters/asyncpg/events/_hub.py index b6370d3be..175f07f1d 100644 --- a/sqlspec/adapters/asyncpg/events/_hub.py +++ b/sqlspec/adapters/asyncpg/events/_hub.py @@ -17,6 +17,7 @@ from sqlspec.exceptions import EventChannelError from sqlspec.extensions.events import normalize_event_channel_name +from sqlspec.extensions.events._buffer import enqueue_with_capacity, resolve_listener_queue_capacity from sqlspec.utils.logging import get_logger, log_with_context from sqlspec.utils.type_guards import has_add_listener @@ -39,6 +40,7 @@ class AsyncpgListenerHub: "_config", "_connection", "_connection_cm", + "_listener_queue_capacity", "_lock", "_pool_destroying_registered", "_queues", @@ -49,6 +51,7 @@ def __init__(self, config: "AsyncpgConfig", backend_name: str = "notify") -> Non self._backend_name = backend_name self._config = config self._lock = asyncio.Lock() + self._listener_queue_capacity = resolve_listener_queue_capacity(config) self._queues: dict[str, WeakKeyDictionary[asyncio.Task[Any], asyncio.Queue[str]]] = {} self._callbacks: dict[str, Callable[..., None]] = {} self._connection_cm: Any | None = None @@ -106,9 +109,12 @@ async def dequeue(self, channel: str, poll_interval: float) -> "str | None": if queue is None: return None try: - return await asyncio.wait_for(queue.get(), timeout=poll_interval) + payload = await asyncio.wait_for(queue.get(), timeout=poll_interval) except asyncio.TimeoutError: return None + else: + self._record_queue_depth() + return payload async def shutdown(self) -> None: async with self._lock: @@ -123,6 +129,7 @@ async def shutdown(self) -> None: self._callbacks.clear() self._connection = None self._connection_cm = None + self._record_queue_depth() if connection is not None: for channel in channels: callback = callbacks.get(channel) @@ -151,7 +158,7 @@ def _get_consumer_queue(self, channel: str) -> "asyncio.Queue[str] | None": return None queue = queues.get(task) if queue is None: - queue = asyncio.Queue() + queue = asyncio.Queue(maxsize=self._listener_queue_capacity or 0) queues[task] = queue return queue @@ -207,7 +214,13 @@ def _dispatch(self, channel: str, payload: str) -> None: if queues is None: return for queue in list(queues.values()): - queue.put_nowait(payload) + if enqueue_with_capacity(queue, payload, self._listener_queue_capacity, empty_error=asyncio.QueueEmpty): + self._config.get_observability_runtime().increment_metric("events.listener.queue.dropped") + self._record_queue_depth() + + def _record_queue_depth(self) -> None: + depth = sum(queue.qsize() for queues in self._queues.values() for queue in queues.values()) + self._config.get_observability_runtime().record_metric("events.listener.queue.depth", depth) def _record_listener_lifecycle(config: "AsyncpgConfig", backend_name: str, status: str) -> None: diff --git a/sqlspec/adapters/psqlpy/events/_hub.py b/sqlspec/adapters/psqlpy/events/_hub.py index 81b523a52..24b30cfb7 100644 --- a/sqlspec/adapters/psqlpy/events/_hub.py +++ b/sqlspec/adapters/psqlpy/events/_hub.py @@ -18,6 +18,7 @@ from sqlspec.core import SQL from sqlspec.extensions.events import normalize_event_channel_name +from sqlspec.extensions.events._buffer import enqueue_with_capacity, resolve_listener_queue_capacity from sqlspec.utils.logging import get_logger, log_with_context from sqlspec.utils.type_guards import is_notification from sqlspec.utils.uuids import uuid4 @@ -79,6 +80,7 @@ class PsqlpyListenerHub: "_callbacks", "_config", "_listener", + "_listener_queue_capacity", "_listener_started", "_lock", "_pool_destroying_registered", @@ -91,6 +93,7 @@ def __init__(self, config: "PsqlpyConfig", backend_name: str = "notify") -> None self._backend_name = backend_name self._config = config self._lock = asyncio.Lock() + self._listener_queue_capacity = resolve_listener_queue_capacity(config) self._queues: dict[str, WeakKeyDictionary[asyncio.Task[Any], asyncio.Queue[str]]] = {} self._callbacks: dict[str, _PsqlpyHubCallback] = {} self._ready_events: dict[str, dict[str, asyncio.Event]] = {} @@ -134,9 +137,12 @@ async def dequeue(self, channel: str, poll_interval: float) -> "str | None": if queue is None: return None try: - return await asyncio.wait_for(queue.get(), timeout=poll_interval) + payload = await asyncio.wait_for(queue.get(), timeout=poll_interval) except asyncio.TimeoutError: return None + else: + self._record_queue_depth() + return payload async def shutdown(self) -> None: async with self._lock: @@ -149,6 +155,7 @@ async def shutdown(self) -> None: self._queues.clear() self._callbacks.clear() self._ready_events.clear() + self._record_queue_depth() self._listener = None self._listener_started = False if listener is not None: @@ -178,7 +185,7 @@ def _get_consumer_queue( return None queue = queues.get(task) if queue is None: - queue = asyncio.Queue() + queue = asyncio.Queue(maxsize=self._listener_queue_capacity or 0) queues[task] = queue return queue @@ -194,7 +201,7 @@ async def _subscribe_locked( queues: WeakKeyDictionary[asyncio.Task[Any], asyncio.Queue[str]] = WeakKeyDictionary() consumer_queue: asyncio.Queue[str] | None = None if consumer_task is not None: - consumer_queue = asyncio.Queue() + consumer_queue = asyncio.Queue(maxsize=self._listener_queue_capacity or 0) queues[consumer_task] = consumer_queue self._queues[channel] = queues self._callbacks[channel] = callback @@ -284,7 +291,13 @@ def _dispatch(self, channel: str, payload: str) -> None: if queues is None: return for queue in list(queues.values()): - queue.put_nowait(payload) + if enqueue_with_capacity(queue, payload, self._listener_queue_capacity, empty_error=asyncio.QueueEmpty): + self._config.get_observability_runtime().increment_metric("events.listener.queue.dropped") + self._record_queue_depth() + + def _record_queue_depth(self) -> None: + depth = sum(queue.qsize() for queues in self._queues.values() for queue in queues.values()) + self._config.get_observability_runtime().record_metric("events.listener.queue.depth", depth) def _record_listener_lifecycle(config: "PsqlpyConfig", backend_name: str, status: str) -> None: diff --git a/sqlspec/adapters/psycopg/events/_hub.py b/sqlspec/adapters/psycopg/events/_hub.py index bfc29238c..3245944ef 100644 --- a/sqlspec/adapters/psycopg/events/_hub.py +++ b/sqlspec/adapters/psycopg/events/_hub.py @@ -20,6 +20,7 @@ from sqlspec.adapters.psycopg._typing import PsycopgComposed, PsycopgIdentifier, PsycopgSQL from sqlspec.exceptions import ImproperConfigurationError from sqlspec.extensions.events import normalize_event_channel_name +from sqlspec.extensions.events._buffer import enqueue_with_capacity, resolve_listener_queue_capacity from sqlspec.utils.logging import get_logger, log_with_context from sqlspec.utils.type_guards import has_notifies @@ -43,6 +44,7 @@ class PsycopgAsyncListenerHub: "_config", "_connection", "_connection_cm", + "_listener_queue_capacity", "_lock", "_pool_destroying_registered", "_pump_task", @@ -55,6 +57,7 @@ def __init__(self, config: "PsycopgAsyncConfig", backend_name: str = "notify") - self._backend_name = backend_name self._config = config self._lock = asyncio.Lock() + self._listener_queue_capacity = resolve_listener_queue_capacity(config) self._queues: dict[str, WeakKeyDictionary[asyncio.Task[Any], asyncio.Queue[str]]] = {} self._connection_cm: Any | None = None self._connection: Any | None = None @@ -100,9 +103,12 @@ async def dequeue(self, channel: str, poll_interval: float) -> "str | None": if queue is None: return None try: - return await asyncio.wait_for(queue.get(), timeout=poll_interval) + payload = await asyncio.wait_for(queue.get(), timeout=poll_interval) except asyncio.TimeoutError: return None + else: + self._record_queue_depth() + return payload async def shutdown(self) -> None: async with self._lock: @@ -115,6 +121,7 @@ async def shutdown(self) -> None: pump_task = self._pump_task channels = list(self._queues.keys()) self._queues.clear() + self._record_queue_depth() self._connection = None self._connection_cm = None self._pump_task = None @@ -146,7 +153,7 @@ def _get_consumer_queue(self, channel: str) -> "asyncio.Queue[str] | None": return None queue = queues.get(task) if queue is None: - queue = asyncio.Queue() + queue = asyncio.Queue(maxsize=self._listener_queue_capacity or 0) queues[task] = queue return queue @@ -235,7 +242,13 @@ def _dispatch(self, channel: str, payload: str) -> None: if queues is None: return for queue in list(queues.values()): - queue.put_nowait(payload) + if enqueue_with_capacity(queue, payload, self._listener_queue_capacity, empty_error=asyncio.QueueEmpty): + self._config.get_observability_runtime().increment_metric("events.listener.queue.dropped") + self._record_queue_depth() + + def _record_queue_depth(self) -> None: + depth = sum(queue.qsize() for queues in self._queues.values() for queue in queues.values()) + self._config.get_observability_runtime().record_metric("events.listener.queue.depth", depth) class PsycopgSyncListenerHub: @@ -253,6 +266,7 @@ class PsycopgSyncListenerHub: "_config", "_connection", "_connection_cm", + "_listener_queue_capacity", "_lock", "_pool_destroying_registered", "_queues", @@ -265,6 +279,7 @@ def __init__(self, config: "PsycopgSyncConfig", backend_name: str = "notify") -> self._backend_name = backend_name self._config = config self._lock = threading.Lock() + self._listener_queue_capacity = resolve_listener_queue_capacity(config) self._queues: dict[str, WeakKeyDictionary[threading.Thread, stdlib_queue.Queue[str]]] = {} self._connection_cm: Any | None = None self._connection: Any | None = None @@ -305,9 +320,12 @@ def dequeue(self, channel: str, poll_interval: float) -> "str | None": if queue is None: return None try: - return queue.get(timeout=poll_interval) + payload = queue.get(timeout=poll_interval) except stdlib_queue.Empty: return None + else: + self._record_queue_depth() + return payload def shutdown(self) -> None: with self._lock: @@ -318,6 +336,7 @@ def shutdown(self) -> None: channels = list(self._queues.keys()) worker_thread = self._worker_thread self._queues.clear() + self._record_queue_depth() self._connection_cm = None self._worker_thread = None if worker_thread is not None: @@ -349,7 +368,7 @@ def _get_consumer_queue(self, channel: str) -> "stdlib_queue.Queue[str] | None": thread = threading.current_thread() queue = queues.get(thread) if queue is None: - queue = stdlib_queue.Queue() + queue = stdlib_queue.Queue(maxsize=self._listener_queue_capacity or 0) queues[thread] = queue return queue @@ -467,7 +486,13 @@ def _dispatch(self, channel: str, payload: str) -> None: return targets = list(queues.values()) for queue in targets: - queue.put_nowait(payload) + if enqueue_with_capacity(queue, payload, self._listener_queue_capacity, empty_error=stdlib_queue.Empty): + self._config.get_observability_runtime().increment_metric("events.listener.queue.dropped") + self._record_queue_depth() + + def _record_queue_depth(self) -> None: + depth = sum(queue.qsize() for queues in self._queues.values() for queue in queues.values()) + self._config.get_observability_runtime().record_metric("events.listener.queue.depth", depth) def _record_listener_lifecycle( diff --git a/sqlspec/config.py b/sqlspec/config.py index 30e4b3c9f..e926150b0 100644 --- a/sqlspec/config.py +++ b/sqlspec/config.py @@ -792,6 +792,9 @@ class EventsConfig(TypedDict): event_poll_interval: NotRequired[float] """Durable event reconciliation interval in seconds. Takes precedence over poll_interval.""" + listener_queue_capacity: NotRequired[int] + """Maximum pending notifications per PostgreSQL listener consumer. Defaults to unbounded.""" + select_for_update: NotRequired[bool] """Use SELECT FOR UPDATE locking when claiming events. Defaults to False.""" diff --git a/sqlspec/extensions/events/_buffer.py b/sqlspec/extensions/events/_buffer.py new file mode 100644 index 000000000..262d0254b --- /dev/null +++ b/sqlspec/extensions/events/_buffer.py @@ -0,0 +1,38 @@ +"""Bounded event buffering helpers.""" + +from typing import Any + +from sqlspec.exceptions import ImproperConfigurationError + +__all__ = ("enqueue_with_capacity", "resolve_listener_queue_capacity", "validate_queue_capacity") + + +def validate_queue_capacity( + value: object, *, name: str, error_type: "type[Exception]" = ImproperConfigurationError +) -> "int | None": + """Validate an optional positive queue capacity.""" + if value is None: + return None + if isinstance(value, bool) or not isinstance(value, int) or value <= 0: + msg = f"{name} must be a positive integer or None" + raise error_type(msg) + return value + + +def resolve_listener_queue_capacity(config: Any) -> "int | None": + """Resolve and validate the PostgreSQL listener queue capacity.""" + settings = getattr(config, "extension_config", {}).get("events", {}) + return validate_queue_capacity(settings.get("listener_queue_capacity"), name="listener_queue_capacity") + + +def enqueue_with_capacity(queue: Any, item: object, capacity: "int | None", *, empty_error: "type[Exception]") -> bool: + """Enqueue an item, evicting the oldest item when capacity is reached.""" + dropped = False + if capacity is not None and queue.qsize() >= capacity: + try: + queue.get_nowait() + dropped = True + except empty_error: + pass + queue.put_nowait(item) + return dropped diff --git a/sqlspec/extensions/events/_channel.py b/sqlspec/extensions/events/_channel.py index b85c26204..407ccca30 100644 --- a/sqlspec/extensions/events/_channel.py +++ b/sqlspec/extensions/events/_channel.py @@ -12,6 +12,7 @@ from typing import TYPE_CHECKING, Any, cast from sqlspec.exceptions import ImproperConfigurationError, MissingDependencyError +from sqlspec.extensions.events._buffer import validate_queue_capacity from sqlspec.extensions.events._hints import get_runtime_hints, resolve_adapter_name from sqlspec.extensions.events._models import EventMessage from sqlspec.extensions.events._names import normalize_event_channel_name @@ -194,6 +195,7 @@ def __init__(self, config: "SyncDatabaseConfig[Any, Any, Any]") -> None: msg = "SyncEventChannel requires a sync configuration" raise ImproperConfigurationError(msg) extension_settings: dict[str, Any] = dict(config.extension_config.get("events", {})) + validate_queue_capacity(extension_settings.get("listener_queue_capacity"), name="listener_queue_capacity") self._adapter_name = resolve_adapter_name(config) hints = get_runtime_hints(self._adapter_name, config) self._event_poll_interval = resolve_event_poll_interval( @@ -463,6 +465,7 @@ def __init__(self, config: "AsyncDatabaseConfig[Any, Any, Any]") -> None: msg = "AsyncEventChannel requires an async configuration" raise ImproperConfigurationError(msg) extension_settings: dict[str, Any] = dict(config.extension_config.get("events", {})) + validate_queue_capacity(extension_settings.get("listener_queue_capacity"), name="listener_queue_capacity") self._adapter_name = resolve_adapter_name(config) hints = get_runtime_hints(self._adapter_name, config) self._event_poll_interval = resolve_event_poll_interval( diff --git a/sqlspec/extensions/events/_store.py b/sqlspec/extensions/events/_store.py index 55c73b694..a2e7c1e5d 100644 --- a/sqlspec/extensions/events/_store.py +++ b/sqlspec/extensions/events/_store.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, Any, ClassVar, Generic, TypeVar, cast from sqlspec.exceptions import ImproperConfigurationError +from sqlspec.extensions.events._buffer import validate_queue_capacity from sqlspec.extensions.events._names import normalize_event_channel_name, normalize_queue_table_name from sqlspec.migrations.schema import SchemaEnsureResult, SchemaTarget, ensure_schema_async, ensure_schema_sync @@ -39,6 +40,7 @@ class BaseEventQueueStore(ABC, Generic[ConfigT]): "create_schema", "event_poll_interval", "lease_seconds", + "listener_queue_capacity", "manage_schema", "migrations_path", "poll_interval", @@ -115,6 +117,7 @@ def _validate_extension_config(self) -> None: keys = ", ".join(repr(key) for key in unsupported) msg = f"Unsupported events configuration key(s) for {adapter}: {keys}" raise ImproperConfigurationError(msg) + validate_queue_capacity(self._extension_settings.get("listener_queue_capacity"), name="listener_queue_capacity") def _schema_management_flags(self) -> "tuple[bool, bool]": """Return automatic-management and missing-table creation flags.""" diff --git a/sqlspec/extensions/litestar/channels.py b/sqlspec/extensions/litestar/channels.py index 3bcea2c5a..f5a08ef49 100644 --- a/sqlspec/extensions/litestar/channels.py +++ b/sqlspec/extensions/litestar/channels.py @@ -8,6 +8,7 @@ from litestar.channels.backends.base import ChannelsBackend +from sqlspec.extensions.events._buffer import enqueue_with_capacity, validate_queue_capacity from sqlspec.utils.logging import get_logger if TYPE_CHECKING: @@ -29,7 +30,12 @@ class SQLSpecChannelsBackend(ChannelsBackend): """ def __init__( - self, event_channel: "AsyncEventChannel", *, channel_prefix: str = "litestar", poll_interval: float = 0.2 + self, + event_channel: "AsyncEventChannel", + *, + channel_prefix: str = "litestar", + poll_interval: float = 0.2, + output_queue_capacity: int | None = None, ) -> None: if not _IDENTIFIER_PATTERN.match(channel_prefix): msg = f"channel_prefix must be a valid identifier, got: {channel_prefix!r}" @@ -37,10 +43,14 @@ def __init__( if poll_interval <= 0: msg = "poll_interval must be greater than zero" raise ValueError(msg) + self._output_queue_capacity = validate_queue_capacity( + output_queue_capacity, name="output_queue_capacity", error_type=ValueError + ) self._event_channel = event_channel self._channel_prefix = channel_prefix self._poll_interval = poll_interval self._output_queue: asyncio.Queue[tuple[str, bytes]] | None = None + self._dropped_message_count = 0 self._shutdown = asyncio.Event() self._tasks: dict[str, asyncio.Task[None]] = {} self._to_db_channel: dict[str, str] = {} @@ -61,8 +71,19 @@ async def on_shutdown(self) -> None: await asyncio.gather(*tasks, return_exceptions=True) self._to_db_channel.clear() self._to_litestar_channel.clear() + self._output_queue = None await self._event_channel.shutdown() + @property + def output_queue_depth(self) -> int: + """Return the number of buffered channel messages.""" + return self._output_queue.qsize() if self._output_queue is not None else 0 + + @property + def dropped_message_count(self) -> int: + """Return the cumulative number of messages dropped due to overflow.""" + return self._dropped_message_count + async def publish(self, data: bytes, channels: "Iterable[str]") -> None: await self.publish_many((data,), channels) @@ -152,7 +173,10 @@ async def _stream_channel(self, channel: str, db_channel: str) -> None: await self._event_channel.ack(message.event_id) continue assert self._output_queue is not None - await self._output_queue.put((channel, decoded)) + if enqueue_with_capacity( + self._output_queue, (channel, decoded), self._output_queue_capacity, empty_error=asyncio.QueueEmpty + ): + self._dropped_message_count += 1 await self._event_channel.ack(message.event_id) except asyncio.CancelledError: raise diff --git a/tests/integration/extensions/litestar/test_channels_backend.py b/tests/integration/extensions/litestar/test_channels_backend.py index afda08891..f490a1977 100644 --- a/tests/integration/extensions/litestar/test_channels_backend.py +++ b/tests/integration/extensions/litestar/test_channels_backend.py @@ -1,8 +1,10 @@ import asyncio import tempfile +from types import SimpleNamespace from typing import Any, cast import msgspec.json +import pytest from litestar.channels.plugin import ChannelsPlugin from sqlspec.adapters.aiosqlite.config import AiosqliteConfig @@ -27,6 +29,24 @@ async def publish_many(self, events: "list[tuple[str, dict[str, str], None]]") - return [f"event-{index}" for index in range(len(events))] +class _StreamingEventChannel: + def __init__(self, payloads: list[dict[str, str]]) -> None: + self.payloads = payloads + self.acked: list[str] = [] + self.shutdown_calls = 0 + + async def iter_events(self, _channel: str, *, poll_interval: float) -> "Any": + _ = poll_interval + for index, payload in enumerate(self.payloads): + yield SimpleNamespace(event_id=str(index), payload=payload) + + async def ack(self, event_id: str) -> None: + self.acked.append(event_id) + + async def shutdown(self) -> None: + self.shutdown_calls += 1 + + async def test_litestar_channels_backend_database_roundtrip(tmp_path: "Any") -> None: migrations = tmp_path / "migrations" migrations.mkdir() @@ -91,3 +111,45 @@ async def test_litestar_channels_backend_groups_multiple_payloads_and_channels() (backend._db_channel_name("alpha"), "c2Vjb25k"), (backend._db_channel_name("beta"), "c2Vjb25k"), ] + + +@pytest.mark.parametrize("capacity", [True, False, 0, -1, 1.5, "1"]) +def test_litestar_channels_backend_rejects_invalid_output_capacity(capacity: object) -> None: + with pytest.raises(ValueError, match="output_queue_capacity must be a positive integer"): + SQLSpecChannelsBackend(cast("Any", _RecordingEventChannel()), output_queue_capacity=capacity) # type: ignore[arg-type] + + +async def test_litestar_channels_backend_drops_oldest_and_preserves_acknowledgements() -> None: + event_channel = _StreamingEventChannel([ + {"data_b64": "Zmlyc3Q="}, + {"data_b64": "c2Vjb25k"}, + {"data_b64": "dGhpcmQ="}, + ]) + backend = SQLSpecChannelsBackend(cast("Any", event_channel), output_queue_capacity=2) + await backend.on_startup() + + await backend._stream_channel("alerts", backend._db_channel_name("alerts")) + + assert backend.output_queue_depth == 2 + assert backend.dropped_message_count == 1 + assert event_channel.acked == ["0", "1", "2"] + stream = backend.stream_events() + assert await anext(stream) == ("alerts", b"second") + assert await anext(stream) == ("alerts", b"third") + assert backend.output_queue_depth == 0 + + await backend.on_shutdown() + assert backend.output_queue_depth == 0 + assert backend.dropped_message_count == 1 + + +async def test_litestar_channels_backend_malformed_payload_does_not_count_as_overflow() -> None: + event_channel = _StreamingEventChannel([{"invalid": "payload"}]) + backend = SQLSpecChannelsBackend(cast("Any", event_channel), output_queue_capacity=1) + await backend.on_startup() + + await backend._stream_channel("alerts", backend._db_channel_name("alerts")) + + assert backend.output_queue_depth == 0 + assert backend.dropped_message_count == 0 + assert event_channel.acked == ["0"] diff --git a/tests/unit/extensions/test_events/test_adapter_stores.py b/tests/unit/extensions/test_events/test_adapter_stores.py index 503ff18e1..427e3f3cd 100644 --- a/tests/unit/extensions/test_events/test_adapter_stores.py +++ b/tests/unit/extensions/test_events/test_adapter_stores.py @@ -1,6 +1,9 @@ # pyright: reportPrivateUsage=false """Unit tests for adapter-specific event queue stores and DDL generation.""" +import importlib +import inspect +from pathlib import Path from typing import TYPE_CHECKING import pytest @@ -16,6 +19,25 @@ BaseEventQueueStoreBase = BaseEventQueueStore +def test_all_adapter_event_stores_declare_listener_queue_capacity_behavior() -> None: + adapter_root = Path(__file__).parents[4] / "sqlspec" / "adapters" + store_paths = sorted(adapter_root.glob("*/events/store.py")) + discovered: set[str] = set() + + for store_path in store_paths: + adapter_name = store_path.parents[1].name + module = importlib.import_module(f"sqlspec.adapters.{adapter_name}.events.store") + for _, store_type in inspect.getmembers(module, inspect.isclass): + if store_type is BaseEventQueueStore or not issubclass(store_type, BaseEventQueueStore): + continue + if store_type.__module__ != module.__name__: + continue + discovered.add(adapter_name) + assert "listener_queue_capacity" in store_type.extension_config_options + + assert discovered == {path.parents[1].name for path in store_paths} + + def test_asyncmy_store_column_types() -> None: """Asyncmy store uses MySQL-compatible column types.""" pytest.importorskip("asyncmy") diff --git a/tests/unit/extensions/test_events/test_events_config.py b/tests/unit/extensions/test_events/test_events_config.py index 0c676fe96..a9b1809bd 100644 --- a/tests/unit/extensions/test_events/test_events_config.py +++ b/tests/unit/extensions/test_events/test_events_config.py @@ -8,6 +8,8 @@ from sqlspec.adapters.sqlite import SqliteConfig from sqlspec.config import EventsConfig +from sqlspec.exceptions import ImproperConfigurationError +from sqlspec.extensions.events import SyncEventChannel def test_events_backend_literal_uses_canonical_transport_names() -> None: @@ -20,6 +22,28 @@ def test_events_backend_literal_uses_canonical_transport_names() -> None: assert set(backend_values) == {"notify", "notify_queue", "poll_queue", "aq", "txeventq"} +@pytest.mark.parametrize("capacity", [True, False, 0, -1, 1.5, "1", []]) +def test_listener_queue_capacity_rejects_non_positive_integers(tmp_path: Path, capacity: object) -> None: + config = SqliteConfig( + connection_config={"database": str(tmp_path / "events-capacity.db")}, + extension_config={"events": {"listener_queue_capacity": capacity}}, # type: ignore[typeddict-item] + ) + + with pytest.raises(ImproperConfigurationError, match="listener_queue_capacity must be a positive integer"): + SyncEventChannel(config) + + +def test_listener_queue_capacity_is_accepted_by_poll_queue_store(tmp_path: Path) -> None: + from sqlspec.adapters.sqlite.events.store import SqliteEventQueueStore + + config = SqliteConfig( + connection_config={"database": str(tmp_path / "events-capacity.db")}, + extension_config={"events": {"listener_queue_capacity": 4}}, + ) + + assert SqliteEventQueueStore(config).settings["listener_queue_capacity"] == 4 + + _POSTGRES_DRIVER_FEATURES = ( ("sqlspec.adapters.asyncpg.config", "AsyncpgDriverFeatures"), ("sqlspec.adapters.psycopg.config", "PsycopgDriverFeatures"), diff --git a/tests/unit/extensions/test_events/test_listener_hubs.py b/tests/unit/extensions/test_events/test_listener_hubs.py index 514128a2e..4421ddd66 100644 --- a/tests/unit/extensions/test_events/test_listener_hubs.py +++ b/tests/unit/extensions/test_events/test_listener_hubs.py @@ -75,22 +75,28 @@ async def remove_listener(self, channel: str, _callback: Any) -> None: class _StubRuntime: def __init__(self) -> None: - self.metrics: list[str] = [] + self.metrics: list[str | tuple[str, float]] = [] self.registered: list[tuple[str, Any]] = [] def increment_metric(self, metric: str) -> None: self.metrics.append(metric) + def record_metric(self, metric: str, value: float) -> None: + self.metrics.append((metric, value)) + def register_lifecycle_hook(self, event: str, callback: Any) -> None: self.registered.append((event, callback)) class _AsyncpgConfig: - def __init__(self, connection: _AsyncpgConnection, *replacement_connections: _AsyncpgConnection) -> None: + def __init__( + self, connection: _AsyncpgConnection, *replacement_connections: _AsyncpgConnection, capacity: int | None = None + ) -> None: self.connections = [connection, *replacement_connections] self.connection_enters = 0 self.connection_exits = 0 self._runtime = _StubRuntime() + self.extension_config = {"events": {"listener_queue_capacity": capacity}} if capacity is not None else {} def provide_connection(self) -> _AsyncConnectionContext: connection = self.connections[min(self.connection_enters, len(self.connections) - 1)] @@ -158,10 +164,16 @@ def listener(self) -> _PsqlpyListener: class _PsqlpyConfig: - def __init__(self, listener_handle: _PsqlpyListener | None = None, *replacement_listeners: _PsqlpyListener) -> None: + def __init__( + self, + listener_handle: _PsqlpyListener | None = None, + *replacement_listeners: _PsqlpyListener, + capacity: int | None = None, + ) -> None: self.listener_handle = listener_handle or _PsqlpyListener() self.pool = _PsqlpyPool(self.listener_handle, *replacement_listeners) self._runtime = _StubRuntime() + self.extension_config = {"events": {"listener_queue_capacity": capacity}} if capacity is not None else {} async def provide_pool(self) -> _PsqlpyPool: return self.pool @@ -220,11 +232,17 @@ async def _empty() -> AsyncIterator[Any]: class _PsycopgAsyncConfig: - def __init__(self, connection: _PsycopgAsyncConnection, *replacement_connections: _PsycopgAsyncConnection) -> None: + def __init__( + self, + connection: _PsycopgAsyncConnection, + *replacement_connections: _PsycopgAsyncConnection, + capacity: int | None = None, + ) -> None: self.connections = [connection, *replacement_connections] self.connection_enters = 0 self.connection_exits = 0 self._runtime = _StubRuntime() + self.extension_config = {"events": {"listener_queue_capacity": capacity}} if capacity is not None else {} def provide_connection(self) -> _AsyncConnectionContext: connection = self.connections[min(self.connection_enters, len(self.connections) - 1)] @@ -250,11 +268,17 @@ def notifies(self, *, timeout: float, stop_after: int) -> list[Any]: class _PsycopgSyncConfig: - def __init__(self, connection: _PsycopgSyncConnection, *replacement_connections: _PsycopgSyncConnection) -> None: + def __init__( + self, + connection: _PsycopgSyncConnection, + *replacement_connections: _PsycopgSyncConnection, + capacity: int | None = None, + ) -> None: self.connections = [connection, *replacement_connections] self.connection_enters = 0 self.connection_exits = 0 self._runtime = _StubRuntime() + self.extension_config = {"events": {"listener_queue_capacity": capacity}} if capacity is not None else {} def provide_connection(self) -> _SyncConnectionContext: connection = self.connections[min(self.connection_enters, len(self.connections) - 1)] @@ -632,6 +656,31 @@ async def receive_once() -> str | None: await hub.shutdown() +@pytest.mark.parametrize("hub_kind", ["asyncpg", "psqlpy", "psycopg"]) +async def test_async_postgres_listener_hubs_drop_oldest_per_consumer(hub_kind: str) -> None: + if hub_kind == "asyncpg": + config = _AsyncpgConfig(_AsyncpgConnection(), capacity=2) + hub = AsyncpgListenerHub(config) # type: ignore[arg-type] + elif hub_kind == "psqlpy": + config = _PsqlpyConfig(capacity=2) + hub = PsqlpyListenerHub(config) # type: ignore[arg-type] + else: + config = _PsycopgAsyncConfig(_PsycopgAsyncConnection(), capacity=2) + hub = PsycopgAsyncListenerHub(config) # type: ignore[arg-type] + + await hub.subscribe("alerts") + assert hub._get_consumer_queue("alerts") is not None + hub._dispatch("alerts", "first") + hub._dispatch("alerts", "second") + hub._dispatch("alerts", "third") + + assert await hub.dequeue("alerts", 0.01) == "second" + assert await hub.dequeue("alerts", 0.01) == "third" + assert "events.listener.queue.dropped" in config.get_observability_runtime().metrics + await hub.shutdown() + assert ("events.listener.queue.depth", 0) in config.get_observability_runtime().metrics + + async def test_asyncpg_listener_hub_reconnects_and_releases_listener_contexts() -> None: first = _AsyncpgConnection() replacement = _AsyncpgConnection() @@ -695,6 +744,23 @@ def receive_once() -> None: hub.shutdown() +def test_psycopg_sync_listener_hub_drops_oldest_per_consumer() -> None: + config = _PsycopgSyncConfig(_PsycopgSyncConnection(), capacity=2) + hub = PsycopgSyncListenerHub(config) # type: ignore[arg-type] + hub.subscribe("alerts") + assert hub._get_consumer_queue("alerts") is not None + + hub._dispatch("alerts", "first") + hub._dispatch("alerts", "second") + hub._dispatch("alerts", "third") + + assert hub.dequeue("alerts", 0.01) == "second" + assert hub.dequeue("alerts", 0.01) == "third" + assert "events.listener.queue.dropped" in config.get_observability_runtime().metrics + hub.shutdown() + assert ("events.listener.queue.depth", 0) in config.get_observability_runtime().metrics + + def test_psycopg_sync_listener_hub_reconnects_and_releases_listener_contexts() -> None: first = _PsycopgSyncConnection() replacement = _PsycopgSyncConnection() @@ -866,7 +932,7 @@ def test_oracle_aq_options_round_subsecond_wait_up_to_one_second() -> None: def test_psycopg_sync_shutdown_submits_stop_before_setting_stop_flag(monkeypatch: pytest.MonkeyPatch) -> None: - hub = PsycopgSyncListenerHub(object()) # type: ignore[arg-type] + hub = PsycopgSyncListenerHub(_PsycopgSyncConfig(_PsycopgSyncConnection())) # type: ignore[arg-type] thread = _Thread() stop_states: list[bool] = [] diff --git a/uv.lock b/uv.lock index a3eea3fc3..f54cce20c 100644 --- a/uv.lock +++ b/uv.lock @@ -809,112 +809,112 @@ wheels = [ [[package]] name = "cffi" -version = "2.1.0" +version = "2.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz", hash = "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9", size = 531036, upload-time = "2026-07-06T21:34:30.382Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/e9/6d7724983b3d5a0908dbf74f64038ade77c18646ff6636ec7894fd392ce1/cffi-2.1.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:b65f590ef2a44640f9a05dbb548a429b4ade77913ce683ac8b1480777658a6c0", size = 183837, upload-time = "2026-07-06T21:32:09.655Z" }, - { url = "https://files.pythonhosted.org/packages/69/aa/24580a278de21fd7322635556334d9b535f1cbc00b0a3919447cdf464c65/cffi-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164bff1657b2a74f0b6d54e11c9b375bc97b931f2ca9c43fcf875838da1570dd", size = 184226, upload-time = "2026-07-06T21:32:11.196Z" }, - { url = "https://files.pythonhosted.org/packages/88/a9/02cae418ec4beb282ace11958d9d4737793439d561fadc7e6d56f2e2b354/cffi-2.1.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:c941bb58d5a6e1c3892d86e42927ed6c180302f07e6d395d08c416e594b98b46", size = 211107, upload-time = "2026-07-06T21:32:12.328Z" }, - { url = "https://files.pythonhosted.org/packages/3b/30/c806937ed5e4c2c7ac30d9d6b76b5dc57ff8b75d83800d9bb11a8253cf2a/cffi-2.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a016194dbe13d14ee9556e734b772d8d67b947092b268d757fd4290e3ba2dfc2", size = 218733, upload-time = "2026-07-06T21:32:13.67Z" }, - { url = "https://files.pythonhosted.org/packages/f9/cf/398272b8bbfd58aa314fda5a7f1cdbb26d1d78ae324a11211521315dd1f0/cffi-2.1.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:03e9810d18c646077e501f661b682fbf5dee4676048527ca3cffe66faa9960dd", size = 205543, upload-time = "2026-07-06T21:32:15.148Z" }, - { url = "https://files.pythonhosted.org/packages/45/ca/f91641185cdd90c36d317a9dc7f85e88ef8682d8b300977baff5e23c35d8/cffi-2.1.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:19c54ac121cad98450b4896fa9a43ee0180d57bc4bc911a33db6cab1efab6cd3", size = 205460, upload-time = "2026-07-06T21:32:16.479Z" }, - { url = "https://files.pythonhosted.org/packages/38/66/04781a77b411f0bb5b234d62c1814754ab75ebe455ccff1b08e8d7aae98f/cffi-2.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d433a51f1870e43a13b6732f92aaf540ff77c2015097c78556f75a2d6c030e0", size = 218760, upload-time = "2026-07-06T21:32:17.98Z" }, - { url = "https://files.pythonhosted.org/packages/d0/9a/bb1d5ed9c3fcae158e9f6391bf309c95d98c2ac37ed56573228471d0af5e/cffi-2.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3d7f118b5adbfdfead90c25822690b02bc8074fba949bb7858bec4ebd55adb43", size = 221230, upload-time = "2026-07-06T21:32:19.407Z" }, - { url = "https://files.pythonhosted.org/packages/41/aa/3c1409cdd26094efacd1c36c66e0a6eb9d4296e4fd4f9901b8b2042f4323/cffi-2.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5f5df567f6eb216de69be06ce55c8b714090fae02b18a3b40da8163b8c5fa9c", size = 213524, upload-time = "2026-07-06T21:32:20.828Z" }, - { url = "https://files.pythonhosted.org/packages/fa/75/74dfb7c3fc6ebbd408038476bd4c1d7e925c62614e7b9c534ecc34218288/cffi-2.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:11b3fb55f4f8ad92274ed26705f65d8f91457de71f5380061eb6d125a768fecd", size = 220341, upload-time = "2026-07-06T21:32:21.9Z" }, - { url = "https://files.pythonhosted.org/packages/70/b6/9003c33a3e7d2c1306f5962e646457dcfe5a8cd8fce6bbe02d7af25db783/cffi-2.1.0-cp310-cp310-win32.whl", hash = "sha256:9d72af0cf10a76a600a9690078fe31c63b9588c8e86bf9fd353f713c84b5db0f", size = 174578, upload-time = "2026-07-06T21:32:23.073Z" }, - { url = "https://files.pythonhosted.org/packages/8a/26/710688310447531c7a22f857c7f79d9855ec18b03e04494ced723fb37e2f/cffi-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb62edb5bb52cca65fab91a63afa7561607120d26090a7e8fda6fb9f064726da", size = 185071, upload-time = "2026-07-06T21:32:24.671Z" }, - { url = "https://files.pythonhosted.org/packages/d3/67/85c89a59ba36a671e79638f44d466749f08179266a57e4f2ffdf92174072/cffi-2.1.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:02cb7ff33ded4f1532476731f89ede53e2e488a8e6205515a82144246ffa7dcc", size = 183845, upload-time = "2026-07-06T21:32:26.32Z" }, - { url = "https://files.pythonhosted.org/packages/ea/dd/e3b0baa2d3d6a857ac72b7efbf18e32e487c9cdafcc13049ad765495b15e/cffi-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f5bce581e6b8c235e566a14768a943b172ada3ed73537bb0c0be1edee312d4e7", size = 184186, upload-time = "2026-07-06T21:32:28.025Z" }, - { url = "https://files.pythonhosted.org/packages/65/68/9f3ef890cf3c6ab97bd531c5677f67613d302165d16f8142b2811782a614/cffi-2.1.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:30b65779d598c370374fefabf138d456fd6f3216bfa7bedfab1ba82025b0cd93", size = 211892, upload-time = "2026-07-06T21:32:29.565Z" }, - { url = "https://files.pythonhosted.org/packages/22/d7/1a74539db16d8bfd839ff1515948948efbb162e574650fd3d846896eea95/cffi-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88023dfe18799507b73f1dbb0d14326a17465de1bc9c9c7655c22845e9ddc3a2", size = 218793, upload-time = "2026-07-06T21:32:30.951Z" }, - { url = "https://files.pythonhosted.org/packages/ec/d1/9a5b7169499e8e8d8e636de70b97ac7c9447104d2ff1a2cd94790cea5162/cffi-2.1.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0a96b74cda968eebbad56d973efe5098974f0a9fb323865bf99ea1fd24e3e64c", size = 205737, upload-time = "2026-07-06T21:32:32.216Z" }, - { url = "https://files.pythonhosted.org/packages/ba/b0/e131a9c41f10607926278453d9596163594fe1c4ebc46efe3b5e5b34eb84/cffi-2.1.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a5781494d4d400a3f47f8f1da94b324f6e6b440a53387774002890a2a2f4b50f", size = 204909, upload-time = "2026-07-06T21:32:33.655Z" }, - { url = "https://files.pythonhosted.org/packages/fb/d2/4398416cd699b35167947c6e22aca52c47e69ad5695073c9f1f2c52e04aa/cffi-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aa7a1b53a2a4452ada2d1b5dade9960b2522f1e61293a811a077439e39029565", size = 217883, upload-time = "2026-07-06T21:32:35.173Z" }, - { url = "https://files.pythonhosted.org/packages/a2/a5/d4fe77b589e5e82d43ebc809bf2e6474afe8e48e32ea050b9357645b6471/cffi-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d8272c0e483b024e1b9ad029821470ed8ec65631dbd90217469da0e7cd89f1c", size = 221251, upload-time = "2026-07-06T21:32:36.527Z" }, - { url = "https://files.pythonhosted.org/packages/22/f0/a2fc43084c0433caf7f461bccc013e28f848d04ee1c5ed7fce71423cf4d9/cffi-2.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7762faa47e8ff7eb80bd261d9a7d8eea2d8baa69de5e95b70c1f338bbe712f02", size = 214250, upload-time = "2026-07-06T21:32:37.852Z" }, - { url = "https://files.pythonhosted.org/packages/04/8c/b925975448cf20634a9fbd5efceb807219db452653648d2897c0989cab2d/cffi-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:89095c1968b4ba8285840e131bf2891b09ae137fe2146905acae0354fbce1b5e", size = 219441, upload-time = "2026-07-06T21:32:39.146Z" }, - { url = "https://files.pythonhosted.org/packages/eb/da/5c4918a2d61d86fa927d716cb3d8e4626ef8dc8f605a599d32f33897f59a/cffi-2.1.0-cp311-cp311-win32.whl", hash = "sha256:64c753a0f87a256020004f37a1c8c02c480e725f910f0b2a0f3f07debd1b2479", size = 174496, upload-time = "2026-07-06T21:32:40.467Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c8/6c2de1d55cf35ef8b92885d5ef280790f0fb9634d87ea1cc315176aecd61/cffi-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:4f26194e3d95e06501b942642855aed4f953d55e95d7d01b7c4483db3ecff458", size = 185113, upload-time = "2026-07-06T21:32:41.761Z" }, - { url = "https://files.pythonhosted.org/packages/9e/4e/e8d7cb5783f1841a3c8fb3a7735838d7484d08ec08c9f984b14cac1ac0e9/cffi-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:35aaea0c7ee0e58a5cd8c2fd1a48fdf7ece0d2699b7ecdda08194e9ce5dd9b3d", size = 179927, upload-time = "2026-07-06T21:32:42.961Z" }, - { url = "https://files.pythonhosted.org/packages/1e/85/990925db5df586ec90beb97529c853497e7f85ba0234830447faf41c3057/cffi-2.1.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:df2b82571a1b30f58a87bf4e5a9e78d2b1eff6c6ce8fd3aa3757221f93f0863f", size = 184829, upload-time = "2026-07-06T21:32:44.324Z" }, - { url = "https://files.pythonhosted.org/packages/4b/92/e7bb136ad6b5352603732cf907ef862ca103f20f2031c1735a46300c20c9/cffi-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78474632761faa0fb96f30b1c928c84ebcf68713cbb80d15bab09dfe61640fde", size = 184728, upload-time = "2026-07-06T21:32:45.683Z" }, - { url = "https://files.pythonhosted.org/packages/c3/c0/d1ec30ffb370f748f2fb54425972bfef9871e0132e82fb589c46b6676049/cffi-2.1.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:5972433ad71a9e46516584ef60a0fda12d9dc459938d1539c3ddecf9bdc1368d", size = 214815, upload-time = "2026-07-06T21:32:48.557Z" }, - { url = "https://files.pythonhosted.org/packages/1b/dc/5620cf930688be01f2d673804291de757a934c90b946dbdc3d84130c2ea4/cffi-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b6422532152adf4e59b110cb2808cee7a033800952f5c036b4af047ee43199e7", size = 222429, upload-time = "2026-07-06T21:32:49.848Z" }, - { url = "https://files.pythonhosted.org/packages/4b/a4/77b53abbf7a1e0beb9637edbef2a94d15f9c822f591e85d439ffd91519a6/cffi-2.1.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:46b1c8db8f6122420f32d02fffb924c2fe9bc772d228c7c711748fff56aabb2b", size = 210315, upload-time = "2026-07-06T21:32:51.221Z" }, - { url = "https://files.pythonhosted.org/packages/58/0c/f528df19cc94b675087324d4760d9e6d5bfae97d6217aa4fac43de4f5fcc/cffi-2.1.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9fafc5aa2e2a39aaf7f8cc0c1f044a9b07fca12e558dca53a3cc5c654ad67a7", size = 208859, upload-time = "2026-07-06T21:32:52.512Z" }, - { url = "https://files.pythonhosted.org/packages/62/f2/c9522a81c32132799a1972c39f5c5f8b4c8b9f00488a23feaa6c06f07741/cffi-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1e9f50d192a3e525b15a75ab5114e442d83d657b7ec29182a991bc9a88fd3a66", size = 221844, upload-time = "2026-07-06T21:32:53.704Z" }, - { url = "https://files.pythonhosted.org/packages/6e/28/bd53988b9833e8f8ad539d26f4c07a6b3f6bcb1e9e02e7ca038250b3428d/cffi-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98fff996e983a36d3aa2eca83af40c5821202e7e6f32d13ae94e3d2286f10cfe", size = 225287, upload-time = "2026-07-06T21:32:54.907Z" }, - { url = "https://files.pythonhosted.org/packages/79/99/0d0fd37f055224085f42bbb2c022d002e17dde4a97972822327b07d84101/cffi-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:379de10ce1ba048b1448599d1b37b24caee16309d1ac98d3982fc997f768700b", size = 223681, upload-time = "2026-07-06T21:32:56.329Z" }, - { url = "https://files.pythonhosted.org/packages/b0/80/c138990aa2a70b1a269f6e06348729836d733d6f970867943f61d367f8cc/cffi-2.1.0-cp312-cp312-win32.whl", hash = "sha256:9b8f0f26ca4e7513c534d351eca551947d053fac438f2a04ac96d882909b0d3a", size = 175269, upload-time = "2026-07-06T21:32:57.777Z" }, - { url = "https://files.pythonhosted.org/packages/a8/eb/f636456ff21a83fc13c032b58cc5dde061691546ac79efa284b2989b7982/cffi-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:c97f080ea627e2863524c5af3836e2270b5f5dfff1f104392b959f8df0c5d384", size = 185881, upload-time = "2026-07-06T21:32:59.253Z" }, - { url = "https://files.pythonhosted.org/packages/dd/2c/400ea43e721727dca8a65c4521390e9196757caba4a45643acb2b63271b8/cffi-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:6d194185eabd279f1c05ebe3504265ddfc5ad2b58d0714f7db9f01da592e9eb6", size = 180088, upload-time = "2026-07-06T21:33:02.278Z" }, - { url = "https://files.pythonhosted.org/packages/96/88/a996879e2eeccb815f6e3a5967b12a308257412acec882039d386bd2aa7b/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:10537b1df4967ca26d21e5072d7d54188354483b91dc75058968d3f0cf13fbda", size = 194331, upload-time = "2026-07-06T21:33:03.697Z" }, - { url = "https://files.pythonhosted.org/packages/58/85/7ae00d5c8dd6266f4e944c3db630f3c5c9a98b61d469c714d848b1d8138a/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a95b05f9baf29b91171b3a8bd2020b028835243e7b0ff6bb23e2a3c228518b1b", size = 196966, upload-time = "2026-07-06T21:33:05.353Z" }, - { url = "https://files.pythonhosted.org/packages/8c/e9/45c3a76ad8d43ad9261f4c95436da61128d3ca545d72b9612c0ab5be0b1c/cffi-2.1.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:15faec4adfff450819f3aee0e2e02c812de6edb88203aa58807955db2003472a", size = 184795, upload-time = "2026-07-06T21:33:06.699Z" }, - { url = "https://files.pythonhosted.org/packages/84/4c/82f132cb4418ee6d953d982b19191e87e2a6372c8a4ce36e50b69d6ade4a/cffi-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:716ff8ec22f20b4d988b12884086bcef0fc99737043e503f7a3935a6be99b1ea", size = 184746, upload-time = "2026-07-06T21:33:08.071Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1c/4ed5a0e5bdca6cbc275556de3328dd1b76fd0c11cc13c88fe66d1d8715f2/cffi-2.1.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:63960549e4f8dc41e31accb97b975abaecfc44c03e396c093a6436763c2ea7db", size = 214747, upload-time = "2026-07-06T21:33:09.671Z" }, - { url = "https://files.pythonhosted.org/packages/3a/a6/e879bb68cc23a2bc9ba8f4b7d8019f0c2694bad2ab6c4a3701d429439f58/cffi-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ff067a8d8d880e7809e4ac88eb009bb848870115317b306666502ccad30b147f", size = 222392, upload-time = "2026-07-06T21:33:10.896Z" }, - { url = "https://files.pythonhosted.org/packages/88/f6/01890cfd63c08f8eb96a8319b0443690197d240a8bd6346048cf7bde9190/cffi-2.1.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3b926723c13eba9f81d2ef3820d63aeceec3b2d4639906047bf675cb8a7a500d", size = 210285, upload-time = "2026-07-06T21:33:12.251Z" }, - { url = "https://files.pythonhosted.org/packages/a6/cf/2b684132056f438567b61e19d690dd31cd0921ace051e0a458be6074369e/cffi-2.1.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:47ff3a8bfd8cb9da1af7524b965127095055654c177fcfc7578debcb015eecd0", size = 208801, upload-time = "2026-07-06T21:33:13.617Z" }, - { url = "https://files.pythonhosted.org/packages/6f/08/f2e7d62c460faae0926f2d6e423694aa409ced3bc1fe2927a0a6e5f05416/cffi-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:799416bae98336e400981ff6e532d67d5c709cfb30afb79865a1315f94b0e224", size = 221808, upload-time = "2026-07-06T21:33:15.466Z" }, - { url = "https://files.pythonhosted.org/packages/38/37/04f54b8e63a02f3d908332c9effbf8c366167c6f733ed8a3d4f79b7e2a1e/cffi-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:961be50688f7fba2fa65f63712d3b9b341a22311f5253460ce933f52f0de1c8c", size = 225241, upload-time = "2026-07-06T21:33:16.869Z" }, - { url = "https://files.pythonhosted.org/packages/a9/d6/c72eecca433cd3e681c65ed313ab4835d9d4a379704d0f628a6a05f51c2e/cffi-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf5c6cf48238b0eb4c086978c492ad1cbc22373fc5b2d7353b3a598ce6db887a", size = 223588, upload-time = "2026-07-06T21:33:18.239Z" }, - { url = "https://files.pythonhosted.org/packages/c6/4b/e706f67279140f92939da3475ad610df18bfd52d50f14953a8e5fede71d5/cffi-2.1.0-cp313-cp313-win32.whl", hash = "sha256:db3eb7d46527159a878ec3460e9d40615bc25ba337d477db681aea6e4f05c5d2", size = 175248, upload-time = "2026-07-06T21:33:19.799Z" }, - { url = "https://files.pythonhosted.org/packages/5a/47/59eb7975cb0e4ef0afa764ea945b29a5bb4537a9f771cb7d6c8a5dd74c95/cffi-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:8e74a6135550c4748af665b1b1118b6aab33b1fc6a16f9aff630af107c3b4512", size = 185717, upload-time = "2026-07-06T21:33:21.47Z" }, - { url = "https://files.pythonhosted.org/packages/5a/af/34fee85c48f8d94efc8597bc09470c9dd274c145f1c12e0fbc6ab6d38d74/cffi-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:2282cd5e38aa8accd03e99d1256af8411c84cdbee6a89d841b563fdbd1f3e50f", size = 180114, upload-time = "2026-07-06T21:33:22.515Z" }, - { url = "https://files.pythonhosted.org/packages/d8/f0/81478e482afa03f6d18dc8f2afb5edc45b3080853b634b5ed91961be0998/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d2117334c3af3bdcb9a88522b844a2bdb5efdc4f71c6c822df55486ae1c3347a", size = 194142, upload-time = "2026-07-06T21:33:23.657Z" }, - { url = "https://files.pythonhosted.org/packages/7d/95/8de304305cd9204974b0ca051b86d307cafca13aa575a0ef1b44d92c0d8c/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:702c436735fbe99d59ada02a1f65cfc0d31c0ee8b7290912f8fbc5cd1e4b16c3", size = 196819, upload-time = "2026-07-06T21:33:25.007Z" }, - { url = "https://files.pythonhosted.org/packages/20/71/7c8372d30e42415602ed9f268f7cfd66f1b855fed881ecd168bcb45dbc0b/cffi-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1ff3456eab0d889592d1936d6125bbfbc7ae4d3354a700f8bd80450a66445d4d", size = 184965, upload-time = "2026-07-06T21:33:26.605Z" }, - { url = "https://files.pythonhosted.org/packages/d6/5c/584e626835f0375c928176c04137c96927165cb8733cdb3150ec04e5ee5e/cffi-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c4165821e131d6d4ca444347c2b694e2311bcfa3fe5a861cc72968f28867beac", size = 184952, upload-time = "2026-07-06T21:33:27.823Z" }, - { url = "https://files.pythonhosted.org/packages/2e/d2/065fcae1c73979fac8e054462478d0ff8a29c40cdc2ed7ea5676a061df53/cffi-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:276f20fffd7b396e12516ba8edf9509210ac248cbbc5acbc39cd512f9f59ebe6", size = 222353, upload-time = "2026-07-06T21:33:29.178Z" }, - { url = "https://files.pythonhosted.org/packages/ed/a5/e8bbb1ce5b3ac2f53ad6a10bde44318a5a8d99d4f4a000d44a6e39aeb3e4/cffi-2.1.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7d5980a3433d4b71a5e120f9dd551403d7824e31e2e67124fe2769c404c06913", size = 210051, upload-time = "2026-07-06T21:33:30.534Z" }, - { url = "https://files.pythonhosted.org/packages/28/ed/c127d3ac36e899c965e3361357c3befacd6578c03f40125183e41c3b219e/cffi-2.1.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6ca4919c6e4f89aa99c42510b42cf54596892c00b3f9077f6bdd1505e24b9c8d", size = 208630, upload-time = "2026-07-06T21:33:31.753Z" }, - { url = "https://files.pythonhosted.org/packages/cc/d7/97d3136f81db489ec8d1d67748c110d6c994268fd7528014aa9f2b085e4e/cffi-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d53d10f7da99ae46f7373b9150393e9c5eab9b224909982b43832668de4779f5", size = 221593, upload-time = "2026-07-06T21:33:33.044Z" }, - { url = "https://files.pythonhosted.org/packages/d3/27/93195977168ee63aed233a1a0993a2178798654d1f4bddcdd321d6fd3b21/cffi-2.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c351efb95e832a853a29361675f33a7ce53de1a109cd73fd47af0712213aa4ce", size = 225146, upload-time = "2026-07-06T21:33:34.224Z" }, - { url = "https://files.pythonhosted.org/packages/b3/c1/6dbd291ee2ae5a50a034aa057207081f545923bbf15dad4511e985aafff5/cffi-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7c7a88e2bac086f06d14577332760bdeecc42bdec8ac4077f6260557d9326", size = 223240, upload-time = "2026-07-06T21:33:35.57Z" }, - { url = "https://files.pythonhosted.org/packages/0f/6f/ade5ce9863a57992a6ea3d0d10d7e29b8749fc127204b3d493d667b2815f/cffi-2.1.0-cp314-cp314-win32.whl", hash = "sha256:1854b724d00f6654c742097d5387569021be12d3a0f770eae1df8f8acfcc6acd", size = 177723, upload-time = "2026-07-06T21:33:51.626Z" }, - { url = "https://files.pythonhosted.org/packages/41/de/92b9eeed4ae4a21d6fd9b2a2c8505cbed573299902ea73981cc13f7ff62c/cffi-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:1b96bfe2c4bd825681b7d311ad6d9b7280a091f43e8f63da5729638083cd3bfb", size = 187937, upload-time = "2026-07-06T21:33:53.403Z" }, - { url = "https://files.pythonhosted.org/packages/2e/1a/cc6ae6c2913a03aab8898eee57963cf1035b8df5872ed8b9115fcc7e2be8/cffi-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:7d28dff1db6764108bc30788d85d61c876beff416d9a49cb9dd7c5a9f34f5804", size = 183001, upload-time = "2026-07-06T21:33:54.74Z" }, - { url = "https://files.pythonhosted.org/packages/14/f0/134c00ce0779ec86dea2aa1aac69339c2741a8045072676763512363a2ea/cffi-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7ea6b3e2c4250ff1de21c630fe72d0f63eb95c2c32ffbf64a358cf4a8836d714", size = 188538, upload-time = "2026-07-06T21:33:36.792Z" }, - { url = "https://files.pythonhosted.org/packages/50/d8/3b86aba791cb610d24e8a3e1b2cd529e71fa15096b04e4d4e360049d4a4c/cffi-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6af371f3767faeffc6ac1ef57cdfd25844403e9d3f476c5537caee499de96376", size = 188230, upload-time = "2026-07-06T21:33:38.011Z" }, - { url = "https://files.pythonhosted.org/packages/14/d0/117dcd9209255ad8571fbc8c92ef32593a1d294dcec91ddc4e4db50606f2/cffi-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb4e8997a49aa2c08a3e43c9045d224448b8941d88e7ac163c7d383e560cbf98", size = 223899, upload-time = "2026-07-06T21:33:39.514Z" }, - { url = "https://files.pythonhosted.org/packages/b6/3d/f20f8b886b254e3ad10e15cd4186d3aed49f3e6a35ab37aab9f8f25f7c03/cffi-2.1.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bf01d8c84cbea96b944c73b22182e6c7c432b3475632b8111dbfdc95ddad6e13", size = 211652, upload-time = "2026-07-06T21:33:40.851Z" }, - { url = "https://files.pythonhosted.org/packages/28/3b/fad54de07260b93ddeef4b96d0131d57ea900675df1d410ae1deee52d7a6/cffi-2.1.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:33eb1ad83ebe8f313e0df035c406227d55a79456704a863fad9842136af5ad7d", size = 210755, upload-time = "2026-07-06T21:33:42.183Z" }, - { url = "https://files.pythonhosted.org/packages/cc/82/3d5c705acb7abbba9bbd7d79b8e62e0f25b6120eb7ae6ac49f1b721722fe/cffi-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ac0f1a2d0cfa7eea3f2aaf006ab6e70e8feeb16b75d65b7e5939982ca2f11056", size = 223933, upload-time = "2026-07-06T21:33:43.603Z" }, - { url = "https://files.pythonhosted.org/packages/6c/d0/47e338384ab6b1004241002fa616301020cea4fc95f283506565d252f276/cffi-2.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c16914df9fb7f500e440e6875fa23ff5e0b31db01fa9c06af98d59a91f0dc2e4", size = 226749, upload-time = "2026-07-06T21:33:45.046Z" }, - { url = "https://files.pythonhosted.org/packages/70/25/65bd5b58ea4bfdfc15cde02cb5365f89ef8ab8b2adfb8fe5c4bd4233382f/cffi-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5ecbd0499275d57506d397eebe1981cee87b47fcd9ef5c22cab7ed7644a39a94", size = 225703, upload-time = "2026-07-06T21:33:46.374Z" }, - { url = "https://files.pythonhosted.org/packages/dc/78/aa01ac599a8a4322533d45a1f9bc93b338276d2d59dabbe7c6d92a775c81/cffi-2.1.0-cp314-cp314t-win32.whl", hash = "sha256:7d034dcffa09e9a46c93fa3a3be402096cb5354ac6e41ab8e5cc9cd8b642ad76", size = 182857, upload-time = "2026-07-06T21:33:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/b9/26/d00496b22de4d4228f32dde94ad996f350c8aad676d63bcca0743c8dea4d/cffi-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0582a58f3051372229ca8e7f5f589f9e5632678208d8636fea3676711fdf7fe5", size = 194065, upload-time = "2026-07-06T21:33:48.953Z" }, - { url = "https://files.pythonhosted.org/packages/d5/dd/0c7dbf815a579ff005008a2d815a55d6bb047c349eef536d9dc53d3f0a8d/cffi-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:510aeeeac94811b138077451da1fb18b308a5feab47dd2b603af55804155e1c8", size = 186404, upload-time = "2026-07-06T21:33:50.309Z" }, - { url = "https://files.pythonhosted.org/packages/55/c7/8c8c50cb11c6750051daf12164098a9a6f027ac4356967fd4d800a07f242/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:2e9dabb9abcb7ad15938c7196ad5c1718a4e6d33cc79b4c0209bdb64c4a54a5c", size = 194121, upload-time = "2026-07-06T21:33:56.109Z" }, - { url = "https://files.pythonhosted.org/packages/99/e2/67680bf19a6b60d2bb7ff83baefa2a4c3d2d7dc0f3277034b802e1fc504c/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:37f525a7e7e50c017fdebe58b787be310ad59357ae43a053943a6e1a6c526001", size = 196820, upload-time = "2026-07-06T21:33:57.288Z" }, - { url = "https://files.pythonhosted.org/packages/ed/da/4bbe583a3b3a5c8c60892124fe17f3fa3656523faf0d3484eae90f091853/cffi-2.1.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:95f2954c2c9473d892eca6e0409f3568b37ab62a8eedb122461f73cc273476e3", size = 184936, upload-time = "2026-07-06T21:33:58.765Z" }, - { url = "https://files.pythonhosted.org/packages/e5/4b/1f4c36ab273980d7aa75bb126ea4f8971f24a96108acad3a0a084028c57b/cffi-2.1.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:cdf2448aab5f661c9315308ec8b93f4e8a1a67a3c733f8631067a2b67d5913dc", size = 185045, upload-time = "2026-07-06T21:34:00.085Z" }, - { url = "https://files.pythonhosted.org/packages/ef/c3/ad299dc38f3583f8d916b299f028af418a9ec98bc695fcbebeae7420691c/cffi-2.1.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:90bec57cf82089383bd06a605b3eb8daebf7e5a668520beaf6e327a83a947699", size = 222342, upload-time = "2026-07-06T21:34:01.814Z" }, - { url = "https://files.pythonhosted.org/packages/eb/d8/df4543cc087245044ed02ef3ad8e0a26619d0075ac7a77a12dc81177851b/cffi-2.1.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6274dcb2d15cef48daa73ed1be5a40d501d74dccd0cd6db364776d12cb6ba022", size = 210073, upload-time = "2026-07-06T21:34:03.255Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0e/fac738d73728c6cea2a88a2883dca54892496cbba88a1dc1f2909cb8a6f5/cffi-2.1.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2b71d409cccee78310ab5dec549aed052aaea483346e282c7b02362596e01bb0", size = 208551, upload-time = "2026-07-06T21:34:04.433Z" }, - { url = "https://files.pythonhosted.org/packages/e6/3f/0b04a700dd64f465c93020253a793a82c9b4dff9961f48facd0df945d9b8/cffi-2.1.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d3538f9c0e50670f4deb93dbb696576e60590369cae2faf7de681e597a8a1f1", size = 221649, upload-time = "2026-07-06T21:34:06.157Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7c/b7379a5704c79eda57ce075869ba70a0368d1c850f803b3c0d078d39dcaf/cffi-2.1.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:8f9ec95b8a043d3dfbc74d9abc6f7baf524dd27a8dc160b0a32ff9cdab650c28", size = 225203, upload-time = "2026-07-06T21:34:07.489Z" }, - { url = "https://files.pythonhosted.org/packages/5a/02/d5e6c43ea85c41bda2a184a3418f195fe7cf602967a8d2b94e085b83deef/cffi-2.1.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:af5e2915d41fe6c961694d7bfdc8562942638200f3ce2765dfb8b745cf997629", size = 223263, upload-time = "2026-07-06T21:34:08.712Z" }, - { url = "https://files.pythonhosted.org/packages/2c/d8/772b8259bf75749adffb1c546828978381fb516f60cf701f6c83daf60c85/cffi-2.1.0-cp315-cp315-win32.whl", hash = "sha256:0a42c688d19fca6e095a53c6a6e2295a5b050a8b289f109adab02a9e61a25de6", size = 177696, upload-time = "2026-07-06T21:34:26.355Z" }, - { url = "https://files.pythonhosted.org/packages/2f/dd/afa2191fc6d57fedd26e5844a2fe2fcc0bbfa00961bbaa5a41e4921e7cca/cffi-2.1.0-cp315-cp315-win_amd64.whl", hash = "sha256:bccbbb5ee76a61f9d99b5bf3846a51d7fca4b6a732fe46f89295610edaf41853", size = 187914, upload-time = "2026-07-06T21:34:27.58Z" }, - { url = "https://files.pythonhosted.org/packages/05/ef/6cd4f8c671517162379dc79cfae5aea9106bc38abb89628d5c16adf6a838/cffi-2.1.0-cp315-cp315-win_arm64.whl", hash = "sha256:8d35c139744adb3e727cd51b1a18324bbe44b8bd41bf8322bca4d41289f48eda", size = 183004, upload-time = "2026-07-06T21:34:28.905Z" }, - { url = "https://files.pythonhosted.org/packages/11/b6/12fc55092817a5faa26fb8c40c7f9d662e11a46ee248c137aafc42517d92/cffi-2.1.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:f9912624a0c0b834b7520d7769b3644453aabc0a7e1c839da7359f050750e9bc", size = 188378, upload-time = "2026-07-06T21:34:09.926Z" }, - { url = "https://files.pythonhosted.org/packages/8d/2e/cdac88979f295fde5daa69622c7d2111e56e7ceb94f211357fbe452339e4/cffi-2.1.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:df92f2aba50eb4d96718b68ef76f2e57a57b54f2fa62333496d16c6d585a85ca", size = 188319, upload-time = "2026-07-06T21:34:11.101Z" }, - { url = "https://files.pythonhosted.org/packages/e0/27/1d0b408497e41a74795af122d7b603c418c5fed0171450f899afd04e594f/cffi-2.1.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0520e1f4c35f44e209cbbb421b67eec42e6a157f59444dfb6058874ff3610e5d", size = 223904, upload-time = "2026-07-06T21:34:12.606Z" }, - { url = "https://files.pythonhosted.org/packages/8b/31/e115c985105dd7ffb32444505f18ceb874bb42d992af05d5dced7ecf1980/cffi-2.1.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3681e031db29958a7502f5c0c9d6bbc4c36cb20f7b104086fa642d1799631ff8", size = 211554, upload-time = "2026-07-06T21:34:13.987Z" }, - { url = "https://files.pythonhosted.org/packages/5a/67/9e6e09409336d9e515c58367e7cfcf4f89df06ad25252675595a58eb59d5/cffi-2.1.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:762f99479dcb369f60ab9017ad4ab97a36a1dd7c1ee5a3b15db0f4b8659120cd", size = 210795, upload-time = "2026-07-06T21:34:15.972Z" }, - { url = "https://files.pythonhosted.org/packages/19/e5/d3cc82a4a0be7902af279c04181ad038449c096734464a5ae1de3e1401bd/cffi-2.1.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0611e7ebf90573a535ebdc33ae9da222d037853983e13359f580fab781ca017f", size = 223843, upload-time = "2026-07-06T21:34:17.509Z" }, - { url = "https://files.pythonhosted.org/packages/b9/65/b434abc97ce7cecc2c640fde160507c0ecc7e21544b483ba3325d2e2ea17/cffi-2.1.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:86cf8755a791f72c85dc287128cc62d4f24d392e3f1e15837245623f4a33cccc", size = 226773, upload-time = "2026-07-06T21:34:19.05Z" }, - { url = "https://files.pythonhosted.org/packages/b5/9f/d4dc66ca651eb1145a133314cda721abf13cfac3d28c4a0402263ae6ad75/cffi-2.1.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:ba00f661f8ba35d075c937174e27c2c421cec3942fd2e0ea3e66996757c0fdd9", size = 225719, upload-time = "2026-07-06T21:34:20.576Z" }, - { url = "https://files.pythonhosted.org/packages/68/5a/e536c528bc8057496c360c0978559a2dc45653f89dd6151078aa7d8fca1a/cffi-2.1.0-cp315-cp315t-win32.whl", hash = "sha256:cb96698e3c7413d906ce83f8ffd245ec1bd94707541f299d0ce4d6b0193e982b", size = 182760, upload-time = "2026-07-06T21:34:22.059Z" }, - { url = "https://files.pythonhosted.org/packages/d3/0b/0ffe8b82d3875bced5fa1e7986a7a46b748262a40ab7f60b475eb9fb1bb3/cffi-2.1.0-cp315-cp315t-win_amd64.whl", hash = "sha256:f146d154428a2523f9cc7936c02353c2459b8f6cf07d3cd1ee1c0a611109c5d5", size = 193769, upload-time = "2026-07-06T21:34:23.589Z" }, - { url = "https://files.pythonhosted.org/packages/a0/17/1073b53b68c9b5ca6914adf5f8bf55aacc2d3be102418c90700160ea8605/cffi-2.1.0-cp315-cp315t-win_arm64.whl", hash = "sha256:cbb7640ce37159548d2147b5b8c241f962143d4c71231431820783f4dc78f210", size = 186405, upload-time = "2026-07-06T21:34:24.857Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/d2/2cde336b375f55c76ca670f0be3978cc048e31e24f3b4d7ce8473150a388/cffi-2.1.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:baed1e86cc735622097354b9d1281406caf42ff42a886d29faa8e8d1630333be", size = 183779, upload-time = "2026-08-03T21:19:15.602Z" }, + { url = "https://files.pythonhosted.org/packages/94/1a/4b2f7c92293ba05cbd4a9a1b28faaf0326272d9488e6354657571c48a7aa/cffi-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca82be1a1d406ecfe1d25dc16cb33488e5a16bf4438c9fb590484ea29d92478b", size = 184178, upload-time = "2026-08-03T21:19:16.67Z" }, + { url = "https://files.pythonhosted.org/packages/17/0b/ba385d8ccedf926c3cd06e8e2f327027da5afe5f0eb30f1f7bc43ac55125/cffi-2.1.1-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:42e2f76b9455f5a9a844f770bf3e200ed3da0e15f5df3db9c31fe80b04b3d004", size = 211037, upload-time = "2026-08-03T21:19:17.705Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b9/0f2e58b2cefa33255bff36935d42b13180fe559bba82596540eb404bde7d/cffi-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5a59cc1c4442bc3d5c703bf720b51138d0bfc173618807c9ee2490a7541dd3d9", size = 218652, upload-time = "2026-08-03T21:19:18.735Z" }, + { url = "https://files.pythonhosted.org/packages/37/15/180e0dab27b9312c7479003d14c9e547634b7dcb934e2cc4650e1b131a7a/cffi-2.1.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:9f8d177621de5cb38ee3e731eda45d421db093ec0739f46a5594babda7987a98", size = 205422, upload-time = "2026-08-03T21:19:19.96Z" }, + { url = "https://files.pythonhosted.org/packages/18/d4/03026f0c850cbbaa9030750490225b4a7f4d524ea4df72c3cc740a90f4ef/cffi-2.1.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:75f80557d1389eddbd0de2681f6a390a0c5338c31ddaa821381c203fc3fd50d9", size = 205444, upload-time = "2026-08-03T21:19:21.246Z" }, + { url = "https://files.pythonhosted.org/packages/75/77/60bebf6f818bec84210ac5b6979ce4eeadce6fbbaabc9c7ab23e506d1ce5/cffi-2.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:194cffa889098ced9976c3fc6340305e43f6303657d298da55366907c05c22d6", size = 218742, upload-time = "2026-08-03T21:19:22.523Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ae/679bf47e73fd77b352171727f07de559a003f14de5d02b904a6ec1fa73ca/cffi-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5bb4e7ea95dcd6a014a6fef62e62467d67d8e582326443f3d68e71d6320a9fcf", size = 221054, upload-time = "2026-08-03T21:19:23.694Z" }, + { url = "https://files.pythonhosted.org/packages/09/b8/eefc0e06913b70aa153bf74c946094a18f58fd4aff11b7f372bfdfdca050/cffi-2.1.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3d22a20b1fb1632cc72c22f95f7b0d2961c3e1c235f245ba4c606c4771035659", size = 213489, upload-time = "2026-08-03T21:19:24.922Z" }, + { url = "https://files.pythonhosted.org/packages/6f/13/4e56852824a03cdf68523a35686f1c28eacd4bd30a7b0a78e682e6e6e1d3/cffi-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1dea0e4d7d4f11f619fe8c1d76caf49e24405b4b5743c0e3be16a500ecd930c9", size = 220241, upload-time = "2026-08-03T21:19:26.214Z" }, + { url = "https://files.pythonhosted.org/packages/99/7f/040f9e163e4acac3ee3d85b02d00b2576e7ca980d8785f0a3a5f1a9bf7f5/cffi-2.1.1-cp310-cp310-win32.whl", hash = "sha256:7ce713ace7c0e4520535b42b77eaa742c16dab813978064913e5a3cf82973b41", size = 174578, upload-time = "2026-08-03T21:19:27.338Z" }, + { url = "https://files.pythonhosted.org/packages/ba/0b/644a2ec1a4eaba49c2939410bb1eb1d25b09d6d0582f5d2f95c537043725/cffi-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a48d62ab9d6f4f98c983223a547af44be6ca3691074c31cecced6facd3ba2dc1", size = 185082, upload-time = "2026-08-03T21:19:28.409Z" }, + { url = "https://files.pythonhosted.org/packages/70/d2/16d99a0c4948febc0ebd133a13b2f688ff7f8cb04da971e1128872ce0c03/cffi-2.1.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12", size = 183838, upload-time = "2026-08-03T21:19:29.637Z" }, + { url = "https://files.pythonhosted.org/packages/cd/95/31b535a9f0220ae9f357de4a08d57ce89cb417653c2fd9f075f50822a388/cffi-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1", size = 184168, upload-time = "2026-08-03T21:19:30.764Z" }, + { url = "https://files.pythonhosted.org/packages/ad/5a/4707a0dc1f203f5dde5a907b0d4e3c25d71120241048bd5bc6f1bb9d4e71/cffi-2.1.1-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0", size = 211805, upload-time = "2026-08-03T21:19:31.867Z" }, + { url = "https://files.pythonhosted.org/packages/ad/66/c19feabb28485b6e0bbaaafa90837a1ef5d302e90f2178bd33f17a49879b/cffi-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813", size = 218716, upload-time = "2026-08-03T21:19:32.896Z" }, + { url = "https://files.pythonhosted.org/packages/a7/92/500760486c8baab49a7a8a58ba7fc3355ec3974b454b8a09e528efde9e1d/cffi-2.1.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990", size = 205569, upload-time = "2026-08-03T21:19:34.142Z" }, + { url = "https://files.pythonhosted.org/packages/a5/a7/a67c733254d6e7373f7822f8082d8d6beade791e0cf12a7611f376fa61c7/cffi-2.1.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af", size = 204907, upload-time = "2026-08-03T21:19:35.174Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a4/4399daaf8f7dfee9d7c3327fdb0426ee041cc63edc358b93911ceb2bfc7a/cffi-2.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632", size = 217807, upload-time = "2026-08-03T21:19:36.286Z" }, + { url = "https://files.pythonhosted.org/packages/28/f7/dabe6da2466ecbd82dc62e7342dc6b1065dad990c06f00f0ede9ebf2a0ed/cffi-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd", size = 221252, upload-time = "2026-08-03T21:19:37.416Z" }, + { url = "https://files.pythonhosted.org/packages/ce/87/616202d8e51342c07d2534c510111c4cc37201775ce8f60802c9335d1edd/cffi-2.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a", size = 214214, upload-time = "2026-08-03T21:19:38.507Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c6/ab025d75d2c26c19b087c0124e75ee31cb65032f4fe345d356d8c507ab97/cffi-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa", size = 219408, upload-time = "2026-08-03T21:19:39.809Z" }, + { url = "https://files.pythonhosted.org/packages/db/e2/7e8109f65445bdc673a7b54f02c677de462db75674220fd1335efc8eb598/cffi-2.1.1-cp311-cp311-win32.whl", hash = "sha256:f8ec5e643a9a937f64e1999eb9f75d072263751912dc5cd06d3c85f8f44be7c3", size = 174470, upload-time = "2026-08-03T21:19:41.246Z" }, + { url = "https://files.pythonhosted.org/packages/73/c0/77ba02423c2f7d7091143c45cd49e0e6575c4c1967394bb542bd923a9b74/cffi-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:42f6930c31dc7f50732c9ae793c2786c7b6b044195967bbdde40bb9be81c4cc0", size = 185096, upload-time = "2026-08-03T21:19:42.615Z" }, + { url = "https://files.pythonhosted.org/packages/7c/47/9f1f85f9672ceda4984dc6c4f8824e8558992a2972c3d3c81fb8eb28d4ba/cffi-2.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:c7659f22557c5a0bc4855cd635f55edec690cc008a40768527762cb9fb263455", size = 179941, upload-time = "2026-08-03T21:19:43.747Z" }, + { url = "https://files.pythonhosted.org/packages/10/69/43965eccfdead3b9220015fd1320e117be8c6ed01a62ffab76eeb752f5d5/cffi-2.1.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0", size = 184821, upload-time = "2026-08-03T21:19:44.887Z" }, + { url = "https://files.pythonhosted.org/packages/54/7d/16e5a096677b5e313ca80cd5e5170efa3ea44624a82bb111925522da64b1/cffi-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf", size = 184719, upload-time = "2026-08-03T21:19:46.129Z" }, + { url = "https://files.pythonhosted.org/packages/56/e6/8941622732edec876dd17d0453dce07317ae96db34f2ec1436c9d3785986/cffi-2.1.1-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a", size = 214799, upload-time = "2026-08-03T21:19:47.218Z" }, + { url = "https://files.pythonhosted.org/packages/44/de/f98430906df1545ffde0d543dd124a7a439bc2cd32b36b9c53f805df7333/cffi-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890", size = 222389, upload-time = "2026-08-03T21:19:48.331Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5b/717f1526b9957b34456313c31645c5b82b8fb5c3fe9e4752999be7128bfc/cffi-2.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50", size = 210249, upload-time = "2026-08-03T21:19:49.543Z" }, + { url = "https://files.pythonhosted.org/packages/64/b3/f8aa4f3e34986c7e4ec45072d1b1b9dd295b6b18007b45518d79726dd725/cffi-2.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e", size = 208775, upload-time = "2026-08-03T21:19:50.918Z" }, + { url = "https://files.pythonhosted.org/packages/b1/db/dceb9dd5b231e1da801793f8acc9f3c52a7e1afe40bb1aae37e02b0faad5/cffi-2.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf", size = 221822, upload-time = "2026-08-03T21:19:52.054Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d2/6cd24ae3be000a634109c247d1475d62e5616d0dc78c82770942ec384248/cffi-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517", size = 225232, upload-time = "2026-08-03T21:19:53.109Z" }, + { url = "https://files.pythonhosted.org/packages/cb/52/3fa190537004dd7f0ab860a6dc7c0175b8667f68d1e618a46f5498d30250/cffi-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735", size = 223597, upload-time = "2026-08-03T21:19:54.515Z" }, + { url = "https://files.pythonhosted.org/packages/80/fb/0bb75b7039588c074b37ae99f40d9bfddf990ecb2fbc346ebccd2e56b9be/cffi-2.1.1-cp312-cp312-win32.whl", hash = "sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e", size = 175292, upload-time = "2026-08-03T21:19:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/d9/79/615cc094e2fb508cade7de88d3b4f6c4ec2bab695c97bce9153dc65aadf5/cffi-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a", size = 185919, upload-time = "2026-08-03T21:19:56.89Z" }, + { url = "https://files.pythonhosted.org/packages/70/c6/d0ea84713fe46b243a436a18fcd47d639732747e21635c8a27191b06dc30/cffi-2.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80", size = 180093, upload-time = "2026-08-03T21:19:58.155Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" }, + { url = "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" }, + { url = "https://files.pythonhosted.org/packages/a7/46/2e5fdde8555706dd98139a910ca11be02809f3f605ce956f655d0214e100/cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6", size = 184805, upload-time = "2026-08-03T21:20:02.02Z" }, + { url = "https://files.pythonhosted.org/packages/55/41/4c7042f317b9217502988f0873af87e16ad606dc20f84e546e3e6ce9764c/cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971", size = 184764, upload-time = "2026-08-03T21:20:03.141Z" }, + { url = "https://files.pythonhosted.org/packages/43/1f/1c3d90d91811c8f86ced9ed637956c54bfe5b79ca98fe976d7f8c8979f6b/cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c", size = 214722, upload-time = "2026-08-03T21:20:04.377Z" }, + { url = "https://files.pythonhosted.org/packages/37/6f/3b5ce4c3b2192d250f04908f2bfd91ef34552ec8f7716a5d4abdb8d67bb2/cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125", size = 222369, upload-time = "2026-08-03T21:20:05.544Z" }, + { url = "https://files.pythonhosted.org/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264", size = 210175, upload-time = "2026-08-03T21:20:06.75Z" }, + { url = "https://files.pythonhosted.org/packages/df/62/14f74b9543e605d17701dc797b815958b8bb70b7624ce1b832ddad48ed6c/cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3", size = 208670, upload-time = "2026-08-03T21:20:08.04Z" }, + { url = "https://files.pythonhosted.org/packages/95/95/86342356ff5953b3fb06f7ef7c5bee212d45e770abc7218d451b9148313c/cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2", size = 221824, upload-time = "2026-08-03T21:20:09.274Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ff/7b3429ff53aafe931ed8a5fc69f481bbef7ba6de87ddcbb63d08f483f613/cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b", size = 225148, upload-time = "2026-08-03T21:20:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/34/a95870b9221e09cf4f2ce3178b1a210abdfe63a1bd357da940418d7b8d15/cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7", size = 223564, upload-time = "2026-08-03T21:20:12.165Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/839b50531021a647fb5e929f72cf97bc1ff702b5472166164b5b6e76b851/cffi-2.1.1-cp313-cp313-win32.whl", hash = "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac", size = 175263, upload-time = "2026-08-03T21:20:13.559Z" }, + { url = "https://files.pythonhosted.org/packages/60/a6/8b149b2c3f2e11aaa1618ef64500b45f50f22c57a977a4dff1aff1f91042/cffi-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d", size = 185688, upload-time = "2026-08-03T21:20:14.69Z" }, + { url = "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", size = 180078, upload-time = "2026-08-03T21:20:15.917Z" }, + { url = "https://files.pythonhosted.org/packages/d3/7b/d6bbf82b8b96e7391438898c42f5bd96dd02030fd5b64937d248220003e2/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c", size = 194064, upload-time = "2026-08-03T21:20:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/94/e6/bcc91b283be94735e268487a054004f0aa19947b6348fa367db53230abc8/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb", size = 196720, upload-time = "2026-08-03T21:20:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/d9/99/c4b0c17cacdc9c3b8f280026286a9826d6a208c0f047591a3c3ce99b91fd/cffi-2.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54", size = 184964, upload-time = "2026-08-03T21:20:19.708Z" }, + { url = "https://files.pythonhosted.org/packages/b3/a9/9db617d05d7367c1ad0ab00b3aa6e6f9281edd689b4ee9ea0e5a84e89c97/cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72", size = 184962, upload-time = "2026-08-03T21:20:20.833Z" }, + { url = "https://files.pythonhosted.org/packages/67/b8/b42132ca113dc567d37684437b46ca1dafc885902b02a110a02d5b511857/cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1", size = 222328, upload-time = "2026-08-03T21:20:22.118Z" }, + { url = "https://files.pythonhosted.org/packages/80/10/c5c0cbf0a657aecf59ef511409734230bf556f05a0d6c9eed7aa5c0a0166/cffi-2.1.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062", size = 209985, upload-time = "2026-08-03T21:20:23.401Z" }, + { url = "https://files.pythonhosted.org/packages/d5/6c/bfa0b87b03b9238148beca990292843c9396ba069b54496596594173de7b/cffi-2.1.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03", size = 208530, upload-time = "2026-08-03T21:20:24.628Z" }, + { url = "https://files.pythonhosted.org/packages/e9/02/4e7d553a7ac4b4238b38b3c1b80d486e9d4436f8d2acbf87a0997fe3f402/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96", size = 221525, upload-time = "2026-08-03T21:20:25.758Z" }, + { url = "https://files.pythonhosted.org/packages/82/1d/a4aaf9babd75acb4d5f223bff71533bee748dd770a382619a798960ee9ba/cffi-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527", size = 225053, upload-time = "2026-08-03T21:20:26.985Z" }, + { url = "https://files.pythonhosted.org/packages/81/10/5dc0e7bdd18e22107054288283380fc97a06ae3f1656a106908d666a3c88/cffi-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13", size = 223213, upload-time = "2026-08-03T21:20:28.277Z" }, + { url = "https://files.pythonhosted.org/packages/0b/e9/d0061c364cde06ee43168a0d076ac1da512cbc380d44767b844ba34fe2b6/cffi-2.1.1-cp314-cp314-win32.whl", hash = "sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c", size = 177682, upload-time = "2026-08-03T21:20:44.288Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1c3e01e3ba14c39f6d10bfbac52753b7e22259e38088e5cfe1d704918690/cffi-2.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48", size = 187949, upload-time = "2026-08-03T21:20:45.623Z" }, + { url = "https://files.pythonhosted.org/packages/87/5b/da4e39efe18eeb89cf580ea9cfc66b6a7c3eadb808fc0cc1d3a295cb5a5d/cffi-2.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836", size = 182947, upload-time = "2026-08-03T21:20:46.955Z" }, + { url = "https://files.pythonhosted.org/packages/23/59/40338bf421c5accea1d45158170c87006ef1cd371b05c077e76476949728/cffi-2.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3", size = 188504, upload-time = "2026-08-03T21:20:29.495Z" }, + { url = "https://files.pythonhosted.org/packages/7d/47/5ecf1023850036e674c77ec4de86182d309ae344e39e7cba984b7df5d647/cffi-2.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2", size = 188259, upload-time = "2026-08-03T21:20:31.291Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9c/92934c3bea9f785b23eba304538c0b4d37a2a96d2431eb3a1bc87a11aa19/cffi-2.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94", size = 223864, upload-time = "2026-08-03T21:20:32.571Z" }, + { url = "https://files.pythonhosted.org/packages/4d/45/ba4c93527bc38616a8bd36488acb69a2212d60486794f0c1f318949bbb76/cffi-2.1.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc", size = 211538, upload-time = "2026-08-03T21:20:33.808Z" }, + { url = "https://files.pythonhosted.org/packages/80/e9/b6ef565e452acb932fb0cb5443f44a78efbd1233e566f02b5a83855e9115/cffi-2.1.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29", size = 210688, upload-time = "2026-08-03T21:20:34.974Z" }, + { url = "https://files.pythonhosted.org/packages/9a/95/eff5f0cee78d2eabc7eebffec40d3fc1876b5f3c95582e018bb4b99601f2/cffi-2.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676", size = 223803, upload-time = "2026-08-03T21:20:36.564Z" }, + { url = "https://files.pythonhosted.org/packages/fa/01/579d39fb8bef00a335a23d83757b44feb24cd6345a2c451b64cb67b9c362/cffi-2.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e", size = 226763, upload-time = "2026-08-03T21:20:37.816Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b0/0b44f47c60b01b57b6e2bbd92343f13a85a1d93bc46ccf6e47e244acd99c/cffi-2.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f", size = 225688, upload-time = "2026-08-03T21:20:38.959Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d2/3b7176cb570a1d3e27faf67b72f591af508036e0d8b2be2ef9af9e8c84bb/cffi-2.1.1-cp314-cp314t-win32.whl", hash = "sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4", size = 182868, upload-time = "2026-08-03T21:20:40.388Z" }, + { url = "https://files.pythonhosted.org/packages/56/78/31f00c1bcd97c9bbf55f1bfdf5bc809a5de8887473e90bb9960dca825e80/cffi-2.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e", size = 194104, upload-time = "2026-08-03T21:20:41.725Z" }, + { url = "https://files.pythonhosted.org/packages/7b/1b/58496f2ed0a35de575250c02a43ab3cc2c04d494a88fed31c1cabc0fd176/cffi-2.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5", size = 186402, upload-time = "2026-08-03T21:20:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/c1/8f/9ebe220eab48a093d1a5a5e339ab0dc7316eef3bb04d63c42f0251b61f50/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:dddad92b554513a31f272570678ba307fb9f618f05e3d4a5eacafff9eae03e1d", size = 194043, upload-time = "2026-08-03T21:20:48.179Z" }, + { url = "https://files.pythonhosted.org/packages/ff/69/844bad3ece306c4782c2ecb93597035b6690d48704b803914c199da1e8b3/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:da0e573f9f97159390c89d9f1a9e41908b66d408cc5b58d08cf3847d844c531b", size = 196737, upload-time = "2026-08-03T21:20:49.457Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8a/af668013284634733f02d683458a0728739c7d6ddb5e14cb0c20832266fe/cffi-2.1.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:fb92203a88b3d3053034db775110081c49d28be6551923805e039924093761e4", size = 184933, upload-time = "2026-08-03T21:20:50.639Z" }, + { url = "https://files.pythonhosted.org/packages/0c/75/2f5207ff6d1a613133b23a5203cc0c2a628313b5eb3974d7956ae3c57950/cffi-2.1.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2ae64be792b8966f2c69538199728b290e34726562896df1e5dc8ffd8d8188e8", size = 185002, upload-time = "2026-08-03T21:20:52.173Z" }, + { url = "https://files.pythonhosted.org/packages/e2/31/9e1313b0a6e30e91b3b3d3fff51ae99c857c07738e3afcce1f7334e1b7ab/cffi-2.1.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:507a24c282e0f42f8ed737cf048572cbf580468da5555764a8331735e9c736b6", size = 222271, upload-time = "2026-08-03T21:20:53.462Z" }, + { url = "https://files.pythonhosted.org/packages/50/e3/f6234a833e6e08c7007003074723c406559eecf9b48dfc97471e5a8eb7a0/cffi-2.1.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:246fa40ce8645a614ff682e0b70f37134e460eaf93a775e0cbe3cca585a67a80", size = 209919, upload-time = "2026-08-03T21:20:54.783Z" }, + { url = "https://files.pythonhosted.org/packages/0d/fc/5f74e293fced6edb51af3a46c4ccf6c23c9943774ecb375ddbd522c76add/cffi-2.1.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:471cee653ae88de62096552e6d24ccb4a5adb8c8c9f10b5054d0122c15bf2779", size = 208529, upload-time = "2026-08-03T21:20:56.066Z" }, + { url = "https://files.pythonhosted.org/packages/44/16/29e6d01b388bef055ecd6ca8244b3f4d336bd09e92d5d892187b9601084e/cffi-2.1.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeae0e330c9f6acd681f647d46cefd30c29f93e3392882e792e82080c9691399", size = 221630, upload-time = "2026-08-03T21:20:57.336Z" }, + { url = "https://files.pythonhosted.org/packages/a4/18/fa7f1f6857d5eb88a4ca99ffcbfb7c387a287ccc154c64a73e86314745d7/cffi-2.1.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:42a494cee34437f05546455144f2b5d9ac09b1face62bcfce597d2e521066688", size = 225134, upload-time = "2026-08-03T21:20:58.675Z" }, + { url = "https://files.pythonhosted.org/packages/e0/9f/e8e3dfa04a1b4c241f8c91faacad872b4d4efd051d49764ad4e2fd4b9fea/cffi-2.1.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:cc572dace3f60ef98d7b12ff411d20f5362feb31a0439eab0085bbfd349982d7", size = 223197, upload-time = "2026-08-03T21:20:59.968Z" }, + { url = "https://files.pythonhosted.org/packages/f8/7e/8debeb04f1ab9fe2a6963964cd6f1aaf7192627b83926586a6a4e089c9fa/cffi-2.1.1-cp315-cp315-win32.whl", hash = "sha256:4f42141fc14250de6dde5ee7ea4432be017252d91f19c5ad043c084cea629cac", size = 177683, upload-time = "2026-08-03T21:21:14.901Z" }, + { url = "https://files.pythonhosted.org/packages/e0/31/5158704cc474ab65c1647932e88be78dc0873f47130e253be38bcaf13d01/cffi-2.1.1-cp315-cp315-win_amd64.whl", hash = "sha256:e6e8cff14d6fb0be70a09c0bdc58096f501952d04624ebf867e0e56da2df8960", size = 187897, upload-time = "2026-08-03T21:21:16.108Z" }, + { url = "https://files.pythonhosted.org/packages/cc/4b/b3a2da8570c704ffc0f9762cdc3ec0f02c8573798e0b5cf7f11c82bbb70f/cffi-2.1.1-cp315-cp315-win_arm64.whl", hash = "sha256:27350daa11d4f10c540e6e89dada4c54feb7256ad03e9a4dc075ebad7ba360d1", size = 182935, upload-time = "2026-08-03T21:21:17.271Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ef/5443574510a1207e6f6bc38ba6e1f1de36cb48fef07b2728bb896a21f430/cffi-2.1.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c26608d2222fb1e94487e4a387d85f13eb55d5ed725cb25a0c589ac4ee60e7bc", size = 188464, upload-time = "2026-08-03T21:21:01.163Z" }, + { url = "https://files.pythonhosted.org/packages/7e/ae/a56fa8c4686ad50e148fcbc8d3ae0d03915ff5c30d795058988c24118cef/cffi-2.1.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:4be96343e422f2dfcd12ab5c9f5aebe03f82f737c6bffeca6830b3875cb44aab", size = 188262, upload-time = "2026-08-03T21:21:02.382Z" }, + { url = "https://files.pythonhosted.org/packages/53/b2/6187f46f2912276a3ae284076109cc5c8680482f11f766ccf26db4a86427/cffi-2.1.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:937c0052c05a31ca1daf18de3158eed4dbfcb9cc107adbea227728d647be701e", size = 223779, upload-time = "2026-08-03T21:21:03.553Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f6/c3ad28bd19f77047a03084424fbd4cbe997303267c14423737324be0385d/cffi-2.1.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:df423d40ee8654634421812bc3b196da3f9bd7d32929da813f8394c4348a5358", size = 211520, upload-time = "2026-08-03T21:21:04.863Z" }, + { url = "https://files.pythonhosted.org/packages/a0/cd/ccac9013a5bd9fd764de118674ab9c805b5ca10c19270d90ee273f8b2240/cffi-2.1.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a730a083190634c65cca36ba5f489531576ebd79bcd5c8e172130f6453127231", size = 210673, upload-time = "2026-08-03T21:21:06.223Z" }, + { url = "https://files.pythonhosted.org/packages/52/86/2976131c639aead931c5bee5aba67e4b09fbeb8018b6f282f70803f923a7/cffi-2.1.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:363e05fa78e15116c3c32c210ee36884fd6b9afa6d440e47112c3bd511d64cb6", size = 223835, upload-time = "2026-08-03T21:21:07.539Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0c/33a7aeab2f9c76918c52e084beb39c570db3588133412929e8ec06fab90b/cffi-2.1.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:770de9db11e84213beec501cfcaa013b019820ca881e03344dea5844f7876d94", size = 226705, upload-time = "2026-08-03T21:21:08.774Z" }, + { url = "https://files.pythonhosted.org/packages/e3/26/2cde30fdde421130bfc18f70395731a6e6b2053c6a1978a5258ff04e72fa/cffi-2.1.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5", size = 225539, upload-time = "2026-08-03T21:21:09.911Z" }, + { url = "https://files.pythonhosted.org/packages/6d/cd/a361394c94b2129d604bb846f624a8e88255a3ee33129c434a00d715e64f/cffi-2.1.1-cp315-cp315t-win32.whl", hash = "sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66", size = 182707, upload-time = "2026-08-03T21:21:11.226Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b5/ba2b299993c26577d529b6ae29841f9e15b9fcf004d65f423f4fcf94ade9/cffi-2.1.1-cp315-cp315t-win_amd64.whl", hash = "sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3", size = 193772, upload-time = "2026-08-03T21:21:12.39Z" }, + { url = "https://files.pythonhosted.org/packages/aa/29/35e016098c814cd93de9cd320c66b5bfba14dc6ecedd3cb518fa7c408c69/cffi-2.1.1-cp315-cp315t-win_arm64.whl", hash = "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692", size = 186360, upload-time = "2026-08-03T21:21:13.636Z" }, ] [[package]] @@ -1494,11 +1494,11 @@ wheels = [ [[package]] name = "extra-platforms" -version = "13.5.3" +version = "13.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/76/18a6be3aed79db31f15bed87c1a1271e4d1a46a613d4247fb23fa4635f19/extra_platforms-13.5.3.tar.gz", hash = "sha256:3e362487b1b6ce6e01fa28a912684d07da53b06d9cfdb6e8af1558e501a62f45", size = 460359, upload-time = "2026-08-02T07:31:52.7Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/20/3d7ba1bd9cd9235eda78a143adcb2a710c6117f5b3f500237bc2f240808c/extra_platforms-13.6.0.tar.gz", hash = "sha256:92b5800c0ca9767820ae2cf3d48b7037432c1360055ed1804bc43a8269a2a090", size = 464011, upload-time = "2026-08-03T07:27:46.482Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/53/ad7a950786f427a77b304e9b88858f23983a277ad643d67ec336ab99e9d0/extra_platforms-13.5.3-py3-none-any.whl", hash = "sha256:7d7ed23284619c1e1f9b355b045bb1c225645525876ef8f85518ffda229ae573", size = 97116, upload-time = "2026-08-02T07:31:51.392Z" }, + { url = "https://files.pythonhosted.org/packages/cd/c4/3e562dc1fe2df78a16699a59ea76dda82474feefe6bed78929644b342925/extra_platforms-13.6.0-py3-none-any.whl", hash = "sha256:1ddfca538dd89fd5e1132ed5eca8b80ecb15f8ac7faa22ad232d4a6ff1a71f26", size = 98540, upload-time = "2026-08-03T07:27:44.994Z" }, ] [[package]] @@ -1793,7 +1793,7 @@ s3 = [ [[package]] name = "google-adk" -version = "2.6.1" +version = "2.6.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -1822,9 +1822,9 @@ dependencies = [ { name = "watchdog" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/84/9eeb008e45edc5522e098b2a38c1e9d469630e20457fdae96852ea68d115/google_adk-2.6.1.tar.gz", hash = "sha256:3bb6fae4c859197ea4cb68045232bfc76c90b46fd0149da732bdfe6c47b6f0d1", size = 3721157, upload-time = "2026-07-31T21:15:53.115Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/39/d9512c13102d20dd32e4c36b8af3918583a2a1ae4e1f062dec01f648bb59/google_adk-2.6.2.tar.gz", hash = "sha256:e95eee1e18811078a3478e8b4a57922dd8eff0fa4c465b864f0f6174c04bfad8", size = 3721236, upload-time = "2026-08-04T01:20:22.206Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/f5/9fffcc19719b6919269810cdbd0a077fdde2c410f5591fc52384059cf55b/google_adk-2.6.1-py3-none-any.whl", hash = "sha256:f585bf99f401881da2fab5a8e7b3dfa55cc0e11cfcf22cf0a9abe9b163990566", size = 4305112, upload-time = "2026-07-31T21:15:50.207Z" }, + { url = "https://files.pythonhosted.org/packages/6d/6f/8bde13dff964e96f2351c5aab0fc8a2ce69a16e1adeaed0c5558331b1f27/google_adk-2.6.2-py3-none-any.whl", hash = "sha256:59908032fa10ba0249aad12fb571d5f5cacb24f1c85e63a6eaec45c7c3ed01f1", size = 4305118, upload-time = "2026-08-04T01:20:20.307Z" }, ] [[package]] @@ -2493,7 +2493,7 @@ dependencies = [ { name = "comm" }, { name = "debugpy" }, { name = "ipython", version = "8.39.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ipython", version = "9.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jupyter-client" }, { name = "jupyter-core" }, { name = "matplotlib-inline" }, @@ -2536,7 +2536,7 @@ wheels = [ [[package]] name = "ipython" -version = "9.16.0" +version = "9.16.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15' and sys_platform == 'win32'", @@ -2568,9 +2568,9 @@ dependencies = [ { name = "traitlets" }, { name = "typing-extensions", marker = "python_full_version < '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/49/04360f83b4d110195751b4171b75dc1cd7b97ba122b18da34b5828172d59/ipython-9.16.0.tar.gz", hash = "sha256:d2f92587b1ef51d84f934dffe05fabb9255f0038ed0a21426f2ea761e39ad09a", size = 4515375, upload-time = "2026-07-31T08:02:51.977Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/96/b150fe7e25a5a29ae9ac1374e71488639605d39a1ea4abb74c9ce33af235/ipython-9.16.1.tar.gz", hash = "sha256:5a3d1f9a47ff216d6cf9cf863124f6a2c1a198d1354c546a4d24a370a283b64c", size = 4515302, upload-time = "2026-08-03T08:36:15.571Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/82/d30656b9eb33b8ed4e421ca55c13c7fff412086f0405bbe53c39a7ee4a3b/ipython-9.16.0-py3-none-any.whl", hash = "sha256:3d02b96de2a59074d153b1ac1c3865de738df114e430e879e6e5ef100a4d470c", size = 625973, upload-time = "2026-07-31T08:02:50.114Z" }, + { url = "https://files.pythonhosted.org/packages/bc/8e/1239df488393d61076653bfb29f759d0f60cab8e030abdf7c17c31539b51/ipython-9.16.1-py3-none-any.whl", hash = "sha256:4acae635506f6d352d94c4899a19d5f85f8bc4d230932342dca556fdab1c69b4", size = 625974, upload-time = "2026-08-03T08:36:13.654Z" }, ] [[package]] @@ -2592,7 +2592,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "comm" }, { name = "ipython", version = "8.39.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ipython", version = "9.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jupyterlab-widgets" }, { name = "traitlets" }, { name = "widgetsnbextension" }, @@ -2721,7 +2721,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ipykernel" }, { name = "ipython", version = "8.39.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ipython", version = "9.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "ipywidgets" }, { name = "nbconvert" }, { name = "nbformat" }, @@ -4194,11 +4194,11 @@ wheels = [ [[package]] name = "packaging" -version = "26.2" +version = "26.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, ] [[package]] @@ -4461,26 +4461,26 @@ wheels = [ [[package]] name = "prek" -version = "0.4.11" +version = "0.4.12" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c4/1a/73b6dae5ce7e997cb35a69bfe1d25a798e85fa3d2eabf95324563f461b30/prek-0.4.11.tar.gz", hash = "sha256:4a14cb9bbae850605ae3904fbdbb12f0e00c12455efaa2266da8fb8e5c0350d7", size = 516254, upload-time = "2026-07-24T17:05:35.107Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/5c/cb6e63f7e5a58a5313ddb70409174f4dc004e4b0910b8a8d3f59b2225a95/prek-0.4.12.tar.gz", hash = "sha256:04beeba7f40437cd2f36804b84101bd7f3c9fb40b52da46a25604642ab2bfb09", size = 519080, upload-time = "2026-08-03T11:28:33.147Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/d7/a00b2de492a80e99b1698e72c1d196ac3ed544dc7ee0ada261ac066e78e0/prek-0.4.11-py3-none-linux_armv6l.whl", hash = "sha256:3830cb7cc47e837888b8b464ecb21355a69235cfacef0fd89101e17f09345d63", size = 5770511, upload-time = "2026-07-24T17:05:12.829Z" }, - { url = "https://files.pythonhosted.org/packages/c1/1e/f97c74defcd5d5645888cb99fcdd9b8b48cc7247cf31e64414d106d56d66/prek-0.4.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:45facadf9c2332b28e6ab2744312ae0275a93ab5a437da8bcc53a8c7260cb4b0", size = 6118049, upload-time = "2026-07-24T17:05:14.451Z" }, - { url = "https://files.pythonhosted.org/packages/d9/92/8367d26421ee6fe6019a63928fb0ed31179cd0d6199879c524f89ef4c95c/prek-0.4.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2f8a194b1d00d24dff8baff691c99e2668339da2da3482b4ee99c1a0f2409378", size = 5601478, upload-time = "2026-07-24T17:05:15.81Z" }, - { url = "https://files.pythonhosted.org/packages/e1/6f/7617c9b87afaadede4167720aae7ef12d7e51167db903bc8fbac0cadef7b/prek-0.4.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:21d93e6d76bf3d7a9bb70c6ac86ef372adaee50068c45749e6b9e1c2ac4ec939", size = 5932071, upload-time = "2026-07-24T17:05:17.217Z" }, - { url = "https://files.pythonhosted.org/packages/ef/41/6796a4011b04212333259064aa885a71d03d9581ba9bff52db3ce58d1f06/prek-0.4.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7fb07cde2d2156efa6980122b3f13dc88f20c84b933c6205675d0f1e7be2cde8", size = 5677617, upload-time = "2026-07-24T17:05:18.658Z" }, - { url = "https://files.pythonhosted.org/packages/03/5f/3e339901f8460b6073313619b4fd9bf4135e48430695c53640b83ace88ea/prek-0.4.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b7f5e446d2aca739bd18b380578e9c78bb4c086299abc3e167d51c47840c56f", size = 6106370, upload-time = "2026-07-24T17:05:20.219Z" }, - { url = "https://files.pythonhosted.org/packages/8e/4e/94e24b5c1910ec15692ecaf33d0d8ef0d02a02a8b5e40d3c976396880cba/prek-0.4.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7059d640e595d098600e2d97af961f46292b4112670edbe632de84f6828389e", size = 6884342, upload-time = "2026-07-24T17:05:21.587Z" }, - { url = "https://files.pythonhosted.org/packages/a2/7c/fc0daa033dcafe74990c00af2da1e16922790b9c1b8da7e8eebf1123838b/prek-0.4.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a4df33998fcac878bce3b2c624e1caeb9609f3d562c640702c1234ed815daf", size = 6331365, upload-time = "2026-07-24T17:05:22.87Z" }, - { url = "https://files.pythonhosted.org/packages/d7/36/49f152b8f539930e9685cff0509695ce4054abcecc22f4c16c4e1e5c23d0/prek-0.4.11-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:866991f387527c5f880ce2cc3ddea9b33cc5b986881f8c7f92524cf0969c1350", size = 5939075, upload-time = "2026-07-24T17:05:24.249Z" }, - { url = "https://files.pythonhosted.org/packages/4f/fe/7b097af9161edae7ddedcc9f5cda4a5f31346a492ce3f92583d96c46f628/prek-0.4.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:247e5d8740e137ebdf24fa96f01a95b2d2f1892e956a1ab00c7b1474a59ebcc0", size = 5799029, upload-time = "2026-07-24T17:05:25.652Z" }, - { url = "https://files.pythonhosted.org/packages/49/63/dc955ff99e1002d3cd375b21467c87046bc41deddfce86d898240757b151/prek-0.4.11-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:603ba9f2fd9d666dddb3ae190a25a5c55091b843cde90ed52e0a1116f50d4062", size = 5651211, upload-time = "2026-07-24T17:05:27.027Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b8/bf2139ec25eefb5afef43afac7620c5181f2c2661f220598beacb1771207/prek-0.4.11-py3-none-musllinux_1_1_i686.whl", hash = "sha256:f2682ece3c5fc7201106c4fdd84b0587ccea4b8a2ecefa3e94d3074d2841f1df", size = 5954784, upload-time = "2026-07-24T17:05:28.391Z" }, - { url = "https://files.pythonhosted.org/packages/fc/29/3fe5990aee1bd7c4d50a03358ad867c5e912d9510aafb83a359c7347e5fa/prek-0.4.11-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:22721d30394192931fecc80d6fc6dd47e8ddf8db8b9693805aba8ec0f50087ec", size = 6448916, upload-time = "2026-07-24T17:05:29.837Z" }, - { url = "https://files.pythonhosted.org/packages/0f/fb/abddacf43738302242ecd237236c7c80cd7c1d27545e16e803770aee76e9/prek-0.4.11-py3-none-win32.whl", hash = "sha256:8b093e7624522146049e994d5cf283d01d71632b638620b79ae0f96afeaa2624", size = 5483539, upload-time = "2026-07-24T17:05:31.228Z" }, - { url = "https://files.pythonhosted.org/packages/00/1e/c293f7a15cb93963c4be36a02e144b93f43906bc02644a3f04e5708e7453/prek-0.4.11-py3-none-win_amd64.whl", hash = "sha256:5a3d7c80b970b456e5f1bcec8382008ee1ae6a3f324a6b9bb4ff7e666ab0f3c4", size = 5861119, upload-time = "2026-07-24T17:05:32.479Z" }, - { url = "https://files.pythonhosted.org/packages/cd/0c/05fe6eb9d6a54d0e02dfa8cc5ad6f86869bf953419ec15909a32466a28ee/prek-0.4.11-py3-none-win_arm64.whl", hash = "sha256:e7b0df37ce05e45a14a9da39ab104691474d72f139bf4f6c860f754763a322cb", size = 5626386, upload-time = "2026-07-24T17:05:33.813Z" }, + { url = "https://files.pythonhosted.org/packages/f3/23/5811a3161e072e5f93e4da01af611ee30c32922507b8ab4d9873df6affd3/prek-0.4.12-py3-none-linux_armv6l.whl", hash = "sha256:cd92000b051e433f26340821cf1cc8e6e3960f1275f3d516ca01f05905abba64", size = 5793226, upload-time = "2026-08-03T11:28:09.534Z" }, + { url = "https://files.pythonhosted.org/packages/a3/88/8607845d94eb1482e1bd335dadf098618f077a15775f7e98de99669052b4/prek-0.4.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5904fe6c6ab26e7d8792a3c7f1e3fc8d94fcfb63ad33b247c35f004b62cb6275", size = 6132269, upload-time = "2026-08-03T11:28:11.147Z" }, + { url = "https://files.pythonhosted.org/packages/ac/28/571d79ba457fbd9ecf40ae879c91952e12f5fa475306218c91139b86db7a/prek-0.4.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:df3eff1db9c24dc293010a07bc7a0ae0c541d55af828f5586405dedc28c4920d", size = 5614964, upload-time = "2026-08-03T11:28:12.983Z" }, + { url = "https://files.pythonhosted.org/packages/b0/a9/3f5cb79a73c764a8ac38d5bcd51e0df57239856eca7949b09bdac4338bf3/prek-0.4.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:c7733b44ca772ea32ec6a8bee669d0358bdf45873e79767afed196065084f31c", size = 5941047, upload-time = "2026-08-03T11:28:14.45Z" }, + { url = "https://files.pythonhosted.org/packages/8c/00/1dfed0ef8af10c5c32aa903486dccd33d2df171f3d945a037c5692f10760/prek-0.4.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87f170cf1ffd6e3a196f947b83dff1f6c2cd68635f8d49740278bebe7b682262", size = 5707994, upload-time = "2026-08-03T11:28:15.914Z" }, + { url = "https://files.pythonhosted.org/packages/c0/bd/5f388f6cbdc0445b850e7c1a160d0be67fcef8bf221e3c8141a1feccef17/prek-0.4.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57dad513831f060cf73808df8edec29d46ec311435aa69f21c80edebf23dc5e1", size = 6133784, upload-time = "2026-08-03T11:28:17.184Z" }, + { url = "https://files.pythonhosted.org/packages/ba/47/342091a987bf68a74acec6d226a40ce7d51faf0019aa4126cc7bc952f8a7/prek-0.4.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b204844abc7ded983471f576ae8dc13b99e9b8d022e4d4b46176c6654769c9d8", size = 6901589, upload-time = "2026-08-03T11:28:18.545Z" }, + { url = "https://files.pythonhosted.org/packages/5b/8a/3ef7bdc3c3441649ebc040b9e164a13163e1e5fabae23e7bbb901992f3de/prek-0.4.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43b0a5a9d3f2f77871fdcb7893bfc5c8fe7e44f4e603ce6e4712bfec96b2d6f2", size = 6342189, upload-time = "2026-08-03T11:28:20Z" }, + { url = "https://files.pythonhosted.org/packages/c4/da/6277908442301b1b92a2879f6b04aaa03accb900f80e42776fc28b8197ef/prek-0.4.12-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:0d188e572c306cc44b96e1bae5647e25b7bd311113f3f3f4a67320c257ee64a3", size = 5951250, upload-time = "2026-08-03T11:28:21.339Z" }, + { url = "https://files.pythonhosted.org/packages/a3/68/bff51a7332837edb1ecbe017325adb7fafd69b9c7828ddc81a1334b884af/prek-0.4.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:986f52d104b7066190f0f32aebe3467710356de265e9bfd892101ba99371db4d", size = 5804147, upload-time = "2026-08-03T11:28:22.656Z" }, + { url = "https://files.pythonhosted.org/packages/aa/de/b7f544971072ed7814125145dfeb1f7c15cce6b78ccea65a96298ff37838/prek-0.4.12-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:13e34d9e09bafcbf1f25a01cf86985e2c5e486591d3f45b2786ba3de82e5153a", size = 5680104, upload-time = "2026-08-03T11:28:24.271Z" }, + { url = "https://files.pythonhosted.org/packages/68/94/95942bcc20a6a91ec2989aa30fdeb00ad095be736ec48b4bbcf0376166b1/prek-0.4.12-py3-none-musllinux_1_1_i686.whl", hash = "sha256:3d0208370da73e8b5bc97f2492dc3975f8dd2c22f4bf6e1f2cf3342503764b52", size = 5975030, upload-time = "2026-08-03T11:28:25.683Z" }, + { url = "https://files.pythonhosted.org/packages/ef/6d/26e6497198d81cf9aa82495400aef46adea8df3e4a4efc5f00e3b6ab3292/prek-0.4.12-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:b1005f42920111bec1403c25e8f2f12ec7af0be06686cc3b8dcf85429af908a8", size = 6458532, upload-time = "2026-08-03T11:28:27.121Z" }, + { url = "https://files.pythonhosted.org/packages/44/02/ee140c2eb4701bd194db429d84630733492be94897d5f72b61d6f11e6619/prek-0.4.12-py3-none-win32.whl", hash = "sha256:afee229488dcceaea282288e4d7096a93da5a8b85649d9ef506dbdbcd78f38a7", size = 5502213, upload-time = "2026-08-03T11:28:28.691Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7b/744cff84def48c1ce38c0b4f643a3553c66976c5bb7869ab7317044870e4/prek-0.4.12-py3-none-win_amd64.whl", hash = "sha256:fdd27bad8adafea8fe77606950ca09200d59296a47ab131cfb88718d460949d7", size = 5868065, upload-time = "2026-08-03T11:28:30.377Z" }, + { url = "https://files.pythonhosted.org/packages/46/1d/e2c0fc222904ef73df1739b11a83edc29e38bc4bc61259f2ca6d2f15abb0/prek-0.4.12-py3-none-win_arm64.whl", hash = "sha256:45e34a24fba4a4e4568682477158591698efc2375b8d1d418ae424691c4bd01b", size = 5632819, upload-time = "2026-08-03T11:28:31.743Z" }, ] [[package]] @@ -6246,7 +6246,7 @@ wheels = [ [[package]] name = "sphinx-autodoc-typehints" -version = "3.13.0" +version = "3.13.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15' and sys_platform == 'win32'", @@ -6265,9 +6265,9 @@ resolution-markers = [ dependencies = [ { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" } }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/99/e5a23a7bc3f9b6b799dbf72ab3955cdc0b2382803b5d11eaafcc76621662/sphinx_autodoc_typehints-3.13.0.tar.gz", hash = "sha256:59eb4fe26f71ccd7a8f10caadddcb3c3b31df7016f91bda546da2a333bae4e12", size = 85285, upload-time = "2026-07-21T13:10:44.981Z" } +sdist = { url = "https://files.pythonhosted.org/packages/02/5f/7f2a768683c24dac122d6d9714b923ed84d87ffde058a37d525996687295/sphinx_autodoc_typehints-3.13.2.tar.gz", hash = "sha256:ea20b9a217b86391b0ece262c4336e3c2f476fe1f76028635d550b4e74cd7498", size = 88492, upload-time = "2026-08-03T22:26:01.344Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/1c/4b3c9b1a3130b9d717f517351ce8325a62fad7c4f846cc324025c22a43c6/sphinx_autodoc_typehints-3.13.0-py3-none-any.whl", hash = "sha256:d71c388c865f3bedc1f32416febb0f8886b3051a4cd8bd8677e64b8b65c9b475", size = 43440, upload-time = "2026-07-21T13:10:43.793Z" }, + { url = "https://files.pythonhosted.org/packages/97/53/7ae6f44d2a9275562ff6fb7cf8be14c35e8ab057ebd4954f9f10558efb0d/sphinx_autodoc_typehints-3.13.2-py3-none-any.whl", hash = "sha256:b2a7b531369a128c7c52867962fe224d89a1d480a462a314621c710a7d6e3ecf", size = 44836, upload-time = "2026-08-03T22:26:00.029Z" }, ] [[package]] @@ -6570,11 +6570,11 @@ asyncio = [ [[package]] name = "sqlglot" -version = "30.15.0" +version = "30.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/fb/c43bfdc662bd6fcdf93158f9239b975ac01b775cfcb56015913fee9b95e2/sqlglot-30.15.0.tar.gz", hash = "sha256:fe5412f4da61075f532d3de2482144bf7139028980a37bbd79fb016c49dc2dbe", size = 5973319, upload-time = "2026-08-04T17:37:12.831Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/42/97ae59ab9479f345f97db948792b41bdaf51c38589f994ab97014ffae2b9/sqlglot-30.16.0.tar.gz", hash = "sha256:26abd1ef583fbecde24931eb56a5df4a086ba50ba0b36738233da9da16ffea13", size = 5984853, upload-time = "2026-08-10T15:43:47.176Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/28/f1e99aca0c23e2406dc8727555476647d58c2d6315d7d8101dffbb2cbc0f/sqlglot-30.15.0-py3-none-any.whl", hash = "sha256:594da0bc9d020edfb9982fd56b61dc93483cb24edca5ed1b3bb17937ba9b59bc", size = 731813, upload-time = "2026-08-04T17:37:11.48Z" }, + { url = "https://files.pythonhosted.org/packages/c8/bc/c92985806306174ae08ca84576a62659e8a406d7f670d2b2bc1a96340f87/sqlglot-30.16.0-py3-none-any.whl", hash = "sha256:f14f119e87bb1397316ab2b3c1f12dddd3b098921185dc69935952111be91788", size = 737757, upload-time = "2026-08-10T15:43:45.331Z" }, ] [package.optional-dependencies] @@ -6584,33 +6584,33 @@ c = [ [[package]] name = "sqlglotc" -version = "30.15.0" +version = "30.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sqlglot" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cb/56/76dc38a4942f79671b99af338e3c253c1567ec74ffbb761b27fbe8f9e101/sqlglotc-30.15.0.tar.gz", hash = "sha256:b6fad5b5a5462a20ffeba0d95b5cc9138f2da938dd6771338f4ce6622a6de14c", size = 509513, upload-time = "2026-08-04T17:36:12.248Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/8a/edf2f4591cf2c8aad32a2204aae63b4794dfba459b60c3967230ba8cb7a2/sqlglotc-30.15.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0eb50a675093c62158cd373577c8d95d94e093c88fe2415120c67a4e02a97322", size = 32635899, upload-time = "2026-08-04T17:35:27.699Z" }, - { url = "https://files.pythonhosted.org/packages/a6/a8/5a3d2e57d43fc58298d131a8ab450f0325371a2ff9a5c5f2102ee3c08c97/sqlglotc-30.15.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6cf89eec583fcd4f708172946b29827ea2eabda6b012687d46f5d59d8b25152", size = 25040899, upload-time = "2026-08-04T17:35:30.101Z" }, - { url = "https://files.pythonhosted.org/packages/13/14/b8baa5ae6c0516bf45184eb2088b7c165a6dc36e8c112a1ae45bc7734ec6/sqlglotc-30.15.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba57278fc12adfcc4358f1b8057801ea895533ae0f576d6eeab1bc901425d916", size = 26184703, upload-time = "2026-08-04T17:35:32.5Z" }, - { url = "https://files.pythonhosted.org/packages/b9/f0/c76406a4a3ae1255b7c737de323a5e5aea168da52b404b15de0a24880b4c/sqlglotc-30.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:082cdaa89503b7a108b957cfbbd17e79cac8d52cd29984ec46c48b2c1473ed6f", size = 10971386, upload-time = "2026-08-04T17:35:34.643Z" }, - { url = "https://files.pythonhosted.org/packages/33/23/ac45a1f4c0f32322d228815cea9d9eea614e97942c0368b139d6f8ccd44b/sqlglotc-30.15.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d6cc138ec396725f5835ed377024a1e547479fa709c2d457f7671981b7cd9c3e", size = 32490077, upload-time = "2026-08-04T17:35:36.531Z" }, - { url = "https://files.pythonhosted.org/packages/5c/8c/194b5cb2c24f2e52dc34c151a977de6491d58da4b13d0c0e23c675909180/sqlglotc-30.15.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:37046791577cbf550fae3269a25bae110c00e11772cf933343e5425486d16c07", size = 25258864, upload-time = "2026-08-04T17:35:39.18Z" }, - { url = "https://files.pythonhosted.org/packages/97/b3/d90e1373db99a5c4eb50afacdce04a43efa4c4b91435aa749b184dbee74f/sqlglotc-30.15.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2dfabf5e9944e92a3594e5ff115742fe97146a2e979e0287ef899ed38db80f3b", size = 26435209, upload-time = "2026-08-04T17:35:41.438Z" }, - { url = "https://files.pythonhosted.org/packages/42/ea/795dd987430faebf1ce259dcd86d2db272e7d588f14f4c17fda9694a3ff4/sqlglotc-30.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:f47c34f65a087c2e2261f55558309a4bcd829de111902c26df1ad87f9aa4d6aa", size = 10964177, upload-time = "2026-08-04T17:35:43.445Z" }, - { url = "https://files.pythonhosted.org/packages/44/ce/e1643a2765d206d679e27142212bc6cec49a98c98fa0039e2e7a00fd2cc0/sqlglotc-30.15.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d8fbd4f32e642905d201b776e418114599025ad39ee4584ba715daf4ca53ce3e", size = 32704218, upload-time = "2026-08-04T17:35:45.347Z" }, - { url = "https://files.pythonhosted.org/packages/02/37/1435eef72e9dbbab708db8a07f1da93684630c123066c49ddc0919c63ebb/sqlglotc-30.15.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dce37377aff9dd71fc1d4c99e7826517e2749fb4c55329b59ba82d16a49c0338", size = 26175379, upload-time = "2026-08-04T17:35:47.915Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9f/15d837e27cc52dfd2558b78ce39e7a56b30c0678ff48e29d0df07329505d/sqlglotc-30.15.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef2fe3407e8d30678762216d5e022ee5538c8a50950ab0570e02586e6788ae0a", size = 27432275, upload-time = "2026-08-04T17:35:50.201Z" }, - { url = "https://files.pythonhosted.org/packages/ec/4e/eebbd52fb0709b5600a32b8bd7ae0529815039524cce78f4f7bf4d0110a5/sqlglotc-30.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:5b0c3bb744352eb65a93a4d0acfa4b69647de93ca31a55267d336b6bcb304f52", size = 11185835, upload-time = "2026-08-04T17:35:52.38Z" }, - { url = "https://files.pythonhosted.org/packages/e4/48/cc17443d80dea19ef9b6d7af7fa44d66050c4e474a110c6ce1dfa54fea79/sqlglotc-30.15.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c51cd338a32909e6c6754592056a8d4e2f1b7c6e6d36d393eb2e9d832f151c1e", size = 32521321, upload-time = "2026-08-04T17:35:54.605Z" }, - { url = "https://files.pythonhosted.org/packages/f2/a4/bfedf69ab0ba7ffa042b0232687d3147ddabeb8be3ad347af67b543d5a0f/sqlglotc-30.15.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2eeef18fe64c07b0d5bb62755b6aa57d553281b1d940445260967481129ac5e2", size = 25752855, upload-time = "2026-08-04T17:35:56.793Z" }, - { url = "https://files.pythonhosted.org/packages/5c/88/a7250d8baaa0eec6015cb52c068dfb66dc2d5a56b357ca2dceb61296985d/sqlglotc-30.15.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e76c2143b09a5e0f39c6ae3aff4bc96690246aa31cc4ea0e2832a7d1d6700bd3", size = 27042411, upload-time = "2026-08-04T17:35:59.243Z" }, - { url = "https://files.pythonhosted.org/packages/6c/4f/b20282e966e6306e4a82adc49bb705b22035c7b287324a62e9870eb94a11/sqlglotc-30.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:784466a58effd1cafa2fec08c7ef25d2ad0e5f449015ae6776512f7de68b920d", size = 11173097, upload-time = "2026-08-04T17:36:01.794Z" }, - { url = "https://files.pythonhosted.org/packages/17/5c/47e113d933eba32910be817aeab1b91d535e8a1dfb757e51f75d74dfb41c/sqlglotc-30.15.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fef426625941b0368d47c8679f2806681ab4b42103b15e12e4048c2b318fc439", size = 32418622, upload-time = "2026-08-04T17:36:03.891Z" }, - { url = "https://files.pythonhosted.org/packages/b0/34/4392249578782149d927eae2da33cc0c3acec019a98de4fe209730e198ae/sqlglotc-30.15.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b39df593b28ea3ffc3c78159ecab0ce1735af1d65a9a616ead9d1b36bff14a4", size = 25750689, upload-time = "2026-08-04T17:36:05.881Z" }, - { url = "https://files.pythonhosted.org/packages/5b/2c/e816418a0d46de9f1af1d6d155a38f278d7496275ee30a562bad26ae43c4/sqlglotc-30.15.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9b7547520ee9f26260090e2834e8329fa1d380b82ffe3c5926e49b22abd47cc", size = 26945235, upload-time = "2026-08-04T17:36:08.308Z" }, - { url = "https://files.pythonhosted.org/packages/ab/c6/63430ccb25c4e93987229c584bb7c2e1e0c829e4fc9c7f34891c4e32afd4/sqlglotc-30.15.0-cp314-cp314-win_amd64.whl", hash = "sha256:317b27808c98f01305181030a5d5e03490f7ccf6f4b792169f9926e7c0796878", size = 11310589, upload-time = "2026-08-04T17:36:10.175Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/eb/d3/88410883278b9e6714d63e1bb09064c630d30ad325e875927bd101160b53/sqlglotc-30.16.0.tar.gz", hash = "sha256:16a6ff31db74384b219416f23d9509c9e6f39c7c02d8796ad6a23cbcd40842b7", size = 514906, upload-time = "2026-08-10T15:42:57.389Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/f9/f6d2fb62512bbcf986ac0c005df239215687e715383cf9fb7133ee0f86c8/sqlglotc-30.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ced7aeddf9464c6506811342f4a229c730f8bafe38ce0f50c4798fa65723f069", size = 32749550, upload-time = "2026-08-10T15:41:48.211Z" }, + { url = "https://files.pythonhosted.org/packages/5f/c5/a71f3c655d1ad61c2d59c69edf08d7d28961322b4ffe217675d6cf12a180/sqlglotc-30.16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5ae23d49d1557e6b4b12473b950c1e6f638cb87d3661f3cb27155c066e2005ad", size = 25085164, upload-time = "2026-08-10T15:41:51.841Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c7/05f36e672be6660a204998be15702e1f8e76aa0cba676d5f53fdd945d995/sqlglotc-30.16.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e44537430c7e736131fdf6d703929bc83b29c72a8d5d1011d0010ae5daa0376", size = 26247541, upload-time = "2026-08-10T15:41:55.941Z" }, + { url = "https://files.pythonhosted.org/packages/c1/0f/87531311d03d0be495d87f58491b1f5e9240fc57d6c46846ec38b6a9c92f/sqlglotc-30.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:447c4b0c5deb7ade5771073f9be317efde5af26cd996f31499e9dfef1c4643d2", size = 11017473, upload-time = "2026-08-10T15:41:59.095Z" }, + { url = "https://files.pythonhosted.org/packages/9f/0c/98daff80535ab17c061ba52e18653f202f3ccd2b5b055fd6837adccb2510/sqlglotc-30.16.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c8285bf1d5b5018ac2ab4d25023b83b2ac979ca69f522eb7a0edd5db845d0262", size = 32600289, upload-time = "2026-08-10T15:42:02.748Z" }, + { url = "https://files.pythonhosted.org/packages/53/98/bdcf624b22076dc6ef83f76055242d0d463d3d40343a32545a86510db281/sqlglotc-30.16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc040dc1c5cee9b183c71860fc30be12e084aaa63743eafd9e0bb9b41225f51", size = 25299345, upload-time = "2026-08-10T15:42:06.25Z" }, + { url = "https://files.pythonhosted.org/packages/06/55/bcaa417a0030fceebe8e5d23ab9e4804a2fb35f07c82aa5ed96728fbe0bc/sqlglotc-30.16.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0464f9358f88c65f7b35f74d1cbd13450a47a2da4a830c259a713967125a3b36", size = 26497946, upload-time = "2026-08-10T15:42:09.788Z" }, + { url = "https://files.pythonhosted.org/packages/cc/00/879a11e5238f51ac69ef90d8426ec85d442c634256c4b765dea070db9a87/sqlglotc-30.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:03db4615020727a2f23b53faecbd40f7597513309a9443832b9c906fa417ebeb", size = 11009428, upload-time = "2026-08-10T15:42:12.577Z" }, + { url = "https://files.pythonhosted.org/packages/d7/51/9e5f192977bd03a13e53b1340d3ce7d341d695fcbd1ac2904e0d34c96702/sqlglotc-30.16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ef4a909e3c47c116768caaf4a8d8f3ea3d2f2bbad3c6aae524c40a38d93d439", size = 32823089, upload-time = "2026-08-10T15:42:16.159Z" }, + { url = "https://files.pythonhosted.org/packages/c6/39/ef69fd3adb7a3f2821a57845db7fbc324f7920a120c569350515c98c3960/sqlglotc-30.16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a471f53046b3d92c8cb26d031cb24930a1b605500806f963bfb112f873d568a", size = 26232383, upload-time = "2026-08-10T15:42:20.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/65/f07bac2f5fed9df2f2bf5c18f6e9a38352950790e93eec5871e04fa6ed6e/sqlglotc-30.16.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7eac6330f42f5dd3c198de9e5102603419bf3e7ee52eda2831215804901c74d", size = 27508633, upload-time = "2026-08-10T15:42:23.892Z" }, + { url = "https://files.pythonhosted.org/packages/2d/03/1cab8c6283ccc0f769d334597597f87c63797ea58adf7bbb63ab86abed2b/sqlglotc-30.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:74fd5278bc47dee9e91a0bf291350fe3f92f7b205f444f69341a600824bb2f73", size = 11230677, upload-time = "2026-08-10T15:42:26.986Z" }, + { url = "https://files.pythonhosted.org/packages/0d/2c/736690b981717ba83aefc35082f890c9ea65808247b8f57d3fcdc168e435/sqlglotc-30.16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:85a9940ea0893d1e381dfe565fc0c130052a2b0d03aad5f239c6e8e32283b1ba", size = 32635113, upload-time = "2026-08-10T15:42:30.82Z" }, + { url = "https://files.pythonhosted.org/packages/05/8c/2f3ce6622e30bd8aa4438e6e319dc5a7a026f768c7ccbfdb5e183fb9d021/sqlglotc-30.16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15241b7919943ab96c0daa953d8ffefa0e0a58afae7249f857f055180b602aab", size = 25817356, upload-time = "2026-08-10T15:42:34.456Z" }, + { url = "https://files.pythonhosted.org/packages/d8/59/9ebed72ce65e71a89aa6ad45fc0bf4ef4c86a40edc752668b55abc3ed520/sqlglotc-30.16.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1540e8c97e592cb605e056880cfe260a8b0f1b2eda90adb87d1d9e82dd566bb", size = 27118365, upload-time = "2026-08-10T15:42:38.547Z" }, + { url = "https://files.pythonhosted.org/packages/1e/7e/2514e7c44a59da35bb405dfc0d6bab8cc024c38ddfdf17a4b3c50464c792/sqlglotc-30.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:1ad579f944b81182d9a0873aaa4ce4410b1a1855129d065004bbafe28c79cc16", size = 11218481, upload-time = "2026-08-10T15:42:41.648Z" }, + { url = "https://files.pythonhosted.org/packages/0f/7e/2a305d8d1a2b497828576bdac93ecb5601b80ae3673d059c5b15e88bbc54/sqlglotc-30.16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:018c4cfafeb82906cf27b1548525044bed70444bccdbd2e52289bf35b3ce0d8d", size = 32531459, upload-time = "2026-08-10T15:42:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ab/4bdae4642c63442a8fddb011641c1330eec7f69eef8da7077fcdc2a3c47a/sqlglotc-30.16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:690ed9d78fc970768b222952707fcd3ed7f88393b40bf568f4c43b8f1aeff4d2", size = 25804540, upload-time = "2026-08-10T15:42:49.089Z" }, + { url = "https://files.pythonhosted.org/packages/17/8e/10ab34931a5503b602aab5e8445eeb5485b41bf09f3dde40d4d789b64f1b/sqlglotc-30.16.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05208b53981dba7328379cc086e0762325faf083bc1d0191c389c4c1151542fa", size = 27011826, upload-time = "2026-08-10T15:42:52.653Z" }, + { url = "https://files.pythonhosted.org/packages/33/86/1ddeb99670b54cc34c72a19083fd3c579f34fc705e2c947af5a4121a086e/sqlglotc-30.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:e1e082d7cf7ced61b957f3e91c196a51fa60ecd57438e65623348fb18c943682", size = 11358150, upload-time = "2026-08-10T15:42:55.427Z" }, ] [[package]] @@ -6624,7 +6624,7 @@ wheels = [ [[package]] name = "sqlspec" -version = "0.59.0" +version = "0.60.0" source = { editable = "." } dependencies = [ { name = "mypy-extensions" }, @@ -6831,7 +6831,7 @@ dev = [ { name = "sphinx-autobuild", version = "2025.8.25", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx-autodoc-typehints", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, - { name = "sphinx-autodoc-typehints", version = "3.13.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "sphinx-autodoc-typehints", version = "3.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx-click" }, { name = "sphinx-copybutton" }, { name = "sphinx-datatables" }, @@ -6870,7 +6870,7 @@ doc = [ { name = "sphinx-autobuild", version = "2025.8.25", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx-autodoc-typehints", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, - { name = "sphinx-autodoc-typehints", version = "3.13.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "sphinx-autodoc-typehints", version = "3.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx-click" }, { name = "sphinx-copybutton" }, { name = "sphinx-datatables" }, @@ -7307,11 +7307,11 @@ wheels = [ [[package]] name = "traitlets" -version = "5.16.0" +version = "5.16.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/61/a1/d7e7d9f461575d8bb77e3c3bd78a6cdfdd2bb4a06bfbbb8a0e1f51ab7bc2/traitlets-5.16.0.tar.gz", hash = "sha256:7de0a3fabaf5971ff15c8905545f9febfa850309fb8e86e1b42bdb5b46b293ed", size = 165946, upload-time = "2026-07-31T12:23:49.785Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/2e/a7fbfe268c8a3b32546930c0297c101d65a4a14c304ad5790a9f478f0e4e/traitlets-5.16.1.tar.gz", hash = "sha256:ed900c2b631aa3a112811139fa97b8d2c3bad5e989656bba4b7e52c7852c18c1", size = 166137, upload-time = "2026-08-03T08:32:36.848Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/bd/f8607e908605262e4926cbfd2560094bc5d04ef7f8aff1340e7fff503016/traitlets-5.16.0-py3-none-any.whl", hash = "sha256:94a9967ba45e89e837cf9934029c8d019bea9149cfffa115ed8c1900f679beba", size = 86093, upload-time = "2026-07-31T12:23:47.533Z" }, + { url = "https://files.pythonhosted.org/packages/ad/66/0d785f0bc5e4315a96c989bb476d0fc07ea4f85132550c7b156ca2035d52/traitlets-5.16.1-py3-none-any.whl", hash = "sha256:f775618166caa0396c8e337099240f2bd3e5e917d203b2e6fbe21a58d3cb1f6b", size = 86211, upload-time = "2026-08-03T08:32:34.48Z" }, ] [[package]]