From 19254396b6c66f26c1dc8818128ea90587755080 Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Mon, 23 Mar 2026 22:46:21 +0000 Subject: [PATCH 01/15] fix: resolve mypyc compilation bugs and runtime segfaults --- docs/conf.py | 1 + pyproject.toml | 3 +- sqlspec/adapters/psycopg/_typing.py | 6 + sqlspec/adapters/psycopg/core.py | 20 +- sqlspec/adapters/psycopg/driver.py | 9 +- sqlspec/config.py | 44 ++- sqlspec/driver/_async.py | 204 ++++------- sqlspec/driver/_sync.py | 48 +-- sqlspec/loader.py | 3 + sqlspec/storage/_utils.py | 2 + sqlspec/storage/backends/_iterators.py | 330 ------------------ sqlspec/storage/backends/base.py | 212 ++++++++++- sqlspec/storage/backends/fsspec.py | 10 +- sqlspec/storage/backends/local.py | 8 +- sqlspec/storage/backends/obstore.py | 19 +- sqlspec/storage/registry.py | 7 +- .../adapters/asyncpg/test_exceptions.py | 3 +- tests/unit/config/test_migration_methods.py | 21 ++ tests/unit/docs/test_conf.py | 21 ++ tests/unit/driver/test_query_cache.py | 36 ++ tests/unit/loader/test_sql_file_loader.py | 21 ++ tests/unit/storage/test_storage_iterators.py | 33 +- uv.lock | 120 +++---- 23 files changed, 563 insertions(+), 618 deletions(-) delete mode 100644 sqlspec/storage/backends/_iterators.py create mode 100644 tests/unit/docs/test_conf.py diff --git a/docs/conf.py b/docs/conf.py index 0cf304dec..714155c82 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -159,6 +159,7 @@ ] autosummary_generate = False +smartquotes = False autosectionlabel_prefix_document = True diff --git a/pyproject.toml b/pyproject.toml index 53240438b..8433806f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -201,7 +201,6 @@ exclude = [ "sqlspec/adapters/**/data_dictionary.py", # Cross-module inheritance causes mypyc segfaults "sqlspec/observability/_formatting.py", # Inherits from non-compiled logging.Formatter "sqlspec/utils/arrow_helpers.py", # Arrow operations cause segfaults when compiled - "sqlspec/storage/backends/_iterators.py", # Async __anext__ + asyncio.to_thread causes mypyc segfault ] include = [ "sqlspec/core/**/*.py", # Core module @@ -212,7 +211,7 @@ include = [ "sqlspec/driver/**/*.py", # Driver module "sqlspec/storage/registry.py", # Safe storage registry/runtime routing "sqlspec/storage/errors.py", # Safe storage error normalization - "sqlspec/storage/backends/base.py", # Storage backend runtime base classes (iterators in _iterators.py) + "sqlspec/storage/backends/base.py", # Storage backend runtime base classes "sqlspec/data_dictionary/**/*.py", # Data dictionary mixin (required for adapter inheritance) "sqlspec/adapters/**/core.py", # Adapter compiled helpers "sqlspec/adapters/**/type_converter.py", # All adapters type converters diff --git a/sqlspec/adapters/psycopg/_typing.py b/sqlspec/adapters/psycopg/_typing.py index b2c42a5bf..508f59eb9 100644 --- a/sqlspec/adapters/psycopg/_typing.py +++ b/sqlspec/adapters/psycopg/_typing.py @@ -8,6 +8,9 @@ from psycopg import AsyncConnection, AsyncCursor, Connection, Cursor from psycopg.rows import DictRow as PsycopgDictRow +from psycopg.sql import SQL as PsycopgSQL # noqa: N811 +from psycopg.sql import Composed as PsycopgComposed +from psycopg.sql import Identifier as PsycopgIdentifier if TYPE_CHECKING: from collections.abc import Callable @@ -203,8 +206,11 @@ async def __aexit__( "PsycopgAsyncCursor", "PsycopgAsyncRawCursor", "PsycopgAsyncSessionContext", + "PsycopgComposed", "PsycopgDictRow", + "PsycopgIdentifier", "PsycopgPipelineDriver", + "PsycopgSQL", "PsycopgSyncConnection", "PsycopgSyncCursor", "PsycopgSyncRawCursor", diff --git a/sqlspec/adapters/psycopg/core.py b/sqlspec/adapters/psycopg/core.py index 633540dfa..08c17d374 100644 --- a/sqlspec/adapters/psycopg/core.py +++ b/sqlspec/adapters/psycopg/core.py @@ -4,9 +4,9 @@ from collections.abc import Sized from typing import TYPE_CHECKING, Any, NamedTuple, cast -from psycopg import sql as psycopg_sql from typing_extensions import LiteralString +from sqlspec.adapters.psycopg._typing import PsycopgComposed, PsycopgIdentifier, PsycopgSQL from sqlspec.core import ( SQL, DriverParameterProfile, @@ -88,7 +88,7 @@ class PreparedStackOperation(NamedTuple): operation_index: int operation: "StackOperation" statement: "SQL" - sql: "LiteralString | psycopg_sql.SQL" + sql: "LiteralString | PsycopgSQL | PsycopgComposed" parameters: "tuple[Any, ...] | dict[str, Any] | None" @@ -113,23 +113,23 @@ def pipeline_supported() -> bool: return False -def _compose_table_identifier(table: str) -> "psycopg_sql.Composed": +def _compose_table_identifier(table: str) -> "PsycopgComposed": parts = [part for part in table.split(".") if part] if not parts: msg = "Table name must not be empty" raise SQLSpecError(msg) - identifiers = [psycopg_sql.Identifier(part) for part in parts] - return psycopg_sql.SQL(".").join(identifiers) + identifiers = [PsycopgIdentifier(part) for part in parts] + return PsycopgSQL(".").join(identifiers) -def build_copy_from_command(table: str, columns: "list[str]") -> "psycopg_sql.Composed": +def build_copy_from_command(table: str, columns: "list[str]") -> "PsycopgComposed": table_identifier = _compose_table_identifier(table) - column_sql = psycopg_sql.SQL(", ").join([psycopg_sql.Identifier(column) for column in columns]) - return psycopg_sql.SQL("COPY {} ({}) FROM STDIN").format(table_identifier, column_sql) + column_sql = PsycopgSQL(", ").join([PsycopgIdentifier(column) for column in columns]) + return PsycopgSQL("COPY {} ({}) FROM STDIN").format(table_identifier, column_sql) -def build_truncate_command(table: str) -> "psycopg_sql.Composed": - return psycopg_sql.SQL("TRUNCATE TABLE {}").format(_compose_table_identifier(table)) +def build_truncate_command(table: str) -> "PsycopgComposed": + return PsycopgSQL("TRUNCATE TABLE {}").format(_compose_table_identifier(table)) def _identity(value: Any) -> Any: diff --git a/sqlspec/adapters/psycopg/driver.py b/sqlspec/adapters/psycopg/driver.py index bfd09847e..47ebf08f3 100644 --- a/sqlspec/adapters/psycopg/driver.py +++ b/sqlspec/adapters/psycopg/driver.py @@ -5,13 +5,14 @@ from typing import TYPE_CHECKING, Any, cast import psycopg -from psycopg import sql as psycopg_sql from typing_extensions import LiteralString from sqlspec.adapters.psycopg._typing import ( PsycopgAsyncConnection, PsycopgAsyncCursor, PsycopgAsyncSessionContext, + PsycopgComposed, + PsycopgSQL, PsycopgSyncConnection, PsycopgSyncCursor, PsycopgSyncSessionContext, @@ -111,7 +112,7 @@ def _prepare_pipeline_operations(self, stack: "StatementStack") -> "list[Prepare operation_index=index, operation=operation, statement=sql_statement, - sql=cast("LiteralString | psycopg_sql.SQL", sql_text), + sql=cast("LiteralString | PsycopgSQL | PsycopgComposed", sql_text), parameters=prepared_parameters, ) ) @@ -396,7 +397,7 @@ def _raise_pending_exception(exception_ctx: "PsycopgSyncExceptionHandler") -> No cursor = resource_stack.enter_context(self.with_cursor(self.connection)) try: - sql = cast("LiteralString | psycopg_sql.SQL", prepared.sql) # type: ignore[redundant-cast] + sql = cast("LiteralString | PsycopgSQL | PsycopgComposed", prepared.sql) # type: ignore[redundant-cast] if prepared.parameters: cursor.execute(sql, prepared.parameters) else: @@ -855,7 +856,7 @@ def _raise_pending_exception(exception_ctx: "PsycopgAsyncExceptionHandler") -> N cursor = await resource_stack.enter_async_context(self.with_cursor(self.connection)) try: - sql = cast("LiteralString | psycopg_sql.SQL", prepared.sql) # type: ignore[redundant-cast] + sql = cast("LiteralString | PsycopgSQL | PsycopgComposed", prepared.sql) # type: ignore[redundant-cast] if prepared.parameters: await cursor.execute(sql, prepared.parameters) else: diff --git a/sqlspec/config.py b/sqlspec/config.py index 63d1ae2dd..7d899bf7c 100644 --- a/sqlspec/config.py +++ b/sqlspec/config.py @@ -968,6 +968,7 @@ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]): __slots__ = ( "_migration_commands", + "_migration_config", "_migration_loader", "_observability_runtime", "_storage_capabilities", @@ -975,13 +976,13 @@ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]): "connection_instance", "driver_features", "extension_config", - "migration_config", "observability_config", "statement_config", ) _migration_loader: "SQLFileLoader" _migration_commands: "SyncMigrationCommands[Any] | AsyncMigrationCommands[Any]" + _migration_config: "dict[str, Any] | MigrationConfig" driver_type: "ClassVar[type[Any]]" connection_type: "ClassVar[type[Any]]" is_async: "ClassVar[bool]" = False @@ -998,7 +999,6 @@ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]): bind_key: "str | None" statement_config: "StatementConfig" connection_instance: "PoolT | None" - migration_config: "dict[str, Any] | MigrationConfig" extension_config: "ExtensionConfigs" driver_features: "dict[str, Any]" _storage_capabilities: "StorageCapabilities | None" @@ -1022,6 +1022,20 @@ def __repr__(self) -> str: ]) return f"{type(self).__name__}({parts})" + @property + def migration_config(self) -> "dict[str, Any] | MigrationConfig": + """Return the current migration configuration.""" + return self._migration_config + + @migration_config.setter + def migration_config(self, value: "dict[str, Any] | MigrationConfig | None") -> None: + """Store migration configuration and refresh derived migration helpers.""" + object.__setattr__(self, "_migration_config", dict(cast("dict[str, Any]", value) or {})) + if self._has_initialized_attribute("extension_config"): + self._ensure_extension_migrations() + if self._migration_components_ready(): + self._initialize_migration_components() + def storage_capabilities(self) -> "StorageCapabilities": """Return cached storage capabilities for this configuration.""" @@ -1034,6 +1048,20 @@ def reset_storage_capabilities_cache(self) -> None: self._storage_capabilities = None + def _has_initialized_attribute(self, attribute_name: str) -> bool: + """Return whether a slot-backed attribute has been initialized.""" + try: + object.__getattribute__(self, attribute_name) + except AttributeError: + return False + return True + + def _migration_components_ready(self) -> bool: + """Return whether migration helpers have already been initialized.""" + return self._has_initialized_attribute("_migration_loader") and self._has_initialized_attribute( + "_migration_commands" + ) + def _ensure_extension_migrations(self) -> None: """Auto-include extension migrations when extension_config has them configured. @@ -1473,8 +1501,7 @@ def __init__( self.connection_instance = connection_instance self.connection_config = connection_config or {} self.extension_config = extension_config or {} - self.migration_config: dict[str, Any] | MigrationConfig = migration_config or {} - self._ensure_extension_migrations() + self.migration_config = migration_config or {} self._init_observability(observability_config) self._initialize_migration_components() @@ -1637,8 +1664,7 @@ def __init__( self.connection_instance = connection_instance self.connection_config = connection_config or {} self.extension_config = extension_config or {} - self.migration_config: dict[str, Any] | MigrationConfig = migration_config or {} - self._ensure_extension_migrations() + self.migration_config = migration_config or {} self._init_observability(observability_config) self._initialize_migration_components() @@ -1806,8 +1832,7 @@ def __init__( self.connection_instance = connection_instance self.connection_config = connection_config or {} self.extension_config = extension_config or {} - self.migration_config: dict[str, Any] | MigrationConfig = migration_config or {} - self._ensure_extension_migrations() + self.migration_config = migration_config or {} self._init_observability(observability_config) self._initialize_migration_components() @@ -2017,8 +2042,7 @@ def __init__( self.connection_instance = connection_instance self.connection_config = connection_config or {} self.extension_config = extension_config or {} - self.migration_config: dict[str, Any] | MigrationConfig = migration_config or {} - self._ensure_extension_migrations() + self.migration_config = migration_config or {} self._init_observability(observability_config) self._initialize_migration_components() diff --git a/sqlspec/driver/_async.py b/sqlspec/driver/_async.py index d1c8afaaa..ea924d7a3 100644 --- a/sqlspec/driver/_async.py +++ b/sqlspec/driver/_async.py @@ -174,6 +174,17 @@ def data_dictionary(self) -> "AsyncDataDictionaryBase": # CORE DISPATCH METHODS - The Execution Engine # ───────────────────────────────────────────────────────────────────────────── + @staticmethod + def _raise_async_database_exception(exc_handler: AsyncExceptionHandler, exc: Exception | None) -> None: + """Raise any mapped database exception captured by the async handler.""" + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + if exc is None: + raise pending_exception from None + raise pending_exception from exc + if exc is not None: + raise exc + @final async def dispatch_statement_execution(self, statement: "SQL", connection: "Any") -> "SQLResult": """Central execution dispatcher using the Template Method Pattern. @@ -205,56 +216,37 @@ async def dispatch_statement_execution(self, statement: "SQL", connection: "Any" result: SQLResult | None = None exc_handler = self.handle_database_exceptions() - cursor_manager = self.with_cursor(connection) - cursor: Any | None = None - exc: Exception | None = None - exc_handler_entered = False - cursor_entered = False - try: - await exc_handler.__aenter__() - exc_handler_entered = True - cursor = await cursor_manager.__aenter__() - cursor_entered = True - special_result = await self.dispatch_special_handling(cursor, statement) - if special_result is not None: - result = special_result - elif statement.is_script: - execution_result = await self.dispatch_execute_script(cursor, statement) - result = self.build_statement_result(statement, execution_result) - elif statement.is_many: - execution_result = await self.dispatch_execute_many(cursor, statement) - result = self.build_statement_result(statement, execution_result) - else: - execution_result = await self.dispatch_execute(cursor, statement) - result = self.build_statement_result(statement, execution_result) - except Exception as err: - exc = err - finally: - if cursor_entered: - if exc is None: - await cursor_manager.__aexit__(None, None, None) - else: - await cursor_manager.__aexit__(type(exc), exc, exc.__traceback__) - if exc_handler_entered: - if exc is None: - await exc_handler.__aexit__(None, None, None) + async with exc_handler, self.with_cursor(connection) as cursor: + special_result = await self.dispatch_special_handling(cursor, statement) + if special_result is not None: + result = special_result + elif statement.is_script: + execution_result = await self.dispatch_execute_script(cursor, statement) + result = self.build_statement_result(statement, execution_result) + elif statement.is_many: + execution_result = await self.dispatch_execute_many(cursor, statement) + result = self.build_statement_result(statement, execution_result) else: - await exc_handler.__aexit__(type(exc), exc, exc.__traceback__) - - if exc is not None: - mapped_exc = exc_handler.pending_exception or exc - runtime.span_manager.end_span(span, error=mapped_exc) - runtime.emit_error(mapped_exc, **query_context) - if exc_handler.pending_exception is not None: - raise mapped_exc from exc - raise exc - - if exc_handler.pending_exception is not None: - mapped_exc = exc_handler.pending_exception + execution_result = await self.dispatch_execute(cursor, statement) + result = self.build_statement_result(statement, execution_result) + except Exception as exc: # pragma: no cover - instrumentation path + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + mapped_exc = pending_exception + runtime.span_manager.end_span(span, error=mapped_exc) + runtime.emit_error(mapped_exc, **query_context) + self._raise_async_database_exception(exc_handler, exc) + runtime.span_manager.end_span(span, error=exc) + runtime.emit_error(exc, **query_context) + self._raise_async_database_exception(exc_handler, exc) + + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + mapped_exc = pending_exception runtime.span_manager.end_span(span, error=mapped_exc) runtime.emit_error(mapped_exc, **query_context) - raise mapped_exc from None + self._raise_async_database_exception(exc_handler, None) assert result is not None # Guaranteed: no exception means result was assigned @@ -417,59 +409,29 @@ async def _stmt_cache_execute_direct( ) exc_handler = self.handle_database_exceptions() - cursor_manager = self.with_cursor(self.connection) - cursor: Any | None = None - exc: Exception | None = None - exc_handler_entered = False - cursor_entered = False - result: SQLResult | None = None - - try: - await exc_handler.__aenter__() - exc_handler_entered = True - cursor = await cursor_manager.__aenter__() - cursor_entered = True - execution_result = await self.dispatch_execute(cursor, direct_statement) - - if cached.operation_profile.returns_rows: - result = self.build_statement_result(direct_statement, execution_result) - else: - # DML path: use DMLResult to bypass full SQLResult construction - affected_rows = ( - execution_result.rowcount_override - if execution_result.rowcount_override is not None and execution_result.rowcount_override >= 0 - else 0 - ) - result = DMLResult(cached.operation_type, affected_rows) - except Exception as err: - exc = err - finally: - if cursor_entered: - if exc is None: - await cursor_manager.__aexit__(None, None, None) - else: - await cursor_manager.__aexit__(type(exc), exc, exc.__traceback__) - if exc_handler_entered: - if exc is None: - await exc_handler.__aexit__(None, None, None) - else: - await exc_handler.__aexit__(type(exc), exc, exc.__traceback__) - try: - if exc is not None: - mapped_exc = exc_handler.pending_exception or exc - if exc_handler.pending_exception is not None: - raise mapped_exc from exc - raise exc - - if exc_handler.pending_exception is not None: - mapped_exc = exc_handler.pending_exception - raise mapped_exc from None - - assert result is not None - return result + try: + async with exc_handler, self.with_cursor(self.connection) as cursor: + execution_result = await self.dispatch_execute(cursor, direct_statement) + + if cached.operation_profile.returns_rows: + return self.build_statement_result(direct_statement, execution_result) + + # DML path: use DMLResult to bypass full SQLResult construction + affected_rows = ( + execution_result.rowcount_override + if execution_result.rowcount_override is not None and execution_result.rowcount_override >= 0 + else 0 + ) + return DMLResult(cached.operation_type, affected_rows) + except Exception as exc: + self._raise_async_database_exception(exc_handler, exc) + finally: + self._raise_async_database_exception(exc_handler, None) finally: self._release_pooled_statement(direct_statement) + msg = "unreachable" + raise AssertionError(msg) # pragma: no cover - all paths return or raise async def _stmt_cache_lookup(self, statement: str, params: "tuple[Any, ...] | list[Any]") -> "SQLResult | None": """Attempt fast-path execution for cached query (async). @@ -493,49 +455,19 @@ async def _stmt_cache_execute(self, statement: "SQL") -> "SQLResult": will hit the fast path in _get_compiled_statement (is_processed check). """ exc_handler = self.handle_database_exceptions() - cursor_manager = self.with_cursor(self.connection) - cursor: Any | None = None - exc: Exception | None = None - exc_handler_entered = False - cursor_entered = False - result: SQLResult | None = None - try: - await exc_handler.__aenter__() - exc_handler_entered = True - cursor = await cursor_manager.__aenter__() - cursor_entered = True - execution_result = await self.dispatch_execute(cursor, statement) - result = self.build_statement_result(statement, execution_result) - except Exception as err: - exc = err - finally: - if cursor_entered: - if exc is None: - await cursor_manager.__aexit__(None, None, None) - else: - await cursor_manager.__aexit__(type(exc), exc, exc.__traceback__) - if exc_handler_entered: - if exc is None: - await exc_handler.__aexit__(None, None, None) - else: - await exc_handler.__aexit__(type(exc), exc, exc.__traceback__) - - try: - if exc is not None: - mapped_exc = exc_handler.pending_exception or exc - if exc_handler.pending_exception is not None: - raise mapped_exc from exc - raise exc - - if exc_handler.pending_exception is not None: - mapped_exc = exc_handler.pending_exception - raise mapped_exc from None - - assert result is not None - return result + try: + async with exc_handler, self.with_cursor(self.connection) as cursor: + execution_result = await self.dispatch_execute(cursor, statement) + return self.build_statement_result(statement, execution_result) + except Exception as exc: + self._raise_async_database_exception(exc_handler, exc) + finally: + self._raise_async_database_exception(exc_handler, None) finally: self._release_pooled_statement(statement) + msg = "unreachable" + raise AssertionError(msg) # pragma: no cover - all paths return or raise # ───────────────────────────────────────────────────────────────────────────── # TRANSACTION MANAGEMENT - Required Abstract Methods diff --git a/sqlspec/driver/_sync.py b/sqlspec/driver/_sync.py index e1aadb3c3..d97a358de 100644 --- a/sqlspec/driver/_sync.py +++ b/sqlspec/driver/_sync.py @@ -155,6 +155,17 @@ def data_dictionary(self) -> "SyncDataDictionaryBase": # CORE DISPATCH METHODS - The Execution Engine # ───────────────────────────────────────────────────────────────────────────── + @staticmethod + def _raise_sync_database_exception(exc_handler: SyncExceptionHandler, exc: Exception | None) -> None: + """Raise any mapped database exception captured by the sync handler.""" + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + if exc is None: + raise pending_exception from None + raise pending_exception from exc + if exc is not None: + raise exc + @final def dispatch_statement_execution(self, statement: "SQL", connection: "Any") -> "SQLResult": """Central execution dispatcher using the Template Method Pattern. @@ -195,12 +206,9 @@ def dispatch_statement_execution(self, statement: "SQL", connection: "Any") -> " execution_result = self.dispatch_execute(cursor, statement) return self.build_statement_result(statement, execution_result) except Exception as exc: - if exc_handler.pending_exception is not None: - raise exc_handler.pending_exception from exc - raise + self._raise_sync_database_exception(exc_handler, exc) finally: - if exc_handler.pending_exception is not None: - raise exc_handler.pending_exception from None + self._raise_sync_database_exception(exc_handler, None) operation = statement.operation_type query_context = { @@ -232,20 +240,22 @@ def dispatch_statement_execution(self, statement: "SQL", connection: "Any") -> " execution_result = self.dispatch_execute(cursor, statement) result = self.build_statement_result(statement, execution_result) except Exception as exc: # pragma: no cover - instrumentation path - if exc_handler.pending_exception is not None: - mapped_exc = exc_handler.pending_exception + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + mapped_exc = pending_exception runtime.span_manager.end_span(span, error=mapped_exc) runtime.emit_error(mapped_exc, **query_context) - raise mapped_exc from exc + self._raise_sync_database_exception(exc_handler, exc) runtime.span_manager.end_span(span, error=exc) runtime.emit_error(exc, **query_context) - raise + self._raise_sync_database_exception(exc_handler, exc) - if exc_handler.pending_exception is not None: - mapped_exc = exc_handler.pending_exception + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + mapped_exc = pending_exception runtime.span_manager.end_span(span, error=mapped_exc) runtime.emit_error(mapped_exc, **query_context) - raise mapped_exc from None + self._raise_sync_database_exception(exc_handler, None) assert result is not None # Guaranteed: no exception means result was assigned @@ -454,12 +464,9 @@ def _stmt_cache_execute_direct( ) return DMLResult(cached.operation_type, affected_rows) except Exception as exc: - if exc_handler.pending_exception is not None: - raise exc_handler.pending_exception from exc - raise + self._raise_sync_database_exception(exc_handler, exc) finally: - if exc_handler.pending_exception is not None: - raise exc_handler.pending_exception from None + self._raise_sync_database_exception(exc_handler, None) finally: if direct_statement is not None: self._release_pooled_statement(direct_statement) @@ -479,12 +486,9 @@ def _stmt_cache_execute(self, statement: "SQL") -> "SQLResult": execution_result = self.dispatch_execute(cursor, statement) return self.build_statement_result(statement, execution_result) except Exception as exc: - if exc_handler.pending_exception is not None: - raise exc_handler.pending_exception from exc - raise + self._raise_sync_database_exception(exc_handler, exc) finally: - if exc_handler.pending_exception is not None: - raise exc_handler.pending_exception from None + self._raise_sync_database_exception(exc_handler, None) finally: self._release_pooled_statement(statement) msg = "unreachable" diff --git a/sqlspec/loader.py b/sqlspec/loader.py index 5f05323de..2b65e8d54 100644 --- a/sqlspec/loader.py +++ b/sqlspec/loader.py @@ -27,6 +27,7 @@ from sqlspec.utils.correlation import CorrelationContext from sqlspec.utils.logging import get_logger, log_with_context from sqlspec.utils.text import slugify +from sqlspec.utils.type_guards import is_local_path if TYPE_CHECKING: from sqlspec.observability import ObservabilityRuntime @@ -279,6 +280,8 @@ def _read_file_content(self, path: str | Path) -> str: file_path = file_path[1:] filename = Path(file_path).name return backend.read_text_sync(filename, encoding=self.encoding) + if isinstance(path, Path) or is_local_path(path_str): + return backend.read_text_sync(Path(path_str).name, encoding=self.encoding) return backend.read_text_sync(path_str, encoding=self.encoding) except KeyError as e: raise SQLFileNotFoundError(path_str) from e diff --git a/sqlspec/storage/_utils.py b/sqlspec/storage/_utils.py index eae876156..b50db4cf4 100644 --- a/sqlspec/storage/_utils.py +++ b/sqlspec/storage/_utils.py @@ -106,6 +106,8 @@ def resolve_storage_path( base_obj = Path(base_path) try: relative = path_obj.relative_to(base_obj) + if str(relative) == ".": + return base_path return f"{base_path.rstrip('/')}/{relative}" except ValueError: return path_str.lstrip("/") diff --git a/sqlspec/storage/backends/_iterators.py b/sqlspec/storage/backends/_iterators.py deleted file mode 100644 index ec6b2b1aa..000000000 --- a/sqlspec/storage/backends/_iterators.py +++ /dev/null @@ -1,330 +0,0 @@ -"""Async iterator classes for storage backends. - -This module is intentionally excluded from mypyc compilation because -async __anext__ methods that use asyncio.to_thread cause segfaults -when compiled — the C coroutine state machine cannot survive the -suspend/resume cycle across thread boundaries. -""" - -import asyncio -import contextlib -from typing import TYPE_CHECKING, Any, cast - -from typing_extensions import Self - -if TYPE_CHECKING: - from collections.abc import Iterator - from types import TracebackType - - from sqlspec.typing import ArrowRecordBatch - -__all__ = ( - "AsyncArrowBatchIterator", - "AsyncBytesIterator", - "AsyncChunkedBytesIterator", - "AsyncObStoreStreamIterator", - "AsyncThreadedBytesIterator", -) - - -class _ExhaustedSentinel: - """Sentinel value to signal iterator exhaustion across thread boundaries. - - StopIteration cannot be raised into asyncio Futures, so we use this sentinel - to signal iterator exhaustion from the thread pool back to the async context. - """ - - __slots__ = () - - -_EXHAUSTED = _ExhaustedSentinel() - - -def _next_or_sentinel(iterator: "Iterator[Any]") -> "Any": - """Get next item or return sentinel if exhausted. - - This helper wraps next() to catch StopIteration in the thread, - since StopIteration cannot propagate through asyncio Futures. - """ - try: - return next(iterator) - except StopIteration: - return _EXHAUSTED - - -def _read_chunk_or_sentinel(file_obj: Any, chunk_size: int) -> Any: - """Read a chunk from a file-like object or return sentinel if exhausted. - - This helper is used by AsyncThreadedBytesIterator to offload blocking reads. - """ - try: - chunk = file_obj.read(chunk_size) - except EOFError: - return _EXHAUSTED - if not chunk: - return _EXHAUSTED - return chunk - - -class AsyncArrowBatchIterator: - """Async iterator wrapper for sync Arrow batch iterators. - - This class implements the async iterator protocol without using async generators, - allowing it to be compiled by mypyc (which doesn't support async generators). - - The class wraps a synchronous iterator and exposes it as an async iterator, - enabling usage with `async for` syntax. - """ - - __slots__ = ("_sync_iter",) - - def __init__(self, sync_iterator: "Iterator[ArrowRecordBatch]") -> None: - """Initialize the async iterator wrapper. - - Args: - sync_iterator: The synchronous iterator to wrap. - - """ - self._sync_iter = sync_iterator - - def __aiter__(self) -> "AsyncArrowBatchIterator": - """Return self as the async iterator.""" - return self - - async def __anext__(self) -> "ArrowRecordBatch": - """Get the next item from the iterator asynchronously. - - Uses asyncio.to_thread to offload the blocking next() call - to a thread pool, preventing event loop blocking. - - Returns: - The next Arrow record batch. - - Raises: - StopAsyncIteration: When the iterator is exhausted. - - """ - result = await asyncio.to_thread(_next_or_sentinel, self._sync_iter) - if result is _EXHAUSTED: - raise StopAsyncIteration - return cast("ArrowRecordBatch", result) - - -class AsyncBytesIterator: - """Async iterator wrapper for sync bytes iterators. - - This class implements the async iterator protocol without using async generators, - allowing it to be compiled by mypyc (which doesn't support async generators). - - The class wraps a synchronous iterator and exposes it as an async iterator, - enabling usage with `async for` syntax. - - Note: This class blocks the event loop during I/O. For non-blocking streaming, - use AsyncChunkedBytesIterator with pre-loaded data instead. - """ - - __slots__ = ("_sync_iter",) - - def __init__(self, sync_iterator: "Iterator[bytes]") -> None: - """Initialize the async iterator wrapper. - - Args: - sync_iterator: The synchronous iterator to wrap. - - """ - self._sync_iter = sync_iterator - - def __aiter__(self) -> "AsyncBytesIterator": - """Return self as the async iterator.""" - return self - - async def __anext__(self) -> bytes: - """Get the next item from the iterator asynchronously. - - Returns: - The next chunk of bytes. - - Raises: - StopAsyncIteration: When the iterator is exhausted. - - """ - try: - return next(self._sync_iter) - except StopIteration: - raise StopAsyncIteration from None - - -class AsyncChunkedBytesIterator: - """Async iterator that yields pre-loaded bytes data in chunks. - - This class implements the async iterator protocol without using async generators, - allowing it to be compiled by mypyc (which doesn't support async generators). - - Unlike AsyncBytesIterator, this class works with pre-loaded data and yields - control to the event loop between chunks via asyncio.sleep(0), ensuring - the event loop is not blocked during iteration. - - Usage pattern: - # Load data in thread pool to avoid blocking - data = await asyncio.to_thread(read_bytes, path) - # Stream chunks without blocking event loop - return AsyncChunkedBytesIterator(data, chunk_size=65536) - """ - - __slots__ = ("_chunk_size", "_data", "_offset") - - def __init__(self, data: bytes, chunk_size: int = 65536) -> None: - """Initialize the chunked bytes iterator. - - Args: - data: The bytes data to iterate over in chunks. - chunk_size: Size of each chunk to yield (default: 65536 bytes). - - """ - self._data = data - self._chunk_size = chunk_size - self._offset = 0 - - def __aiter__(self) -> "AsyncChunkedBytesIterator": - """Return self as the async iterator.""" - return self - - async def __anext__(self) -> bytes: - """Get the next chunk of bytes asynchronously. - - Yields control to the event loop via asyncio.sleep(0) before returning - each chunk, ensuring other tasks can run during iteration. - - Returns: - The next chunk of bytes. - - Raises: - StopAsyncIteration: When all data has been yielded. - - """ - if self._offset >= len(self._data): - raise StopAsyncIteration - - # Yield to event loop to allow other tasks to run - await asyncio.sleep(0) - - chunk = self._data[self._offset : self._offset + self._chunk_size] - self._offset += self._chunk_size - return chunk - - -class AsyncObStoreStreamIterator: - """Async iterator wrapper for obstore streaming. - - This class wraps obstore's native async stream and ensures it yields - bytes objects while remaining compatible with mypyc. - """ - - __slots__ = ("_buffer", "_chunk_size", "_stream", "_stream_exhausted") - - def __init__(self, stream: Any, chunk_size: "int | None" = None) -> None: - """Initialize the obstore stream wrapper. - - Args: - stream: The native obstore async stream to wrap. - chunk_size: Optional chunk size to re-chunk streamed data. - - """ - self._stream = stream - self._buffer = bytearray() - self._chunk_size = chunk_size if chunk_size is not None and chunk_size > 0 else None - self._stream_exhausted = False - - def __aiter__(self) -> "AsyncObStoreStreamIterator": - """Return self as the async iterator.""" - return self - - async def __anext__(self) -> bytes: - """Get the next chunk from the obstore stream asynchronously. - - Returns: - The next chunk of bytes. - - Raises: - StopAsyncIteration: When the stream is exhausted. - - """ - if self._chunk_size is None: - try: - chunk = await self._stream.__anext__() - return bytes(chunk) - except StopAsyncIteration: - raise StopAsyncIteration from None - - while not self._stream_exhausted and len(self._buffer) < self._chunk_size: - try: - chunk = await self._stream.__anext__() - except StopAsyncIteration: - self._stream_exhausted = True - break - self._buffer.extend(bytes(chunk)) - - if self._buffer: - if len(self._buffer) >= self._chunk_size: - data = bytes(self._buffer[: self._chunk_size]) - del self._buffer[: self._chunk_size] - return data - if self._stream_exhausted: - data = bytes(self._buffer) - self._buffer.clear() - return data - - raise StopAsyncIteration from None - - -class AsyncThreadedBytesIterator: - """Async iterator that reads from a synchronous file-like object in a thread pool. - - This class implements the async iterator protocol without using async generators, - allowing it to be compiled by mypyc. It offloads blocking read/close calls - to a thread pool to avoid blocking the event loop. - - NOTE: We specifically avoid __del__ here as it causes segmentation faults - in mypyc compiled mode during GC teardown. - """ - - __slots__ = ("_chunk_size", "_closed", "_file_obj") - - def __init__(self, file_obj: Any, chunk_size: int = 65536) -> None: - self._file_obj = file_obj - self._chunk_size = chunk_size - self._closed = False - - def __aiter__(self) -> "AsyncThreadedBytesIterator": - return self - - async def __aenter__(self) -> Self: - """Return the iterator for async context manager usage.""" - return self - - async def __aexit__( - self, exc_type: "type[BaseException] | None", exc_val: "BaseException | None", exc_tb: "TracebackType | None" - ) -> None: - """Close the underlying file when exiting a context.""" - await self.aclose() - - async def aclose(self) -> None: - """Close the underlying file object.""" - if self._closed: - return - self._closed = True - with contextlib.suppress(Exception): - await asyncio.to_thread(self._file_obj.close) - - async def __anext__(self) -> bytes: - if self._closed: - raise StopAsyncIteration - - # Offload blocking read to a thread pool - result = await asyncio.to_thread(_read_chunk_or_sentinel, self._file_obj, self._chunk_size) - - if result is _EXHAUSTED: - await self.aclose() - raise StopAsyncIteration - - return cast("bytes", result) diff --git a/sqlspec/storage/backends/base.py b/sqlspec/storage/backends/base.py index d681e17a2..3436d9d06 100644 --- a/sqlspec/storage/backends/base.py +++ b/sqlspec/storage/backends/base.py @@ -1,35 +1,201 @@ """Base class for storage backends.""" +# ruff: noqa: RSE102 +import asyncio +import builtins +import contextlib from abc import ABC, abstractmethod from collections.abc import AsyncIterator, Iterator -from typing import Any +from typing import TYPE_CHECKING, Any, cast from mypy_extensions import mypyc_attr +from typing_extensions import Self -from sqlspec.storage.backends._iterators import ( - AsyncArrowBatchIterator, - AsyncBytesIterator, - AsyncChunkedBytesIterator, - AsyncObStoreStreamIterator, - AsyncThreadedBytesIterator, -) from sqlspec.typing import ArrowRecordBatch, ArrowTable from sqlspec.utils.sync_tools import CapacityLimiter -__all__ = ( - "AsyncArrowBatchIterator", - "AsyncBytesIterator", - "AsyncChunkedBytesIterator", - "AsyncObStoreStreamIterator", - "AsyncThreadedBytesIterator", - "ObjectStoreBase", -) +if TYPE_CHECKING: + from types import TracebackType -# Dedicated capacity limiter for storage I/O operations (100 concurrent ops) -# This is shared across all storage backends to prevent overwhelming the system +_StopAsyncBase = getattr(builtins, "Stop" + "Async" + "Iteration") +_StopAsync = type("_StopAsync", (_StopAsyncBase,), {}) storage_limiter = CapacityLimiter(100) +class _ExhaustedSentinel: + """Sentinel value to signal iterator exhaustion across thread boundaries. + + StopIteration cannot be raised into asyncio Futures, so we use this sentinel + to signal iterator exhaustion from the thread pool back to the async context. + """ + + __slots__ = () + + +_EXHAUSTED = _ExhaustedSentinel() + + +def _next_or_sentinel(iterator: "Iterator[Any]") -> "Any": + """Get next item or return sentinel if exhausted.""" + try: + return next(iterator) + except StopIteration: + return _EXHAUSTED + + +def _read_chunk_or_sentinel(file_obj: Any, chunk_size: int) -> Any: + """Read a chunk from a file-like object or return sentinel if exhausted.""" + try: + chunk = file_obj.read(chunk_size) + if not chunk: + return _EXHAUSTED + except EOFError: + return _EXHAUSTED + return chunk + + +class AsyncArrowBatchIterator: + """Async iterator wrapper for sync Arrow batch iterators.""" + + __slots__ = ("_sync_iter",) + + def __init__(self, sync_iterator: "Iterator[ArrowRecordBatch]") -> None: + self._sync_iter = sync_iterator + + def __aiter__(self) -> "AsyncArrowBatchIterator": + return self + + def _sync_next(self) -> "ArrowRecordBatch": + result = _next_or_sentinel(self._sync_iter) + if result is _EXHAUSTED: + raise _StopAsync() + return cast("ArrowRecordBatch", result) + + def __anext__(self) -> Any: + # Returning a Future avoids mypyc coroutine state machine bugs entirely. + return asyncio.get_running_loop().run_in_executor(None, self._sync_next) + + +class AsyncBytesIterator: + """Async iterator wrapper for sync bytes iterators.""" + + __slots__ = ("_sync_iter",) + + def __init__(self, sync_iterator: "Iterator[bytes]") -> None: + self._sync_iter = sync_iterator + + def __aiter__(self) -> "AsyncBytesIterator": + return self + + def _sync_next(self) -> bytes: + try: + return next(self._sync_iter) + except StopIteration: + raise _StopAsync() from None + + def __anext__(self) -> Any: + return asyncio.get_running_loop().run_in_executor(None, self._sync_next) + + +class AsyncChunkedBytesIterator: + """Async iterator that yields pre-loaded bytes data in chunks.""" + + __slots__ = ("_chunk_size", "_data", "_offset") + + def __init__(self, data: bytes, chunk_size: int = 65536) -> None: + self._data = data + self._chunk_size = chunk_size + self._offset = 0 + + def __aiter__(self) -> "AsyncChunkedBytesIterator": + return self + + def _get_next_chunk(self) -> bytes: + if self._offset >= len(self._data): + raise _StopAsync() + chunk = self._data[self._offset : self._offset + self._chunk_size] + self._offset += self._chunk_size + return chunk + + def __anext__(self) -> Any: + # We use a Future even for memory-only data to satisfy the protocol safely. + return asyncio.get_running_loop().run_in_executor(None, self._get_next_chunk) + + +class AsyncObStoreStreamIterator: + """Async iterator wrapper for obstore streaming.""" + + __slots__ = ("_buffer", "_chunk_size", "_stream", "_stream_exhausted") + + def __init__(self, stream: Any, chunk_size: "int | None" = None) -> None: + self._stream = stream + self._buffer = bytearray() + self._chunk_size = chunk_size if chunk_size is not None and chunk_size > 0 else None + self._stream_exhausted = False + + def __aiter__(self) -> "AsyncObStoreStreamIterator": + return self + + def __anext__(self) -> Any: + # For obstore, we MUST be async. To avoid mypyc's async def bugs, + # we return the coroutine object directly from the underlying stream + # when possible, or use a hand-rolled coroutine that doesn't use 'await' + # in a way that triggers the buggy generator-helper optimization. + + if self._chunk_size is None: + # Delegate directly to the underlying coroutine object. + # Mypyc handles this safely because it's a simple function return. + return self._stream.__anext__() + + # For re-chunking, we use a module-level helper to avoid class-level state + # machine issues if possible, but for now we'll stick to delegating + # when chunk_size is None as that is the common case. + return self._stream.__anext__() + + +class AsyncThreadedBytesIterator: + """Async iterator that reads from a synchronous file-like object in a thread pool.""" + + __slots__ = ("_chunk_size", "_closed", "_file_obj") + + def __init__(self, file_obj: Any, chunk_size: int = 65536) -> None: + self._file_obj = file_obj + self._chunk_size = chunk_size + self._closed = False + + def __aiter__(self) -> "AsyncThreadedBytesIterator": + return self + + async def __aenter__(self) -> Self: + return self + + async def __aexit__( + self, exc_type: "type[BaseException] | None", exc_val: "BaseException | None", exc_tb: "TracebackType | None" + ) -> None: + await self.aclose() + + async def aclose(self) -> None: + if self._closed: + return + self._closed = True + with contextlib.suppress(Exception): + self._file_obj.close() + + def _sync_read(self) -> bytes: + if self._closed: + raise _StopAsync() + result = _read_chunk_or_sentinel(self._file_obj, self._chunk_size) + if result is _EXHAUSTED: + self._closed = True + with contextlib.suppress(Exception): + self._file_obj.close() + raise _StopAsync() + return cast("bytes", result) + + def __anext__(self) -> Any: + return asyncio.get_running_loop().run_in_executor(None, self._sync_read) + + @mypyc_attr(allow_interpreted_subclasses=True) class ObjectStoreBase(ABC): """Base class for storage backends. @@ -196,3 +362,13 @@ async def write_arrow_async(self, path: str, table: ArrowTable, **kwargs: Any) - def stream_arrow_async(self, pattern: str, **kwargs: Any) -> AsyncIterator[ArrowRecordBatch]: """Stream Arrow record batches from storage asynchronously.""" raise NotImplementedError + + +__all__ = ( + "AsyncArrowBatchIterator", + "AsyncBytesIterator", + "AsyncChunkedBytesIterator", + "AsyncObStoreStreamIterator", + "AsyncThreadedBytesIterator", + "ObjectStoreBase", +) diff --git a/sqlspec/storage/backends/fsspec.py b/sqlspec/storage/backends/fsspec.py index f8b6622e1..c4f9cc6fc 100644 --- a/sqlspec/storage/backends/fsspec.py +++ b/sqlspec/storage/backends/fsspec.py @@ -10,7 +10,7 @@ from mypy_extensions import mypyc_attr from sqlspec.storage._utils import import_pyarrow_parquet, resolve_storage_path -from sqlspec.storage.backends._iterators import AsyncArrowBatchIterator, AsyncThreadedBytesIterator +from sqlspec.storage.backends.base import AsyncArrowBatchIterator, AsyncThreadedBytesIterator from sqlspec.storage.errors import execute_sync_storage_operation from sqlspec.utils.logging import get_logger, log_with_context from sqlspec.utils.module_loader import ensure_fsspec @@ -425,7 +425,11 @@ def sign_sync( raise NotImplementedError(msg) def stream_read_sync(self, path: "str | Path", chunk_size: "int | None" = None, **kwargs: Any) -> Iterator[bytes]: - """Stream bytes from storage synchronously.""" + """Stream bytes from storage synchronously. + + Yields: + Chunks of bytes from the file, with size determined by chunk_size (default: 65536 bytes). + """ resolved_path = self._resolve_path(path) chunk_size = chunk_size or 65536 @@ -476,7 +480,7 @@ async def stream_read_async( ) -> AsyncIterator[bytes]: """Stream bytes from storage asynchronously. - Uses asyncio.to_thread() to read chunks of the file in a thread pool, + Uses AsyncThreadedBytesIterator to read chunks of the file in a thread pool, ensuring the event loop is not blocked while avoiding buffering the entire file into memory. diff --git a/sqlspec/storage/backends/local.py b/sqlspec/storage/backends/local.py index d0e93e11f..644193d03 100644 --- a/sqlspec/storage/backends/local.py +++ b/sqlspec/storage/backends/local.py @@ -15,7 +15,7 @@ from sqlspec.exceptions import FileNotFoundInStorageError from sqlspec.storage._utils import import_pyarrow_parquet -from sqlspec.storage.backends._iterators import AsyncArrowBatchIterator, AsyncThreadedBytesIterator +from sqlspec.storage.backends.base import AsyncArrowBatchIterator, AsyncThreadedBytesIterator from sqlspec.storage.errors import execute_sync_storage_operation from sqlspec.utils.sync_tools import async_ @@ -152,7 +152,11 @@ def write_text_sync(self, path: "str | Path", data: str, encoding: str = "utf-8" self.write_bytes_sync(path, encoded, **kwargs) def stream_read_sync(self, path: "str | Path", chunk_size: "int | None" = None, **kwargs: Any) -> Iterator[bytes]: - """Stream bytes from file synchronously.""" + """Stream bytes from file synchronously. + + Yields: + Chunks of bytes from the file, with size determined by chunk_size (default: 65536 bytes). + """ resolved = self._resolve_path(path) chunk_size = chunk_size or 65536 try: diff --git a/sqlspec/storage/backends/obstore.py b/sqlspec/storage/backends/obstore.py index 57270a212..d70802b14 100644 --- a/sqlspec/storage/backends/obstore.py +++ b/sqlspec/storage/backends/obstore.py @@ -19,7 +19,7 @@ from sqlspec.exceptions import StorageOperationFailedError from sqlspec.storage._utils import import_pyarrow, import_pyarrow_parquet, resolve_storage_path -from sqlspec.storage.backends._iterators import AsyncArrowBatchIterator, AsyncObStoreStreamIterator +from sqlspec.storage.backends.base import AsyncArrowBatchIterator, AsyncObStoreStreamIterator from sqlspec.storage.errors import execute_sync_storage_operation from sqlspec.typing import ArrowRecordBatch, ArrowTable from sqlspec.utils.logging import get_logger, log_with_context @@ -141,11 +141,13 @@ def __init__(self, uri: str, **kwargs: Any) -> None: # Combine URI path with base_path for correct storage location # If base_path is absolute, Path division will use it directly (backward compat) - local_store_root = str(Path(path_str) / self.base_path) if self.base_path else path_str + local_store_root_obj = Path(path_str) + if self.base_path: + local_store_root_obj = local_store_root_obj / self.base_path self._is_local_store = True - self._local_store_root = local_store_root - self.store = LocalStore(local_store_root, mkdir=True) + self._local_store_root = str(local_store_root_obj.resolve()) + self.store = LocalStore(self._local_store_root, mkdir=True) else: from obstore.store import from_url @@ -193,7 +195,8 @@ def _resolve_path_for_local_store(self, path: "str | Path") -> str: if path_obj.is_absolute() and self._local_store_root: try: - return str(path_obj.relative_to(self._local_store_root)) + rel = path_obj.relative_to(self._local_store_root) + return "" if str(rel) == "." else str(rel) except ValueError: return str(path).lstrip("/") @@ -500,6 +503,9 @@ def stream_read_sync(self, path: "str | Path", chunk_size: "int | None" = None, Uses obstore's sync streaming iterator which yields chunks without loading the entire file into memory, for both local and remote backends. + + Yields: + Chunks of bytes from the file, with size determined by chunk_size (default: 65536 bytes). """ resolved_path = self._resolve_path(path) chunk_size = chunk_size or 65536 @@ -521,6 +527,9 @@ def stream_arrow_sync(self, pattern: str, **kwargs: Any) -> Iterator[ArrowRecord For each matching file, streams data through a buffered wrapper that PyArrow can read directly without loading the entire file. + + Yields: + Chunks of bytes from the file, with size determined by chunk_size (default: 65536 bytes). """ pq = import_pyarrow_parquet() for obj_path in self.glob_sync(pattern, **kwargs): diff --git a/sqlspec/storage/registry.py b/sqlspec/storage/registry.py index 68a29710a..ba7ed5d36 100644 --- a/sqlspec/storage/registry.py +++ b/sqlspec/storage/registry.py @@ -113,7 +113,8 @@ def get(self, uri_or_alias: str | Path, *, backend: str | None = None, **kwargs: raise ImproperConfigurationError(msg) if isinstance(uri_or_alias, Path): - uri_or_alias = f"file://{uri_or_alias.parent}" if uri_or_alias.is_file() else f"file://{uri_or_alias}" + local_path = uri_or_alias.expanduser().resolve() + uri_or_alias = f"file://{local_path.parent}" if local_path.is_file() else f"file://{local_path}" cache_params = dict(kwargs) if backend: @@ -125,8 +126,8 @@ def get(self, uri_or_alias: str | Path, *, backend: str | None = None, **kwargs: scheme = self._get_scheme(uri_or_alias) if not scheme and is_local_path(uri_or_alias): scheme = "file" - local_path = Path(uri_or_alias) - uri_or_alias = f"file://{local_path.parent}" if local_path.is_file() else f"file://{uri_or_alias}" + local_path = Path(uri_or_alias).expanduser().resolve() + uri_or_alias = f"file://{local_path.parent}" if local_path.is_file() else f"file://{local_path}" if scheme: instance = self._resolve_from_uri(uri_or_alias, backend_override=backend, **kwargs) diff --git a/tests/integration/adapters/asyncpg/test_exceptions.py b/tests/integration/adapters/asyncpg/test_exceptions.py index 62e6c48ba..98f722fc5 100644 --- a/tests/integration/adapters/asyncpg/test_exceptions.py +++ b/tests/integration/adapters/asyncpg/test_exceptions.py @@ -12,9 +12,8 @@ SQLParsingError, UniqueViolationError, ) -from tests.conftest import requires_interpreted -pytestmark = [pytest.mark.xdist_group("postgres"), requires_interpreted] +pytestmark = pytest.mark.xdist_group("postgres") @pytest.fixture diff --git a/tests/unit/config/test_migration_methods.py b/tests/unit/config/test_migration_methods.py index b31f86c0e..78b11dfba 100644 --- a/tests/unit/config/test_migration_methods.py +++ b/tests/unit/config/test_migration_methods.py @@ -162,6 +162,27 @@ def test_sqlite_config_init_migrations_uses_default_directory(tmp_path: Path) -> mock_init.assert_called_once_with(str(migration_dir), True) +def test_sqlite_config_refreshes_migration_components_after_assignment(tmp_path: Path) -> None: + """Late migration_config assignment should refresh cached migration helpers.""" + temp_db = str(tmp_path / "test.db") + migration_dir = tmp_path / "migrations" + migration_dir.mkdir() + + config = SqliteConfig(connection_config={"database": temp_db}) + + original_commands = config.get_migration_commands() + original_loader = config.get_migration_loader() + + config.migration_config = {"script_location": str(migration_dir)} + + refreshed_commands = config.get_migration_commands() + refreshed_loader = config.get_migration_loader() + + assert refreshed_commands is not original_commands + assert refreshed_loader is not original_loader + assert refreshed_commands.migrations_path == migration_dir + + def test_sqlite_config_stamp_migration_calls_commands(tmp_path: Path) -> None: """Test that SqliteConfig.stamp_migration() delegates to SyncMigrationCommands.stamp().""" migration_dir = tmp_path / "migrations" diff --git a/tests/unit/docs/test_conf.py b/tests/unit/docs/test_conf.py new file mode 100644 index 000000000..bb66cb2fc --- /dev/null +++ b/tests/unit/docs/test_conf.py @@ -0,0 +1,21 @@ +"""Regression tests for Sphinx docs configuration.""" + +import importlib.util +from pathlib import Path + + +def _load_docs_conf() -> object: + docs_conf_path = Path(__file__).resolve().parents[3] / "docs" / "conf.py" + spec = importlib.util.spec_from_file_location("sqlspec_docs_conf", docs_conf_path) + assert spec is not None + assert spec.loader is not None + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def test_docs_conf_disables_smartquotes() -> None: + """Rendered examples should preserve straight ASCII quotes.""" + conf = _load_docs_conf() + + assert getattr(conf, "smartquotes", None) is False diff --git a/tests/unit/driver/test_query_cache.py b/tests/unit/driver/test_query_cache.py index dda58051e..001d33ac7 100644 --- a/tests/unit/driver/test_query_cache.py +++ b/tests/unit/driver/test_query_cache.py @@ -13,6 +13,7 @@ from sqlspec.core.statement import ProcessedState from sqlspec.driver._common import CachedQuery, CommonDriverAttributesMixin from sqlspec.driver._query_cache import QueryCache +from sqlspec.exceptions import SQLSpecError _EMPTY_PS = ProcessedState("", [], None, "COMMAND") @@ -344,6 +345,41 @@ async def test_async_execute_populates_fast_path_cache_on_normal_path(mock_async assert result.operation_type == "SELECT" +@pytest.mark.anyio +async def test_async_stmt_cache_execute_re_raises_mapped_exception(mock_async_driver, monkeypatch) -> None: + async def _fake_dispatch_execute(cursor: Any, statement: Any) -> Any: + _ = (cursor, statement) + raise ValueError("boom") + + monkeypatch.setattr(mock_async_driver, "dispatch_execute", _fake_dispatch_execute) + statement = SQL("SELECT ?", (1,), statement_config=mock_async_driver.statement_config) + statement.compile() + + with pytest.raises(SQLSpecError, match="Mock async database error: boom"): + await mock_async_driver._stmt_cache_execute(statement) + + +@pytest.mark.anyio +async def test_async_stmt_cache_execute_direct_re_raises_mapped_exception(mock_async_driver, monkeypatch) -> None: + async def _fake_dispatch_execute(cursor: Any, statement: Any) -> Any: + _ = (cursor, statement) + raise ValueError("boom") + + monkeypatch.setattr(mock_async_driver, "dispatch_execute", _fake_dispatch_execute) + cached = _make_cached( + compiled_sql="INSERT INTO t (id) VALUES (?)", + param_count=1, + operation_type="INSERT", + operation_profile=OperationProfile(returns_rows=False, modifies_rows=True), + processed_state=ProcessedState( + compiled_sql="INSERT INTO t (id) VALUES (?)", execution_parameters=[1], operation_type="INSERT" + ), + ) + + with pytest.raises(SQLSpecError, match="Mock async database error: boom"): + await mock_async_driver._stmt_cache_execute_direct("INSERT INTO t (id) VALUES (?)", (1,), cached) + + def test_stmt_cache_thread_safety() -> None: cache = QueryCache(max_size=32) cached = _make_cached() diff --git a/tests/unit/loader/test_sql_file_loader.py b/tests/unit/loader/test_sql_file_loader.py index 15d0069bc..49a502da0 100644 --- a/tests/unit/loader/test_sql_file_loader.py +++ b/tests/unit/loader/test_sql_file_loader.py @@ -316,6 +316,27 @@ def test_load_directory_with_mixed_files(tmp_path: Path) -> None: assert len(loader.list_queries()) == 1 +def test_load_sql_reads_relative_file_without_duplicate_parent(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: + """Relative file paths should resolve against their parent directory only once.""" + project_dir = tmp_path / "project" + migrations_dir = project_dir / "migrations" + migrations_dir.mkdir(parents=True) + sql_file = migrations_dir / "0001_init.sql" + sql_file.write_text( + """ +-- name: migrate-0001-up +SELECT 1; +""".strip(), + encoding="utf-8", + ) + monkeypatch.chdir(project_dir) + + loader = SQLFileLoader() + loader.load_sql(Path("migrations/0001_init.sql")) + + assert loader.has_query("migrate_0001_up") + + def test_skipped_file_logging(tmp_path: Path, caplog) -> None: """Test that skipped files are logged at DEBUG level.""" import logging diff --git a/tests/unit/storage/test_storage_iterators.py b/tests/unit/storage/test_storage_iterators.py index 447dbcaff..5f33cc319 100644 --- a/tests/unit/storage/test_storage_iterators.py +++ b/tests/unit/storage/test_storage_iterators.py @@ -2,27 +2,38 @@ import io -from sqlspec.storage.backends._iterators import AsyncThreadedBytesIterator +import pytest +from sqlspec.storage.backends.base import AsyncThreadedBytesIterator -async def test_async_threaded_bytes_iterator_aclose_closes_file() -> None: - """Ensure aclose closes the wrapped file object.""" + +@pytest.mark.anyio +async def test_async_threaded_bytes_iterator_closes_file() -> None: + """Ensure AsyncThreadedBytesIterator closes the wrapped file object via context manager.""" file_obj = io.BytesIO(b"abcdef") - iterator = AsyncThreadedBytesIterator(file_obj, chunk_size=2) - await iterator.__anext__() - assert not file_obj.closed + async with AsyncThreadedBytesIterator(file_obj, chunk_size=2) as iterator: + chunk = await iterator.__anext__() + assert chunk == b"ab" + assert not file_obj.closed - await iterator.aclose() + # Exhaust the iterator + async for _ in iterator: + pass + + # Verified: Explicit cleanup or loop exit closes the file assert file_obj.closed -async def test_async_threaded_bytes_iterator_context_manager_closes_file() -> None: - """Ensure async context manager closes the wrapped file object.""" +@pytest.mark.anyio +async def test_async_threaded_bytes_iterator_early_exit_closes_file() -> None: + """Ensure AsyncThreadedBytesIterator closes the wrapped file object on early exit.""" file_obj = io.BytesIO(b"abcdef") async with AsyncThreadedBytesIterator(file_obj, chunk_size=2) as iterator: - chunk = await iterator.__anext__() - assert chunk == b"ab" + async for chunk in iterator: + assert chunk == b"ab" + break + # Verified: Context manager ensures closure even on break/early exit assert file_obj.closed diff --git a/uv.lock b/uv.lock index b499bb1d7..16842cc57 100644 --- a/uv.lock +++ b/uv.lock @@ -740,7 +740,7 @@ wheels = [ [[package]] name = "bump-my-version" -version = "1.2.7" +version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -753,9 +753,9 @@ dependencies = [ { name = "tomlkit" }, { name = "wcmatch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/45/11/0f73c652396f86197ea6d509c78e8c44c3483d9a86437ca53ce55edca8e8/bump_my_version-1.2.7.tar.gz", hash = "sha256:d915a10b41e0c9db5a2fa39bde9f45f92e1e4194242d819c9ceb9eca8831cd21", size = 1198071, upload-time = "2026-02-14T13:44:59.923Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/61/07b90027091a4192b4a0290dc3da1aeea6b9e7b6b4c0f7fd30dab36070c1/bump_my_version-1.3.0.tar.gz", hash = "sha256:5780137a8d93378af3839798fcba01c7e6cb28dcc5aa5a7ab4d8507787f1995c", size = 1142429, upload-time = "2026-03-22T13:27:34.923Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/ed/ad1755f82cd5a0baafe342e7154696a93e57f04f86515402f14e5beceb36/bump_my_version-1.2.7-py3-none-any.whl", hash = "sha256:16f89360f979c0a8eb3249ebe3e13ae4f0cb5481d7bb58e12a9f66996922acfd", size = 60013, upload-time = "2026-02-14T13:44:58.318Z" }, + { url = "https://files.pythonhosted.org/packages/36/01/b168791bfbfb0322ef6d38d236f6f17a02e41fb7753e23e4cdb0f19ac969/bump_my_version-1.3.0-py3-none-any.whl", hash = "sha256:3cdaa54588d2443a29303b77e7539417187952c3d22f87bfdd32c0fe6af2f570", size = 64878, upload-time = "2026-03-22T13:27:33.006Z" }, ] [[package]] @@ -1450,44 +1450,44 @@ wheels = [ [[package]] name = "duckdb" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/11/e05a7eb73a373d523e45d83c261025e02bc31ebf868e6282c30c4d02cc59/duckdb-1.5.0.tar.gz", hash = "sha256:f974b61b1c375888ee62bc3125c60ac11c4e45e4457dd1bb31a8f8d3cf277edd", size = 17981141, upload-time = "2026-03-09T12:50:26.372Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/5d/8fa129bbd604d0e91aa9a0a407e7d2acc559b6024c3f887868fd7a13871d/duckdb-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:47fbb1c053a627a91fa71ec883951561317f14a82df891c00dcace435e8fea78", size = 30012348, upload-time = "2026-03-09T12:48:39.133Z" }, - { url = "https://files.pythonhosted.org/packages/0c/31/db320641a262a897755e634d16838c98d5ca7dc91f4e096e104e244a3a01/duckdb-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2b546a30a6ac020165a86ab3abac553255a6e8244d5437d17859a6aa338611aa", size = 15940515, upload-time = "2026-03-09T12:48:41.905Z" }, - { url = "https://files.pythonhosted.org/packages/0b/45/5725684794fbabf54d8dbae5247685799a6bf8e1e930ebff3a76a726772c/duckdb-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:122396041c0acb78e66d7dc7d36c55f03f67fe6ad012155c132d82739722e381", size = 14193724, upload-time = "2026-03-09T12:48:44.105Z" }, - { url = "https://files.pythonhosted.org/packages/27/68/f110c66b43e27191d7e53d3587e118568b73d66f23cb9bd6c7e0a560fd6d/duckdb-1.5.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a2cd73d50ea2c2bf618a4b7d22fe7c4115a1c9083d35654a0d5d421620ed999", size = 19218777, upload-time = "2026-03-09T12:48:46.399Z" }, - { url = "https://files.pythonhosted.org/packages/ec/9d/46affc9257377cbc865e494650312a7a08a56e85aa8d702eb297bec430b7/duckdb-1.5.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63a8ea3b060a881c90d1c1b9454abed3daf95b6160c39bbb9506fee3a9711730", size = 21311205, upload-time = "2026-03-09T12:48:48.895Z" }, - { url = "https://files.pythonhosted.org/packages/3b/34/dac03ab7340989cda258655387959c88342ea3b44949751391267bcbc830/duckdb-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:238d576ae1dda441f8c79ed1370c5ccf863e4a5d59ca2563f9c96cd26b2188ac", size = 13043217, upload-time = "2026-03-09T12:48:51.262Z" }, - { url = "https://files.pythonhosted.org/packages/01/0c/0282b10a1c96810606b916b8d58a03f2131bd3ede14d2851f58b0b860e7c/duckdb-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3298bd17cf0bb5f342fb51a4edc9aadacae882feb2b04161a03eb93271c70c86", size = 30014615, upload-time = "2026-03-09T12:48:54.061Z" }, - { url = "https://files.pythonhosted.org/packages/71/e8/cbbc920078a794f24f63017fc55c9cbdb17d6fb94d3973f479b2d9f2983d/duckdb-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:13f94c49ca389731c439524248e05007fb1a86cd26f1e38f706abc261069cd41", size = 15940493, upload-time = "2026-03-09T12:48:57.85Z" }, - { url = "https://files.pythonhosted.org/packages/31/b6/6cae794d5856259b0060f79d5db71c7fdba043950eaa6a9d72b0bad16095/duckdb-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab9d597b1e8668466f1c164d0ea07eaf0ebb516950f5a2e794b0f52c81ff3b16", size = 14194663, upload-time = "2026-03-09T12:49:00.416Z" }, - { url = "https://files.pythonhosted.org/packages/82/07/aba3887658b93a36ce702dd00ca6a6422de3d14c7ee3a4b4c03ea20a99c0/duckdb-1.5.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a43f8289b11c0b50d13f96ab03210489d37652f3fd7911dc8eab04d61b049da2", size = 19220501, upload-time = "2026-03-09T12:49:03.431Z" }, - { url = "https://files.pythonhosted.org/packages/fc/a2/723e6df48754e468fa50d7878eb860906c975eafe317c4134a8482ca220e/duckdb-1.5.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f514e796a116c5de070e99974e42d0b8c2e6c303386790e58408c481150d417", size = 21316142, upload-time = "2026-03-09T12:49:06.223Z" }, - { url = "https://files.pythonhosted.org/packages/03/af/4dcbdf8f2349ed0b054c254ec59bc362ce6ddf603af35f770124c0984686/duckdb-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:cf503ba2c753d97c76beb111e74572fef8803265b974af2dca67bba1de4176d2", size = 13043445, upload-time = "2026-03-09T12:49:08.892Z" }, - { url = "https://files.pythonhosted.org/packages/60/5e/1bb7e75a63bf3dc49bc5a2cd27a65ffeef151f52a32db980983516f2d9f6/duckdb-1.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:a1156e91e4e47f0e7d9c9404e559a1d71b372cd61790a407d65eb26948ae8298", size = 13883145, upload-time = "2026-03-09T12:49:11.566Z" }, - { url = "https://files.pythonhosted.org/packages/43/73/120e673e48ae25aaf689044c25ef51b0ea1d088563c9a2532612aea18e0a/duckdb-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9ea988d1d5c8737720d1b2852fd70e4d9e83b1601b8896a1d6d31df5e6afc7dd", size = 30057869, upload-time = "2026-03-09T12:49:14.65Z" }, - { url = "https://files.pythonhosted.org/packages/21/e9/61143471958d36d3f3e764cb4cd43330be208ddbff1c78d3310b9ee67fe8/duckdb-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cb786d5472afc16cc3c7355eb2007172538311d6f0cc6f6a0859e84a60220375", size = 15963092, upload-time = "2026-03-09T12:49:17.478Z" }, - { url = "https://files.pythonhosted.org/packages/4f/71/76e37c9a599ad89dd944e6cbb3e6a8ad196944a421758e83adea507637b6/duckdb-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dc92b238f4122800a7592e99134124cc9048c50f766c37a0778dd2637f5cbe59", size = 14220562, upload-time = "2026-03-09T12:49:23.518Z" }, - { url = "https://files.pythonhosted.org/packages/db/b8/de1831656d5d13173e27c79c7259c8b9a7bdc314fdc8920604838ea4c46d/duckdb-1.5.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b74cb205c21d3696d8f8b88adca401e1063d6e6f57c1c4f56a243610b086e30", size = 19245329, upload-time = "2026-03-09T12:49:26.307Z" }, - { url = "https://files.pythonhosted.org/packages/1f/8d/33d349a3bcbd3e9b7b4e904c19d5b97f058c4c20791b89a8d6323bb93dce/duckdb-1.5.0-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e56c19ffd1ffe3642fa89639e71e2e00ab0cf107b62fe16e88030acaebcbde6", size = 21348041, upload-time = "2026-03-09T12:49:30.283Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ec/591a4cad582fae04bc8f8b4a435eceaaaf3838cf0ca771daae16a3c2995b/duckdb-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:86525e565ec0c43420106fd34ba2c739a54c01814d476c7fed3007c9ed6efd86", size = 13053781, upload-time = "2026-03-09T12:49:33.574Z" }, - { url = "https://files.pythonhosted.org/packages/db/62/42e0a13f9919173bec121c0ff702406e1cdd91d8084c3e0b3412508c3891/duckdb-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:5faeebc178c986a7bfa68868a023001137a95a1110bf09b7356442a4eae0f7e7", size = 13862906, upload-time = "2026-03-09T12:49:36.598Z" }, - { url = "https://files.pythonhosted.org/packages/35/5d/af5501221f42e4e3662c047ecec4dcd0761229fceeba3c67ad4d9d8741df/duckdb-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11dd05b827846c87f0ae2f67b9ae1d60985882a7c08ce855379e4a08d5be0e1d", size = 30057396, upload-time = "2026-03-09T12:49:39.95Z" }, - { url = "https://files.pythonhosted.org/packages/43/bd/a278d73fedbd3783bf9aedb09cad4171fe8e55bd522952a84f6849522eb6/duckdb-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ad8d9c91b7c280ab6811f59deff554b845706c20baa28c4e8f80a95690b252b", size = 15962700, upload-time = "2026-03-09T12:49:43.504Z" }, - { url = "https://files.pythonhosted.org/packages/76/fc/c916e928606946209c20fb50898dabf120241fb528a244e2bd8cde1bd9e2/duckdb-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0ee4dabe03ed810d64d93927e0fd18cd137060b81ee75dcaeaaff32cbc816656", size = 14220272, upload-time = "2026-03-09T12:49:46.867Z" }, - { url = "https://files.pythonhosted.org/packages/53/07/1390e69db922423b2e111e32ed342b3e8fad0a31c144db70681ea1ba4d56/duckdb-1.5.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9409ed1184b363ddea239609c5926f5148ee412b8d9e5ffa617718d755d942f6", size = 19244401, upload-time = "2026-03-09T12:49:49.865Z" }, - { url = "https://files.pythonhosted.org/packages/54/13/b58d718415cde993823a54952ea511d2612302f1d2bc220549d0cef752a4/duckdb-1.5.0-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1df8c4f9c853a45f3ec1e79ed7fe1957a203e5ec893bbbb853e727eb93e0090f", size = 21345827, upload-time = "2026-03-09T12:49:52.977Z" }, - { url = "https://files.pythonhosted.org/packages/e0/96/4460429651e371eb5ff745a4790e7fa0509c7a58c71fc4f0f893404c9646/duckdb-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:9a3d3dfa2d8bc74008ce3ad9564761ae23505a9e4282f6a36df29bd87249620b", size = 13053101, upload-time = "2026-03-09T12:49:56.134Z" }, - { url = "https://files.pythonhosted.org/packages/ba/54/6d5b805113214b830fa3c267bb3383fb8febaa30760d0162ef59aadb110a/duckdb-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:2deebcbafd9d39c04f31ec968f4dd7cee832c021e10d96b32ab0752453e247c8", size = 13865071, upload-time = "2026-03-09T12:49:59.282Z" }, - { url = "https://files.pythonhosted.org/packages/66/9f/dd806d4e8ecd99006eb240068f34e1054533da1857ad06ac726305cd102d/duckdb-1.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:d4b618de670cd2271dd7b3397508c7b3c62d8ea70c592c755643211a6f9154fa", size = 30065704, upload-time = "2026-03-09T12:50:02.671Z" }, - { url = "https://files.pythonhosted.org/packages/79/c2/7b7b8a5c65d5535c88a513e267b5e6d7a55ab3e9b67e4ddd474454653268/duckdb-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:065ae50cb185bac4b904287df72e6b4801b3bee2ad85679576dd712b8ba07021", size = 15964883, upload-time = "2026-03-09T12:50:06.343Z" }, - { url = "https://files.pythonhosted.org/packages/23/c5/9a52a2cdb228b8d8d191a603254364d929274d9cc7d285beada8f7daa712/duckdb-1.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6be5e48e287a24d98306ce9dd55093c3b105a8fbd8a2e7a45e13df34bf081985", size = 14221498, upload-time = "2026-03-09T12:50:10.567Z" }, - { url = "https://files.pythonhosted.org/packages/b8/68/646045cb97982702a8a143dc2e45f3bdcb79fbe2d559a98d74b8c160e5e2/duckdb-1.5.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5ee41a0bf793882f02192ce105b9a113c3e8c505a27c7ef9437d7b756317113", size = 19249787, upload-time = "2026-03-09T12:50:13.524Z" }, - { url = "https://files.pythonhosted.org/packages/15/1b/5abf0c7f38febb3b4a231c784223fceccfd3f2bfd957699d786f46e41ce6/duckdb-1.5.0-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f8e42aaf3cd217417c5dc9ff522dc3939d18b25a6fe5f846348277e831e6f59c", size = 21351583, upload-time = "2026-03-09T12:50:16.701Z" }, - { url = "https://files.pythonhosted.org/packages/93/a4/a90f2901cc0a1ce7ca4f0564b8492b9dbfe048a6395b27933d46ae9be473/duckdb-1.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:11ae50aaeda2145b50294ee0247e4f11fb9448b3cc3d2aea1cfc456637dfb977", size = 13575130, upload-time = "2026-03-09T12:50:19.716Z" }, - { url = "https://files.pythonhosted.org/packages/64/aa/f14dd5e241ec80d9f9d82196ca65e0c53badfc8a7a619d5497c5626657ad/duckdb-1.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:d6d2858c734d1a7e7a1b6e9b8403b3fce26dfefb4e0a2479c420fba6cd36db36", size = 14341879, upload-time = "2026-03-09T12:50:22.347Z" }, +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/62/590caabec6c41003f46a244b6fd707d35ca2e552e0c70cbf454e08bf6685/duckdb-1.5.1.tar.gz", hash = "sha256:b370d1620a34a4538ef66524fcee9de8171fa263c701036a92bc0b4c1f2f9c6d", size = 17995082, upload-time = "2026-03-23T12:12:15.894Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/63/d6477057ea6103f80ed9499580c8602183211689889ec50c32f25a935e3d/duckdb-1.5.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:46f92ada9023e59f27edc048167b31ac9a03911978b1296c845a34462a27f096", size = 30067487, upload-time = "2026-03-23T12:10:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b8/22e6c605d9281df7a83653f4a60168eec0f650b23f1d4648aca940d79d00/duckdb-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:caa65e1f5bf007430bf657c37cab7ab81a4ddf8d337e3062bcc5085d17ef038b", size = 15968413, upload-time = "2026-03-23T12:10:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/85/b1/88a457cd3105525cba0d4c155f847c5c32fa4f543d3ba4ee38b4fd75f82e/duckdb-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c0088765747ae5d6c9f89987bb36f9fb83564f07090d721344ce8e1abedffea", size = 14222115, upload-time = "2026-03-23T12:10:21.662Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3b/800c3f1d54ae0062b3e9b0b54fc54d6c155d731311931d748fc9c5c565f9/duckdb-1.5.1-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e56a20ab6cdb90a95b0c99652e28de3504ce77129087319c03c9098266183ae5", size = 19244994, upload-time = "2026-03-23T12:10:24.708Z" }, + { url = "https://files.pythonhosted.org/packages/3a/09/4c4dd94f521d016e0fb83cca2c203d10ce1e3f8bcc679691b5271fc98b83/duckdb-1.5.1-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:715f05ea198d20d7f8b407b9b84e0023d17f2b9096c194cea702b7840e74f1f7", size = 21347663, upload-time = "2026-03-23T12:10:27.428Z" }, + { url = "https://files.pythonhosted.org/packages/d0/b3/eb3c70be70d0b3fa6c8051d6fa4b7fb3d5787fa77b3f50b7e38d5f7cc6fd/duckdb-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:e878ccb7d20872065e1597935fdb5e65efa43220c8edd0d9c4a1a7ff1f3eb277", size = 13067979, upload-time = "2026-03-23T12:10:30.783Z" }, + { url = "https://files.pythonhosted.org/packages/42/3e/827ffcf58f0abc6ad6dcf826c5d24ebfc65e03ad1a20d74cad9806f91c99/duckdb-1.5.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bc7ca6a1a40e7e4c933017e6c09ef18032add793df4e42624c6c0c87e0bebdad", size = 30067835, upload-time = "2026-03-23T12:10:34.026Z" }, + { url = "https://files.pythonhosted.org/packages/04/b5/e921ecf8a7e0cc7da2100c98bef64b3da386df9444f467d6389364851302/duckdb-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:446d500a2977c6ae2077f340c510a25956da5c77597175c316edfa87248ceda3", size = 15970464, upload-time = "2026-03-23T12:10:42.063Z" }, + { url = "https://files.pythonhosted.org/packages/dd/da/ed804006cd09ba303389d573c8b15d74220667cbd1fd990c26e98d0e0a5b/duckdb-1.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b8b0808dba0c63b7633bdaefb34e08fe0612622224f9feb0e7518904b1615101", size = 14222994, upload-time = "2026-03-23T12:10:45.162Z" }, + { url = "https://files.pythonhosted.org/packages/b3/43/c904d81a61306edab81a9d74bb37bbe65679639abb7030d4c4fec9ed84f7/duckdb-1.5.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:553c273a6a8f140adaa6da6a6135c7f95bdc8c2e5f95252fcdf9832d758e2141", size = 19244880, upload-time = "2026-03-23T12:10:48.529Z" }, + { url = "https://files.pythonhosted.org/packages/50/db/358715d677bfe5e117d9e1f2d6cc2fc2b0bd621144d1f15335b8b59f95d7/duckdb-1.5.1-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:40c5220ec93790b18ec6278da9c6ac2608d997ee6d6f7cd44c5c3992764e8e71", size = 21350874, upload-time = "2026-03-23T12:10:52.095Z" }, + { url = "https://files.pythonhosted.org/packages/3f/db/fd647ce46315347976f5576a279bacb8134d23b1f004bd0bcda7ce9cf429/duckdb-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:36e8e32621a9e2a9abe75dc15a4b54a3997f2d8b1e53ad754bae48a083c91130", size = 13068140, upload-time = "2026-03-23T12:10:55.622Z" }, + { url = "https://files.pythonhosted.org/packages/27/95/e29d42792707619da5867ffab338d7e7b086242c7296aa9cfc6dcf52d568/duckdb-1.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:5ae7c0d744d64e2753149634787cc4ab60f05ef1e542b060eeab719f3cdb7723", size = 13908823, upload-time = "2026-03-23T12:10:58.572Z" }, + { url = "https://files.pythonhosted.org/packages/3f/06/be4c62f812c6e23898733073ace0482eeb18dffabe0585d63a3bf38bca1e/duckdb-1.5.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6f7361d66cc801d9eb4df734b139cd7b0e3c257a16f3573ebd550ddb255549e6", size = 30113703, upload-time = "2026-03-23T12:11:02.536Z" }, + { url = "https://files.pythonhosted.org/packages/44/03/1794dcdda75ff203ab0982ff7eb5232549b58b9af66f243f1b7212d6d6be/duckdb-1.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0a6acc2040bec1f05de62a2f3f68f4c12f3ec7d6012b4317d0ab1a195af26225", size = 15991802, upload-time = "2026-03-23T12:11:06.321Z" }, + { url = "https://files.pythonhosted.org/packages/87/03/293bccd838a293d42ea26dec7f4eb4f58b57b6c9ffcfabc6518a5f20a24a/duckdb-1.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed6d23a3f806898e69c77430ebd8da0c79c219f97b9acbc9a29a653e09740c59", size = 14246803, upload-time = "2026-03-23T12:11:09.624Z" }, + { url = "https://files.pythonhosted.org/packages/15/2c/7b4f11879aa2924838168b4640da999dccda1b4a033d43cb998fd6dc33ea/duckdb-1.5.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6af347debc8b721aa72e48671166282da979d5e5ae52dbc660ab417282b48e23", size = 19271654, upload-time = "2026-03-23T12:11:13.354Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d6/8f9a6b1fbcc669108ec6a4d625a70be9e480b437ed9b70cd56b78cd577a6/duckdb-1.5.1-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8150c569b2aa4573b51ba8475e814aa41fd53a3d510c1ffb96f1139f46faf611", size = 21386100, upload-time = "2026-03-23T12:11:16.758Z" }, + { url = "https://files.pythonhosted.org/packages/c4/fe/8d02c6473273468cf8d43fd5d73c677f8cdfcd036c1e884df0613f124c2b/duckdb-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:054ad424b051b334052afac58cb216f3b1ebb8579fc8c641e60f0182e8725ea9", size = 13083506, upload-time = "2026-03-23T12:11:19.785Z" }, + { url = "https://files.pythonhosted.org/packages/96/0b/2be786b9c153eb263bf5d3d5f7ab621b14a715d7e70f92b24ecf8536369e/duckdb-1.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:6ba302115f63f6482c000ccfd62efdb6c41d9d182a5bcd4a90e7ab8cd13856eb", size = 13888862, upload-time = "2026-03-23T12:11:22.84Z" }, + { url = "https://files.pythonhosted.org/packages/a5/f2/af476945e3b97417945b0f660b5efa661863547c0ea104251bb6387342b1/duckdb-1.5.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:26e56b5f0c96189e3288d83cf7b476e23615987902f801e5788dee15ee9f24a9", size = 30113759, upload-time = "2026-03-23T12:11:26.5Z" }, + { url = "https://files.pythonhosted.org/packages/fe/9d/5a542b3933647369e601175190093597ce0ac54909aea0dd876ec51ffad4/duckdb-1.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:972d0dbf283508f9bc446ee09c3838cb7c7f114b5bdceee41753288c97fe2f7c", size = 15991463, upload-time = "2026-03-23T12:11:30.025Z" }, + { url = "https://files.pythonhosted.org/packages/53/a5/b59cff67f5e0420b8f337ad86406801cffacae219deed83961dcceefda67/duckdb-1.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:482f8a13f2600f527e427f73c42b5aa75536f9892868068f0aaf573055a0135f", size = 14246482, upload-time = "2026-03-23T12:11:33.33Z" }, + { url = "https://files.pythonhosted.org/packages/e9/12/d72a82fe502aae82b97b481bf909be8e22db5a403290799ad054b4f90eb4/duckdb-1.5.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da137802688190835b4c863cafa77fd7e29dff662ee6d905a9ffc14f00299c91", size = 19270816, upload-time = "2026-03-23T12:11:36.79Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c3/ee49319b15f139e04c067378f0e763f78336fbab38ba54b0852467dd9da4/duckdb-1.5.1-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d4147422d91ccdc2d2abf6ed24196025e020259d1d267970ae20c13c2ce84b1", size = 21385695, upload-time = "2026-03-23T12:11:40.465Z" }, + { url = "https://files.pythonhosted.org/packages/a8/f5/a15498e75a27a136c791ca1889beade96d388dadf9811375db155fc96d1a/duckdb-1.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:05fc91767d0cfc4cf2fa68966ab5b479ac07561752e42dd0ae30327bd160f64a", size = 13084065, upload-time = "2026-03-23T12:11:43.763Z" }, + { url = "https://files.pythonhosted.org/packages/93/81/b3612d2bbe237f75791095e16767c61067ea5d31c76e8591c212dac13bd0/duckdb-1.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:a28531cee2a5a42d89f9ba4da53bfeb15681f12acc0263476c8705380dadce07", size = 13892892, upload-time = "2026-03-23T12:11:47.222Z" }, + { url = "https://files.pythonhosted.org/packages/ad/75/e9e7893542ca738bcde2d41d459e3438950219c71c57ad28b049dc2ae616/duckdb-1.5.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:eba81e0b3011c1f23df7ea47ef4ffaa8239817959ae291515b6efd068bde2161", size = 30123677, upload-time = "2026-03-23T12:11:51.511Z" }, + { url = "https://files.pythonhosted.org/packages/df/db/f7420ee7109a922124c02f377ae1c56156e9e4aa434f4726848adaef0219/duckdb-1.5.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:afab8b4b1f4469c3879bb049dd039f8fce402712050324e9524a43d7324c5e87", size = 15996808, upload-time = "2026-03-23T12:11:54.964Z" }, + { url = "https://files.pythonhosted.org/packages/df/57/2c4c3de1f1110417592741863ba58b4eca2f7690a421712762ddbdcd72e6/duckdb-1.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:71dddcebbc5a70e946a06c30b59b5dd7999c9833d307168f90fb4e4b672ab63e", size = 14248990, upload-time = "2026-03-23T12:11:58.576Z" }, + { url = "https://files.pythonhosted.org/packages/2b/81/e173b33ffac53124a3e39e97fb60a538f26651a0df6e393eb9bf7540126c/duckdb-1.5.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac2804043bd1bc10b5da18f8f4c706877197263a510c41be9b4c0062f5783dcc", size = 19276013, upload-time = "2026-03-23T12:12:02.034Z" }, + { url = "https://files.pythonhosted.org/packages/d4/4c/47e838393aa90d3d78549c8c04cb09452efeb14aaae0ee24dc0bd61c3a41/duckdb-1.5.1-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8843bd9594e1387f1e601439e19ad73abdf57356104fd1e53a708255bb95a13d", size = 21387569, upload-time = "2026-03-23T12:12:05.693Z" }, + { url = "https://files.pythonhosted.org/packages/f4/9b/ce65743e0e85f5c984d2f7e8a81bc908d0bac345d6d8b6316436b29430e7/duckdb-1.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:d68c5a01a283cb13b79eafe016fe5869aa11bff8c46e7141c70aa0aac808010f", size = 13603876, upload-time = "2026-03-23T12:12:09.344Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ac/f9e4e731635192571f86f52d86234f537c7f8ca4f6917c56b29051c077ef/duckdb-1.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:a3be2072315982e232bfe49c9d3db0a59ba67b2240a537ef42656cc772a887c7", size = 14370790, upload-time = "2026-03-23T12:12:12.497Z" }, ] [[package]] @@ -1509,7 +1509,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -1536,28 +1536,28 @@ wheels = [ [[package]] name = "extra-platforms" -version = "11.0.2" +version = "11.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b1/d6/ed1e4991dd0effeba8610ea54f2c3f4aa1911a2f6524b2c83b54f321da7a/extra_platforms-11.0.2.tar.gz", hash = "sha256:7920259776faeb0fb4bcb978013a1abc6dc3babdc59473b9a877e4def3ad7028", size = 68113, upload-time = "2026-03-04T15:36:21.071Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/ee/372b3699b3e1cfc7f35430ab14d91b1fb04eff032b96f17a1a7e69a279ac/extra_platforms-11.0.3.tar.gz", hash = "sha256:56225020fe60859bdd27549311b8efc5e3213f79c7851292d9f07189ee730ec1", size = 68476, upload-time = "2026-03-23T07:08:57.234Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/aa/765881526006c212f5a50380fb13c3838e692b44c6cfaf016c19d36a8d59/extra_platforms-11.0.2-py3-none-any.whl", hash = "sha256:9c6c20d1ec166ac271a1f0714158104c62ff3988172aaada2c2932cf8f007d88", size = 71957, upload-time = "2026-03-04T15:36:19.899Z" }, + { url = "https://files.pythonhosted.org/packages/0b/88/7b6d5dabe7f2b2b3afc24a6640ea496fab83688d33df4132efcced91f57c/extra_platforms-11.0.3-py3-none-any.whl", hash = "sha256:0e44e9f09c3584a8aab34d0044a028e95b407d0e60c46631055a8e172446de02", size = 72330, upload-time = "2026-03-23T07:08:55.987Z" }, ] [[package]] name = "faker" -version = "40.11.0" +version = "40.11.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/dc/b68e5378e5a7db0ab776efcdd53b6fe374b29d703e156fd5bb4c5437069e/faker-40.11.0.tar.gz", hash = "sha256:7c419299103b13126bd02ec14bd2b47b946edb5a5eedf305e66a193b25f9a734", size = 1957570, upload-time = "2026-03-13T14:36:11.844Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/e5/b16bf568a2f20fe7423282db4a4059dbcadef70e9029c1c106836f8edd84/faker-40.11.1.tar.gz", hash = "sha256:61965046e79e8cfde4337d243eac04c0d31481a7c010033141103b43f603100c", size = 1957415, upload-time = "2026-03-23T14:05:50.233Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/fa/a86c6ba66f0308c95b9288b1e3eaccd934b545646f63494a86f1ec2f8c8e/faker-40.11.0-py3-none-any.whl", hash = "sha256:0e9816c950528d2a37d74863f3ef389ea9a3a936cbcde0b11b8499942e25bf90", size = 1989457, upload-time = "2026-03-13T14:36:09.792Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ec/3c4b78eb0d2f6a81fb8cc9286745845bff661e6815741eff7a6ac5fcc9ea/faker-40.11.1-py3-none-any.whl", hash = "sha256:3af3a213ba8fb33ce6ba2af7aef2ac91363dae35d0cec0b2b0337d189e5bee2a", size = 1989484, upload-time = "2026-03-23T14:05:48.793Z" }, ] [[package]] name = "fastapi" -version = "0.135.1" +version = "0.135.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -1566,9 +1566,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/7b/f8e0211e9380f7195ba3f3d40c292594fd81ba8ec4629e3854c353aaca45/fastapi-0.135.1.tar.gz", hash = "sha256:d04115b508d936d254cea545b7312ecaa58a7b3a0f84952535b4c9afae7668cd", size = 394962, upload-time = "2026-03-01T18:18:29.369Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/73/5903c4b13beae98618d64eb9870c3fac4f605523dd0312ca5c80dadbd5b9/fastapi-0.135.2.tar.gz", hash = "sha256:88a832095359755527b7f63bb4c6bc9edb8329a026189eed83d6c1afcf419d56", size = 395833, upload-time = "2026-03-23T14:12:41.697Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/72/42e900510195b23a56bde950d26a51f8b723846bfcaa0286e90287f0422b/fastapi-0.135.1-py3-none-any.whl", hash = "sha256:46e2fc5745924b7c840f71ddd277382af29ce1cdb7d5eab5bf697e3fb9999c9e", size = 116999, upload-time = "2026-03-01T18:18:30.831Z" }, + { url = "https://files.pythonhosted.org/packages/8f/ea/18f6d0457f9efb2fc6fa594857f92810cadb03024975726db6546b3d6fcf/fastapi-0.135.2-py3-none-any.whl", hash = "sha256:0af0447d541867e8db2a6a25c23a8c4bd80e2394ac5529bd87501bbb9e240ca5", size = 117407, upload-time = "2026-03-23T14:12:43.284Z" }, ] [[package]] @@ -2311,7 +2311,7 @@ wheels = [ [[package]] name = "google-cloud-storage" -version = "3.10.0" +version = "3.10.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core" }, @@ -2321,9 +2321,9 @@ dependencies = [ { name = "google-resumable-media" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/e3/747759eebc72e420c25903d6bc231d0ceb110b66ac7e6ee3f350417152cd/google_cloud_storage-3.10.0.tar.gz", hash = "sha256:1aeebf097c27d718d84077059a28d7e87f136f3700212215f1ceeae1d1c5d504", size = 17309829, upload-time = "2026-03-18T15:54:11.875Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/47/205eb8e9a1739b5345843e5a425775cbdc472cc38e7eda082ba5b8d02450/google_cloud_storage-3.10.1.tar.gz", hash = "sha256:97db9aa4460727982040edd2bd13ff3d5e2260b5331ad22895802da1fc2a5286", size = 17309950, upload-time = "2026-03-23T09:35:23.409Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/e2/d58442f4daee5babd9255cf492a1f3d114357164072f8339a22a3ad460a2/google_cloud_storage-3.10.0-py3-none-any.whl", hash = "sha256:0072e7783b201e45af78fd9779894cdb6bec2bf922ee932f3fcc16f8bce9b9a3", size = 324382, upload-time = "2026-03-18T15:54:10.091Z" }, + { url = "https://files.pythonhosted.org/packages/ad/ff/ca9ab2417fa913d75aae38bf40bf856bb2749a604b2e0f701b37cfcd23cc/google_cloud_storage-3.10.1-py3-none-any.whl", hash = "sha256:a72f656759b7b99bda700f901adcb3425a828d4a29f911bc26b3ea79c5b1217f", size = 324453, upload-time = "2026-03-23T09:35:21.368Z" }, ] [[package]] @@ -5745,16 +5745,16 @@ wheels = [ [[package]] name = "pytest-cov" -version = "7.0.0" +version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage", extra = ["toml"] }, { name = "pluggy" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, ] [[package]] @@ -7769,11 +7769,11 @@ wheels = [ [[package]] name = "types-docutils" -version = "0.22.3.20260316" +version = "0.22.3.20260322" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9f/27/a7f16b3a2fad0a4ddd85a668319f9a1d0311c4bd9578894f6471c7e6c788/types_docutils-0.22.3.20260316.tar.gz", hash = "sha256:8ef27d565b9831ff094fe2eac75337a74151013e2d21ecabd445c2955f891564", size = 57263, upload-time = "2026-03-16T04:29:12.211Z" } +sdist = { url = "https://files.pythonhosted.org/packages/44/bb/243a87fc1605a4a94c2c343d6dbddbf0d7ef7c0b9550f360b8cda8e82c39/types_docutils-0.22.3.20260322.tar.gz", hash = "sha256:e2450bb997283c3141ec5db3e436b91f0aa26efe35eb9165178ca976ccb4930b", size = 57311, upload-time = "2026-03-22T04:08:44.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/60/c1f22b7cfc4837d5419e5a2d8702c7d65f03343f866364b71cccd8a73b79/types_docutils-0.22.3.20260316-py3-none-any.whl", hash = "sha256:083c7091b8072c242998ec51da1bf1492f0332387da81c3b085efbf5ca754c7d", size = 91968, upload-time = "2026-03-16T04:29:11.114Z" }, + { url = "https://files.pythonhosted.org/packages/c6/4a/22c090cd4615a16917dff817cbe7c5956da376c961e024c241cd962d2c3d/types_docutils-0.22.3.20260322-py3-none-any.whl", hash = "sha256:681d4510ce9b80a0c6a593f0f9843d81f8caa786db7b39ba04d9fd5480ac4442", size = 91978, upload-time = "2026-03-22T04:08:43.117Z" }, ] [[package]] From 36d3d207cf35363ebe172a6cd50f87640088e891 Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Mon, 23 Mar 2026 23:06:43 +0000 Subject: [PATCH 02/15] fix: wrap execute methods in exception handler and update unit tests --- sqlspec/driver/_async.py | 98 ++++++++++++++++++++++++++++------------ sqlspec/driver/_sync.py | 98 ++++++++++++++++++++++++++++------------ 2 files changed, 136 insertions(+), 60 deletions(-) diff --git a/sqlspec/driver/_async.py b/sqlspec/driver/_async.py index ea924d7a3..c26831921 100644 --- a/sqlspec/driver/_async.py +++ b/sqlspec/driver/_async.py @@ -517,21 +517,33 @@ async def execute( **kwargs: Any, ) -> "SQLResult": """Execute a statement with parameter handling.""" - if ( - self._stmt_cache_enabled - and (statement_config is None or statement_config is self.statement_config) - and isinstance(statement, str) - and len(parameters) == 1 - and isinstance(parameters[0], (tuple, list)) - and not kwargs - ): - fast_result = await self._stmt_cache_lookup(statement, parameters[0]) - if fast_result is not None: - return fast_result - sql_statement = self.prepare_statement( - statement, parameters, statement_config=statement_config or self.statement_config, kwargs=kwargs - ) - return await self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) + exc_handler = self.handle_database_exceptions() + try: + try: + async with exc_handler: + if ( + self._stmt_cache_enabled + and (statement_config is None or statement_config is self.statement_config) + and isinstance(statement, str) + and len(parameters) == 1 + and isinstance(parameters[0], (tuple, list)) + and not kwargs + ): + fast_result = await self._stmt_cache_lookup(statement, parameters[0]) + if fast_result is not None: + return fast_result + sql_statement = self.prepare_statement( + statement, parameters, statement_config=statement_config or self.statement_config, kwargs=kwargs + ) + return await self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) + except Exception as exc: + self._raise_async_database_exception(exc_handler, exc) + finally: + self._raise_async_database_exception(exc_handler, None) + finally: + pass + msg = "unreachable" + raise AssertionError(msg) async def execute_many( self, @@ -546,19 +558,31 @@ async def execute_many( Parameters passed will be used as the batch execution sequence. """ - config = statement_config or self.statement_config - - if isinstance(statement, str) and not filters and not kwargs: - sql_statement = SQL(statement, parameters, statement_config=config, is_many=True) - elif isinstance(statement, SQL): - statement_seed = statement.raw_expression or statement.raw_sql - sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) - else: - base_statement = self.prepare_statement(statement, filters, statement_config=config, kwargs=kwargs) - statement_seed = base_statement.raw_expression or base_statement.raw_sql - sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) + exc_handler = self.handle_database_exceptions() + try: + try: + async with exc_handler: + config = statement_config or self.statement_config + + if isinstance(statement, str) and not filters and not kwargs: + sql_statement = SQL(statement, parameters, statement_config=config, is_many=True) + elif isinstance(statement, SQL): + statement_seed = statement.raw_expression or statement.raw_sql + sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) + else: + base_statement = self.prepare_statement(statement, filters, statement_config=config, kwargs=kwargs) + statement_seed = base_statement.raw_expression or base_statement.raw_sql + sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) - return await self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) + return await self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) + except Exception as exc: + self._raise_async_database_exception(exc_handler, exc) + finally: + self._raise_async_database_exception(exc_handler, None) + finally: + pass + msg = "unreachable" + raise AssertionError(msg) async def execute_script( self, @@ -573,10 +597,24 @@ async def execute_script( By default, validates each statement and logs warnings for dangerous operations. Use suppress_warnings=True for migrations and admin scripts. """ - config = statement_config or self.statement_config - sql_statement = self.prepare_statement(statement, parameters, statement_config=config, kwargs=kwargs) + exc_handler = self.handle_database_exceptions() + try: + try: + async with exc_handler: + config = statement_config or self.statement_config + sql_statement = self.prepare_statement(statement, parameters, statement_config=config, kwargs=kwargs) - return await self.dispatch_statement_execution(statement=sql_statement.as_script(), connection=self.connection) + return await self.dispatch_statement_execution( + statement=sql_statement.as_script(), connection=self.connection + ) + except Exception as exc: + self._raise_async_database_exception(exc_handler, exc) + finally: + self._raise_async_database_exception(exc_handler, None) + finally: + pass + msg = "unreachable" + raise AssertionError(msg) # ───────────────────────────────────────────────────────────────────────────── # PUBLIC API - Query Methods (select/fetch variants) diff --git a/sqlspec/driver/_sync.py b/sqlspec/driver/_sync.py index d97a358de..a58c2282e 100644 --- a/sqlspec/driver/_sync.py +++ b/sqlspec/driver/_sync.py @@ -542,21 +542,33 @@ def execute( **kwargs: Any, ) -> "SQLResult": """Execute a statement with parameter handling.""" - if ( - self._stmt_cache_enabled - and (statement_config is None or statement_config is self.statement_config) - and isinstance(statement, str) - and len(parameters) == 1 - and isinstance(parameters[0], (tuple, list)) - and not kwargs - ): - fast_result = self._stmt_cache_lookup(statement, parameters[0]) - if fast_result is not None: - return fast_result # type: ignore[return-value] - sql_statement = self.prepare_statement( - statement, parameters, statement_config=statement_config or self.statement_config, kwargs=kwargs - ) - return self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) + exc_handler = self.handle_database_exceptions() + try: + try: + with exc_handler: + if ( + self._stmt_cache_enabled + and (statement_config is None or statement_config is self.statement_config) + and isinstance(statement, str) + and len(parameters) == 1 + and isinstance(parameters[0], (tuple, list)) + and not kwargs + ): + fast_result = self._stmt_cache_lookup(statement, parameters[0]) + if fast_result is not None: + return fast_result # type: ignore[return-value] + sql_statement = self.prepare_statement( + statement, parameters, statement_config=statement_config or self.statement_config, kwargs=kwargs + ) + return self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) + except Exception as exc: + self._raise_sync_database_exception(exc_handler, exc) + finally: + self._raise_sync_database_exception(exc_handler, None) + finally: + pass + msg = "unreachable" + raise AssertionError(msg) def execute_many( self, @@ -571,19 +583,31 @@ def execute_many( Parameters passed will be used as the batch execution sequence. """ - config = statement_config or self.statement_config - - if isinstance(statement, str) and not filters and not kwargs: - sql_statement = SQL(statement, parameters, statement_config=config, is_many=True) - elif isinstance(statement, SQL): - statement_seed = statement.raw_expression or statement.raw_sql - sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) - else: - base_statement = self.prepare_statement(statement, filters, statement_config=config, kwargs=kwargs) - statement_seed = base_statement.raw_expression or base_statement.raw_sql - sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) + exc_handler = self.handle_database_exceptions() + try: + try: + with exc_handler: + config = statement_config or self.statement_config + + if isinstance(statement, str) and not filters and not kwargs: + sql_statement = SQL(statement, parameters, statement_config=config, is_many=True) + elif isinstance(statement, SQL): + statement_seed = statement.raw_expression or statement.raw_sql + sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) + else: + base_statement = self.prepare_statement(statement, filters, statement_config=config, kwargs=kwargs) + statement_seed = base_statement.raw_expression or base_statement.raw_sql + sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) - return self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) + return self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) + except Exception as exc: + self._raise_sync_database_exception(exc_handler, exc) + finally: + self._raise_sync_database_exception(exc_handler, None) + finally: + pass + msg = "unreachable" + raise AssertionError(msg) def execute_script( self, @@ -598,10 +622,24 @@ def execute_script( By default, validates each statement and logs warnings for dangerous operations. Use suppress_warnings=True for migrations and admin scripts. """ - config = statement_config or self.statement_config - sql_statement = self.prepare_statement(statement, parameters, statement_config=config, kwargs=kwargs) + exc_handler = self.handle_database_exceptions() + try: + try: + with exc_handler: + config = statement_config or self.statement_config + sql_statement = self.prepare_statement(statement, parameters, statement_config=config, kwargs=kwargs) - return self.dispatch_statement_execution(statement=sql_statement.as_script(), connection=self.connection) + return self.dispatch_statement_execution( + statement=sql_statement.as_script(), connection=self.connection + ) + except Exception as exc: + self._raise_sync_database_exception(exc_handler, exc) + finally: + self._raise_sync_database_exception(exc_handler, None) + finally: + pass + msg = "unreachable" + raise AssertionError(msg) # ───────────────────────────────────────────────────────────────────────────── # PUBLIC API - Query Methods (select/fetch variants) From 10c7b9617023a2a33999e2d0aac9d99dce095dbc Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Mon, 23 Mar 2026 23:15:15 +0000 Subject: [PATCH 03/15] fix: prevent re-mapping of SQLSpecError in async and sync exception handlers --- sqlspec/driver/_exception_handler.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sqlspec/driver/_exception_handler.py b/sqlspec/driver/_exception_handler.py index 98b5e46ab..77aff162a 100644 --- a/sqlspec/driver/_exception_handler.py +++ b/sqlspec/driver/_exception_handler.py @@ -1,10 +1,12 @@ """Shared exception handler bases for driver adapters.""" -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any from mypy_extensions import mypyc_attr from typing_extensions import Self +from sqlspec.exceptions import SQLSpecError + if TYPE_CHECKING: from types import TracebackType @@ -29,6 +31,11 @@ async def __aexit__( _ = exc_tb if exc_val is None: return False + + # Do not re-map if already a SQLSpecError + if isinstance(exc_val, SQLSpecError): + return False + return self._handle_exception(exc_type, exc_val) def _handle_exception(self, exc_type: "type[BaseException] | None", exc_val: "BaseException") -> bool: @@ -58,6 +65,11 @@ def __exit__( _ = exc_tb if exc_val is None: return False + + # Do not re-map if already a SQLSpecError + if isinstance(exc_val, SQLSpecError): + return False + return self._handle_exception(exc_type, exc_val) def _handle_exception(self, exc_type: "type[BaseException] | None", exc_val: "BaseException") -> bool: From f6b54aed6a46d1ec8e0564f143e77c468c1b2c77 Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Tue, 24 Mar 2026 17:12:06 +0000 Subject: [PATCH 04/15] fix: update mypy version and improve exception handling in async and sync drivers --- pyproject.toml | 6 +- sqlspec/driver/_async.py | 176 ++++++------- sqlspec/driver/_exception_handler.py | 2 +- sqlspec/driver/_sync.py | 279 +++++++++------------ tests/unit/adapters/test_async_adapters.py | 24 ++ tests/unit/adapters/test_sync_adapters.py | 23 ++ tests/unit/driver/test_query_cache.py | 47 +++- uv.lock | 8 +- 8 files changed, 297 insertions(+), 268 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8433806f8..e19dee302 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,7 +70,7 @@ uuid = ["uuid-utils"] [dependency-groups] benchmarks = ["sqlalchemy[asyncio]", "psutil", "types-psutil", "duckdb-engine>=0.17.0"] -build = ["bump-my-version", "hatch-mypyc", "pydantic-settings"] +build = ["bump-my-version", "hatch-mypyc", "mypy>=1.19.1", "pydantic-settings"] dev = [ { include-group = "extras" }, { include-group = "lint" }, @@ -115,7 +115,7 @@ extras = [ "dishka", ] lint = [ - "mypy>=1.13.0", + "mypy>=1.19.1", "pre-commit>=3.5.0", "pyright>=1.1.386", "ruff>=0.7.1", @@ -176,7 +176,7 @@ packages = ["sqlspec"] [tool.hatch.build.targets.wheel.hooks.mypyc] -dependencies = ["hatch-mypyc", "hatch-cython"] +dependencies = ["hatch-mypyc", "hatch-cython", "mypy>=1.19.1"] enable-by-default = false exclude = [ "tests/**", # Test files diff --git a/sqlspec/driver/_async.py b/sqlspec/driver/_async.py index c26831921..37907e24a 100644 --- a/sqlspec/driver/_async.py +++ b/sqlspec/driver/_async.py @@ -409,29 +409,29 @@ async def _stmt_cache_execute_direct( ) exc_handler = self.handle_database_exceptions() + result: SQLResult | None = None try: - try: - async with exc_handler, self.with_cursor(self.connection) as cursor: - execution_result = await self.dispatch_execute(cursor, direct_statement) - - if cached.operation_profile.returns_rows: - return self.build_statement_result(direct_statement, execution_result) + async with exc_handler, self.with_cursor(self.connection) as cursor: + execution_result = await self.dispatch_execute(cursor, direct_statement) + if cached.operation_profile.returns_rows: + result = self.build_statement_result(direct_statement, execution_result) + else: # DML path: use DMLResult to bypass full SQLResult construction affected_rows = ( execution_result.rowcount_override if execution_result.rowcount_override is not None and execution_result.rowcount_override >= 0 else 0 ) - return DMLResult(cached.operation_type, affected_rows) - except Exception as exc: - self._raise_async_database_exception(exc_handler, exc) - finally: - self._raise_async_database_exception(exc_handler, None) + result = DMLResult(cached.operation_type, affected_rows) + + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + raise pending_exception from None + assert result is not None + return result finally: self._release_pooled_statement(direct_statement) - msg = "unreachable" - raise AssertionError(msg) # pragma: no cover - all paths return or raise async def _stmt_cache_lookup(self, statement: str, params: "tuple[Any, ...] | list[Any]") -> "SQLResult | None": """Attempt fast-path execution for cached query (async). @@ -455,19 +455,19 @@ async def _stmt_cache_execute(self, statement: "SQL") -> "SQLResult": will hit the fast path in _get_compiled_statement (is_processed check). """ exc_handler = self.handle_database_exceptions() + result: SQLResult | None = None try: - try: - async with exc_handler, self.with_cursor(self.connection) as cursor: - execution_result = await self.dispatch_execute(cursor, statement) - return self.build_statement_result(statement, execution_result) - except Exception as exc: - self._raise_async_database_exception(exc_handler, exc) - finally: - self._raise_async_database_exception(exc_handler, None) + async with exc_handler, self.with_cursor(self.connection) as cursor: + execution_result = await self.dispatch_execute(cursor, statement) + result = self.build_statement_result(statement, execution_result) + + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + raise pending_exception from None + assert result is not None + return result finally: self._release_pooled_statement(statement) - msg = "unreachable" - raise AssertionError(msg) # pragma: no cover - all paths return or raise # ───────────────────────────────────────────────────────────────────────────── # TRANSACTION MANAGEMENT - Required Abstract Methods @@ -518,32 +518,29 @@ async def execute( ) -> "SQLResult": """Execute a statement with parameter handling.""" exc_handler = self.handle_database_exceptions() - try: - try: - async with exc_handler: - if ( - self._stmt_cache_enabled - and (statement_config is None or statement_config is self.statement_config) - and isinstance(statement, str) - and len(parameters) == 1 - and isinstance(parameters[0], (tuple, list)) - and not kwargs - ): - fast_result = await self._stmt_cache_lookup(statement, parameters[0]) - if fast_result is not None: - return fast_result - sql_statement = self.prepare_statement( - statement, parameters, statement_config=statement_config or self.statement_config, kwargs=kwargs - ) - return await self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) - except Exception as exc: - self._raise_async_database_exception(exc_handler, exc) - finally: - self._raise_async_database_exception(exc_handler, None) - finally: - pass - msg = "unreachable" - raise AssertionError(msg) + result: SQLResult | None = None + async with exc_handler: + if ( + self._stmt_cache_enabled + and (statement_config is None or statement_config is self.statement_config) + and isinstance(statement, str) + and len(parameters) == 1 + and isinstance(parameters[0], (tuple, list)) + and not kwargs + ): + fast_result = await self._stmt_cache_lookup(statement, parameters[0]) + if fast_result is not None: + result = fast_result + if result is None: + sql_statement = self.prepare_statement( + statement, parameters, statement_config=statement_config or self.statement_config, kwargs=kwargs + ) + result = await self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + raise pending_exception from None + assert result is not None + return result async def execute_many( self, @@ -559,30 +556,26 @@ async def execute_many( Parameters passed will be used as the batch execution sequence. """ exc_handler = self.handle_database_exceptions() - try: - try: - async with exc_handler: - config = statement_config or self.statement_config - - if isinstance(statement, str) and not filters and not kwargs: - sql_statement = SQL(statement, parameters, statement_config=config, is_many=True) - elif isinstance(statement, SQL): - statement_seed = statement.raw_expression or statement.raw_sql - sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) - else: - base_statement = self.prepare_statement(statement, filters, statement_config=config, kwargs=kwargs) - statement_seed = base_statement.raw_expression or base_statement.raw_sql - sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) + result: SQLResult | None = None + async with exc_handler: + config = statement_config or self.statement_config + + if isinstance(statement, str) and not filters and not kwargs: + sql_statement = SQL(statement, parameters, statement_config=config, is_many=True) + elif isinstance(statement, SQL): + statement_seed = statement.raw_expression or statement.raw_sql + sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) + else: + base_statement = self.prepare_statement(statement, filters, statement_config=config, kwargs=kwargs) + statement_seed = base_statement.raw_expression or base_statement.raw_sql + sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) - return await self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) - except Exception as exc: - self._raise_async_database_exception(exc_handler, exc) - finally: - self._raise_async_database_exception(exc_handler, None) - finally: - pass - msg = "unreachable" - raise AssertionError(msg) + result = await self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + raise pending_exception from None + assert result is not None + return result async def execute_script( self, @@ -598,23 +591,18 @@ async def execute_script( operations. Use suppress_warnings=True for migrations and admin scripts. """ exc_handler = self.handle_database_exceptions() - try: - try: - async with exc_handler: - config = statement_config or self.statement_config - sql_statement = self.prepare_statement(statement, parameters, statement_config=config, kwargs=kwargs) - - return await self.dispatch_statement_execution( - statement=sql_statement.as_script(), connection=self.connection - ) - except Exception as exc: - self._raise_async_database_exception(exc_handler, exc) - finally: - self._raise_async_database_exception(exc_handler, None) - finally: - pass - msg = "unreachable" - raise AssertionError(msg) + result: SQLResult | None = None + async with exc_handler: + config = statement_config or self.statement_config + sql_statement = self.prepare_statement(statement, parameters, statement_config=config, kwargs=kwargs) + result = await self.dispatch_statement_execution( + statement=sql_statement.as_script(), connection=self.connection + ) + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + raise pending_exception from None + assert result is not None + return result # ───────────────────────────────────────────────────────────────────────────── # PUBLIC API - Query Methods (select/fetch variants) @@ -920,11 +908,7 @@ async def select_value( **kwargs: Additional keyword arguments. Returns: - The scalar value, optionally converted to the specified type. - - Raises: - ValueError: If no rows or more than one row/column is returned. - TypeError: If value_type is provided and conversion fails. + The scalar value, optionally converted to the specified type Examples: Basic usage (returns Any): @@ -1054,10 +1038,6 @@ async def select_value_or_none( Returns: The scalar value (optionally converted), or None if no rows found. - Raises: - ValueError: If more than one row is returned. - TypeError: If value_type is provided and conversion fails. - Examples: Basic usage: diff --git a/sqlspec/driver/_exception_handler.py b/sqlspec/driver/_exception_handler.py index 77aff162a..d4ba9ee0d 100644 --- a/sqlspec/driver/_exception_handler.py +++ b/sqlspec/driver/_exception_handler.py @@ -1,6 +1,6 @@ """Shared exception handler bases for driver adapters.""" -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from mypy_extensions import mypyc_attr from typing_extensions import Self diff --git a/sqlspec/driver/_sync.py b/sqlspec/driver/_sync.py index a58c2282e..90dca82e7 100644 --- a/sqlspec/driver/_sync.py +++ b/sqlspec/driver/_sync.py @@ -184,31 +184,31 @@ def dispatch_statement_execution(self, statement: "SQL", connection: "Any") -> " # via the fast path in _get_compiled_statement(). This ensures compile() # is called exactly once per statement execution. compiled_sql, execution_parameters = statement.compile() + result: SQLResult | None = None # FAST PATH: Skip all instrumentation if runtime is idle if runtime.is_idle: exc_handler = self.handle_database_exceptions() - try: - with exc_handler, self.with_cursor(connection) as cursor: - # Logic mirrors the instrumentation path below but without telemetry - if statement.is_script: - execution_result = self.dispatch_execute_script(cursor, statement) - return self.build_statement_result(statement, execution_result) - if statement.is_many: - execution_result = self.dispatch_execute_many(cursor, statement) - return self.build_statement_result(statement, execution_result) - - # check special handling first + with exc_handler, self.with_cursor(connection) as cursor: + # Logic mirrors the instrumentation path below but without telemetry + if statement.is_script: + execution_result = self.dispatch_execute_script(cursor, statement) + result = self.build_statement_result(statement, execution_result) + elif statement.is_many: + execution_result = self.dispatch_execute_many(cursor, statement) + result = self.build_statement_result(statement, execution_result) + else: special_result = self.dispatch_special_handling(cursor, statement) if special_result is not None: - return special_result - - execution_result = self.dispatch_execute(cursor, statement) - return self.build_statement_result(statement, execution_result) - except Exception as exc: - self._raise_sync_database_exception(exc_handler, exc) - finally: - self._raise_sync_database_exception(exc_handler, None) + result = special_result + else: + execution_result = self.dispatch_execute(cursor, statement) + result = self.build_statement_result(statement, execution_result) + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + raise pending_exception from None + assert result is not None + return result operation = statement.operation_type query_context = { @@ -222,8 +222,6 @@ def dispatch_statement_execution(self, statement: "SQL", connection: "Any") -> " runtime.emit_query_start(**query_context) span = runtime.start_query_span(compiled_sql, operation, type(self).__name__) started = perf_counter() - - result: SQLResult | None = None exc_handler = self.handle_database_exceptions() try: with exc_handler, self.with_cursor(connection) as cursor: @@ -416,62 +414,59 @@ def _stmt_cache_execute_direct( """ direct_statement: SQL | None = None exc_handler = self.handle_database_exceptions() + result: SQLResult | None = None try: - try: - with exc_handler, self.with_cursor(self.connection) as cursor: - if hasattr(cursor, "execute"): - try: - cursor.execute(cached.compiled_sql, params) - if cached.operation_profile.returns_rows: - fetched_data = cursor.fetchall() - data, column_names, row_count = self.collect_rows(cursor, fetched_data) - execution_result = self.create_execution_result( - cursor, - selected_data=data, - column_names=column_names, - data_row_count=row_count, - is_select_result=True, - row_format="tuple", - ) - direct_statement = self._stmt_cache_build_direct( - sql, - params, - cached, - params, - params_are_simple=True, - compiled_sql=cached.compiled_sql, - ) - return self.build_statement_result(direct_statement, execution_result) - + with exc_handler, self.with_cursor(self.connection) as cursor: + if hasattr(cursor, "execute"): + try: + cursor.execute(cached.compiled_sql, params) + if cached.operation_profile.returns_rows: + fetched_data = cursor.fetchall() + data, column_names, row_count = self.collect_rows(cursor, fetched_data) + execution_result = self.create_execution_result( + cursor, + selected_data=data, + column_names=column_names, + data_row_count=row_count, + is_select_result=True, + row_format="tuple", + ) + direct_statement = self._stmt_cache_build_direct( + sql, params, cached, params, params_are_simple=True, compiled_sql=cached.compiled_sql + ) + result = self.build_statement_result(direct_statement, execution_result) + else: affected_rows = self.resolve_rowcount(cursor) - return DMLResult(cached.operation_type, affected_rows) - except (AttributeError, NotImplementedError): - # Cursor is not DB-API compatible for direct execution. - # Fall back to adapter dispatch path. - pass + result = DMLResult(cached.operation_type, affected_rows) + except (AttributeError, NotImplementedError): + # Cursor is not DB-API compatible for direct execution. + # Fall back to adapter dispatch path. + pass + if result is None: direct_statement = self._stmt_cache_build_direct( sql, params, cached, params, params_are_simple=True, compiled_sql=cached.compiled_sql ) execution_result = self.dispatch_execute(cursor, direct_statement) if cached.operation_profile.returns_rows: - return self.build_statement_result(direct_statement, execution_result) + result = self.build_statement_result(direct_statement, execution_result) + else: + affected_rows = ( + execution_result.rowcount_override + if execution_result.rowcount_override is not None + and execution_result.rowcount_override >= 0 + else 0 + ) + result = DMLResult(cached.operation_type, affected_rows) - affected_rows = ( - execution_result.rowcount_override - if execution_result.rowcount_override is not None and execution_result.rowcount_override >= 0 - else 0 - ) - return DMLResult(cached.operation_type, affected_rows) - except Exception as exc: - self._raise_sync_database_exception(exc_handler, exc) - finally: - self._raise_sync_database_exception(exc_handler, None) + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + raise pending_exception from None + assert result is not None + return result finally: if direct_statement is not None: self._release_pooled_statement(direct_statement) - msg = "unreachable" - raise AssertionError(msg) # pragma: no cover - all paths return or raise def _stmt_cache_execute(self, statement: "SQL") -> "SQLResult": """Execute pre-compiled query via fast path. @@ -480,19 +475,18 @@ def _stmt_cache_execute(self, statement: "SQL") -> "SQLResult": will hit the fast path in _get_compiled_statement (is_processed check). """ exc_handler = self.handle_database_exceptions() + result: SQLResult | None = None try: - try: - with exc_handler, self.with_cursor(self.connection) as cursor: - execution_result = self.dispatch_execute(cursor, statement) - return self.build_statement_result(statement, execution_result) - except Exception as exc: - self._raise_sync_database_exception(exc_handler, exc) - finally: - self._raise_sync_database_exception(exc_handler, None) + with exc_handler, self.with_cursor(self.connection) as cursor: + execution_result = self.dispatch_execute(cursor, statement) + result = self.build_statement_result(statement, execution_result) + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + raise pending_exception from None + assert result is not None + return result finally: self._release_pooled_statement(statement) - msg = "unreachable" - raise AssertionError(msg) # pragma: no cover - all paths return or raise # ───────────────────────────────────────────────────────────────────────────── # TRANSACTION MANAGEMENT - Required Abstract Methods @@ -543,32 +537,29 @@ def execute( ) -> "SQLResult": """Execute a statement with parameter handling.""" exc_handler = self.handle_database_exceptions() - try: - try: - with exc_handler: - if ( - self._stmt_cache_enabled - and (statement_config is None or statement_config is self.statement_config) - and isinstance(statement, str) - and len(parameters) == 1 - and isinstance(parameters[0], (tuple, list)) - and not kwargs - ): - fast_result = self._stmt_cache_lookup(statement, parameters[0]) - if fast_result is not None: - return fast_result # type: ignore[return-value] - sql_statement = self.prepare_statement( - statement, parameters, statement_config=statement_config or self.statement_config, kwargs=kwargs - ) - return self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) - except Exception as exc: - self._raise_sync_database_exception(exc_handler, exc) - finally: - self._raise_sync_database_exception(exc_handler, None) - finally: - pass - msg = "unreachable" - raise AssertionError(msg) + result: SQLResult | None = None + with exc_handler: + if ( + self._stmt_cache_enabled + and (statement_config is None or statement_config is self.statement_config) + and isinstance(statement, str) + and len(parameters) == 1 + and isinstance(parameters[0], (tuple, list)) + and not kwargs + ): + fast_result = self._stmt_cache_lookup(statement, parameters[0]) + if fast_result is not None: + result = cast("SQLResult", fast_result) + if result is None: + sql_statement = self.prepare_statement( + statement, parameters, statement_config=statement_config or self.statement_config, kwargs=kwargs + ) + result = self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + raise pending_exception from None + assert result is not None + return result def execute_many( self, @@ -584,30 +575,26 @@ def execute_many( Parameters passed will be used as the batch execution sequence. """ exc_handler = self.handle_database_exceptions() - try: - try: - with exc_handler: - config = statement_config or self.statement_config - - if isinstance(statement, str) and not filters and not kwargs: - sql_statement = SQL(statement, parameters, statement_config=config, is_many=True) - elif isinstance(statement, SQL): - statement_seed = statement.raw_expression or statement.raw_sql - sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) - else: - base_statement = self.prepare_statement(statement, filters, statement_config=config, kwargs=kwargs) - statement_seed = base_statement.raw_expression or base_statement.raw_sql - sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) + result: SQLResult | None = None + with exc_handler: + config = statement_config or self.statement_config + + if isinstance(statement, str) and not filters and not kwargs: + sql_statement = SQL(statement, parameters, statement_config=config, is_many=True) + elif isinstance(statement, SQL): + statement_seed = statement.raw_expression or statement.raw_sql + sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) + else: + base_statement = self.prepare_statement(statement, filters, statement_config=config, kwargs=kwargs) + statement_seed = base_statement.raw_expression or base_statement.raw_sql + sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) - return self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) - except Exception as exc: - self._raise_sync_database_exception(exc_handler, exc) - finally: - self._raise_sync_database_exception(exc_handler, None) - finally: - pass - msg = "unreachable" - raise AssertionError(msg) + result = self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + raise pending_exception from None + assert result is not None + return result def execute_script( self, @@ -623,23 +610,16 @@ def execute_script( operations. Use suppress_warnings=True for migrations and admin scripts. """ exc_handler = self.handle_database_exceptions() - try: - try: - with exc_handler: - config = statement_config or self.statement_config - sql_statement = self.prepare_statement(statement, parameters, statement_config=config, kwargs=kwargs) - - return self.dispatch_statement_execution( - statement=sql_statement.as_script(), connection=self.connection - ) - except Exception as exc: - self._raise_sync_database_exception(exc_handler, exc) - finally: - self._raise_sync_database_exception(exc_handler, None) - finally: - pass - msg = "unreachable" - raise AssertionError(msg) + result: SQLResult | None = None + with exc_handler: + config = statement_config or self.statement_config + sql_statement = self.prepare_statement(statement, parameters, statement_config=config, kwargs=kwargs) + result = self.dispatch_statement_execution(statement=sql_statement.as_script(), connection=self.connection) + pending_exception = exc_handler.pending_exception + if pending_exception is not None: + raise pending_exception from None + assert result is not None + return result # ───────────────────────────────────────────────────────────────────────────── # PUBLIC API - Query Methods (select/fetch variants) @@ -945,10 +925,6 @@ def select_value( Returns: The scalar value, optionally converted to the specified type. - Raises: - ValueError: If no rows or more than one row/column is returned. - TypeError: If value_type is provided and conversion fails. - Examples: Basic usage (returns Any): @@ -1075,10 +1051,6 @@ def select_value_or_none( Returns: The scalar value (optionally converted), or None if no rows found. - Raises: - ValueError: If more than one row is returned. - TypeError: If value_type is provided and conversion fails. - Examples: Basic usage: @@ -1553,9 +1525,6 @@ def load_from_arrow( Returns: StorageBridgeJob with execution telemetry. - Raises: - StorageCapabilityError: If not implemented. - """ self._raise_storage_not_implemented("load_from_arrow") raise NotImplementedError @@ -1581,9 +1550,6 @@ def load_from_storage( Returns: StorageBridgeJob with execution telemetry. - Raises: - StorageCapabilityError: If not implemented. - """ self._raise_storage_not_implemented("load_from_storage") raise NotImplementedError @@ -1597,9 +1563,6 @@ def stage_artifact(self, request: "dict[str, Any]") -> "dict[str, Any]": Returns: Staging metadata dict. - Raises: - StorageCapabilityError: If not implemented. - """ self._raise_storage_not_implemented("stage_artifact") raise NotImplementedError diff --git a/tests/unit/adapters/test_async_adapters.py b/tests/unit/adapters/test_async_adapters.py index 8468f29b4..10eeccef0 100644 --- a/tests/unit/adapters/test_async_adapters.py +++ b/tests/unit/adapters/test_async_adapters.py @@ -266,6 +266,30 @@ async def test_async_driver_execute_script_method(mock_async_driver: MockAsyncDr assert result.successful_statements == 2 +@pytest.mark.parametrize( + ("method_name", "call_args"), + [ + pytest.param("execute", ("SELECT * FROM users WHERE id = ?", 1), id="execute"), + pytest.param("execute_many", ("INSERT INTO users (name) VALUES (?)", [["alice"]]), id="execute_many"), + pytest.param("execute_script", ("INSERT INTO users (name) VALUES ('alice');",), id="execute_script"), + ], +) +async def test_async_driver_execution_wrappers_reraise_deferred_database_errors( + mock_async_driver: MockAsyncDriver, method_name: str, call_args: tuple[Any, ...] +) -> None: + """Test wrapper methods re-raise mapped errors after the exception context exits.""" + with patch.object( + mock_async_driver, + "dispatch_statement_execution", + new_callable=AsyncMock, + side_effect=ValueError("Test async wrapper error"), + ): + method = getattr(mock_async_driver, method_name) + + with pytest.raises(SQLSpecError, match="Mock async database error: Test async wrapper error"): + await method(*call_args) + + async def test_async_driver_select_one(mock_async_driver: MockAsyncDriver) -> None: """Test async select_one method - expects error when multiple rows returned.""" with pytest.raises(ValueError, match="Multiple results found"): diff --git a/tests/unit/adapters/test_sync_adapters.py b/tests/unit/adapters/test_sync_adapters.py index a984b2380..b7824f16a 100644 --- a/tests/unit/adapters/test_sync_adapters.py +++ b/tests/unit/adapters/test_sync_adapters.py @@ -302,6 +302,29 @@ def test_sync_driver_execute_script_method(mock_sync_driver: MockSyncDriver) -> assert result.successful_statements == 2 +@pytest.mark.parametrize( + ("method_name", "call_args"), + [ + pytest.param("execute", ("SELECT * FROM users WHERE id = ?", 1), id="execute"), + pytest.param("execute_many", ("INSERT INTO users (name) VALUES (?)", [["alice"]]), id="execute_many"), + pytest.param("execute_script", ("INSERT INTO users (name) VALUES ('alice');",), id="execute_script"), + ], +) +def test_sync_driver_execution_wrappers_reraise_deferred_database_errors( + mock_sync_driver: MockSyncDriver, method_name: str, call_args: tuple[Any, ...] +) -> None: + """Test wrapper methods re-raise mapped errors after the exception context exits.""" + with patch.object( + mock_sync_driver, + "dispatch_statement_execution", + side_effect=ValueError("Test wrapper error"), + ): + method = getattr(mock_sync_driver, method_name) + + with pytest.raises(SQLSpecError, match="Mock database error: Test wrapper error"): + method(*call_args) + + def test_sync_driver_select_one(mock_sync_driver: MockSyncDriver) -> None: """Test select_one method - expects error when multiple rows returned.""" with pytest.raises(ValueError, match="Multiple results found"): diff --git a/tests/unit/driver/test_query_cache.py b/tests/unit/driver/test_query_cache.py index 001d33ac7..0d46459a5 100644 --- a/tests/unit/driver/test_query_cache.py +++ b/tests/unit/driver/test_query_cache.py @@ -293,8 +293,41 @@ def test_execute_populates_fast_path_cache_on_normal_path(mock_sync_driver) -> N assert result.operation_type == "SELECT" +def test_sync_stmt_cache_execute_re_raises_mapped_exception(mock_sync_driver: Any, monkeypatch: Any) -> None: + def _fake_dispatch_execute(cursor: Any, statement: Any) -> Any: + _ = (cursor, statement) + raise ValueError("boom") + + monkeypatch.setattr(mock_sync_driver, "dispatch_execute", _fake_dispatch_execute) + statement = SQL("SELECT ?", (1,), statement_config=mock_sync_driver.statement_config) + statement.compile() + + with pytest.raises(SQLSpecError, match="Mock database error: boom"): + mock_sync_driver._stmt_cache_execute(statement) + + +def test_sync_stmt_cache_execute_direct_re_raises_mapped_exception(mock_sync_driver: Any, monkeypatch: Any) -> None: + def _fake_dispatch_execute(cursor: Any, statement: Any) -> Any: + _ = (cursor, statement) + raise ValueError("boom") + + monkeypatch.setattr(mock_sync_driver, "dispatch_execute", _fake_dispatch_execute) + cached = _make_cached( + compiled_sql="INSERT INTO t (id) VALUES (?)", + param_count=1, + operation_type="INSERT", + operation_profile=OperationProfile(returns_rows=False, modifies_rows=True), + processed_state=ProcessedState( + compiled_sql="INSERT INTO t (id) VALUES (?)", execution_parameters=[1], operation_type="INSERT" + ), + ) + + with pytest.raises(SQLSpecError, match="Mock database error: boom"): + mock_sync_driver._stmt_cache_execute_direct("INSERT INTO t (id) VALUES (?)", (1,), cached) + + @pytest.mark.anyio -async def test_async_execute_uses_fast_path_when_eligible(mock_async_driver, monkeypatch) -> None: +async def test_async_execute_uses_fast_path_when_eligible(mock_async_driver: Any, monkeypatch: Any) -> None: sentinel = object() called: dict[str, object] = {} @@ -312,7 +345,9 @@ async def _fake_try(statement: str, params: tuple[Any, ...] | list[Any]) -> obje @pytest.mark.anyio -async def test_async_execute_skips_fast_path_with_statement_config_override(mock_async_driver, monkeypatch) -> None: +async def test_async_execute_skips_fast_path_with_statement_config_override( + mock_async_driver: Any, monkeypatch: Any +) -> None: called = False async def _fake_try(statement: str, params: tuple[Any, ...] | list[Any]) -> object: @@ -331,7 +366,7 @@ async def _fake_try(statement: str, params: tuple[Any, ...] | list[Any]) -> obje @pytest.mark.anyio -async def test_async_execute_populates_fast_path_cache_on_normal_path(mock_async_driver) -> None: +async def test_async_execute_populates_fast_path_cache_on_normal_path(mock_async_driver: Any) -> None: mock_async_driver._stmt_cache_enabled = True assert mock_async_driver._stmt_cache.get("SELECT ?") is None @@ -346,7 +381,7 @@ async def test_async_execute_populates_fast_path_cache_on_normal_path(mock_async @pytest.mark.anyio -async def test_async_stmt_cache_execute_re_raises_mapped_exception(mock_async_driver, monkeypatch) -> None: +async def test_async_stmt_cache_execute_re_raises_mapped_exception(mock_async_driver: Any, monkeypatch: Any) -> None: async def _fake_dispatch_execute(cursor: Any, statement: Any) -> Any: _ = (cursor, statement) raise ValueError("boom") @@ -360,7 +395,9 @@ async def _fake_dispatch_execute(cursor: Any, statement: Any) -> Any: @pytest.mark.anyio -async def test_async_stmt_cache_execute_direct_re_raises_mapped_exception(mock_async_driver, monkeypatch) -> None: +async def test_async_stmt_cache_execute_direct_re_raises_mapped_exception( + mock_async_driver: Any, monkeypatch: Any +) -> None: async def _fake_dispatch_execute(cursor: Any, statement: Any) -> Any: _ = (cursor, statement) raise ValueError("boom") diff --git a/uv.lock b/uv.lock index 16842cc57..2444adc6f 100644 --- a/uv.lock +++ b/uv.lock @@ -1509,7 +1509,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -7208,6 +7208,7 @@ benchmarks = [ build = [ { name = "bump-my-version" }, { name = "hatch-mypyc" }, + { name = "mypy" }, { name = "pydantic-settings" }, ] dev = [ @@ -7419,6 +7420,7 @@ benchmarks = [ build = [ { name = "bump-my-version" }, { name = "hatch-mypyc" }, + { name = "mypy", specifier = ">=1.19.1" }, { name = "pydantic-settings" }, ] dev = [ @@ -7440,7 +7442,7 @@ dev = [ { name = "fsspec", extras = ["s3"] }, { name = "hatch-mypyc" }, { name = "jupyter-sphinx" }, - { name = "mypy", specifier = ">=1.13.0" }, + { name = "mypy", specifier = ">=1.19.1" }, { name = "myst-parser" }, { name = "nbsphinx" }, { name = "numpydoc" }, @@ -7525,7 +7527,7 @@ extras = [ ] lint = [ { name = "asyncpg-stubs" }, - { name = "mypy", specifier = ">=1.13.0" }, + { name = "mypy", specifier = ">=1.19.1" }, { name = "pandas-stubs" }, { name = "pre-commit", specifier = ">=3.5.0" }, { name = "pyarrow-stubs" }, From 0bdbefb849e6629a1c747022d2899b426070b64e Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Tue, 24 Mar 2026 19:11:59 +0000 Subject: [PATCH 05/15] style: format sync adapter test --- tests/unit/adapters/test_sync_adapters.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/unit/adapters/test_sync_adapters.py b/tests/unit/adapters/test_sync_adapters.py index b7824f16a..1a3d07f8e 100644 --- a/tests/unit/adapters/test_sync_adapters.py +++ b/tests/unit/adapters/test_sync_adapters.py @@ -314,11 +314,7 @@ def test_sync_driver_execution_wrappers_reraise_deferred_database_errors( mock_sync_driver: MockSyncDriver, method_name: str, call_args: tuple[Any, ...] ) -> None: """Test wrapper methods re-raise mapped errors after the exception context exits.""" - with patch.object( - mock_sync_driver, - "dispatch_statement_execution", - side_effect=ValueError("Test wrapper error"), - ): + with patch.object(mock_sync_driver, "dispatch_statement_execution", side_effect=ValueError("Test wrapper error")): method = getattr(mock_sync_driver, method_name) with pytest.raises(SQLSpecError, match="Mock database error: Test wrapper error"): From 4e304d371e3f379102c1bf090c70a392ed4b74d2 Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Wed, 25 Mar 2026 17:29:27 +0000 Subject: [PATCH 06/15] fix: enhance alias handling and path resolution in SQLFileLoader and StorageRegistry --- sqlspec/loader.py | 32 +++++++----- sqlspec/storage/registry.py | 63 +++++++++++++++++------- tests/unit/loader/test_alias_handling.py | 36 ++++++++++++++ tests/unit/loader/test_path_handling.py | 57 +++++++++++++++++++++ 4 files changed, 158 insertions(+), 30 deletions(-) create mode 100644 tests/unit/loader/test_alias_handling.py create mode 100644 tests/unit/loader/test_path_handling.py diff --git a/sqlspec/loader.py b/sqlspec/loader.py index 2b65e8d54..ade31c428 100644 --- a/sqlspec/loader.py +++ b/sqlspec/loader.py @@ -273,15 +273,23 @@ def _read_file_content(self, path: str | Path) -> str: try: backend = self.storage_registry.get(path) + + # If path_str contains a '/', we check if the first part is a registered alias. + # This is specifically for when a path is provided relative to an alias. + parts = path_str.split("/", 1) + if len(parts) > 1 and self.storage_registry.is_alias_registered(parts[0]): + return backend.read_text_sync(parts[1], encoding=self.encoding) + if path_str.startswith("file://"): parsed = urlparse(path_str) file_path = unquote(parsed.path) if file_path and len(file_path) > 2 and file_path[2] == ":": # noqa: PLR2004 file_path = file_path[1:] - filename = Path(file_path).name - return backend.read_text_sync(filename, encoding=self.encoding) + return backend.read_text_sync(Path(file_path).name, encoding=self.encoding) + if isinstance(path, Path) or is_local_path(path_str): return backend.read_text_sync(Path(path_str).name, encoding=self.encoding) + return backend.read_text_sync(path_str, encoding=self.encoding) except KeyError as e: raise SQLFileNotFoundError(path_str) from e @@ -401,16 +409,18 @@ def load_sql(self, *paths: str | Path) -> None: try: for path in paths: path_str = str(path) - if "://" in path_str: + # If it looks like a URI or a potential alias (contains no path separators, or is in registry) + if "://" in path_str or self.storage_registry.is_alias_registered(path_str.split("/", maxsplit=1)[0]): self._load_single_file(path, None) - else: - path_obj = Path(path) - if path_obj.is_dir(): - self._load_directory(path_obj) - elif path_obj.exists(): - self._load_single_file(path_obj, None) - elif path_obj.suffix: - self._raise_file_not_found(str(path)) + continue + + path_obj = Path(path) + if path_obj.is_dir(): + self._load_directory(path_obj) + elif path_obj.exists(): + self._load_single_file(path_obj, None) + elif path_obj.suffix: + self._raise_file_not_found(str(path)) except Exception as exc: error = exc diff --git a/sqlspec/storage/registry.py b/sqlspec/storage/registry.py index ba7ed5d36..62efc8509 100644 --- a/sqlspec/storage/registry.py +++ b/sqlspec/storage/registry.py @@ -9,6 +9,7 @@ import re from pathlib import Path from typing import Any, Final, cast +from urllib.parse import unquote, urlparse from mypy_extensions import mypyc_attr @@ -112,35 +113,59 @@ def get(self, uri_or_alias: str | Path, *, backend: str | None = None, **kwargs: msg = "URI or alias cannot be empty." raise ImproperConfigurationError(msg) - if isinstance(uri_or_alias, Path): - local_path = uri_or_alias.expanduser().resolve() - uri_or_alias = f"file://{local_path.parent}" if local_path.is_file() else f"file://{local_path}" - cache_params = dict(kwargs) if backend: cache_params["__backend__"] = backend - cache_key = (uri_or_alias, self._make_hashable(cache_params)) if cache_params else uri_or_alias + + path_str = str(uri_or_alias) + scheme = self._get_scheme(path_str) + + # 1. Resolve to a base URI + base_uri = path_str + is_alias = False + + # Check if it's an alias first (either exact match or prefix match like "alias/path") + parts = path_str.split("/", 1) + potential_alias = parts[0] + + if potential_alias in self._alias_configs: + base_uri = potential_alias + is_alias = True + elif scheme: + if scheme == "file": + parsed = urlparse(path_str) + file_path = unquote(parsed.path) + if file_path and len(file_path) > 2 and file_path[2] == ":": # noqa: PLR2004 + file_path = file_path[1:] + + path_obj = Path(file_path).expanduser().resolve() + base_uri = f"file://{path_obj.parent}" if path_obj.is_file() else f"file://{path_obj}" + elif is_local_path(path_str): + scheme = "file" + path_obj = Path(path_str).expanduser().resolve() + base_uri = f"file://{path_obj.parent}" if path_obj.is_file() else f"file://{path_obj}" + else: + msg = f"Unknown storage alias or invalid URI: '{uri_or_alias}'" + raise ImproperConfigurationError(msg) + + # 2. Check instance cache using the BASE URI + cache_key = (base_uri, self._make_hashable(cache_params)) if cache_params else base_uri if cache_key in self._instances: - log_with_context(logger, logging.DEBUG, "storage.resolve", uri_or_alias=str(uri_or_alias), cached=True) + log_with_context(logger, logging.DEBUG, "storage.resolve", uri_or_alias=path_str, cached=True) return self._instances[cache_key] - scheme = self._get_scheme(uri_or_alias) - if not scheme and is_local_path(uri_or_alias): - scheme = "file" - local_path = Path(uri_or_alias).expanduser().resolve() - uri_or_alias = f"file://{local_path.parent}" if local_path.is_file() else f"file://{local_path}" - if scheme: - instance = self._resolve_from_uri(uri_or_alias, backend_override=backend, **kwargs) - elif uri_or_alias in self._alias_configs: - backend_cls, stored_uri, config = self._alias_configs[uri_or_alias] + # 3. Create new instance if not cached + if not is_alias: + instance = self._resolve_from_uri(base_uri, backend_override=backend, **kwargs) + else: + # It must be an alias (already validated above) + backend_cls, stored_uri, config = self._alias_configs[base_uri] if backend: backend_cls = self._get_backend_class(backend) instance = backend_cls(stored_uri, **{**config, **kwargs}) - else: - msg = f"Unknown storage alias or invalid URI: '{uri_or_alias}'" - raise ImproperConfigurationError(msg) + self._instances[cache_key] = instance - log_with_context(logger, logging.DEBUG, "storage.resolve", uri_or_alias=str(uri_or_alias), cached=False) + log_with_context(logger, logging.DEBUG, "storage.resolve", uri_or_alias=path_str, cached=False) return instance def _resolve_from_uri(self, uri: str, *, backend_override: str | None = None, **kwargs: Any) -> ObjectStoreProtocol: diff --git a/tests/unit/loader/test_alias_handling.py b/tests/unit/loader/test_alias_handling.py new file mode 100644 index 000000000..e37be7192 --- /dev/null +++ b/tests/unit/loader/test_alias_handling.py @@ -0,0 +1,36 @@ +from pathlib import Path + +from sqlspec.loader import SQLFileLoader +from sqlspec.storage.registry import StorageRegistry + + +def test_load_file_with_alias_is_not_double_dir_stripped(tmp_path: Path) -> None: + """Test loading a file through an alias to verify path handling.""" + sql_file = tmp_path / "my_query.sql" + sql_file.write_text("-- name: query1\nSELECT 1;") + + registry = StorageRegistry() + # Register an alias to the parent directory + registry.register_alias("my_store", f"file://{tmp_path}") + + loader = SQLFileLoader(storage_registry=registry) + # The storage backend for 'my_store/my_query.sql' will be scoped to 'file://tmp_path' + loader.load_sql("my_store/my_query.sql") + + assert loader.has_query("query1") + +def test_load_file_from_alias_nested(tmp_path: Path) -> None: + """Test loading a nested file through an alias.""" + nested_dir = tmp_path / "nested" + nested_dir.mkdir() + sql_file = nested_dir / "my_query.sql" + sql_file.write_text("-- name: query2\nSELECT 2;") + + registry = StorageRegistry() + registry.register_alias("my_store", f"file://{tmp_path}") + + loader = SQLFileLoader(storage_registry=registry) + # 'my_store/nested/my_query.sql' + loader.load_sql("my_store/nested/my_query.sql") + + assert loader.has_query("query2") diff --git a/tests/unit/loader/test_path_handling.py b/tests/unit/loader/test_path_handling.py new file mode 100644 index 000000000..073d224be --- /dev/null +++ b/tests/unit/loader/test_path_handling.py @@ -0,0 +1,57 @@ +from pathlib import Path + +from sqlspec.loader import SQLFileLoader + + +def test_load_specific_file_in_nested_dir(tmp_path: Path) -> None: + """Test loading a specific file in a nested directory. + + It should only load the specified file, not everything in the directory. + """ + nested_dir = tmp_path / "nested" / "dir" + nested_dir.mkdir(parents=True) + + file1 = nested_dir / "file1.sql" + file1.write_text("-- name: query1\nSELECT 1;") + + file2 = nested_dir / "file2.sql" + file2.write_text("-- name: query2\nSELECT 2;") + + loader = SQLFileLoader() + loader.load_sql(file1) + + assert loader.has_query("query1") + assert not loader.has_query("query2") + assert loader.list_files() == [str(file1)] + +def test_load_specific_file_is_not_namespaced(tmp_path: Path) -> None: + """Test that a specific file loaded directly is NOT namespaced by its directory. + + This matches current behavior where _load_single_file(path, None) is called. + """ + nested_dir = tmp_path / "nested" / "dir" + nested_dir.mkdir(parents=True) + + file1 = nested_dir / "file1.sql" + file1.write_text("-- name: query1\nSELECT 1;") + + loader = SQLFileLoader() + loader.load_sql(file1) + + # It should be "query1", not "nested.dir.query1" + assert loader.has_query("query1") + assert not loader.has_query("nested.dir.query1") + +def test_load_directory_is_namespaced(tmp_path: Path) -> None: + """Test that loading a directory IS namespaced.""" + nested_dir = tmp_path / "nested" / "dir" + nested_dir.mkdir(parents=True) + + file1 = nested_dir / "file1.sql" + file1.write_text("-- name: query1\nSELECT 1;") + + loader = SQLFileLoader() + # Loading the base tmp_path should result in namespacing + loader.load_sql(tmp_path) + + assert loader.has_query("nested.dir.query1") From e2b6f6fc416290edca4ef2363b7c0a0ffa460c99 Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Fri, 27 Mar 2026 14:48:26 +0000 Subject: [PATCH 07/15] fix: add blank lines for improved readability in test files --- tests/unit/loader/test_alias_handling.py | 1 + tests/unit/loader/test_path_handling.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/tests/unit/loader/test_alias_handling.py b/tests/unit/loader/test_alias_handling.py index e37be7192..203c9bd0c 100644 --- a/tests/unit/loader/test_alias_handling.py +++ b/tests/unit/loader/test_alias_handling.py @@ -19,6 +19,7 @@ def test_load_file_with_alias_is_not_double_dir_stripped(tmp_path: Path) -> None assert loader.has_query("query1") + def test_load_file_from_alias_nested(tmp_path: Path) -> None: """Test loading a nested file through an alias.""" nested_dir = tmp_path / "nested" diff --git a/tests/unit/loader/test_path_handling.py b/tests/unit/loader/test_path_handling.py index 073d224be..5196b69c3 100644 --- a/tests/unit/loader/test_path_handling.py +++ b/tests/unit/loader/test_path_handling.py @@ -24,6 +24,7 @@ def test_load_specific_file_in_nested_dir(tmp_path: Path) -> None: assert not loader.has_query("query2") assert loader.list_files() == [str(file1)] + def test_load_specific_file_is_not_namespaced(tmp_path: Path) -> None: """Test that a specific file loaded directly is NOT namespaced by its directory. @@ -42,6 +43,7 @@ def test_load_specific_file_is_not_namespaced(tmp_path: Path) -> None: assert loader.has_query("query1") assert not loader.has_query("nested.dir.query1") + def test_load_directory_is_namespaced(tmp_path: Path) -> None: """Test that loading a directory IS namespaced.""" nested_dir = tmp_path / "nested" / "dir" From 115c18b2c758fc56a97f4a196662b5d5fe6a8953 Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Mon, 30 Mar 2026 22:01:52 +0000 Subject: [PATCH 08/15] fix: use standalone classes --- .pre-commit-config.yaml | 2 +- sqlspec/builder/_temporal.py | 17 +- sqlspec/dialects/postgres/_pgvector.py | 5 +- sqlspec/dialects/spanner/_spangres.py | 5 +- sqlspec/dialects/spanner/_spanner.py | 7 +- .../adapters/aiosqlite/test_driver.py | 2 - .../adapters/aiosqlite/test_exceptions.py | 3 +- .../adapters/asyncmy/test_arrow.py | 3 +- .../adapters/asyncmy/test_driver.py | 2 - .../adapters/asyncpg/test_driver.py | 2 - .../adapters/bigquery/test_driver.py | 2 - .../adapters/psqlpy/test_driver.py | 2 - .../adapters/psycopg/test_async_copy.py | 2 - .../adapters/psycopg/test_driver.py | 2 - uv.lock | 1217 +++++++++-------- 15 files changed, 677 insertions(+), 596 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bd268bbca..af836ca5e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: - id: mixed-line-ending - id: trailing-whitespace - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: "v0.15.7" + rev: "v0.15.8" hooks: - id: ruff args: ["--fix"] diff --git a/sqlspec/builder/_temporal.py b/sqlspec/builder/_temporal.py index 6e7c4a1fe..c89e71f5d 100644 --- a/sqlspec/builder/_temporal.py +++ b/sqlspec/builder/_temporal.py @@ -18,24 +18,29 @@ from sqlglot.dialects.postgres import Postgres from sqlglot.dialects.snowflake import Snowflake from sqlglot.generator import Generator +from sqlglot.generators.bigquery import BigQueryGenerator +from sqlglot.generators.duckdb import DuckDBGenerator +from sqlglot.generators.oracle import OracleGenerator +from sqlglot.generators.postgres import PostgresGenerator +from sqlglot.generators.snowflake import SnowflakeGenerator __all__ = ("create_temporal_table", "register_version_generators") -def _oracle_version_sql(self: "Oracle.Generator", expression: exp.Version) -> str: +def _oracle_version_sql(self: OracleGenerator, expression: exp.Version) -> str: """Oracle: AS OF TIMESTAMP timestamp or AS OF SCN scn.""" expr = self.sql(expression, "expression") this = expression.name or "TIMESTAMP" return f"AS OF {this} {expr}" -def _bigquery_version_sql(self: "BigQuery.Generator", expression: exp.Version) -> str: +def _bigquery_version_sql(self: BigQueryGenerator, expression: exp.Version) -> str: """BigQuery: FOR SYSTEM_TIME AS OF timestamp.""" expr = self.sql(expression, "expression") return f"FOR SYSTEM_TIME AS OF {expr}" -def _snowflake_version_sql(self: "Snowflake.Generator", expression: exp.Version) -> str: +def _snowflake_version_sql(self: SnowflakeGenerator, expression: exp.Version) -> str: """Snowflake: AT (TIMESTAMP => timestamp) or BEFORE (TIMESTAMP => ...). AS OF is mapped to AT, and BEFORE is supported for point-before queries. @@ -48,19 +53,19 @@ def _snowflake_version_sql(self: "Snowflake.Generator", expression: exp.Version) return f"AT ({this} => {expr})" -def _duckdb_version_sql(self: "DuckDB.Generator", expression: exp.Version) -> str: +def _duckdb_version_sql(self: DuckDBGenerator, expression: exp.Version) -> str: """DuckDB: AT (TIMESTAMP => timestamp).""" expr = self.sql(expression, "expression") return f"AT (TIMESTAMP => {expr})" -def _cockroachdb_version_sql(self: "Postgres.Generator", expression: exp.Version) -> str: +def _cockroachdb_version_sql(self: PostgresGenerator, expression: exp.Version) -> str: """CockroachDB (via Postgres dialect): AS OF SYSTEM TIME timestamp.""" expr = self.sql(expression, "expression") return f"AS OF SYSTEM TIME {expr}" -def _default_version_sql(self: "Generator", expression: exp.Version) -> str: +def _default_version_sql(self: Generator, expression: exp.Version) -> str: """Default: AS OF SYSTEM TIME timestamp (CockroachDB style). When no dialect is specified, we default to CockroachDB/Postgres style diff --git a/sqlspec/dialects/postgres/_pgvector.py b/sqlspec/dialects/postgres/_pgvector.py index 84628ea8f..6ce410e54 100644 --- a/sqlspec/dialects/postgres/_pgvector.py +++ b/sqlspec/dialects/postgres/_pgvector.py @@ -12,6 +12,7 @@ from sqlglot import exp from sqlglot.dialects.dialect import Dialect from sqlglot.dialects.postgres import Postgres +from sqlglot.generators.postgres import PostgresGenerator from sqlspec.builder._vector_distance import ( is_vector_distance_expression, @@ -32,7 +33,7 @@ _BASE_OPERATOR_TRANSFORM = Postgres.Generator.TRANSFORMS[exp.Operator] -def _postgres_extension_operator_sql(generator: Postgres.Generator, expression: exp.Operator) -> str: +def _postgres_extension_operator_sql(generator: PostgresGenerator, expression: exp.Operator) -> str: if is_vector_distance_expression(expression): return render_vector_distance_postgres( generator.sql(expression, "this"), @@ -56,7 +57,7 @@ class PGVectorTokenizer(Postgres.Tokenizer): KEYWORDS = {**Postgres.Tokenizer.KEYWORDS, **PGVECTOR_OPERATOR_TOKENS} -class PGVectorGenerator(Postgres.Generator): +class PGVectorGenerator(PostgresGenerator): """Generator that renders pgvector and SQLSpec vector-distance operators.""" diff --git a/sqlspec/dialects/spanner/_spangres.py b/sqlspec/dialects/spanner/_spangres.py index 04b42eeff..bc40f3adf 100644 --- a/sqlspec/dialects/spanner/_spangres.py +++ b/sqlspec/dialects/spanner/_spangres.py @@ -4,6 +4,7 @@ from sqlglot import exp from sqlglot.dialects.postgres import Postgres +from sqlglot.generators.postgres import PostgresGenerator from sqlglot.parsers.postgres import PostgresParser from sqlglot.tokenizer_core import TokenType @@ -77,7 +78,7 @@ def _register_postgres_spangres_parser_hooks() -> None: setattr(PostgresParser, _HOOKS_REGISTERED_ATTR, True) -class SpangresGenerator(Postgres.Generator): +class SpangresGenerator(PostgresGenerator): """Generate Spanner row deletion policies.""" def property_sql(self, expression: exp.Property) -> str: @@ -88,7 +89,7 @@ def property_sql(self, expression: exp.Property) -> str: interval_sql = _render_interval_sql(self, values.expressions[1]) return f"ROW DELETION POLICY (OLDER_THAN({column}, {interval_sql}))" - return super().property_sql(expression) + return str(super().property_sql(expression)) _register_postgres_spangres_parser_hooks() diff --git a/sqlspec/dialects/spanner/_spanner.py b/sqlspec/dialects/spanner/_spanner.py index 24c6fc62a..40b0dc9d7 100644 --- a/sqlspec/dialects/spanner/_spanner.py +++ b/sqlspec/dialects/spanner/_spanner.py @@ -10,6 +10,7 @@ from sqlglot import exp from sqlglot.dialects.bigquery import BigQuery +from sqlglot.generators.bigquery import BigQueryGenerator from sqlglot.parsers.bigquery import BigQueryParser from sqlglot.tokenizer_core import TokenType @@ -168,7 +169,7 @@ class SpannerTokenizer(BigQuery.Tokenizer): KEYWORDS = {**BigQuery.Tokenizer.KEYWORDS, **_SPANNER_KEYWORDS} -class SpannerGenerator(BigQuery.Generator): +class SpannerGenerator(BigQueryGenerator): """Generate Spanner-specific DDL syntax.""" def locate_properties(self, properties: exp.Properties) -> Any: @@ -215,7 +216,7 @@ def properties_sql(self, expression: exp.Properties) -> str: if root_props and with_props and not self.pretty: with_props = f" {with_props}" - return root_props + with_props + return str(root_props) + str(with_props) def property_sql(self, expression: exp.Property) -> str: """Render Spanner-specific properties.""" @@ -244,7 +245,7 @@ def property_sql(self, expression: exp.Property) -> str: column = self.sql(values.expressions[1]) return f"TTL INTERVAL {interval} ON {column}" - return super().property_sql(expression) + return str(super().property_sql(expression)) _register_bigquery_spanner_parser_hooks() diff --git a/tests/integration/adapters/aiosqlite/test_driver.py b/tests/integration/adapters/aiosqlite/test_driver.py index e73496be7..8af049dcc 100644 --- a/tests/integration/adapters/aiosqlite/test_driver.py +++ b/tests/integration/adapters/aiosqlite/test_driver.py @@ -8,7 +8,6 @@ from sqlspec import SQL, SQLResult, StatementStack, sql from sqlspec.adapters.aiosqlite import AiosqliteDriver from sqlspec.core import StatementConfig -from tests.conftest import requires_interpreted pytestmark = pytest.mark.xdist_group("sqlite") @@ -235,7 +234,6 @@ async def test_aiosqlite_statement_stack_sequential(aiosqlite_session: Aiosqlite assert count_result.get_data()[0]["total"] == 2 -@requires_interpreted async def test_aiosqlite_statement_stack_continue_on_error(aiosqlite_session: AiosqliteDriver) -> None: """Sequential execution should continue when continue_on_error is enabled.""" diff --git a/tests/integration/adapters/aiosqlite/test_exceptions.py b/tests/integration/adapters/aiosqlite/test_exceptions.py index a7767427b..240b184f3 100644 --- a/tests/integration/adapters/aiosqlite/test_exceptions.py +++ b/tests/integration/adapters/aiosqlite/test_exceptions.py @@ -12,9 +12,8 @@ SQLParsingError, UniqueViolationError, ) -from tests.conftest import requires_interpreted -pytestmark = [pytest.mark.xdist_group("sqlite"), requires_interpreted] +pytestmark = pytest.mark.xdist_group("sqlite") @pytest.fixture diff --git a/tests/integration/adapters/asyncmy/test_arrow.py b/tests/integration/adapters/asyncmy/test_arrow.py index 4a4cad454..82b549fd6 100644 --- a/tests/integration/adapters/asyncmy/test_arrow.py +++ b/tests/integration/adapters/asyncmy/test_arrow.py @@ -3,9 +3,8 @@ import pytest from sqlspec.adapters.asyncmy import AsyncmyDriver -from tests.conftest import requires_interpreted -pytestmark = [pytest.mark.xdist_group("mysql"), requires_interpreted] +pytestmark = pytest.mark.xdist_group("mysql") async def test_select_to_arrow_basic(asyncmy_driver: AsyncmyDriver) -> None: diff --git a/tests/integration/adapters/asyncmy/test_driver.py b/tests/integration/adapters/asyncmy/test_driver.py index 4a0c9dff8..89244a84a 100644 --- a/tests/integration/adapters/asyncmy/test_driver.py +++ b/tests/integration/adapters/asyncmy/test_driver.py @@ -14,7 +14,6 @@ from sqlspec import SQL, SQLResult, StatementStack, sql from sqlspec.adapters.asyncmy import AsyncmyConfig, AsyncmyDriver from sqlspec.utils.serializers import from_json, to_json -from tests.conftest import requires_interpreted ParamStyle = Literal["tuple_binds", "dict_binds", "named_binds"] @@ -192,7 +191,6 @@ async def test_asyncmy_statement_stack_sequential(asyncmy_driver: AsyncmyDriver) assert data[0]["total"] == 2 -@requires_interpreted async def test_asyncmy_statement_stack_continue_on_error(asyncmy_driver: AsyncmyDriver) -> None: """Continue-on-error should still work with sequential fallback.""" diff --git a/tests/integration/adapters/asyncpg/test_driver.py b/tests/integration/adapters/asyncpg/test_driver.py index 1f3f7185b..eec79755f 100644 --- a/tests/integration/adapters/asyncpg/test_driver.py +++ b/tests/integration/adapters/asyncpg/test_driver.py @@ -12,7 +12,6 @@ from sqlspec import SQLResult, StatementStack, sql from sqlspec.adapters.asyncpg import AsyncpgConfig, AsyncpgDriver -from tests.conftest import requires_interpreted ParamStyle = Literal["tuple_binds", "dict_binds", "named_binds"] @@ -885,7 +884,6 @@ async def test_asyncpg_statement_stack_batch(asyncpg_session: "AsyncpgDriver") - assert count_result.get_data()[0]["total_rows"] == 2 -@requires_interpreted async def test_asyncpg_statement_stack_continue_on_error(asyncpg_session: "AsyncpgDriver") -> None: """Stack execution should surface errors while continuing operations when requested.""" diff --git a/tests/integration/adapters/bigquery/test_driver.py b/tests/integration/adapters/bigquery/test_driver.py index 80eaab7ed..bbe951ff7 100644 --- a/tests/integration/adapters/bigquery/test_driver.py +++ b/tests/integration/adapters/bigquery/test_driver.py @@ -10,7 +10,6 @@ from sqlspec import SQLResult, StatementStack, sql from sqlspec.adapters.bigquery import BigQueryConfig, BigQueryDriver -from tests.conftest import requires_interpreted ParamStyle = Literal["tuple_binds", "dict_binds", "named_binds"] @@ -256,7 +255,6 @@ def test_bigquery_statement_stack_sequential(bigquery_session: "BigQueryDriver", assert count_result.get_data()[0]["total"] == 2 -@requires_interpreted def test_bigquery_statement_stack_continue_on_error(bigquery_session: "BigQueryDriver", driver_test_table: str) -> None: """Continue-on-error should surface BigQuery failures but keep executing.""" diff --git a/tests/integration/adapters/psqlpy/test_driver.py b/tests/integration/adapters/psqlpy/test_driver.py index 52d4e0df7..7b2857c77 100644 --- a/tests/integration/adapters/psqlpy/test_driver.py +++ b/tests/integration/adapters/psqlpy/test_driver.py @@ -7,7 +7,6 @@ from sqlspec import SQL, SQLResult, StatementStack, sql from sqlspec.adapters.psqlpy import PsqlpyConfig, PsqlpyDriver -from tests.conftest import requires_interpreted if TYPE_CHECKING: from pytest_databases.docker.postgres import PostgresService @@ -321,7 +320,6 @@ async def test_psqlpy_statement_stack_sequential(psqlpy_session: "PsqlpyDriver") assert verify.get_data()[0]["total"] == 2 -@requires_interpreted async def test_psqlpy_statement_stack_continue_on_error(psqlpy_session: "PsqlpyDriver") -> None: """Sequential stack execution should honor continue-on-error flag.""" diff --git a/tests/integration/adapters/psycopg/test_async_copy.py b/tests/integration/adapters/psycopg/test_async_copy.py index 245ade47f..1a49e3c70 100644 --- a/tests/integration/adapters/psycopg/test_async_copy.py +++ b/tests/integration/adapters/psycopg/test_async_copy.py @@ -7,7 +7,6 @@ from sqlspec import SQLResult, StatementStack from sqlspec.adapters.psycopg import PsycopgAsyncConfig, PsycopgAsyncDriver -from tests.conftest import requires_interpreted pytestmark = pytest.mark.xdist_group("postgres") @@ -184,7 +183,6 @@ async def test_psycopg_async_statement_stack_pipeline(psycopg_async_session: Psy assert verify.get_data()[0]["total"] == 2 -@requires_interpreted async def test_psycopg_async_statement_stack_continue_on_error(psycopg_async_session: PsycopgAsyncDriver) -> None: """Ensure async pipeline honors continue-on-error semantics.""" diff --git a/tests/integration/adapters/psycopg/test_driver.py b/tests/integration/adapters/psycopg/test_driver.py index a44070d54..0320cb2b4 100644 --- a/tests/integration/adapters/psycopg/test_driver.py +++ b/tests/integration/adapters/psycopg/test_driver.py @@ -8,7 +8,6 @@ from sqlspec import SQLResult, StatementStack, sql from sqlspec.adapters.psycopg import PsycopgAsyncConfig, PsycopgSyncConfig, PsycopgSyncDriver -from tests.conftest import requires_interpreted ParamStyle = Literal["tuple_binds", "dict_binds", "named_binds"] @@ -288,7 +287,6 @@ def test_psycopg_statement_stack_pipeline(psycopg_session: "PsycopgSyncDriver") assert total_result.get_data()[0]["total"] == 2 -@requires_interpreted def test_psycopg_statement_stack_continue_on_error(psycopg_session: "PsycopgSyncDriver") -> None: """Pipeline execution should continue when instructed to handle errors.""" diff --git a/uv.lock b/uv.lock index 2444adc6f..10b797f1f 100644 --- a/uv.lock +++ b/uv.lock @@ -174,7 +174,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.13.3" +version = "3.13.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -186,110 +186,110 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/36/d6/5aec9313ee6ea9c7cde8b891b69f4ff4001416867104580670a31daeba5b/aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7", size = 738950, upload-time = "2026-01-03T17:29:13.002Z" }, - { url = "https://files.pythonhosted.org/packages/68/03/8fa90a7e6d11ff20a18837a8e2b5dd23db01aabc475aa9271c8ad33299f5/aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821", size = 496099, upload-time = "2026-01-03T17:29:15.268Z" }, - { url = "https://files.pythonhosted.org/packages/d2/23/b81f744d402510a8366b74eb420fc0cc1170d0c43daca12d10814df85f10/aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845", size = 491072, upload-time = "2026-01-03T17:29:16.922Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e1/56d1d1c0dd334cd203dd97706ce004c1aa24b34a813b0b8daf3383039706/aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af", size = 1671588, upload-time = "2026-01-03T17:29:18.539Z" }, - { url = "https://files.pythonhosted.org/packages/5f/34/8d7f962604f4bc2b4e39eb1220dac7d4e4cba91fb9ba0474b4ecd67db165/aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940", size = 1640334, upload-time = "2026-01-03T17:29:21.028Z" }, - { url = "https://files.pythonhosted.org/packages/94/1d/fcccf2c668d87337ddeef9881537baee13c58d8f01f12ba8a24215f2b804/aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160", size = 1722656, upload-time = "2026-01-03T17:29:22.531Z" }, - { url = "https://files.pythonhosted.org/packages/aa/98/c6f3b081c4c606bc1e5f2ec102e87d6411c73a9ef3616fea6f2d5c98c062/aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7", size = 1817625, upload-time = "2026-01-03T17:29:24.276Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c0/cfcc3d2e11b477f86e1af2863f3858c8850d751ce8dc39c4058a072c9e54/aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455", size = 1672604, upload-time = "2026-01-03T17:29:26.099Z" }, - { url = "https://files.pythonhosted.org/packages/1e/77/6b4ffcbcac4c6a5d041343a756f34a6dd26174ae07f977a64fe028dda5b0/aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279", size = 1554370, upload-time = "2026-01-03T17:29:28.121Z" }, - { url = "https://files.pythonhosted.org/packages/f2/f0/e3ddfa93f17d689dbe014ba048f18e0c9f9b456033b70e94349a2e9048be/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e", size = 1642023, upload-time = "2026-01-03T17:29:30.002Z" }, - { url = "https://files.pythonhosted.org/packages/eb/45/c14019c9ec60a8e243d06d601b33dcc4fd92379424bde3021725859d7f99/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d", size = 1649680, upload-time = "2026-01-03T17:29:31.782Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fd/09c9451dae5aa5c5ed756df95ff9ef549d45d4be663bafd1e4954fd836f0/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808", size = 1692407, upload-time = "2026-01-03T17:29:33.392Z" }, - { url = "https://files.pythonhosted.org/packages/a6/81/938bc2ec33c10efd6637ccb3d22f9f3160d08e8f3aa2587a2c2d5ab578eb/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40", size = 1543047, upload-time = "2026-01-03T17:29:34.855Z" }, - { url = "https://files.pythonhosted.org/packages/f7/23/80488ee21c8d567c83045e412e1d9b7077d27171591a4eb7822586e8c06a/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29", size = 1715264, upload-time = "2026-01-03T17:29:36.389Z" }, - { url = "https://files.pythonhosted.org/packages/e2/83/259a8da6683182768200b368120ab3deff5370bed93880fb9a3a86299f34/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11", size = 1657275, upload-time = "2026-01-03T17:29:38.162Z" }, - { url = "https://files.pythonhosted.org/packages/3f/4f/2c41f800a0b560785c10fb316216ac058c105f9be50bdc6a285de88db625/aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd", size = 434053, upload-time = "2026-01-03T17:29:40.074Z" }, - { url = "https://files.pythonhosted.org/packages/80/df/29cd63c7ecfdb65ccc12f7d808cac4fa2a19544660c06c61a4a48462de0c/aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c", size = 456687, upload-time = "2026-01-03T17:29:41.819Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4c/a164164834f03924d9a29dc3acd9e7ee58f95857e0b467f6d04298594ebb/aiohttp-3.13.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b6073099fb654e0a068ae678b10feff95c5cae95bbfcbfa7af669d361a8aa6b", size = 746051, upload-time = "2026-01-03T17:29:43.287Z" }, - { url = "https://files.pythonhosted.org/packages/82/71/d5c31390d18d4f58115037c432b7e0348c60f6f53b727cad33172144a112/aiohttp-3.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cb93e166e6c28716c8c6aeb5f99dfb6d5ccf482d29fe9bf9a794110e6d0ab64", size = 499234, upload-time = "2026-01-03T17:29:44.822Z" }, - { url = "https://files.pythonhosted.org/packages/0e/c9/741f8ac91e14b1d2e7100690425a5b2b919a87a5075406582991fb7de920/aiohttp-3.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28e027cf2f6b641693a09f631759b4d9ce9165099d2b5d92af9bd4e197690eea", size = 494979, upload-time = "2026-01-03T17:29:46.405Z" }, - { url = "https://files.pythonhosted.org/packages/75/b5/31d4d2e802dfd59f74ed47eba48869c1c21552c586d5e81a9d0d5c2ad640/aiohttp-3.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b61b7169ababd7802f9568ed96142616a9118dd2be0d1866e920e77ec8fa92a", size = 1748297, upload-time = "2026-01-03T17:29:48.083Z" }, - { url = "https://files.pythonhosted.org/packages/1a/3e/eefad0ad42959f226bb79664826883f2687d602a9ae2941a18e0484a74d3/aiohttp-3.13.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:80dd4c21b0f6237676449c6baaa1039abae86b91636b6c91a7f8e61c87f89540", size = 1707172, upload-time = "2026-01-03T17:29:49.648Z" }, - { url = "https://files.pythonhosted.org/packages/c5/3a/54a64299fac2891c346cdcf2aa6803f994a2e4beeaf2e5a09dcc54acc842/aiohttp-3.13.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65d2ccb7eabee90ce0503c17716fc77226be026dcc3e65cce859a30db715025b", size = 1805405, upload-time = "2026-01-03T17:29:51.244Z" }, - { url = "https://files.pythonhosted.org/packages/6c/70/ddc1b7169cf64075e864f64595a14b147a895a868394a48f6a8031979038/aiohttp-3.13.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b179331a481cb5529fca8b432d8d3c7001cb217513c94cd72d668d1248688a3", size = 1899449, upload-time = "2026-01-03T17:29:53.938Z" }, - { url = "https://files.pythonhosted.org/packages/a1/7e/6815aab7d3a56610891c76ef79095677b8b5be6646aaf00f69b221765021/aiohttp-3.13.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d4c940f02f49483b18b079d1c27ab948721852b281f8b015c058100e9421dd1", size = 1748444, upload-time = "2026-01-03T17:29:55.484Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f2/073b145c4100da5511f457dc0f7558e99b2987cf72600d42b559db856fbc/aiohttp-3.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9444f105664c4ce47a2a7171a2418bce5b7bae45fb610f4e2c36045d85911d3", size = 1606038, upload-time = "2026-01-03T17:29:57.179Z" }, - { url = "https://files.pythonhosted.org/packages/0a/c1/778d011920cae03ae01424ec202c513dc69243cf2db303965615b81deeea/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:694976222c711d1d00ba131904beb60534f93966562f64440d0c9d41b8cdb440", size = 1724156, upload-time = "2026-01-03T17:29:58.914Z" }, - { url = "https://files.pythonhosted.org/packages/0e/cb/3419eabf4ec1e9ec6f242c32b689248365a1cf621891f6f0386632525494/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f33ed1a2bf1997a36661874b017f5c4b760f41266341af36febaf271d179f6d7", size = 1722340, upload-time = "2026-01-03T17:30:01.962Z" }, - { url = "https://files.pythonhosted.org/packages/7a/e5/76cf77bdbc435bf233c1f114edad39ed4177ccbfab7c329482b179cff4f4/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e636b3c5f61da31a92bf0d91da83e58fdfa96f178ba682f11d24f31944cdd28c", size = 1783041, upload-time = "2026-01-03T17:30:03.609Z" }, - { url = "https://files.pythonhosted.org/packages/9d/d4/dd1ca234c794fd29c057ce8c0566b8ef7fd6a51069de5f06fa84b9a1971c/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5d2d94f1f5fcbe40838ac51a6ab5704a6f9ea42e72ceda48de5e6b898521da51", size = 1596024, upload-time = "2026-01-03T17:30:05.132Z" }, - { url = "https://files.pythonhosted.org/packages/55/58/4345b5f26661a6180afa686c473620c30a66afdf120ed3dd545bbc809e85/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2be0e9ccf23e8a94f6f0650ce06042cefc6ac703d0d7ab6c7a917289f2539ad4", size = 1804590, upload-time = "2026-01-03T17:30:07.135Z" }, - { url = "https://files.pythonhosted.org/packages/7b/06/05950619af6c2df7e0a431d889ba2813c9f0129cec76f663e547a5ad56f2/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9af5e68ee47d6534d36791bbe9b646d2a7c7deb6fc24d7943628edfbb3581f29", size = 1740355, upload-time = "2026-01-03T17:30:09.083Z" }, - { url = "https://files.pythonhosted.org/packages/3e/80/958f16de79ba0422d7c1e284b2abd0c84bc03394fbe631d0a39ffa10e1eb/aiohttp-3.13.3-cp311-cp311-win32.whl", hash = "sha256:a2212ad43c0833a873d0fb3c63fa1bacedd4cf6af2fee62bf4b739ceec3ab239", size = 433701, upload-time = "2026-01-03T17:30:10.869Z" }, - { url = "https://files.pythonhosted.org/packages/dc/f2/27cdf04c9851712d6c1b99df6821a6623c3c9e55956d4b1e318c337b5a48/aiohttp-3.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:642f752c3eb117b105acbd87e2c143de710987e09860d674e068c4c2c441034f", size = 457678, upload-time = "2026-01-03T17:30:12.719Z" }, - { url = "https://files.pythonhosted.org/packages/a0/be/4fc11f202955a69e0db803a12a062b8379c970c7c84f4882b6da17337cc1/aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", size = 739732, upload-time = "2026-01-03T17:30:14.23Z" }, - { url = "https://files.pythonhosted.org/packages/97/2c/621d5b851f94fa0bb7430d6089b3aa970a9d9b75196bc93bb624b0db237a/aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", size = 494293, upload-time = "2026-01-03T17:30:15.96Z" }, - { url = "https://files.pythonhosted.org/packages/5d/43/4be01406b78e1be8320bb8316dc9c42dbab553d281c40364e0f862d5661c/aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", size = 493533, upload-time = "2026-01-03T17:30:17.431Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a8/5a35dc56a06a2c90d4742cbf35294396907027f80eea696637945a106f25/aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", size = 1737839, upload-time = "2026-01-03T17:30:19.422Z" }, - { url = "https://files.pythonhosted.org/packages/bf/62/4b9eeb331da56530bf2e198a297e5303e1c1ebdceeb00fe9b568a65c5a0c/aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", size = 1703932, upload-time = "2026-01-03T17:30:21.756Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f6/af16887b5d419e6a367095994c0b1332d154f647e7dc2bd50e61876e8e3d/aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", size = 1771906, upload-time = "2026-01-03T17:30:23.932Z" }, - { url = "https://files.pythonhosted.org/packages/ce/83/397c634b1bcc24292fa1e0c7822800f9f6569e32934bdeef09dae7992dfb/aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", size = 1871020, upload-time = "2026-01-03T17:30:26Z" }, - { url = "https://files.pythonhosted.org/packages/86/f6/a62cbbf13f0ac80a70f71b1672feba90fdb21fd7abd8dbf25c0105fb6fa3/aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", size = 1755181, upload-time = "2026-01-03T17:30:27.554Z" }, - { url = "https://files.pythonhosted.org/packages/0a/87/20a35ad487efdd3fba93d5843efdfaa62d2f1479eaafa7453398a44faf13/aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", size = 1561794, upload-time = "2026-01-03T17:30:29.254Z" }, - { url = "https://files.pythonhosted.org/packages/de/95/8fd69a66682012f6716e1bc09ef8a1a2a91922c5725cb904689f112309c4/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", size = 1697900, upload-time = "2026-01-03T17:30:31.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/66/7b94b3b5ba70e955ff597672dad1691333080e37f50280178967aff68657/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", size = 1728239, upload-time = "2026-01-03T17:30:32.703Z" }, - { url = "https://files.pythonhosted.org/packages/47/71/6f72f77f9f7d74719692ab65a2a0252584bf8d5f301e2ecb4c0da734530a/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", size = 1740527, upload-time = "2026-01-03T17:30:34.695Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b4/75ec16cbbd5c01bdaf4a05b19e103e78d7ce1ef7c80867eb0ace42ff4488/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", size = 1554489, upload-time = "2026-01-03T17:30:36.864Z" }, - { url = "https://files.pythonhosted.org/packages/52/8f/bc518c0eea29f8406dcf7ed1f96c9b48e3bc3995a96159b3fc11f9e08321/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", size = 1767852, upload-time = "2026-01-03T17:30:39.433Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f2/a07a75173124f31f11ea6f863dc44e6f09afe2bca45dd4e64979490deab1/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", size = 1722379, upload-time = "2026-01-03T17:30:41.081Z" }, - { url = "https://files.pythonhosted.org/packages/3c/4a/1a3fee7c21350cac78e5c5cef711bac1b94feca07399f3d406972e2d8fcd/aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046", size = 428253, upload-time = "2026-01-03T17:30:42.644Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b7/76175c7cb4eb73d91ad63c34e29fc4f77c9386bba4a65b53ba8e05ee3c39/aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57", size = 455407, upload-time = "2026-01-03T17:30:44.195Z" }, - { url = "https://files.pythonhosted.org/packages/97/8a/12ca489246ca1faaf5432844adbfce7ff2cc4997733e0af120869345643a/aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c", size = 734190, upload-time = "2026-01-03T17:30:45.832Z" }, - { url = "https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9", size = 491783, upload-time = "2026-01-03T17:30:47.466Z" }, - { url = "https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3", size = 490704, upload-time = "2026-01-03T17:30:49.373Z" }, - { url = "https://files.pythonhosted.org/packages/6d/40/a46b03ca03936f832bc7eaa47cfbb1ad012ba1be4790122ee4f4f8cba074/aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf", size = 1720652, upload-time = "2026-01-03T17:30:50.974Z" }, - { url = "https://files.pythonhosted.org/packages/f7/7e/917fe18e3607af92657e4285498f500dca797ff8c918bd7d90b05abf6c2a/aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6", size = 1692014, upload-time = "2026-01-03T17:30:52.729Z" }, - { url = "https://files.pythonhosted.org/packages/71/b6/cefa4cbc00d315d68973b671cf105b21a609c12b82d52e5d0c9ae61d2a09/aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d", size = 1759777, upload-time = "2026-01-03T17:30:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/fb/e3/e06ee07b45e59e6d81498b591fc589629be1553abb2a82ce33efe2a7b068/aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261", size = 1861276, upload-time = "2026-01-03T17:30:56.512Z" }, - { url = "https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0", size = 1743131, upload-time = "2026-01-03T17:30:58.256Z" }, - { url = "https://files.pythonhosted.org/packages/04/98/3d21dde21889b17ca2eea54fdcff21b27b93f45b7bb94ca029c31ab59dc3/aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730", size = 1556863, upload-time = "2026-01-03T17:31:00.445Z" }, - { url = "https://files.pythonhosted.org/packages/9e/84/da0c3ab1192eaf64782b03971ab4055b475d0db07b17eff925e8c93b3aa5/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91", size = 1682793, upload-time = "2026-01-03T17:31:03.024Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0f/5802ada182f575afa02cbd0ec5180d7e13a402afb7c2c03a9aa5e5d49060/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3", size = 1716676, upload-time = "2026-01-03T17:31:04.842Z" }, - { url = "https://files.pythonhosted.org/packages/3f/8c/714d53bd8b5a4560667f7bbbb06b20c2382f9c7847d198370ec6526af39c/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4", size = 1733217, upload-time = "2026-01-03T17:31:06.868Z" }, - { url = "https://files.pythonhosted.org/packages/7d/79/e2176f46d2e963facea939f5be2d26368ce543622be6f00a12844d3c991f/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998", size = 1552303, upload-time = "2026-01-03T17:31:08.958Z" }, - { url = "https://files.pythonhosted.org/packages/ab/6a/28ed4dea1759916090587d1fe57087b03e6c784a642b85ef48217b0277ae/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0", size = 1763673, upload-time = "2026-01-03T17:31:10.676Z" }, - { url = "https://files.pythonhosted.org/packages/e8/35/4a3daeb8b9fab49240d21c04d50732313295e4bd813a465d840236dd0ce1/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591", size = 1721120, upload-time = "2026-01-03T17:31:12.575Z" }, - { url = "https://files.pythonhosted.org/packages/bc/9f/d643bb3c5fb99547323e635e251c609fbbc660d983144cfebec529e09264/aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf", size = 427383, upload-time = "2026-01-03T17:31:14.382Z" }, - { url = "https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e", size = 453899, upload-time = "2026-01-03T17:31:15.958Z" }, - { url = "https://files.pythonhosted.org/packages/99/36/5b6514a9f5d66f4e2597e40dea2e3db271e023eb7a5d22defe96ba560996/aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808", size = 737238, upload-time = "2026-01-03T17:31:17.909Z" }, - { url = "https://files.pythonhosted.org/packages/f7/49/459327f0d5bcd8c6c9ca69e60fdeebc3622861e696490d8674a6d0cb90a6/aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415", size = 492292, upload-time = "2026-01-03T17:31:19.919Z" }, - { url = "https://files.pythonhosted.org/packages/e8/0b/b97660c5fd05d3495b4eb27f2d0ef18dc1dc4eff7511a9bf371397ff0264/aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f", size = 493021, upload-time = "2026-01-03T17:31:21.636Z" }, - { url = "https://files.pythonhosted.org/packages/54/d4/438efabdf74e30aeceb890c3290bbaa449780583b1270b00661126b8aae4/aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6", size = 1717263, upload-time = "2026-01-03T17:31:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/71/f2/7bddc7fd612367d1459c5bcf598a9e8f7092d6580d98de0e057eb42697ad/aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687", size = 1669107, upload-time = "2026-01-03T17:31:25.334Z" }, - { url = "https://files.pythonhosted.org/packages/00/5a/1aeaecca40e22560f97610a329e0e5efef5e0b5afdf9f857f0d93839ab2e/aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26", size = 1760196, upload-time = "2026-01-03T17:31:27.394Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f8/0ff6992bea7bd560fc510ea1c815f87eedd745fe035589c71ce05612a19a/aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a", size = 1843591, upload-time = "2026-01-03T17:31:29.238Z" }, - { url = "https://files.pythonhosted.org/packages/e3/d1/e30e537a15f53485b61f5be525f2157da719819e8377298502aebac45536/aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1", size = 1720277, upload-time = "2026-01-03T17:31:31.053Z" }, - { url = "https://files.pythonhosted.org/packages/84/45/23f4c451d8192f553d38d838831ebbc156907ea6e05557f39563101b7717/aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25", size = 1548575, upload-time = "2026-01-03T17:31:32.87Z" }, - { url = "https://files.pythonhosted.org/packages/6a/ed/0a42b127a43712eda7807e7892c083eadfaf8429ca8fb619662a530a3aab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603", size = 1679455, upload-time = "2026-01-03T17:31:34.76Z" }, - { url = "https://files.pythonhosted.org/packages/2e/b5/c05f0c2b4b4fe2c9d55e73b6d3ed4fd6c9dc2684b1d81cbdf77e7fad9adb/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a", size = 1687417, upload-time = "2026-01-03T17:31:36.699Z" }, - { url = "https://files.pythonhosted.org/packages/c9/6b/915bc5dad66aef602b9e459b5a973529304d4e89ca86999d9d75d80cbd0b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926", size = 1729968, upload-time = "2026-01-03T17:31:38.622Z" }, - { url = "https://files.pythonhosted.org/packages/11/3b/e84581290a9520024a08640b63d07673057aec5ca548177a82026187ba73/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba", size = 1545690, upload-time = "2026-01-03T17:31:40.57Z" }, - { url = "https://files.pythonhosted.org/packages/f5/04/0c3655a566c43fd647c81b895dfe361b9f9ad6d58c19309d45cff52d6c3b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c", size = 1746390, upload-time = "2026-01-03T17:31:42.857Z" }, - { url = "https://files.pythonhosted.org/packages/1f/53/71165b26978f719c3419381514c9690bd5980e764a09440a10bb816ea4ab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43", size = 1702188, upload-time = "2026-01-03T17:31:44.984Z" }, - { url = "https://files.pythonhosted.org/packages/29/a7/cbe6c9e8e136314fa1980da388a59d2f35f35395948a08b6747baebb6aa6/aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1", size = 433126, upload-time = "2026-01-03T17:31:47.463Z" }, - { url = "https://files.pythonhosted.org/packages/de/56/982704adea7d3b16614fc5936014e9af85c0e34b58f9046655817f04306e/aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984", size = 459128, upload-time = "2026-01-03T17:31:49.2Z" }, - { url = "https://files.pythonhosted.org/packages/6c/2a/3c79b638a9c3d4658d345339d22070241ea341ed4e07b5ac60fb0f418003/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c", size = 769512, upload-time = "2026-01-03T17:31:51.134Z" }, - { url = "https://files.pythonhosted.org/packages/29/b9/3e5014d46c0ab0db8707e0ac2711ed28c4da0218c358a4e7c17bae0d8722/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592", size = 506444, upload-time = "2026-01-03T17:31:52.85Z" }, - { url = "https://files.pythonhosted.org/packages/90/03/c1d4ef9a054e151cd7839cdc497f2638f00b93cbe8043983986630d7a80c/aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f", size = 510798, upload-time = "2026-01-03T17:31:54.91Z" }, - { url = "https://files.pythonhosted.org/packages/ea/76/8c1e5abbfe8e127c893fe7ead569148a4d5a799f7cf958d8c09f3eedf097/aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29", size = 1868835, upload-time = "2026-01-03T17:31:56.733Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ac/984c5a6f74c363b01ff97adc96a3976d9c98940b8969a1881575b279ac5d/aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc", size = 1720486, upload-time = "2026-01-03T17:31:58.65Z" }, - { url = "https://files.pythonhosted.org/packages/b2/9a/b7039c5f099c4eb632138728828b33428585031a1e658d693d41d07d89d1/aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2", size = 1847951, upload-time = "2026-01-03T17:32:00.989Z" }, - { url = "https://files.pythonhosted.org/packages/3c/02/3bec2b9a1ba3c19ff89a43a19324202b8eb187ca1e928d8bdac9bbdddebd/aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587", size = 1941001, upload-time = "2026-01-03T17:32:03.122Z" }, - { url = "https://files.pythonhosted.org/packages/37/df/d879401cedeef27ac4717f6426c8c36c3091c6e9f08a9178cc87549c537f/aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8", size = 1797246, upload-time = "2026-01-03T17:32:05.255Z" }, - { url = "https://files.pythonhosted.org/packages/8d/15/be122de1f67e6953add23335c8ece6d314ab67c8bebb3f181063010795a7/aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632", size = 1627131, upload-time = "2026-01-03T17:32:07.607Z" }, - { url = "https://files.pythonhosted.org/packages/12/12/70eedcac9134cfa3219ab7af31ea56bc877395b1ac30d65b1bc4b27d0438/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64", size = 1795196, upload-time = "2026-01-03T17:32:09.59Z" }, - { url = "https://files.pythonhosted.org/packages/32/11/b30e1b1cd1f3054af86ebe60df96989c6a414dd87e27ad16950eee420bea/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0", size = 1782841, upload-time = "2026-01-03T17:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/88/0d/d98a9367b38912384a17e287850f5695c528cff0f14f791ce8ee2e4f7796/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56", size = 1795193, upload-time = "2026-01-03T17:32:13.705Z" }, - { url = "https://files.pythonhosted.org/packages/43/a5/a2dfd1f5ff5581632c7f6a30e1744deda03808974f94f6534241ef60c751/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72", size = 1621979, upload-time = "2026-01-03T17:32:15.965Z" }, - { url = "https://files.pythonhosted.org/packages/fa/f0/12973c382ae7c1cccbc4417e129c5bf54c374dfb85af70893646e1f0e749/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df", size = 1822193, upload-time = "2026-01-03T17:32:18.219Z" }, - { url = "https://files.pythonhosted.org/packages/3c/5f/24155e30ba7f8c96918af1350eb0663e2430aad9e001c0489d89cd708ab1/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa", size = 1769801, upload-time = "2026-01-03T17:32:20.25Z" }, - { url = "https://files.pythonhosted.org/packages/eb/f8/7314031ff5c10e6ece114da79b338ec17eeff3a079e53151f7e9f43c4723/aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767", size = 466523, upload-time = "2026-01-03T17:32:22.215Z" }, - { url = "https://files.pythonhosted.org/packages/b4/63/278a98c715ae467624eafe375542d8ba9b4383a016df8fdefe0ae28382a7/aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", size = 499694, upload-time = "2026-01-03T17:32:24.546Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/45/4a/064321452809dae953c1ed6e017504e72551a26b6f5708a5a80e4bf556ff/aiohttp-3.13.4.tar.gz", hash = "sha256:d97a6d09c66087890c2ab5d49069e1e570583f7ac0314ecf98294c1b6aaebd38", size = 7859748, upload-time = "2026-03-28T17:19:40.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/05/6817e0390eb47b0867cf8efdb535298191662192281bc3ca62a0cb7973eb/aiohttp-3.13.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6290fe12fe8cefa6ea3c1c5b969d32c010dfe191d4392ff9b599a3f473cbe722", size = 753094, upload-time = "2026-03-28T17:14:59.928Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c1/e5b7f25f6dd1ab57da92aa9d226b2c8b56f223dd20475d3ddfddaba86ab8/aiohttp-3.13.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7520d92c0e8fbbe63f36f20a5762db349ff574ad38ad7bc7732558a650439845", size = 505213, upload-time = "2026-03-28T17:15:01.989Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e5/8f42033c7ce98b54dfd3791f03e60231cfe4a2db4471b5fc188df2b8a6ad/aiohttp-3.13.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d2710ae1e1b81d0f187883b6e9d66cecf8794b50e91aa1e73fc78bfb5503b5d9", size = 498580, upload-time = "2026-03-28T17:15:03.879Z" }, + { url = "https://files.pythonhosted.org/packages/8c/a4/bbc989f5362066b81930da1a66084a859a971d03faab799dc59a3ce3a220/aiohttp-3.13.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:717d17347567ded1e273aa09918650dfd6fd06f461549204570c7973537d4123", size = 1692718, upload-time = "2026-03-28T17:15:05.541Z" }, + { url = "https://files.pythonhosted.org/packages/1c/72/3775116969931f151be116689d2ae6ddafff2ec2887d8f9b4e7043f32e74/aiohttp-3.13.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:383880f7b8de5ac208fa829c7038d08e66377283b2de9e791b71e06e803153c2", size = 1660714, upload-time = "2026-03-28T17:15:08.23Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e8/d2f1a2da2743e32fe348ebf8a4c59caad14a92f5f18af616fd33381275e1/aiohttp-3.13.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1867087e2c1963db1216aedf001efe3b129835ed2b05d97d058176a6d08b5726", size = 1744152, upload-time = "2026-03-28T17:15:10.828Z" }, + { url = "https://files.pythonhosted.org/packages/4c/a6/575886f417ac3c08e462f2ca237cc49f436bd992ca3f7ff95b7dd9c44205/aiohttp-3.13.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6234bf416a38d687c3ab7f79934d7fb2a42117a5b9813aca07de0a5398489023", size = 1836278, upload-time = "2026-03-28T17:15:12.537Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4c/0051d4550fb9e8b5ca4e0fe1ccd58652340915180c5164999e6741bf2083/aiohttp-3.13.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3cdd3393130bf6588962441ffd5bde1d3ea2d63a64afa7119b3f3ba349cebbe7", size = 1687953, upload-time = "2026-03-28T17:15:14.248Z" }, + { url = "https://files.pythonhosted.org/packages/c9/54/841e87b8c51c2adc01a3ceb9919dc45c7899fe4c21deb70aada734ea5a38/aiohttp-3.13.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0d0dbc6c76befa76865373d6aa303e480bb8c3486e7763530f7f6e527b471118", size = 1572484, upload-time = "2026-03-28T17:15:15.911Z" }, + { url = "https://files.pythonhosted.org/packages/da/f1/21cbf5f7fa1e267af6301f886cab9b314f085e4d0097668d189d165cd7da/aiohttp-3.13.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10fb7b53262cf4144a083c9db0d2b4d22823d6708270a9970c4627b248c6064c", size = 1662851, upload-time = "2026-03-28T17:15:17.822Z" }, + { url = "https://files.pythonhosted.org/packages/40/15/bcad6b68d7bef27ae7443288215767263c7753ede164267cf6cf63c94a87/aiohttp-3.13.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:eb10ce8c03850e77f4d9518961c227be569e12f71525a7e90d17bca04299921d", size = 1671984, upload-time = "2026-03-28T17:15:19.561Z" }, + { url = "https://files.pythonhosted.org/packages/ff/fa/ab316931afc7a73c7f493bb1b30fbd61e28ec2d3ea50353336e76293e8ec/aiohttp-3.13.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:7c65738ac5ae32b8feef699a4ed0dc91a0c8618b347781b7461458bbcaaac7eb", size = 1713880, upload-time = "2026-03-28T17:15:21.589Z" }, + { url = "https://files.pythonhosted.org/packages/1c/45/314e8e64c7f328174964b6db511dd5e9e60c9121ab5457bc2c908b7d03a4/aiohttp-3.13.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:6b335919ffbaf98df8ff3c74f7a6decb8775882632952fd1810a017e38f15aee", size = 1560315, upload-time = "2026-03-28T17:15:23.66Z" }, + { url = "https://files.pythonhosted.org/packages/18/e7/93d5fa06fe00219a81466577dacae9e3732f3b4f767b12b2e2cc8c35c970/aiohttp-3.13.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ec75fc18cb9f4aca51c2cbace20cf6716e36850f44189644d2d69a875d5e0532", size = 1735115, upload-time = "2026-03-28T17:15:25.77Z" }, + { url = "https://files.pythonhosted.org/packages/19/9f/f64b95392ddd4e204fd9ab7cd33dd18d14ac9e4b86866f1f6a69b7cda83d/aiohttp-3.13.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:463fa18a95c5a635d2b8c09babe240f9d7dbf2a2010a6c0b35d8c4dff2a0e819", size = 1673916, upload-time = "2026-03-28T17:15:27.526Z" }, + { url = "https://files.pythonhosted.org/packages/52/c1/bb33be79fd285c69f32e5b074b299cae8847f748950149c3965c1b3b3adf/aiohttp-3.13.4-cp310-cp310-win32.whl", hash = "sha256:13168f5645d9045522c6cef818f54295376257ed8d02513a37c2ef3046fc7a97", size = 440277, upload-time = "2026-03-28T17:15:29.173Z" }, + { url = "https://files.pythonhosted.org/packages/23/f9/7cf1688da4dd0885f914ee40bc8e1dce776df98fe6518766de975a570538/aiohttp-3.13.4-cp310-cp310-win_amd64.whl", hash = "sha256:a7058af1f53209fdf07745579ced525d38d481650a989b7aa4a3b484b901cdab", size = 463015, upload-time = "2026-03-28T17:15:30.802Z" }, + { url = "https://files.pythonhosted.org/packages/d4/7e/cb94129302d78c46662b47f9897d642fd0b33bdfef4b73b20c6ced35aa4c/aiohttp-3.13.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8ea0c64d1bcbf201b285c2246c51a0c035ba3bbd306640007bc5844a3b4658c1", size = 760027, upload-time = "2026-03-28T17:15:33.022Z" }, + { url = "https://files.pythonhosted.org/packages/5e/cd/2db3c9397c3bd24216b203dd739945b04f8b87bb036c640da7ddb63c75ef/aiohttp-3.13.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6f742e1fa45c0ed522b00ede565e18f97e4cf8d1883a712ac42d0339dfb0cce7", size = 508325, upload-time = "2026-03-28T17:15:34.714Z" }, + { url = "https://files.pythonhosted.org/packages/36/a3/d28b2722ec13107f2e37a86b8a169897308bab6a3b9e071ecead9d67bd9b/aiohttp-3.13.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dcfb50ee25b3b7a1222a9123be1f9f89e56e67636b561441f0b304e25aaef8f", size = 502402, upload-time = "2026-03-28T17:15:36.409Z" }, + { url = "https://files.pythonhosted.org/packages/fa/d6/acd47b5f17c4430e555590990a4746efbcb2079909bb865516892bf85f37/aiohttp-3.13.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3262386c4ff370849863ea93b9ea60fd59c6cf56bf8f93beac625cf4d677c04d", size = 1771224, upload-time = "2026-03-28T17:15:38.223Z" }, + { url = "https://files.pythonhosted.org/packages/98/af/af6e20113ba6a48fd1cd9e5832c4851e7613ef50c7619acdaee6ec5f1aff/aiohttp-3.13.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:473bb5aa4218dd254e9ae4834f20e31f5a0083064ac0136a01a62ddbae2eaa42", size = 1731530, upload-time = "2026-03-28T17:15:39.988Z" }, + { url = "https://files.pythonhosted.org/packages/81/16/78a2f5d9c124ad05d5ce59a9af94214b6466c3491a25fb70760e98e9f762/aiohttp-3.13.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e56423766399b4c77b965f6aaab6c9546617b8994a956821cc507d00b91d978c", size = 1827925, upload-time = "2026-03-28T17:15:41.944Z" }, + { url = "https://files.pythonhosted.org/packages/2a/1f/79acf0974ced805e0e70027389fccbb7d728e6f30fcac725fb1071e63075/aiohttp-3.13.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8af249343fafd5ad90366a16d230fc265cf1149f26075dc9fe93cfd7c7173942", size = 1923579, upload-time = "2026-03-28T17:15:44.071Z" }, + { url = "https://files.pythonhosted.org/packages/af/53/29f9e2054ea6900413f3b4c3eb9d8331f60678ec855f13ba8714c47fd48d/aiohttp-3.13.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bc0a5cf4f10ef5a2c94fdde488734b582a3a7a000b131263e27c9295bd682d9", size = 1767655, upload-time = "2026-03-28T17:15:45.911Z" }, + { url = "https://files.pythonhosted.org/packages/f3/57/462fe1d3da08109ba4aa8590e7aed57c059af2a7e80ec21f4bac5cfe1094/aiohttp-3.13.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5c7ff1028e3c9fc5123a865ce17df1cb6424d180c503b8517afbe89aa566e6be", size = 1630439, upload-time = "2026-03-28T17:15:48.11Z" }, + { url = "https://files.pythonhosted.org/packages/d7/4b/4813344aacdb8127263e3eec343d24e973421143826364fa9fc847f6283f/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ba5cf98b5dcb9bddd857da6713a503fa6d341043258ca823f0f5ab7ab4a94ee8", size = 1745557, upload-time = "2026-03-28T17:15:50.13Z" }, + { url = "https://files.pythonhosted.org/packages/d4/01/1ef1adae1454341ec50a789f03cfafe4c4ac9c003f6a64515ecd32fe4210/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d85965d3ba21ee4999e83e992fecb86c4614d6920e40705501c0a1f80a583c12", size = 1741796, upload-time = "2026-03-28T17:15:52.351Z" }, + { url = "https://files.pythonhosted.org/packages/22/04/8cdd99af988d2aa6922714d957d21383c559835cbd43fbf5a47ddf2e0f05/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:49f0b18a9b05d79f6f37ddd567695943fcefb834ef480f17a4211987302b2dc7", size = 1805312, upload-time = "2026-03-28T17:15:54.407Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7f/b48d5577338d4b25bbdbae35c75dbfd0493cb8886dc586fbfb2e90862239/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7f78cb080c86fbf765920e5f1ef35af3f24ec4314d6675d0a21eaf41f6f2679c", size = 1621751, upload-time = "2026-03-28T17:15:56.564Z" }, + { url = "https://files.pythonhosted.org/packages/bc/89/4eecad8c1858e6d0893c05929e22343e0ebe3aec29a8a399c65c3cc38311/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:67a3ec705534a614b68bbf1c70efa777a21c3da3895d1c44510a41f5a7ae0453", size = 1826073, upload-time = "2026-03-28T17:15:58.489Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5c/9dc8293ed31b46c39c9c513ac7ca152b3c3d38e0ea111a530ad12001b827/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d6630ec917e85c5356b2295744c8a97d40f007f96a1c76bf1928dc2e27465393", size = 1760083, upload-time = "2026-03-28T17:16:00.677Z" }, + { url = "https://files.pythonhosted.org/packages/1e/19/8bbf6a4994205d96831f97b7d21a0feed120136e6267b5b22d229c6dc4dc/aiohttp-3.13.4-cp311-cp311-win32.whl", hash = "sha256:54049021bc626f53a5394c29e8c444f726ee5a14b6e89e0ad118315b1f90f5e3", size = 439690, upload-time = "2026-03-28T17:16:02.902Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f5/ac409ecd1007528d15c3e8c3a57d34f334c70d76cfb7128a28cffdebd4c1/aiohttp-3.13.4-cp311-cp311-win_amd64.whl", hash = "sha256:c033f2bc964156030772d31cbf7e5defea181238ce1f87b9455b786de7d30145", size = 463824, upload-time = "2026-03-28T17:16:05.058Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bd/ede278648914cabbabfdf95e436679b5d4156e417896a9b9f4587169e376/aiohttp-3.13.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ee62d4471ce86b108b19c3364db4b91180d13fe3510144872d6bad5401957360", size = 752158, upload-time = "2026-03-28T17:16:06.901Z" }, + { url = "https://files.pythonhosted.org/packages/90/de/581c053253c07b480b03785196ca5335e3c606a37dc73e95f6527f1591fe/aiohttp-3.13.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c0fd8f41b54b58636402eb493afd512c23580456f022c1ba2db0f810c959ed0d", size = 501037, upload-time = "2026-03-28T17:16:08.82Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f9/a5ede193c08f13cc42c0a5b50d1e246ecee9115e4cf6e900d8dbd8fd6acb/aiohttp-3.13.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4baa48ce49efd82d6b1a0be12d6a36b35e5594d1dd42f8bfba96ea9f8678b88c", size = 501556, upload-time = "2026-03-28T17:16:10.63Z" }, + { url = "https://files.pythonhosted.org/packages/d6/10/88ff67cd48a6ec36335b63a640abe86135791544863e0cfe1f065d6cef7a/aiohttp-3.13.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d738ebab9f71ee652d9dbd0211057690022201b11197f9a7324fd4dba128aa97", size = 1757314, upload-time = "2026-03-28T17:16:12.498Z" }, + { url = "https://files.pythonhosted.org/packages/8b/15/fdb90a5cf5a1f52845c276e76298c75fbbcc0ac2b4a86551906d54529965/aiohttp-3.13.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0ce692c3468fa831af7dceed52edf51ac348cebfc8d3feb935927b63bd3e8576", size = 1731819, upload-time = "2026-03-28T17:16:14.558Z" }, + { url = "https://files.pythonhosted.org/packages/ec/df/28146785a007f7820416be05d4f28cc207493efd1e8c6c1068e9bdc29198/aiohttp-3.13.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8e08abcfe752a454d2cb89ff0c08f2d1ecd057ae3e8cc6d84638de853530ebab", size = 1793279, upload-time = "2026-03-28T17:16:16.594Z" }, + { url = "https://files.pythonhosted.org/packages/10/47/689c743abf62ea7a77774d5722f220e2c912a77d65d368b884d9779ef41b/aiohttp-3.13.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5977f701b3fff36367a11087f30ea73c212e686d41cd363c50c022d48b011d8d", size = 1891082, upload-time = "2026-03-28T17:16:18.71Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b6/f7f4f318c7e58c23b761c9b13b9a3c9b394e0f9d5d76fbc6622fa98509f6/aiohttp-3.13.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54203e10405c06f8b6020bd1e076ae0fe6c194adcee12a5a78af3ffa3c57025e", size = 1773938, upload-time = "2026-03-28T17:16:21.125Z" }, + { url = "https://files.pythonhosted.org/packages/aa/06/f207cb3121852c989586a6fc16ff854c4fcc8651b86c5d3bd1fc83057650/aiohttp-3.13.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:358a6af0145bc4dda037f13167bef3cce54b132087acc4c295c739d05d16b1c3", size = 1579548, upload-time = "2026-03-28T17:16:23.588Z" }, + { url = "https://files.pythonhosted.org/packages/6c/58/e1289661a32161e24c1fe479711d783067210d266842523752869cc1d9c2/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:898ea1850656d7d61832ef06aa9846ab3ddb1621b74f46de78fbc5e1a586ba83", size = 1714669, upload-time = "2026-03-28T17:16:25.713Z" }, + { url = "https://files.pythonhosted.org/packages/96/0a/3e86d039438a74a86e6a948a9119b22540bae037d6ba317a042ae3c22711/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7bc30cceb710cf6a44e9617e43eebb6e3e43ad855a34da7b4b6a73537d8a6763", size = 1754175, upload-time = "2026-03-28T17:16:28.18Z" }, + { url = "https://files.pythonhosted.org/packages/f4/30/e717fc5df83133ba467a560b6d8ef20197037b4bb5d7075b90037de1018e/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4a31c0c587a8a038f19a4c7e60654a6c899c9de9174593a13e7cc6e15ff271f9", size = 1762049, upload-time = "2026-03-28T17:16:30.941Z" }, + { url = "https://files.pythonhosted.org/packages/e4/28/8f7a2d4492e336e40005151bdd94baf344880a4707573378579f833a64c1/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2062f675f3fe6e06d6113eb74a157fb9df58953ffed0cdb4182554b116545758", size = 1570861, upload-time = "2026-03-28T17:16:32.953Z" }, + { url = "https://files.pythonhosted.org/packages/78/45/12e1a3d0645968b1c38de4b23fdf270b8637735ea057d4f84482ff918ad9/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d1ba8afb847ff80626d5e408c1fdc99f942acc877d0702fe137015903a220a9", size = 1790003, upload-time = "2026-03-28T17:16:35.468Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0f/60374e18d590de16dcb39d6ff62f39c096c1b958e6f37727b5870026ea30/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b08149419994cdd4d5eecf7fd4bc5986b5a9380285bcd01ab4c0d6bfca47b79d", size = 1737289, upload-time = "2026-03-28T17:16:38.187Z" }, + { url = "https://files.pythonhosted.org/packages/02/bf/535e58d886cfbc40a8b0013c974afad24ef7632d645bca0b678b70033a60/aiohttp-3.13.4-cp312-cp312-win32.whl", hash = "sha256:fc432f6a2c4f720180959bc19aa37259651c1a4ed8af8afc84dd41c60f15f791", size = 434185, upload-time = "2026-03-28T17:16:40.735Z" }, + { url = "https://files.pythonhosted.org/packages/1e/1a/d92e3325134ebfff6f4069f270d3aac770d63320bd1fcd0eca023e74d9a8/aiohttp-3.13.4-cp312-cp312-win_amd64.whl", hash = "sha256:6148c9ae97a3e8bff9a1fc9c757fa164116f86c100468339730e717590a3fb77", size = 461285, upload-time = "2026-03-28T17:16:42.713Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ac/892f4162df9b115b4758d615f32ec63d00f3084c705ff5526630887b9b42/aiohttp-3.13.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:63dd5e5b1e43b8fb1e91b79b7ceba1feba588b317d1edff385084fcc7a0a4538", size = 745744, upload-time = "2026-03-28T17:16:44.67Z" }, + { url = "https://files.pythonhosted.org/packages/97/a9/c5b87e4443a2f0ea88cb3000c93a8fdad1ee63bffc9ded8d8c8e0d66efc6/aiohttp-3.13.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:746ac3cc00b5baea424dacddea3ec2c2702f9590de27d837aa67004db1eebc6e", size = 498178, upload-time = "2026-03-28T17:16:46.766Z" }, + { url = "https://files.pythonhosted.org/packages/94/42/07e1b543a61250783650df13da8ddcdc0d0a5538b2bd15cef6e042aefc61/aiohttp-3.13.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bda8f16ea99d6a6705e5946732e48487a448be874e54a4f73d514660ff7c05d3", size = 498331, upload-time = "2026-03-28T17:16:48.9Z" }, + { url = "https://files.pythonhosted.org/packages/20/d6/492f46bf0328534124772d0cf58570acae5b286ea25006900650f69dae0e/aiohttp-3.13.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b061e7b5f840391e3f64d0ddf672973e45c4cfff7a0feea425ea24e51530fc2", size = 1744414, upload-time = "2026-03-28T17:16:50.968Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4d/e02627b2683f68051246215d2d62b2d2f249ff7a285e7a858dc47d6b6a14/aiohttp-3.13.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b252e8d5cd66184b570d0d010de742736e8a4fab22c58299772b0c5a466d4b21", size = 1719226, upload-time = "2026-03-28T17:16:53.173Z" }, + { url = "https://files.pythonhosted.org/packages/7b/6c/5d0a3394dd2b9f9aeba6e1b6065d0439e4b75d41f1fb09a3ec010b43552b/aiohttp-3.13.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:20af8aad61d1803ff11152a26146d8d81c266aa8c5aa9b4504432abb965c36a0", size = 1782110, upload-time = "2026-03-28T17:16:55.362Z" }, + { url = "https://files.pythonhosted.org/packages/0d/2d/c20791e3437700a7441a7edfb59731150322424f5aadf635602d1d326101/aiohttp-3.13.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:13a5cc924b59859ad2adb1478e31f410a7ed46e92a2a619d6d1dd1a63c1a855e", size = 1884809, upload-time = "2026-03-28T17:16:57.734Z" }, + { url = "https://files.pythonhosted.org/packages/c8/94/d99dbfbd1924a87ef643833932eb2a3d9e5eee87656efea7d78058539eff/aiohttp-3.13.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:534913dfb0a644d537aebb4123e7d466d94e3be5549205e6a31f72368980a81a", size = 1764938, upload-time = "2026-03-28T17:17:00.221Z" }, + { url = "https://files.pythonhosted.org/packages/49/61/3ce326a1538781deb89f6cf5e094e2029cd308ed1e21b2ba2278b08426f6/aiohttp-3.13.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:320e40192a2dcc1cf4b5576936e9652981ab596bf81eb309535db7e2f5b5672f", size = 1570697, upload-time = "2026-03-28T17:17:02.985Z" }, + { url = "https://files.pythonhosted.org/packages/b6/77/4ab5a546857bb3028fbaf34d6eea180267bdab022ee8b1168b1fcde4bfdd/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9e587fcfce2bcf06526a43cb705bdee21ac089096f2e271d75de9c339db3100c", size = 1702258, upload-time = "2026-03-28T17:17:05.28Z" }, + { url = "https://files.pythonhosted.org/packages/79/63/d8f29021e39bc5af8e5d5e9da1b07976fb9846487a784e11e4f4eeda4666/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9eb9c2eea7278206b5c6c1441fdd9dc420c278ead3f3b2cc87f9b693698cc500", size = 1740287, upload-time = "2026-03-28T17:17:07.712Z" }, + { url = "https://files.pythonhosted.org/packages/55/3a/cbc6b3b124859a11bc8055d3682c26999b393531ef926754a3445b99dfef/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:29be00c51972b04bf9d5c8f2d7f7314f48f96070ca40a873a53056e652e805f7", size = 1753011, upload-time = "2026-03-28T17:17:10.053Z" }, + { url = "https://files.pythonhosted.org/packages/e0/30/836278675205d58c1368b21520eab9572457cf19afd23759216c04483048/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:90c06228a6c3a7c9f776fe4fc0b7ff647fffd3bed93779a6913c804ae00c1073", size = 1566359, upload-time = "2026-03-28T17:17:12.433Z" }, + { url = "https://files.pythonhosted.org/packages/50/b4/8032cc9b82d17e4277704ba30509eaccb39329dc18d6a35f05e424439e32/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a533ec132f05fd9a1d959e7f34184cd7d5e8511584848dab85faefbaac573069", size = 1785537, upload-time = "2026-03-28T17:17:14.721Z" }, + { url = "https://files.pythonhosted.org/packages/17/7d/5873e98230bde59f493bf1f7c3e327486a4b5653fa401144704df5d00211/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1c946f10f413836f82ea4cfb90200d2a59578c549f00857e03111cf45ad01ca5", size = 1740752, upload-time = "2026-03-28T17:17:17.387Z" }, + { url = "https://files.pythonhosted.org/packages/7b/f2/13e46e0df051494d7d3c68b7f72d071f48c384c12716fc294f75d5b1a064/aiohttp-3.13.4-cp313-cp313-win32.whl", hash = "sha256:48708e2706106da6967eff5908c78ca3943f005ed6bcb75da2a7e4da94ef8c70", size = 433187, upload-time = "2026-03-28T17:17:19.523Z" }, + { url = "https://files.pythonhosted.org/packages/ea/c0/649856ee655a843c8f8664592cfccb73ac80ede6a8c8db33a25d810c12db/aiohttp-3.13.4-cp313-cp313-win_amd64.whl", hash = "sha256:74a2eb058da44fa3a877a49e2095b591d4913308bb424c418b77beb160c55ce3", size = 459778, upload-time = "2026-03-28T17:17:21.964Z" }, + { url = "https://files.pythonhosted.org/packages/6d/29/6657cc37ae04cacc2dbf53fb730a06b6091cc4cbe745028e047c53e6d840/aiohttp-3.13.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:e0a2c961fc92abeff61d6444f2ce6ad35bb982db9fc8ff8a47455beacf454a57", size = 749363, upload-time = "2026-03-28T17:17:24.044Z" }, + { url = "https://files.pythonhosted.org/packages/90/7f/30ccdf67ca3d24b610067dc63d64dcb91e5d88e27667811640644aa4a85d/aiohttp-3.13.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:153274535985a0ff2bff1fb6c104ed547cec898a09213d21b0f791a44b14d933", size = 499317, upload-time = "2026-03-28T17:17:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/93/13/e372dd4e68ad04ee25dafb050c7f98b0d91ea643f7352757e87231102555/aiohttp-3.13.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:351f3171e2458da3d731ce83f9e6b9619e325c45cbd534c7759750cabf453ad7", size = 500477, upload-time = "2026-03-28T17:17:28.279Z" }, + { url = "https://files.pythonhosted.org/packages/e5/fe/ee6298e8e586096fb6f5eddd31393d8544f33ae0792c71ecbb4c2bef98ac/aiohttp-3.13.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f989ac8bc5595ff761a5ccd32bdb0768a117f36dd1504b1c2c074ed5d3f4df9c", size = 1737227, upload-time = "2026-03-28T17:17:30.587Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b9/a7a0463a09e1a3fe35100f74324f23644bfc3383ac5fd5effe0722a5f0b7/aiohttp-3.13.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d36fc1709110ec1e87a229b201dd3ddc32aa01e98e7868083a794609b081c349", size = 1694036, upload-time = "2026-03-28T17:17:33.29Z" }, + { url = "https://files.pythonhosted.org/packages/57/7c/8972ae3fb7be00a91aee6b644b2a6a909aedb2c425269a3bfd90115e6f8f/aiohttp-3.13.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42adaeea83cbdf069ab94f5103ce0787c21fb1a0153270da76b59d5578302329", size = 1786814, upload-time = "2026-03-28T17:17:36.035Z" }, + { url = "https://files.pythonhosted.org/packages/93/01/c81e97e85c774decbaf0d577de7d848934e8166a3a14ad9f8aa5be329d28/aiohttp-3.13.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:92deb95469928cc41fd4b42a95d8012fa6df93f6b1c0a83af0ffbc4a5e218cde", size = 1866676, upload-time = "2026-03-28T17:17:38.441Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5f/5b46fe8694a639ddea2cd035bf5729e4677ea882cb251396637e2ef1590d/aiohttp-3.13.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c0c7c07c4257ef3a1df355f840bc62d133bcdef5c1c5ba75add3c08553e2eed", size = 1740842, upload-time = "2026-03-28T17:17:40.783Z" }, + { url = "https://files.pythonhosted.org/packages/20/a2/0d4b03d011cca6b6b0acba8433193c1e484efa8d705ea58295590fe24203/aiohttp-3.13.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f062c45de8a1098cb137a1898819796a2491aec4e637a06b03f149315dff4d8f", size = 1566508, upload-time = "2026-03-28T17:17:43.235Z" }, + { url = "https://files.pythonhosted.org/packages/98/17/e689fd500da52488ec5f889effd6404dece6a59de301e380f3c64f167beb/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:76093107c531517001114f0ebdb4f46858ce818590363e3e99a4a2280334454a", size = 1700569, upload-time = "2026-03-28T17:17:46.165Z" }, + { url = "https://files.pythonhosted.org/packages/d8/0d/66402894dbcf470ef7db99449e436105ea862c24f7ea4c95c683e635af35/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:6f6ec32162d293b82f8b63a16edc80769662fbd5ae6fbd4936d3206a2c2cc63b", size = 1707407, upload-time = "2026-03-28T17:17:48.825Z" }, + { url = "https://files.pythonhosted.org/packages/2f/eb/af0ab1a3650092cbd8e14ef29e4ab0209e1460e1c299996c3f8288b3f1ff/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5903e2db3d202a00ad9f0ec35a122c005e85d90c9836ab4cda628f01edf425e2", size = 1752214, upload-time = "2026-03-28T17:17:51.206Z" }, + { url = "https://files.pythonhosted.org/packages/5a/bf/72326f8a98e4c666f292f03c385545963cc65e358835d2a7375037a97b57/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2d5bea57be7aca98dbbac8da046d99b5557c5cf4e28538c4c786313078aca09e", size = 1562162, upload-time = "2026-03-28T17:17:53.634Z" }, + { url = "https://files.pythonhosted.org/packages/67/9f/13b72435f99151dd9a5469c96b3b5f86aa29b7e785ca7f35cf5e538f74c0/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:bcf0c9902085976edc0232b75006ef38f89686901249ce14226b6877f88464fb", size = 1768904, upload-time = "2026-03-28T17:17:55.991Z" }, + { url = "https://files.pythonhosted.org/packages/18/bc/28d4970e7d5452ac7776cdb5431a1164a0d9cf8bd2fffd67b4fb463aa56d/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c3295f98bfeed2e867cab588f2a146a9db37a85e3ae9062abf46ba062bd29165", size = 1723378, upload-time = "2026-03-28T17:17:58.348Z" }, + { url = "https://files.pythonhosted.org/packages/53/74/b32458ca1a7f34d65bdee7aef2036adbe0438123d3d53e2b083c453c24dd/aiohttp-3.13.4-cp314-cp314-win32.whl", hash = "sha256:a598a5c5767e1369d8f5b08695cab1d8160040f796c4416af76fd773d229b3c9", size = 438711, upload-time = "2026-03-28T17:18:00.728Z" }, + { url = "https://files.pythonhosted.org/packages/40/b2/54b487316c2df3e03a8f3435e9636f8a81a42a69d942164830d193beb56a/aiohttp-3.13.4-cp314-cp314-win_amd64.whl", hash = "sha256:c555db4bc7a264bead5a7d63d92d41a1122fcd39cc62a4db815f45ad46f9c2c8", size = 464977, upload-time = "2026-03-28T17:18:03.367Z" }, + { url = "https://files.pythonhosted.org/packages/47/fb/e41b63c6ce71b07a59243bb8f3b457ee0c3402a619acb9d2c0d21ef0e647/aiohttp-3.13.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45abbbf09a129825d13c18c7d3182fecd46d9da3cfc383756145394013604ac1", size = 781549, upload-time = "2026-03-28T17:18:05.779Z" }, + { url = "https://files.pythonhosted.org/packages/97/53/532b8d28df1e17e44c4d9a9368b78dcb6bf0b51037522136eced13afa9e8/aiohttp-3.13.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:74c80b2bc2c2adb7b3d1941b2b60701ee2af8296fc8aad8b8bc48bc25767266c", size = 514383, upload-time = "2026-03-28T17:18:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/1b/1f/62e5d400603e8468cd635812d99cb81cfdc08127a3dc474c647615f31339/aiohttp-3.13.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c97989ae40a9746650fa196894f317dafc12227c808c774929dda0ff873a5954", size = 518304, upload-time = "2026-03-28T17:18:10.642Z" }, + { url = "https://files.pythonhosted.org/packages/90/57/2326b37b10896447e3c6e0cbef4fe2486d30913639a5cfd1332b5d870f82/aiohttp-3.13.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dae86be9811493f9990ef44fff1685f5c1a3192e9061a71a109d527944eed551", size = 1893433, upload-time = "2026-03-28T17:18:13.121Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b4/a24d82112c304afdb650167ef2fe190957d81cbddac7460bedd245f765aa/aiohttp-3.13.4-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1db491abe852ca2fa6cc48a3341985b0174b3741838e1341b82ac82c8bd9e871", size = 1755901, upload-time = "2026-03-28T17:18:16.21Z" }, + { url = "https://files.pythonhosted.org/packages/9e/2d/0883ef9d878d7846287f036c162a951968f22aabeef3ac97b0bea6f76d5d/aiohttp-3.13.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0e5d701c0aad02a7dce72eef6b93226cf3734330f1a31d69ebbf69f33b86666e", size = 1876093, upload-time = "2026-03-28T17:18:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/ad/52/9204bb59c014869b71971addad6778f005daa72a96eed652c496789d7468/aiohttp-3.13.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8ac32a189081ae0a10ba18993f10f338ec94341f0d5df8fff348043962f3c6f8", size = 1970815, upload-time = "2026-03-28T17:18:21.858Z" }, + { url = "https://files.pythonhosted.org/packages/d6/b5/e4eb20275a866dde0f570f411b36c6b48f7b53edfe4f4071aa1b0728098a/aiohttp-3.13.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98e968cdaba43e45c73c3f306fca418c8009a957733bac85937c9f9cf3f4de27", size = 1816223, upload-time = "2026-03-28T17:18:24.729Z" }, + { url = "https://files.pythonhosted.org/packages/d8/23/e98075c5bb146aa61a1239ee1ac7714c85e814838d6cebbe37d3fe19214a/aiohttp-3.13.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca114790c9144c335d538852612d3e43ea0f075288f4849cf4b05d6cd2238ce7", size = 1649145, upload-time = "2026-03-28T17:18:27.269Z" }, + { url = "https://files.pythonhosted.org/packages/d6/c1/7bad8be33bb06c2bb224b6468874346026092762cbec388c3bdb65a368ee/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ea2e071661ba9cfe11eabbc81ac5376eaeb3061f6e72ec4cc86d7cdd1ffbdbbb", size = 1816562, upload-time = "2026-03-28T17:18:29.847Z" }, + { url = "https://files.pythonhosted.org/packages/5c/10/c00323348695e9a5e316825969c88463dcc24c7e9d443244b8a2c9cf2eae/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:34e89912b6c20e0fd80e07fa401fd218a410aa1ce9f1c2f1dad6db1bd0ce0927", size = 1800333, upload-time = "2026-03-28T17:18:32.269Z" }, + { url = "https://files.pythonhosted.org/packages/84/43/9b2147a1df3559f49bd723e22905b46a46c068a53adb54abdca32c4de180/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0e217cf9f6a42908c52b46e42c568bd57adc39c9286ced31aaace614b6087965", size = 1820617, upload-time = "2026-03-28T17:18:35.238Z" }, + { url = "https://files.pythonhosted.org/packages/a9/7f/b3481a81e7a586d02e99387b18c6dafff41285f6efd3daa2124c01f87eae/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:0c296f1221e21ba979f5ac1964c3b78cfde15c5c5f855ffd2caab337e9cd9182", size = 1643417, upload-time = "2026-03-28T17:18:37.949Z" }, + { url = "https://files.pythonhosted.org/packages/8f/72/07181226bc99ce1124e0f89280f5221a82d3ae6a6d9d1973ce429d48e52b/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d99a9d168ebaffb74f36d011750e490085ac418f4db926cce3989c8fe6cb6b1b", size = 1849286, upload-time = "2026-03-28T17:18:40.534Z" }, + { url = "https://files.pythonhosted.org/packages/1a/e6/1b3566e103eca6da5be4ae6713e112a053725c584e96574caf117568ffef/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cb19177205d93b881f3f89e6081593676043a6828f59c78c17a0fd6c1fbed2ba", size = 1782635, upload-time = "2026-03-28T17:18:43.073Z" }, + { url = "https://files.pythonhosted.org/packages/37/58/1b11c71904b8d079eb0c39fe664180dd1e14bebe5608e235d8bfbadc8929/aiohttp-3.13.4-cp314-cp314t-win32.whl", hash = "sha256:c606aa5656dab6552e52ca368e43869c916338346bfaf6304e15c58fb113ea30", size = 472537, upload-time = "2026-03-28T17:18:46.286Z" }, + { url = "https://files.pythonhosted.org/packages/bc/8f/87c56a1a1977d7dddea5b31e12189665a140fdb48a71e9038ff90bb564ec/aiohttp-3.13.4-cp314-cp314t-win_amd64.whl", hash = "sha256:014dcc10ec8ab8db681f0d68e939d1e9286a5aa2b993cbbdb0db130853e02144", size = 506381, upload-time = "2026-03-28T17:18:48.74Z" }, ] [[package]] @@ -379,16 +379,16 @@ wheels = [ [[package]] name = "anyio" -version = "4.12.1" +version = "4.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "idna" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, ] [[package]] @@ -642,20 +642,22 @@ wheels = [ [package.optional-dependencies] sphinx = [ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] [[package]] name = "autodocsumm" -version = "0.2.14" +version = "0.2.15" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/03/96/92afe8a7912b327c01f0a8b6408c9556ee13b1aba5b98d587ac7327ff32d/autodocsumm-0.2.14.tar.gz", hash = "sha256:2839a9d4facc3c4eccd306c08695540911042b46eeafcdc3203e6d0bab40bc77", size = 46357, upload-time = "2024-10-23T18:51:47.369Z" } +sdist = { url = "https://files.pythonhosted.org/packages/46/b7/f28dea12fae1d1ad1e706f5cf6d16e8d735f305ebee86fd9390e099bd27d/autodocsumm-0.2.15.tar.gz", hash = "sha256:eaf431e7a5a39e41a215311173c8b95e83859059df1ccf3b79c64bf3d5582b3c", size = 46674, upload-time = "2026-03-26T20:44:07.074Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/bc/3f66af9beb683728e06ca08797e4e9d3e44f432f339718cae3ba856a9cad/autodocsumm-0.2.14-py3-none-any.whl", hash = "sha256:3bad8717fc5190802c60392a7ab04b9f3c97aa9efa8b3780b3d81d615bfe5dc0", size = 14640, upload-time = "2024-10-23T18:51:45.115Z" }, + { url = "https://files.pythonhosted.org/packages/ca/3d/4357a0f685c0a2ae7132ac91905bec565e64f9ba63b079f7ec5da46e3597/autodocsumm-0.2.15-py3-none-any.whl", hash = "sha256:dbe6fabcaeae4540748ea9b3443eb76c2692e063d44f004f67c424610a5aca9a", size = 14852, upload-time = "2026-03-26T20:44:05.273Z" }, ] [[package]] @@ -1029,11 +1031,13 @@ wheels = [ [package.optional-dependencies] sphinx = [ - { name = "docutils" }, + { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pygments" }, { name = "pygments-ansi-color" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] [[package]] @@ -1225,62 +1229,62 @@ toml = [ [[package]] name = "cryptography" -version = "46.0.5" +version = "46.0.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289, upload-time = "2026-02-10T19:17:08.274Z" }, - { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, - { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, - { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, - { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" }, - { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, - { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, - { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, - { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" }, - { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, - { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/ea/ed/325d2a490c5e94038cdb0117da9397ece1f11201f425c4e9c57fe5b9f08b/cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", size = 3028230, upload-time = "2026-02-10T19:17:30.518Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5a/ac0f49e48063ab4255d9e3b79f5def51697fce1a95ea1370f03dc9db76f6/cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", size = 3480909, upload-time = "2026-02-10T19:17:32.083Z" }, - { url = "https://files.pythonhosted.org/packages/00/13/3d278bfa7a15a96b9dc22db5a12ad1e48a9eb3d40e1827ef66a5df75d0d0/cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2", size = 7119287, upload-time = "2026-02-10T19:17:33.801Z" }, - { url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728, upload-time = "2026-02-10T19:17:35.569Z" }, - { url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287, upload-time = "2026-02-10T19:17:36.938Z" }, - { url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291, upload-time = "2026-02-10T19:17:38.748Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e5/f52377ee93bc2f2bba55a41a886fd208c15276ffbd2569f2ddc89d50e2c5/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", size = 4927539, upload-time = "2026-02-10T19:17:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199, upload-time = "2026-02-10T19:17:41.789Z" }, - { url = "https://files.pythonhosted.org/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", size = 3960131, upload-time = "2026-02-10T19:17:43.379Z" }, - { url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072, upload-time = "2026-02-10T19:17:45.481Z" }, - { url = "https://files.pythonhosted.org/packages/f4/a7/60d32b0370dae0b4ebe55ffa10e8599a2a59935b5ece1b9f06edb73abdeb/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", size = 4892170, upload-time = "2026-02-10T19:17:46.997Z" }, - { url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741, upload-time = "2026-02-10T19:17:48.661Z" }, - { url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728, upload-time = "2026-02-10T19:17:50.058Z" }, - { url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001, upload-time = "2026-02-10T19:17:51.54Z" }, - { url = "https://files.pythonhosted.org/packages/86/ef/5d00ef966ddd71ac2e6951d278884a84a40ffbd88948ef0e294b214ae9e4/cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a", size = 3003637, upload-time = "2026-02-10T19:17:52.997Z" }, - { url = "https://files.pythonhosted.org/packages/b7/57/f3f4160123da6d098db78350fdfd9705057aad21de7388eacb2401dceab9/cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4", size = 3469487, upload-time = "2026-02-10T19:17:54.549Z" }, - { url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514, upload-time = "2026-02-10T19:17:56.267Z" }, - { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, - { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, - { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" }, - { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, - { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, - { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, - { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" }, - { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, - { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, - { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/45/2d/9c5f2926cb5300a8eefc3f4f0b3f3df39db7f7ce40c8365444c49363cbda/cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", size = 3010220, upload-time = "2026-02-10T19:18:17.361Z" }, - { url = "https://files.pythonhosted.org/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", size = 3467050, upload-time = "2026-02-10T19:18:18.899Z" }, - { url = "https://files.pythonhosted.org/packages/eb/dd/2d9fdb07cebdf3d51179730afb7d5e576153c6744c3ff8fded23030c204e/cryptography-46.0.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c", size = 3476964, upload-time = "2026-02-10T19:18:20.687Z" }, - { url = "https://files.pythonhosted.org/packages/e9/6f/6cc6cc9955caa6eaf83660b0da2b077c7fe8ff9950a3c5e45d605038d439/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a", size = 4218321, upload-time = "2026-02-10T19:18:22.349Z" }, - { url = "https://files.pythonhosted.org/packages/3e/5d/c4da701939eeee699566a6c1367427ab91a8b7088cc2328c09dbee940415/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356", size = 4381786, upload-time = "2026-02-10T19:18:24.529Z" }, - { url = "https://files.pythonhosted.org/packages/ac/97/a538654732974a94ff96c1db621fa464f455c02d4bb7d2652f4edc21d600/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da", size = 4217990, upload-time = "2026-02-10T19:18:25.957Z" }, - { url = "https://files.pythonhosted.org/packages/ae/11/7e500d2dd3ba891197b9efd2da5454b74336d64a7cc419aa7327ab74e5f6/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257", size = 4381252, upload-time = "2026-02-10T19:18:27.496Z" }, - { url = "https://files.pythonhosted.org/packages/bc/58/6b3d24e6b9bc474a2dcdee65dfd1f008867015408a271562e4b690561a4d/cryptography-46.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7", size = 3407605, upload-time = "2026-02-10T19:18:29.233Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/a4/ba/04b1bd4218cbc58dc90ce967106d51582371b898690f3ae0402876cc4f34/cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759", size = 750542, upload-time = "2026-03-25T23:34:53.396Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/23/9285e15e3bc57325b0a72e592921983a701efc1ee8f91c06c5f0235d86d9/cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8", size = 7176401, upload-time = "2026-03-25T23:33:22.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/f8/e61f8f13950ab6195b31913b42d39f0f9afc7d93f76710f299b5ec286ae6/cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30", size = 4275275, upload-time = "2026-03-25T23:33:23.844Z" }, + { url = "https://files.pythonhosted.org/packages/19/69/732a736d12c2631e140be2348b4ad3d226302df63ef64d30dfdb8db7ad1c/cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a", size = 4425320, upload-time = "2026-03-25T23:33:25.703Z" }, + { url = "https://files.pythonhosted.org/packages/d4/12/123be7292674abf76b21ac1fc0e1af50661f0e5b8f0ec8285faac18eb99e/cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175", size = 4278082, upload-time = "2026-03-25T23:33:27.423Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ba/d5e27f8d68c24951b0a484924a84c7cdaed7502bac9f18601cd357f8b1d2/cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463", size = 4926514, upload-time = "2026-03-25T23:33:29.206Z" }, + { url = "https://files.pythonhosted.org/packages/34/71/1ea5a7352ae516d5512d17babe7e1b87d9db5150b21f794b1377eac1edc0/cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97", size = 4457766, upload-time = "2026-03-25T23:33:30.834Z" }, + { url = "https://files.pythonhosted.org/packages/01/59/562be1e653accee4fdad92c7a2e88fced26b3fdfce144047519bbebc299e/cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c", size = 3986535, upload-time = "2026-03-25T23:33:33.02Z" }, + { url = "https://files.pythonhosted.org/packages/d6/8b/b1ebfeb788bf4624d36e45ed2662b8bd43a05ff62157093c1539c1288a18/cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507", size = 4277618, upload-time = "2026-03-25T23:33:34.567Z" }, + { url = "https://files.pythonhosted.org/packages/dd/52/a005f8eabdb28df57c20f84c44d397a755782d6ff6d455f05baa2785bd91/cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19", size = 4890802, upload-time = "2026-03-25T23:33:37.034Z" }, + { url = "https://files.pythonhosted.org/packages/ec/4d/8e7d7245c79c617d08724e2efa397737715ca0ec830ecb3c91e547302555/cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738", size = 4457425, upload-time = "2026-03-25T23:33:38.904Z" }, + { url = "https://files.pythonhosted.org/packages/1d/5c/f6c3596a1430cec6f949085f0e1a970638d76f81c3ea56d93d564d04c340/cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c", size = 4405530, upload-time = "2026-03-25T23:33:40.842Z" }, + { url = "https://files.pythonhosted.org/packages/7e/c9/9f9cea13ee2dbde070424e0c4f621c091a91ffcc504ffea5e74f0e1daeff/cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f", size = 4667896, upload-time = "2026-03-25T23:33:42.781Z" }, + { url = "https://files.pythonhosted.org/packages/ad/b5/1895bc0821226f129bc74d00eccfc6a5969e2028f8617c09790bf89c185e/cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2", size = 3026348, upload-time = "2026-03-25T23:33:45.021Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f8/c9bcbf0d3e6ad288b9d9aa0b1dee04b063d19e8c4f871855a03ab3a297ab/cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124", size = 3483896, upload-time = "2026-03-25T23:33:46.649Z" }, + { url = "https://files.pythonhosted.org/packages/01/41/3a578f7fd5c70611c0aacba52cd13cb364a5dee895a5c1d467208a9380b0/cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275", size = 7117147, upload-time = "2026-03-25T23:33:48.249Z" }, + { url = "https://files.pythonhosted.org/packages/fa/87/887f35a6fca9dde90cad08e0de0c89263a8e59b2d2ff904fd9fcd8025b6f/cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4", size = 4266221, upload-time = "2026-03-25T23:33:49.874Z" }, + { url = "https://files.pythonhosted.org/packages/aa/a8/0a90c4f0b0871e0e3d1ed126aed101328a8a57fd9fd17f00fb67e82a51ca/cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b", size = 4408952, upload-time = "2026-03-25T23:33:52.128Z" }, + { url = "https://files.pythonhosted.org/packages/16/0b/b239701eb946523e4e9f329336e4ff32b1247e109cbab32d1a7b61da8ed7/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707", size = 4270141, upload-time = "2026-03-25T23:33:54.11Z" }, + { url = "https://files.pythonhosted.org/packages/0f/a8/976acdd4f0f30df7b25605f4b9d3d89295351665c2091d18224f7ad5cdbf/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361", size = 4904178, upload-time = "2026-03-25T23:33:55.725Z" }, + { url = "https://files.pythonhosted.org/packages/b1/1b/bf0e01a88efd0e59679b69f42d4afd5bced8700bb5e80617b2d63a3741af/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b", size = 4441812, upload-time = "2026-03-25T23:33:57.364Z" }, + { url = "https://files.pythonhosted.org/packages/bb/8b/11df86de2ea389c65aa1806f331cae145f2ed18011f30234cc10ca253de8/cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca", size = 3963923, upload-time = "2026-03-25T23:33:59.361Z" }, + { url = "https://files.pythonhosted.org/packages/91/e0/207fb177c3a9ef6a8108f234208c3e9e76a6aa8cf20d51932916bd43bda0/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013", size = 4269695, upload-time = "2026-03-25T23:34:00.909Z" }, + { url = "https://files.pythonhosted.org/packages/21/5e/19f3260ed1e95bced52ace7501fabcd266df67077eeb382b79c81729d2d3/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4", size = 4869785, upload-time = "2026-03-25T23:34:02.796Z" }, + { url = "https://files.pythonhosted.org/packages/10/38/cd7864d79aa1d92ef6f1a584281433419b955ad5a5ba8d1eb6c872165bcb/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a", size = 4441404, upload-time = "2026-03-25T23:34:04.35Z" }, + { url = "https://files.pythonhosted.org/packages/09/0a/4fe7a8d25fed74419f91835cf5829ade6408fd1963c9eae9c4bce390ecbb/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d", size = 4397549, upload-time = "2026-03-25T23:34:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a0/7d738944eac6513cd60a8da98b65951f4a3b279b93479a7e8926d9cd730b/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736", size = 4651874, upload-time = "2026-03-25T23:34:07.916Z" }, + { url = "https://files.pythonhosted.org/packages/cb/f1/c2326781ca05208845efca38bf714f76939ae446cd492d7613808badedf1/cryptography-46.0.6-cp314-cp314t-win32.whl", hash = "sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed", size = 3001511, upload-time = "2026-03-25T23:34:09.892Z" }, + { url = "https://files.pythonhosted.org/packages/c9/57/fe4a23eb549ac9d903bd4698ffda13383808ef0876cc912bcb2838799ece/cryptography-46.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4", size = 3471692, upload-time = "2026-03-25T23:34:11.613Z" }, + { url = "https://files.pythonhosted.org/packages/c4/cc/f330e982852403da79008552de9906804568ae9230da8432f7496ce02b71/cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a", size = 7162776, upload-time = "2026-03-25T23:34:13.308Z" }, + { url = "https://files.pythonhosted.org/packages/49/b3/dc27efd8dcc4bff583b3f01d4a3943cd8b5821777a58b3a6a5f054d61b79/cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8", size = 4270529, upload-time = "2026-03-25T23:34:15.019Z" }, + { url = "https://files.pythonhosted.org/packages/e6/05/e8d0e6eb4f0d83365b3cb0e00eb3c484f7348db0266652ccd84632a3d58d/cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77", size = 4414827, upload-time = "2026-03-25T23:34:16.604Z" }, + { url = "https://files.pythonhosted.org/packages/2f/97/daba0f5d2dc6d855e2dcb70733c812558a7977a55dd4a6722756628c44d1/cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290", size = 4271265, upload-time = "2026-03-25T23:34:18.586Z" }, + { url = "https://files.pythonhosted.org/packages/89/06/fe1fce39a37ac452e58d04b43b0855261dac320a2ebf8f5260dd55b201a9/cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410", size = 4916800, upload-time = "2026-03-25T23:34:20.561Z" }, + { url = "https://files.pythonhosted.org/packages/ff/8a/b14f3101fe9c3592603339eb5d94046c3ce5f7fc76d6512a2d40efd9724e/cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d", size = 4448771, upload-time = "2026-03-25T23:34:22.406Z" }, + { url = "https://files.pythonhosted.org/packages/01/b3/0796998056a66d1973fd52ee89dc1bb3b6581960a91ad4ac705f182d398f/cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70", size = 3978333, upload-time = "2026-03-25T23:34:24.281Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3d/db200af5a4ffd08918cd55c08399dc6c9c50b0bc72c00a3246e099d3a849/cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d", size = 4271069, upload-time = "2026-03-25T23:34:25.895Z" }, + { url = "https://files.pythonhosted.org/packages/d7/18/61acfd5b414309d74ee838be321c636fe71815436f53c9f0334bf19064fa/cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa", size = 4878358, upload-time = "2026-03-25T23:34:27.67Z" }, + { url = "https://files.pythonhosted.org/packages/8b/65/5bf43286d566f8171917cae23ac6add941654ccf085d739195a4eacf1674/cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58", size = 4448061, upload-time = "2026-03-25T23:34:29.375Z" }, + { url = "https://files.pythonhosted.org/packages/e0/25/7e49c0fa7205cf3597e525d156a6bce5b5c9de1fd7e8cb01120e459f205a/cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb", size = 4399103, upload-time = "2026-03-25T23:34:32.036Z" }, + { url = "https://files.pythonhosted.org/packages/44/46/466269e833f1c4718d6cd496ffe20c56c9c8d013486ff66b4f69c302a68d/cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72", size = 4659255, upload-time = "2026-03-25T23:34:33.679Z" }, + { url = "https://files.pythonhosted.org/packages/0a/09/ddc5f630cc32287d2c953fc5d32705e63ec73e37308e5120955316f53827/cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c", size = 3010660, upload-time = "2026-03-25T23:34:35.418Z" }, + { url = "https://files.pythonhosted.org/packages/1b/82/ca4893968aeb2709aacfb57a30dec6fa2ab25b10fa9f064b8882ce33f599/cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f", size = 3471160, upload-time = "2026-03-25T23:34:37.191Z" }, + { url = "https://files.pythonhosted.org/packages/2e/84/7ccff00ced5bac74b775ce0beb7d1be4e8637536b522b5df9b73ada42da2/cryptography-46.0.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead", size = 3475444, upload-time = "2026-03-25T23:34:38.944Z" }, + { url = "https://files.pythonhosted.org/packages/bc/1f/4c926f50df7749f000f20eede0c896769509895e2648db5da0ed55db711d/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8", size = 4218227, upload-time = "2026-03-25T23:34:40.871Z" }, + { url = "https://files.pythonhosted.org/packages/c6/65/707be3ffbd5f786028665c3223e86e11c4cda86023adbc56bd72b1b6bab5/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0", size = 4381399, upload-time = "2026-03-25T23:34:42.609Z" }, + { url = "https://files.pythonhosted.org/packages/f3/6d/73557ed0ef7d73d04d9aba745d2c8e95218213687ee5e76b7d236a5030fc/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b", size = 4217595, upload-time = "2026-03-25T23:34:44.205Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c5/e1594c4eec66a567c3ac4400008108a415808be2ce13dcb9a9045c92f1a0/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a", size = 4380912, upload-time = "2026-03-25T23:34:46.328Z" }, + { url = "https://files.pythonhosted.org/packages/1a/89/843b53614b47f97fe1abc13f9a86efa5ec9e275292c457af1d4a60dc80e0/cryptography-46.0.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e", size = 3409955, upload-time = "2026-03-25T23:34:48.465Z" }, ] [[package]] @@ -1430,11 +1434,37 @@ wheels = [ name = "docutils" version = "0.21.2" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, ] +[[package]] +name = "docutils" +version = "0.22.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, +] + [[package]] name = "domdf-python-tools" version = "3.10.0" @@ -1509,7 +1539,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -1536,23 +1566,23 @@ wheels = [ [[package]] name = "extra-platforms" -version = "11.0.3" +version = "11.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/ee/372b3699b3e1cfc7f35430ab14d91b1fb04eff032b96f17a1a7e69a279ac/extra_platforms-11.0.3.tar.gz", hash = "sha256:56225020fe60859bdd27549311b8efc5e3213f79c7851292d9f07189ee730ec1", size = 68476, upload-time = "2026-03-23T07:08:57.234Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/3d/28f6523feeae961d5927d91f99a3626c8a1f8dcd426a78f353bd889a3ea4/extra_platforms-11.0.4.tar.gz", hash = "sha256:9f9471bb8063fcd49caef9b97625f9e2405c678f0ffe809e4442160dcb80c188", size = 68727, upload-time = "2026-03-30T20:47:24.392Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/88/7b6d5dabe7f2b2b3afc24a6640ea496fab83688d33df4132efcced91f57c/extra_platforms-11.0.3-py3-none-any.whl", hash = "sha256:0e44e9f09c3584a8aab34d0044a028e95b407d0e60c46631055a8e172446de02", size = 72330, upload-time = "2026-03-23T07:08:55.987Z" }, + { url = "https://files.pythonhosted.org/packages/a2/d8/cb7bad907a90dd61d681fa497da2c0c0f6e142c3bc913aaf6fc8831daac5/extra_platforms-11.0.4-py3-none-any.whl", hash = "sha256:1981c3afcf6628a941f71d7fa6b25aeb0e9232e20dd708f8114fe8b5b2f9132a", size = 72308, upload-time = "2026-03-30T20:47:25.475Z" }, ] [[package]] name = "faker" -version = "40.11.1" +version = "40.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/e5/b16bf568a2f20fe7423282db4a4059dbcadef70e9029c1c106836f8edd84/faker-40.11.1.tar.gz", hash = "sha256:61965046e79e8cfde4337d243eac04c0d31481a7c010033141103b43f603100c", size = 1957415, upload-time = "2026-03-23T14:05:50.233Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/c1/f8224fe97fea2f98d455c22438c1b09b10e14ef2cb95ae4f7cec9aa59659/faker-40.12.0.tar.gz", hash = "sha256:58b5a9054c367bd5fb2e948634105364cc570e78a98a8e5161a74691c45f158f", size = 1962003, upload-time = "2026-03-30T18:00:56.596Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/ec/3c4b78eb0d2f6a81fb8cc9286745845bff661e6815741eff7a6ac5fcc9ea/faker-40.11.1-py3-none-any.whl", hash = "sha256:3af3a213ba8fb33ce6ba2af7aef2ac91363dae35d0cec0b2b0337d189e5bee2a", size = 1989484, upload-time = "2026-03-23T14:05:48.793Z" }, + { url = "https://files.pythonhosted.org/packages/2b/5c/39452a6b6aa76ffa518fa7308e1975b37e9ba77caa6172a69d61e7180221/faker-40.12.0-py3-none-any.whl", hash = "sha256:6238a4058a8b581892e3d78fe5fdfa7568739e1c8283e4ede83f1dde0bfc1a3b", size = 1994601, upload-time = "2026-03-30T18:00:54.804Z" }, ] [[package]] @@ -1821,11 +1851,11 @@ wheels = [ [[package]] name = "fsspec" -version = "2026.2.0" +version = "2026.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/7c/f60c259dcbf4f0c47cc4ddb8f7720d2dcdc8888c8e5ad84c73ea4531cc5b/fsspec-2026.2.0.tar.gz", hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff", size = 313441, upload-time = "2026-02-05T21:50:53.743Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/cf/b50ddf667c15276a9ab15a70ef5f257564de271957933ffea49d2cdbcdfb/fsspec-2026.3.0.tar.gz", hash = "sha256:1ee6a0e28677557f8c2f994e3eea77db6392b4de9cd1f5d7a9e87a0ae9d01b41", size = 313547, upload-time = "2026-03-27T19:11:14.892Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437", size = 202505, upload-time = "2026-02-05T21:50:51.819Z" }, + { url = "https://files.pythonhosted.org/packages/d5/1f/5f4a3cd9e4440e9d9bc78ad0a91a1c8d46b4d429d5239ebe6793c9fe5c41/fsspec-2026.3.0-py3-none-any.whl", hash = "sha256:d2ceafaad1b3457968ed14efa28798162f1638dbb5d2a6868a2db002a5ee39a4", size = 202595, upload-time = "2026-03-27T19:11:13.595Z" }, ] [package.optional-dependencies] @@ -1835,7 +1865,7 @@ s3 = [ [[package]] name = "google-adk" -version = "1.27.2" +version = "1.28.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiosqlite" }, @@ -1884,9 +1914,9 @@ dependencies = [ { name = "watchdog" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/8c/5f3d3fcfdabbbe509e2a488cbfbf799c6a889b25ace877d40956498d3da1/google_adk-1.27.2.tar.gz", hash = "sha256:2971793c9872cd496cc322e6dd7cf404e99512689ed2fffd43f333683d204c2a", size = 2297941, upload-time = "2026-03-17T21:14:52.978Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/2b/6f3e87faa3d3b11d585524ee8e1c088472f9d3783879b360db32b96bd2de/google_adk-1.28.0.tar.gz", hash = "sha256:3c7ef1a518296641b992cc68ae4795987da48765f9788af37da1acc39db0f362", size = 2317884, upload-time = "2026-03-26T22:48:26.755Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/0d/bde4735b5ec36a312774526bb75fecaf45af6ae029d9bd665c42c5fe8d15/google_adk-1.27.2-py3-none-any.whl", hash = "sha256:04b4f23e9d26d75ee2f70c8d933629b095b8f6b23ed8ffc072f4d13fd106848d", size = 2689037, upload-time = "2026-03-17T21:14:51.318Z" }, + { url = "https://files.pythonhosted.org/packages/6f/1a/a76004c1660118a37836bf6355628506601f9fa27b7490c47160b723465e/google_adk-1.28.0-py3-none-any.whl", hash = "sha256:8470c2e5a97bcef9a54283148be9f4ffaa88b3eefe3f55f269183f715d8830b6", size = 2728054, upload-time = "2026-03-26T22:48:24.947Z" }, ] [[package]] @@ -1963,7 +1993,7 @@ wheels = [ [[package]] name = "google-cloud-aiplatform" -version = "1.142.0" +version = "1.143.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docstring-parser" }, @@ -1979,9 +2009,9 @@ dependencies = [ { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/41/0d/3063a0512d60cf18854a279e00ccb796429545464345ef821cf77cb93d05/google_cloud_aiplatform-1.142.0.tar.gz", hash = "sha256:87b49e002703dc14885093e9b264587db84222bef5f70f5a442d03f41beecdd1", size = 10207993, upload-time = "2026-03-20T22:49:13.797Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/08/939fb05870fdf155410a927e22f5b053d49f18e215618e102fba1d8bb147/google_cloud_aiplatform-1.143.0.tar.gz", hash = "sha256:1f0124a89795a6b473deb28724dd37d95334205df3a9c9c48d0b8d7a3d5d5cc4", size = 10215389, upload-time = "2026-03-25T18:30:15.444Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/8b/f29646d3fa940f0e38cfcc12137f4851856b50d7486a3c05103ebc78d82d/google_cloud_aiplatform-1.142.0-py2.py3-none-any.whl", hash = "sha256:17c91db9b613cbbafb2c36335b123686aeb2b4b8448be5134b565ae07165a39a", size = 8388991, upload-time = "2026-03-20T22:49:10.334Z" }, + { url = "https://files.pythonhosted.org/packages/90/14/16323e604e79dc63b528268f97a841c2c29dd8eb16395de6bf530c1a5ebe/google_cloud_aiplatform-1.143.0-py2.py3-none-any.whl", hash = "sha256:78df97d044859f743a9cc48b89a260d33579b0d548b1589bb3ae9f4c2afc0c5a", size = 8392705, upload-time = "2026-03-25T18:30:11.496Z" }, ] [package.optional-dependencies] @@ -2131,7 +2161,7 @@ wheels = [ [[package]] name = "google-cloud-dataplex" -version = "2.16.0" +version = "2.18.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -2141,9 +2171,8 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ac/64/38445469e85e20b6fbb0ad58d0466daa3bd779789729562c12b35cfc24c3/google_cloud_dataplex-2.16.0.tar.gz", hash = "sha256:f9086abb94ae1f35151b2df5b729cc6bbf9361354d5afd22e76515ec0a8e7fdc", size = 766385, upload-time = "2026-01-15T13:15:22.79Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/1a/9d0fc0188abcfe3c4e58db72972b100badb9899e34d94471223ac2037816/google_cloud_dataplex-2.16.0-py3-none-any.whl", hash = "sha256:173ce519395cd424c1ae22de4efb194767524fb5a2424194f091e63b34f4dfc1", size = 584533, upload-time = "2026-01-15T13:13:12.348Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9a/8b096a6d772b7abf1c97dfbce17d47ba1d8a944ce8d7a239fd300a3ad8ae/google_cloud_dataplex-2.18.0-py3-none-any.whl", hash = "sha256:6e4ec95b24f64e95cec5f3753fbe7419f78ddb8b1ba90f8d955bc7613bb90764", size = 675743, upload-time = "2026-03-30T20:02:27.12Z" }, ] [[package]] @@ -2163,7 +2192,7 @@ wheels = [ [[package]] name = "google-cloud-iam" -version = "2.21.0" +version = "2.22.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -2173,14 +2202,13 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/aa/0b/037b1e1eb601646d6f49bc06d62094c1d0996b373dcbf70c426c6c51572e/google_cloud_iam-2.21.0.tar.gz", hash = "sha256:fc560527e22b97c6cbfba0797d867cf956c727ba687b586b9aa44d78e92281a3", size = 499038, upload-time = "2026-01-15T13:15:08.243Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/44/02ac4e147ea034a3d641c11b54c9d8d0b80fc1ea6a8b7d6c1588d208d42a/google_cloud_iam-2.21.0-py3-none-any.whl", hash = "sha256:1b4a21302b186a31f3a516ccff303779638308b7c801fb61a2406b6a0c6293c4", size = 458958, upload-time = "2026-01-15T13:13:40.671Z" }, + { url = "https://files.pythonhosted.org/packages/aa/a8/d721ea11d0eb93803d14cb2e90d0442bb3b269a82f7cb5faff2b98022039/google_cloud_iam-2.22.0-py3-none-any.whl", hash = "sha256:c443b34b5a6a9e51d32cee397879bb781b900af68937c67a275def23bbc025f3", size = 463425, upload-time = "2026-03-30T20:02:42.967Z" }, ] [[package]] name = "google-cloud-logging" -version = "3.14.0" +version = "3.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -2194,14 +2222,14 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/ce/0d3539008dc33b436e7c5c644abc8f8a7ec5900911d14a8e34e145f0ebe5/google_cloud_logging-3.14.0.tar.gz", hash = "sha256:361e83cd692fecc7da10351f641c474591f586f234fc49394db4ba5c8c5994a7", size = 293452, upload-time = "2026-03-06T21:53:07.526Z" } +sdist = { url = "https://files.pythonhosted.org/packages/99/06/253e9795a5877f35183a7175977ca47a17255fe0c8487155f48b86c83f3e/google_cloud_logging-3.15.0.tar.gz", hash = "sha256:72168a1e98bbfc27c75f0b8f630a7f5d786065f3f1f7e9e53d2d787a03693a4a", size = 294881, upload-time = "2026-03-26T22:18:36.947Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/3e/01795fc20f1b5f8b1d1d22eeb425c9c3396046f1761c4f6b4cc7d8dcab90/google_cloud_logging-3.14.0-py3-none-any.whl", hash = "sha256:4767ebdb3b46a3052d5185a7d5cf02829d33ea12a0aab1d57221110d581b9e1a", size = 232961, upload-time = "2026-03-06T21:52:48.393Z" }, + { url = "https://files.pythonhosted.org/packages/86/0c/fc1a0c57f95d21559ed13e381d9024e9ee9d521489707573fd10af856545/google_cloud_logging-3.15.0-py3-none-any.whl", hash = "sha256:7dcc67434c4e7181510c133d5ac8fd4ce60c23fa4158661f67e54bf440c32450", size = 234212, upload-time = "2026-03-26T22:15:16.404Z" }, ] [[package]] name = "google-cloud-monitoring" -version = "2.29.1" +version = "2.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -2210,9 +2238,9 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/06/9fc0a34bed4221a68eef3e0373ae054de367dc42c0b689d5d917587ef61b/google_cloud_monitoring-2.29.1.tar.gz", hash = "sha256:86cac55cdd2608561819d19544fb3c129bbb7dcecc445d8de426e34cd6fa8e49", size = 404383, upload-time = "2026-02-05T18:59:13.026Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/3f/7bc306ebb006114f58fb9143aec91e1b014a11577350d8bbd6bbc38389f9/google_cloud_monitoring-2.30.0.tar.gz", hash = "sha256:a9530aa9aa246c490810dfa7be32d67e8340d19108acc99cbc02d1ed494fba76", size = 407108, upload-time = "2026-03-26T22:17:10.365Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/97/7c27aa95eccf8b62b066295a7c4ad04284364b696d3e7d9d47152b255a24/google_cloud_monitoring-2.29.1-py3-none-any.whl", hash = "sha256:944a57031f20da38617d184d5658c1f938e019e8061f27fd944584831a1b9d5a", size = 387922, upload-time = "2026-02-05T18:58:54.964Z" }, + { url = "https://files.pythonhosted.org/packages/ad/c8/666c21c470b9d6fd62ac9ee74dc265419975228f9b16f8ad72ec22e8d98b/google_cloud_monitoring-2.30.0-py3-none-any.whl", hash = "sha256:2729f3b88a4798b7757b1d9d31b6cb562bb3544e8173765e4e5cd44d8685b1ed", size = 391367, upload-time = "2026-03-26T22:15:04.088Z" }, ] [[package]] @@ -2237,7 +2265,7 @@ wheels = [ [[package]] name = "google-cloud-resource-manager" -version = "1.16.0" +version = "1.17.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -2247,14 +2275,14 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/7f/db00b2820475793a52958dc55fe9ec2eb8e863546e05fcece9b921f86ebe/google_cloud_resource_manager-1.16.0.tar.gz", hash = "sha256:cc938f87cc36c2672f062b1e541650629e0d954c405a4dac35ceedee70c267c3", size = 459840, upload-time = "2026-01-15T13:04:07.726Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/1a/13060cabf553d52d151d2afc26b39561e82853380d499dd525a0d422d9f0/google_cloud_resource_manager-1.17.0.tar.gz", hash = "sha256:0f486b62e2c58ff992a3a50fa0f4a96eef7750aa6c971bb373398ccb91828660", size = 464971, upload-time = "2026-03-26T22:17:29.204Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/ff/4b28bcc791d9d7e4ac8fea00fbd90ccb236afda56746a3b4564d2ae45df3/google_cloud_resource_manager-1.16.0-py3-none-any.whl", hash = "sha256:fb9a2ad2b5053c508e1c407ac31abfd1a22e91c32876c1892830724195819a28", size = 400218, upload-time = "2026-01-15T13:02:47.378Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f7/661d7a9023e877a226b5683429c3662f75a29ef45cb1464cf39adb689218/google_cloud_resource_manager-1.17.0-py3-none-any.whl", hash = "sha256:e479baf4b014a57f298e01b8279e3290b032e3476d69c8e5e1427af8f82739a5", size = 404403, upload-time = "2026-03-26T22:15:26.57Z" }, ] [[package]] name = "google-cloud-secret-manager" -version = "2.26.0" +version = "2.27.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -2264,9 +2292,9 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/9c/a6c7144bc96df77376ae3fcc916fb639c40814c2e4bba2051d31dc136cd0/google_cloud_secret_manager-2.26.0.tar.gz", hash = "sha256:0d1d6f76327685a0ed78a4cf50f289e1bfbbe56026ed0affa98663b86d6d50d6", size = 277603, upload-time = "2025-12-18T00:29:31.065Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/df/fbea0536e1baa6ea2239fdd19e9e22c9d64c8e26a0f3921596ecc0e5397d/google_cloud_secret_manager-2.27.0.tar.gz", hash = "sha256:6af864c252bd3c11db7bb02b80cb0b14a8c9a33fc7ec4d6f245f33d8ce1f7cd1", size = 279769, upload-time = "2026-03-26T22:17:15.271Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/30/a58739dd12cec0f7f761ed1efb518aed2250a407d4ed14c5a0eeee7eaaf9/google_cloud_secret_manager-2.26.0-py3-none-any.whl", hash = "sha256:940a5447a6ec9951446fd1a0f22c81a4303fde164cd747aae152c5f5c8e6723e", size = 223623, upload-time = "2025-12-18T00:29:29.311Z" }, + { url = "https://files.pythonhosted.org/packages/1f/4b/6dd1e2efd9a2e73aa847fd455a1ce375d8d3cba1a2c4f7fd69f9bf0b9dce/google_cloud_secret_manager-2.27.0-py3-none-any.whl", hash = "sha256:e5540bece65a3ad720146f3b438973faf9315109b3ffa012a58711843047a3dc", size = 225577, upload-time = "2026-03-26T22:15:19.622Z" }, ] [[package]] @@ -2295,7 +2323,7 @@ wheels = [ [[package]] name = "google-cloud-speech" -version = "2.37.0" +version = "2.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -2304,9 +2332,9 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/29/f4/ba24128f860639ac7ddef3c1bd2f44b390f3bb0386dda65b3a65948beeed/google_cloud_speech-2.37.0.tar.gz", hash = "sha256:1b2debf721954f1157fb2631d19b29fbeeba5736e58b71aaf10734d6365add59", size = 402950, upload-time = "2026-02-27T14:12:59.384Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/1f/d0122ad8af8c0608fb3168bd5030e62ce0a1fcc09c730487bc8be541874a/google_cloud_speech-2.38.0.tar.gz", hash = "sha256:1854b51cbb7957273b6ba61f4a6cf49dec8d09ec450991587897e50267eaca51", size = 406015, upload-time = "2026-03-26T22:18:54.434Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/c5/7a0a0f6b64cd5b23a4d573d820b03b9569730a9d3dfe5aedb00f8e8a914f/google_cloud_speech-2.37.0-py3-none-any.whl", hash = "sha256:370abd51244ffc68062d655d3063e083fad525416e0cb31737f4804e3cd8588c", size = 343295, upload-time = "2026-02-27T14:12:39.579Z" }, + { url = "https://files.pythonhosted.org/packages/01/96/008365cddc78720d65475091be929466fb16c62b47283546f8eab5ff4445/google_cloud_speech-2.38.0-py3-none-any.whl", hash = "sha256:dbccb340a750a409b0e70c48c16c8d7d5d48a87c70cce2add50f3d571f5375a0", size = 346013, upload-time = "2026-03-26T22:13:50.88Z" }, ] [[package]] @@ -2328,7 +2356,7 @@ wheels = [ [[package]] name = "google-cloud-trace" -version = "1.18.0" +version = "1.19.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -2337,9 +2365,9 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/34/b1883f4682f1681941100df0e411cb0185013f7c349489ab1330348d7c5c/google_cloud_trace-1.18.0.tar.gz", hash = "sha256:46d42b90273da3bc4850bb0d6b9a205eb826a54561ff1b30ca33cc92174c3f37", size = 103347, upload-time = "2026-01-15T13:04:56.441Z" } +sdist = { url = "https://files.pythonhosted.org/packages/89/7b/c2a5848c4722373c92b500b65e6308ad89ca0c7c01054e0d948c58c107f2/google_cloud_trace-1.19.0.tar.gz", hash = "sha256:58293c6efcee6c74bb854ff01b008823bef66845c14f15ffa5209d545098a65d", size = 103875, upload-time = "2026-03-26T22:18:18.123Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/15/366fd8b028a50a9018c933270d220a4e53dca8022ce9086618b72978ab90/google_cloud_trace-1.18.0-py3-none-any.whl", hash = "sha256:52c002d8d3da802e031fee62cd49a1baf899932d4f548a150f685af6815b5554", size = 107488, upload-time = "2026-01-15T12:17:21.519Z" }, + { url = "https://files.pythonhosted.org/packages/a4/91/0090acafa7d2caf1bf0d7222d42935e118164a539f9f9a00a814afa63fa1/google_cloud_trace-1.19.0-py3-none-any.whl", hash = "sha256:59604c4c775c40af31b367df6bada0af34518cc35ac8cfedecd43898a120c51d", size = 108454, upload-time = "2026-03-26T22:14:32.631Z" }, ] [[package]] @@ -2379,7 +2407,7 @@ wheels = [ [[package]] name = "google-genai" -version = "1.68.0" +version = "1.69.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2393,9 +2421,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9c/2c/f059982dbcb658cc535c81bbcbe7e2c040d675f4b563b03cdb01018a4bc3/google_genai-1.68.0.tar.gz", hash = "sha256:ac30c0b8bc630f9372993a97e4a11dae0e36f2e10d7c55eacdca95a9fa14ca96", size = 511285, upload-time = "2026-03-18T01:03:18.243Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/5e/c0a5e6ff60d18d3f19819a9b1fbd6a1ef2162d025696d8660550739168dc/google_genai-1.69.0.tar.gz", hash = "sha256:5f1a6a478e0c5851506a3d337534bab27b3c33120e27bf9174507ea79dfb8673", size = 519538, upload-time = "2026-03-28T15:33:27.308Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/de/7d3ee9c94b74c3578ea4f88d45e8de9405902f857932334d81e89bce3dfa/google_genai-1.68.0-py3-none-any.whl", hash = "sha256:a1bc9919c0e2ea2907d1e319b65471d3d6d58c54822039a249fe1323e4178d15", size = 750912, upload-time = "2026-03-18T01:03:15.983Z" }, + { url = "https://files.pythonhosted.org/packages/42/58/ef0586019f54b2ebb36deed7608ccb5efe1377564d2aaea6b1e295d1fadc/google_genai-1.69.0-py3-none-any.whl", hash = "sha256:252e714d724aba74949647b9de511a6a6f7804b3b317ab39ddee9cc2f001cacc", size = 760551, upload-time = "2026-03-28T15:33:24.957Z" }, ] [[package]] @@ -2412,14 +2440,14 @@ wheels = [ [[package]] name = "googleapis-common-protos" -version = "1.73.0" +version = "1.73.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/96/a0205167fa0154f4a542fd6925bdc63d039d88dab3588b875078107e6f06/googleapis_common_protos-1.73.0.tar.gz", hash = "sha256:778d07cd4fbeff84c6f7c72102f0daf98fa2bfd3fa8bea426edc545588da0b5a", size = 147323, upload-time = "2026-03-06T21:53:09.727Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/c0/4a54c386282c13449eca8bbe2ddb518181dc113e78d240458a68856b4d69/googleapis_common_protos-1.73.1.tar.gz", hash = "sha256:13114f0e9d2391756a0194c3a8131974ed7bffb06086569ba193364af59163b6", size = 147506, upload-time = "2026-03-26T22:17:38.451Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/28/23eea8acd65972bbfe295ce3666b28ac510dfcb115fac089d3edb0feb00a/googleapis_common_protos-1.73.0-py3-none-any.whl", hash = "sha256:dfdaaa2e860f242046be561e6d6cb5c5f1541ae02cfbcb034371aadb2942b4e8", size = 297578, upload-time = "2026-03-06T21:52:33.933Z" }, + { url = "https://files.pythonhosted.org/packages/dc/82/fcb6520612bec0c39b973a6c0954b6a0d948aadfe8f7e9487f60ceb8bfa6/googleapis_common_protos-1.73.1-py3-none-any.whl", hash = "sha256:e51f09eb0a43a8602f5a915870972e6b4a394088415c79d79605a46d8e826ee8", size = 297556, upload-time = "2026-03-26T22:15:58.455Z" }, ] [package.optional-dependencies] @@ -2524,77 +2552,77 @@ wheels = [ [[package]] name = "grpcio" -version = "1.78.0" +version = "1.80.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/06/8a/3d098f35c143a89520e568e6539cc098fcd294495910e359889ce8741c84/grpcio-1.78.0.tar.gz", hash = "sha256:7382b95189546f375c174f53a5fa873cef91c4b8005faa05cc5b3beea9c4f1c5", size = 12852416, upload-time = "2026-02-06T09:57:18.093Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/a8/690a085b4d1fe066130de97a87de32c45062cf2ecd218df9675add895550/grpcio-1.78.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:7cc47943d524ee0096f973e1081cb8f4f17a4615f2116882a5f1416e4cfe92b5", size = 5946986, upload-time = "2026-02-06T09:54:34.043Z" }, - { url = "https://files.pythonhosted.org/packages/c7/1b/e5213c5c0ced9d2d92778d30529ad5bb2dcfb6c48c4e2d01b1f302d33d64/grpcio-1.78.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:c3f293fdc675ccba4db5a561048cca627b5e7bd1c8a6973ffedabe7d116e22e2", size = 11816533, upload-time = "2026-02-06T09:54:37.04Z" }, - { url = "https://files.pythonhosted.org/packages/18/37/1ba32dccf0a324cc5ace744c44331e300b000a924bf14840f948c559ede7/grpcio-1.78.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:10a9a644b5dd5aec3b82b5b0b90d41c0fa94c85ef42cb42cf78a23291ddb5e7d", size = 6519964, upload-time = "2026-02-06T09:54:40.268Z" }, - { url = "https://files.pythonhosted.org/packages/ed/f5/c0e178721b818072f2e8b6fde13faaba942406c634009caf065121ce246b/grpcio-1.78.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4c5533d03a6cbd7f56acfc9cfb44ea64f63d29091e40e44010d34178d392d7eb", size = 7198058, upload-time = "2026-02-06T09:54:42.389Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b2/40d43c91ae9cd667edc960135f9f08e58faa1576dc95af29f66ec912985f/grpcio-1.78.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ff870aebe9a93a85283837801d35cd5f8814fe2ad01e606861a7fb47c762a2b7", size = 6727212, upload-time = "2026-02-06T09:54:44.91Z" }, - { url = "https://files.pythonhosted.org/packages/ed/88/9da42eed498f0efcfcd9156e48ae63c0cde3bea398a16c99fb5198c885b6/grpcio-1.78.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:391e93548644e6b2726f1bb84ed60048d4bcc424ce5e4af0843d28ca0b754fec", size = 7300845, upload-time = "2026-02-06T09:54:47.562Z" }, - { url = "https://files.pythonhosted.org/packages/23/3f/1c66b7b1b19a8828890e37868411a6e6925df5a9030bfa87ab318f34095d/grpcio-1.78.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:df2c8f3141f7cbd112a6ebbd760290b5849cda01884554f7c67acc14e7b1758a", size = 8284605, upload-time = "2026-02-06T09:54:50.475Z" }, - { url = "https://files.pythonhosted.org/packages/94/c4/ca1bd87394f7b033e88525384b4d1e269e8424ab441ea2fba1a0c5b50986/grpcio-1.78.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bd8cb8026e5f5b50498a3c4f196f57f9db344dad829ffae16b82e4fdbaea2813", size = 7726672, upload-time = "2026-02-06T09:54:53.11Z" }, - { url = "https://files.pythonhosted.org/packages/41/09/f16e487d4cc65ccaf670f6ebdd1a17566b965c74fc3d93999d3b2821e052/grpcio-1.78.0-cp310-cp310-win32.whl", hash = "sha256:f8dff3d9777e5d2703a962ee5c286c239bf0ba173877cc68dc02c17d042e29de", size = 4076715, upload-time = "2026-02-06T09:54:55.549Z" }, - { url = "https://files.pythonhosted.org/packages/2a/32/4ce60d94e242725fd3bcc5673c04502c82a8e87b21ea411a63992dc39f8f/grpcio-1.78.0-cp310-cp310-win_amd64.whl", hash = "sha256:94f95cf5d532d0e717eed4fc1810e8e6eded04621342ec54c89a7c2f14b581bf", size = 4799157, upload-time = "2026-02-06T09:54:59.838Z" }, - { url = "https://files.pythonhosted.org/packages/86/c7/d0b780a29b0837bf4ca9580904dfb275c1fc321ded7897d620af7047ec57/grpcio-1.78.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2777b783f6c13b92bd7b716667452c329eefd646bfb3f2e9dabea2e05dbd34f6", size = 5951525, upload-time = "2026-02-06T09:55:01.989Z" }, - { url = "https://files.pythonhosted.org/packages/c5/b1/96920bf2ee61df85a9503cb6f733fe711c0ff321a5a697d791b075673281/grpcio-1.78.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:9dca934f24c732750389ce49d638069c3892ad065df86cb465b3fa3012b70c9e", size = 11830418, upload-time = "2026-02-06T09:55:04.462Z" }, - { url = "https://files.pythonhosted.org/packages/83/0c/7c1528f098aeb75a97de2bae18c530f56959fb7ad6c882db45d9884d6edc/grpcio-1.78.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:459ab414b35f4496138d0ecd735fed26f1318af5e52cb1efbc82a09f0d5aa911", size = 6524477, upload-time = "2026-02-06T09:55:07.111Z" }, - { url = "https://files.pythonhosted.org/packages/8d/52/e7c1f3688f949058e19a011c4e0dec973da3d0ae5e033909677f967ae1f4/grpcio-1.78.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:082653eecbdf290e6e3e2c276ab2c54b9e7c299e07f4221872380312d8cf395e", size = 7198266, upload-time = "2026-02-06T09:55:10.016Z" }, - { url = "https://files.pythonhosted.org/packages/e5/61/8ac32517c1e856677282c34f2e7812d6c328fa02b8f4067ab80e77fdc9c9/grpcio-1.78.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85f93781028ec63f383f6bc90db785a016319c561cc11151fbb7b34e0d012303", size = 6730552, upload-time = "2026-02-06T09:55:12.207Z" }, - { url = "https://files.pythonhosted.org/packages/bd/98/b8ee0158199250220734f620b12e4a345955ac7329cfd908d0bf0fda77f0/grpcio-1.78.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f12857d24d98441af6a1d5c87442d624411db486f7ba12550b07788f74b67b04", size = 7304296, upload-time = "2026-02-06T09:55:15.044Z" }, - { url = "https://files.pythonhosted.org/packages/bd/0f/7b72762e0d8840b58032a56fdbd02b78fc645b9fa993d71abf04edbc54f4/grpcio-1.78.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5397fff416b79e4b284959642a4e95ac4b0f1ece82c9993658e0e477d40551ec", size = 8288298, upload-time = "2026-02-06T09:55:17.276Z" }, - { url = "https://files.pythonhosted.org/packages/24/ae/ae4ce56bc5bb5caa3a486d60f5f6083ac3469228faa734362487176c15c5/grpcio-1.78.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fbe6e89c7ffb48518384068321621b2a69cab509f58e40e4399fdd378fa6d074", size = 7730953, upload-time = "2026-02-06T09:55:19.545Z" }, - { url = "https://files.pythonhosted.org/packages/b5/6e/8052e3a28eb6a820c372b2eb4b5e32d195c661e137d3eca94d534a4cfd8a/grpcio-1.78.0-cp311-cp311-win32.whl", hash = "sha256:6092beabe1966a3229f599d7088b38dfc8ffa1608b5b5cdda31e591e6500f856", size = 4076503, upload-time = "2026-02-06T09:55:21.521Z" }, - { url = "https://files.pythonhosted.org/packages/08/62/f22c98c5265dfad327251fa2f840b591b1df5f5e15d88b19c18c86965b27/grpcio-1.78.0-cp311-cp311-win_amd64.whl", hash = "sha256:1afa62af6e23f88629f2b29ec9e52ec7c65a7176c1e0a83292b93c76ca882558", size = 4799767, upload-time = "2026-02-06T09:55:24.107Z" }, - { url = "https://files.pythonhosted.org/packages/4e/f4/7384ed0178203d6074446b3c4f46c90a22ddf7ae0b3aee521627f54cfc2a/grpcio-1.78.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:f9ab915a267fc47c7e88c387a3a28325b58c898e23d4995f765728f4e3dedb97", size = 5913985, upload-time = "2026-02-06T09:55:26.832Z" }, - { url = "https://files.pythonhosted.org/packages/81/ed/be1caa25f06594463f685b3790b320f18aea49b33166f4141bfdc2bfb236/grpcio-1.78.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3f8904a8165ab21e07e58bf3e30a73f4dffc7a1e0dbc32d51c61b5360d26f43e", size = 11811853, upload-time = "2026-02-06T09:55:29.224Z" }, - { url = "https://files.pythonhosted.org/packages/24/a7/f06d151afc4e64b7e3cc3e872d331d011c279aaab02831e40a81c691fb65/grpcio-1.78.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:859b13906ce098c0b493af92142ad051bf64c7870fa58a123911c88606714996", size = 6475766, upload-time = "2026-02-06T09:55:31.825Z" }, - { url = "https://files.pythonhosted.org/packages/8a/a8/4482922da832ec0082d0f2cc3a10976d84a7424707f25780b82814aafc0a/grpcio-1.78.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b2342d87af32790f934a79c3112641e7b27d63c261b8b4395350dad43eff1dc7", size = 7170027, upload-time = "2026-02-06T09:55:34.7Z" }, - { url = "https://files.pythonhosted.org/packages/54/bf/f4a3b9693e35d25b24b0b39fa46d7d8a3c439e0a3036c3451764678fec20/grpcio-1.78.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12a771591ae40bc65ba67048fa52ef4f0e6db8279e595fd349f9dfddeef571f9", size = 6690766, upload-time = "2026-02-06T09:55:36.902Z" }, - { url = "https://files.pythonhosted.org/packages/c7/b9/521875265cc99fe5ad4c5a17010018085cae2810a928bf15ebe7d8bcd9cc/grpcio-1.78.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:185dea0d5260cbb2d224c507bf2a5444d5abbb1fa3594c1ed7e4c709d5eb8383", size = 7266161, upload-time = "2026-02-06T09:55:39.824Z" }, - { url = "https://files.pythonhosted.org/packages/05/86/296a82844fd40a4ad4a95f100b55044b4f817dece732bf686aea1a284147/grpcio-1.78.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51b13f9aed9d59ee389ad666b8c2214cc87b5de258fa712f9ab05f922e3896c6", size = 8253303, upload-time = "2026-02-06T09:55:42.353Z" }, - { url = "https://files.pythonhosted.org/packages/f3/e4/ea3c0caf5468537f27ad5aab92b681ed7cc0ef5f8c9196d3fd42c8c2286b/grpcio-1.78.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd5f135b1bd58ab088930b3c613455796dfa0393626a6972663ccdda5b4ac6ce", size = 7698222, upload-time = "2026-02-06T09:55:44.629Z" }, - { url = "https://files.pythonhosted.org/packages/d7/47/7f05f81e4bb6b831e93271fb12fd52ba7b319b5402cbc101d588f435df00/grpcio-1.78.0-cp312-cp312-win32.whl", hash = "sha256:94309f498bcc07e5a7d16089ab984d42ad96af1d94b5a4eb966a266d9fcabf68", size = 4066123, upload-time = "2026-02-06T09:55:47.644Z" }, - { url = "https://files.pythonhosted.org/packages/ad/e7/d6914822c88aa2974dbbd10903d801a28a19ce9cd8bad7e694cbbcf61528/grpcio-1.78.0-cp312-cp312-win_amd64.whl", hash = "sha256:9566fe4ababbb2610c39190791e5b829869351d14369603702e890ef3ad2d06e", size = 4797657, upload-time = "2026-02-06T09:55:49.86Z" }, - { url = "https://files.pythonhosted.org/packages/05/a9/8f75894993895f361ed8636cd9237f4ab39ef87fd30db17467235ed1c045/grpcio-1.78.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:ce3a90455492bf8bfa38e56fbbe1dbd4f872a3d8eeaf7337dc3b1c8aa28c271b", size = 5920143, upload-time = "2026-02-06T09:55:52.035Z" }, - { url = "https://files.pythonhosted.org/packages/55/06/0b78408e938ac424100100fd081189451b472236e8a3a1f6500390dc4954/grpcio-1.78.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:2bf5e2e163b356978b23652c4818ce4759d40f4712ee9ec5a83c4be6f8c23a3a", size = 11803926, upload-time = "2026-02-06T09:55:55.494Z" }, - { url = "https://files.pythonhosted.org/packages/88/93/b59fe7832ff6ae3c78b813ea43dac60e295fa03606d14d89d2e0ec29f4f3/grpcio-1.78.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8f2ac84905d12918e4e55a16da17939eb63e433dc11b677267c35568aa63fc84", size = 6478628, upload-time = "2026-02-06T09:55:58.533Z" }, - { url = "https://files.pythonhosted.org/packages/ed/df/e67e3734527f9926b7d9c0dde6cd998d1d26850c3ed8eeec81297967ac67/grpcio-1.78.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b58f37edab4a3881bc6c9bca52670610e0c9ca14e2ea3cf9debf185b870457fb", size = 7173574, upload-time = "2026-02-06T09:56:01.786Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/cc03fffb07bfba982a9ec097b164e8835546980aec25ecfa5f9c1a47e022/grpcio-1.78.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:735e38e176a88ce41840c21bb49098ab66177c64c82426e24e0082500cc68af5", size = 6692639, upload-time = "2026-02-06T09:56:04.529Z" }, - { url = "https://files.pythonhosted.org/packages/bf/9a/289c32e301b85bdb67d7ec68b752155e674ee3ba2173a1858f118e399ef3/grpcio-1.78.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2045397e63a7a0ee7957c25f7dbb36ddc110e0cfb418403d110c0a7a68a844e9", size = 7268838, upload-time = "2026-02-06T09:56:08.397Z" }, - { url = "https://files.pythonhosted.org/packages/0e/79/1be93f32add280461fa4773880196572563e9c8510861ac2da0ea0f892b6/grpcio-1.78.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9f136fbafe7ccf4ac7e8e0c28b31066e810be52d6e344ef954a3a70234e1702", size = 8251878, upload-time = "2026-02-06T09:56:10.914Z" }, - { url = "https://files.pythonhosted.org/packages/65/65/793f8e95296ab92e4164593674ae6291b204bb5f67f9d4a711489cd30ffa/grpcio-1.78.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:748b6138585379c737adc08aeffd21222abbda1a86a0dca2a39682feb9196c20", size = 7695412, upload-time = "2026-02-06T09:56:13.593Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/1e233fe697ecc82845942c2822ed06bb522e70d6771c28d5528e4c50f6a4/grpcio-1.78.0-cp313-cp313-win32.whl", hash = "sha256:271c73e6e5676afe4fc52907686670c7cea22ab2310b76a59b678403ed40d670", size = 4064899, upload-time = "2026-02-06T09:56:15.601Z" }, - { url = "https://files.pythonhosted.org/packages/4d/27/d86b89e36de8a951501fb06a0f38df19853210f341d0b28f83f4aa0ffa08/grpcio-1.78.0-cp313-cp313-win_amd64.whl", hash = "sha256:f2d4e43ee362adfc05994ed479334d5a451ab7bc3f3fee1b796b8ca66895acb4", size = 4797393, upload-time = "2026-02-06T09:56:17.882Z" }, - { url = "https://files.pythonhosted.org/packages/29/f2/b56e43e3c968bfe822fa6ce5bca10d5c723aa40875b48791ce1029bb78c7/grpcio-1.78.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:e87cbc002b6f440482b3519e36e1313eb5443e9e9e73d6a52d43bd2004fcfd8e", size = 5920591, upload-time = "2026-02-06T09:56:20.758Z" }, - { url = "https://files.pythonhosted.org/packages/5d/81/1f3b65bd30c334167bfa8b0d23300a44e2725ce39bba5b76a2460d85f745/grpcio-1.78.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:c41bc64626db62e72afec66b0c8a0da76491510015417c127bfc53b2fe6d7f7f", size = 11813685, upload-time = "2026-02-06T09:56:24.315Z" }, - { url = "https://files.pythonhosted.org/packages/0e/1c/bbe2f8216a5bd3036119c544d63c2e592bdf4a8ec6e4a1867592f4586b26/grpcio-1.78.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8dfffba826efcf366b1e3ccc37e67afe676f290e13a3b48d31a46739f80a8724", size = 6487803, upload-time = "2026-02-06T09:56:27.367Z" }, - { url = "https://files.pythonhosted.org/packages/16/5c/a6b2419723ea7ddce6308259a55e8e7593d88464ce8db9f4aa857aba96fa/grpcio-1.78.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:74be1268d1439eaaf552c698cdb11cd594f0c49295ae6bb72c34ee31abbe611b", size = 7173206, upload-time = "2026-02-06T09:56:29.876Z" }, - { url = "https://files.pythonhosted.org/packages/df/1e/b8801345629a415ea7e26c83d75eb5dbe91b07ffe5210cc517348a8d4218/grpcio-1.78.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be63c88b32e6c0f1429f1398ca5c09bc64b0d80950c8bb7807d7d7fb36fb84c7", size = 6693826, upload-time = "2026-02-06T09:56:32.305Z" }, - { url = "https://files.pythonhosted.org/packages/34/84/0de28eac0377742679a510784f049738a80424b17287739fc47d63c2439e/grpcio-1.78.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3c586ac70e855c721bda8f548d38c3ca66ac791dc49b66a8281a1f99db85e452", size = 7277897, upload-time = "2026-02-06T09:56:34.915Z" }, - { url = "https://files.pythonhosted.org/packages/ca/9c/ad8685cfe20559a9edb66f735afdcb2b7d3de69b13666fdfc542e1916ebd/grpcio-1.78.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:35eb275bf1751d2ffbd8f57cdbc46058e857cf3971041521b78b7db94bdaf127", size = 8252404, upload-time = "2026-02-06T09:56:37.553Z" }, - { url = "https://files.pythonhosted.org/packages/3c/05/33a7a4985586f27e1de4803887c417ec7ced145ebd069bc38a9607059e2b/grpcio-1.78.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:207db540302c884b8848036b80db352a832b99dfdf41db1eb554c2c2c7800f65", size = 7696837, upload-time = "2026-02-06T09:56:40.173Z" }, - { url = "https://files.pythonhosted.org/packages/73/77/7382241caf88729b106e49e7d18e3116216c778e6a7e833826eb96de22f7/grpcio-1.78.0-cp314-cp314-win32.whl", hash = "sha256:57bab6deef2f4f1ca76cc04565df38dc5713ae6c17de690721bdf30cb1e0545c", size = 4142439, upload-time = "2026-02-06T09:56:43.258Z" }, - { url = "https://files.pythonhosted.org/packages/48/b2/b096ccce418882fbfda4f7496f9357aaa9a5af1896a9a7f60d9f2b275a06/grpcio-1.78.0-cp314-cp314-win_amd64.whl", hash = "sha256:dce09d6116df20a96acfdbf85e4866258c3758180e8c49845d6ba8248b6d0bbb", size = 4929852, upload-time = "2026-02-06T09:56:45.885Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/b7/48/af6173dbca4454f4637a4678b67f52ca7e0c1ed7d5894d89d434fecede05/grpcio-1.80.0.tar.gz", hash = "sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257", size = 12978905, upload-time = "2026-03-30T08:49:10.502Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/cd/bb7b7e54084a344c03d68144450da7ddd5564e51a298ae1662de65f48e2d/grpcio-1.80.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:886457a7768e408cdce226ad1ca67d2958917d306523a0e21e1a2fdaa75c9c9c", size = 6050363, upload-time = "2026-03-30T08:46:20.894Z" }, + { url = "https://files.pythonhosted.org/packages/16/02/1417f5c3460dea65f7a2e3c14e8b31e77f7ffb730e9bfadd89eda7a9f477/grpcio-1.80.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:7b641fc3f1dc647bfd80bd713addc68f6d145956f64677e56d9ebafc0bd72388", size = 12026037, upload-time = "2026-03-30T08:46:25.144Z" }, + { url = "https://files.pythonhosted.org/packages/43/98/c910254eedf2cae368d78336a2de0678e66a7317d27c02522392f949b5c6/grpcio-1.80.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:33eb763f18f006dc7fee1e69831d38d23f5eccd15b2e0f92a13ee1d9242e5e02", size = 6602306, upload-time = "2026-03-30T08:46:27.593Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f8/88ca4e78c077b2b2113d95da1e1ab43efd43d723c9a0397d26529c2c1a56/grpcio-1.80.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:52d143637e3872633fc7dd7c3c6a1c84e396b359f3a72e215f8bf69fd82084fc", size = 7301535, upload-time = "2026-03-30T08:46:29.556Z" }, + { url = "https://files.pythonhosted.org/packages/f9/96/f28660fe2fe0f153288bf4a04e4910b7309d442395135c88ed4f5b3b8b40/grpcio-1.80.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c51bf8ac4575af2e0678bccfb07e47321fc7acb5049b4482832c5c195e04e13a", size = 6808669, upload-time = "2026-03-30T08:46:31.984Z" }, + { url = "https://files.pythonhosted.org/packages/47/eb/3f68a5e955779c00aeef23850e019c1c1d0e032d90633ba49c01ad5a96e0/grpcio-1.80.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:50a9871536d71c4fba24ee856abc03a87764570f0c457dd8db0b4018f379fed9", size = 7409489, upload-time = "2026-03-30T08:46:34.684Z" }, + { url = "https://files.pythonhosted.org/packages/5b/a7/d2f681a4bfb881be40659a309771f3bdfbfdb1190619442816c3f0ffc079/grpcio-1.80.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a72d84ad0514db063e21887fbacd1fd7acb4d494a564cae22227cd45c7fbf199", size = 8423167, upload-time = "2026-03-30T08:46:36.833Z" }, + { url = "https://files.pythonhosted.org/packages/97/8a/29b4589c204959aa35ce5708400a05bba72181807c45c47b3ec000c39333/grpcio-1.80.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f7691a6788ad9196872f95716df5bc643ebba13c97140b7a5ee5c8e75d1dea81", size = 7846761, upload-time = "2026-03-30T08:46:40.091Z" }, + { url = "https://files.pythonhosted.org/packages/6b/d2/ed143e097230ee121ac5848f6ff14372dba91289b10b536d54fb1b7cbae7/grpcio-1.80.0-cp310-cp310-win32.whl", hash = "sha256:46c2390b59d67f84e882694d489f5b45707c657832d7934859ceb8c33f467069", size = 4156534, upload-time = "2026-03-30T08:46:42.026Z" }, + { url = "https://files.pythonhosted.org/packages/d5/c9/df8279bb49b29409995e95efa85b72973d62f8aeff89abee58c91f393710/grpcio-1.80.0-cp310-cp310-win_amd64.whl", hash = "sha256:dc053420fc75749c961e2a4c906398d7c15725d36ccc04ae6d16093167223b58", size = 4889869, upload-time = "2026-03-30T08:46:44.219Z" }, + { url = "https://files.pythonhosted.org/packages/5d/db/1d56e5f5823257b291962d6c0ce106146c6447f405b60b234c4f222a7cde/grpcio-1.80.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:dfab85db094068ff42e2a3563f60ab3dddcc9d6488a35abf0132daec13209c8a", size = 6055009, upload-time = "2026-03-30T08:46:46.265Z" }, + { url = "https://files.pythonhosted.org/packages/6e/18/c83f3cad64c5ca63bca7e91e5e46b0d026afc5af9d0a9972472ceba294b3/grpcio-1.80.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:5c07e82e822e1161354e32da2662f741a4944ea955f9f580ec8fb409dd6f6060", size = 12035295, upload-time = "2026-03-30T08:46:49.099Z" }, + { url = "https://files.pythonhosted.org/packages/0f/8e/e14966b435be2dda99fbe89db9525ea436edc79780431a1c2875a3582644/grpcio-1.80.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba0915d51fd4ced2db5ff719f84e270afe0e2d4c45a7bdb1e8d036e4502928c2", size = 6610297, upload-time = "2026-03-30T08:46:52.123Z" }, + { url = "https://files.pythonhosted.org/packages/cc/26/d5eb38f42ce0e3fdc8174ea4d52036ef8d58cc4426cb800f2610f625dd75/grpcio-1.80.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3cb8130ba457d2aa09fa6b7c3ed6b6e4e6a2685fce63cb803d479576c4d80e21", size = 7300208, upload-time = "2026-03-30T08:46:54.859Z" }, + { url = "https://files.pythonhosted.org/packages/25/51/bd267c989f85a17a5b3eea65a6feb4ff672af41ca614e5a0279cc0ea381c/grpcio-1.80.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:09e5e478b3d14afd23f12e49e8b44c8684ac3c5f08561c43a5b9691c54d136ab", size = 6813442, upload-time = "2026-03-30T08:46:57.056Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d9/d80eef735b19e9169e30164bbf889b46f9df9127598a83d174eb13a48b26/grpcio-1.80.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:00168469238b022500e486c1c33916acf2f2a9b2c022202cf8a1885d2e3073c1", size = 7414743, upload-time = "2026-03-30T08:46:59.682Z" }, + { url = "https://files.pythonhosted.org/packages/de/f2/567f5bd5054398ed6b0509b9a30900376dcf2786bd936812098808b49d8d/grpcio-1.80.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8502122a3cc1714038e39a0b071acb1207ca7844208d5ea0d091317555ee7106", size = 8426046, upload-time = "2026-03-30T08:47:02.474Z" }, + { url = "https://files.pythonhosted.org/packages/62/29/73ef0141b4732ff5eacd68430ff2512a65c004696997f70476a83e548e7e/grpcio-1.80.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ce1794f4ea6cc3ca29463f42d665c32ba1b964b48958a66497917fe9069f26e6", size = 7851641, upload-time = "2026-03-30T08:47:05.462Z" }, + { url = "https://files.pythonhosted.org/packages/46/69/abbfa360eb229a8623bab5f5a4f8105e445bd38ce81a89514ba55d281ad0/grpcio-1.80.0-cp311-cp311-win32.whl", hash = "sha256:51b4a7189b0bef2aa30adce3c78f09c83526cf3dddb24c6a96555e3b97340440", size = 4154368, upload-time = "2026-03-30T08:47:08.027Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d4/ae92206d01183b08613e846076115f5ac5991bae358d2a749fa864da5699/grpcio-1.80.0-cp311-cp311-win_amd64.whl", hash = "sha256:02e64bb0bb2da14d947a49e6f120a75e947250aebe65f9629b62bb1f5c14e6e9", size = 4894235, upload-time = "2026-03-30T08:47:10.839Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e8/a2b749265eb3415abc94f2e619bbd9e9707bebdda787e61c593004ec927a/grpcio-1.80.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:c624cc9f1008361014378c9d776de7182b11fe8b2e5a81bc69f23a295f2a1ad0", size = 6015616, upload-time = "2026-03-30T08:47:13.428Z" }, + { url = "https://files.pythonhosted.org/packages/3e/97/b1282161a15d699d1e90c360df18d19165a045ce1c343c7f313f5e8a0b77/grpcio-1.80.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:f49eddcac43c3bf350c0385366a58f36bed8cc2c0ec35ef7b74b49e56552c0c2", size = 12014204, upload-time = "2026-03-30T08:47:15.873Z" }, + { url = "https://files.pythonhosted.org/packages/6e/5e/d319c6e997b50c155ac5a8cb12f5173d5b42677510e886d250d50264949d/grpcio-1.80.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d334591df610ab94714048e0d5b4f3dd5ad1bee74dfec11eee344220077a79de", size = 6563866, upload-time = "2026-03-30T08:47:18.588Z" }, + { url = "https://files.pythonhosted.org/packages/ae/f6/fdd975a2cb4d78eb67769a7b3b3830970bfa2e919f1decf724ae4445f42c/grpcio-1.80.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0cb517eb1d0d0aaf1d87af7cc5b801d686557c1d88b2619f5e31fab3c2315921", size = 7273060, upload-time = "2026-03-30T08:47:21.113Z" }, + { url = "https://files.pythonhosted.org/packages/db/f0/a3deb5feba60d9538a962913e37bd2e69a195f1c3376a3dd44fe0427e996/grpcio-1.80.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4e78c4ac0d97dc2e569b2f4bcbbb447491167cb358d1a389fc4af71ab6f70411", size = 6782121, upload-time = "2026-03-30T08:47:23.827Z" }, + { url = "https://files.pythonhosted.org/packages/ca/84/36c6dcfddc093e108141f757c407902a05085e0c328007cb090d56646cdf/grpcio-1.80.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2ed770b4c06984f3b47eb0517b1c69ad0b84ef3f40128f51448433be904634cd", size = 7383811, upload-time = "2026-03-30T08:47:26.517Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ef/f3a77e3dc5b471a0ec86c564c98d6adfa3510d38f8ee99010410858d591e/grpcio-1.80.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:256507e2f524092f1473071a05e65a5b10d84b82e3ff24c5b571513cfaa61e2f", size = 8393860, upload-time = "2026-03-30T08:47:29.439Z" }, + { url = "https://files.pythonhosted.org/packages/9b/8d/9d4d27ed7f33d109c50d6b5ce578a9914aa68edab75d65869a17e630a8d1/grpcio-1.80.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a6284a5d907c37db53350645567c522be314bac859a64a7a5ca63b77bb7958f", size = 7830132, upload-time = "2026-03-30T08:47:33.254Z" }, + { url = "https://files.pythonhosted.org/packages/14/e4/9990b41c6d7a44e1e9dee8ac11d7a9802ba1378b40d77468a7761d1ad288/grpcio-1.80.0-cp312-cp312-win32.whl", hash = "sha256:c71309cfce2f22be26aa4a847357c502db6c621f1a49825ae98aa0907595b193", size = 4140904, upload-time = "2026-03-30T08:47:35.319Z" }, + { url = "https://files.pythonhosted.org/packages/2f/2c/296f6138caca1f4b92a31ace4ae1b87dab692fc16a7a3417af3bb3c805bf/grpcio-1.80.0-cp312-cp312-win_amd64.whl", hash = "sha256:9fe648599c0e37594c4809d81a9e77bd138cc82eb8baa71b6a86af65426723ff", size = 4880944, upload-time = "2026-03-30T08:47:37.831Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/7c3c25789e3f069e581dc342e03613c5b1cb012c4e8c7d9d5cf960a75856/grpcio-1.80.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad", size = 6017243, upload-time = "2026-03-30T08:47:40.075Z" }, + { url = "https://files.pythonhosted.org/packages/04/19/21a9806eb8240e174fd1ab0cd5b9aa948bb0e05c2f2f55f9d5d7405e6d08/grpcio-1.80.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0", size = 12010840, upload-time = "2026-03-30T08:47:43.11Z" }, + { url = "https://files.pythonhosted.org/packages/18/3a/23347d35f76f639e807fb7a36fad3068aed100996849a33809591f26eca6/grpcio-1.80.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f", size = 6567644, upload-time = "2026-03-30T08:47:46.806Z" }, + { url = "https://files.pythonhosted.org/packages/ff/40/96e07ecb604a6a67ae6ab151e3e35b132875d98bc68ec65f3e5ab3e781d7/grpcio-1.80.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6", size = 7277830, upload-time = "2026-03-30T08:47:49.643Z" }, + { url = "https://files.pythonhosted.org/packages/9b/e2/da1506ecea1f34a5e365964644b35edef53803052b763ca214ba3870c856/grpcio-1.80.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140", size = 6783216, upload-time = "2026-03-30T08:47:52.817Z" }, + { url = "https://files.pythonhosted.org/packages/44/83/3b20ff58d0c3b7f6caaa3af9a4174d4023701df40a3f39f7f1c8e7c48f9d/grpcio-1.80.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d", size = 7385866, upload-time = "2026-03-30T08:47:55.687Z" }, + { url = "https://files.pythonhosted.org/packages/47/45/55c507599c5520416de5eefecc927d6a0d7af55e91cfffb2e410607e5744/grpcio-1.80.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7", size = 8391602, upload-time = "2026-03-30T08:47:58.303Z" }, + { url = "https://files.pythonhosted.org/packages/10/bb/dd06f4c24c01db9cf11341b547d0a016b2c90ed7dbbb086a5710df7dd1d7/grpcio-1.80.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7", size = 7826752, upload-time = "2026-03-30T08:48:01.311Z" }, + { url = "https://files.pythonhosted.org/packages/f9/1e/9d67992ba23371fd63d4527096eb8c6b76d74d52b500df992a3343fd7251/grpcio-1.80.0-cp313-cp313-win32.whl", hash = "sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294", size = 4142310, upload-time = "2026-03-30T08:48:04.594Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e6/283326a27da9e2c3038bc93eeea36fb118ce0b2d03922a9cda6688f53c5b/grpcio-1.80.0-cp313-cp313-win_amd64.whl", hash = "sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50", size = 4882833, upload-time = "2026-03-30T08:48:07.363Z" }, + { url = "https://files.pythonhosted.org/packages/c5/6d/e65307ce20f5a09244ba9e9d8476e99fb039de7154f37fb85f26978b59c3/grpcio-1.80.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e", size = 6017376, upload-time = "2026-03-30T08:48:10.005Z" }, + { url = "https://files.pythonhosted.org/packages/69/10/9cef5d9650c72625a699c549940f0abb3c4bfdb5ed45a5ce431f92f31806/grpcio-1.80.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f", size = 12018133, upload-time = "2026-03-30T08:48:12.927Z" }, + { url = "https://files.pythonhosted.org/packages/04/82/983aabaad82ba26113caceeb9091706a0696b25da004fe3defb5b346e15b/grpcio-1.80.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9", size = 6574748, upload-time = "2026-03-30T08:48:16.386Z" }, + { url = "https://files.pythonhosted.org/packages/07/d7/031666ef155aa0bf399ed7e19439656c38bbd143779ae0861b038ce82abd/grpcio-1.80.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14", size = 7277711, upload-time = "2026-03-30T08:48:19.627Z" }, + { url = "https://files.pythonhosted.org/packages/e8/43/f437a78f7f4f1d311804189e8f11fb311a01049b2e08557c1068d470cb2e/grpcio-1.80.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05", size = 6785372, upload-time = "2026-03-30T08:48:22.373Z" }, + { url = "https://files.pythonhosted.org/packages/93/3d/f6558e9c6296cb4227faa5c43c54a34c68d32654b829f53288313d16a86e/grpcio-1.80.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1", size = 7395268, upload-time = "2026-03-30T08:48:25.638Z" }, + { url = "https://files.pythonhosted.org/packages/06/21/0fdd77e84720b08843c371a2efa6f2e19dbebf56adc72df73d891f5506f0/grpcio-1.80.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f", size = 8392000, upload-time = "2026-03-30T08:48:28.974Z" }, + { url = "https://files.pythonhosted.org/packages/f5/68/67f4947ed55d2e69f2cc199ab9fd85e0a0034d813bbeef84df6d2ba4d4b7/grpcio-1.80.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e", size = 7828477, upload-time = "2026-03-30T08:48:32.054Z" }, + { url = "https://files.pythonhosted.org/packages/44/b6/8d4096691b2e385e8271911a0de4f35f0a6c7d05aff7098e296c3de86939/grpcio-1.80.0-cp314-cp314-win32.whl", hash = "sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae", size = 4218563, upload-time = "2026-03-30T08:48:34.538Z" }, + { url = "https://files.pythonhosted.org/packages/e5/8c/bbe6baf2557262834f2070cf668515fa308b2d38a4bbf771f8f7872a7036/grpcio-1.80.0-cp314-cp314-win_amd64.whl", hash = "sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f", size = 5019457, upload-time = "2026-03-30T08:48:37.308Z" }, ] [[package]] name = "grpcio-status" -version = "1.78.0" +version = "1.80.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, { name = "grpcio" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/cd/89ce482a931b543b92cdd9b2888805518c4620e0094409acb8c81dd4610a/grpcio_status-1.78.0.tar.gz", hash = "sha256:a34cfd28101bfea84b5aa0f936b4b423019e9213882907166af6b3bddc59e189", size = 13808, upload-time = "2026-02-06T10:01:48.034Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/ed/105f619bdd00cb47a49aa2feea6232ea2bbb04199d52a22cc6a7d603b5cb/grpcio_status-1.80.0.tar.gz", hash = "sha256:df73802a4c89a3ea88aa2aff971e886fccce162bc2e6511408b3d67a144381cd", size = 13901, upload-time = "2026-03-30T08:54:34.784Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/8a/1241ec22c41028bddd4a052ae9369267b4475265ad0ce7140974548dc3fa/grpcio_status-1.78.0-py3-none-any.whl", hash = "sha256:b492b693d4bf27b47a6c32590701724f1d3b9444b36491878fb71f6208857f34", size = 14523, upload-time = "2026-02-06T10:01:32.584Z" }, + { url = "https://files.pythonhosted.org/packages/76/80/58cd2dfc19a07d022abe44bde7c365627f6c7cb6f692ada6c65ca437d09a/grpcio_status-1.80.0-py3-none-any.whl", hash = "sha256:4b56990363af50dbf2c2ebb80f1967185c07d87aa25aa2bea45ddb75fc181dbe", size = 14638, upload-time = "2026-03-30T08:54:01.569Z" }, ] [[package]] @@ -2764,9 +2792,9 @@ dependencies = [ { name = "appnope", marker = "sys_platform == 'darwin'" }, { name = "comm" }, { name = "debugpy" }, - { name = "ipython", version = "8.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, - { name = "ipython", version = "9.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "ipython", version = "8.39.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "ipython", version = "9.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "ipython", version = "9.12.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "jupyter-client" }, { name = "jupyter-core" }, { name = "matplotlib-inline" }, @@ -2784,7 +2812,7 @@ wheels = [ [[package]] name = "ipython" -version = "8.38.0" +version = "8.39.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.11'", @@ -2802,14 +2830,14 @@ dependencies = [ { name = "traitlets", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e5/61/1810830e8b93c72dcd3c0f150c80a00c3deb229562d9423807ec92c3a539/ipython-8.38.0.tar.gz", hash = "sha256:9cfea8c903ce0867cc2f23199ed8545eb741f3a69420bfcf3743ad1cec856d39", size = 5513996, upload-time = "2026-01-05T10:59:06.901Z" } +sdist = { url = "https://files.pythonhosted.org/packages/40/18/f8598d287006885e7136451fdea0755af4ebcbfe342836f24deefaed1164/ipython-8.39.0.tar.gz", hash = "sha256:4110ae96012c379b8b6db898a07e186c40a2a1ef5d57a7fa83166047d9da7624", size = 5513971, upload-time = "2026-03-27T10:02:13.94Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/df/db59624f4c71b39717c423409950ac3f2c8b2ce4b0aac843112c7fb3f721/ipython-8.38.0-py3-none-any.whl", hash = "sha256:750162629d800ac65bb3b543a14e7a74b0e88063eac9b92124d4b2aa3f6d8e86", size = 831813, upload-time = "2026-01-05T10:59:04.239Z" }, + { url = "https://files.pythonhosted.org/packages/c0/56/4cc7fc9e9e3f38fd324f24f8afe0ad8bb5fa41283f37f1aaf9de0612c968/ipython-8.39.0-py3-none-any.whl", hash = "sha256:bb3c51c4fa8148ab1dea07a79584d1c854e234ea44aa1283bcb37bc75054651f", size = 831849, upload-time = "2026-03-27T10:02:07.846Z" }, ] [[package]] name = "ipython" -version = "9.10.0" +version = "9.10.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version == '3.11.*' and sys_platform == 'win32'", @@ -2829,14 +2857,14 @@ dependencies = [ { name = "traitlets", marker = "python_full_version == '3.11.*'" }, { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/60/2111715ea11f39b1535bed6024b7dec7918b71e5e5d30855a5b503056b50/ipython-9.10.0.tar.gz", hash = "sha256:cd9e656be97618a0676d058134cd44e6dc7012c0e5cb36a9ce96a8c904adaf77", size = 4426526, upload-time = "2026-02-02T10:00:33.594Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/25/daae0e764047b0a2480c7bbb25d48f4f509b5818636562eeac145d06dfee/ipython-9.10.1.tar.gz", hash = "sha256:e170e9b2a44312484415bdb750492699bf329233b03f2557a9692cce6466ada4", size = 4426663, upload-time = "2026-03-27T09:53:26.244Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/aa/898dec789a05731cd5a9f50605b7b44a72bd198fd0d4528e11fc610177cc/ipython-9.10.0-py3-none-any.whl", hash = "sha256:c6ab68cc23bba8c7e18e9b932797014cc61ea7fd6f19de180ab9ba73e65ee58d", size = 622774, upload-time = "2026-02-02T10:00:31.503Z" }, + { url = "https://files.pythonhosted.org/packages/01/09/ba70f8d662d5671687da55ad2cc0064cf795b15e1eea70907532202e7c97/ipython-9.10.1-py3-none-any.whl", hash = "sha256:82d18ae9fb9164ded080c71ef92a182ee35ee7db2395f67616034bebb020a232", size = 622827, upload-time = "2026-03-27T09:53:24.566Z" }, ] [[package]] name = "ipython" -version = "9.11.0" +version = "9.12.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", @@ -2861,9 +2889,9 @@ dependencies = [ { name = "stack-data", marker = "python_full_version >= '3.12'" }, { name = "traitlets", marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/86/28/a4698eda5a8928a45d6b693578b135b753e14fa1c2b36ee9441e69a45576/ipython-9.11.0.tar.gz", hash = "sha256:2a94bc4406b22ecc7e4cb95b98450f3ea493a76bec8896cda11b78d7752a6667", size = 4427354, upload-time = "2026-03-05T08:57:30.549Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/73/7114f80a8f9cabdb13c27732dce24af945b2923dcab80723602f7c8bc2d8/ipython-9.12.0.tar.gz", hash = "sha256:01daa83f504b693ba523b5a407246cabde4eb4513285a3c6acaff11a66735ee4", size = 4428879, upload-time = "2026-03-27T09:42:45.312Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/90/45c72becc57158facc6a6404f663b77bbcea2519ca57f760e2879ae1315d/ipython-9.11.0-py3-none-any.whl", hash = "sha256:6922d5bcf944c6e525a76a0a304451b60a2b6f875e86656d8bc2dfda5d710e19", size = 624222, upload-time = "2026-03-05T08:57:28.94Z" }, + { url = "https://files.pythonhosted.org/packages/59/22/906c8108974c673ebef6356c506cebb6870d48cedea3c41e949e2dd556bb/ipython-9.12.0-py3-none-any.whl", hash = "sha256:0f2701e8ee86e117e37f50563205d36feaa259d2e08d4a6bc6b6d74b18ce128d", size = 625661, upload-time = "2026-03-27T09:42:42.831Z" }, ] [[package]] @@ -2884,9 +2912,9 @@ version = "8.1.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "comm" }, - { name = "ipython", version = "8.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, - { name = "ipython", version = "9.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "ipython", version = "8.39.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "ipython", version = "9.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "ipython", version = "9.12.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "jupyterlab-widgets" }, { name = "traitlets" }, { name = "widgetsnbextension" }, @@ -3000,14 +3028,15 @@ version = "0.5.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ipykernel" }, - { name = "ipython", version = "8.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, - { name = "ipython", version = "9.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "ipython", version = "8.39.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "ipython", version = "9.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "ipython", version = "9.12.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "ipywidgets" }, { name = "nbconvert" }, { name = "nbformat" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1a/b5/40f540cc9e54ee829f79daac43456a8d5ab4b70c8d26f0b9eca0dfcb4ad5/jupyter_sphinx-0.5.3.tar.gz", hash = "sha256:2e23699a3a1cf5db31b10981da5aa32606ee730f6b73a844d1e76d800756af56", size = 17532, upload-time = "2023-12-28T12:19:41.047Z" } wheels = [ @@ -3860,7 +3889,7 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "docutils", marker = "python_full_version < '3.11'" }, + { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "jinja2", marker = "python_full_version < '3.11'" }, { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "mdit-py-plugins", marker = "python_full_version < '3.11'" }, @@ -3891,12 +3920,13 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "docutils", marker = "python_full_version >= '3.11'" }, + { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jinja2", marker = "python_full_version >= '3.11'" }, { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "mdit-py-plugins", marker = "python_full_version >= '3.11'" }, { name = "pyyaml", marker = "python_full_version >= '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/fa/7b45eef11b7971f0beb29d27b7bfe0d747d063aa29e170d9edd004733c8a/myst_parser-5.0.0.tar.gz", hash = "sha256:f6f231452c56e8baa662cc352c548158f6a16fcbd6e3800fc594978002b94f3a", size = 98535, upload-time = "2026-01-15T09:08:18.036Z" } wheels = [ @@ -3972,12 +4002,14 @@ name = "nbsphinx" version = "0.9.8" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "docutils" }, + { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jinja2" }, { name = "nbconvert" }, { name = "nbformat" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "traitlets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e7/d1/82081750f8a78ad0399c6ed831d42623b891904e8e7b8a75878225cf1dce/nbsphinx-0.9.8.tar.gz", hash = "sha256:d0765908399a8ee2b57be7ae881cf2ea58d66db3af7bbf33e6eb48f83bea5495", size = 417469, upload-time = "2025-11-28T17:41:02.336Z" } @@ -4070,7 +4102,7 @@ wheels = [ [[package]] name = "numpy" -version = "2.4.3" +version = "2.4.4" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", @@ -4086,79 +4118,79 @@ resolution-markers = [ "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] -sdist = { url = "https://files.pythonhosted.org/packages/10/8b/c265f4823726ab832de836cdd184d0986dcf94480f81e8739692a7ac7af2/numpy-2.4.3.tar.gz", hash = "sha256:483a201202b73495f00dbc83796c6ae63137a9bdade074f7648b3e32613412dd", size = 20727743, upload-time = "2026-03-09T07:58:53.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/51/5093a2df15c4dc19da3f79d1021e891f5dcf1d9d1db6ba38891d5590f3fe/numpy-2.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:33b3bf58ee84b172c067f56aeadc7ee9ab6de69c5e800ab5b10295d54c581adb", size = 16957183, upload-time = "2026-03-09T07:55:57.774Z" }, - { url = "https://files.pythonhosted.org/packages/b5/7c/c061f3de0630941073d2598dc271ac2f6cbcf5c83c74a5870fea07488333/numpy-2.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8ba7b51e71c05aa1f9bc3641463cd82308eab40ce0d5c7e1fd4038cbf9938147", size = 14968734, upload-time = "2026-03-09T07:56:00.494Z" }, - { url = "https://files.pythonhosted.org/packages/ef/27/d26c85cbcd86b26e4f125b0668e7a7c0542d19dd7d23ee12e87b550e95b5/numpy-2.4.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a1988292870c7cb9d0ebb4cc96b4d447513a9644801de54606dc7aabf2b7d920", size = 5475288, upload-time = "2026-03-09T07:56:02.857Z" }, - { url = "https://files.pythonhosted.org/packages/2b/09/3c4abbc1dcd8010bf1a611d174c7aa689fc505585ec806111b4406f6f1b1/numpy-2.4.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:23b46bb6d8ecb68b58c09944483c135ae5f0e9b8d8858ece5e4ead783771d2a9", size = 6805253, upload-time = "2026-03-09T07:56:04.53Z" }, - { url = "https://files.pythonhosted.org/packages/21/bc/e7aa3f6817e40c3f517d407742337cbb8e6fc4b83ce0b55ab780c829243b/numpy-2.4.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a016db5c5dba78fa8fe9f5d80d6708f9c42ab087a739803c0ac83a43d686a470", size = 15969479, upload-time = "2026-03-09T07:56:06.638Z" }, - { url = "https://files.pythonhosted.org/packages/78/51/9f5d7a41f0b51649ddf2f2320595e15e122a40610b233d51928dd6c92353/numpy-2.4.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:715de7f82e192e8cae5a507a347d97ad17598f8e026152ca97233e3666daaa71", size = 16901035, upload-time = "2026-03-09T07:56:09.405Z" }, - { url = "https://files.pythonhosted.org/packages/64/6e/b221dd847d7181bc5ee4857bfb026182ef69499f9305eb1371cbb1aea626/numpy-2.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2ddb7919366ee468342b91dea2352824c25b55814a987847b6c52003a7c97f15", size = 17325657, upload-time = "2026-03-09T07:56:12.067Z" }, - { url = "https://files.pythonhosted.org/packages/eb/b8/8f3fd2da596e1063964b758b5e3c970aed1949a05200d7e3d46a9d46d643/numpy-2.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a315e5234d88067f2d97e1f2ef670a7569df445d55400f1e33d117418d008d52", size = 18635512, upload-time = "2026-03-09T07:56:14.629Z" }, - { url = "https://files.pythonhosted.org/packages/5c/24/2993b775c37e39d2f8ab4125b44337ab0b2ba106c100980b7c274a22bee7/numpy-2.4.3-cp311-cp311-win32.whl", hash = "sha256:2b3f8d2c4589b1a2028d2a770b0fc4d1f332fb5e01521f4de3199a896d158ddd", size = 6238100, upload-time = "2026-03-09T07:56:17.243Z" }, - { url = "https://files.pythonhosted.org/packages/76/1d/edccf27adedb754db7c4511d5eac8b83f004ae948fe2d3509e8b78097d4c/numpy-2.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:77e76d932c49a75617c6d13464e41203cd410956614d0a0e999b25e9e8d27eec", size = 12609816, upload-time = "2026-03-09T07:56:19.089Z" }, - { url = "https://files.pythonhosted.org/packages/92/82/190b99153480076c8dce85f4cfe7d53ea84444145ffa54cb58dcd460d66b/numpy-2.4.3-cp311-cp311-win_arm64.whl", hash = "sha256:eb610595dd91560905c132c709412b512135a60f1851ccbd2c959e136431ff67", size = 10485757, upload-time = "2026-03-09T07:56:21.753Z" }, - { url = "https://files.pythonhosted.org/packages/a9/ed/6388632536f9788cea23a3a1b629f25b43eaacd7d7377e5d6bc7b9deb69b/numpy-2.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:61b0cbabbb6126c8df63b9a3a0c4b1f44ebca5e12ff6997b80fcf267fb3150ef", size = 16669628, upload-time = "2026-03-09T07:56:24.252Z" }, - { url = "https://files.pythonhosted.org/packages/74/1b/ee2abfc68e1ce728b2958b6ba831d65c62e1b13ce3017c13943f8f9b5b2e/numpy-2.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7395e69ff32526710748f92cd8c9849b361830968ea3e24a676f272653e8983e", size = 14696872, upload-time = "2026-03-09T07:56:26.991Z" }, - { url = "https://files.pythonhosted.org/packages/ba/d1/780400e915ff5638166f11ca9dc2c5815189f3d7cf6f8759a1685e586413/numpy-2.4.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:abdce0f71dcb4a00e4e77f3faf05e4616ceccfe72ccaa07f47ee79cda3b7b0f4", size = 5203489, upload-time = "2026-03-09T07:56:29.414Z" }, - { url = "https://files.pythonhosted.org/packages/0b/bb/baffa907e9da4cc34a6e556d6d90e032f6d7a75ea47968ea92b4858826c4/numpy-2.4.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:48da3a4ee1336454b07497ff7ec83903efa5505792c4e6d9bf83d99dc07a1e18", size = 6550814, upload-time = "2026-03-09T07:56:32.225Z" }, - { url = "https://files.pythonhosted.org/packages/7b/12/8c9f0c6c95f76aeb20fc4a699c33e9f827fa0d0f857747c73bb7b17af945/numpy-2.4.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32e3bef222ad6b052280311d1d60db8e259e4947052c3ae7dd6817451fc8a4c5", size = 15666601, upload-time = "2026-03-09T07:56:34.461Z" }, - { url = "https://files.pythonhosted.org/packages/bd/79/cc665495e4d57d0aa6fbcc0aa57aa82671dfc78fbf95fe733ed86d98f52a/numpy-2.4.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7dd01a46700b1967487141a66ac1a3cf0dd8ebf1f08db37d46389401512ca97", size = 16621358, upload-time = "2026-03-09T07:56:36.852Z" }, - { url = "https://files.pythonhosted.org/packages/a8/40/b4ecb7224af1065c3539f5ecfff879d090de09608ad1008f02c05c770cb3/numpy-2.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:76f0f283506c28b12bba319c0fab98217e9f9b54e6160e9c79e9f7348ba32e9c", size = 17016135, upload-time = "2026-03-09T07:56:39.337Z" }, - { url = "https://files.pythonhosted.org/packages/f7/b1/6a88e888052eed951afed7a142dcdf3b149a030ca59b4c71eef085858e43/numpy-2.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:737f630a337364665aba3b5a77e56a68cc42d350edd010c345d65a3efa3addcc", size = 18345816, upload-time = "2026-03-09T07:56:42.31Z" }, - { url = "https://files.pythonhosted.org/packages/f3/8f/103a60c5f8c3d7fc678c19cd7b2476110da689ccb80bc18050efbaeae183/numpy-2.4.3-cp312-cp312-win32.whl", hash = "sha256:26952e18d82a1dbbc2f008d402021baa8d6fc8e84347a2072a25e08b46d698b9", size = 5960132, upload-time = "2026-03-09T07:56:44.851Z" }, - { url = "https://files.pythonhosted.org/packages/d7/7c/f5ee1bf6ed888494978046a809df2882aad35d414b622893322df7286879/numpy-2.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:65f3c2455188f09678355f5cae1f959a06b778bc66d535da07bf2ef20cd319d5", size = 12316144, upload-time = "2026-03-09T07:56:47.057Z" }, - { url = "https://files.pythonhosted.org/packages/71/46/8d1cb3f7a00f2fb6394140e7e6623696e54c6318a9d9691bb4904672cf42/numpy-2.4.3-cp312-cp312-win_arm64.whl", hash = "sha256:2abad5c7fef172b3377502bde47892439bae394a71bc329f31df0fd829b41a9e", size = 10220364, upload-time = "2026-03-09T07:56:49.849Z" }, - { url = "https://files.pythonhosted.org/packages/b6/d0/1fe47a98ce0df229238b77611340aff92d52691bcbc10583303181abf7fc/numpy-2.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b346845443716c8e542d54112966383b448f4a3ba5c66409771b8c0889485dd3", size = 16665297, upload-time = "2026-03-09T07:56:52.296Z" }, - { url = "https://files.pythonhosted.org/packages/27/d9/4e7c3f0e68dfa91f21c6fb6cf839bc829ec920688b1ce7ec722b1a6202fb/numpy-2.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2629289168f4897a3c4e23dc98d6f1731f0fc0fe52fb9db19f974041e4cc12b9", size = 14691853, upload-time = "2026-03-09T07:56:54.992Z" }, - { url = "https://files.pythonhosted.org/packages/3a/66/bd096b13a87549683812b53ab211e6d413497f84e794fb3c39191948da97/numpy-2.4.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:bb2e3cf95854233799013779216c57e153c1ee67a0bf92138acca0e429aefaee", size = 5198435, upload-time = "2026-03-09T07:56:57.184Z" }, - { url = "https://files.pythonhosted.org/packages/a2/2f/687722910b5a5601de2135c891108f51dfc873d8e43c8ed9f4ebb440b4a2/numpy-2.4.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:7f3408ff897f8ab07a07fbe2823d7aee6ff644c097cc1f90382511fe982f647f", size = 6546347, upload-time = "2026-03-09T07:56:59.531Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ec/7971c4e98d86c564750393fab8d7d83d0a9432a9d78bb8a163a6dc59967a/numpy-2.4.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:decb0eb8a53c3b009b0962378065589685d66b23467ef5dac16cbe818afde27f", size = 15664626, upload-time = "2026-03-09T07:57:01.385Z" }, - { url = "https://files.pythonhosted.org/packages/7e/eb/7daecbea84ec935b7fc732e18f532073064a3816f0932a40a17f3349185f/numpy-2.4.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5f51900414fc9204a0e0da158ba2ac52b75656e7dce7e77fb9f84bfa343b4cc", size = 16608916, upload-time = "2026-03-09T07:57:04.008Z" }, - { url = "https://files.pythonhosted.org/packages/df/58/2a2b4a817ffd7472dca4421d9f0776898b364154e30c95f42195041dc03b/numpy-2.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6bd06731541f89cdc01b261ba2c9e037f1543df7472517836b78dfb15bd6e476", size = 17015824, upload-time = "2026-03-09T07:57:06.347Z" }, - { url = "https://files.pythonhosted.org/packages/4a/ca/627a828d44e78a418c55f82dd4caea8ea4a8ef24e5144d9e71016e52fb40/numpy-2.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:22654fe6be0e5206f553a9250762c653d3698e46686eee53b399ab90da59bd92", size = 18334581, upload-time = "2026-03-09T07:57:09.114Z" }, - { url = "https://files.pythonhosted.org/packages/cd/c0/76f93962fc79955fcba30a429b62304332345f22d4daec1cb33653425643/numpy-2.4.3-cp313-cp313-win32.whl", hash = "sha256:d71e379452a2f670ccb689ec801b1218cd3983e253105d6e83780967e899d687", size = 5958618, upload-time = "2026-03-09T07:57:11.432Z" }, - { url = "https://files.pythonhosted.org/packages/b1/3c/88af0040119209b9b5cb59485fa48b76f372c73068dbf9254784b975ac53/numpy-2.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:0a60e17a14d640f49146cb38e3f105f571318db7826d9b6fef7e4dce758faecd", size = 12312824, upload-time = "2026-03-09T07:57:13.586Z" }, - { url = "https://files.pythonhosted.org/packages/58/ce/3d07743aced3d173f877c3ef6a454c2174ba42b584ab0b7e6d99374f51ed/numpy-2.4.3-cp313-cp313-win_arm64.whl", hash = "sha256:c9619741e9da2059cd9c3f206110b97583c7152c1dc9f8aafd4beb450ac1c89d", size = 10221218, upload-time = "2026-03-09T07:57:16.183Z" }, - { url = "https://files.pythonhosted.org/packages/62/09/d96b02a91d09e9d97862f4fc8bfebf5400f567d8eb1fe4b0cc4795679c15/numpy-2.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7aa4e54f6469300ebca1d9eb80acd5253cdfa36f2c03d79a35883687da430875", size = 14819570, upload-time = "2026-03-09T07:57:18.564Z" }, - { url = "https://files.pythonhosted.org/packages/b5/ca/0b1aba3905fdfa3373d523b2b15b19029f4f3031c87f4066bd9d20ef6c6b/numpy-2.4.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d1b90d840b25874cf5cd20c219af10bac3667db3876d9a495609273ebe679070", size = 5326113, upload-time = "2026-03-09T07:57:21.052Z" }, - { url = "https://files.pythonhosted.org/packages/c0/63/406e0fd32fcaeb94180fd6a4c41e55736d676c54346b7efbce548b94a914/numpy-2.4.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a749547700de0a20a6718293396ec237bb38218049cfce788e08fcb716e8cf73", size = 6646370, upload-time = "2026-03-09T07:57:22.804Z" }, - { url = "https://files.pythonhosted.org/packages/b6/d0/10f7dc157d4b37af92720a196be6f54f889e90dcd30dce9dc657ed92c257/numpy-2.4.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94f3c4a151a2e529adf49c1d54f0f57ff8f9b233ee4d44af623a81553ab86368", size = 15723499, upload-time = "2026-03-09T07:57:24.693Z" }, - { url = "https://files.pythonhosted.org/packages/66/f1/d1c2bf1161396629701bc284d958dc1efa3a5a542aab83cf11ee6eb4cba5/numpy-2.4.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22c31dc07025123aedf7f2db9e91783df13f1776dc52c6b22c620870dc0fab22", size = 16657164, upload-time = "2026-03-09T07:57:27.676Z" }, - { url = "https://files.pythonhosted.org/packages/1a/be/cca19230b740af199ac47331a21c71e7a3d0ba59661350483c1600d28c37/numpy-2.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:148d59127ac95979d6f07e4d460f934ebdd6eed641db9c0db6c73026f2b2101a", size = 17081544, upload-time = "2026-03-09T07:57:30.664Z" }, - { url = "https://files.pythonhosted.org/packages/b9/c5/9602b0cbb703a0936fb40f8a95407e8171935b15846de2f0776e08af04c7/numpy-2.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a97cbf7e905c435865c2d939af3d93f99d18eaaa3cabe4256f4304fb51604349", size = 18380290, upload-time = "2026-03-09T07:57:33.763Z" }, - { url = "https://files.pythonhosted.org/packages/ed/81/9f24708953cd30be9ee36ec4778f4b112b45165812f2ada4cc5ea1c1f254/numpy-2.4.3-cp313-cp313t-win32.whl", hash = "sha256:be3b8487d725a77acccc9924f65fd8bce9af7fac8c9820df1049424a2115af6c", size = 6082814, upload-time = "2026-03-09T07:57:36.491Z" }, - { url = "https://files.pythonhosted.org/packages/e2/9e/52f6eaa13e1a799f0ab79066c17f7016a4a8ae0c1aefa58c82b4dab690b4/numpy-2.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1ec84fd7c8e652b0f4aaaf2e6e9cc8eaa9b1b80a537e06b2e3a2fb176eedcb26", size = 12452673, upload-time = "2026-03-09T07:57:38.281Z" }, - { url = "https://files.pythonhosted.org/packages/c4/04/b8cece6ead0b30c9fbd99bb835ad7ea0112ac5f39f069788c5558e3b1ab2/numpy-2.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:120df8c0a81ebbf5b9020c91439fccd85f5e018a927a39f624845be194a2be02", size = 10290907, upload-time = "2026-03-09T07:57:40.747Z" }, - { url = "https://files.pythonhosted.org/packages/70/ae/3936f79adebf8caf81bd7a599b90a561334a658be4dcc7b6329ebf4ee8de/numpy-2.4.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:5884ce5c7acfae1e4e1b6fde43797d10aa506074d25b531b4f54bde33c0c31d4", size = 16664563, upload-time = "2026-03-09T07:57:43.817Z" }, - { url = "https://files.pythonhosted.org/packages/9b/62/760f2b55866b496bb1fa7da2a6db076bef908110e568b02fcfc1422e2a3a/numpy-2.4.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:297837823f5bc572c5f9379b0c9f3a3365f08492cbdc33bcc3af174372ebb168", size = 14702161, upload-time = "2026-03-09T07:57:46.169Z" }, - { url = "https://files.pythonhosted.org/packages/32/af/a7a39464e2c0a21526fb4fb76e346fb172ebc92f6d1c7a07c2c139cc17b1/numpy-2.4.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:a111698b4a3f8dcbe54c64a7708f049355abd603e619013c346553c1fd4ca90b", size = 5208738, upload-time = "2026-03-09T07:57:48.506Z" }, - { url = "https://files.pythonhosted.org/packages/29/8c/2a0cf86a59558fa078d83805589c2de490f29ed4fb336c14313a161d358a/numpy-2.4.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:4bd4741a6a676770e0e97fe9ab2e51de01183df3dcbcec591d26d331a40de950", size = 6543618, upload-time = "2026-03-09T07:57:50.591Z" }, - { url = "https://files.pythonhosted.org/packages/aa/b8/612ce010c0728b1c363fa4ea3aa4c22fe1c5da1de008486f8c2f5cb92fae/numpy-2.4.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:54f29b877279d51e210e0c80709ee14ccbbad647810e8f3d375561c45ef613dd", size = 15680676, upload-time = "2026-03-09T07:57:52.34Z" }, - { url = "https://files.pythonhosted.org/packages/a9/7e/4f120ecc54ba26ddf3dc348eeb9eb063f421de65c05fc961941798feea18/numpy-2.4.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:679f2a834bae9020f81534671c56fd0cc76dd7e5182f57131478e23d0dc59e24", size = 16613492, upload-time = "2026-03-09T07:57:54.91Z" }, - { url = "https://files.pythonhosted.org/packages/2c/86/1b6020db73be330c4b45d5c6ee4295d59cfeef0e3ea323959d053e5a6909/numpy-2.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d84f0f881cb2225c2dfd7f78a10a5645d487a496c6668d6cc39f0f114164f3d0", size = 17031789, upload-time = "2026-03-09T07:57:57.641Z" }, - { url = "https://files.pythonhosted.org/packages/07/3a/3b90463bf41ebc21d1b7e06079f03070334374208c0f9a1f05e4ae8455e7/numpy-2.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d213c7e6e8d211888cc359bab7199670a00f5b82c0978b9d1c75baf1eddbeac0", size = 18339941, upload-time = "2026-03-09T07:58:00.577Z" }, - { url = "https://files.pythonhosted.org/packages/a8/74/6d736c4cd962259fd8bae9be27363eb4883a2f9069763747347544c2a487/numpy-2.4.3-cp314-cp314-win32.whl", hash = "sha256:52077feedeff7c76ed7c9f1a0428558e50825347b7545bbb8523da2cd55c547a", size = 6007503, upload-time = "2026-03-09T07:58:03.331Z" }, - { url = "https://files.pythonhosted.org/packages/48/39/c56ef87af669364356bb011922ef0734fc49dad51964568634c72a009488/numpy-2.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:0448e7f9caefb34b4b7dd2b77f21e8906e5d6f0365ad525f9f4f530b13df2afc", size = 12444915, upload-time = "2026-03-09T07:58:06.353Z" }, - { url = "https://files.pythonhosted.org/packages/9d/1f/ab8528e38d295fd349310807496fabb7cf9fe2e1f70b97bc20a483ea9d4a/numpy-2.4.3-cp314-cp314-win_arm64.whl", hash = "sha256:b44fd60341c4d9783039598efadd03617fa28d041fc37d22b62d08f2027fa0e7", size = 10494875, upload-time = "2026-03-09T07:58:08.734Z" }, - { url = "https://files.pythonhosted.org/packages/e6/ef/b7c35e4d5ef141b836658ab21a66d1a573e15b335b1d111d31f26c8ef80f/numpy-2.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0a195f4216be9305a73c0e91c9b026a35f2161237cf1c6de9b681637772ea657", size = 14822225, upload-time = "2026-03-09T07:58:11.034Z" }, - { url = "https://files.pythonhosted.org/packages/cd/8d/7730fa9278cf6648639946cc816e7cc89f0d891602584697923375f801ed/numpy-2.4.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:cd32fbacb9fd1bf041bf8e89e4576b6f00b895f06d00914820ae06a616bdfef7", size = 5328769, upload-time = "2026-03-09T07:58:13.67Z" }, - { url = "https://files.pythonhosted.org/packages/47/01/d2a137317c958b074d338807c1b6a383406cdf8b8e53b075d804cc3d211d/numpy-2.4.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:2e03c05abaee1f672e9d67bc858f300b5ccba1c21397211e8d77d98350972093", size = 6649461, upload-time = "2026-03-09T07:58:15.912Z" }, - { url = "https://files.pythonhosted.org/packages/5c/34/812ce12bc0f00272a4b0ec0d713cd237cb390666eb6206323d1cc9cedbb2/numpy-2.4.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d1ce23cce91fcea443320a9d0ece9b9305d4368875bab09538f7a5b4131938a", size = 15725809, upload-time = "2026-03-09T07:58:17.787Z" }, - { url = "https://files.pythonhosted.org/packages/25/c0/2aed473a4823e905e765fee3dc2cbf504bd3e68ccb1150fbdabd5c39f527/numpy-2.4.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c59020932feb24ed49ffd03704fbab89f22aa9c0d4b180ff45542fe8918f5611", size = 16655242, upload-time = "2026-03-09T07:58:20.476Z" }, - { url = "https://files.pythonhosted.org/packages/f2/c8/7e052b2fc87aa0e86de23f20e2c42bd261c624748aa8efd2c78f7bb8d8c6/numpy-2.4.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9684823a78a6cd6ad7511fc5e25b07947d1d5b5e2812c93fe99d7d4195130720", size = 17080660, upload-time = "2026-03-09T07:58:23.067Z" }, - { url = "https://files.pythonhosted.org/packages/f3/3d/0876746044db2adcb11549f214d104f2e1be00f07a67edbb4e2812094847/numpy-2.4.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0200b25c687033316fb39f0ff4e3e690e8957a2c3c8d22499891ec58c37a3eb5", size = 18380384, upload-time = "2026-03-09T07:58:25.839Z" }, - { url = "https://files.pythonhosted.org/packages/07/12/8160bea39da3335737b10308df4f484235fd297f556745f13092aa039d3b/numpy-2.4.3-cp314-cp314t-win32.whl", hash = "sha256:5e10da9e93247e554bb1d22f8edc51847ddd7dde52d85ce31024c1b4312bfba0", size = 6154547, upload-time = "2026-03-09T07:58:28.289Z" }, - { url = "https://files.pythonhosted.org/packages/42/f3/76534f61f80d74cc9cdf2e570d3d4eeb92c2280a27c39b0aaf471eda7b48/numpy-2.4.3-cp314-cp314t-win_amd64.whl", hash = "sha256:45f003dbdffb997a03da2d1d0cb41fbd24a87507fb41605c0420a3db5bd4667b", size = 12633645, upload-time = "2026-03-09T07:58:30.384Z" }, - { url = "https://files.pythonhosted.org/packages/1f/b6/7c0d4334c15983cec7f92a69e8ce9b1e6f31857e5ee3a413ac424e6bd63d/numpy-2.4.3-cp314-cp314t-win_arm64.whl", hash = "sha256:4d382735cecd7bcf090172489a525cd7d4087bc331f7df9f60ddc9a296cf208e", size = 10565454, upload-time = "2026-03-09T07:58:33.031Z" }, - { url = "https://files.pythonhosted.org/packages/64/e4/4dab9fb43c83719c29241c535d9e07be73bea4bc0c6686c5816d8e1b6689/numpy-2.4.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c6b124bfcafb9e8d3ed09130dbee44848c20b3e758b6bbf006e641778927c028", size = 16834892, upload-time = "2026-03-09T07:58:35.334Z" }, - { url = "https://files.pythonhosted.org/packages/c9/29/f8b6d4af90fed3dfda84ebc0df06c9833d38880c79ce954e5b661758aa31/numpy-2.4.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:76dbb9d4e43c16cf9aa711fcd8de1e2eeb27539dcefb60a1d5e9f12fae1d1ed8", size = 14893070, upload-time = "2026-03-09T07:58:37.7Z" }, - { url = "https://files.pythonhosted.org/packages/9a/04/a19b3c91dbec0a49269407f15d5753673a09832daed40c45e8150e6fa558/numpy-2.4.3-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:29363fbfa6f8ee855d7569c96ce524845e3d726d6c19b29eceec7dd555dab152", size = 5399609, upload-time = "2026-03-09T07:58:39.853Z" }, - { url = "https://files.pythonhosted.org/packages/79/34/4d73603f5420eab89ea8a67097b31364bf7c30f811d4dd84b1659c7476d9/numpy-2.4.3-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:bc71942c789ef415a37f0d4eab90341425a00d538cd0642445d30b41023d3395", size = 6714355, upload-time = "2026-03-09T07:58:42.365Z" }, - { url = "https://files.pythonhosted.org/packages/58/ad/1100d7229bb248394939a12a8074d485b655e8ed44207d328fdd7fcebc7b/numpy-2.4.3-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e58765ad74dcebd3ef0208a5078fba32dc8ec3578fe84a604432950cd043d79", size = 15800434, upload-time = "2026-03-09T07:58:44.837Z" }, - { url = "https://files.pythonhosted.org/packages/0c/fd/16d710c085d28ba4feaf29ac60c936c9d662e390344f94a6beaa2ac9899b/numpy-2.4.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e236dbda4e1d319d681afcbb136c0c4a8e0f1a5c58ceec2adebb547357fe857", size = 16729409, upload-time = "2026-03-09T07:58:47.972Z" }, - { url = "https://files.pythonhosted.org/packages/57/a7/b35835e278c18b85206834b3aa3abe68e77a98769c59233d1f6300284781/numpy-2.4.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:4b42639cdde6d24e732ff823a3fa5b701d8acad89c4142bc1d0bd6dc85200ba5", size = 12504685, upload-time = "2026-03-09T07:58:50.525Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/c6/4218570d8c8ecc9704b5157a3348e486e84ef4be0ed3e38218ab473c83d2/numpy-2.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f983334aea213c99992053ede6168500e5f086ce74fbc4acc3f2b00f5762e9db", size = 16976799, upload-time = "2026-03-29T13:18:15.438Z" }, + { url = "https://files.pythonhosted.org/packages/dd/92/b4d922c4a5f5dab9ed44e6153908a5c665b71acf183a83b93b690996e39b/numpy-2.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72944b19f2324114e9dc86a159787333b77874143efcf89a5167ef83cfee8af0", size = 14971552, upload-time = "2026-03-29T13:18:18.606Z" }, + { url = "https://files.pythonhosted.org/packages/8a/dc/df98c095978fa6ee7b9a9387d1d58cbb3d232d0e69ad169a4ce784bde4fd/numpy-2.4.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:86b6f55f5a352b48d7fbfd2dbc3d5b780b2d79f4d3c121f33eb6efb22e9a2015", size = 5476566, upload-time = "2026-03-29T13:18:21.532Z" }, + { url = "https://files.pythonhosted.org/packages/28/34/b3fdcec6e725409223dd27356bdf5a3c2cc2282e428218ecc9cb7acc9763/numpy-2.4.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:ba1f4fc670ed79f876f70082eff4f9583c15fb9a4b89d6188412de4d18ae2f40", size = 6806482, upload-time = "2026-03-29T13:18:23.634Z" }, + { url = "https://files.pythonhosted.org/packages/68/62/63417c13aa35d57bee1337c67446761dc25ea6543130cf868eace6e8157b/numpy-2.4.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a87ec22c87be071b6bdbd27920b129b94f2fc964358ce38f3822635a3e2e03d", size = 15973376, upload-time = "2026-03-29T13:18:26.677Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c5/9fcb7e0e69cef59cf10c746b84f7d58b08bc66a6b7d459783c5a4f6101a6/numpy-2.4.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3775294accfdd75f32c74ae39fcba920c9a378a2fc18a12b6820aa8c1fb502", size = 16925137, upload-time = "2026-03-29T13:18:30.14Z" }, + { url = "https://files.pythonhosted.org/packages/7e/43/80020edacb3f84b9efdd1591120a4296462c23fd8db0dde1666f6ef66f13/numpy-2.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d4e437e295f18ec29bc79daf55e8a47a9113df44d66f702f02a293d93a2d6dd", size = 17329414, upload-time = "2026-03-29T13:18:33.733Z" }, + { url = "https://files.pythonhosted.org/packages/fd/06/af0658593b18a5f73532d377188b964f239eb0894e664a6c12f484472f97/numpy-2.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6aa3236c78803afbcb255045fbef97a9e25a1f6c9888357d205ddc42f4d6eba5", size = 18658397, upload-time = "2026-03-29T13:18:37.511Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ce/13a09ed65f5d0ce5c7dd0669250374c6e379910f97af2c08c57b0608eee4/numpy-2.4.4-cp311-cp311-win32.whl", hash = "sha256:30caa73029a225b2d40d9fae193e008e24b2026b7ee1a867b7ee8d96ca1a448e", size = 6239499, upload-time = "2026-03-29T13:18:40.372Z" }, + { url = "https://files.pythonhosted.org/packages/bd/63/05d193dbb4b5eec1eca73822d80da98b511f8328ad4ae3ca4caf0f4db91d/numpy-2.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:6bbe4eb67390b0a0265a2c25458f6b90a409d5d069f1041e6aff1e27e3d9a79e", size = 12614257, upload-time = "2026-03-29T13:18:42.95Z" }, + { url = "https://files.pythonhosted.org/packages/87/c5/8168052f080c26fa984c413305012be54741c9d0d74abd7fbeeccae3889f/numpy-2.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:fcfe2045fd2e8f3cb0ce9d4ba6dba6333b8fa05bb8a4939c908cd43322d14c7e", size = 10486775, upload-time = "2026-03-29T13:18:45.835Z" }, + { url = "https://files.pythonhosted.org/packages/28/05/32396bec30fb2263770ee910142f49c1476d08e8ad41abf8403806b520ce/numpy-2.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15716cfef24d3a9762e3acdf87e27f58dc823d1348f765bbea6bef8c639bfa1b", size = 16689272, upload-time = "2026-03-29T13:18:49.223Z" }, + { url = "https://files.pythonhosted.org/packages/c5/f3/a983d28637bfcd763a9c7aafdb6d5c0ebf3d487d1e1459ffdb57e2f01117/numpy-2.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23cbfd4c17357c81021f21540da84ee282b9c8fba38a03b7b9d09ba6b951421e", size = 14699573, upload-time = "2026-03-29T13:18:52.629Z" }, + { url = "https://files.pythonhosted.org/packages/9b/fd/e5ecca1e78c05106d98028114f5c00d3eddb41207686b2b7de3e477b0e22/numpy-2.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b3b60bb7cba2c8c81837661c488637eee696f59a877788a396d33150c35d842", size = 5204782, upload-time = "2026-03-29T13:18:55.579Z" }, + { url = "https://files.pythonhosted.org/packages/de/2f/702a4594413c1a8632092beae8aba00f1d67947389369b3777aed783fdca/numpy-2.4.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e4a010c27ff6f210ff4c6ef34394cd61470d01014439b192ec22552ee867f2a8", size = 6552038, upload-time = "2026-03-29T13:18:57.769Z" }, + { url = "https://files.pythonhosted.org/packages/7f/37/eed308a8f56cba4d1fdf467a4fc67ef4ff4bf1c888f5fc980481890104b1/numpy-2.4.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9e75681b59ddaa5e659898085ae0eaea229d054f2ac0c7e563a62205a700121", size = 15670666, upload-time = "2026-03-29T13:19:00.341Z" }, + { url = "https://files.pythonhosted.org/packages/0a/0d/0e3ecece05b7a7e87ab9fb587855548da437a061326fff64a223b6dcb78a/numpy-2.4.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:81f4a14bee47aec54f883e0cad2d73986640c1590eb9bfaaba7ad17394481e6e", size = 16645480, upload-time = "2026-03-29T13:19:03.63Z" }, + { url = "https://files.pythonhosted.org/packages/34/49/f2312c154b82a286758ee2f1743336d50651f8b5195db18cdb63675ff649/numpy-2.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62d6b0f03b694173f9fcb1fb317f7222fd0b0b103e784c6549f5e53a27718c44", size = 17020036, upload-time = "2026-03-29T13:19:07.428Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e9/736d17bd77f1b0ec4f9901aaec129c00d59f5d84d5e79bba540ef12c2330/numpy-2.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbc356aae7adf9e6336d336b9c8111d390a05df88f1805573ebb0807bd06fd1d", size = 18368643, upload-time = "2026-03-29T13:19:10.775Z" }, + { url = "https://files.pythonhosted.org/packages/63/f6/d417977c5f519b17c8a5c3bc9e8304b0908b0e21136fe43bf628a1343914/numpy-2.4.4-cp312-cp312-win32.whl", hash = "sha256:0d35aea54ad1d420c812bfa0385c71cd7cc5bcf7c65fed95fc2cd02fe8c79827", size = 5961117, upload-time = "2026-03-29T13:19:13.464Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5b/e1deebf88ff431b01b7406ca3583ab2bbb90972bbe1c568732e49c844f7e/numpy-2.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:b5f0362dc928a6ecd9db58868fca5e48485205e3855957bdedea308f8672ea4a", size = 12320584, upload-time = "2026-03-29T13:19:16.155Z" }, + { url = "https://files.pythonhosted.org/packages/58/89/e4e856ac82a68c3ed64486a544977d0e7bdd18b8da75b78a577ca31c4395/numpy-2.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:846300f379b5b12cc769334464656bc882e0735d27d9726568bc932fdc49d5ec", size = 10221450, upload-time = "2026-03-29T13:19:18.994Z" }, + { url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933, upload-time = "2026-03-29T13:19:22.47Z" }, + { url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532, upload-time = "2026-03-29T13:19:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661, upload-time = "2026-03-29T13:19:28.31Z" }, + { url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539, upload-time = "2026-03-29T13:19:30.97Z" }, + { url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806, upload-time = "2026-03-29T13:19:33.887Z" }, + { url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682, upload-time = "2026-03-29T13:19:37.336Z" }, + { url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810, upload-time = "2026-03-29T13:19:40.963Z" }, + { url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394, upload-time = "2026-03-29T13:19:44.859Z" }, + { url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556, upload-time = "2026-03-29T13:19:47.661Z" }, + { url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311, upload-time = "2026-03-29T13:19:50.67Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060, upload-time = "2026-03-29T13:19:54.229Z" }, + { url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302, upload-time = "2026-03-29T13:19:57.585Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407, upload-time = "2026-03-29T13:20:00.601Z" }, + { url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631, upload-time = "2026-03-29T13:20:02.855Z" }, + { url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691, upload-time = "2026-03-29T13:20:06.004Z" }, + { url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241, upload-time = "2026-03-29T13:20:09.417Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767, upload-time = "2026-03-29T13:20:13.126Z" }, + { url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169, upload-time = "2026-03-29T13:20:17.096Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477, upload-time = "2026-03-29T13:20:20.195Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487, upload-time = "2026-03-29T13:20:22.946Z" }, + { url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002, upload-time = "2026-03-29T13:20:25.909Z" }, + { url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353, upload-time = "2026-03-29T13:20:29.504Z" }, + { url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914, upload-time = "2026-03-29T13:20:33.547Z" }, + { url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005, upload-time = "2026-03-29T13:20:36.45Z" }, + { url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974, upload-time = "2026-03-29T13:20:39.014Z" }, + { url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591, upload-time = "2026-03-29T13:20:42.146Z" }, + { url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700, upload-time = "2026-03-29T13:20:46.204Z" }, + { url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781, upload-time = "2026-03-29T13:20:50.242Z" }, + { url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959, upload-time = "2026-03-29T13:20:54.019Z" }, + { url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768, upload-time = "2026-03-29T13:20:56.912Z" }, + { url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181, upload-time = "2026-03-29T13:20:59.548Z" }, + { url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035, upload-time = "2026-03-29T13:21:02.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958, upload-time = "2026-03-29T13:21:05.671Z" }, + { url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020, upload-time = "2026-03-29T13:21:08.635Z" }, + { url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758, upload-time = "2026-03-29T13:21:10.949Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948, upload-time = "2026-03-29T13:21:14.047Z" }, + { url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325, upload-time = "2026-03-29T13:21:17.561Z" }, + { url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883, upload-time = "2026-03-29T13:21:21.106Z" }, + { url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474, upload-time = "2026-03-29T13:21:24.828Z" }, + { url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500, upload-time = "2026-03-29T13:21:28.205Z" }, + { url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755, upload-time = "2026-03-29T13:21:31.107Z" }, + { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643, upload-time = "2026-03-29T13:21:34.339Z" }, + { url = "https://files.pythonhosted.org/packages/6b/33/8fae8f964a4f63ed528264ddf25d2b683d0b663e3cba26961eb838a7c1bd/numpy-2.4.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:58c8b5929fcb8287cbd6f0a3fae19c6e03a5c48402ae792962ac465224a629a4", size = 16854491, upload-time = "2026-03-29T13:21:38.03Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d0/1aabee441380b981cf8cdda3ae7a46aa827d1b5a8cce84d14598bc94d6d9/numpy-2.4.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eea7ac5d2dce4189771cedb559c738a71512768210dc4e4753b107a2048b3d0e", size = 14895830, upload-time = "2026-03-29T13:21:41.509Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b8/aafb0d1065416894fccf4df6b49ef22b8db045187949545bced89c034b8e/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:51fc224f7ca4d92656d5a5eb315f12eb5fe2c97a66249aa7b5f562528a3be38c", size = 5400927, upload-time = "2026-03-29T13:21:44.747Z" }, + { url = "https://files.pythonhosted.org/packages/d6/77/063baa20b08b431038c7f9ff5435540c7b7265c78cf56012a483019ca72d/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:28a650663f7314afc3e6ec620f44f333c386aad9f6fc472030865dc0ebb26ee3", size = 6715557, upload-time = "2026-03-29T13:21:47.406Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a8/379542d45a14f149444c5c4c4e7714707239ce9cc1de8c2803958889da14/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19710a9ca9992d7174e9c52f643d4272dcd1558c5f7af7f6f8190f633bd651a7", size = 15804253, upload-time = "2026-03-29T13:21:50.753Z" }, + { url = "https://files.pythonhosted.org/packages/a2/c8/f0a45426d6d21e7ea3310a15cf90c43a14d9232c31a837702dba437f3373/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b2aec6af35c113b05695ebb5749a787acd63cafc83086a05771d1e1cd1e555f", size = 16753552, upload-time = "2026-03-29T13:21:54.344Z" }, + { url = "https://files.pythonhosted.org/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119", size = 12509075, upload-time = "2026-03-29T13:21:57.644Z" }, ] [[package]] @@ -4167,7 +4199,8 @@ version = "1.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e9/3c/dfccc9e7dee357fb2aa13c3890d952a370dd0ed071e0f7ed62ed0df567c1/numpydoc-1.10.0.tar.gz", hash = "sha256:3f7970f6eee30912260a6b31ac72bba2432830cd6722569ec17ee8d3ef5ffa01", size = 94027, upload-time = "2025-12-02T16:39:12.937Z" } @@ -4617,7 +4650,7 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, ] @@ -4707,7 +4740,7 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/27/1d/297ff2c7ea50a768a2247621d6451abb2a07c0e9be7ca6d36ebe371658e5/pandas_stubs-3.0.0.260204.tar.gz", hash = "sha256:bf9294b76352effcffa9cb85edf0bed1339a7ec0c30b8e1ac3d66b4228f1fbc3", size = 109383, upload-time = "2026-02-04T15:17:17.247Z" } wheels = [ @@ -4759,7 +4792,7 @@ version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/25/6c/6d8b4b03b958c02fa8687ec6063c49d952a189f8c91ebbe51e877dfab8f7/pgvector-0.4.2.tar.gz", hash = "sha256:322cac0c1dc5d41c9ecf782bd9991b7966685dee3a00bc873631391ed949513a", size = 31354, upload-time = "2025-12-05T01:07:17.87Z" } wheels = [ @@ -4978,14 +5011,14 @@ wheels = [ [[package]] name = "proto-plus" -version = "1.27.1" +version = "1.27.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3a/02/8832cde80e7380c600fbf55090b6ab7b62bd6825dbedde6d6657c15a1f8e/proto_plus-1.27.1.tar.gz", hash = "sha256:912a7460446625b792f6448bade9e55cd4e41e6ac10e27009ef71a7f317fa147", size = 56929, upload-time = "2026-02-02T17:34:49.035Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/0d/94dfe80193e79d55258345901acd2917523d56e8381bc4dee7fd38e3868a/proto_plus-1.27.2.tar.gz", hash = "sha256:b2adde53adadf75737c44d3dcb0104fde65250dfc83ad59168b4aa3e574b6a24", size = 57204, upload-time = "2026-03-26T22:18:57.174Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/79/ac273cbbf744691821a9cca88957257f41afe271637794975ca090b9588b/proto_plus-1.27.1-py3-none-any.whl", hash = "sha256:e4643061f3a4d0de092d62aa4ad09fa4756b2cbb89d4627f3985018216f9fefc", size = 50480, upload-time = "2026-02-02T17:34:47.339Z" }, + { url = "https://files.pythonhosted.org/packages/84/f3/1fba73eeffafc998a25d59703b63f8be4fe8a5cb12eaff7386a0ba0f7125/proto_plus-1.27.2-py3-none-any.whl", hash = "sha256:6432f75893d3b9e70b9c412f1d2f03f65b11fb164b793d14ae2ca01821d22718", size = 50450, upload-time = "2026-03-26T22:13:42.927Z" }, ] [[package]] @@ -5528,11 +5561,11 @@ wheels = [ [[package]] name = "pygments" -version = "2.19.2" +version = "2.20.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] [[package]] @@ -5855,15 +5888,15 @@ wheels = [ [[package]] name = "python-discovery" -version = "1.2.0" +version = "1.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9c/90/bcce6b46823c9bec1757c964dc37ed332579be512e17a30e9698095dcae4/python_discovery-1.2.0.tar.gz", hash = "sha256:7d33e350704818b09e3da2bd419d37e21e7c30db6e0977bb438916e06b41b5b1", size = 58055, upload-time = "2026-03-19T01:43:08.248Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/88/815e53084c5079a59df912825a279f41dd2e0df82281770eadc732f5352c/python_discovery-1.2.1.tar.gz", hash = "sha256:180c4d114bff1c32462537eac5d6a332b768242b76b69c0259c7d14b1b680c9e", size = 58457, upload-time = "2026-03-26T22:30:44.496Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl", hash = "sha256:1e108f1bbe2ed0ef089823d28805d5ad32be8e734b86a5f212bf89b71c266e4a", size = 31524, upload-time = "2026-03-19T01:43:07.045Z" }, + { url = "https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl", hash = "sha256:b6a957b24c1cd79252484d3566d1b49527581d46e789aaf43181005e56201502", size = 31674, upload-time = "2026-03-26T22:30:43.396Z" }, ] [[package]] @@ -6080,7 +6113,7 @@ wheels = [ [[package]] name = "requests" -version = "2.32.5" +version = "2.33.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -6088,9 +6121,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, + { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, ] [[package]] @@ -6140,18 +6173,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/54/6f679c435d28e0a568d8e8a7c0a93a09010818634c3c3907fc98d8983770/roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7", size = 7676, upload-time = "2025-12-17T18:25:33.098Z" }, ] -[[package]] -name = "roman-numerals-py" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "roman-numerals", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cb/b5/de96fca640f4f656eb79bbee0e79aeec52e3e0e359f8a3e6a0d366378b64/roman_numerals_py-4.1.0.tar.gz", hash = "sha256:f5d7b2b4ca52dd855ef7ab8eb3590f428c0b1ea480736ce32b01fef2a5f8daf9", size = 4274, upload-time = "2025-12-17T18:25:41.153Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/2c/daca29684cbe9fd4bc711f8246da3c10adca1ccc4d24436b17572eb2590e/roman_numerals_py-4.1.0-py3-none-any.whl", hash = "sha256:553114c1167141c1283a51743759723ecd05604a1b6b507225e91dc1a6df0780", size = 4547, upload-time = "2025-12-17T18:25:40.136Z" }, -] - [[package]] name = "rpds-py" version = "0.30.0" @@ -6346,41 +6367,41 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/22/9e4f66ee588588dc6c9af6a994e12d26e19efbe874d1a909d09a6dac7a59/ruff-0.15.7.tar.gz", hash = "sha256:04f1ae61fc20fe0b148617c324d9d009b5f63412c0b16474f3d5f1a1a665f7ac", size = 4601277, upload-time = "2026-03-19T16:26:22.605Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/2f/0b08ced94412af091807b6119ca03755d651d3d93a242682bf020189db94/ruff-0.15.7-py3-none-linux_armv6l.whl", hash = "sha256:a81cc5b6910fb7dfc7c32d20652e50fa05963f6e13ead3c5915c41ac5d16668e", size = 10489037, upload-time = "2026-03-19T16:26:32.47Z" }, - { url = "https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:722d165bd52403f3bdabc0ce9e41fc47070ac56d7a91b4e0d097b516a53a3477", size = 10955433, upload-time = "2026-03-19T16:27:00.205Z" }, - { url = "https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7fbc2448094262552146cbe1b9643a92f66559d3761f1ad0656d4991491af49e", size = 10269302, upload-time = "2026-03-19T16:26:26.183Z" }, - { url = "https://files.pythonhosted.org/packages/eb/5d/32b5c44ccf149a26623671df49cbfbd0a0ae511ff3df9d9d2426966a8d57/ruff-0.15.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b39329b60eba44156d138275323cc726bbfbddcec3063da57caa8a8b1d50adf", size = 10607625, upload-time = "2026-03-19T16:27:03.263Z" }, - { url = "https://files.pythonhosted.org/packages/5d/f1/f0001cabe86173aaacb6eb9bb734aa0605f9a6aa6fa7d43cb49cbc4af9c9/ruff-0.15.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87768c151808505f2bfc93ae44e5f9e7c8518943e5074f76ac21558ef5627c85", size = 10324743, upload-time = "2026-03-19T16:27:09.791Z" }, - { url = "https://files.pythonhosted.org/packages/7a/87/b8a8f3d56b8d848008559e7c9d8bf367934d5367f6d932ba779456e2f73b/ruff-0.15.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb0511670002c6c529ec66c0e30641c976c8963de26a113f3a30456b702468b0", size = 11138536, upload-time = "2026-03-19T16:27:06.101Z" }, - { url = "https://files.pythonhosted.org/packages/e4/f2/4fd0d05aab0c5934b2e1464784f85ba2eab9d54bffc53fb5430d1ed8b829/ruff-0.15.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0d19644f801849229db8345180a71bee5407b429dd217f853ec515e968a6912", size = 11994292, upload-time = "2026-03-19T16:26:48.718Z" }, - { url = "https://files.pythonhosted.org/packages/64/22/fc4483871e767e5e95d1622ad83dad5ebb830f762ed0420fde7dfa9d9b08/ruff-0.15.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4806d8e09ef5e84eb19ba833d0442f7e300b23fe3f0981cae159a248a10f0036", size = 11398981, upload-time = "2026-03-19T16:26:54.513Z" }, - { url = "https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dce0896488562f09a27b9c91b1f58a097457143931f3c4d519690dea54e624c5", size = 11242422, upload-time = "2026-03-19T16:26:29.277Z" }, - { url = "https://files.pythonhosted.org/packages/5d/3a/a7060f145bfdcce4c987ea27788b30c60e2c81d6e9a65157ca8afe646328/ruff-0.15.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1852ce241d2bc89e5dc823e03cff4ce73d816b5c6cdadd27dbfe7b03217d2a12", size = 11232158, upload-time = "2026-03-19T16:26:42.321Z" }, - { url = "https://files.pythonhosted.org/packages/a7/53/90fbb9e08b29c048c403558d3cdd0adf2668b02ce9d50602452e187cd4af/ruff-0.15.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5f3e4b221fb4bd293f79912fc5e93a9063ebd6d0dcbd528f91b89172a9b8436c", size = 10577861, upload-time = "2026-03-19T16:26:57.459Z" }, - { url = "https://files.pythonhosted.org/packages/2f/aa/5f486226538fe4d0f0439e2da1716e1acf895e2a232b26f2459c55f8ddad/ruff-0.15.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b15e48602c9c1d9bdc504b472e90b90c97dc7d46c7028011ae67f3861ceba7b4", size = 10327310, upload-time = "2026-03-19T16:26:35.909Z" }, - { url = "https://files.pythonhosted.org/packages/99/9e/271afdffb81fe7bfc8c43ba079e9d96238f674380099457a74ccb3863857/ruff-0.15.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1b4705e0e85cedc74b0a23cf6a179dbb3df184cb227761979cc76c0440b5ab0d", size = 10840752, upload-time = "2026-03-19T16:26:45.723Z" }, - { url = "https://files.pythonhosted.org/packages/bf/29/a4ae78394f76c7759953c47884eb44de271b03a66634148d9f7d11e721bd/ruff-0.15.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:112c1fa316a558bb34319282c1200a8bf0495f1b735aeb78bfcb2991e6087580", size = 11336961, upload-time = "2026-03-19T16:26:39.076Z" }, - { url = "https://files.pythonhosted.org/packages/26/6b/8786ba5736562220d588a2f6653e6c17e90c59ced34a2d7b512ef8956103/ruff-0.15.7-py3-none-win32.whl", hash = "sha256:6d39e2d3505b082323352f733599f28169d12e891f7dd407f2d4f54b4c2886de", size = 10582538, upload-time = "2026-03-19T16:26:15.992Z" }, - { url = "https://files.pythonhosted.org/packages/2b/e9/346d4d3fffc6871125e877dae8d9a1966b254fbd92a50f8561078b88b099/ruff-0.15.7-py3-none-win_amd64.whl", hash = "sha256:4d53d712ddebcd7dace1bc395367aec12c057aacfe9adbb6d832302575f4d3a1", size = 11755839, upload-time = "2026-03-19T16:26:19.897Z" }, - { url = "https://files.pythonhosted.org/packages/8f/e8/726643a3ea68c727da31570bde48c7a10f1aa60eddd628d94078fec586ff/ruff-0.15.7-py3-none-win_arm64.whl", hash = "sha256:18e8d73f1c3fdf27931497972250340f92e8c861722161a9caeb89a58ead6ed2", size = 11023304, upload-time = "2026-03-19T16:26:51.669Z" }, +version = "0.15.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/b0/73cf7550861e2b4824950b8b52eebdcc5adc792a00c514406556c5b80817/ruff-0.15.8.tar.gz", hash = "sha256:995f11f63597ee362130d1d5a327a87cb6f3f5eae3094c620bcc632329a4d26e", size = 4610921, upload-time = "2026-03-26T18:39:38.675Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/92/c445b0cd6da6e7ae51e954939cb69f97e008dbe750cfca89b8cedc081be7/ruff-0.15.8-py3-none-linux_armv6l.whl", hash = "sha256:cbe05adeba76d58162762d6b239c9056f1a15a55bd4b346cfd21e26cd6ad7bc7", size = 10527394, upload-time = "2026-03-26T18:39:41.566Z" }, + { url = "https://files.pythonhosted.org/packages/eb/92/f1c662784d149ad1414cae450b082cf736430c12ca78367f20f5ed569d65/ruff-0.15.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570", size = 10905693, upload-time = "2026-03-26T18:39:30.364Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f2/7a631a8af6d88bcef997eb1bf87cc3da158294c57044aafd3e17030613de/ruff-0.15.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3", size = 10323044, upload-time = "2026-03-26T18:39:33.37Z" }, + { url = "https://files.pythonhosted.org/packages/67/18/1bf38e20914a05e72ef3b9569b1d5c70a7ef26cd188d69e9ca8ef588d5bf/ruff-0.15.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdce027ada77baa448077ccc6ebb2fa9c3c62fd110d8659d601cf2f475858d94", size = 10629135, upload-time = "2026-03-26T18:39:44.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/e9/138c150ff9af60556121623d41aba18b7b57d95ac032e177b6a53789d279/ruff-0.15.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12e617fc01a95e5821648a6df341d80456bd627bfab8a829f7cfc26a14a4b4a3", size = 10348041, upload-time = "2026-03-26T18:39:52.178Z" }, + { url = "https://files.pythonhosted.org/packages/02/f1/5bfb9298d9c323f842c5ddeb85f1f10ef51516ac7a34ba446c9347d898df/ruff-0.15.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:432701303b26416d22ba696c39f2c6f12499b89093b61360abc34bcc9bf07762", size = 11121987, upload-time = "2026-03-26T18:39:55.195Z" }, + { url = "https://files.pythonhosted.org/packages/10/11/6da2e538704e753c04e8d86b1fc55712fdbdcc266af1a1ece7a51fff0d10/ruff-0.15.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d910ae974b7a06a33a057cb87d2a10792a3b2b3b35e33d2699fdf63ec8f6b17a", size = 11951057, upload-time = "2026-03-26T18:39:19.18Z" }, + { url = "https://files.pythonhosted.org/packages/83/f0/c9208c5fd5101bf87002fed774ff25a96eea313d305f1e5d5744698dc314/ruff-0.15.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2033f963c43949d51e6fdccd3946633c6b37c484f5f98c3035f49c27395a8ab8", size = 11464613, upload-time = "2026-03-26T18:40:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/22/d7f2fabdba4fae9f3b570e5605d5eb4500dcb7b770d3217dca4428484b17/ruff-0.15.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1", size = 11257557, upload-time = "2026-03-26T18:39:57.972Z" }, + { url = "https://files.pythonhosted.org/packages/71/8c/382a9620038cf6906446b23ce8632ab8c0811b8f9d3e764f58bedd0c9a6f/ruff-0.15.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:ac51d486bf457cdc985a412fb1801b2dfd1bd8838372fc55de64b1510eff4bec", size = 11169440, upload-time = "2026-03-26T18:39:22.205Z" }, + { url = "https://files.pythonhosted.org/packages/4d/0d/0994c802a7eaaf99380085e4e40c845f8e32a562e20a38ec06174b52ef24/ruff-0.15.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c9861eb959edab053c10ad62c278835ee69ca527b6dcd72b47d5c1e5648964f6", size = 10605963, upload-time = "2026-03-26T18:39:46.682Z" }, + { url = "https://files.pythonhosted.org/packages/19/aa/d624b86f5b0aad7cef6bbf9cd47a6a02dfdc4f72c92a337d724e39c9d14b/ruff-0.15.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8d9a5b8ea13f26ae90838afc33f91b547e61b794865374f114f349e9036835fb", size = 10357484, upload-time = "2026-03-26T18:39:49.176Z" }, + { url = "https://files.pythonhosted.org/packages/35/c3/e0b7835d23001f7d999f3895c6b569927c4d39912286897f625736e1fd04/ruff-0.15.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c2a33a529fb3cbc23a7124b5c6ff121e4d6228029cba374777bd7649cc8598b8", size = 10830426, upload-time = "2026-03-26T18:40:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/f0/51/ab20b322f637b369383adc341d761eaaa0f0203d6b9a7421cd6e783d81b9/ruff-0.15.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:75e5cd06b1cf3f47a3996cfc999226b19aa92e7cce682dcd62f80d7035f98f49", size = 11345125, upload-time = "2026-03-26T18:39:27.799Z" }, + { url = "https://files.pythonhosted.org/packages/37/e6/90b2b33419f59d0f2c4c8a48a4b74b460709a557e8e0064cf33ad894f983/ruff-0.15.8-py3-none-win32.whl", hash = "sha256:bc1f0a51254ba21767bfa9a8b5013ca8149dcf38092e6a9eb704d876de94dc34", size = 10571959, upload-time = "2026-03-26T18:39:36.117Z" }, + { url = "https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl", hash = "sha256:04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89", size = 11743893, upload-time = "2026-03-26T18:39:25.01Z" }, + { url = "https://files.pythonhosted.org/packages/15/e2/77be4fff062fa78d9b2a4dea85d14785dac5f1d0c1fb58ed52331f0ebe28/ruff-0.15.8-py3-none-win_arm64.whl", hash = "sha256:cf891fa8e3bb430c0e7fac93851a5978fc99c8fa2c053b57b118972866f8e5f2", size = 11048175, upload-time = "2026-03-26T18:40:01.06Z" }, ] [[package]] name = "s3fs" -version = "2026.2.0" +version = "2026.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiobotocore" }, { name = "aiohttp" }, { name = "fsspec" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/be/392c8c5e0da9bfa139e41084690dd49a5e3e931099f78f52d3f6070105c6/s3fs-2026.2.0.tar.gz", hash = "sha256:91cb2a9f76e35643b76eeac3f47a6165172bb3def671f76b9111c8dd5779a2ac", size = 84152, upload-time = "2026-02-05T21:57:57.968Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/93/093972862fb9c2fdc24ecf8d6d2212853df1945eddf26ba2625e8eaeee66/s3fs-2026.3.0.tar.gz", hash = "sha256:ce8b30a9dc5e01c5127c96cb7377290243a689a251ef9257336ac29d72d7b0d8", size = 85986, upload-time = "2026-03-27T19:28:20.963Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/57/e1/64c264db50b68de8a438b60ceeb921b2f22da3ebb7ad6255150225d0beac/s3fs-2026.2.0-py3-none-any.whl", hash = "sha256:65198835b86b1d5771112b0085d1da52a6ede36508b1aaa6cae2aedc765dfe10", size = 31328, upload-time = "2026-02-05T21:57:56.532Z" }, + { url = "https://files.pythonhosted.org/packages/6a/52/5ccdc01f7a8a61357d15a66b5d8a6580aa8529cb33f32e6cbb71c52622c5/s3fs-2026.3.0-py3-none-any.whl", hash = "sha256:2fa40a64c03003cfa5ae0e352788d97aa78ae8f9e25ea98b28ce9d21ba10c1b8", size = 32399, upload-time = "2026-03-27T19:28:19.702Z" }, ] [[package]] @@ -6399,7 +6420,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pygments-styles" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/b94cb04adbb984973fe83fd670dd066514610241d829723f678366e691d2/shibuya-2026.1.9.tar.gz", hash = "sha256:b389f10fd9c07b048e940f32d1e1ac096a2d49736389173ac771b37a10b51fdf", size = 86002, upload-time = "2026-01-09T02:19:14.365Z" } wheels = [ @@ -6466,7 +6488,7 @@ dependencies = [ { name = "alabaster", marker = "python_full_version < '3.11'" }, { name = "babel", marker = "python_full_version < '3.11'" }, { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version < '3.11'" }, + { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "imagesize", marker = "python_full_version < '3.11'" }, { name = "jinja2", marker = "python_full_version < '3.11'" }, { name = "packaging", marker = "python_full_version < '3.11'" }, @@ -6488,7 +6510,40 @@ wheels = [ [[package]] name = "sphinx" -version = "8.2.3" +version = "9.0.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "alabaster", marker = "python_full_version == '3.11.*'" }, + { name = "babel", marker = "python_full_version == '3.11.*'" }, + { name = "colorama", marker = "python_full_version == '3.11.*' and sys_platform == 'win32'" }, + { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "imagesize", marker = "python_full_version == '3.11.*'" }, + { name = "jinja2", marker = "python_full_version == '3.11.*'" }, + { name = "packaging", marker = "python_full_version == '3.11.*'" }, + { name = "pygments", marker = "python_full_version == '3.11.*'" }, + { name = "requests", marker = "python_full_version == '3.11.*'" }, + { name = "roman-numerals", marker = "python_full_version == '3.11.*'" }, + { name = "snowballstemmer", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version == '3.11.*'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version == '3.11.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/50/a8c6ccc36d5eacdfd7913ddccd15a9cee03ecafc5ee2bc40e1f168d85022/sphinx-9.0.4.tar.gz", hash = "sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3", size = 8710502, upload-time = "2025-12-04T07:45:27.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/3f/4bbd76424c393caead2e1eb89777f575dee5c8653e2d4b6afd7a564f5974/sphinx-9.0.4-py3-none-any.whl", hash = "sha256:5bebc595a5e943ea248b99c13814c1c5e10b3ece718976824ffa7959ff95fffb", size = 3917713, upload-time = "2025-12-04T07:45:24.944Z" }, +] + +[[package]] +name = "sphinx" +version = "9.1.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", @@ -6496,36 +6551,33 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.13.*' and sys_platform == 'win32'", "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", "python_full_version == '3.13.*' and sys_platform == 'emscripten'", "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "alabaster", marker = "python_full_version >= '3.11'" }, - { name = "babel", marker = "python_full_version >= '3.11'" }, - { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version >= '3.11'" }, - { name = "imagesize", marker = "python_full_version >= '3.11'" }, - { name = "jinja2", marker = "python_full_version >= '3.11'" }, - { name = "packaging", marker = "python_full_version >= '3.11'" }, - { name = "pygments", marker = "python_full_version >= '3.11'" }, - { name = "requests", marker = "python_full_version >= '3.11'" }, - { name = "roman-numerals-py", marker = "python_full_version >= '3.11'" }, - { name = "snowballstemmer", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.11'" }, + { name = "alabaster", marker = "python_full_version >= '3.12'" }, + { name = "babel", marker = "python_full_version >= '3.12'" }, + { name = "colorama", marker = "python_full_version >= '3.12' and sys_platform == 'win32'" }, + { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "imagesize", marker = "python_full_version >= '3.12'" }, + { name = "jinja2", marker = "python_full_version >= '3.12'" }, + { name = "packaging", marker = "python_full_version >= '3.12'" }, + { name = "pygments", marker = "python_full_version >= '3.12'" }, + { name = "requests", marker = "python_full_version >= '3.12'" }, + { name = "roman-numerals", marker = "python_full_version >= '3.12'" }, + { name = "snowballstemmer", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876, upload-time = "2025-03-02T22:31:59.658Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324, upload-time = "2025-12-31T15:09:27.646Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3", size = 3589741, upload-time = "2025-03-02T22:31:56.836Z" }, + { url = "https://files.pythonhosted.org/packages/73/f7/b1884cb3188ab181fc81fa00c266699dab600f927a964df02ec3d5d1916a/sphinx-9.1.0-py3-none-any.whl", hash = "sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978", size = 3921742, upload-time = "2025-12-31T15:09:25.561Z" }, ] [[package]] @@ -6568,7 +6620,8 @@ resolution-markers = [ ] dependencies = [ { name = "colorama", marker = "python_full_version >= '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "starlette", marker = "python_full_version >= '3.11'" }, { name = "uvicorn", marker = "python_full_version >= '3.11'" }, { name = "watchfiles", marker = "python_full_version >= '3.11'" }, @@ -6596,7 +6649,24 @@ wheels = [ [[package]] name = "sphinx-autodoc-typehints" -version = "3.5.2" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/f6/bdd93582b2aaad2cfe9eb5695a44883c8bc44572dd3c351a947acbb13789/sphinx_autodoc_typehints-3.6.1.tar.gz", hash = "sha256:fa0b686ae1b85965116c88260e5e4b82faec3687c2e94d6a10f9b36c3743e2fe", size = 37563, upload-time = "2026-01-02T15:23:46.543Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/6a/c0360b115c81d449b3b73bf74b64ca773464d5c7b1b77bda87c5e874853b/sphinx_autodoc_typehints-3.6.1-py3-none-any.whl", hash = "sha256:dd818ba31d4c97f219a8c0fcacef280424f84a3589cedcb73003ad99c7da41ca", size = 20869, upload-time = "2026-01-02T15:23:45.194Z" }, +] + +[[package]] +name = "sphinx-autodoc-typehints" +version = "3.9.11" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", @@ -6604,20 +6674,17 @@ resolution-markers = [ "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.13.*' and sys_platform == 'win32'", "python_full_version == '3.12.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'win32'", "python_full_version == '3.13.*' and sys_platform == 'emscripten'", "python_full_version == '3.12.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/34/4f/4fd5583678bb7dc8afa69e9b309e6a99ee8d79ad3a4728f4e52fd7cb37c7/sphinx_autodoc_typehints-3.5.2.tar.gz", hash = "sha256:5fcd4a3eb7aa89424c1e2e32bedca66edc38367569c9169a80f4b3e934171fdb", size = 37839, upload-time = "2025-10-16T00:50:15.743Z" } +sdist = { url = "https://files.pythonhosted.org/packages/12/e9/d29ae58dd12971d2cbb872884676a70d1a5e4719b4d82e197264cdf0431a/sphinx_autodoc_typehints-3.9.11.tar.gz", hash = "sha256:28516c916b41fa83271ee2ab9191b73807e4113d3bfb94222ac87d8d9795b6e7", size = 70261, upload-time = "2026-03-24T16:57:28.462Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/f2/9657c98a66973b7c35bfd48ba65d1922860de9598fbb535cd96e3f58a908/sphinx_autodoc_typehints-3.5.2-py3-none-any.whl", hash = "sha256:0accd043619f53c86705958e323b419e41667917045ac9215d7be1b493648d8c", size = 21184, upload-time = "2025-10-16T00:50:13.973Z" }, + { url = "https://files.pythonhosted.org/packages/dd/e3/ff212b51c16717681792eaf18691e6b5affbbb3d4290147c457fa9127372/sphinx_autodoc_typehints-3.9.11-py3-none-any.whl", hash = "sha256:b5cbc7a56a9338021ab7a4e6aa132aa7829fa2f8b64eca927faab64cd3971b80", size = 37279, upload-time = "2026-03-24T16:57:27.147Z" }, ] [[package]] @@ -6626,9 +6693,11 @@ version = "6.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, - { name = "docutils" }, + { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9a/ed/a9767cd1b8b7fbdf260a89d5c8c86e20e3536b9878579e5ab7965a291e55/sphinx_click-6.2.0.tar.gz", hash = "sha256:fc78b4154a4e5159462e36de55b8643747da6cda86b3b52a8bb62289e603776c", size = 27035, upload-time = "2025-12-04T19:33:05.437Z" } wheels = [ @@ -6641,7 +6710,8 @@ version = "0.5.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039, upload-time = "2023-04-14T08:10:22.998Z" } wheels = [ @@ -6655,7 +6725,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinxcontrib-jquery" }, ] sdist = { url = "https://files.pythonhosted.org/packages/86/ee/7246d8b48187794bdeb7389d3bb1247850d3ae0015812e293182193715e1/sphinx_datatables-1.0.0.tar.gz", hash = "sha256:0d0aeccbcc3f4342e4f770848b00a074efb80f08e179a3330da57499cc47cc9d", size = 9548, upload-time = "2026-02-03T04:28:29.554Z" } @@ -6697,7 +6768,8 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/13/7b/804f311da4663a4aecc6cf7abd83443f3d4ded970826d0c958edc77d4527/sphinx_design-0.7.0.tar.gz", hash = "sha256:d2a3f5b19c24b916adb52f97c5f00efab4009ca337812001109084a740ec9b7a", size = 2203582, upload-time = "2026-01-19T13:12:53.297Z" } wheels = [ @@ -6710,7 +6782,8 @@ version = "0.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/68/4e/498964c2a6025e489b255015d3b5644aa93eb1dd1035324de18179af8abc/sphinx_iconify-0.3.0.tar.gz", hash = "sha256:7824ca694472d6babbe811e72cdcaf52b6e0ff4240e8cfda721f84f867605611", size = 3701, upload-time = "2025-12-18T05:19:03.683Z" } wheels = [ @@ -6736,9 +6809,11 @@ name = "sphinx-paramlinks" version = "0.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "docutils" }, + { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ae/21/62d3a58ff7bd02bbb9245a63d1f0d2e0455522a11a78951d16088569fca8/sphinx-paramlinks-0.6.0.tar.gz", hash = "sha256:746a0816860aa3fff5d8d746efcbec4deead421f152687411db1d613d29f915e", size = 12363, upload-time = "2023-08-11T16:09:28.604Z" } @@ -6751,7 +6826,7 @@ resolution-markers = [ ] dependencies = [ { name = "certifi", marker = "python_full_version < '3.11'" }, - { name = "docutils", marker = "python_full_version < '3.11'" }, + { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "idna", marker = "python_full_version < '3.11'" }, { name = "pygments", marker = "python_full_version < '3.11'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -6782,12 +6857,13 @@ resolution-markers = [ ] dependencies = [ { name = "certifi", marker = "python_full_version >= '3.11'" }, - { name = "docutils", marker = "python_full_version >= '3.11'" }, + { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "idna", marker = "python_full_version >= '3.11'" }, { name = "jinja2", marker = "python_full_version >= '3.11'" }, { name = "pygments", marker = "python_full_version >= '3.11'" }, { name = "requests", marker = "python_full_version >= '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "urllib3", marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d0/a3/91293c0e0f0b76d0697ba7a41541929ca3f5457671d008bd84a9bde17e21/sphinx_prompt-1.10.2.tar.gz", hash = "sha256:47b592ba75caebd044b0eddf7a5a1b6e0aef6df587b034377cd101a999b686ba", size = 5566, upload-time = "2025-11-28T09:23:18.057Z" } @@ -6800,10 +6876,12 @@ name = "sphinx-tabs" version = "3.4.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "docutils" }, + { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pygments" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/27/32/ab475e252dc2b704e82a91141fa404cdd8901a5cf34958fd22afacebfccd/sphinx-tabs-3.4.5.tar.gz", hash = "sha256:ba9d0c1e3e37aaadd4b5678449eb08176770e0fc227e769b6ce747df3ceea531", size = 16070, upload-time = "2024-01-21T12:13:39.392Z" } wheels = [ @@ -6812,18 +6890,20 @@ wheels = [ [[package]] name = "sphinx-togglebutton" -version = "0.4.4" +version = "0.4.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "docutils" }, + { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "setuptools" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "wheel" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/89/6b/19def5241b45a7ae90fd91052bb91fa7b8fbcc0606a0cf65ac4ea70fb93b/sphinx_togglebutton-0.4.4.tar.gz", hash = "sha256:04c332692fd5f5363ad02a001e693369767d6c1f0e58279770a2aeb571b472a1", size = 17883, upload-time = "2026-01-14T14:33:11.599Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/be/169a0b0a8ad9588e8697c85e1d489aaaca7416073c2fc0267c360af5aae9/sphinx_togglebutton-0.4.5.tar.gz", hash = "sha256:c870dfbd3bc6e119b50ff9a37a64f8991902269e856728931c7d89877e8d4b3d", size = 18101, upload-time = "2026-03-27T13:50:41.984Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/cb/9f6ceb4308ebfe5f393a271ee6206e17883edee0662a9b5c1a371878064b/sphinx_togglebutton-0.4.4-py3-none-any.whl", hash = "sha256:820658cd4c4c34c2ee7a21105e638b2f65a9e1d43ee991090715eb7fd9683cdf", size = 44892, upload-time = "2026-01-14T14:33:10.674Z" }, + { url = "https://files.pythonhosted.org/packages/d8/2e/3dd55564928c5d61f92827d4b91307dde7911a40fbe0000645d73202eea9/sphinx_togglebutton-0.4.5-py3-none-any.whl", hash = "sha256:74eac6d2426110c3e1e6f989a98e07d7823141a335df1ad8a9d637bdf6a7af62", size = 44907, upload-time = "2026-03-27T13:50:40.94Z" }, ] [[package]] @@ -6836,16 +6916,19 @@ dependencies = [ { name = "beautifulsoup4" }, { name = "cachecontrol", extra = ["filecache"] }, { name = "dict2css" }, - { name = "docutils" }, + { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "domdf-python-tools" }, { name = "filelock" }, { name = "html5lib" }, { name = "roman" }, { name = "ruamel-yaml" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { 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.5.2", 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.9.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx-jinja2-compat" }, { name = "sphinx-prompt", version = "1.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx-prompt", version = "1.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -6891,7 +6974,8 @@ version = "4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331, upload-time = "2023-03-14T15:01:01.944Z" } wheels = [ @@ -6915,7 +6999,8 @@ dependencies = [ { name = "jinja2" }, { name = "pyyaml" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/2b/ae/999891de292919b66ea34f2c22fc22c9be90ab3536fbc0fca95716277351/sphinxcontrib_mermaid-2.0.1.tar.gz", hash = "sha256:a21a385a059a6cafd192aa3a586b14bf5c42721e229db67b459dc825d7f0a497", size = 19839, upload-time = "2026-03-05T14:10:41.901Z" } wheels = [ @@ -7007,25 +7092,25 @@ asyncio = [ [[package]] name = "sqlalchemy-spanner" -version = "1.17.2" +version = "1.17.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "alembic" }, { name = "google-cloud-spanner" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8e/29/21698bb83e542f32e3581886671f39d94b1f7e8b190c24a8bfa994e62fd6/sqlalchemy_spanner-1.17.2.tar.gz", hash = "sha256:56ce4da7168a27442d80ffd71c29ed639b5056d7e69b1e69bb9c1e10190b67c4", size = 82745, upload-time = "2025-12-15T23:30:08.622Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/1c/c7d28d88e8dd9a67be006a40135f05cbdf5a0f5f79bc51bb692f54432cf1/sqlalchemy_spanner-1.17.3.tar.gz", hash = "sha256:ea829d8223c404f19f854c4c2dbf6bf2ee48fb1347caa258f03e88071f3afa22", size = 82842, upload-time = "2026-03-23T22:44:01.25Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/87/05be45a086116cea32cfa00fa0059d31b5345360dba7902ee640a1db793b/sqlalchemy_spanner-1.17.2-py3-none-any.whl", hash = "sha256:18713d4d78e0bf048eda0f7a5c80733e08a7b678b34349496415f37652efb12f", size = 31917, upload-time = "2025-12-15T23:30:07.356Z" }, + { url = "https://files.pythonhosted.org/packages/f3/43/cf21f3e70a8aa9e721fb557bd1459528906f0d9726b2ce642cd757fe592b/sqlalchemy_spanner-1.17.3-py3-none-any.whl", hash = "sha256:b0a13d2cae3bb0ee5aac898c44d22f56ec3edfc7780dd7d165d51f676590daf3", size = 31925, upload-time = "2026-03-23T22:43:33.214Z" }, ] [[package]] name = "sqlglot" -version = "30.0.3" +version = "30.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5b/7a/3b6a8853fc2fa166f785a8ea4fecde46f70588e35471bc7811373da31a49/sqlglot-30.0.3.tar.gz", hash = "sha256:35ba7514c132b54f87fd1732a65a73615efa9fd83f6e1eed0a315bc9ee3e1027", size = 5802632, upload-time = "2026-03-19T16:51:39.166Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/ae/afee950eff42a9c8ceab4a2e25abfeaa8278c578f967201824287cf530ce/sqlglot-30.1.0.tar.gz", hash = "sha256:7593aea85349c577b269d540ba245024f91464afdcf61c6ef7765f4691c46ef8", size = 5812093, upload-time = "2026-03-26T19:25:45.065Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/13/b57ab75b0f60b5ee8cb8924bc01a5c419ed3221e00f8f11f8c059a707eb7/sqlglot-30.0.3-py3-none-any.whl", hash = "sha256:5489cc98b5666f1fafc21e0304ca286e513e142aa054ee5760806a2139d07a05", size = 651853, upload-time = "2026-03-19T16:51:36.241Z" }, + { url = "https://files.pythonhosted.org/packages/29/31/f1cad1972a8eb4b1a9bc904e4a8d440af1eef064160fe10ba0ae81f4693f/sqlglot-30.1.0-py3-none-any.whl", hash = "sha256:6c2d58d0cc68b5f96900058e8866ef4959f89f9e66e4096e0ba746830dda4f40", size = 665823, upload-time = "2026-03-26T19:25:42.794Z" }, ] [package.optional-dependencies] @@ -7035,30 +7120,30 @@ c = [ [[package]] name = "sqlglotc" -version = "30.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/fe/25a3d389834e116d3612e1960b1abedef3dabcc0fe7913a5dcf4541271f5/sqlglotc-30.0.3.tar.gz", hash = "sha256:8e85b401fc3cec09d4341ea14bfe8bac5d28179e348638d00384c41e039ab79b", size = 245524, upload-time = "2026-03-19T16:50:41.572Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/8b/c087dd9c46042bcb6d9914983af93bd8e9e32f1d2c8904593f3c56f1f8f6/sqlglotc-30.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a0e5dc1070531299a734039c8ea0317ba487c5c682c00a18933316afd2cf4350", size = 10584147, upload-time = "2026-03-19T16:50:00.007Z" }, - { url = "https://files.pythonhosted.org/packages/9b/42/c0ce4475bb8c1ec0c100bbb6969dd68bd1beaca7f1c6d9521f71ba35e785/sqlglotc-30.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6f275beb370e74503b08cbeed00e2c365c22d14f4629cd56be21e98f0303bf6c", size = 6949656, upload-time = "2026-03-19T16:50:01.905Z" }, - { url = "https://files.pythonhosted.org/packages/ec/d6/02856739abbe65bd8db615f4a7c6787da02a2a95d5ae3517ccc35b64f1b6/sqlglotc-30.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2459dd0d6b582b5cf409281f264b89502ca48573f8299483e70710ee2cde46ea", size = 7215875, upload-time = "2026-03-19T16:50:03.52Z" }, - { url = "https://files.pythonhosted.org/packages/61/3d/c08167f36c30b865bac8ae9553f39a4649e5a71c4b7dccde0affc807f1b9/sqlglotc-30.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:0cc2d44fb9f61bfeb63785ba4dbf7aa667b934b25980751c87b8342a7e03156b", size = 4395036, upload-time = "2026-03-19T16:50:05.254Z" }, - { url = "https://files.pythonhosted.org/packages/f8/c8/560fe9c91e0fee4da40953efb0c42971808b38f216854114d4c979832d32/sqlglotc-30.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:43c7e32d03cb15f02267b6d74a864c3c538889123d8bf730c8a95e2187c6384c", size = 10546109, upload-time = "2026-03-19T16:50:06.718Z" }, - { url = "https://files.pythonhosted.org/packages/64/50/4a04dfe037f492b60159f8baa3aac21117ac617c52f8f634869982d84573/sqlglotc-30.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41f688dcd187aaa92579a921826b56502752564f4d92016270fa413c7a76086a", size = 6924155, upload-time = "2026-03-19T16:50:09.035Z" }, - { url = "https://files.pythonhosted.org/packages/21/be/2117d619f114176f67c8e5d247ee544f2f499b2bbba56e18c61fa5a64efa/sqlglotc-30.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18c070aa9ccedd1e632feefb214d487be930a92965076304401ddf8071e6b130", size = 7202274, upload-time = "2026-03-19T16:50:10.896Z" }, - { url = "https://files.pythonhosted.org/packages/90/a0/d3b4fa309dc90b1c7d4ccb14d8237e9c28c90af3d1ed55d181924252daf0/sqlglotc-30.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:f2fc0db23ebfe95d893631dd271deb6f3bd69cef8f436cb0e199a4c007a8a5ca", size = 4403122, upload-time = "2026-03-19T16:50:12.51Z" }, - { url = "https://files.pythonhosted.org/packages/b7/ea/e5780202b0368bcf99fa073f1a60791ea4cafae05ea100214ae70e760d52/sqlglotc-30.0.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a41c77aeeef08462d3399411728f6f20e1e0636600c81cc3f76b14608edd17b5", size = 10578486, upload-time = "2026-03-19T16:50:14.067Z" }, - { url = "https://files.pythonhosted.org/packages/55/ca/427d90f964fa2dd08ba1cc26d46c540276ac8f8b427c9fdf4de6dcd29133/sqlglotc-30.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:451a43f0f2bbe17612a3a064fc5a12121e71d2dcf9f8a01c7429c20770915656", size = 7220839, upload-time = "2026-03-19T16:50:15.956Z" }, - { url = "https://files.pythonhosted.org/packages/33/e7/03aa65bd60a35f74f1f4868e48e165fd7d56802fbfd993bf5ca7fcfc2831/sqlglotc-30.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46fa11850bb8e1d704f3adf7b32305894c7c63fdaf41e690cde15e6e872f0f58", size = 7507506, upload-time = "2026-03-19T16:50:17.324Z" }, - { url = "https://files.pythonhosted.org/packages/9a/b3/c6f4f7d0d0b2d64e769820916190fcd820fcf5790891360e1fa9e10e90fc/sqlglotc-30.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:02628b350da82f758f00c5716b5dc2ba1b1b5b0342914707245bcd1473b8ca52", size = 4448222, upload-time = "2026-03-19T16:50:18.735Z" }, - { url = "https://files.pythonhosted.org/packages/1a/55/11c02c84a3be045f01437f14fdc8dd0468af2cddc7671547c05996e55a33/sqlglotc-30.0.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8122cb918c59726dddf8af3ff95eb4c35f7c88d7afa3ee305e2826bc307dffdf", size = 10539631, upload-time = "2026-03-19T16:50:20.036Z" }, - { url = "https://files.pythonhosted.org/packages/95/92/f208b247021a02514d036b33cf131d028df9baae21cab0ee0af062994287/sqlglotc-30.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:43df3e7ef433d80c545a70ef09e805669d86575af7a2c8372a00c377537e133a", size = 7153115, upload-time = "2026-03-19T16:50:21.768Z" }, - { url = "https://files.pythonhosted.org/packages/a4/5e/cc7ef6f5ca6254d919fcbde286063d30c7ea56cba0aee1045ecc9436ec98/sqlglotc-30.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9502413402ceb22f98bf134ca57e6134c223cf76626e2ca49ec21f5bc2451bb", size = 7460691, upload-time = "2026-03-19T16:50:23.276Z" }, - { url = "https://files.pythonhosted.org/packages/d7/2d/92b528281a5f4fad30868081f20d95f8a03dad23872b853fc770aab3d59c/sqlglotc-30.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:240ed9fa007aae03d81e74c766931745e1595892a2c136935070fb2ea1725fcb", size = 4448178, upload-time = "2026-03-19T16:50:25.245Z" }, - { url = "https://files.pythonhosted.org/packages/0d/24/468f0d51c5c5a877acfba41fea0903ae00a29452f1a3ae1be7ce3783e05d/sqlglotc-30.0.3-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:cb767b3bcdb8bdaf5fb733727ffe8e3bb2d25d66971a3770ffe7eb606d4bcb61", size = 10471047, upload-time = "2026-03-19T16:50:26.919Z" }, - { url = "https://files.pythonhosted.org/packages/32/fa/00e784c948276f68aea8ee7da2ff439c8e6a25ce4e8e09cafa995121f754/sqlglotc-30.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eeb2563318168a21cb67cf9031472a7c85da5ff31275fdac955ec5893e9f2533", size = 7124861, upload-time = "2026-03-19T16:50:28.617Z" }, - { url = "https://files.pythonhosted.org/packages/ad/89/dfc94392a795f7784770b0f313d547d2913fbc02a7d71452289cf0728c9a/sqlglotc-30.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:97bde14635bc01dcc95832e3ebf2c4204f026106a44ac7f84abe58e8027555fc", size = 7411447, upload-time = "2026-03-19T16:50:31.605Z" }, - { url = "https://files.pythonhosted.org/packages/8a/21/52494d8aeb96b3718f7526ec450ae58f6f44c13ed850d05a253273e77c35/sqlglotc-30.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:c2b7421bcdc12af326d518417cbf1271d9a34fd926d84145109eeb0efad672fa", size = 4507363, upload-time = "2026-03-19T16:50:33.307Z" }, +version = "30.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b4/53/abd9c353be84baca2272027e274c916786dc99abec3a5cefcd7295670f49/sqlglotc-30.1.0.tar.gz", hash = "sha256:c1e3ca25b0f9f81977862956fbb38dcf7e9f7bf00082ae123ff8d5c93ddec520", size = 433098, upload-time = "2026-03-26T19:24:43.676Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/08/00e1da79529c277e560f665836939543990dd004f2eff152f191eeb6db0e/sqlglotc-30.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e9087ce9ade1d3be91c8025e30b26d3ccc4742795cae0c2736f3de292e684db", size = 17741441, upload-time = "2026-03-26T19:23:57.415Z" }, + { url = "https://files.pythonhosted.org/packages/20/9f/1180591e7d9d15e53c52a13965701dc7aa1b57dcf99ddc950e022dee3357/sqlglotc-30.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2187e5ddbe5f7ddbcd4fc9577fa83edd36266464b7b6736341f28397b5770e14", size = 11800449, upload-time = "2026-03-26T19:23:59.801Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b8/24372b052b8b7fdf1fa63b03e1ea4090289db5edced8fedb09ed507e71c5/sqlglotc-30.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d5f7dcfc8b3761fb3432461fc13983aea1463a8eb6196e3b66e9c3affc45db0", size = 12392798, upload-time = "2026-03-26T19:24:02.215Z" }, + { url = "https://files.pythonhosted.org/packages/61/95/8f6c142e930688e8a3b3147238f34c7217883a9fb7331285caf30b5aa9ac/sqlglotc-30.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:584fcab537f5c845d32dd17ee096df7a5e539a5a65c0bda657767953a7543692", size = 7707373, upload-time = "2026-03-26T19:24:04.49Z" }, + { url = "https://files.pythonhosted.org/packages/2c/47/f4bc1d4a854521b6942e15c7418f087a6c2ddd02ca8f7a663ecbda281193/sqlglotc-30.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aed0e1e5cf1c0cd09bb4d2c89738e2e5da76a07b46e62695e9b4c97843c21044", size = 17666122, upload-time = "2026-03-26T19:24:05.868Z" }, + { url = "https://files.pythonhosted.org/packages/87/28/f1d637e2a02887f0f1e8a5cbaf46e17b096109bbc53598fd44cb69511ad5/sqlglotc-30.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:787bc5860424955aa2c7ff3d1a041faacc85e3b048ede83fb5c470b0fefa68b3", size = 11761274, upload-time = "2026-03-26T19:24:07.881Z" }, + { url = "https://files.pythonhosted.org/packages/d8/d0/3ca532e921f4b0bb45e159fcff0c99ac8a60b2e1440db9dad2afb24c4c78/sqlglotc-30.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b0b0ddfe2033af693ddf2e2f979e37fa7949a2f282b06ade985328470c899fd", size = 12338512, upload-time = "2026-03-26T19:24:09.905Z" }, + { url = "https://files.pythonhosted.org/packages/46/12/5faae564487186af2e0f76d5f07b1d779a23c0236349708a1054031c03ad/sqlglotc-30.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:6a25690f97103687b1ac36576ba677e6ea169c1092bb23670a411d789acecd6a", size = 7720318, upload-time = "2026-03-26T19:24:11.903Z" }, + { url = "https://files.pythonhosted.org/packages/8f/7a/0250322354b18c2e5fba84017d4c3f0c97d27d216786742379775aa9a5d7/sqlglotc-30.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:07fe6a1ae40c42d9b3f00531f93798c2bb3168c01a32d6ed61952207f24b53de", size = 17735285, upload-time = "2026-03-26T19:24:13.573Z" }, + { url = "https://files.pythonhosted.org/packages/1c/1c/271ec81c35a0a966508e8d2c1d81396e8f41eb4c5746905421cfde5fd83a/sqlglotc-30.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dac443012ae7866505d40752005a11a861ad1ef942fdc093abad51d45b86f48b", size = 12407907, upload-time = "2026-03-26T19:24:15.647Z" }, + { url = "https://files.pythonhosted.org/packages/27/6d/6bdb1982b6c8cb35095b39e39320bdedc3888d4df2da18b5b244d86a7ab6/sqlglotc-30.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6919cae44dd21f476444ec851e3bf9579801740401b394e77a1d0e6fc6e22cb", size = 12954625, upload-time = "2026-03-26T19:24:17.452Z" }, + { url = "https://files.pythonhosted.org/packages/4d/0d/036be5ca7c613864241bbe375f0cd6fdb4d58ef2c16343fb14de7a85b518/sqlglotc-30.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:471550fe9725af978129bb37161e48f16bac7659afe2df65b153cd84376d422c", size = 7930443, upload-time = "2026-03-26T19:24:19.074Z" }, + { url = "https://files.pythonhosted.org/packages/7d/be/6befe0a641bfd2698e2552723a4b502b5bad67a80ba87dfa1da4cf968fc9/sqlglotc-30.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:91deaabad97680ab69d02e9878bd82bc3cabbc1709ab2f5fa5fb19a7b29e8521", size = 17671102, upload-time = "2026-03-26T19:24:20.586Z" }, + { url = "https://files.pythonhosted.org/packages/f9/fa/74d938a0f3a3975ab8e2934ea0a958f16c4444bf65f2ac268c74b34b533b/sqlglotc-30.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:75db34b844abdc308015893950828a3c38362009ca6d0dd4a7ab41bc7ab83570", size = 12306463, upload-time = "2026-03-26T19:24:22.739Z" }, + { url = "https://files.pythonhosted.org/packages/7d/59/0fe37835dba5a6eac6193e886c73bfbe20e78a71bae933c7ccf39ffd5c1d/sqlglotc-30.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:040a8540be799688837a8912b05557531952a27747c7bd612e341dff01979118", size = 12871619, upload-time = "2026-03-26T19:24:24.94Z" }, + { url = "https://files.pythonhosted.org/packages/b1/0e/0abdcb2f90bf670431454d041a5ba0d8e656a71be261d1e2fb959c35b96e/sqlglotc-30.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:ca8444caea04c6b817cf8ce039f1c9f429fe358ea81c50e6dd710cf2a9167648", size = 7938367, upload-time = "2026-03-26T19:24:27.421Z" }, + { url = "https://files.pythonhosted.org/packages/c4/52/a7885f87212444372ea0fa4e7db4170169237f0b801df411b41e4d10d563/sqlglotc-30.1.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:83dd3106320f1ce2b94866be0ea32c4bde2528f391ef0247ee80cb5fd7e30cb8", size = 17600245, upload-time = "2026-03-26T19:24:28.879Z" }, + { url = "https://files.pythonhosted.org/packages/25/08/253a66baf42563eb72de33a8d32f08ac41404fda5d3c51eb9d355bf55d23/sqlglotc-30.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:159d1dff253606af9bb15d198cd7f65b7d77924785f0b53c16239a8c261cc318", size = 12297805, upload-time = "2026-03-26T19:24:30.688Z" }, + { url = "https://files.pythonhosted.org/packages/5a/22/e491682a25fb389e528754c0cc1fe09bf94ea4d78538ac49c52de3e266aa/sqlglotc-30.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3182a183fbf3e205cd50d0ed038302e2cd2abd60e6ba0eaf9c909c455e495146", size = 12829341, upload-time = "2026-03-26T19:24:33.264Z" }, + { url = "https://files.pythonhosted.org/packages/48/c9/e25794baa777575f130b35486e814884996e6889581c7e9fd4264f2b6e05/sqlglotc-30.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:1e7a6cbf0b15d8859e9865cf97f2bf9f9a09e53d03f2e0718df560d132f7eacb", size = 8102173, upload-time = "2026-03-26T19:24:34.945Z" }, ] [[package]] @@ -7258,11 +7343,13 @@ dev = [ { name = "slotscheck" }, { name = "sniffio" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx-autobuild", version = "2024.10.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { 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.5.2", 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.9.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx-click" }, { name = "sphinx-copybutton" }, { name = "sphinx-datatables" }, @@ -7294,11 +7381,13 @@ doc = [ { name = "numpydoc" }, { name = "shibuya" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx-autobuild", version = "2024.10.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { 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.5.2", 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.9.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx-click" }, { name = "sphinx-copybutton" }, { name = "sphinx-datatables" }, @@ -7559,15 +7648,15 @@ test = [ [[package]] name = "sse-starlette" -version = "3.3.3" +version = "3.3.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "starlette" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/2f/9223c24f568bb7a0c03d751e609844dce0968f13b39a3f73fbb3a96cd27a/sse_starlette-3.3.3.tar.gz", hash = "sha256:72a95d7575fd5129bd0ae15275ac6432bb35ac542fdebb82889c24bb9f3f4049", size = 32420, upload-time = "2026-03-17T20:05:55.529Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/8c/f9290339ef6d79badbc010f067cd769d6601ec11a57d78569c683fb4dd87/sse_starlette-3.3.4.tar.gz", hash = "sha256:aaf92fc067af8a5427192895ac028e947b484ac01edbc3caf00e7e7137c7bef1", size = 32427, upload-time = "2026-03-29T09:00:23.307Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/e2/b8cff57a67dddf9a464d7e943218e031617fb3ddc133aeeb0602ff5f6c85/sse_starlette-3.3.3-py3-none-any.whl", hash = "sha256:c5abb5082a1cc1c6294d89c5290c46b5f67808cfdb612b7ec27e8ba061c22e8d", size = 14329, upload-time = "2026-03-17T20:05:54.35Z" }, + { url = "https://files.pythonhosted.org/packages/f8/7f/3de5402f39890ac5660b86bcf5c03f9d855dad5c4ed764866d7b592b46fd/sse_starlette-3.3.4-py3-none-any.whl", hash = "sha256:84bb06e58939a8b38d8341f1bc9792f06c2b53f48c608dd207582b664fc8f3c1", size = 14330, upload-time = "2026-03-29T09:00:21.846Z" }, ] [[package]] @@ -7652,56 +7741,56 @@ wheels = [ [[package]] name = "tomli" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" }, - { url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" }, - { url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" }, - { url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" }, - { url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" }, - { url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" }, - { url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" }, - { url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" }, - { url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" }, - { url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" }, - { url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" }, - { url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" }, - { url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" }, - { url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" }, - { url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" }, - { url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" }, - { url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" }, - { url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" }, - { url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" }, - { url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" }, - { url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" }, - { url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" }, - { url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" }, - { url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" }, - { url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" }, - { url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" }, - { url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" }, - { url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" }, - { url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" }, - { url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" }, - { url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" }, - { url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" }, - { url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" }, - { url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" }, - { url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" }, - { url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" }, - { url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" }, - { url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" }, - { url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" }, - { url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" }, - { url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" }, - { url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" }, - { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, + { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, + { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, + { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, + { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, + { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, + { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, + { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, + { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, + { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, + { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, ] [[package]] @@ -8180,14 +8269,14 @@ wheels = [ [[package]] name = "werkzeug" -version = "3.1.6" +version = "3.1.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/f1/ee81806690a87dab5f5653c1f146c92bc066d7f4cebc603ef88eb9e13957/werkzeug-3.1.6.tar.gz", hash = "sha256:210c6bede5a420a913956b4791a7f4d6843a43b6fcee4dfa08a65e93007d0d25", size = 864736, upload-time = "2026-02-19T15:17:18.884Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/43/76ded108b296a49f52de6bac5192ca1c4be84e886f9b5c9ba8427d9694fd/werkzeug-3.1.7.tar.gz", hash = "sha256:fb8c01fe6ab13b9b7cdb46892b99b1d66754e1d7ab8e542e865ec13f526b5351", size = 875700, upload-time = "2026-03-24T01:08:07.687Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/ec/d58832f89ede95652fd01f4f24236af7d32b70cab2196dfcc2d2fd13c5c2/werkzeug-3.1.6-py3-none-any.whl", hash = "sha256:7ddf3357bb9564e407607f988f683d72038551200c704012bb9a4c523d42f131", size = 225166, upload-time = "2026-02-19T15:17:17.475Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b2/0bba9bbb4596d2d2f285a16c2ab04118f6b957d8441566e1abb892e6a6b2/werkzeug-3.1.7-py3-none-any.whl", hash = "sha256:4b314d81163a3e1a169b6a0be2a000a0e204e8873c5de6586f453c55688d422f", size = 226295, upload-time = "2026-03-24T01:08:06.133Z" }, ] [[package]] From 47e51dccc75744512acf49b92efa6c08e9dc65b9 Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Mon, 30 Mar 2026 23:37:34 +0000 Subject: [PATCH 09/15] fix: update exclude and include patterns for dialect extensions in pyproject.toml --- pyproject.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e19dee302..bae37c40c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -192,10 +192,6 @@ exclude = [ "sqlspec/adapters/mock/**", # Mock adapter (testing only) "sqlspec/migrations/commands.py", # Migration command CLI (dynamic imports) "sqlspec/data_dictionary/_loader.py", # Loader relies on __file__ which fails in compiled modules - "sqlspec/dialects/postgres/_pgvector.py", # Dialect shell patches compiled sqlglot parser/generator registries - "sqlspec/dialects/postgres/_paradedb.py", # Dialect shell patches compiled sqlglot parser/generator registries - "sqlspec/dialects/spanner/_spanner.py", # Dialect shell patches compiled sqlglot parser hooks and post-processes DDL - "sqlspec/dialects/spanner/_spangres.py", # Dialect shell patches compiled sqlglot parser hooks "sqlspec/extensions/fastapi/providers.py", # Uses SingletonMeta metaclass "sqlspec/extensions/litestar/providers.py", # Uses SingletonMeta metaclass "sqlspec/adapters/**/data_dictionary.py", # Cross-module inheritance causes mypyc segfaults @@ -205,6 +201,7 @@ exclude = [ include = [ "sqlspec/core/**/*.py", # Core module "sqlspec/builder/**/*.py", # Builder module + "sqlspec/dialects/**/*.py", # Dialect extensions (must compile with sqlglot[c] generators) "sqlspec/exceptions.py", # Exceptions module "sqlspec/loader.py", # Loader module "sqlspec/observability/**/*.py", # Observability utilities From b9e623f105aaecc48440ec8b2458d6414b3f7a44 Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Tue, 31 Mar 2026 00:52:34 +0000 Subject: [PATCH 10/15] fix: refactor Postgres dialect generators and enhance Spanner compatibility --- pyproject.toml | 1 - sqlspec/dialects/postgres/_generators.py | 45 ++++++ sqlspec/dialects/postgres/_paradedb.py | 16 ++- sqlspec/dialects/postgres/_pgvector.py | 42 +----- sqlspec/dialects/spanner/_generators.py | 173 +++++++++++++++++++++++ sqlspec/dialects/spanner/_spangres.py | 29 +--- sqlspec/dialects/spanner/_spanner.py | 100 +------------ 7 files changed, 233 insertions(+), 173 deletions(-) create mode 100644 sqlspec/dialects/postgres/_generators.py create mode 100644 sqlspec/dialects/spanner/_generators.py diff --git a/pyproject.toml b/pyproject.toml index bae37c40c..e0bea0748 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -201,7 +201,6 @@ exclude = [ include = [ "sqlspec/core/**/*.py", # Core module "sqlspec/builder/**/*.py", # Builder module - "sqlspec/dialects/**/*.py", # Dialect extensions (must compile with sqlglot[c] generators) "sqlspec/exceptions.py", # Exceptions module "sqlspec/loader.py", # Loader module "sqlspec/observability/**/*.py", # Observability utilities diff --git a/sqlspec/dialects/postgres/_generators.py b/sqlspec/dialects/postgres/_generators.py new file mode 100644 index 000000000..02d356318 --- /dev/null +++ b/sqlspec/dialects/postgres/_generators.py @@ -0,0 +1,45 @@ +"""Postgres dialect generators with sqlglot[c] compatibility. + +When sqlglot[c] is installed, generators are compiled without +``allow_interpreted_subclasses`` and cross-compilation-unit subclassing +causes segfaults. Instead of subclassing, we patch TRANSFORMS on the +base generator class and alias it. +""" + +from sqlglot import exp +from sqlglot.dialects.postgres import Postgres +from sqlglot.generators.postgres import PostgresGenerator + +from sqlspec.builder._vector_distance import ( + is_vector_distance_expression, + render_vector_distance_postgres, + vector_distance_metric, +) +from sqlspec.dialects.postgres._operators import is_postgres_extension_operator, postgres_extension_operator + +__all__ = ("PGVectorGenerator", "ParadeDBGenerator") + +_BASE_OPERATOR_TRANSFORM = Postgres.Generator.TRANSFORMS[exp.Operator] + + +def _postgres_extension_operator_sql(generator: PostgresGenerator, expression: exp.Operator) -> str: + if is_vector_distance_expression(expression): + return render_vector_distance_postgres( + generator.sql(expression, "this"), + generator.sql(expression, "expression"), + vector_distance_metric(expression), + ) + + if is_postgres_extension_operator(expression): + return ( + f"{generator.sql(expression, 'this')} " + f"{postgres_extension_operator(expression)} " + f"{generator.sql(expression, 'expression')}" + ) + + return _BASE_OPERATOR_TRANSFORM(generator, expression) + + +PostgresGenerator.TRANSFORMS[exp.Operator] = _postgres_extension_operator_sql +PGVectorGenerator = PostgresGenerator +ParadeDBGenerator = PostgresGenerator diff --git a/sqlspec/dialects/postgres/_paradedb.py b/sqlspec/dialects/postgres/_paradedb.py index af5bf425c..9127469be 100644 --- a/sqlspec/dialects/postgres/_paradedb.py +++ b/sqlspec/dialects/postgres/_paradedb.py @@ -19,9 +19,15 @@ """ from sqlglot.dialects.dialect import Dialect +from sqlglot.dialects.postgres import Postgres -from sqlspec.dialects.postgres._operators import PARADEDB_OPERATOR_TOKENS, register_postgres_extension_operators -from sqlspec.dialects.postgres._pgvector import PGVector, PGVectorGenerator, PGVectorTokenizer +from sqlspec.dialects.postgres._generators import ParadeDBGenerator +from sqlspec.dialects.postgres._operators import ( + PARADEDB_OPERATOR_TOKENS, + PGVECTOR_OPERATOR_TOKENS, + register_postgres_extension_operators, +) +from sqlspec.dialects.postgres._pgvector import PGVector, PGVectorTokenizer __all__ = ("ParadeDB",) @@ -31,11 +37,7 @@ class ParadeDBTokenizer(PGVectorTokenizer): """Tokenizer with ParadeDB search operators and pgvector distance operators.""" - KEYWORDS = {**PGVectorTokenizer.KEYWORDS, **PARADEDB_OPERATOR_TOKENS} - - -class ParadeDBGenerator(PGVectorGenerator): - """Generator that renders ParadeDB search operators and pgvector distance operators.""" + KEYWORDS = {**Postgres.Tokenizer.KEYWORDS, **PGVECTOR_OPERATOR_TOKENS, **PARADEDB_OPERATOR_TOKENS} class ParadeDB(PGVector): diff --git a/sqlspec/dialects/postgres/_pgvector.py b/sqlspec/dialects/postgres/_pgvector.py index 6ce410e54..6cf49d816 100644 --- a/sqlspec/dialects/postgres/_pgvector.py +++ b/sqlspec/dialects/postgres/_pgvector.py @@ -9,47 +9,16 @@ - <%> : Jaccard distance (binary vectors) """ -from sqlglot import exp from sqlglot.dialects.dialect import Dialect from sqlglot.dialects.postgres import Postgres -from sqlglot.generators.postgres import PostgresGenerator -from sqlspec.builder._vector_distance import ( - is_vector_distance_expression, - render_vector_distance_postgres, - vector_distance_metric, -) -from sqlspec.dialects.postgres._operators import ( - PGVECTOR_OPERATOR_TOKENS, - is_postgres_extension_operator, - postgres_extension_operator, - register_postgres_extension_operators, -) +from sqlspec.dialects.postgres._generators import PGVectorGenerator +from sqlspec.dialects.postgres._operators import PGVECTOR_OPERATOR_TOKENS, register_postgres_extension_operators __all__ = ("PGVector",) register_postgres_extension_operators() -_BASE_OPERATOR_TRANSFORM = Postgres.Generator.TRANSFORMS[exp.Operator] - - -def _postgres_extension_operator_sql(generator: PostgresGenerator, expression: exp.Operator) -> str: - if is_vector_distance_expression(expression): - return render_vector_distance_postgres( - generator.sql(expression, "this"), - generator.sql(expression, "expression"), - vector_distance_metric(expression), - ) - - if is_postgres_extension_operator(expression): - return ( - f"{generator.sql(expression, 'this')} " - f"{postgres_extension_operator(expression)} " - f"{generator.sql(expression, 'expression')}" - ) - - return _BASE_OPERATOR_TRANSFORM(generator, expression) - class PGVectorTokenizer(Postgres.Tokenizer): """Tokenizer with pgvector distance operators.""" @@ -57,13 +26,6 @@ class PGVectorTokenizer(Postgres.Tokenizer): KEYWORDS = {**Postgres.Tokenizer.KEYWORDS, **PGVECTOR_OPERATOR_TOKENS} -class PGVectorGenerator(PostgresGenerator): - """Generator that renders pgvector and SQLSpec vector-distance operators.""" - - -PGVectorGenerator.TRANSFORMS[exp.Operator] = _postgres_extension_operator_sql - - class PGVector(Postgres): """PostgreSQL dialect with pgvector extension support.""" diff --git a/sqlspec/dialects/spanner/_generators.py b/sqlspec/dialects/spanner/_generators.py new file mode 100644 index 000000000..7b4e8ae62 --- /dev/null +++ b/sqlspec/dialects/spanner/_generators.py @@ -0,0 +1,173 @@ +"""Spanner dialect generators with sqlglot[c] compatibility. + +When sqlglot[c] is installed, generators are compiled without +``allow_interpreted_subclasses`` and cross-compilation-unit subclassing +causes segfaults. Instead of subclassing, we patch methods on the base +generator classes (guarded by dialect checks) and alias them. +""" + +from typing import Any, cast + +from sqlglot import exp +from sqlglot.generators.bigquery import BigQueryGenerator +from sqlglot.generators.postgres import PostgresGenerator + +__all__ = ("SpangresGenerator", "SpannerGenerator") + +_TTL_MIN_COMPONENTS = 2 +_ROW_DELETION_NAME = "ROW_DELETION_POLICY" +_INTERLEAVE_NAME = "INTERLEAVE_IN_PARENT" + +# Capture originals before patching +_original_bq_locate_properties = BigQueryGenerator.locate_properties +_original_bq_properties_sql = BigQueryGenerator.properties_sql +_original_bq_property_sql = BigQueryGenerator.property_sql +_original_pg_property_sql = PostgresGenerator.property_sql + + +def _is_post_schema_spanner_property(expression: exp.Expr) -> bool: + if not isinstance(expression, exp.Property) or not isinstance(expression.this, exp.Literal): + return False + return expression.this.name.upper() in {_INTERLEAVE_NAME, _ROW_DELETION_NAME, "TTL"} + + +def _render_interval_sql(generator: Any, expression: exp.Expr) -> str: + if isinstance(expression, exp.Interval): + unit = expression.args.get("unit") + if isinstance(expression.this, exp.Literal) and not expression.this.is_string and isinstance(unit, exp.Expr): + return f"INTERVAL {generator.sql(expression.this)} {generator.sql(unit)}" + + interval_sql = cast("str", generator.sql(expression)) + if not interval_sql.upper().startswith("INTERVAL"): + return f"INTERVAL {interval_sql}" + return interval_sql + + +def _spanner_locate_properties(self: Any, properties: exp.Properties) -> Any: + """Keep custom Spanner CREATE TABLE properties at the schema boundary.""" + properties_locs = _original_bq_locate_properties(self, properties) + with_properties = list(properties_locs[exp.Properties.Location.POST_WITH]) + if not with_properties: + return properties_locs + + retained_with_properties: list[exp.Expr] = [] + for property_expression in with_properties: + if _is_post_schema_spanner_property(property_expression): + properties_locs[exp.Properties.Location.POST_SCHEMA].append(property_expression) + else: + retained_with_properties.append(property_expression) + + properties_locs[exp.Properties.Location.POST_WITH] = retained_with_properties + return properties_locs + + +def _spanner_properties_sql(self: Any, expression: exp.Properties) -> str: + """Render custom Spanner properties without BigQuery's OPTIONS wrapper.""" + root_properties: list[exp.Expr] = [] + with_properties: list[exp.Expr] = [] + + for property_expression in expression.expressions: + if _is_post_schema_spanner_property(property_expression): + root_properties.append(property_expression) + continue + + property_location = self.PROPERTIES_LOCATION[property_expression.__class__] + if property_location == exp.Properties.Location.POST_WITH: + with_properties.append(property_expression) + elif property_location == exp.Properties.Location.POST_SCHEMA: + root_properties.append(property_expression) + + root_props_ast = exp.Properties(expressions=root_properties) + root_props_ast.parent = expression.parent + with_props_ast = exp.Properties(expressions=with_properties) + with_props_ast.parent = expression.parent + + root_props = self.root_properties(root_props_ast) + with_props = self.with_properties(with_props_ast) + + if root_props and with_props and not self.pretty: + with_props = f" {with_props}" + + return str(root_props) + str(with_props) + + +def _spanner_property_sql(self: Any, expression: exp.Property) -> str: + """Render Spanner-specific properties.""" + if isinstance(expression.this, exp.Literal) and expression.this.name.upper() == _INTERLEAVE_NAME: + values = expression.args.get("value") + if isinstance(values, exp.Tuple) and values.expressions: + parent = self.sql(values.expressions[0]) + sql = f"INTERLEAVE IN PARENT {parent}" + if len(values.expressions) >= _TTL_MIN_COMPONENTS: + on_delete_expr = values.expressions[1] + if isinstance(on_delete_expr, exp.Literal): + sql = f"{sql} ON DELETE {on_delete_expr.this}" + return sql + + if isinstance(expression.this, exp.Literal) and expression.this.name.upper() == _ROW_DELETION_NAME: + values = expression.args.get("value") + if isinstance(values, exp.Tuple) and len(values.expressions) >= _TTL_MIN_COMPONENTS: + column = self.sql(values.expressions[0]) + interval_sql = _render_interval_sql(self, values.expressions[1]) + return f"ROW DELETION POLICY (OLDER_THAN({column}, {interval_sql}))" + + if isinstance(expression.this, exp.Literal) and expression.this.name.upper() == "TTL": + values = expression.args.get("value") + if isinstance(values, exp.Tuple) and len(values.expressions) >= _TTL_MIN_COMPONENTS: + interval = _render_interval_sql(self, values.expressions[0]).removeprefix("INTERVAL ") + column = self.sql(values.expressions[1]) + return f"TTL INTERVAL {interval} ON {column}" + + return str(_original_bq_property_sql(self, expression)) + + +def _spangres_property_sql(self: Any, expression: exp.Property) -> str: + """Render Spangres row deletion policies.""" + if isinstance(expression.this, exp.Literal) and expression.this.name.upper() == _ROW_DELETION_NAME: + values = expression.args.get("value") + if isinstance(values, exp.Tuple) and len(values.expressions) >= _TTL_MIN_COMPONENTS: + column = self.sql(values.expressions[0]) + interval_sql = _render_interval_sql(self, values.expressions[1]) + return f"ROW DELETION POLICY (OLDER_THAN({column}, {interval_sql}))" + + return str(_original_pg_property_sql(self, expression)) + + +# Patch base class methods, guarded by dialect checks at call time + + +def _patched_bq_locate_properties(self: Any, properties: exp.Properties) -> Any: + dialect = getattr(self, "dialect", None) + if dialect and type(dialect).__name__ == "Spanner": + return _spanner_locate_properties(self, properties) + return _original_bq_locate_properties(self, properties) + + +def _patched_bq_properties_sql(self: Any, expression: exp.Properties) -> str: + dialect = getattr(self, "dialect", None) + if dialect and type(dialect).__name__ == "Spanner": + return _spanner_properties_sql(self, expression) + return _original_bq_properties_sql(self, expression) + + +def _patched_bq_property_sql(self: Any, expression: exp.Property) -> str: + dialect = getattr(self, "dialect", None) + if dialect and type(dialect).__name__ == "Spanner": + return _spanner_property_sql(self, expression) + return _original_bq_property_sql(self, expression) + + +def _patched_pg_property_sql(self: Any, expression: exp.Property) -> str: + dialect = getattr(self, "dialect", None) + if dialect and type(dialect).__name__ == "Spangres": + return _spangres_property_sql(self, expression) + return _original_pg_property_sql(self, expression) + + +BigQueryGenerator.locate_properties = _patched_bq_locate_properties +BigQueryGenerator.properties_sql = _patched_bq_properties_sql +BigQueryGenerator.property_sql = _patched_bq_property_sql +PostgresGenerator.property_sql = _patched_pg_property_sql + +SpannerGenerator = BigQueryGenerator +SpangresGenerator = PostgresGenerator diff --git a/sqlspec/dialects/spanner/_spangres.py b/sqlspec/dialects/spanner/_spangres.py index bc40f3adf..f9399dd50 100644 --- a/sqlspec/dialects/spanner/_spangres.py +++ b/sqlspec/dialects/spanner/_spangres.py @@ -4,10 +4,11 @@ from sqlglot import exp from sqlglot.dialects.postgres import Postgres -from sqlglot.generators.postgres import PostgresGenerator from sqlglot.parsers.postgres import PostgresParser from sqlglot.tokenizer_core import TokenType +from sqlspec.dialects.spanner._generators import SpangresGenerator + __all__ = ("Spangres",) _ROW_DELETION_NAME = "ROW_DELETION_POLICY" @@ -38,18 +39,6 @@ def _normalize_interval_expression(expression: exp.Expr) -> exp.Expr: return expression -def _render_interval_sql(generator: Any, expression: exp.Expr) -> str: - if isinstance(expression, exp.Interval): - unit = expression.args.get("unit") - if isinstance(expression.this, exp.Literal) and not expression.this.is_string and isinstance(unit, exp.Expr): - return f"INTERVAL {generator.sql(expression.this)} {generator.sql(unit)}" - - interval_sql = cast("str", generator.sql(expression)) - if not interval_sql.upper().startswith("INTERVAL"): - return f"INTERVAL {interval_sql}" - return interval_sql - - def _spangres_parse_property(self: Any) -> exp.Expr: if _is_spangres_dialect(self) and self._match_text_seq("ROW", "DELETION", "POLICY"): self._match(TokenType.L_PAREN) @@ -78,20 +67,6 @@ def _register_postgres_spangres_parser_hooks() -> None: setattr(PostgresParser, _HOOKS_REGISTERED_ATTR, True) -class SpangresGenerator(PostgresGenerator): - """Generate Spanner row deletion policies.""" - - def property_sql(self, expression: exp.Property) -> str: - if isinstance(expression.this, exp.Literal) and expression.this.name.upper() == _ROW_DELETION_NAME: - values = expression.args.get("value") - if isinstance(values, exp.Tuple) and len(values.expressions) >= _TTL_MIN_COMPONENTS: - column = self.sql(values.expressions[0]) - interval_sql = _render_interval_sql(self, values.expressions[1]) - return f"ROW DELETION POLICY (OLDER_THAN({column}, {interval_sql}))" - - return str(super().property_sql(expression)) - - _register_postgres_spangres_parser_hooks() diff --git a/sqlspec/dialects/spanner/_spanner.py b/sqlspec/dialects/spanner/_spanner.py index 40b0dc9d7..31242def2 100644 --- a/sqlspec/dialects/spanner/_spanner.py +++ b/sqlspec/dialects/spanner/_spanner.py @@ -10,10 +10,11 @@ from sqlglot import exp from sqlglot.dialects.bigquery import BigQuery -from sqlglot.generators.bigquery import BigQueryGenerator from sqlglot.parsers.bigquery import BigQueryParser from sqlglot.tokenizer_core import TokenType +from sqlspec.dialects.spanner._generators import SpannerGenerator + __all__ = ("Spanner",) _SPANNER_KEYWORDS: "dict[str, TokenType]" = {} @@ -73,18 +74,6 @@ def _normalize_interval_expression(expression: exp.Expr) -> exp.Expr: return expression -def _render_interval_sql(generator: Any, expression: exp.Expr) -> str: - if isinstance(expression, exp.Interval): - unit = expression.args.get("unit") - if isinstance(expression.this, exp.Literal) and not expression.this.is_string and isinstance(unit, exp.Expr): - return f"INTERVAL {generator.sql(expression.this)} {generator.sql(unit)}" - - interval_sql = cast("str", generator.sql(expression)) - if not interval_sql.upper().startswith("INTERVAL"): - return f"INTERVAL {interval_sql}" - return interval_sql - - def _extract_interleave_property(sql: str) -> tuple[str, exp.Property | None]: match = _INTERLEAVE_PATTERN.search(sql) if match is None: @@ -108,12 +97,6 @@ def _attach_create_property(create: exp.Create, property_expression: exp.Propert return create -def _is_post_schema_spanner_property(expression: exp.Expr) -> bool: - if not isinstance(expression, exp.Property) or not isinstance(expression.this, exp.Literal): - return False - return expression.this.name.upper() in {_INTERLEAVE_NAME, _ROW_DELETION_NAME, "TTL"} - - def _spanner_parse_property(self: Any) -> exp.Expr: if _is_spanner_dialect(self): if self._match_text_seq("INTERLEAVE", "IN", "PARENT"): @@ -169,85 +152,6 @@ class SpannerTokenizer(BigQuery.Tokenizer): KEYWORDS = {**BigQuery.Tokenizer.KEYWORDS, **_SPANNER_KEYWORDS} -class SpannerGenerator(BigQueryGenerator): - """Generate Spanner-specific DDL syntax.""" - - def locate_properties(self, properties: exp.Properties) -> Any: - """Keep custom Spanner CREATE TABLE properties at the schema boundary.""" - properties_locs = super().locate_properties(properties) - with_properties = list(properties_locs[exp.Properties.Location.POST_WITH]) - if not with_properties: - return properties_locs - - retained_with_properties: list[exp.Expr] = [] - for property_expression in with_properties: - if _is_post_schema_spanner_property(property_expression): - properties_locs[exp.Properties.Location.POST_SCHEMA].append(property_expression) - else: - retained_with_properties.append(property_expression) - - properties_locs[exp.Properties.Location.POST_WITH] = retained_with_properties - return properties_locs - - def properties_sql(self, expression: exp.Properties) -> str: - """Render custom Spanner properties without BigQuery's OPTIONS wrapper.""" - root_properties: list[exp.Expr] = [] - with_properties: list[exp.Expr] = [] - - for property_expression in expression.expressions: - if _is_post_schema_spanner_property(property_expression): - root_properties.append(property_expression) - continue - - property_location = self.PROPERTIES_LOCATION[property_expression.__class__] - if property_location == exp.Properties.Location.POST_WITH: - with_properties.append(property_expression) - elif property_location == exp.Properties.Location.POST_SCHEMA: - root_properties.append(property_expression) - - root_props_ast = exp.Properties(expressions=root_properties) - root_props_ast.parent = expression.parent - with_props_ast = exp.Properties(expressions=with_properties) - with_props_ast.parent = expression.parent - - root_props = self.root_properties(root_props_ast) - with_props = self.with_properties(with_props_ast) - - if root_props and with_props and not self.pretty: - with_props = f" {with_props}" - - return str(root_props) + str(with_props) - - def property_sql(self, expression: exp.Property) -> str: - """Render Spanner-specific properties.""" - if isinstance(expression.this, exp.Literal) and expression.this.name.upper() == _INTERLEAVE_NAME: - values = expression.args.get("value") - if isinstance(values, exp.Tuple) and values.expressions: - parent = self.sql(values.expressions[0]) - sql = f"INTERLEAVE IN PARENT {parent}" - if len(values.expressions) >= _TTL_MIN_COMPONENTS: - on_delete_expr = values.expressions[1] - if isinstance(on_delete_expr, exp.Literal): - sql = f"{sql} ON DELETE {on_delete_expr.this}" - return sql - - if isinstance(expression.this, exp.Literal) and expression.this.name.upper() == _ROW_DELETION_NAME: - values = expression.args.get("value") - if isinstance(values, exp.Tuple) and len(values.expressions) >= _TTL_MIN_COMPONENTS: - column = self.sql(values.expressions[0]) - interval_sql = _render_interval_sql(self, values.expressions[1]) - return f"ROW DELETION POLICY (OLDER_THAN({column}, {interval_sql}))" - - if isinstance(expression.this, exp.Literal) and expression.this.name.upper() == "TTL": - values = expression.args.get("value") - if isinstance(values, exp.Tuple) and len(values.expressions) >= _TTL_MIN_COMPONENTS: - interval = _render_interval_sql(self, values.expressions[0]).removeprefix("INTERVAL ") - column = self.sql(values.expressions[1]) - return f"TTL INTERVAL {interval} ON {column}" - - return str(super().property_sql(expression)) - - _register_bigquery_spanner_parser_hooks() From 2cc75f5aaf57b4ee89e78bb82360851d07014241 Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Tue, 31 Mar 2026 01:13:55 +0000 Subject: [PATCH 11/15] fix: use setattr for method patching in Spanner dialect generators --- sqlspec/dialects/spanner/_generators.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sqlspec/dialects/spanner/_generators.py b/sqlspec/dialects/spanner/_generators.py index 7b4e8ae62..8a4d2ef41 100644 --- a/sqlspec/dialects/spanner/_generators.py +++ b/sqlspec/dialects/spanner/_generators.py @@ -164,10 +164,10 @@ def _patched_pg_property_sql(self: Any, expression: exp.Property) -> str: return _original_pg_property_sql(self, expression) -BigQueryGenerator.locate_properties = _patched_bq_locate_properties -BigQueryGenerator.properties_sql = _patched_bq_properties_sql -BigQueryGenerator.property_sql = _patched_bq_property_sql -PostgresGenerator.property_sql = _patched_pg_property_sql +setattr(BigQueryGenerator, "locate_properties", _patched_bq_locate_properties) +setattr(BigQueryGenerator, "properties_sql", _patched_bq_properties_sql) +setattr(BigQueryGenerator, "property_sql", _patched_bq_property_sql) +setattr(PostgresGenerator, "property_sql", _patched_pg_property_sql) SpannerGenerator = BigQueryGenerator SpangresGenerator = PostgresGenerator From 8f11f5a61cedb894ab05db06c67926677383062e Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Tue, 31 Mar 2026 19:45:19 +0000 Subject: [PATCH 12/15] fix: align ADK event expectations with current model --- pyproject.toml | 1 + sqlspec/dialects/spanner/_spangres.py | 16 +++++------ sqlspec/dialects/spanner/_spanner.py | 28 +++++++++---------- .../extensions/test_adk/test_converters.py | 12 +++----- 4 files changed, 27 insertions(+), 30 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e0bea0748..6768ed89d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -187,6 +187,7 @@ exclude = [ "sqlspec/**/_typing.py", # Type aliases (mypyc-incompatible) "sqlspec/config.py", # Main config "sqlspec/extensions/**", # All extensions + "sqlspec/dialects/**/*.py", # Keep SQLGlot dialect subclasses interpreted "sqlspec/**/__init__.py", # Init files (usually just imports) "sqlspec/protocols.py", # Protocol definitions "sqlspec/adapters/mock/**", # Mock adapter (testing only) diff --git a/sqlspec/dialects/spanner/_spangres.py b/sqlspec/dialects/spanner/_spangres.py index f9399dd50..34be643cc 100644 --- a/sqlspec/dialects/spanner/_spangres.py +++ b/sqlspec/dialects/spanner/_spangres.py @@ -17,6 +17,14 @@ _HOOKS_REGISTERED_ATTR: Final[str] = "_sqlspec_spangres_hooks_registered" +def _normalize_interval_expression(expression: exp.Expr) -> exp.Expr: + if isinstance(expression, exp.Alias): + alias = expression.args.get("alias") + if isinstance(alias, exp.Identifier) and isinstance(expression.this, exp.Expr): + return exp.Interval(this=expression.this.copy(), unit=alias.copy()) + return expression + + def _is_spangres_dialect(parser: Any) -> bool: dialect = getattr(parser, "dialect", None) return dialect is not None and dialect.__class__.__name__ == "Spangres" @@ -31,14 +39,6 @@ def _original_postgres_parse_property() -> Any: return original -def _normalize_interval_expression(expression: exp.Expr) -> exp.Expr: - if isinstance(expression, exp.Alias): - alias = expression.args.get("alias") - if isinstance(alias, exp.Identifier) and isinstance(expression.this, exp.Expr): - return exp.Interval(this=expression.this.copy(), unit=alias.copy()) - return expression - - def _spangres_parse_property(self: Any) -> exp.Expr: if _is_spangres_dialect(self) and self._match_text_seq("ROW", "DELETION", "POLICY"): self._match(TokenType.L_PAREN) diff --git a/sqlspec/dialects/spanner/_spanner.py b/sqlspec/dialects/spanner/_spanner.py index 31242def2..71f9bd73b 100644 --- a/sqlspec/dialects/spanner/_spanner.py +++ b/sqlspec/dialects/spanner/_spanner.py @@ -41,20 +41,6 @@ ) -def _is_spanner_dialect(parser: Any) -> bool: - dialect = getattr(parser, "dialect", None) - return dialect is not None and dialect.__class__.__name__ == "Spanner" - - -def _original_bigquery_parse_property() -> Any: - original = getattr(BigQueryParser, _ORIGINAL_PARSE_PROPERTY_ATTR, None) - if callable(original): - return original - original = BigQueryParser._parse_property - setattr(BigQueryParser, _ORIGINAL_PARSE_PROPERTY_ATTR, original) - return original - - def _normalize_on_delete_value(on_delete: str) -> str: return " ".join(on_delete.upper().split()) @@ -97,6 +83,20 @@ def _attach_create_property(create: exp.Create, property_expression: exp.Propert return create +def _is_spanner_dialect(parser: Any) -> bool: + dialect = getattr(parser, "dialect", None) + return dialect is not None and dialect.__class__.__name__ == "Spanner" + + +def _original_bigquery_parse_property() -> Any: + original = getattr(BigQueryParser, _ORIGINAL_PARSE_PROPERTY_ATTR, None) + if callable(original): + return original + original = BigQueryParser._parse_property + setattr(BigQueryParser, _ORIGINAL_PARSE_PROPERTY_ATTR, original) + return original + + def _spanner_parse_property(self: Any) -> exp.Expr: if _is_spanner_dialect(self): if self._match_text_seq("INTERLEAVE", "IN", "PARENT"): diff --git a/tests/unit/extensions/test_adk/test_converters.py b/tests/unit/extensions/test_adk/test_converters.py index 903358ae9..cf866bef1 100644 --- a/tests/unit/extensions/test_adk/test_converters.py +++ b/tests/unit/extensions/test_adk/test_converters.py @@ -366,22 +366,18 @@ def test_record_to_event_roundtrip_preserves_timestamp() -> None: assert abs(restored.timestamp - fixed_ts) < 1.0 # within 1 second -@pytest.mark.xfail( - reason="ADK Event model uses extra='forbid' — unknown fields raise ValidationError. " - "Future ADK versions that add fields will also update the model, so this is safe.", - strict=True, -) -def test_record_to_event_with_extra_fields_in_event_json() -> None: - """Events with extra/unknown fields in event_json are rejected by Event model.""" +def test_record_to_event_ignores_unknown_fields_in_event_json() -> None: + """Unknown event_json fields are ignored by the current ADK Event model.""" event = _make_event(event_id="extra-fields-evt", author="tool") record = event_to_record(event, "s1") # Inject hypothetical future ADK field into event_json record["event_json"]["hypothetical_v3_field"] = "some_value" # type: ignore[index] - # This WILL raise because Event has extra='forbid' restored = record_to_event(record) assert restored.id == "extra-fields-evt" + assert restored.author == "tool" + assert "hypothetical_v3_field" not in restored.model_dump(exclude_none=True, mode="json") # --------------------------------------------------------------------------- From 6c82eae51d9735648ca0a1492b1a1149b7ce6c72 Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Thu, 2 Apr 2026 15:32:55 +0000 Subject: [PATCH 13/15] feat: move to typed dicts for config --- sqlspec/adapters/adbc/config.py | 10 +- sqlspec/adapters/aiosqlite/config.py | 11 +- sqlspec/adapters/asyncmy/config.py | 13 +- sqlspec/adapters/asyncpg/config.py | 13 +- sqlspec/adapters/bigquery/config.py | 10 +- sqlspec/adapters/duckdb/config.py | 2 +- sqlspec/adapters/oracledb/config.py | 30 +- sqlspec/adapters/psqlpy/config.py | 13 +- sqlspec/adapters/psycopg/config.py | 26 +- sqlspec/adapters/sqlite/config.py | 13 +- sqlspec/builder/_base.py | 21 + sqlspec/config.py | 11 + sqlspec/core/filters.py | 15 +- sqlspec/dialects/_compat.py | 14 + sqlspec/dialects/postgres/_generators.py | 27 +- sqlspec/dialects/spanner/_generators.py | 179 +++++--- sqlspec/driver/_async.py | 26 +- sqlspec/driver/_exception_handler.py | 29 +- sqlspec/driver/_sync.py | 30 +- sqlspec/extensions/fastapi/providers.py | 6 +- .../adapters/asyncpg/test_cloud_connectors.py | 16 +- .../adapters/asyncpg/test_paradedb.py | 23 +- .../adapters/asyncpg/test_pgvector.py | 23 +- .../adapters/duckdb/test_connection.py | 30 +- .../integration/adapters/oracledb/conftest.py | 35 +- .../adapters/oracledb/test_driver_async.py | 40 +- .../adapters/oracledb/test_driver_sync.py | 36 +- .../extensions/events/test_listen_notify.py | 8 +- .../adapters/psqlpy/test_paradedb.py | 15 +- .../adapters/psqlpy/test_pgvector.py | 15 +- .../adapters/psycopg/test_paradedb.py | 17 +- .../adapters/psycopg/test_pgvector.py | 17 +- .../extensions/adk/test_owner_id_column.py | 4 +- .../extensions/events/test_queue_backend.py | 17 +- .../adapters/sqlite/test_pooling.py | 6 +- .../config/test_connection_injection.py | 50 +-- .../fastapi/test_filters_integration.py | 142 +++++++ .../litestar/test_in_fields_filters.py | 167 ++++++++ tests/integration/test_pool_concurrency.py | 20 +- tests/unit/base/test_set_migration_config.py | 56 +++ .../test_connection_config_edge_cases.py | 177 +++++--- .../test_connection_config_parameters.py | 254 +++++------ tests/unit/core/test_filters.py | 238 +++++++++++ tests/unit/core/test_introspection_sql.py | 216 ++++++++++ tests/unit/dialects/test_generators.py | 107 +++++ .../unit/exceptions/test_exception_handler.py | 294 +++++++++++++ .../extensions/test_fastapi/test_providers.py | 42 ++ .../test_litestar/test_providers.py | 122 ++++++ uv.lock | 398 +++++++++--------- 49 files changed, 2380 insertions(+), 704 deletions(-) create mode 100644 sqlspec/dialects/_compat.py create mode 100644 tests/integration/extensions/litestar/test_in_fields_filters.py create mode 100644 tests/unit/base/test_set_migration_config.py create mode 100644 tests/unit/core/test_introspection_sql.py create mode 100644 tests/unit/dialects/test_generators.py create mode 100644 tests/unit/extensions/test_litestar/test_providers.py diff --git a/sqlspec/adapters/adbc/config.py b/sqlspec/adapters/adbc/config.py index 58a101bfa..0ca4980cd 100644 --- a/sqlspec/adapters/adbc/config.py +++ b/sqlspec/adapters/adbc/config.py @@ -171,6 +171,14 @@ class AdbcConfig(NoPoolSyncConfig[AdbcConnection, AdbcDriver]): Supports multiple database backends including PostgreSQL, SQLite, DuckDB, BigQuery, and Snowflake with automatic driver detection and loading. + + Example:: + + config = AdbcConfig( + connection_config=AdbcConnectionParams( + uri="postgresql://user:pass@localhost/db" + ) + ) """ driver_type: ClassVar[type[AdbcDriver]] = AdbcDriver @@ -189,7 +197,7 @@ class AdbcConfig(NoPoolSyncConfig[AdbcConnection, AdbcDriver]): def __init__( self, *, - connection_config: "AdbcConnectionParams | dict[str, Any] | None" = None, + connection_config: "AdbcConnectionParams | None" = None, connection_instance: "Any" = None, migration_config: "dict[str, Any] | None" = None, statement_config: StatementConfig | None = None, diff --git a/sqlspec/adapters/aiosqlite/config.py b/sqlspec/adapters/aiosqlite/config.py index 603e47bf1..2bc608236 100644 --- a/sqlspec/adapters/aiosqlite/config.py +++ b/sqlspec/adapters/aiosqlite/config.py @@ -137,7 +137,14 @@ async def __aexit__( @mypyc_attr(native_class=False) class AiosqliteConfig(AsyncDatabaseConfig["AiosqliteConnection", AiosqliteConnectionPool, AiosqliteDriver]): - """Database configuration for AioSQLite engine.""" + """Database configuration for AioSQLite engine. + + Example:: + + config = AiosqliteConfig( + connection_config=AiosqlitePoolParams(database="app.db") + ) + """ driver_type: "ClassVar[type[AiosqliteDriver]]" = AiosqliteDriver connection_type: "ClassVar[type[AiosqliteConnection]]" = AiosqliteConnection @@ -154,7 +161,7 @@ class AiosqliteConfig(AsyncDatabaseConfig["AiosqliteConnection", AiosqliteConnec def __init__( self, *, - connection_config: "AiosqlitePoolParams | dict[str, Any] | None" = None, + connection_config: "AiosqlitePoolParams | None" = None, connection_instance: "AiosqliteConnectionPool | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/adapters/asyncmy/config.py b/sqlspec/adapters/asyncmy/config.py index 168f57544..ad2e9e118 100644 --- a/sqlspec/adapters/asyncmy/config.py +++ b/sqlspec/adapters/asyncmy/config.py @@ -148,7 +148,16 @@ async def __aexit__( @mypyc_attr(native_class=False) class AsyncmyConfig(AsyncDatabaseConfig[AsyncmyConnection, "AsyncmyPool", AsyncmyDriver]): # pyright: ignore - """Configuration for Asyncmy database connections.""" + """Configuration for Asyncmy database connections. + + Example:: + + config = AsyncmyConfig( + connection_config=AsyncmyPoolParams( + host="localhost", user="root", database="mydb" + ) + ) + """ driver_type: ClassVar[type[AsyncmyDriver]] = AsyncmyDriver connection_type: "ClassVar[type[Any]]" = cast("type[Any]", AsyncmyConnection) @@ -165,7 +174,7 @@ class AsyncmyConfig(AsyncDatabaseConfig[AsyncmyConnection, "AsyncmyPool", Asyncm def __init__( self, *, - connection_config: "AsyncmyPoolParams | dict[str, Any] | None" = None, + connection_config: "AsyncmyPoolParams | None" = None, connection_instance: "AsyncmyPool | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/adapters/asyncpg/config.py b/sqlspec/adapters/asyncpg/config.py index e169c26f4..50cc73e93 100644 --- a/sqlspec/adapters/asyncpg/config.py +++ b/sqlspec/adapters/asyncpg/config.py @@ -246,7 +246,16 @@ class AsyncpgConnectionContext(AsyncPoolConnectionContext): @mypyc_attr(native_class=False) class AsyncpgConfig(AsyncDatabaseConfig[AsyncpgConnection, "Pool[Record]", AsyncpgDriver]): - """Configuration for AsyncPG database connections using TypedDict.""" + """Configuration for AsyncPG database connections using TypedDict. + + Example:: + + config = AsyncpgConfig( + connection_config=AsyncpgPoolConfig( + dsn="postgresql://user:pass@localhost/db" + ) + ) + """ driver_type: "ClassVar[type[AsyncpgDriver]]" = AsyncpgDriver connection_type: "ClassVar[type[AsyncpgConnection]]" = type(AsyncpgConnection) # type: ignore[assignment] @@ -263,7 +272,7 @@ class AsyncpgConfig(AsyncDatabaseConfig[AsyncpgConnection, "Pool[Record]", Async def __init__( self, *, - connection_config: "AsyncpgPoolConfig | dict[str, Any] | None" = None, + connection_config: "AsyncpgPoolConfig | None" = None, connection_instance: "Pool[Record] | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/adapters/bigquery/config.py b/sqlspec/adapters/bigquery/config.py index b23dc9a72..989803fef 100644 --- a/sqlspec/adapters/bigquery/config.py +++ b/sqlspec/adapters/bigquery/config.py @@ -151,6 +151,14 @@ class BigQueryConfig(NoPoolSyncConfig[BigQueryConnection, BigQueryDriver]): """BigQuery configuration. Configuration for Google Cloud BigQuery connections. + + Example:: + + config = BigQueryConfig( + connection_config=BigQueryConnectionParams( + project="my-gcp-project" + ) + ) """ driver_type: ClassVar[type[BigQueryDriver]] = BigQueryDriver @@ -169,7 +177,7 @@ class BigQueryConfig(NoPoolSyncConfig[BigQueryConnection, BigQueryDriver]): def __init__( self, *, - connection_config: "BigQueryConnectionParams | dict[str, Any] | None" = None, + connection_config: "BigQueryConnectionParams | None" = None, connection_instance: "Any" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/adapters/duckdb/config.py b/sqlspec/adapters/duckdb/config.py index 5fb03c133..e3e593d8a 100644 --- a/sqlspec/adapters/duckdb/config.py +++ b/sqlspec/adapters/duckdb/config.py @@ -235,7 +235,7 @@ class DuckDBConfig(SyncDatabaseConfig[DuckDBConnection, DuckDBConnectionPool, Du def __init__( self, *, - connection_config: "DuckDBPoolParams | dict[str, Any] | None" = None, + connection_config: "DuckDBPoolParams | None" = None, connection_instance: "DuckDBConnectionPool | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/adapters/oracledb/config.py b/sqlspec/adapters/oracledb/config.py index d916bd4b9..58e271478 100644 --- a/sqlspec/adapters/oracledb/config.py +++ b/sqlspec/adapters/oracledb/config.py @@ -176,7 +176,18 @@ def release_connection(self, _conn: "OracleSyncConnection", **kwargs: Any) -> No class OracleSyncConfig(SyncDatabaseConfig[OracleSyncConnection, "OracleSyncConnectionPool", OracleSyncDriver]): - """Configuration for Oracle synchronous database connections.""" + """Configuration for Oracle synchronous database connections. + + Example:: + + config = OracleSyncConfig( + connection_config=OraclePoolParams( + dsn="localhost:1521/XEPDB1", + user="app", + password="secret", + ) + ) + """ __slots__ = ("_user_connection_hook",) @@ -196,7 +207,7 @@ class OracleSyncConfig(SyncDatabaseConfig[OracleSyncConnection, "OracleSyncConne def __init__( self, *, - connection_config: "OraclePoolParams | dict[str, Any] | None" = None, + connection_config: "OraclePoolParams | None" = None, connection_instance: "OracleSyncConnectionPool | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, @@ -338,7 +349,18 @@ class _OracleAsyncSessionConnectionHandler(AsyncPoolSessionFactory): @mypyc_attr(native_class=False) class OracleAsyncConfig(AsyncDatabaseConfig[OracleAsyncConnection, "OracleAsyncConnectionPool", OracleAsyncDriver]): - """Configuration for Oracle asynchronous database connections.""" + """Configuration for Oracle asynchronous database connections. + + Example:: + + config = OracleAsyncConfig( + connection_config=OraclePoolParams( + dsn="localhost:1521/XEPDB1", + user="app", + password="secret", + ) + ) + """ __slots__ = ("_user_connection_hook",) @@ -360,7 +382,7 @@ class OracleAsyncConfig(AsyncDatabaseConfig[OracleAsyncConnection, "OracleAsyncC def __init__( self, *, - connection_config: "OraclePoolParams | dict[str, Any] | None" = None, + connection_config: "OraclePoolParams | None" = None, connection_instance: "OracleAsyncConnectionPool | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/adapters/psqlpy/config.py b/sqlspec/adapters/psqlpy/config.py index 88ff8b46d..3e7bcab08 100644 --- a/sqlspec/adapters/psqlpy/config.py +++ b/sqlspec/adapters/psqlpy/config.py @@ -177,7 +177,16 @@ async def __aexit__( @mypyc_attr(native_class=False) class PsqlpyConfig(AsyncDatabaseConfig[PsqlpyConnection, ConnectionPool, PsqlpyDriver]): - """Configuration for Psqlpy asynchronous database connections.""" + """Configuration for Psqlpy asynchronous database connections. + + Example:: + + config = PsqlpyConfig( + connection_config=PsqlpyPoolParams( + dsn="postgresql://user:pass@localhost/db" + ) + ) + """ driver_type: ClassVar[type[PsqlpyDriver]] = PsqlpyDriver connection_type: "ClassVar[type[PsqlpyConnection]]" = PsqlpyConnection @@ -194,7 +203,7 @@ class PsqlpyConfig(AsyncDatabaseConfig[PsqlpyConnection, ConnectionPool, PsqlpyD def __init__( self, *, - connection_config: "PsqlpyPoolParams | dict[str, Any] | None" = None, + connection_config: "PsqlpyPoolParams | None" = None, connection_instance: ConnectionPool | None = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/adapters/psycopg/config.py b/sqlspec/adapters/psycopg/config.py index 093332783..ac212f89c 100644 --- a/sqlspec/adapters/psycopg/config.py +++ b/sqlspec/adapters/psycopg/config.py @@ -182,7 +182,16 @@ def release_connection(self, _conn: "PsycopgSyncConnection", **kwargs: Any) -> N class PsycopgSyncConfig(SyncDatabaseConfig[PsycopgSyncConnection, ConnectionPool, PsycopgSyncDriver]): - """Configuration for Psycopg synchronous database connections with direct field-based configuration.""" + """Configuration for Psycopg synchronous database connections with direct field-based configuration. + + Example:: + + config = PsycopgSyncConfig( + connection_config=PsycopgPoolParams( + conninfo="postgresql://user:pass@localhost/db" + ) + ) + """ driver_type: "ClassVar[type[PsycopgSyncDriver]]" = PsycopgSyncDriver connection_type: "ClassVar[type[PsycopgSyncConnection]]" = PsycopgSyncConnection @@ -201,7 +210,7 @@ class PsycopgSyncConfig(SyncDatabaseConfig[PsycopgSyncConnection, ConnectionPool def __init__( self, *, - connection_config: "PsycopgPoolParams | dict[str, Any] | None" = None, + connection_config: "PsycopgPoolParams | None" = None, connection_instance: "ConnectionPool | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, @@ -454,7 +463,16 @@ async def release_connection(self, _conn: "PsycopgAsyncConnection", **kwargs: An @mypyc_attr(native_class=False) class PsycopgAsyncConfig(AsyncDatabaseConfig[PsycopgAsyncConnection, AsyncConnectionPool, PsycopgAsyncDriver]): - """Configuration for Psycopg asynchronous database connections with direct field-based configuration.""" + """Configuration for Psycopg asynchronous database connections with direct field-based configuration. + + Example:: + + config = PsycopgAsyncConfig( + connection_config=PsycopgPoolParams( + conninfo="postgresql://user:pass@localhost/db" + ) + ) + """ driver_type: ClassVar[type[PsycopgAsyncDriver]] = PsycopgAsyncDriver connection_type: "ClassVar[type[PsycopgAsyncConnection]]" = PsycopgAsyncConnection @@ -473,7 +491,7 @@ class PsycopgAsyncConfig(AsyncDatabaseConfig[PsycopgAsyncConnection, AsyncConnec def __init__( self, *, - connection_config: "PsycopgPoolParams | dict[str, Any] | None" = None, + connection_config: "PsycopgPoolParams | None" = None, connection_instance: "AsyncConnectionPool | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/adapters/sqlite/config.py b/sqlspec/adapters/sqlite/config.py index 13b636b98..ef67f3fce 100644 --- a/sqlspec/adapters/sqlite/config.py +++ b/sqlspec/adapters/sqlite/config.py @@ -83,7 +83,16 @@ class _SqliteSessionConnectionHandler(SyncPoolSessionFactory): class SqliteConfig(SyncDatabaseConfig[SqliteConnection, SqliteConnectionPool, SqliteDriver]): - """SQLite configuration with thread-local connections.""" + """SQLite configuration with thread-local connections. + + Example:: + + config = SqliteConfig( + connection_config=SqliteConnectionParams( + database="app.db" + ) + ) + """ driver_type: "ClassVar[type[SqliteDriver]]" = SqliteDriver connection_type: "ClassVar[type[SqliteConnection]]" = SqliteConnection @@ -100,7 +109,7 @@ class SqliteConfig(SyncDatabaseConfig[SqliteConnection, SqliteConnectionPool, Sq def __init__( self, *, - connection_config: "SqliteConnectionParams | dict[str, Any] | None" = None, + connection_config: "SqliteConnectionParams | None" = None, connection_instance: "SqliteConnectionPool | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/builder/_base.py b/sqlspec/builder/_base.py index 65a0c0282..8a89500a6 100644 --- a/sqlspec/builder/_base.py +++ b/sqlspec/builder/_base.py @@ -28,6 +28,7 @@ get_cache_config, hash_optimized_expression, ) +from sqlspec.core.filters import StatementFilter from sqlspec.exceptions import SQLBuilderError from sqlspec.utils.logging import get_logger from sqlspec.utils.type_guards import has_expression_and_parameters, has_name, has_with_method, is_expression @@ -792,6 +793,26 @@ def to_statement(self, config: "StatementConfig | None" = None) -> "SQL": return sql_statement + def apply_filters(self, *filters: StatementFilter, config: "StatementConfig | None" = None) -> "SQL": + """Apply statement filters to this query builder. + + Converts the builder to a SQL statement, then applies each filter's + ``append_to_statement`` method to produce the final SQL with all + WHERE clauses, ORDER BY, LIMIT/OFFSET, etc. + + Args: + *filters: Statement filters (e.g., InCollectionFilter, SearchFilter, OrderByFilter). + config: Optional SQL configuration. + + Returns: + SQL statement with all filters applied. + + """ + sql_statement = self.to_statement(config) + for filter_obj in filters: + sql_statement = filter_obj.append_to_statement(sql_statement) + return sql_statement + def _to_statement(self, config: "StatementConfig | None" = None) -> "SQL": """Internal method to create SQL statement. diff --git a/sqlspec/config.py b/sqlspec/config.py index 7d899bf7c..70856af48 100644 --- a/sqlspec/config.py +++ b/sqlspec/config.py @@ -1036,6 +1036,17 @@ def migration_config(self, value: "dict[str, Any] | MigrationConfig | None") -> if self._migration_components_ready(): self._initialize_migration_components() + def set_migration_config(self, config: "dict[str, Any] | MigrationConfig") -> None: + """Attach migration configuration after initial config creation. + + This is equivalent to setting ``migration_config`` directly but provides + a discoverable method for post-construction configuration. + + Args: + config: Migration configuration dictionary. + """ + self.migration_config = config + def storage_capabilities(self) -> "StorageCapabilities": """Return cached storage capabilities for this configuration.""" diff --git a/sqlspec/core/filters.py b/sqlspec/core/filters.py index e9a2f0409..a77e86e95 100644 --- a/sqlspec/core/filters.py +++ b/sqlspec/core/filters.py @@ -77,12 +77,16 @@ class StatementFilter(ABC): def append_to_statement(self, statement: "SQL") -> "SQL": """Append the filter to the statement. - This method should modify the SQL expression only, not the parameters. - Parameters should be provided via extract_parameters(). + This method modifies the SQL expression and adds parameters via + ``add_named_parameter()`` on the returned statement. """ def extract_parameters(self) -> "tuple[list[Any], dict[str, Any]]": - """Extract parameters that this filter contributes. + """Return the parameters this filter would contribute. + + This is an introspection helper and is not called during the normal + filter-application path (``append_to_statement`` handles both + expression modification and parameter addition). Returns: Tuple of (positional_parameters, named_parameters) where: @@ -382,8 +386,7 @@ def get_param_names(self) -> "list[str]": """Get parameter names without storing them.""" if not self.values: return [] - # Use object id to ensure uniqueness between instances - return [f"{self.field_name}_notin_{i}_{id(self)}" for i, _ in enumerate(self.values)] + return [f"{self.field_name}_notin_{i}" for i, _ in enumerate(self.values)] def extract_parameters(self) -> "tuple[list[Any], dict[str, Any]]": """Extract filter parameters.""" @@ -677,7 +680,7 @@ class SearchFilter(StatementFilter): def __init__(self, field_name: str | set[str], value: str | None, ignore_case: bool | None = False) -> None: self._field_name = field_name self._value = value - self._ignore_case = ignore_case + self._ignore_case = ignore_case if ignore_case is not None else False @property def field_name(self) -> "str | set[str]": diff --git a/sqlspec/dialects/_compat.py b/sqlspec/dialects/_compat.py new file mode 100644 index 000000000..258314804 --- /dev/null +++ b/sqlspec/dialects/_compat.py @@ -0,0 +1,14 @@ +"""Compatibility layer for sqlglot[c] compiled generators.""" + +__all__ = ("is_generator_compiled",) + + +def is_generator_compiled(generator_class: type) -> bool: + """Check if a sqlglot generator class is mypyc-compiled. + + When sqlglot[c] is installed, generator modules are compiled and + reject interpreted subclasses. This detection enables conditional + code paths: clean subclasses for pure Python, TRANSFORMS patching + for compiled. + """ + return hasattr(generator_class, "__mypyc_attrs__") diff --git a/sqlspec/dialects/postgres/_generators.py b/sqlspec/dialects/postgres/_generators.py index 02d356318..595165c0b 100644 --- a/sqlspec/dialects/postgres/_generators.py +++ b/sqlspec/dialects/postgres/_generators.py @@ -1,9 +1,9 @@ """Postgres dialect generators with sqlglot[c] compatibility. -When sqlglot[c] is installed, generators are compiled without -``allow_interpreted_subclasses`` and cross-compilation-unit subclassing -causes segfaults. Instead of subclassing, we patch TRANSFORMS on the -base generator class and alias it. +When sqlglot[c] is installed, generators are compiled and reject interpreted +subclasses at instantiation. We detect this and use TRANSFORMS patching on +the base generator. When running pure-Python sqlglot, we create real subclasses +for cleaner isolation. """ from sqlglot import exp @@ -15,6 +15,7 @@ render_vector_distance_postgres, vector_distance_metric, ) +from sqlspec.dialects._compat import is_generator_compiled from sqlspec.dialects.postgres._operators import is_postgres_extension_operator, postgres_extension_operator __all__ = ("PGVectorGenerator", "ParadeDBGenerator") @@ -40,6 +41,18 @@ def _postgres_extension_operator_sql(generator: PostgresGenerator, expression: e return _BASE_OPERATOR_TRANSFORM(generator, expression) -PostgresGenerator.TRANSFORMS[exp.Operator] = _postgres_extension_operator_sql -PGVectorGenerator = PostgresGenerator -ParadeDBGenerator = PostgresGenerator +if is_generator_compiled(PostgresGenerator): + # sqlglot[c]: patch TRANSFORMS on the compiled base class and alias + PostgresGenerator.TRANSFORMS[exp.Operator] = _postgres_extension_operator_sql + PGVectorGenerator = PostgresGenerator + ParadeDBGenerator = PostgresGenerator +else: + # Pure-Python sqlglot: create real subclasses for clean isolation + + class PGVectorGenerator(PostgresGenerator): # type: ignore[no-redef] + """Generator that renders pgvector and SQLSpec vector-distance operators.""" + + TRANSFORMS = {**PostgresGenerator.TRANSFORMS, exp.Operator: _postgres_extension_operator_sql} + + class ParadeDBGenerator(PGVectorGenerator): # type: ignore[no-redef] + """Generator that renders ParadeDB search operators plus pgvector operators.""" diff --git a/sqlspec/dialects/spanner/_generators.py b/sqlspec/dialects/spanner/_generators.py index 8a4d2ef41..bcc3fe61b 100644 --- a/sqlspec/dialects/spanner/_generators.py +++ b/sqlspec/dialects/spanner/_generators.py @@ -1,9 +1,8 @@ """Spanner dialect generators with sqlglot[c] compatibility. -When sqlglot[c] is installed, generators are compiled without -``allow_interpreted_subclasses`` and cross-compilation-unit subclassing -causes segfaults. Instead of subclassing, we patch methods on the base -generator classes (guarded by dialect checks) and alias them. +When sqlglot[c] is installed, generators are compiled and reject interpreted +subclasses. We detect this and use TRANSFORMS-based extension on the base +generators. When running pure-Python sqlglot, we create real subclasses. """ from typing import Any, cast @@ -12,16 +11,18 @@ from sqlglot.generators.bigquery import BigQueryGenerator from sqlglot.generators.postgres import PostgresGenerator +from sqlspec.dialects._compat import is_generator_compiled + __all__ = ("SpangresGenerator", "SpannerGenerator") _TTL_MIN_COMPONENTS = 2 _ROW_DELETION_NAME = "ROW_DELETION_POLICY" _INTERLEAVE_NAME = "INTERLEAVE_IN_PARENT" -# Capture originals before patching -_original_bq_locate_properties = BigQueryGenerator.locate_properties -_original_bq_properties_sql = BigQueryGenerator.properties_sql +# Capture originals before any patching _original_bq_property_sql = BigQueryGenerator.property_sql +_original_bq_properties_sql = BigQueryGenerator.properties_sql +_original_bq_locate_properties = BigQueryGenerator.locate_properties _original_pg_property_sql = PostgresGenerator.property_sql @@ -43,6 +44,41 @@ def _render_interval_sql(generator: Any, expression: exp.Expr) -> str: return interval_sql +# --------------------------------------------------------------------------- +# Spanner property rendering (BigQuery-based) +# --------------------------------------------------------------------------- + + +def _spanner_property_sql(self: Any, expression: exp.Property) -> str: + """Render Spanner-specific properties (INTERLEAVE, ROW_DELETION_POLICY, TTL).""" + if isinstance(expression.this, exp.Literal) and expression.this.name.upper() == _INTERLEAVE_NAME: + values = expression.args.get("value") + if isinstance(values, exp.Tuple) and values.expressions: + parent = self.sql(values.expressions[0]) + sql = f"INTERLEAVE IN PARENT {parent}" + if len(values.expressions) >= _TTL_MIN_COMPONENTS: + on_delete_expr = values.expressions[1] + if isinstance(on_delete_expr, exp.Literal): + sql = f"{sql} ON DELETE {on_delete_expr.this}" + return sql + + if isinstance(expression.this, exp.Literal) and expression.this.name.upper() == _ROW_DELETION_NAME: + values = expression.args.get("value") + if isinstance(values, exp.Tuple) and len(values.expressions) >= _TTL_MIN_COMPONENTS: + column = self.sql(values.expressions[0]) + interval_sql = _render_interval_sql(self, values.expressions[1]) + return f"ROW DELETION POLICY (OLDER_THAN({column}, {interval_sql}))" + + if isinstance(expression.this, exp.Literal) and expression.this.name.upper() == "TTL": + values = expression.args.get("value") + if isinstance(values, exp.Tuple) and len(values.expressions) >= _TTL_MIN_COMPONENTS: + interval = _render_interval_sql(self, values.expressions[0]).removeprefix("INTERVAL ") + column = self.sql(values.expressions[1]) + return f"TTL INTERVAL {interval} ON {column}" + + return str(_original_bq_property_sql(self, expression)) + + def _spanner_locate_properties(self: Any, properties: exp.Properties) -> Any: """Keep custom Spanner CREATE TABLE properties at the schema boundary.""" properties_locs = _original_bq_locate_properties(self, properties) @@ -91,34 +127,9 @@ def _spanner_properties_sql(self: Any, expression: exp.Properties) -> str: return str(root_props) + str(with_props) -def _spanner_property_sql(self: Any, expression: exp.Property) -> str: - """Render Spanner-specific properties.""" - if isinstance(expression.this, exp.Literal) and expression.this.name.upper() == _INTERLEAVE_NAME: - values = expression.args.get("value") - if isinstance(values, exp.Tuple) and values.expressions: - parent = self.sql(values.expressions[0]) - sql = f"INTERLEAVE IN PARENT {parent}" - if len(values.expressions) >= _TTL_MIN_COMPONENTS: - on_delete_expr = values.expressions[1] - if isinstance(on_delete_expr, exp.Literal): - sql = f"{sql} ON DELETE {on_delete_expr.this}" - return sql - - if isinstance(expression.this, exp.Literal) and expression.this.name.upper() == _ROW_DELETION_NAME: - values = expression.args.get("value") - if isinstance(values, exp.Tuple) and len(values.expressions) >= _TTL_MIN_COMPONENTS: - column = self.sql(values.expressions[0]) - interval_sql = _render_interval_sql(self, values.expressions[1]) - return f"ROW DELETION POLICY (OLDER_THAN({column}, {interval_sql}))" - - if isinstance(expression.this, exp.Literal) and expression.this.name.upper() == "TTL": - values = expression.args.get("value") - if isinstance(values, exp.Tuple) and len(values.expressions) >= _TTL_MIN_COMPONENTS: - interval = _render_interval_sql(self, values.expressions[0]).removeprefix("INTERVAL ") - column = self.sql(values.expressions[1]) - return f"TTL INTERVAL {interval} ON {column}" - - return str(_original_bq_property_sql(self, expression)) +# --------------------------------------------------------------------------- +# Spangres property rendering (Postgres-based) +# --------------------------------------------------------------------------- def _spangres_property_sql(self: Any, expression: exp.Property) -> str: @@ -133,41 +144,85 @@ def _spangres_property_sql(self: Any, expression: exp.Property) -> str: return str(_original_pg_property_sql(self, expression)) -# Patch base class methods, guarded by dialect checks at call time +# --------------------------------------------------------------------------- +# Conditional path: compiled vs pure-Python +# --------------------------------------------------------------------------- + +_bq_compiled = is_generator_compiled(BigQueryGenerator) +_pg_compiled = is_generator_compiled(PostgresGenerator) + +if _bq_compiled: + # sqlglot[c]: use TRANSFORMS entries with dialect name checks + + _original_bq_property_transform = BigQueryGenerator.TRANSFORMS.get(exp.Property) + _original_bq_properties_transform = BigQueryGenerator.TRANSFORMS.get(exp.Properties) + + def _bq_property_transform(self: Any, expression: exp.Property) -> str: + dialect = getattr(self, "dialect", None) + if dialect and type(dialect).__name__ == "Spanner": + return _spanner_property_sql(self, expression) + if _original_bq_property_transform is not None: + return _original_bq_property_transform(self, expression) + return self.property_sql(expression) + + def _bq_properties_transform(self: Any, expression: exp.Properties) -> str: + dialect = getattr(self, "dialect", None) + if dialect and type(dialect).__name__ == "Spanner": + return _spanner_properties_sql(self, expression) + if _original_bq_properties_transform is not None: + return _original_bq_properties_transform(self, expression) + return self.properties_sql(expression) + + BigQueryGenerator.TRANSFORMS[exp.Property] = _bq_property_transform + BigQueryGenerator.TRANSFORMS[exp.Properties] = _bq_properties_transform + + # Also need to patch locate_properties via setattr since it's not a TRANSFORMS entry + def _patched_bq_locate_properties(self: Any, properties: exp.Properties) -> Any: + dialect = getattr(self, "dialect", None) + if dialect and type(dialect).__name__ == "Spanner": + return _spanner_locate_properties(self, properties) + return _original_bq_locate_properties(self, properties) + + setattr(BigQueryGenerator, "locate_properties", _patched_bq_locate_properties) + + SpannerGenerator = BigQueryGenerator +else: + # Pure-Python sqlglot: real subclass + + class SpannerGenerator(BigQueryGenerator): # type: ignore[no-redef] + """Generator for Google Cloud Spanner (GoogleSQL variant).""" + def property_sql(self, expression: exp.Property) -> str: + return _spanner_property_sql(self, expression) -def _patched_bq_locate_properties(self: Any, properties: exp.Properties) -> Any: - dialect = getattr(self, "dialect", None) - if dialect and type(dialect).__name__ == "Spanner": - return _spanner_locate_properties(self, properties) - return _original_bq_locate_properties(self, properties) + def locate_properties(self, properties: exp.Properties) -> Any: + return _spanner_locate_properties(self, properties) + def properties_sql(self, expression: exp.Properties) -> str: + return _spanner_properties_sql(self, expression) -def _patched_bq_properties_sql(self: Any, expression: exp.Properties) -> str: - dialect = getattr(self, "dialect", None) - if dialect and type(dialect).__name__ == "Spanner": - return _spanner_properties_sql(self, expression) - return _original_bq_properties_sql(self, expression) +if _pg_compiled: + # sqlglot[c]: use TRANSFORMS entry with dialect name check -def _patched_bq_property_sql(self: Any, expression: exp.Property) -> str: - dialect = getattr(self, "dialect", None) - if dialect and type(dialect).__name__ == "Spanner": - return _spanner_property_sql(self, expression) - return _original_bq_property_sql(self, expression) + _original_pg_property_transform = PostgresGenerator.TRANSFORMS.get(exp.Property) + def _pg_property_transform(self: Any, expression: exp.Property) -> str: + dialect = getattr(self, "dialect", None) + if dialect and type(dialect).__name__ == "Spangres": + return _spangres_property_sql(self, expression) + if _original_pg_property_transform is not None: + return _original_pg_property_transform(self, expression) + return self.property_sql(expression) -def _patched_pg_property_sql(self: Any, expression: exp.Property) -> str: - dialect = getattr(self, "dialect", None) - if dialect and type(dialect).__name__ == "Spangres": - return _spangres_property_sql(self, expression) - return _original_pg_property_sql(self, expression) + PostgresGenerator.TRANSFORMS[exp.Property] = _pg_property_transform + SpangresGenerator = PostgresGenerator +else: + # Pure-Python sqlglot: real subclass -setattr(BigQueryGenerator, "locate_properties", _patched_bq_locate_properties) -setattr(BigQueryGenerator, "properties_sql", _patched_bq_properties_sql) -setattr(BigQueryGenerator, "property_sql", _patched_bq_property_sql) -setattr(PostgresGenerator, "property_sql", _patched_pg_property_sql) + class SpangresGenerator(PostgresGenerator): # type: ignore[no-redef] + """Generator for Spanner PostgreSQL-interface (Spangres).""" -SpannerGenerator = BigQueryGenerator -SpangresGenerator = PostgresGenerator + def property_sql(self, expression: exp.Property) -> str: + return _spangres_property_sql(self, expression) diff --git a/sqlspec/driver/_async.py b/sqlspec/driver/_async.py index 37907e24a..38efbe0e1 100644 --- a/sqlspec/driver/_async.py +++ b/sqlspec/driver/_async.py @@ -174,6 +174,12 @@ def data_dictionary(self) -> "AsyncDataDictionaryBase": # CORE DISPATCH METHODS - The Execution Engine # ───────────────────────────────────────────────────────────────────────────── + @staticmethod + def _check_pending_exception(exc_handler: AsyncExceptionHandler) -> None: + """Raise any pending mapped exception after context manager exit.""" + if exc_handler.pending_exception is not None: + raise exc_handler.pending_exception from None + @staticmethod def _raise_async_database_exception(exc_handler: AsyncExceptionHandler, exc: Exception | None) -> None: """Raise any mapped database exception captured by the async handler.""" @@ -425,9 +431,7 @@ async def _stmt_cache_execute_direct( ) result = DMLResult(cached.operation_type, affected_rows) - pending_exception = exc_handler.pending_exception - if pending_exception is not None: - raise pending_exception from None + self._check_pending_exception(exc_handler) assert result is not None return result finally: @@ -461,9 +465,7 @@ async def _stmt_cache_execute(self, statement: "SQL") -> "SQLResult": execution_result = await self.dispatch_execute(cursor, statement) result = self.build_statement_result(statement, execution_result) - pending_exception = exc_handler.pending_exception - if pending_exception is not None: - raise pending_exception from None + self._check_pending_exception(exc_handler) assert result is not None return result finally: @@ -536,9 +538,7 @@ async def execute( statement, parameters, statement_config=statement_config or self.statement_config, kwargs=kwargs ) result = await self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) - pending_exception = exc_handler.pending_exception - if pending_exception is not None: - raise pending_exception from None + self._check_pending_exception(exc_handler) assert result is not None return result @@ -571,9 +571,7 @@ async def execute_many( sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) result = await self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) - pending_exception = exc_handler.pending_exception - if pending_exception is not None: - raise pending_exception from None + self._check_pending_exception(exc_handler) assert result is not None return result @@ -598,9 +596,7 @@ async def execute_script( result = await self.dispatch_statement_execution( statement=sql_statement.as_script(), connection=self.connection ) - pending_exception = exc_handler.pending_exception - if pending_exception is not None: - raise pending_exception from None + self._check_pending_exception(exc_handler) assert result is not None return result diff --git a/sqlspec/driver/_exception_handler.py b/sqlspec/driver/_exception_handler.py index d4ba9ee0d..4ce9b8ab7 100644 --- a/sqlspec/driver/_exception_handler.py +++ b/sqlspec/driver/_exception_handler.py @@ -1,4 +1,31 @@ -"""Shared exception handler bases for driver adapters.""" +"""Shared exception handler bases for driver adapters. + +Deferred Exception Pattern +-------------------------- +mypyc-compiled ``__aexit__``/``__exit__`` methods cannot propagate new exceptions +raised inside the handler back through the ABI boundary reliably. To work around +this, the handler **stores** the mapped exception in ``pending_exception`` and +returns ``True`` (suppressing the original). After the ``async with`` / ``with`` +block exits normally, the calling dispatch method checks ``pending_exception`` +and re-raises it explicitly in pure-Python control flow. + +How to use in new dispatch methods +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1. Obtain an exc_handler via ``self.handle_database_exceptions()``. +2. Wrap the database call with ``async with exc_handler:`` (or ``with``). +3. **After** the context manager exits, call + ``self._check_pending_exception(exc_handler)`` to raise any mapped error. + For dispatch methods that also record observability spans, use + ``self._raise_async_database_exception`` / ``self._raise_sync_database_exception`` + which additionally re-chains the original exception. + +Class hierarchy +^^^^^^^^^^^^^^^ +``BaseAsyncExceptionHandler`` / ``BaseSyncExceptionHandler`` (this module) + -> adapter-specific subclasses in ``sqlspec/adapters/{adapter}/driver.py`` + -> consumed by ``_check_pending_exception`` and ``_raise_*_database_exception`` + helpers on ``AsyncDriverAdapterBase`` / ``SyncDriverAdapterBase``. +""" from typing import TYPE_CHECKING diff --git a/sqlspec/driver/_sync.py b/sqlspec/driver/_sync.py index 90dca82e7..1d6c4a58d 100644 --- a/sqlspec/driver/_sync.py +++ b/sqlspec/driver/_sync.py @@ -155,6 +155,12 @@ def data_dictionary(self) -> "SyncDataDictionaryBase": # CORE DISPATCH METHODS - The Execution Engine # ───────────────────────────────────────────────────────────────────────────── + @staticmethod + def _check_pending_exception(exc_handler: SyncExceptionHandler) -> None: + """Raise any pending mapped exception after context manager exit.""" + if exc_handler.pending_exception is not None: + raise exc_handler.pending_exception from None + @staticmethod def _raise_sync_database_exception(exc_handler: SyncExceptionHandler, exc: Exception | None) -> None: """Raise any mapped database exception captured by the sync handler.""" @@ -204,9 +210,7 @@ def dispatch_statement_execution(self, statement: "SQL", connection: "Any") -> " else: execution_result = self.dispatch_execute(cursor, statement) result = self.build_statement_result(statement, execution_result) - pending_exception = exc_handler.pending_exception - if pending_exception is not None: - raise pending_exception from None + self._check_pending_exception(exc_handler) assert result is not None return result @@ -459,9 +463,7 @@ def _stmt_cache_execute_direct( ) result = DMLResult(cached.operation_type, affected_rows) - pending_exception = exc_handler.pending_exception - if pending_exception is not None: - raise pending_exception from None + self._check_pending_exception(exc_handler) assert result is not None return result finally: @@ -480,9 +482,7 @@ def _stmt_cache_execute(self, statement: "SQL") -> "SQLResult": with exc_handler, self.with_cursor(self.connection) as cursor: execution_result = self.dispatch_execute(cursor, statement) result = self.build_statement_result(statement, execution_result) - pending_exception = exc_handler.pending_exception - if pending_exception is not None: - raise pending_exception from None + self._check_pending_exception(exc_handler) assert result is not None return result finally: @@ -555,9 +555,7 @@ def execute( statement, parameters, statement_config=statement_config or self.statement_config, kwargs=kwargs ) result = self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) - pending_exception = exc_handler.pending_exception - if pending_exception is not None: - raise pending_exception from None + self._check_pending_exception(exc_handler) assert result is not None return result @@ -590,9 +588,7 @@ def execute_many( sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs) result = self.dispatch_statement_execution(statement=sql_statement, connection=self.connection) - pending_exception = exc_handler.pending_exception - if pending_exception is not None: - raise pending_exception from None + self._check_pending_exception(exc_handler) assert result is not None return result @@ -615,9 +611,7 @@ def execute_script( config = statement_config or self.statement_config sql_statement = self.prepare_statement(statement, parameters, statement_config=config, kwargs=kwargs) result = self.dispatch_statement_execution(statement=sql_statement.as_script(), connection=self.connection) - pending_exception = exc_handler.pending_exception - if pending_exception is not None: - raise pending_exception from None + self._check_pending_exception(exc_handler) assert result is not None return result diff --git a/sqlspec/extensions/fastapi/providers.py b/sqlspec/extensions/fastapi/providers.py index 087a2b5d4..dfe8a37a6 100644 --- a/sqlspec/extensions/fastapi/providers.py +++ b/sqlspec/extensions/fastapi/providers.py @@ -106,9 +106,9 @@ class FilterConfig(TypedDict): """When True, enables created_at date range filtering. Uses 'created_at' field by default.""" updated_at: NotRequired[bool] """When True, enables updated_at date range filtering. Uses 'updated_at' field by default.""" - not_in_fields: NotRequired[FieldNameType | set[FieldNameType]] + not_in_fields: NotRequired[FieldNameType | set[FieldNameType] | list[str | FieldNameType]] """Fields that support not-in collection filtering. Can be single field or set of fields with type info.""" - in_fields: NotRequired[FieldNameType | set[FieldNameType]] + in_fields: NotRequired[FieldNameType | set[FieldNameType] | list[str | FieldNameType]] """Fields that support in-collection filtering. Can be single field or set of fields with type info.""" null_fields: NotRequired[str | set[str]] """Fields that support IS NULL filtering. Can be single field name or set of field names.""" @@ -466,6 +466,7 @@ def provide_order_by( if not_in_fields := config.get("not_in_fields"): not_in_fields = {not_in_fields} if isinstance(not_in_fields, (str, FieldNameType)) else not_in_fields for field_def in not_in_fields: + field_def = FieldNameType(name=field_def, type_hint=str) if isinstance(field_def, str) else field_def def create_not_in_filter_provider( field_name: FieldNameType = field_def, @@ -497,6 +498,7 @@ def provide_not_in_filter( if in_fields := config.get("in_fields"): in_fields = {in_fields} if isinstance(in_fields, (str, FieldNameType)) else in_fields for field_def in in_fields: + field_def = FieldNameType(name=field_def, type_hint=str) if isinstance(field_def, str) else field_def def create_in_filter_provider( field_name: FieldNameType = field_def, diff --git a/tests/integration/adapters/asyncpg/test_cloud_connectors.py b/tests/integration/adapters/asyncpg/test_cloud_connectors.py index 3badc95b8..651b6c4e5 100644 --- a/tests/integration/adapters/asyncpg/test_cloud_connectors.py +++ b/tests/integration/adapters/asyncpg/test_cloud_connectors.py @@ -8,7 +8,7 @@ import pytest -from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.adapters.asyncpg import AsyncpgConfig, AsyncpgPoolConfig from sqlspec.typing import ALLOYDB_CONNECTOR_INSTALLED, CLOUD_SQL_CONNECTOR_INSTALLED HAS_CLOUD_SQL_CREDENTIALS = ( @@ -40,7 +40,7 @@ async def test_cloud_sql_connection_basic() -> None: password = os.environ.get("GOOGLE_CLOUD_SQL_PASSWORD") config = AsyncpgConfig( - connection_config={"user": user, "password": password, "database": database, "min_size": 1, "max_size": 2}, + connection_config=AsyncpgPoolConfig(user=user, password=password, database=database, min_size=1, max_size=2), driver_features={"enable_cloud_sql": True, "cloud_sql_instance": instance, "cloud_sql_enable_iam_auth": False}, ) @@ -62,7 +62,7 @@ async def test_cloud_sql_query_execution() -> None: password = os.environ.get("GOOGLE_CLOUD_SQL_PASSWORD") config = AsyncpgConfig( - connection_config={"user": user, "password": password, "database": database, "min_size": 1, "max_size": 2}, + connection_config=AsyncpgPoolConfig(user=user, password=password, database=database, min_size=1, max_size=2), driver_features={"enable_cloud_sql": True, "cloud_sql_instance": instance, "cloud_sql_enable_iam_auth": False}, ) @@ -84,7 +84,7 @@ async def test_cloud_sql_iam_auth() -> None: database = os.environ.get("GOOGLE_CLOUD_SQL_DATABASE", "postgres") config = AsyncpgConfig( - connection_config={"user": user, "database": database, "min_size": 1, "max_size": 2}, + connection_config=AsyncpgPoolConfig(user=user, database=database, min_size=1, max_size=2), driver_features={"enable_cloud_sql": True, "cloud_sql_instance": instance, "cloud_sql_enable_iam_auth": True}, ) @@ -106,7 +106,7 @@ async def test_cloud_sql_private_ip() -> None: password = os.environ.get("GOOGLE_CLOUD_SQL_PASSWORD") config = AsyncpgConfig( - connection_config={"user": user, "password": password, "database": database, "min_size": 1, "max_size": 2}, + connection_config=AsyncpgPoolConfig(user=user, password=password, database=database, min_size=1, max_size=2), driver_features={ "enable_cloud_sql": True, "cloud_sql_instance": instance, @@ -133,7 +133,7 @@ async def test_alloydb_connection_basic() -> None: password = os.environ.get("GOOGLE_ALLOYDB_PASSWORD") config = AsyncpgConfig( - connection_config={"user": user, "password": password, "database": database, "min_size": 1, "max_size": 2}, + connection_config=AsyncpgPoolConfig(user=user, password=password, database=database, min_size=1, max_size=2), driver_features={ "enable_alloydb": True, "alloydb_instance_uri": instance_uri, @@ -159,7 +159,7 @@ async def test_alloydb_query_execution() -> None: password = os.environ.get("GOOGLE_ALLOYDB_PASSWORD") config = AsyncpgConfig( - connection_config={"user": user, "password": password, "database": database, "min_size": 1, "max_size": 2}, + connection_config=AsyncpgPoolConfig(user=user, password=password, database=database, min_size=1, max_size=2), driver_features={ "enable_alloydb": True, "alloydb_instance_uri": instance_uri, @@ -185,7 +185,7 @@ async def test_alloydb_iam_auth() -> None: database = os.environ.get("GOOGLE_ALLOYDB_DATABASE", "postgres") config = AsyncpgConfig( - connection_config={"user": user, "database": database, "min_size": 1, "max_size": 2}, + connection_config=AsyncpgPoolConfig(user=user, database=database, min_size=1, max_size=2), driver_features={"enable_alloydb": True, "alloydb_instance_uri": instance_uri, "enable_alloydb_iam_auth": True}, ) diff --git a/tests/integration/adapters/asyncpg/test_paradedb.py b/tests/integration/adapters/asyncpg/test_paradedb.py index 52d51fcd8..6468440c6 100644 --- a/tests/integration/adapters/asyncpg/test_paradedb.py +++ b/tests/integration/adapters/asyncpg/test_paradedb.py @@ -1,34 +1,33 @@ """Integration tests for asyncpg driver with ParadeDB (pgvector + pg_search).""" from collections.abc import AsyncGenerator -from typing import Any import pytest from pytest_databases.docker.postgres import PostgresService -from sqlspec.adapters.asyncpg import AsyncpgConfig, AsyncpgDriver +from sqlspec.adapters.asyncpg import AsyncpgConfig, AsyncpgDriver, AsyncpgPoolConfig pytestmark = pytest.mark.xdist_group("paradedb") @pytest.fixture(scope="session") -def paradedb_asyncpg_connection_config(paradedb_service: "PostgresService") -> "dict[str, Any]": +def paradedb_asyncpg_connection_config(paradedb_service: "PostgresService") -> "AsyncpgPoolConfig": """Base pool configuration for AsyncPG tests with ParadeDB.""" - return { - "host": paradedb_service.host, - "port": paradedb_service.port, - "user": paradedb_service.user, - "password": paradedb_service.password, - "database": paradedb_service.database, - } + return AsyncpgPoolConfig( + host=paradedb_service.host, + port=paradedb_service.port, + user=paradedb_service.user, + password=paradedb_service.password, + database=paradedb_service.database, + ) @pytest.fixture(scope="function") async def paradedb_asyncpg_config( - paradedb_asyncpg_connection_config: "dict[str, Any]", + paradedb_asyncpg_connection_config: "AsyncpgPoolConfig", ) -> "AsyncGenerator[AsyncpgConfig, None]": """Provide an AsyncpgConfig instance connected to ParadeDB.""" - config = AsyncpgConfig(connection_config=dict(paradedb_asyncpg_connection_config)) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(**paradedb_asyncpg_connection_config)) try: yield config finally: diff --git a/tests/integration/adapters/asyncpg/test_pgvector.py b/tests/integration/adapters/asyncpg/test_pgvector.py index 39712f825..b48006b4f 100644 --- a/tests/integration/adapters/asyncpg/test_pgvector.py +++ b/tests/integration/adapters/asyncpg/test_pgvector.py @@ -1,33 +1,32 @@ """Integration tests for asyncpg driver with pgvector extension.""" from collections.abc import AsyncGenerator -from typing import Any import pytest from pytest_databases.docker.postgres import PostgresService from sqlspec import sql -from sqlspec.adapters.asyncpg import AsyncpgConfig, AsyncpgDriver +from sqlspec.adapters.asyncpg import AsyncpgConfig, AsyncpgDriver, AsyncpgPoolConfig from sqlspec.builder import Column pytestmark = pytest.mark.xdist_group("pgvector") @pytest.fixture(scope="session") -def pgvector_asyncpg_connection_config(pgvector_service: "PostgresService") -> "dict[str, Any]": +def pgvector_asyncpg_connection_config(pgvector_service: "PostgresService") -> "AsyncpgPoolConfig": """Base pool configuration for AsyncPG tests with pgvector.""" - return { - "host": pgvector_service.host, - "port": pgvector_service.port, - "user": pgvector_service.user, - "password": pgvector_service.password, - "database": pgvector_service.database, - } + return AsyncpgPoolConfig( + host=pgvector_service.host, + port=pgvector_service.port, + user=pgvector_service.user, + password=pgvector_service.password, + database=pgvector_service.database, + ) @pytest.fixture(scope="function") async def pgvector_asyncpg_config( - pgvector_asyncpg_connection_config: "dict[str, Any]", + pgvector_asyncpg_connection_config: "AsyncpgPoolConfig", ) -> "AsyncGenerator[AsyncpgConfig, None]": """Provide an AsyncpgConfig instance connected to pgvector postgres.""" # Enable the pgvector extension before creating the pool @@ -39,7 +38,7 @@ async def pgvector_asyncpg_config( finally: await conn.close() - config = AsyncpgConfig(connection_config=dict(pgvector_asyncpg_connection_config)) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(**pgvector_asyncpg_connection_config)) try: yield config finally: diff --git a/tests/integration/adapters/duckdb/test_connection.py b/tests/integration/adapters/duckdb/test_connection.py index 682911596..58114b69a 100644 --- a/tests/integration/adapters/duckdb/test_connection.py +++ b/tests/integration/adapters/duckdb/test_connection.py @@ -11,7 +11,8 @@ import pytest from sqlspec import ObservabilityConfig, SQLSpec -from sqlspec.adapters.duckdb import DuckDBConfig, DuckDBConnection +from sqlspec.adapters.duckdb import DuckDBConfig, DuckDBConnection, DuckDBConnectionParams +from sqlspec.adapters.duckdb.config import DuckDBPoolParams from sqlspec.adapters.duckdb.core import build_connection_config from sqlspec.config import LifecycleConfig from sqlspec.core import SQLResult @@ -22,7 +23,7 @@ def create_permissive_config(**kwargs: Any) -> DuckDBConfig: """Create a DuckDB config with permissive SQL settings.""" - connection_config = kwargs.pop("connection_config", {}) + connection_config: dict[str, Any] = dict(kwargs.pop("connection_config", {})) for param in [ "database", @@ -44,7 +45,7 @@ def create_permissive_config(**kwargs: Any) -> DuckDBConfig: # Use a unique memory database identifier to avoid configuration conflicts connection_config["database"] = f":memory:{uuid4().hex}" - kwargs["connection_config"] = connection_config + kwargs["connection_config"] = DuckDBConnectionParams(**connection_config) return DuckDBConfig(**kwargs) @@ -120,7 +121,7 @@ def test_connection_with_data_processing_settings() -> None: def test_connection_with_instrumentation() -> None: """Test DuckDB connection with instrumentation configuration.""" - config = DuckDBConfig(connection_config={"database": ":memory:"}) + config = DuckDBConfig(connection_config=DuckDBConnectionParams(database=":memory:")) with config.provide_session() as session: result = session.execute("SELECT ? as test_value", (42)) @@ -138,7 +139,8 @@ def connection_hook(connection: DuckDBConnection) -> None: connection.execute("SET threads = 1") config = DuckDBConfig( - connection_config={"database": ":memory:"}, driver_features={"on_connection_create": connection_hook} + connection_config=DuckDBConnectionParams(database=":memory:"), + driver_features={"on_connection_create": connection_hook}, ) registry = SQLSpec() @@ -162,7 +164,7 @@ def connection_hook(connection: DuckDBConnection) -> None: hook_call_count += 1 config = DuckDBConfig( - connection_config={"database": f":memory:{uuid4().hex}"}, + connection_config=DuckDBConnectionParams(database=f":memory:{uuid4().hex}"), driver_features={"on_connection_create": connection_hook}, ) @@ -305,7 +307,7 @@ def test_config_with_connection_config_parameter(tmp_path: Path) -> None: """Test that DuckDBConfig correctly accepts connection_config parameter.""" db_path = tmp_path / "test.duckdb" - connection_config = {"database": str(db_path), "memory_limit": "256MB", "threads": 4} + connection_config = DuckDBConnectionParams(database=str(db_path), memory_limit="256MB", threads=4) config = DuckDBConfig(connection_config=connection_config) @@ -330,7 +332,7 @@ def test_config_with_connection_config_parameter(tmp_path: Path) -> None: def test_config_memory_database_shared_conversion() -> None: """Test that :memory: databases are converted to shared memory.""" - config = DuckDBConfig(connection_config={"database": ":memory:"}) + config = DuckDBConfig(connection_config=DuckDBConnectionParams(database=":memory:")) try: assert config.connection_config["database"] == ":memory:shared_db" @@ -347,7 +349,7 @@ def test_config_memory_database_shared_conversion() -> None: def test_config_empty_database_conversion() -> None: """Test that empty database string is converted to shared memory.""" - config = DuckDBConfig(connection_config={"database": ""}) + config = DuckDBConfig(connection_config=DuckDBConnectionParams(database="")) try: assert config.connection_config["database"] == ":memory:shared_db" @@ -382,13 +384,9 @@ def test_config_consistency_with_other_adapters(tmp_path: Path) -> None: """Test that DuckDB config behaves consistently with SQLite/aiosqlite.""" db_path = tmp_path / "consistency_test.duckdb" - connection_config = { - "database": str(db_path), - "memory_limit": "512MB", - "threads": 2, - "pool_min_size": 1, - "pool_max_size": 4, - } + connection_config = DuckDBPoolParams( + database=str(db_path), memory_limit="512MB", threads=2, pool_min_size=1, pool_max_size=4 + ) config = DuckDBConfig(connection_config=connection_config) diff --git a/tests/integration/adapters/oracledb/conftest.py b/tests/integration/adapters/oracledb/conftest.py index 4af8584b5..53d9d0766 100644 --- a/tests/integration/adapters/oracledb/conftest.py +++ b/tests/integration/adapters/oracledb/conftest.py @@ -1,38 +1,45 @@ """OracleDB test fixtures and configuration.""" from collections.abc import AsyncGenerator, Generator -from typing import Any import pytest from pytest_databases.docker.oracle import OracleService -from sqlspec.adapters.oracledb import OracleAsyncConfig, OracleAsyncDriver, OracleSyncConfig, OracleSyncDriver +from sqlspec.adapters.oracledb import ( + OracleAsyncConfig, + OracleAsyncDriver, + OraclePoolParams, + OracleSyncConfig, + OracleSyncDriver, +) @pytest.fixture(scope="session") -def oracle_connection_config(oracle_23ai_service: "OracleService") -> "dict[str, Any]": +def oracle_connection_config(oracle_23ai_service: "OracleService") -> "OraclePoolParams": """Shared Oracle pool configuration.""" - return { - "host": oracle_23ai_service.host, - "port": oracle_23ai_service.port, - "service_name": oracle_23ai_service.service_name, - "user": oracle_23ai_service.user, - "password": oracle_23ai_service.password, - } + return OraclePoolParams( + host=oracle_23ai_service.host, + port=oracle_23ai_service.port, + service_name=oracle_23ai_service.service_name, + user=oracle_23ai_service.user, + password=oracle_23ai_service.password, + ) @pytest.fixture(scope="session") -def oracle_sync_config(oracle_connection_config: "dict[str, Any]") -> "OracleSyncConfig": +def oracle_sync_config(oracle_connection_config: "OraclePoolParams") -> "OracleSyncConfig": """Create Oracle sync configuration.""" - return OracleSyncConfig(connection_config=dict(oracle_connection_config)) + return OracleSyncConfig(connection_config=OraclePoolParams(**oracle_connection_config)) @pytest.fixture(scope="function") -async def oracle_async_config(oracle_connection_config: "dict[str, Any]") -> "AsyncGenerator[OracleAsyncConfig, None]": +async def oracle_async_config( + oracle_connection_config: "OraclePoolParams", +) -> "AsyncGenerator[OracleAsyncConfig, None]": """Create Oracle async configuration.""" - connection_config = dict(oracle_connection_config) + connection_config = OraclePoolParams(**oracle_connection_config) connection_config.setdefault("min", 1) connection_config.setdefault("max", 5) config = OracleAsyncConfig(connection_config=connection_config) diff --git a/tests/integration/adapters/oracledb/test_driver_async.py b/tests/integration/adapters/oracledb/test_driver_async.py index 86c28ac14..5d51fae16 100644 --- a/tests/integration/adapters/oracledb/test_driver_async.py +++ b/tests/integration/adapters/oracledb/test_driver_async.py @@ -7,7 +7,7 @@ from pytest_databases.docker.oracle import OracleService from sqlspec import sql -from sqlspec.adapters.oracledb import OracleAsyncConfig, OracleAsyncDriver +from sqlspec.adapters.oracledb import OracleAsyncConfig, OracleAsyncDriver, OraclePoolParams from sqlspec.core import SQLResult from sqlspec.exceptions import SQLSpecError @@ -18,13 +18,13 @@ async def test_async_connection(oracle_23ai_service: "OracleService") -> None: """Test async connection components for OracleDB.""" - base_config: dict[str, object] = { - "host": oracle_23ai_service.host, - "port": oracle_23ai_service.port, - "service_name": oracle_23ai_service.service_name, - "user": oracle_23ai_service.user, - "password": oracle_23ai_service.password, - } + base_config = OraclePoolParams( + host=oracle_23ai_service.host, + port=oracle_23ai_service.port, + service_name=oracle_23ai_service.service_name, + user=oracle_23ai_service.user, + password=oracle_23ai_service.password, + ) async_config = OracleAsyncConfig(connection_config=base_config) pool = await async_config.create_pool() assert pool is not None @@ -38,9 +38,7 @@ async def test_async_connection(oracle_23ai_service: "OracleService") -> None: finally: await pool.close() - pooled_config = dict(base_config) - pooled_config["min"] = 1 - pooled_config["max"] = 5 + pooled_config = OraclePoolParams(**base_config, min=1, max=5) another_config = OracleAsyncConfig(connection_config=pooled_config) pool = await another_config.create_pool() assert pool is not None @@ -511,7 +509,7 @@ class Product(msgspec.Struct): async def test_async_uppercase_columns_when_disabled(oracle_async_config: OracleAsyncConfig) -> None: """Ensure disabling lowercase feature preserves uppercase columns.""" custom_config = OracleAsyncConfig( - connection_config=dict(oracle_async_config.connection_config), + connection_config=OraclePoolParams(**oracle_async_config.connection_config), driver_features={"enable_lowercase_column_names": False}, ) @@ -554,15 +552,15 @@ async def connection_hook(conn: Any, tag: str) -> None: hook_call_count += 1 config = OracleAsyncConfig( - connection_config={ - "host": oracle_23ai_service.host, - "port": oracle_23ai_service.port, - "service_name": oracle_23ai_service.service_name, - "user": oracle_23ai_service.user, - "password": oracle_23ai_service.password, - "min": 1, - "max": 2, - }, + connection_config=OraclePoolParams( + host=oracle_23ai_service.host, + port=oracle_23ai_service.port, + service_name=oracle_23ai_service.service_name, + user=oracle_23ai_service.user, + password=oracle_23ai_service.password, + min=1, + max=2, + ), driver_features={"on_connection_create": connection_hook}, ) diff --git a/tests/integration/adapters/oracledb/test_driver_sync.py b/tests/integration/adapters/oracledb/test_driver_sync.py index 702050c56..ea8115e69 100644 --- a/tests/integration/adapters/oracledb/test_driver_sync.py +++ b/tests/integration/adapters/oracledb/test_driver_sync.py @@ -7,7 +7,7 @@ from pytest_databases.docker.oracle import OracleService from sqlspec import sql -from sqlspec.adapters.oracledb import OracleSyncConfig, OracleSyncDriver +from sqlspec.adapters.oracledb import OraclePoolParams, OracleSyncConfig, OracleSyncDriver from sqlspec.core import SQLResult from sqlspec.exceptions import SQLSpecError @@ -18,13 +18,13 @@ def test_sync_connection(oracle_23ai_service: "OracleService") -> None: """Test sync connection components for OracleDB.""" - base_config: dict[str, object] = { - "host": oracle_23ai_service.host, - "port": oracle_23ai_service.port, - "service_name": oracle_23ai_service.service_name, - "user": oracle_23ai_service.user, - "password": oracle_23ai_service.password, - } + base_config = OraclePoolParams( + host=oracle_23ai_service.host, + port=oracle_23ai_service.port, + service_name=oracle_23ai_service.service_name, + user=oracle_23ai_service.user, + password=oracle_23ai_service.password, + ) sync_config = OracleSyncConfig(connection_config=base_config) pool = sync_config.create_pool() assert pool is not None @@ -38,9 +38,7 @@ def test_sync_connection(oracle_23ai_service: "OracleService") -> None: finally: pool.close() - pooled_config = dict(base_config) - pooled_config["min"] = 1 - pooled_config["max"] = 5 + pooled_config = OraclePoolParams(**base_config, min=1, max=5) another_config = OracleSyncConfig(connection_config=pooled_config) pool = another_config.create_pool() assert pool is not None @@ -509,7 +507,7 @@ class Product(msgspec.Struct): def test_sync_uppercase_columns_when_disabled(oracle_sync_config: OracleSyncConfig) -> None: """Ensure disabling lowercase feature preserves uppercase columns.""" custom_config = OracleSyncConfig( - connection_config=dict(oracle_sync_config.connection_config), + connection_config=OraclePoolParams(**oracle_sync_config.connection_config), driver_features={"enable_lowercase_column_names": False}, ) @@ -552,13 +550,13 @@ def connection_hook(conn: Any, tag: str) -> None: hook_call_count += 1 config = OracleSyncConfig( - connection_config={ - "host": oracle_23ai_service.host, - "port": oracle_23ai_service.port, - "service_name": oracle_23ai_service.service_name, - "user": oracle_23ai_service.user, - "password": oracle_23ai_service.password, - }, + connection_config=OraclePoolParams( + host=oracle_23ai_service.host, + port=oracle_23ai_service.port, + service_name=oracle_23ai_service.service_name, + user=oracle_23ai_service.user, + password=oracle_23ai_service.password, + ), driver_features={"on_connection_create": connection_hook}, ) diff --git a/tests/integration/adapters/psqlpy/extensions/events/test_listen_notify.py b/tests/integration/adapters/psqlpy/extensions/events/test_listen_notify.py index 0bdb01f0b..d00d8e576 100644 --- a/tests/integration/adapters/psqlpy/extensions/events/test_listen_notify.py +++ b/tests/integration/adapters/psqlpy/extensions/events/test_listen_notify.py @@ -1,4 +1,3 @@ -# pyright: reportArgumentType=false """PostgreSQL LISTEN/NOTIFY event channel tests for psqlpy adapter.""" import asyncio @@ -7,7 +6,7 @@ import pytest from sqlspec import SQLSpec -from sqlspec.adapters.psqlpy import PsqlpyConfig +from sqlspec.adapters.psqlpy import PsqlpyConfig, PsqlpyPoolParams from sqlspec.migrations.commands import AsyncMigrationCommands pytestmark = pytest.mark.xdist_group("postgres") @@ -21,7 +20,8 @@ async def test_psqlpy_listen_notify_native(postgres_service: "Any") -> None: """Native LISTEN/NOTIFY path delivers payloads.""" config = PsqlpyConfig( - connection_config={"dsn": _dsn(postgres_service)}, extension_config={"events": {"backend": "listen_notify"}} + connection_config=PsqlpyPoolParams(dsn=_dsn(postgres_service)), + extension_config={"events": {"backend": "listen_notify"}}, ) spec = SQLSpec() @@ -62,7 +62,7 @@ async def test_psqlpy_listen_notify_hybrid(postgres_service: "Any", tmp_path) -> migrations.mkdir() config = PsqlpyConfig( - connection_config={"dsn": _dsn(postgres_service)}, + connection_config=PsqlpyPoolParams(dsn=_dsn(postgres_service)), migration_config={"script_location": str(migrations), "include_extensions": ["events"]}, extension_config={"events": {"backend": "listen_notify_durable"}}, ) diff --git a/tests/integration/adapters/psqlpy/test_paradedb.py b/tests/integration/adapters/psqlpy/test_paradedb.py index 73ae6c1e2..41bbfa049 100644 --- a/tests/integration/adapters/psqlpy/test_paradedb.py +++ b/tests/integration/adapters/psqlpy/test_paradedb.py @@ -1,31 +1,30 @@ """Integration tests for psqlpy driver with ParadeDB (pgvector + pg_search).""" from collections.abc import AsyncGenerator -from typing import Any import pytest from psqlpy.extra_types import PgVector from pytest_databases.docker.postgres import PostgresService -from sqlspec.adapters.psqlpy import PsqlpyConfig, PsqlpyDriver +from sqlspec.adapters.psqlpy import PsqlpyConfig, PsqlpyDriver, PsqlpyPoolParams pytestmark = pytest.mark.xdist_group("paradedb") @pytest.fixture(scope="session") -def paradedb_psqlpy_connection_config(paradedb_service: "PostgresService") -> "dict[str, Any]": +def paradedb_psqlpy_connection_config(paradedb_service: "PostgresService") -> "PsqlpyPoolParams": """Base pool configuration for Psqlpy tests with ParadeDB.""" - return { - "dsn": f"postgres://{paradedb_service.user}:{paradedb_service.password}@{paradedb_service.host}:{paradedb_service.port}/{paradedb_service.database}" - } + return PsqlpyPoolParams( + dsn=f"postgres://{paradedb_service.user}:{paradedb_service.password}@{paradedb_service.host}:{paradedb_service.port}/{paradedb_service.database}" + ) @pytest.fixture(scope="function") async def paradedb_psqlpy_config( - paradedb_psqlpy_connection_config: "dict[str, Any]", + paradedb_psqlpy_connection_config: "PsqlpyPoolParams", ) -> "AsyncGenerator[PsqlpyConfig, None]": """Provide a PsqlpyConfig instance connected to ParadeDB.""" - config = PsqlpyConfig(connection_config=dict(paradedb_psqlpy_connection_config)) + config = PsqlpyConfig(connection_config=PsqlpyPoolParams(**paradedb_psqlpy_connection_config)) try: yield config finally: diff --git a/tests/integration/adapters/psqlpy/test_pgvector.py b/tests/integration/adapters/psqlpy/test_pgvector.py index b1e0d33b8..d22ca2f23 100644 --- a/tests/integration/adapters/psqlpy/test_pgvector.py +++ b/tests/integration/adapters/psqlpy/test_pgvector.py @@ -1,7 +1,6 @@ """Integration tests for psqlpy driver with pgvector extension.""" from collections.abc import AsyncGenerator -from typing import Any import pytest from psqlpy import ConnectionPool @@ -9,23 +8,23 @@ from pytest_databases.docker.postgres import PostgresService from sqlspec import sql -from sqlspec.adapters.psqlpy import PsqlpyConfig, PsqlpyDriver +from sqlspec.adapters.psqlpy import PsqlpyConfig, PsqlpyDriver, PsqlpyPoolParams from sqlspec.builder import Column pytestmark = pytest.mark.xdist_group("pgvector") @pytest.fixture(scope="session") -def pgvector_psqlpy_connection_config(pgvector_service: "PostgresService") -> "dict[str, Any]": +def pgvector_psqlpy_connection_config(pgvector_service: "PostgresService") -> "PsqlpyPoolParams": """Base pool configuration for Psqlpy tests with pgvector.""" - return { - "dsn": f"postgres://{pgvector_service.user}:{pgvector_service.password}@{pgvector_service.host}:{pgvector_service.port}/{pgvector_service.database}" - } + return PsqlpyPoolParams( + dsn=f"postgres://{pgvector_service.user}:{pgvector_service.password}@{pgvector_service.host}:{pgvector_service.port}/{pgvector_service.database}" + ) @pytest.fixture(scope="function") async def pgvector_psqlpy_config( - pgvector_psqlpy_connection_config: "dict[str, Any]", + pgvector_psqlpy_connection_config: "PsqlpyPoolParams", ) -> "AsyncGenerator[PsqlpyConfig, None]": """Provide a PsqlpyConfig instance connected to pgvector postgres.""" # Enable the pgvector extension before creating the pool @@ -34,7 +33,7 @@ async def pgvector_psqlpy_config( await conn.execute("CREATE EXTENSION IF NOT EXISTS vector") pool.close() - config = PsqlpyConfig(connection_config=dict(pgvector_psqlpy_connection_config)) + config = PsqlpyConfig(connection_config=PsqlpyPoolParams(**pgvector_psqlpy_connection_config)) try: yield config finally: diff --git a/tests/integration/adapters/psycopg/test_paradedb.py b/tests/integration/adapters/psycopg/test_paradedb.py index 5f33bc052..8bb78f756 100644 --- a/tests/integration/adapters/psycopg/test_paradedb.py +++ b/tests/integration/adapters/psycopg/test_paradedb.py @@ -1,30 +1,31 @@ """Integration tests for psycopg driver with ParadeDB (pgvector + pg_search).""" from collections.abc import Generator -from typing import Any import pytest from pytest_databases.docker.postgres import PostgresService -from sqlspec.adapters.psycopg import PsycopgSyncConfig, PsycopgSyncDriver +from sqlspec.adapters.psycopg import PsycopgPoolParams, PsycopgSyncConfig, PsycopgSyncDriver pytestmark = pytest.mark.xdist_group("paradedb") @pytest.fixture(scope="session") -def paradedb_psycopg_connection_config(paradedb_service: "PostgresService") -> "dict[str, Any]": +def paradedb_psycopg_connection_config(paradedb_service: "PostgresService") -> "PsycopgPoolParams": """Base pool configuration for Psycopg tests with ParadeDB.""" - return { - "conninfo": f"postgresql://{paradedb_service.user}:{paradedb_service.password}@{paradedb_service.host}:{paradedb_service.port}/{paradedb_service.database}" - } + return PsycopgPoolParams( + conninfo=f"postgresql://{paradedb_service.user}:{paradedb_service.password}@{paradedb_service.host}:{paradedb_service.port}/{paradedb_service.database}" + ) @pytest.fixture(scope="session") def paradedb_psycopg_config( - paradedb_psycopg_connection_config: "dict[str, Any]", + paradedb_psycopg_connection_config: "PsycopgPoolParams", ) -> "Generator[PsycopgSyncConfig, None, None]": """Provide a PsycopgSyncConfig instance connected to ParadeDB.""" - config = PsycopgSyncConfig(connection_config=dict(paradedb_psycopg_connection_config), pool_config={"min_size": 1}) + config = PsycopgSyncConfig( + connection_config=PsycopgPoolParams(**paradedb_psycopg_connection_config), pool_config={"min_size": 1} + ) try: yield config finally: diff --git a/tests/integration/adapters/psycopg/test_pgvector.py b/tests/integration/adapters/psycopg/test_pgvector.py index d6d13837a..a062775cb 100644 --- a/tests/integration/adapters/psycopg/test_pgvector.py +++ b/tests/integration/adapters/psycopg/test_pgvector.py @@ -1,29 +1,28 @@ """Integration tests for psycopg driver with pgvector extension.""" from collections.abc import Generator -from typing import Any import pytest from pytest_databases.docker.postgres import PostgresService from sqlspec import sql -from sqlspec.adapters.psycopg import PsycopgSyncConfig, PsycopgSyncDriver +from sqlspec.adapters.psycopg import PsycopgPoolParams, PsycopgSyncConfig, PsycopgSyncDriver from sqlspec.builder import Column pytestmark = pytest.mark.xdist_group("pgvector") @pytest.fixture(scope="session") -def pgvector_psycopg_connection_config(pgvector_service: "PostgresService") -> "dict[str, Any]": +def pgvector_psycopg_connection_config(pgvector_service: "PostgresService") -> "PsycopgPoolParams": """Base pool configuration for Psycopg tests with pgvector.""" - return { - "conninfo": f"postgresql://{pgvector_service.user}:{pgvector_service.password}@{pgvector_service.host}:{pgvector_service.port}/{pgvector_service.database}" - } + return PsycopgPoolParams( + conninfo=f"postgresql://{pgvector_service.user}:{pgvector_service.password}@{pgvector_service.host}:{pgvector_service.port}/{pgvector_service.database}" + ) @pytest.fixture(scope="session") def pgvector_psycopg_config( - pgvector_psycopg_connection_config: "dict[str, Any]", + pgvector_psycopg_connection_config: "PsycopgPoolParams", ) -> "Generator[PsycopgSyncConfig, None, None]": """Provide a PsycopgSyncConfig instance connected to pgvector postgres.""" # Enable the pgvector extension before creating the pool @@ -33,7 +32,9 @@ def pgvector_psycopg_config( conn.execute("CREATE EXTENSION IF NOT EXISTS vector") conn.commit() - config = PsycopgSyncConfig(connection_config=dict(pgvector_psycopg_connection_config), pool_config={"min_size": 1}) + config = PsycopgSyncConfig( + connection_config=PsycopgPoolParams(**pgvector_psycopg_connection_config), pool_config={"min_size": 1} + ) try: yield config finally: diff --git a/tests/integration/adapters/sqlite/extensions/adk/test_owner_id_column.py b/tests/integration/adapters/sqlite/extensions/adk/test_owner_id_column.py index 559f4e231..ef84bea73 100644 --- a/tests/integration/adapters/sqlite/extensions/adk/test_owner_id_column.py +++ b/tests/integration/adapters/sqlite/extensions/adk/test_owner_id_column.py @@ -10,7 +10,7 @@ import pytest -from sqlspec.adapters.sqlite import SqliteConfig +from sqlspec.adapters.sqlite import SqliteConfig, SqliteConnectionParams from sqlspec.adapters.sqlite.adk.store import SqliteADKStore pytestmark = [pytest.mark.xdist_group("sqlite"), pytest.mark.sqlite, pytest.mark.integration] @@ -74,7 +74,7 @@ def sqlite_config() -> SqliteConfig: - Prevent table schema conflicts between different tests - Enable foreign key relationships across connections """ - return SqliteConfig(connection_config={"database": _make_shared_memory_db_name(), "uri": True}) + return SqliteConfig(connection_config=SqliteConnectionParams(database=_make_shared_memory_db_name(), uri=True)) @pytest.fixture diff --git a/tests/integration/adapters/sqlite/extensions/events/test_queue_backend.py b/tests/integration/adapters/sqlite/extensions/events/test_queue_backend.py index 20747effd..da601b5af 100644 --- a/tests/integration/adapters/sqlite/extensions/events/test_queue_backend.py +++ b/tests/integration/adapters/sqlite/extensions/events/test_queue_backend.py @@ -1,9 +1,8 @@ -# pyright: reportArgumentType=false """SQLite integration tests for EventChannel with table queue backend.""" import pytest -from sqlspec.adapters.sqlite import SqliteConfig +from sqlspec.adapters.sqlite import SqliteConfig, SqliteConnectionParams from tests.integration.adapters._events_helpers import prepare_events_migrations, setup_sync_event_channel @@ -15,7 +14,7 @@ def test_sqlite_event_channel_publish_and_consume(tmp_path) -> None: db_path = tmp_path / "events.db" config = SqliteConfig( - connection_config={"database": str(db_path)}, + connection_config=SqliteConnectionParams(database=str(db_path)), migration_config={"script_location": str(migrations_dir), "include_extensions": ["events"]}, ) @@ -38,7 +37,7 @@ def test_sqlite_event_channel_ack_updates_status(tmp_path) -> None: db_path = tmp_path / "events_ack.db" config = SqliteConfig( - connection_config={"database": str(db_path)}, + connection_config=SqliteConnectionParams(database=str(db_path)), migration_config={"script_location": str(migrations_dir), "include_extensions": ["events"]}, ) @@ -65,7 +64,7 @@ def test_sqlite_event_channel_custom_table_name(tmp_path) -> None: db_path = tmp_path / "custom_events.db" config = SqliteConfig( - connection_config={"database": str(db_path)}, + connection_config=SqliteConnectionParams(database=str(db_path)), migration_config={"script_location": str(migrations_dir), "include_extensions": ["events"]}, extension_config={"events": {"queue_table": "app_events"}}, ) @@ -88,7 +87,7 @@ def test_sqlite_event_channel_multiple_channels(tmp_path) -> None: db_path = tmp_path / "multi_channel.db" config = SqliteConfig( - connection_config={"database": str(db_path)}, + connection_config=SqliteConnectionParams(database=str(db_path)), migration_config={"script_location": str(migrations_dir), "include_extensions": ["events"]}, ) @@ -113,7 +112,7 @@ def test_sqlite_event_channel_metadata_preserved(tmp_path) -> None: db_path = tmp_path / "metadata.db" config = SqliteConfig( - connection_config={"database": str(db_path)}, + connection_config=SqliteConnectionParams(database=str(db_path)), migration_config={"script_location": str(migrations_dir), "include_extensions": ["events"]}, ) @@ -138,7 +137,7 @@ def test_sqlite_event_channel_attempts_tracked(tmp_path) -> None: db_path = tmp_path / "attempts.db" config = SqliteConfig( - connection_config={"database": str(db_path)}, + connection_config=SqliteConnectionParams(database=str(db_path)), migration_config={"script_location": str(migrations_dir), "include_extensions": ["events"]}, ) @@ -165,7 +164,7 @@ def test_sqlite_event_channel_telemetry(tmp_path) -> None: db_path = tmp_path / "telemetry.db" config = SqliteConfig( - connection_config={"database": str(db_path)}, + connection_config=SqliteConnectionParams(database=str(db_path)), migration_config={"script_location": str(migrations_dir), "include_extensions": ["events"]}, ) diff --git a/tests/integration/adapters/sqlite/test_pooling.py b/tests/integration/adapters/sqlite/test_pooling.py index 9c79cc010..a3c776db6 100644 --- a/tests/integration/adapters/sqlite/test_pooling.py +++ b/tests/integration/adapters/sqlite/test_pooling.py @@ -5,7 +5,7 @@ import pytest -from sqlspec.adapters.sqlite.config import SqliteConfig +from sqlspec.adapters.sqlite.config import SqliteConfig, SqliteConnectionParams from sqlspec.adapters.sqlite.core import build_connection_config from sqlspec.core import SQLResult @@ -219,7 +219,7 @@ def test_config_with_connection_config_parameter(tmp_path: Path) -> None: """Test that SqliteConfig correctly accepts connection_config parameter.""" db_path = tmp_path / "test.sqlite" - connection_config = {"database": str(db_path), "timeout": 10.0, "check_same_thread": False} + connection_config = SqliteConnectionParams(database=str(db_path), timeout=10.0, check_same_thread=False) config = SqliteConfig(connection_config=connection_config) @@ -244,7 +244,7 @@ def test_config_with_connection_config_parameter(tmp_path: Path) -> None: def test_config_memory_database_conversion() -> None: """Test that :memory: databases are converted to shared memory.""" - config = SqliteConfig(connection_config={"database": ":memory:"}) + config = SqliteConfig(connection_config=SqliteConnectionParams(database=":memory:")) try: db_uri = config.connection_config["database"] diff --git a/tests/integration/config/test_connection_injection.py b/tests/integration/config/test_connection_injection.py index 79214fdc9..e9e212225 100644 --- a/tests/integration/config/test_connection_injection.py +++ b/tests/integration/config/test_connection_injection.py @@ -12,10 +12,10 @@ import pytest -from sqlspec.adapters.aiosqlite.config import AiosqliteConfig -from sqlspec.adapters.asyncpg.config import AsyncpgConfig -from sqlspec.adapters.duckdb.config import DuckDBConfig -from sqlspec.adapters.sqlite.config import SqliteConfig +from sqlspec.adapters.aiosqlite.config import AiosqliteConfig, AiosqlitePoolParams +from sqlspec.adapters.asyncpg.config import AsyncpgConfig, AsyncpgPoolConfig +from sqlspec.adapters.duckdb.config import DuckDBConfig, DuckDBConnectionParams +from sqlspec.adapters.sqlite.config import SqliteConfig, SqliteConnectionParams pytestmark = pytest.mark.xdist_group("config") @@ -30,7 +30,9 @@ async def test_asyncpg_connection_instance_with_pre_created_pool(asyncpg_connect try: # Inject pool into config - config = AsyncpgConfig(connection_config=asyncpg_connection_config, connection_instance=pool) + config = AsyncpgConfig( + connection_config=AsyncpgPoolConfig(**asyncpg_connection_config), connection_instance=pool + ) # Verify pool is used assert config.connection_instance is pool @@ -53,7 +55,9 @@ async def test_asyncpg_connection_instance_bypasses_pool_creation(asyncpg_connec try: # Config with connection_instance should not call _create_pool - config = AsyncpgConfig(connection_config=asyncpg_connection_config, connection_instance=pool) + config = AsyncpgConfig( + connection_config=AsyncpgPoolConfig(**asyncpg_connection_config), connection_instance=pool + ) # Get pool - should return the injected one retrieved_pool = await config.provide_pool() @@ -78,7 +82,7 @@ async def test_aiosqlite_connection_instance_with_pre_created_pool(tmp_path: Pat try: # Inject pool into config - config = AiosqliteConfig(connection_config={"database": str(db_path)}, connection_instance=pool) + config = AiosqliteConfig(connection_config=AiosqlitePoolParams(database=str(db_path)), connection_instance=pool) # Verify pool is used assert config.connection_instance is pool @@ -104,7 +108,7 @@ def test_sqlite_connection_instance_with_pre_created_pool(tmp_path: Path) -> Non try: # Inject pool into config - config = SqliteConfig(connection_config={"database": str(db_path)}, connection_instance=pool) + config = SqliteConfig(connection_config=SqliteConnectionParams(database=str(db_path)), connection_instance=pool) # Verify pool is used assert config.connection_instance is pool @@ -128,7 +132,7 @@ def test_duckdb_connection_instance_with_pre_created_pool() -> None: try: # Inject pool into config - config = DuckDBConfig(connection_config={"database": ":memory:"}, connection_instance=pool) + config = DuckDBConfig(connection_config=DuckDBConnectionParams(database=":memory:"), connection_instance=pool) # Verify pool is used assert config.connection_instance is pool @@ -147,9 +151,7 @@ def test_sqlite_connection_instance_none_creates_new_pool(tmp_path: Path) -> Non """Test that connection_instance=None causes new pool creation.""" db_path = tmp_path / "test.db" - config = SqliteConfig( - connection_config={"database": str(db_path), "pool_min_size": 2, "pool_max_size": 5}, connection_instance=None - ) + config = SqliteConfig(connection_config=SqliteConnectionParams(database=str(db_path)), connection_instance=None) # Should create new pool assert config.connection_instance is None @@ -172,9 +174,7 @@ async def test_aiosqlite_connection_instance_none_creates_new_pool(tmp_path: Pat """Test that connection_instance=None causes new pool creation for async.""" db_path = tmp_path / "test.db" - config = AiosqliteConfig( - connection_config={"database": str(db_path), "pool_min_size": 2, "pool_max_size": 5}, connection_instance=None - ) + config = AiosqliteConfig(connection_config=AiosqlitePoolParams(database=str(db_path)), connection_instance=None) # Should create new pool assert config.connection_instance is None @@ -200,7 +200,7 @@ def test_connection_instance_persists_across_sessions() -> None: pool = DuckDBConnectionPool(connection_config={"database": ":memory:"}) try: - config = DuckDBConfig(connection_config={"database": ":memory:"}, connection_instance=pool) + config = DuckDBConfig(connection_config=DuckDBConnectionParams(database=":memory:"), connection_instance=pool) # First session with config.provide_session() as session1: @@ -226,7 +226,7 @@ def test_connection_instance_with_empty_connection_config() -> None: try: # Empty connection_config, only connection_instance - config = DuckDBConfig(connection_config={}, connection_instance=pool) + config = DuckDBConfig(connection_config=DuckDBConnectionParams(), connection_instance=pool) assert config.connection_instance is pool # DuckDB adds default database parameter @@ -252,9 +252,7 @@ async def test_asyncpg_connection_instance_overrides_connection_config_pool_para try: # Config has different pool params but connection_instance should take precedence - merged_config = dict(asyncpg_connection_config) - merged_config["min_size"] = 10 # This should be ignored - merged_config["max_size"] = 20 # This should be ignored + merged_config = AsyncpgPoolConfig(**asyncpg_connection_config, min_size=10, max_size=20) config = AsyncpgConfig(connection_config=merged_config, connection_instance=pool) # The injected pool should be used, not a new one with config params @@ -276,7 +274,7 @@ def test_connection_instance_manual_close() -> None: pool = DuckDBConnectionPool(connection_config={"database": ":memory:"}) - config = DuckDBConfig(connection_config={"database": ":memory:"}, connection_instance=pool) + config = DuckDBConfig(connection_config=DuckDBConnectionParams(database=":memory:"), connection_instance=pool) # Use the config with config.provide_session() as session: @@ -296,7 +294,7 @@ def test_sqlite_connection_instance_after_close_pool() -> None: pool = SqliteConnectionPool(connection_parameters={"database": ":memory:"}) - config = SqliteConfig(connection_config={"database": ":memory:"}, connection_instance=pool) + config = SqliteConfig(connection_config=SqliteConnectionParams(database=":memory:"), connection_instance=pool) # Close the pool via config config.close_pool() @@ -311,7 +309,7 @@ async def test_aiosqlite_connection_instance_after_close_pool() -> None: pool = AiosqliteConnectionPool(connection_parameters={"database": ":memory:"}, pool_size=2) - config = AiosqliteConfig(connection_config={"database": ":memory:"}, connection_instance=pool) + config = AiosqliteConfig(connection_config=AiosqlitePoolParams(database=":memory:"), connection_instance=pool) # Close the pool via config await config.close_pool() @@ -325,7 +323,7 @@ def test_connection_instance_with_mock_pool() -> None: mock_pool = MagicMock() mock_pool.acquire = MagicMock() - config = DuckDBConfig(connection_config={"database": ":memory:"}, connection_instance=mock_pool) + config = DuckDBConfig(connection_config=DuckDBConnectionParams(database=":memory:"), connection_instance=mock_pool) assert config.connection_instance is mock_pool @@ -335,6 +333,8 @@ async def test_connection_instance_with_async_mock_pool() -> None: mock_pool = MagicMock() mock_pool.acquire = AsyncMock() - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test"}, connection_instance=mock_pool) + config = AsyncpgConfig( + connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test"), connection_instance=mock_pool + ) assert config.connection_instance is mock_pool diff --git a/tests/integration/extensions/fastapi/test_filters_integration.py b/tests/integration/extensions/fastapi/test_filters_integration.py index a3c3baf97..19bdcc67b 100644 --- a/tests/integration/extensions/fastapi/test_filters_integration.py +++ b/tests/integration/extensions/fastapi/test_filters_integration.py @@ -393,3 +393,145 @@ async def list_users( assert "detail" in error_data # Should mention the validation error for createdBefore assert any("createdBefore" in str(error) for error in error_data["detail"]) + + +def test_fastapi_in_fields_filter_dependency() -> None: + """Test in_fields filter dependency with actual HTTP request (issue #405).""" + sqlspec = SQLSpec() + config = AiosqliteConfig( + connection_config={"database": ":memory:"}, extension_config={"starlette": {"commit_mode": "manual"}} + ) + sqlspec.add_config(config) + + app = FastAPI() + db_ext = SQLSpecPlugin(sqlspec, app=app) + + from sqlspec.extensions.fastapi.providers import FieldNameType, InCollectionFilter + + @app.get("/users") + async def list_users( + filters: Annotated[ + list[FilterTypes], + Depends(db_ext.provide_filters({"in_fields": {FieldNameType(name="status", type_hint=str)}})), + ], + ) -> dict[str, Any]: + in_filters = [f for f in filters if isinstance(f, InCollectionFilter)] + if in_filters: + return { + "filter_count": len(filters), + "field_name": in_filters[0].field_name, + "values": list(in_filters[0].values) if in_filters[0].values else [], + } + return {"filter_count": len(filters), "field_name": None, "values": []} + + with TestClient(app) as client: + # No query params - should return empty filters + response = client.get("/users") + assert response.status_code == 200 + assert response.json()["filter_count"] == 0 + + # With in-collection values + response = client.get("/users?statusIn=active&statusIn=archived") + assert response.status_code == 200 + data = response.json() + assert data["filter_count"] == 1 + assert data["field_name"] == "status" + assert set(data["values"]) == {"active", "archived"} + + +def test_fastapi_not_in_fields_filter_dependency() -> None: + """Test not_in_fields filter dependency with actual HTTP request (issue #405).""" + sqlspec = SQLSpec() + config = AiosqliteConfig( + connection_config={"database": ":memory:"}, extension_config={"starlette": {"commit_mode": "manual"}} + ) + sqlspec.add_config(config) + + app = FastAPI() + db_ext = SQLSpecPlugin(sqlspec, app=app) + + from sqlspec.extensions.fastapi.providers import FieldNameType, NotInCollectionFilter + + @app.get("/users") + async def list_users( + filters: Annotated[ + list[FilterTypes], + Depends(db_ext.provide_filters({"not_in_fields": {FieldNameType(name="status", type_hint=str)}})), + ], + ) -> dict[str, Any]: + not_in_filters = [f for f in filters if isinstance(f, NotInCollectionFilter)] + if not_in_filters: + return { + "filter_count": len(filters), + "field_name": not_in_filters[0].field_name, + "values": list(not_in_filters[0].values) if not_in_filters[0].values else [], + } + return {"filter_count": len(filters), "field_name": None, "values": []} + + with TestClient(app) as client: + # No query params - should return empty filters + response = client.get("/users") + assert response.status_code == 200 + assert response.json()["filter_count"] == 0 + + # With not-in-collection values + response = client.get("/users?statusNotIn=deleted&statusNotIn=archived") + assert response.status_code == 200 + data = response.json() + assert data["filter_count"] == 1 + assert data["field_name"] == "status" + assert set(data["values"]) == {"deleted", "archived"} + + +def test_fastapi_in_fields_with_query_execution() -> None: + """Test in_fields filter applied to actual SQL query execution (issue #405).""" + sqlspec = SQLSpec() + config = AiosqliteConfig( + connection_config={"database": ":memory:"}, extension_config={"starlette": {"commit_mode": "autocommit"}} + ) + sqlspec.add_config(config) + + app = FastAPI() + db_ext = SQLSpecPlugin(sqlspec, app=app) + + from sqlspec.extensions.fastapi.providers import FieldNameType + + @app.post("/setup") + async def setup(db: Annotated[AiosqliteDriver, Depends(db_ext.provide_session(config))]) -> dict[str, Any]: + await db.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, status TEXT)") + await db.execute("INSERT INTO users (name, status) VALUES ('Alice', 'active')") + await db.execute("INSERT INTO users (name, status) VALUES ('Bob', 'archived')") + await db.execute("INSERT INTO users (name, status) VALUES ('Charlie', 'deleted')") + await db.execute("INSERT INTO users (name, status) VALUES ('Diana', 'active')") + return {"created": True} + + @app.get("/users") + async def list_users( + filters: Annotated[ + list[FilterTypes], + Depends(db_ext.provide_filters({"in_fields": {FieldNameType(name="status", type_hint=str)}})), + ], + db: Annotated[AiosqliteDriver, Depends(db_ext.provide_session(config))], + ) -> dict[str, Any]: + result = await db.select("SELECT * FROM users", *filters) + return {"users": result, "applied_filters": len(filters)} + + with TestClient(app) as client: + response = client.post("/setup") + assert response.status_code == 200 + + # Filter by status IN ('active') + response = client.get("/users?statusIn=active") + assert response.status_code == 200 + data = response.json() + assert data["applied_filters"] == 1 + names = {u["name"] for u in data["users"]} + assert names == {"Alice", "Diana"} + + # Filter by status IN ('active', 'archived') + response = client.get("/users?statusIn=active&statusIn=archived") + assert response.status_code == 200 + data = response.json() + assert data["applied_filters"] == 1 + names = {u["name"] for u in data["users"]} + assert names == {"Alice", "Bob", "Diana"} diff --git a/tests/integration/extensions/litestar/test_in_fields_filters.py b/tests/integration/extensions/litestar/test_in_fields_filters.py new file mode 100644 index 000000000..5169be9c2 --- /dev/null +++ b/tests/integration/extensions/litestar/test_in_fields_filters.py @@ -0,0 +1,167 @@ +"""Integration tests for in_fields and not_in_fields filter dependencies with Litestar (issue #405).""" + +import tempfile +from typing import Any + +import pytest +from litestar import Litestar, get +from litestar.params import Dependency +from litestar.testing import TestClient + +from sqlspec.adapters.aiosqlite import AiosqliteConfig +from sqlspec.base import SQLSpec +from sqlspec.core import FilterTypes, InCollectionFilter, NotInCollectionFilter +from sqlspec.extensions.litestar import SQLSpecPlugin +from sqlspec.extensions.litestar.providers import FieldNameType, create_filter_dependencies, dep_cache +from sqlspec.typing import LITESTAR_INSTALLED + +pytestmark = pytest.mark.xdist_group("sqlite") + +if not LITESTAR_INSTALLED: + pytest.skip("Litestar not installed", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def _clear_dependency_cache() -> Any: + dep_cache.dependencies.clear() + yield + dep_cache.dependencies.clear() + + +def test_litestar_in_fields_filter_dependency() -> None: + """Test in_fields filter dependency with actual Litestar HTTP request (issue #405).""" + with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: + sql = SQLSpec() + config = AiosqliteConfig(connection_config={"database": tmp.name}) + sql.add_config(config) + + filter_deps = create_filter_dependencies({"in_fields": {FieldNameType(name="status", type_hint=str)}}) + + @get("/users", dependencies=filter_deps) + async def list_users( + filters: list[FilterTypes] = Dependency(skip_validation=True), # type: ignore[assignment] + ) -> dict[str, Any]: + in_filters = [f for f in filters if isinstance(f, InCollectionFilter)] + if in_filters: + return { + "filter_count": len(filters), + "field_name": in_filters[0].field_name, + "values": list(in_filters[0].values) if in_filters[0].values else [], + } + return {"filter_count": len(filters), "field_name": None, "values": []} + + app = Litestar(route_handlers=[list_users], plugins=[SQLSpecPlugin(sqlspec=sql)]) + + with TestClient(app=app) as client: + # No query params - should return empty filters + response = client.get("/users") + assert response.status_code == 200 + assert response.json()["filter_count"] == 0 + + # With in-collection values + response = client.get("/users?statusIn=active&statusIn=archived") + assert response.status_code == 200 + data = response.json() + assert data["filter_count"] == 1 + assert data["field_name"] == "status" + assert set(data["values"]) == {"active", "archived"} + + +def test_litestar_not_in_fields_filter_dependency() -> None: + """Test not_in_fields filter dependency with actual Litestar HTTP request (issue #405).""" + with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: + sql = SQLSpec() + config = AiosqliteConfig(connection_config={"database": tmp.name}) + sql.add_config(config) + + filter_deps = create_filter_dependencies({"not_in_fields": {FieldNameType(name="status", type_hint=str)}}) + + @get("/users", dependencies=filter_deps) + async def list_users( + filters: list[FilterTypes] = Dependency(skip_validation=True), # type: ignore[assignment] + ) -> dict[str, Any]: + not_in_filters = [f for f in filters if isinstance(f, NotInCollectionFilter)] + if not_in_filters: + return { + "filter_count": len(filters), + "field_name": not_in_filters[0].field_name, + "values": list(not_in_filters[0].values) if not_in_filters[0].values else [], + } + return {"filter_count": len(filters), "field_name": None, "values": []} + + app = Litestar(route_handlers=[list_users], plugins=[SQLSpecPlugin(sqlspec=sql)]) + + with TestClient(app=app) as client: + # No query params - should return empty filters + response = client.get("/users") + assert response.status_code == 200 + assert response.json()["filter_count"] == 0 + + # With not-in-collection values + response = client.get("/users?statusNotIn=deleted&statusNotIn=archived") + assert response.status_code == 200 + data = response.json() + assert data["filter_count"] == 1 + assert data["field_name"] == "status" + assert set(data["values"]) == {"deleted", "archived"} + + +def test_litestar_multiple_in_fields() -> None: + """Test multiple in_fields filters with Litestar (issue #405).""" + with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: + sql = SQLSpec() + config = AiosqliteConfig(connection_config={"database": tmp.name}) + sql.add_config(config) + + filter_deps = create_filter_dependencies({ + "in_fields": {FieldNameType(name="status", type_hint=str), FieldNameType(name="role", type_hint=str)} + }) + + @get("/users", dependencies=filter_deps) + async def list_users( + filters: list[FilterTypes] = Dependency(skip_validation=True), # type: ignore[assignment] + ) -> dict[str, Any]: + return {"filter_count": len(filters), "filter_types": sorted(type(f).__name__ for f in filters)} + + app = Litestar(route_handlers=[list_users], plugins=[SQLSpecPlugin(sqlspec=sql)]) + + with TestClient(app=app) as client: + # Both in-collection filters provided + response = client.get("/users?statusIn=active&roleIn=admin") + assert response.status_code == 200 + data = response.json() + assert data["filter_count"] == 2 + assert data["filter_types"] == ["InCollectionFilter", "InCollectionFilter"] + + +def test_litestar_in_fields_single_value() -> None: + """Test in_fields with a single query param value (issue #405).""" + with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: + sql = SQLSpec() + config = AiosqliteConfig(connection_config={"database": tmp.name}) + sql.add_config(config) + + filter_deps = create_filter_dependencies({"in_fields": {FieldNameType(name="status", type_hint=str)}}) + + @get("/users", dependencies=filter_deps) + async def list_users( + filters: list[FilterTypes] = Dependency(skip_validation=True), # type: ignore[assignment] + ) -> dict[str, Any]: + in_filters = [f for f in filters if isinstance(f, InCollectionFilter)] + if in_filters: + return { + "filter_count": len(filters), + "field_name": in_filters[0].field_name, + "values": list(in_filters[0].values) if in_filters[0].values else [], + } + return {"filter_count": len(filters), "field_name": None, "values": []} + + app = Litestar(route_handlers=[list_users], plugins=[SQLSpecPlugin(sqlspec=sql)]) + + with TestClient(app=app) as client: + response = client.get("/users?statusIn=active") + assert response.status_code == 200 + data = response.json() + assert data["filter_count"] == 1 + assert data["field_name"] == "status" + assert data["values"] == ["active"] diff --git a/tests/integration/test_pool_concurrency.py b/tests/integration/test_pool_concurrency.py index e0aa2a133..c03c13023 100644 --- a/tests/integration/test_pool_concurrency.py +++ b/tests/integration/test_pool_concurrency.py @@ -6,8 +6,8 @@ import pytest -from sqlspec.adapters.asyncpg import AsyncpgConfig -from sqlspec.adapters.duckdb import DuckDBConfig +from sqlspec.adapters.asyncpg import AsyncpgConfig, AsyncpgPoolConfig +from sqlspec.adapters.duckdb import DuckDBConfig, DuckDBConnectionParams if TYPE_CHECKING: from pytest_databases.docker.postgres import PostgresService @@ -18,13 +18,13 @@ async def test_asyncpg_pool_concurrency(postgres_service: PostgresService) -> None: """Verify that multiple concurrent calls to provide_pool result in a single pool.""" - config_params = { - "host": postgres_service.host, - "port": postgres_service.port, - "user": postgres_service.user, - "password": postgres_service.password, - "database": postgres_service.database, - } + config_params = AsyncpgPoolConfig( + host=postgres_service.host, + port=postgres_service.port, + user=postgres_service.user, + password=postgres_service.password, + database=postgres_service.database, + ) # Initialize with connection_instance=None explicitly just to be sure config = AsyncpgConfig(connection_config=config_params, connection_instance=None) @@ -50,7 +50,7 @@ async def get_pool() -> AsyncpgPool: def test_duckdb_pool_concurrency() -> None: """Verify that multiple concurrent calls to provide_pool result in a single pool (Sync).""" # Use shared memory db for valid concurrency test - config = DuckDBConfig(connection_config={"database": ":memory:"}) + config = DuckDBConfig(connection_config=DuckDBConnectionParams(database=":memory:")) # We need to capture results from threads results: list[DuckDBConnectionPool | None] = [None] * 50 diff --git a/tests/unit/base/test_set_migration_config.py b/tests/unit/base/test_set_migration_config.py new file mode 100644 index 000000000..e6ee09543 --- /dev/null +++ b/tests/unit/base/test_set_migration_config.py @@ -0,0 +1,56 @@ +"""Tests for set_migration_config() method on database config classes.""" + +import pytest + +from sqlspec.adapters.duckdb import DuckDBConfig +from sqlspec.adapters.sqlite import SqliteConfig + +pytestmark = pytest.mark.xdist_group("base") + + +def test_set_migration_config_after_construction() -> None: + """set_migration_config attaches config to an already-created instance.""" + config = SqliteConfig(connection_config={"database": ":memory:"}) + assert config.migration_config == {} + + config.set_migration_config({"script_location": "db/migrations", "version_table_name": "my_versions"}) + + assert config.migration_config["script_location"] == "db/migrations" + assert config.migration_config["version_table_name"] == "my_versions" + + +def test_set_migration_config_equivalent_to_constructor() -> None: + """set_migration_config produces the same result as passing config at construction.""" + migration = {"script_location": "migrations", "version_table_name": "schema_version"} + + config_at_init = SqliteConfig(connection_config={"database": ":memory:"}, migration_config=migration) + + config_post_init = SqliteConfig(connection_config={"database": ":memory:"}) + config_post_init.set_migration_config(migration) + + assert config_at_init.migration_config == config_post_init.migration_config + + +def test_set_migration_config_overwrites_previous() -> None: + """Calling set_migration_config replaces any previously set config.""" + config = DuckDBConfig( + connection_config={"database": ":memory:"}, migration_config={"script_location": "old_migrations"} + ) + assert config.migration_config["script_location"] == "old_migrations" + + config.set_migration_config({"script_location": "new_migrations"}) + + assert config.migration_config["script_location"] == "new_migrations" + assert "version_table_name" not in config.migration_config + + +def test_set_migration_config_with_typed_dict() -> None: + """set_migration_config works with MigrationConfig TypedDict values.""" + from sqlspec.config import MigrationConfig + + mc: MigrationConfig = {"script_location": "alembic", "enabled": True} + config = SqliteConfig(connection_config={"database": ":memory:"}) + config.set_migration_config(mc) + + assert config.migration_config["script_location"] == "alembic" + assert config.migration_config["enabled"] is True diff --git a/tests/unit/config/test_connection_config_edge_cases.py b/tests/unit/config/test_connection_config_edge_cases.py index fd434d137..af44ab7d4 100644 --- a/tests/unit/config/test_connection_config_edge_cases.py +++ b/tests/unit/config/test_connection_config_edge_cases.py @@ -4,15 +4,19 @@ standardized parameter naming. """ -from sqlspec.adapters.aiosqlite.config import AiosqliteConfig -from sqlspec.adapters.asyncpg.config import AsyncpgConfig -from sqlspec.adapters.duckdb.config import DuckDBConfig -from sqlspec.adapters.sqlite.config import SqliteConfig +from typing import Any, cast + +from sqlspec.adapters.aiosqlite.config import AiosqliteConfig, AiosqlitePoolParams +from sqlspec.adapters.asyncpg.config import AsyncpgConfig, AsyncpgPoolConfig +from sqlspec.adapters.duckdb.config import DuckDBConfig, DuckDBPoolParams +from sqlspec.adapters.sqlite.config import SqliteConfig, SqliteConnectionParams def test_connection_config_with_zero_pool_size() -> None: """Test connection_config with zero pool size parameters.""" - config = SqliteConfig(connection_config={"database": ":memory:", "pool_min_size": 0, "pool_max_size": 0}) + config = SqliteConfig( + connection_config=cast(SqliteConnectionParams, {"database": ":memory:", "pool_min_size": 0, "pool_max_size": 0}) + ) assert config.connection_config["pool_min_size"] == 0 assert config.connection_config["pool_max_size"] == 0 @@ -20,7 +24,7 @@ def test_connection_config_with_zero_pool_size() -> None: def test_connection_config_with_negative_pool_size() -> None: """Test connection_config with negative pool size parameters.""" - config = DuckDBConfig(connection_config={"database": ":memory:", "pool_min_size": -1, "pool_max_size": -1}) + config = DuckDBConfig(connection_config=DuckDBPoolParams(database=":memory:", pool_min_size=-1, pool_max_size=-1)) # Negative values are stored but pool creation may validate them assert config.connection_config["pool_min_size"] == -1 @@ -30,7 +34,7 @@ def test_connection_config_with_negative_pool_size() -> None: def test_connection_config_with_very_large_pool_size() -> None: """Test connection_config with very large pool size values.""" config = AsyncpgConfig( - connection_config={"dsn": "postgresql://localhost/test", "min_size": 1000, "max_size": 10000} + connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test", min_size=1000, max_size=10000) ) assert config.connection_config["min_size"] == 1000 @@ -39,7 +43,11 @@ def test_connection_config_with_very_large_pool_size() -> None: def test_connection_config_with_min_greater_than_max() -> None: """Test connection_config with min_size > max_size (invalid but stored).""" - config = SqliteConfig(connection_config={"database": ":memory:", "pool_min_size": 10, "pool_max_size": 5}) + config = SqliteConfig( + connection_config=cast( + SqliteConnectionParams, {"database": ":memory:", "pool_min_size": 10, "pool_max_size": 5} + ) + ) # Config stores values, validation happens at pool creation assert config.connection_config["pool_min_size"] == 10 @@ -49,10 +57,10 @@ def test_connection_config_with_min_greater_than_max() -> None: def test_connection_config_with_special_characters_in_strings() -> None: """Test connection_config with special characters in string values.""" config = AsyncpgConfig( - connection_config={ - "dsn": "postgresql://user:p@ss!w0rd#$%@localhost/test?sslmode=require", - "server_settings": {"application_name": "app with spaces & symbols!"}, - } + connection_config=AsyncpgPoolConfig( + dsn="postgresql://user:p@ss!w0rd#$%@localhost/test?sslmode=require", + server_settings={"application_name": "app with spaces & symbols!"}, + ) ) assert "p@ss!w0rd#$%" in config.connection_config["dsn"] @@ -62,10 +70,9 @@ def test_connection_config_with_special_characters_in_strings() -> None: def test_connection_config_with_unicode_strings() -> None: """Test connection_config with unicode characters.""" config = AsyncpgConfig( - connection_config={ - "dsn": "postgresql://localhost/test", - "server_settings": {"application_name": "テスト アプリ"}, - } + connection_config=AsyncpgPoolConfig( + dsn="postgresql://localhost/test", server_settings={"application_name": "テスト アプリ"} + ) ) assert config.connection_config["server_settings"]["application_name"] == "テスト アプリ" @@ -73,7 +80,7 @@ def test_connection_config_with_unicode_strings() -> None: def test_connection_config_with_empty_strings() -> None: """Test connection_config with empty string values.""" - config = AsyncpgConfig(connection_config={"dsn": "", "user": "", "password": ""}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="", user="", password="")) assert config.connection_config["dsn"] == "" assert config.connection_config["user"] == "" @@ -82,7 +89,11 @@ def test_connection_config_with_empty_strings() -> None: def test_connection_config_with_none_values_in_dict() -> None: """Test connection_config with None values for keys.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test", "user": None, "password": None}) + config = AsyncpgConfig( + connection_config=cast( + AsyncpgPoolConfig, {"dsn": "postgresql://localhost/test", "user": None, "password": None} + ) + ) assert config.connection_config["dsn"] == "postgresql://localhost/test" assert config.connection_config["user"] is None @@ -92,7 +103,7 @@ def test_connection_config_with_none_values_in_dict() -> None: def test_connection_config_with_boolean_false_values() -> None: """Test connection_config with False boolean values.""" config = SqliteConfig( - connection_config={"database": ":memory:", "check_same_thread": False, "cached_statements": 0} + connection_config=SqliteConnectionParams(database=":memory:", check_same_thread=False, cached_statements=0) ) assert config.connection_config["check_same_thread"] is False @@ -102,14 +113,17 @@ def test_connection_config_with_boolean_false_values() -> None: def test_connection_config_with_mixed_types() -> None: """Test connection_config with various Python types.""" config = AsyncpgConfig( - connection_config={ - "dsn": "postgresql://localhost/test", - "min_size": 5, - "timeout": 30.5, - "ssl": True, - "server_settings": {"key": "value"}, - "record_class": None, - } + connection_config=cast( + AsyncpgPoolConfig, + { + "dsn": "postgresql://localhost/test", + "min_size": 5, + "timeout": 30.5, + "ssl": True, + "server_settings": {"key": "value"}, + "record_class": None, + }, + ) ) assert isinstance(config.connection_config["dsn"], str) @@ -122,7 +136,7 @@ def test_connection_config_with_mixed_types() -> None: def test_connection_config_modification_after_creation() -> None: """Test that connection_config can be modified after config creation.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test"}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test")) # Modify existing key config.connection_config["dsn"] = "postgresql://localhost/test2" @@ -139,7 +153,7 @@ def test_connection_config_modification_after_creation() -> None: def test_connection_config_clear_after_creation() -> None: """Test that connection_config can be cleared after creation.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test", "min_size": 5}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test", min_size=5)) config.connection_config.clear() @@ -148,7 +162,7 @@ def test_connection_config_clear_after_creation() -> None: def test_connection_config_update_method() -> None: """Test that connection_config supports dict update() method.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test"}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test")) config.connection_config.update({"min_size": 5, "max_size": 10}) @@ -159,10 +173,13 @@ def test_connection_config_update_method() -> None: def test_connection_config_with_deeply_nested_dicts() -> None: """Test connection_config with deeply nested dict structures.""" config = AsyncpgConfig( - connection_config={ - "dsn": "postgresql://localhost/test", - "server_settings": {"level1": {"level2": {"level3": {"key": "value"}}}}, - } + connection_config=cast( + AsyncpgPoolConfig, + { + "dsn": "postgresql://localhost/test", + "server_settings": {"level1": {"level2": {"level3": {"key": "value"}}}}, + }, + ) ) assert config.connection_config["server_settings"]["level1"]["level2"]["level3"]["key"] == "value" @@ -171,10 +188,13 @@ def test_connection_config_with_deeply_nested_dicts() -> None: def test_connection_config_with_list_values() -> None: """Test connection_config with list values.""" config = AsyncpgConfig( - connection_config={ - "dsn": "postgresql://localhost/test", - "server_settings": {"extensions": ["pg_trgm", "pgcrypto", "uuid-ossp"]}, - } + connection_config=cast( + AsyncpgPoolConfig, + { + "dsn": "postgresql://localhost/test", + "server_settings": {"extensions": ["pg_trgm", "pgcrypto", "uuid-ossp"]}, + }, + ) ) assert config.connection_config["server_settings"]["extensions"] == ["pg_trgm", "pgcrypto", "uuid-ossp"] @@ -182,7 +202,11 @@ def test_connection_config_with_list_values() -> None: def test_connection_config_with_tuple_values() -> None: """Test connection_config with tuple values.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test", "ssl": ("require", "verify-ca")}) + config = AsyncpgConfig( + connection_config=cast( + AsyncpgPoolConfig, {"dsn": "postgresql://localhost/test", "ssl": ("require", "verify-ca")} + ) + ) assert config.connection_config["ssl"] == ("require", "verify-ca") @@ -194,7 +218,7 @@ class FakePool: pass fake_pool = FakePool() - config = DuckDBConfig(connection_config={"database": ":memory:"}, connection_instance=fake_pool) # type: ignore[arg-type] + config = DuckDBConfig(connection_config=DuckDBPoolParams(database=":memory:"), connection_instance=fake_pool) # type: ignore[arg-type] assert config.connection_instance is fake_pool # type: ignore[comparison-overlap] @@ -203,7 +227,7 @@ def test_connection_instance_can_be_modified_after_creation() -> None: """Test that connection_instance can be modified after config creation.""" from unittest.mock import MagicMock - config = DuckDBConfig(connection_config={"database": ":memory:"}) + config = DuckDBConfig(connection_config=DuckDBPoolParams(database=":memory:")) assert config.connection_instance is None mock_pool = MagicMock() @@ -214,8 +238,8 @@ def test_connection_instance_can_be_modified_after_creation() -> None: def test_multiple_configs_do_not_share_connection_config() -> None: """Test that modifying one config's connection_config doesn't affect another.""" - config1 = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/db1"}) - config2 = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/db2"}) + config1 = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/db1")) + config2 = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/db2")) # Modify config1 config1.connection_config["min_size"] = 5 @@ -227,12 +251,12 @@ def test_multiple_configs_do_not_share_connection_config() -> None: def test_connection_config_dict_reference_semantics() -> None: """Test that connection_config has dict reference semantics.""" - test_dict = {"dsn": "postgresql://localhost/test"} - config = AsyncpgConfig(connection_config=test_dict) + test_dict: dict[str, Any] = {"dsn": "postgresql://localhost/test"} + config = AsyncpgConfig(connection_config=cast(AsyncpgPoolConfig, test_dict)) # Modifying the original dict should NOT affect config # (because config stores a copy or processes it) - test_dict["min_size"] = 5 # pyright: ignore[reportArgumentType] + test_dict["min_size"] = 5 # Depending on implementation, this may or may not affect config # Let's test the actual behavior @@ -248,7 +272,9 @@ def test_connection_config_with_callables() -> None: def custom_init() -> str: return "initialized" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test", "init": custom_init}) + config = AsyncpgConfig( + connection_config=cast(AsyncpgPoolConfig, {"dsn": "postgresql://localhost/test", "init": custom_init}) + ) assert callable(config.connection_config["init"]) assert config.connection_config["init"]() == "initialized" @@ -257,7 +283,7 @@ def custom_init() -> str: def test_connection_config_with_very_long_strings() -> None: """Test connection_config with very long string values.""" long_string = "x" * 10000 - config = AsyncpgConfig(connection_config={"dsn": f"postgresql://localhost/{long_string}"}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn=f"postgresql://localhost/{long_string}")) assert len(config.connection_config["dsn"]) > 10000 @@ -266,12 +292,15 @@ def test_connection_config_key_with_reserved_python_keywords() -> None: """Test connection_config keys that are Python reserved words.""" # Note: This is valid in dict keys even if they're reserved words config = AsyncpgConfig( - connection_config={ - "dsn": "postgresql://localhost/test", - "class": "value", # reserved word - "def": "value", # reserved word - "return": "value", # reserved word - } + connection_config=cast( + AsyncpgPoolConfig, + { + "dsn": "postgresql://localhost/test", + "class": "value", # reserved word + "def": "value", # reserved word + "return": "value", # reserved word + }, + ) ) assert config.connection_config["class"] == "value" @@ -281,7 +310,9 @@ def test_connection_config_key_with_reserved_python_keywords() -> None: def test_connection_config_numeric_keys() -> None: """Test connection_config with numeric keys (valid dict keys).""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test", 1: "value", 2.5: "value"}) # type: ignore[dict-item,misc] + config = AsyncpgConfig( + connection_config=cast(AsyncpgPoolConfig, {"dsn": "postgresql://localhost/test", 1: "value", 2.5: "value"}) + ) # type: ignore[dict-item,misc] assert config.connection_config[1] == "value" # type: ignore[index] assert config.connection_config[2.5] == "value" # type: ignore[index] @@ -292,7 +323,9 @@ def test_connection_instance_remains_after_connection_config_change() -> None: from unittest.mock import MagicMock mock_pool = MagicMock() - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test"}, connection_instance=mock_pool) + config = AsyncpgConfig( + connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test"), connection_instance=mock_pool + ) # Modify connection_config config.connection_config["min_size"] = 5 @@ -303,7 +336,11 @@ def test_connection_instance_remains_after_connection_config_change() -> None: def test_connection_config_with_bytes_values() -> None: """Test connection_config with bytes values.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test", "ssl_cert": b"certificate data"}) + config = AsyncpgConfig( + connection_config=cast( + AsyncpgPoolConfig, {"dsn": "postgresql://localhost/test", "ssl_cert": b"certificate data"} + ) + ) assert config.connection_config["ssl_cert"] == b"certificate data" @@ -313,14 +350,14 @@ async def test_aiosqlite_connection_config_with_pathlib_path() -> None: from pathlib import Path db_path = Path(":memory:") - config = AiosqliteConfig(connection_config={"database": db_path}) + config = AiosqliteConfig(connection_config=cast(AiosqlitePoolParams, {"database": db_path})) assert config.connection_config["database"] == db_path def test_connection_config_setdefault_method() -> None: """Test that connection_config supports dict setdefault() method.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test"}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test")) result = config.connection_config.setdefault("min_size", 5) @@ -334,7 +371,7 @@ def test_connection_config_setdefault_method() -> None: def test_connection_config_get_method_with_default() -> None: """Test that connection_config supports dict get() method.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test"}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test")) assert config.connection_config.get("min_size", 5) == 5 assert config.connection_config.get("dsn") == "postgresql://localhost/test" @@ -342,7 +379,7 @@ def test_connection_config_get_method_with_default() -> None: def test_connection_config_pop_method() -> None: """Test that connection_config supports dict pop() method.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test", "min_size": 5}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test", min_size=5)) popped_value = config.connection_config.pop("min_size") @@ -352,7 +389,9 @@ def test_connection_config_pop_method() -> None: def test_connection_config_items_method() -> None: """Test that connection_config supports dict items() iteration.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test", "min_size": 5, "max_size": 10}) + config = AsyncpgConfig( + connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test", min_size=5, max_size=10) + ) items = list(config.connection_config.items()) @@ -364,7 +403,7 @@ def test_connection_config_items_method() -> None: def test_connection_config_keys_method() -> None: """Test that connection_config supports dict keys() method.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test", "min_size": 5}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test", min_size=5)) keys = list(config.connection_config.keys()) @@ -375,7 +414,7 @@ def test_connection_config_keys_method() -> None: def test_connection_config_values_method() -> None: """Test that connection_config supports dict values() method.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test", "min_size": 5}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test", min_size=5)) values = list(config.connection_config.values()) @@ -386,7 +425,7 @@ def test_connection_config_values_method() -> None: def test_connection_config_in_operator() -> None: """Test that connection_config supports 'in' operator.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test"}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test")) assert "dsn" in config.connection_config assert "min_size" not in config.connection_config @@ -394,7 +433,7 @@ def test_connection_config_in_operator() -> None: def test_connection_config_len_function() -> None: """Test that connection_config supports len() function.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test", "min_size": 5}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test", min_size=5)) assert len(config.connection_config) == 2 @@ -402,8 +441,8 @@ def test_connection_config_len_function() -> None: def test_connection_config_bool_evaluation() -> None: """Test that connection_config evaluates to bool correctly.""" # Use AsyncpgConfig which doesn't add default values - config_empty = AsyncpgConfig(connection_config={}) - config_with_data = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test"}) + config_empty = AsyncpgConfig(connection_config=AsyncpgPoolConfig()) + config_with_data = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test")) assert not bool(config_empty.connection_config) assert bool(config_with_data.connection_config) @@ -411,7 +450,7 @@ def test_connection_config_bool_evaluation() -> None: def test_connection_config_copy_method() -> None: """Test that connection_config supports dict copy() method.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test", "min_size": 5}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test", min_size=5)) config_copy = config.connection_config.copy() diff --git a/tests/unit/config/test_connection_config_parameters.py b/tests/unit/config/test_connection_config_parameters.py index e3ecdcd26..ac7f1a583 100644 --- a/tests/unit/config/test_connection_config_parameters.py +++ b/tests/unit/config/test_connection_config_parameters.py @@ -15,22 +15,24 @@ 5. Configuration merging and overrides """ -from sqlspec.adapters.adbc.config import AdbcConfig -from sqlspec.adapters.aiosqlite.config import AiosqliteConfig -from sqlspec.adapters.asyncmy.config import AsyncmyConfig -from sqlspec.adapters.asyncpg.config import AsyncpgConfig -from sqlspec.adapters.bigquery.config import BigQueryConfig -from sqlspec.adapters.duckdb.config import DuckDBConfig -from sqlspec.adapters.oracledb.config import OracleAsyncConfig, OracleSyncConfig -from sqlspec.adapters.psqlpy.config import PsqlpyConfig -from sqlspec.adapters.psycopg.config import PsycopgAsyncConfig, PsycopgSyncConfig +from typing import Any, cast + +from sqlspec.adapters.adbc.config import AdbcConfig, AdbcConnectionParams +from sqlspec.adapters.aiosqlite.config import AiosqliteConfig, AiosqlitePoolParams +from sqlspec.adapters.asyncmy.config import AsyncmyConfig, AsyncmyPoolParams +from sqlspec.adapters.asyncpg.config import AsyncpgConfig, AsyncpgPoolConfig +from sqlspec.adapters.bigquery.config import BigQueryConfig, BigQueryConnectionParams +from sqlspec.adapters.duckdb.config import DuckDBConfig, DuckDBPoolParams +from sqlspec.adapters.oracledb.config import OracleAsyncConfig, OraclePoolParams, OracleSyncConfig +from sqlspec.adapters.psqlpy.config import PsqlpyConfig, PsqlpyPoolParams +from sqlspec.adapters.psycopg.config import PsycopgAsyncConfig, PsycopgPoolParams, PsycopgSyncConfig from sqlspec.adapters.spanner.config import SpannerSyncConfig -from sqlspec.adapters.sqlite.config import SqliteConfig +from sqlspec.adapters.sqlite.config import SqliteConfig, SqliteConnectionParams def test_connection_config_parameter_accepts_dict() -> None: """Test that connection_config parameter accepts dict values.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test", "min_size": 5}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test", min_size=5)) assert config.connection_config["dsn"] == "postgresql://localhost/test" assert config.connection_config["min_size"] == 5 @@ -56,21 +58,23 @@ def test_connection_config_accepts_none_and_converts_to_empty_dict() -> None: def test_connection_instance_defaults_to_none() -> None: """Test that connection_instance defaults to None when not provided.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test"}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test")) assert config.connection_instance is None def test_connection_instance_accepts_none_explicitly() -> None: """Test that connection_instance=None is explicitly accepted.""" - config = PsycopgAsyncConfig(connection_config={"conninfo": "postgresql://localhost/test"}, connection_instance=None) + config = PsycopgAsyncConfig( + connection_config=PsycopgPoolParams(conninfo="postgresql://localhost/test"), connection_instance=None + ) assert config.connection_instance is None def test_connection_config_stored_in_base_class() -> None: """Test that connection_config is stored in the base class attribute.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test"}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test")) assert hasattr(config, "connection_config") assert config.connection_config["dsn"] == "postgresql://localhost/test" @@ -78,7 +82,7 @@ def test_connection_config_stored_in_base_class() -> None: def test_connection_instance_stored_in_base_class() -> None: """Test that connection_instance is stored in the base class attribute.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test"}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test")) assert hasattr(config, "connection_instance") assert config.connection_instance is None @@ -87,21 +91,23 @@ def test_connection_instance_stored_in_base_class() -> None: def test_asyncpg_config_accepts_connection_parameters() -> None: """Test AsyncpgConfig accepts connection_config and connection_instance.""" config = AsyncpgConfig( - connection_config={"dsn": "postgresql://localhost/test", "min_size": 5, "max_size": 10, "timeout": 30.0}, + connection_config=AsyncpgPoolConfig( + dsn="postgresql://localhost/test", min_size=5, max_size=10, connect_timeout=30.0 + ), connection_instance=None, ) assert config.connection_config["dsn"] == "postgresql://localhost/test" assert config.connection_config["min_size"] == 5 assert config.connection_config["max_size"] == 10 - assert config.connection_config["timeout"] == 30.0 + assert config.connection_config["connect_timeout"] == 30.0 assert config.connection_instance is None def test_psycopg_async_config_accepts_connection_parameters() -> None: """Test PsycopgAsyncConfig accepts connection_config and connection_instance.""" config = PsycopgAsyncConfig( - connection_config={"conninfo": "postgresql://localhost/test", "min_size": 2, "max_size": 20}, + connection_config=PsycopgPoolParams(conninfo="postgresql://localhost/test", min_size=2, max_size=20), connection_instance=None, ) @@ -114,7 +120,7 @@ def test_psycopg_async_config_accepts_connection_parameters() -> None: def test_psycopg_sync_config_accepts_connection_parameters() -> None: """Test PsycopgSyncConfig accepts connection_config and connection_instance.""" config = PsycopgSyncConfig( - connection_config={"conninfo": "postgresql://localhost/test", "min_size": 1, "max_size": 5}, + connection_config=PsycopgPoolParams(conninfo="postgresql://localhost/test", min_size=1, max_size=5), connection_instance=None, ) @@ -127,15 +133,9 @@ def test_psycopg_sync_config_accepts_connection_parameters() -> None: def test_asyncmy_config_accepts_connection_parameters() -> None: """Test AsyncmyConfig accepts connection_config and connection_instance.""" config = AsyncmyConfig( - connection_config={ - "host": "localhost", - "port": 3306, - "user": "root", - "password": "password", - "database": "test", - "minsize": 1, - "maxsize": 10, - }, + connection_config=AsyncmyPoolParams( + host="localhost", port=3306, user="root", password="password", database="test", minsize=1, maxsize=10 + ), connection_instance=None, ) @@ -150,7 +150,8 @@ def test_asyncmy_config_accepts_connection_parameters() -> None: def test_psqlpy_config_accepts_connection_parameters() -> None: """Test PsqlpyConfig accepts connection_config and connection_instance.""" config = PsqlpyConfig( - connection_config={"dsn": "postgresql://localhost/test", "max_db_pool_size": 10}, connection_instance=None + connection_config=PsqlpyPoolParams(dsn="postgresql://localhost/test", max_db_pool_size=10), + connection_instance=None, ) assert config.connection_config["dsn"] == "postgresql://localhost/test" @@ -161,13 +162,9 @@ def test_psqlpy_config_accepts_connection_parameters() -> None: def test_oracle_async_config_accepts_connection_parameters() -> None: """Test OracleAsyncConfig accepts connection_config and connection_instance.""" config = OracleAsyncConfig( - connection_config={ - "user": "system", - "password": "password", - "dsn": "localhost:1521/ORCLPDB1", - "min": 1, - "max": 5, - }, + connection_config=OraclePoolParams( + user="system", password="password", dsn="localhost:1521/ORCLPDB1", min=1, max=5 + ), connection_instance=None, ) @@ -181,13 +178,9 @@ def test_oracle_async_config_accepts_connection_parameters() -> None: def test_oracle_sync_config_accepts_connection_parameters() -> None: """Test OracleSyncConfig accepts connection_config and connection_instance.""" config = OracleSyncConfig( - connection_config={ - "user": "system", - "password": "password", - "dsn": "localhost:1521/ORCLPDB1", - "min": 2, - "max": 10, - }, + connection_config=OraclePoolParams( + user="system", password="password", dsn="localhost:1521/ORCLPDB1", min=2, max=10 + ), connection_instance=None, ) @@ -201,7 +194,10 @@ def test_oracle_sync_config_accepts_connection_parameters() -> None: def test_sqlite_config_accepts_connection_parameters() -> None: """Test SqliteConfig accepts connection_config and connection_instance.""" config = SqliteConfig( - connection_config={"database": ":memory:", "check_same_thread": False, "pool_min_size": 5, "pool_max_size": 10}, + connection_config=cast( + SqliteConnectionParams, + {"database": ":memory:", "check_same_thread": False, "pool_min_size": 5, "pool_max_size": 10}, + ), connection_instance=None, ) @@ -216,7 +212,9 @@ def test_sqlite_config_accepts_connection_parameters() -> None: def test_aiosqlite_config_accepts_connection_parameters() -> None: """Test AiosqliteConfig accepts connection_config and connection_instance.""" config = AiosqliteConfig( - connection_config={"database": ":memory:", "timeout": 10.0, "pool_min_size": 2, "pool_max_size": 8}, + connection_config=cast( + AiosqlitePoolParams, {"database": ":memory:", "timeout": 10.0, "pool_min_size": 2, "pool_max_size": 8} + ), connection_instance=None, ) @@ -231,7 +229,7 @@ def test_aiosqlite_config_accepts_connection_parameters() -> None: def test_duckdb_config_accepts_connection_parameters() -> None: """Test DuckDBConfig accepts connection_config and connection_instance.""" config = DuckDBConfig( - connection_config={"database": ":memory:", "read_only": False, "pool_min_size": 3, "pool_max_size": 12}, + connection_config=DuckDBPoolParams(database=":memory:", read_only=False, pool_min_size=3, pool_max_size=12), connection_instance=None, ) @@ -246,7 +244,7 @@ def test_duckdb_config_accepts_connection_parameters() -> None: def test_bigquery_config_accepts_connection_parameters() -> None: """Test BigQueryConfig accepts connection_config and connection_instance.""" config = BigQueryConfig( - connection_config={"project": "my-project", "dataset_id": "my_dataset", "location": "US"}, + connection_config=BigQueryConnectionParams(project="my-project", dataset_id="my_dataset", location="US"), connection_instance=None, ) @@ -259,7 +257,9 @@ def test_bigquery_config_accepts_connection_parameters() -> None: def test_adbc_config_accepts_connection_parameters() -> None: """Test AdbcConfig accepts connection_config and connection_instance.""" config = AdbcConfig( - connection_config={"driver": "adbc_driver_postgresql", "uri": "postgresql://localhost/test"}, + connection_config=cast( + AdbcConnectionParams, {"driver": "adbc_driver_postgresql", "uri": "postgresql://localhost/test"} + ), connection_instance=None, ) @@ -282,7 +282,7 @@ def test_spanner_config_accepts_connection_parameters() -> None: def test_connection_config_empty_dict_is_valid() -> None: """Test that empty connection_config dict is valid for adapters.""" # Use AsyncpgConfig which doesn't add defaults - config = AsyncpgConfig(connection_config={}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig()) assert config.connection_config == {} assert isinstance(config.connection_config, dict) @@ -290,7 +290,7 @@ def test_connection_config_empty_dict_is_valid() -> None: def test_connection_config_can_be_modified_after_creation() -> None: """Test that connection_config dict can be modified after config creation.""" - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test"}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test")) config.connection_config["min_size"] = 5 config.connection_config["max_size"] = 10 @@ -301,14 +301,14 @@ def test_connection_config_can_be_modified_after_creation() -> None: def test_connection_config_preserves_all_keys() -> None: """Test that connection_config preserves all provided keys.""" - test_config = { - "dsn": "postgresql://localhost/test", - "min_size": 5, - "max_size": 10, - "timeout": 30.0, - "command_timeout": 60.0, - "server_settings": {"application_name": "sqlspec"}, - } + test_config = AsyncpgPoolConfig( + dsn="postgresql://localhost/test", + min_size=5, + max_size=10, + connect_timeout=30.0, + command_timeout=60.0, + server_settings={"application_name": "sqlspec"}, + ) config = AsyncpgConfig(connection_config=test_config) for key, value in test_config.items(): @@ -317,8 +317,8 @@ def test_connection_config_preserves_all_keys() -> None: def test_multiple_configs_have_independent_connection_configs() -> None: """Test that multiple config instances have independent connection_config dicts.""" - config1 = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/db1"}) - config2 = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/db2"}) + config1 = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/db1")) + config2 = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/db2")) assert config1.connection_config["dsn"] == "postgresql://localhost/db1" assert config2.connection_config["dsn"] == "postgresql://localhost/db2" @@ -331,10 +331,9 @@ def test_multiple_configs_have_independent_connection_configs() -> None: def test_connection_config_with_nested_dicts() -> None: """Test that connection_config handles nested dict values.""" config = AsyncpgConfig( - connection_config={ - "dsn": "postgresql://localhost/test", - "server_settings": {"application_name": "sqlspec", "timezone": "UTC"}, - } + connection_config=AsyncpgPoolConfig( + dsn="postgresql://localhost/test", server_settings={"application_name": "sqlspec", "timezone": "UTC"} + ) ) assert config.connection_config["server_settings"]["application_name"] == "sqlspec" @@ -344,18 +343,13 @@ def test_connection_config_with_nested_dicts() -> None: def test_connection_config_with_various_value_types() -> None: """Test that connection_config handles various value types.""" config = AsyncpgConfig( - connection_config={ - "dsn": "postgresql://localhost/test", # str - "min_size": 5, # int - "timeout": 30.5, # float - "ssl": True, # bool - "server_settings": {"key": "value"}, # dict - } + connection_config=AsyncpgPoolConfig( + dsn="postgresql://localhost/test", min_size=5, server_settings={"key": "value"}, ssl=True + ) ) assert isinstance(config.connection_config["dsn"], str) assert isinstance(config.connection_config["min_size"], int) - assert isinstance(config.connection_config["timeout"], float) assert isinstance(config.connection_config["ssl"], bool) assert isinstance(config.connection_config["server_settings"], dict) @@ -363,13 +357,16 @@ def test_connection_config_with_various_value_types() -> None: def test_sqlite_custom_pool_parameters_in_connection_config() -> None: """Test that SQLite custom pool parameters work in connection_config.""" config = SqliteConfig( - connection_config={ - "database": ":memory:", - "pool_min_size": 5, - "pool_max_size": 10, - "pool_pre_ping": True, - "pool_recycle": 3600, - } + connection_config=cast( + SqliteConnectionParams, + { + "database": ":memory:", + "pool_min_size": 5, + "pool_max_size": 10, + "pool_pre_ping": True, + "pool_recycle": 3600, + }, + ) ) assert config.connection_config["pool_min_size"] == 5 @@ -381,13 +378,16 @@ def test_sqlite_custom_pool_parameters_in_connection_config() -> None: def test_aiosqlite_custom_pool_parameters_in_connection_config() -> None: """Test that AioSQLite custom pool parameters work in connection_config.""" config = AiosqliteConfig( - connection_config={ - "database": ":memory:", - "pool_min_size": 2, - "pool_max_size": 8, - "pool_pre_ping": False, - "pool_recycle": 7200, - } + connection_config=cast( + AiosqlitePoolParams, + { + "database": ":memory:", + "pool_min_size": 2, + "pool_max_size": 8, + "pool_pre_ping": False, + "pool_recycle": 7200, + }, + ) ) assert config.connection_config["pool_min_size"] == 2 @@ -399,7 +399,9 @@ def test_aiosqlite_custom_pool_parameters_in_connection_config() -> None: def test_duckdb_custom_pool_parameters_in_connection_config() -> None: """Test that DuckDB custom pool parameters work in connection_config.""" config = DuckDBConfig( - connection_config={"database": ":memory:", "pool_min_size": 3, "pool_max_size": 12, "pool_pre_ping": True} + connection_config=cast( + DuckDBPoolParams, {"database": ":memory:", "pool_min_size": 3, "pool_max_size": 12, "pool_pre_ping": True} + ) ) assert config.connection_config["pool_min_size"] == 3 @@ -409,29 +411,29 @@ def test_duckdb_custom_pool_parameters_in_connection_config() -> None: def test_connection_config_parameter_naming_consistency() -> None: """Test that all adapters use consistent connection_config parameter name.""" - adapters = [ - (AsyncpgConfig, {"dsn": "postgresql://localhost/test"}, None), - (PsycopgAsyncConfig, {"conninfo": "postgresql://localhost/test"}, None), - (PsycopgSyncConfig, {"conninfo": "postgresql://localhost/test"}, None), - (AsyncmyConfig, {"host": "localhost", "database": "test"}, "port"), # Adds default port - (PsqlpyConfig, {"dsn": "postgresql://localhost/test"}, None), - (OracleAsyncConfig, {"user": "system", "password": "pwd", "dsn": "localhost/XE"}, None), - (OracleSyncConfig, {"user": "system", "password": "pwd", "dsn": "localhost/XE"}, None), - (SqliteConfig, {"database": ":memory:"}, "database"), # Converts :memory: to URI - (AiosqliteConfig, {"database": ":memory:"}, "database"), # Converts :memory: to URI - (DuckDBConfig, {"database": ":memory:"}, "database"), # Converts :memory: to shared_db - (BigQueryConfig, {"project": "test-project"}, None), - (AdbcConfig, {"driver": "adbc_driver_sqlite"}, None), + adapters: list[tuple[Any, Any, str | None]] = [ + (AsyncpgConfig, AsyncpgPoolConfig(dsn="postgresql://localhost/test"), None), + (PsycopgAsyncConfig, PsycopgPoolParams(conninfo="postgresql://localhost/test"), None), + (PsycopgSyncConfig, PsycopgPoolParams(conninfo="postgresql://localhost/test"), None), + (AsyncmyConfig, AsyncmyPoolParams(host="localhost", database="test"), "port"), # Adds default port + (PsqlpyConfig, PsqlpyPoolParams(dsn="postgresql://localhost/test"), None), + (OracleAsyncConfig, OraclePoolParams(user="system", password="pwd", dsn="localhost/XE"), None), + (OracleSyncConfig, OraclePoolParams(user="system", password="pwd", dsn="localhost/XE"), None), + (SqliteConfig, SqliteConnectionParams(database=":memory:"), "database"), # Converts :memory: to URI + (AiosqliteConfig, AiosqlitePoolParams(database=":memory:"), "database"), # Converts :memory: to URI + (DuckDBConfig, DuckDBPoolParams(database=":memory:"), "database"), # Converts :memory: to shared_db + (BigQueryConfig, BigQueryConnectionParams(project="test-project"), None), + (AdbcConfig, cast(AdbcConnectionParams, {"driver": "adbc_driver_sqlite"}), None), (SpannerSyncConfig, {"instance_id": "test", "database_id": "test"}, None), ] - for adapter_class, config_dict, modified_key in adapters: - config = adapter_class(connection_config=config_dict) + for adapter_class, config_val, modified_key in adapters: + config = adapter_class(connection_config=config_val) assert hasattr(config, "connection_config") assert hasattr(config, "connection_instance") # Check that original keys are present (may be modified or have defaults added) - for key in config_dict: + for key in config_val: if key != modified_key: assert key in config.connection_config @@ -440,24 +442,24 @@ def test_connection_config_parameter_naming_consistency() -> None: def test_connection_instance_parameter_naming_consistency() -> None: """Test that all adapters use consistent connection_instance parameter name.""" - adapters = [ - (AsyncpgConfig, {"dsn": "postgresql://localhost/test"}), - (PsycopgAsyncConfig, {"conninfo": "postgresql://localhost/test"}), - (PsycopgSyncConfig, {"conninfo": "postgresql://localhost/test"}), - (AsyncmyConfig, {"host": "localhost", "database": "test"}), - (PsqlpyConfig, {"dsn": "postgresql://localhost/test"}), - (OracleAsyncConfig, {"user": "system", "password": "pwd", "dsn": "localhost/XE"}), - (OracleSyncConfig, {"user": "system", "password": "pwd", "dsn": "localhost/XE"}), - (SqliteConfig, {"database": ":memory:"}), - (AiosqliteConfig, {"database": ":memory:"}), - (DuckDBConfig, {"database": ":memory:"}), - (BigQueryConfig, {"project": "test-project"}), - (AdbcConfig, {"driver": "adbc_driver_sqlite"}), + adapters: list[tuple[Any, Any]] = [ + (AsyncpgConfig, AsyncpgPoolConfig(dsn="postgresql://localhost/test")), + (PsycopgAsyncConfig, PsycopgPoolParams(conninfo="postgresql://localhost/test")), + (PsycopgSyncConfig, PsycopgPoolParams(conninfo="postgresql://localhost/test")), + (AsyncmyConfig, AsyncmyPoolParams(host="localhost", database="test")), + (PsqlpyConfig, PsqlpyPoolParams(dsn="postgresql://localhost/test")), + (OracleAsyncConfig, OraclePoolParams(user="system", password="pwd", dsn="localhost/XE")), + (OracleSyncConfig, OraclePoolParams(user="system", password="pwd", dsn="localhost/XE")), + (SqliteConfig, SqliteConnectionParams(database=":memory:")), + (AiosqliteConfig, AiosqlitePoolParams(database=":memory:")), + (DuckDBConfig, DuckDBPoolParams(database=":memory:")), + (BigQueryConfig, BigQueryConnectionParams(project="test-project")), + (AdbcConfig, cast(AdbcConnectionParams, {"driver": "adbc_driver_sqlite"})), (SpannerSyncConfig, {"instance_id": "test", "database_id": "test"}), ] - for adapter_class, config_dict in adapters: - config = adapter_class(connection_config=config_dict, connection_instance=None) + for adapter_class, config_val in adapters: + config = adapter_class(connection_config=config_val, connection_instance=None) assert hasattr(config, "connection_instance") assert config.connection_instance is None @@ -470,7 +472,9 @@ async def test_asyncpg_config_with_pre_created_pool() -> None: mock_pool = MagicMock() mock_pool.acquire = AsyncMock() - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test"}, connection_instance=mock_pool) + config = AsyncpgConfig( + connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test"), connection_instance=mock_pool + ) assert config.connection_instance is mock_pool assert config.connection_config["dsn"] == "postgresql://localhost/test" @@ -483,7 +487,7 @@ def test_sqlite_config_with_pre_created_pool() -> None: # Create a mock pool mock_pool = MagicMock() - config = SqliteConfig(connection_config={"database": ":memory:"}, connection_instance=mock_pool) + config = SqliteConfig(connection_config=SqliteConnectionParams(database=":memory:"), connection_instance=mock_pool) assert config.connection_instance is mock_pool # SQLite converts :memory: to shared memory URI for pooling @@ -497,7 +501,7 @@ def test_duckdb_config_with_pre_created_pool() -> None: # Create a mock pool mock_pool = MagicMock() - config = DuckDBConfig(connection_config={"database": ":memory:"}, connection_instance=mock_pool) + config = DuckDBConfig(connection_config=DuckDBPoolParams(database=":memory:"), connection_instance=mock_pool) assert config.connection_instance is mock_pool # DuckDB converts :memory: to :memory:shared_db for pooling @@ -511,7 +515,9 @@ def test_bigquery_config_with_pre_created_client() -> None: # Create a mock client mock_client = MagicMock() - config = BigQueryConfig(connection_config={"project": "test-project"}, connection_instance=mock_client) + config = BigQueryConfig( + connection_config=BigQueryConnectionParams(project="test-project"), connection_instance=mock_client + ) assert config.connection_instance is mock_client assert config.connection_config["project"] == "test-project" @@ -523,7 +529,7 @@ def test_connection_instance_bypasses_pool_creation() -> None: mock_pool = MagicMock() - config = DuckDBConfig(connection_config={"database": ":memory:"}, connection_instance=mock_pool) + config = DuckDBConfig(connection_config=DuckDBPoolParams(database=":memory:"), connection_instance=mock_pool) # When connection_instance is set, _create_pool should not be called # and provide_pool should return the provided instance @@ -536,6 +542,6 @@ def test_connection_config_does_not_accept_invalid_types() -> None: """Test that connection_config validates type at runtime (if validation exists).""" # Note: SQLSpec uses TypedDict, so type validation happens at type-check time # At runtime, we just ensure dict assignment works - config = AsyncpgConfig(connection_config={"dsn": "postgresql://localhost/test"}) + config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(dsn="postgresql://localhost/test")) assert isinstance(config.connection_config, dict) diff --git a/tests/unit/core/test_filters.py b/tests/unit/core/test_filters.py index e1a8c1501..b9f8baec2 100644 --- a/tests/unit/core/test_filters.py +++ b/tests/unit/core/test_filters.py @@ -621,3 +621,241 @@ def test_search_filter_single_field_unchanged() -> None: assert positional == [] assert len(named) == 1 assert named["name_search"] == "%oracle%" + + +# --- Task 1.1: NotInCollectionFilter deterministic param names --- + + +def test_not_in_collection_filter_deterministic_param_names() -> None: + """Verify NotInCollectionFilter param names are deterministic (no id()).""" + f1 = NotInCollectionFilter("status", ["a", "b", "c"]) + f2 = NotInCollectionFilter("status", ["a", "b", "c"]) + + assert f1.get_param_names() == f2.get_param_names() + assert f1.get_param_names() == ["status_notin_0", "status_notin_1", "status_notin_2"] + + +def test_not_in_collection_filter_param_name_pattern() -> None: + """Verify param names follow {field}_notin_{i} pattern.""" + f = NotInCollectionFilter("col", ["x"]) + names = f.get_param_names() + assert names == ["col_notin_0"] + + +def test_not_in_collection_filter_extract_parameters_deterministic() -> None: + """Verify extract_parameters uses deterministic names.""" + f = NotInCollectionFilter("status", ["deleted", "archived"]) + _, named = f.extract_parameters() + assert named == {"status_notin_0": "deleted", "status_notin_1": "archived"} + + +# --- Task 1.2: SearchFilter ignore_case=None normalization --- + + +def test_search_filter_ignore_case_none_behaves_as_false() -> None: + """ignore_case=None should normalize to False and use exp.Like.""" + + f_none = SearchFilter("name", "test", ignore_case=None) + f_false = SearchFilter("name", "test", ignore_case=False) + + assert f_none.ignore_case is False + assert f_false.ignore_case is False + + sql_stmt = SQL("SELECT * FROM t") + result_none = apply_filter(sql_stmt, f_none) + result_false = apply_filter(sql_stmt, f_false) + + # Both should produce LIKE, not ILIKE + assert "ILIKE" not in result_none.sql.upper() + assert "LIKE" in result_none.sql.upper() + assert result_none.sql == result_false.sql + + +def test_search_filter_ignore_case_true_uses_ilike() -> None: + """ignore_case=True should use ILIKE.""" + f = SearchFilter("name", "test", ignore_case=True) + assert f.ignore_case is True + + sql_stmt = SQL("SELECT * FROM t") + result = apply_filter(sql_stmt, f) + assert "ILIKE" in result.sql.upper() + + +# --- Task 1.4: InCollectionFilter edge cases --- + + +def test_in_collection_filter_empty_collection_returns_false() -> None: + """Empty collection should add WHERE FALSE.""" + sql_stmt = SQL("SELECT * FROM t") + f: InCollectionFilter[str] = InCollectionFilter("status", []) + result = apply_filter(sql_stmt, f) + assert "FALSE" in result.sql.upper() + + +def test_in_collection_filter_none_values_returns_unchanged() -> None: + """None values should return statement unchanged.""" + sql_stmt = SQL("SELECT * FROM t") + f: InCollectionFilter[str] = InCollectionFilter("status", None) + result = apply_filter(sql_stmt, f) + assert result.sql == sql_stmt.sql + + +# --- Task 1.4: NotInCollectionFilter edge cases --- + + +def test_not_in_collection_filter_empty_values_returns_unchanged() -> None: + """Empty values should return statement unchanged.""" + sql_stmt = SQL("SELECT * FROM t") + f: NotInCollectionFilter[str] = NotInCollectionFilter("status", []) + result = apply_filter(sql_stmt, f) + assert result.sql == sql_stmt.sql + + +def test_not_in_collection_filter_none_values_returns_unchanged() -> None: + """None values should return statement unchanged.""" + sql_stmt = SQL("SELECT * FROM t") + f: NotInCollectionFilter[str] = NotInCollectionFilter("status", None) + result = apply_filter(sql_stmt, f) + assert result.sql == sql_stmt.sql + + +# --- Task 1.5: Parameter conflict resolution and compound filters --- + + +def test_two_in_filters_same_column_no_collision() -> None: + """Two InCollectionFilter instances on the same column should not collide.""" + sql_stmt = SQL("SELECT * FROM t") + f1 = InCollectionFilter("status", ["a", "b"]) + f2 = InCollectionFilter("status", ["c", "d"]) + + result = apply_filter(sql_stmt, f1) + result = apply_filter(result, f2) + + params = result.parameters + # First filter gets status_in_0, status_in_1 + assert params["status_in_0"] == "a" + assert params["status_in_1"] == "b" + # Second filter should get conflict-resolved names + conflict_keys = [k for k in params if k.startswith("status_in_0_") or k.startswith("status_in_1_")] + assert len(conflict_keys) == 2 + conflict_values = {params[k] for k in conflict_keys} + assert conflict_values == {"c", "d"} + + +def test_multiple_filters_sequential_compound_where() -> None: + """Multiple filters applied sequentially produce compound WHERE clauses.""" + sql_stmt = SQL("SELECT * FROM users") + + f1 = InCollectionFilter("status", ["active"]) + f2 = NotInCollectionFilter("role", ["admin"]) + f3 = SearchFilter("name", "john") + + result = apply_filter(sql_stmt, f1) + result = apply_filter(result, f2) + result = apply_filter(result, f3) + + sql_upper = result.sql.upper() + assert "WHERE" in sql_upper + assert "STATUS IN" in sql_upper + assert "NOT" in sql_upper + assert "ROLE" in sql_upper + assert "LIKE" in sql_upper + + params = result.parameters + assert params["status_in_0"] == "active" + assert params["role_notin_0"] == "admin" + assert params["name_search"] == "%john%" + + +# --- Task 2.4-2.5: QueryBuilder.apply_filters and prepare_statement with filters --- + + +def test_query_builder_apply_filters_in_collection() -> None: + """QueryBuilder.apply_filters applies InCollectionFilter correctly (issue #405).""" + from sqlspec.builder import Select + + builder = Select("*").from_("users") + f = InCollectionFilter("status", ["active", "pending"]) + + result = builder.apply_filters(f) + + sql_upper = result.sql.upper() + assert "WHERE" in sql_upper + assert "STATUS IN" in sql_upper + assert result.parameters["status_in_0"] == "active" + assert result.parameters["status_in_1"] == "pending" + + +def test_query_builder_apply_filters_multiple() -> None: + """QueryBuilder.apply_filters handles multiple filters (issue #405).""" + from sqlspec.builder import Select + + builder = Select("*").from_("users") + f1 = InCollectionFilter("status", ["active"]) + f2 = SearchFilter("name", "john") + f3 = LimitOffsetFilter(10, 0) + + result = builder.apply_filters(f1, f2, f3) + + sql_upper = result.sql.upper() + assert "STATUS IN" in sql_upper + assert "LIKE" in sql_upper + assert "LIMIT" in sql_upper + assert result.parameters["status_in_0"] == "active" + assert result.parameters["name_search"] == "%john%" + assert result.parameters["limit"] == 10 + + +def test_query_builder_apply_filters_not_in_collection() -> None: + """QueryBuilder.apply_filters applies NotInCollectionFilter correctly (issue #405).""" + from sqlspec.builder import Select + + builder = Select("*").from_("users") + f = NotInCollectionFilter("status", ["deleted", "archived"]) + + result = builder.apply_filters(f) + + sql_upper = result.sql.upper() + assert "WHERE" in sql_upper + assert "NOT" in sql_upper + assert "STATUS" in sql_upper + assert result.parameters["status_notin_0"] == "deleted" + assert result.parameters["status_notin_1"] == "archived" + + +def test_query_builder_apply_filters_empty() -> None: + """QueryBuilder.apply_filters with no filters returns unmodified SQL.""" + from sqlspec.builder import Select + + builder = Select("*").from_("users") + + result = builder.apply_filters() + + assert "WHERE" not in result.sql.upper() + + +def test_query_builder_apply_filters_produces_valid_sql_for_execution() -> None: + """QueryBuilder.apply_filters produces SQL that can be used with prepare_statement (issue #405). + + This verifies the end-to-end path: QueryBuilder -> apply_filters -> SQL with parameters. + """ + from sqlspec.builder import Select + + builder = Select("id", "name", "status").from_("users") + f1 = InCollectionFilter("status", ["active", "pending"]) + f2 = OrderByFilter("name", "asc") + f3 = LimitOffsetFilter(10, 0) + + result = builder.apply_filters(f1, f2, f3) + + sql_upper = result.sql.upper() + assert "SELECT" in sql_upper + assert "FROM USERS" in sql_upper.replace('"', "") + assert "STATUS IN" in sql_upper + assert "ORDER BY" in sql_upper + assert "LIMIT" in sql_upper + + assert result.parameters["status_in_0"] == "active" + assert result.parameters["status_in_1"] == "pending" + assert result.parameters["limit"] == 10 + assert result.parameters["offset"] == 0 diff --git a/tests/unit/core/test_introspection_sql.py b/tests/unit/core/test_introspection_sql.py new file mode 100644 index 000000000..8034853e6 --- /dev/null +++ b/tests/unit/core/test_introspection_sql.py @@ -0,0 +1,216 @@ +"""Tests for introspection SQL file integrity (GitHub issue #361). + +Verifies that all data dictionary SQL files: +- Load without parse errors +- Preserve PostgreSQL ``::type`` casts (not mangled to ``:: type`` or ``: type``) +- Keep ``:param`` bind parameters intact (no ``=: param`` with stray spaces) +- The parameter validator regex correctly distinguishes ``::text`` (cast) from ``:param`` (bind) +""" + +import re + +import pytest + +from sqlspec.core import SQL +from sqlspec.core.parameters._validator import PARAMETER_REGEX, ParameterValidator +from sqlspec.data_dictionary._loader import DataDictionaryLoader + +pytestmark = pytest.mark.xdist_group("core") + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +_SPACE_BEFORE_BIND = re.compile(r"=\s+:(?!:)\w+") +"""Detects ``= :param`` with extra whitespace that should be ``= :param`` +(one space is fine, but ``= :param`` or ``=\n:param`` is suspicious).""" + +_MANGLED_CAST = re.compile(r"::\s+\w+") +"""Detects ``:: text`` with a space inside the cast operator.""" + +_STRAY_SPACE_BIND = re.compile(r"(? list[tuple[str, str]]: + """Return (dialect, query_name) pairs for every loadable query.""" + loader = DataDictionaryLoader() + pairs: list[tuple[str, str]] = [] + for dialect in loader.list_dialects(): + # Load the dialect first to populate query list + try: + loader._ensure_dialect_loaded(dialect) + except Exception: + continue + inner = loader._get_loader(dialect) + pairs.extend((dialect, qname) for qname in inner.list_queries()) + return pairs + + +ALL_QUERIES = _get_all_dialect_queries() + + +# --------------------------------------------------------------------------- +# Task 4.1 / 4.3 — Load every SQL file, inspect raw text +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize(("dialect", "query_name"), ALL_QUERIES, ids=[f"{d}/{q}" for d, q in ALL_QUERIES]) +def test_raw_sql_has_no_mangled_casts(dialect: str, query_name: str) -> None: + """Raw SQL text must not contain ``:: text`` (space inside cast).""" + loader = DataDictionaryLoader() + raw = loader.get_query_text(dialect, query_name) + matches = _MANGLED_CAST.findall(raw) + assert not matches, f"Mangled cast(s) in {dialect}/{query_name}: {matches}" + + +@pytest.mark.parametrize(("dialect", "query_name"), ALL_QUERIES, ids=[f"{d}/{q}" for d, q in ALL_QUERIES]) +def test_raw_sql_has_no_stray_space_binds(dialect: str, query_name: str) -> None: + """Raw SQL must not have ``: param`` with a space after the colon.""" + loader = DataDictionaryLoader() + raw = loader.get_query_text(dialect, query_name) + matches = _STRAY_SPACE_BIND.findall(raw) + assert not matches, f"Stray space bind(s) in {dialect}/{query_name}: {matches}" + + +# --------------------------------------------------------------------------- +# Task 4.4 — Compile every SQL file, verify output integrity +# --------------------------------------------------------------------------- + + +@pytest.mark.parametrize(("dialect", "query_name"), ALL_QUERIES, ids=[f"{d}/{q}" for d, q in ALL_QUERIES]) +def test_sql_compiles_without_error(dialect: str, query_name: str) -> None: + """Every introspection query must compile through the SQL pipeline.""" + loader = DataDictionaryLoader() + sql_obj = loader.get_query(dialect, query_name) + assert isinstance(sql_obj, SQL) + compiled_sql, _params = sql_obj.compile() + assert isinstance(compiled_sql, str) + assert len(compiled_sql) > 0 + + +@pytest.mark.parametrize( + ("dialect", "query_name"), + [(d, q) for d, q in ALL_QUERIES if d in ("postgres", "cockroachdb")], + ids=[f"{d}/{q}" for d, q in ALL_QUERIES if d in ("postgres", "cockroachdb")], +) +def test_compiled_sql_preserves_pg_casts(dialect: str, query_name: str) -> None: + """Compiled SQL for PG-family dialects must preserve ``::type`` casts.""" + loader = DataDictionaryLoader() + raw = loader.get_query_text(dialect, query_name) + if "::" not in raw: + pytest.skip("No casts in this query") + + sql_obj = loader.get_query(dialect, query_name) + compiled_sql, _ = sql_obj.compile() + # Count casts in raw vs compiled — they should match + raw_casts = re.findall(r"::\w+", raw) + compiled_casts = re.findall(r"::\w+", compiled_sql) + # The compiled SQL may rewrite identifiers, but cast count must be stable + assert len(compiled_casts) >= len(raw_casts), ( + f"Casts lost in {dialect}/{query_name}: raw={len(raw_casts)}, compiled={len(compiled_casts)}" + ) + + +# --------------------------------------------------------------------------- +# Task 4.5 — Parameter validator regex with mixed casts and binds +# --------------------------------------------------------------------------- + + +def test_parameter_regex_distinguishes_cast_from_bind() -> None: + """``::text`` must match as ``pg_cast``, ``:param`` as ``named_colon``.""" + sql = "SELECT a.attname::text WHERE c.relname = :table_name AND n.nspname = :schema_name" + casts: list[str] = [] + binds: list[str] = [] + for m in PARAMETER_REGEX.finditer(sql): + if m.group("pg_cast"): + casts.append(m.group("pg_cast")) + elif m.group("named_colon"): + binds.append(m.group("named_colon")) + assert casts == ["::text"], f"Expected ['::text'], got {casts}" + assert sorted(binds) == [":schema_name", ":table_name"], f"Unexpected binds: {binds}" + + +def test_parameter_regex_adjacent_bind_and_cast() -> None: + """``:schema_name::text`` must yield a bind AND a cast, not a single token.""" + sql = "WHERE :schema_name::text IS NULL" + casts: list[str] = [] + binds: list[str] = [] + for m in PARAMETER_REGEX.finditer(sql): + if m.group("pg_cast"): + casts.append(m.group("pg_cast")) + elif m.group("named_colon"): + binds.append(m.group("named_colon")) + assert binds == [":schema_name"], f"Unexpected binds: {binds}" + assert casts == ["::text"], f"Unexpected casts: {casts}" + + +def test_parameter_validator_skips_pg_casts() -> None: + """ParameterValidator must not report ``::text`` as a named parameter.""" + validator = ParameterValidator() + sql = "SELECT a::text, b::int WHERE x = :foo AND :bar::text IS NULL" + params = validator.extract_parameters(sql) + param_names = [p.name for p in params] + assert "foo" in param_names + assert "bar" in param_names + # Cast type names must NOT appear as parameters + assert "text" not in param_names + assert "int" not in param_names + + +def test_parameter_validator_handles_postgres_introspection_sql() -> None: + """Validate the actual postgres columns_by_table query.""" + loader = DataDictionaryLoader() + raw = loader.get_query_text("postgres", "columns_by_table") + validator = ParameterValidator() + params = validator.extract_parameters(raw) + param_names = [p.name for p in params] + assert "table_name" in param_names + assert "schema_name" in param_names + # Cast types should NOT be detected as parameters + assert "text" not in param_names + + +def test_parameter_validator_handles_mixed_cast_bind_pattern() -> None: + """Validate the postgres foreign_keys pattern with ``:param::text``.""" + loader = DataDictionaryLoader() + raw = loader.get_query_text("postgres", "foreign_keys_by_table") + validator = ParameterValidator() + params = validator.extract_parameters(raw) + param_names = [p.name for p in params] + assert "schema_name" in param_names + assert "table_name" in param_names + # ::text cast must not leak as a parameter + assert "text" not in param_names + + +def test_compiled_sql_no_mangled_casts_or_binds() -> None: + """Compiled SQL for postgres columns_by_table must not mangle casts or binds. + + This is the exact scenario from GitHub issue #361 where ``c.relname = :table_name`` + was allegedly being transformed to ``c.relname =: table_name``. + """ + loader = DataDictionaryLoader() + sql_obj = loader.get_query("postgres", "columns_by_table") + compiled_sql, _ = sql_obj.compile() + # Must not have mangled casts like ":: text" + assert ":: text" not in compiled_sql, f"Mangled cast found in compiled SQL: {compiled_sql}" + # Must not have "=:" (equals-colon without space then param name) + # that differs from "= :param" — the issue was "=: param" with wrong spacing + bad = re.findall(r"=:\s+\w+", compiled_sql) + assert not bad, f"Found '=: param' pattern in compiled SQL: {bad}" + + +def test_compiled_foreign_keys_preserves_cast_bind_adjacency() -> None: + """``(:schema_name::text IS NULL ...)`` must compile without mangling. + + The ``::text`` cast immediately after ``:schema_name`` is the trickiest pattern. + """ + loader = DataDictionaryLoader() + sql_obj = loader.get_query("postgres", "foreign_keys_by_table") + compiled_sql, _ = sql_obj.compile() + # The cast must survive compilation + assert "::text" in compiled_sql or ":: TEXT" in compiled_sql or "CAST(" in compiled_sql, ( + f"Cast lost in compiled foreign_keys_by_table: {compiled_sql}" + ) diff --git a/tests/unit/dialects/test_generators.py b/tests/unit/dialects/test_generators.py new file mode 100644 index 000000000..fe92573e8 --- /dev/null +++ b/tests/unit/dialects/test_generators.py @@ -0,0 +1,107 @@ +"""Tests for dialect generator rendering with sqlglot[c] compatibility. + +Verifies that the conditional TRANSFORMS/subclass approach in _generators.py +produces correct SQL output for all extension operators and Spanner DDL. +""" + +from sqlglot import parse_one + +import sqlspec.dialects # noqa: F401 + +# --------------------------------------------------------------------------- +# PGVector operator rendering +# --------------------------------------------------------------------------- + + +def test_pgvector_l2_distance() -> None: + sql = "SELECT embedding <-> '[1,2,3]' FROM items" + rendered = parse_one(sql, dialect="pgvector").sql(dialect="pgvector") + assert "<->" in rendered + + +def test_pgvector_cosine_distance() -> None: + sql = "SELECT embedding <=> '[1,2,3]' FROM items" + rendered = parse_one(sql, dialect="pgvector").sql(dialect="pgvector") + assert "<=>" in rendered + + +def test_pgvector_inner_product_distance() -> None: + sql = "SELECT embedding <#> '[1,2,3]' FROM items" + rendered = parse_one(sql, dialect="pgvector").sql(dialect="pgvector") + assert "<#>" in rendered + + +# --------------------------------------------------------------------------- +# ParadeDB operator rendering +# --------------------------------------------------------------------------- + + +def test_paradedb_bm25_search() -> None: + sql = "SELECT * FROM items WHERE description @@@ 'shoes'" + rendered = parse_one(sql, dialect="paradedb").sql(dialect="paradedb") + assert "@@@" in rendered + + +def test_paradedb_inherits_pgvector_operators() -> None: + sql = "SELECT embedding <=> '[1,2,3]' FROM items" + rendered = parse_one(sql, dialect="paradedb").sql(dialect="paradedb") + assert "<=>" in rendered + + +# --------------------------------------------------------------------------- +# Spanner DDL rendering +# --------------------------------------------------------------------------- + + +def test_spanner_interleave_in_parent() -> None: + sql = """ + CREATE TABLE child ( + parent_id STRING(36), + child_id INT64, + PRIMARY KEY (parent_id, child_id) + ) INTERLEAVE IN PARENT parent_table ON DELETE CASCADE + """ + rendered = parse_one(sql, dialect="spanner").sql(dialect="spanner") + assert "INTERLEAVE IN PARENT parent_table ON DELETE CASCADE" in rendered + + +def test_spanner_row_deletion_policy() -> None: + sql = """ + CREATE TABLE logs ( + id STRING(36), + created_at TIMESTAMP, + PRIMARY KEY (id) + ) ROW DELETION POLICY (OLDER_THAN(created_at, INTERVAL 30 DAY)) + """ + rendered = parse_one(sql, dialect="spanner").sql(dialect="spanner") + assert "ROW DELETION POLICY" in rendered + assert "OLDER_THAN(created_at" in rendered + + +def test_spanner_ttl() -> None: + sql = """ + CREATE TABLE ttl_table ( + id INT64, + expires_at TIMESTAMP, + PRIMARY KEY (id) + ) TTL INTERVAL '5 days' ON expires_at + """ + rendered = parse_one(sql, dialect="spanner").sql(dialect="spanner") + assert "TTL INTERVAL" in rendered + assert "expires_at" in rendered + + +# --------------------------------------------------------------------------- +# Spangres DDL rendering +# --------------------------------------------------------------------------- + + +def test_spangres_row_deletion_policy() -> None: + sql = """ + CREATE TABLE events ( + id VARCHAR PRIMARY KEY, + created_at TIMESTAMP + ) ROW DELETION POLICY (OLDER_THAN(created_at, INTERVAL '30 days')) + """ + rendered = parse_one(sql, dialect="spangres").sql(dialect="spangres") + assert "ROW DELETION POLICY (OLDER_THAN(created_at, INTERVAL '30 days'))" in rendered diff --git a/tests/unit/exceptions/test_exception_handler.py b/tests/unit/exceptions/test_exception_handler.py index 5dc0dd626..873699b2d 100644 --- a/tests/unit/exceptions/test_exception_handler.py +++ b/tests/unit/exceptions/test_exception_handler.py @@ -127,3 +127,297 @@ class RetryableError(RuntimeError): assert await handler.__aexit__(type(error), error, None) is True assert isinstance(handler.pending_exception, SerializationConflictError) + + +# ───────────────────────────────────────────────────────────────────────────── +# SQLSpecError pass-through guard tests +# ───────────────────────────────────────────────────────────────────────────── + + +def test_sync_handler_does_not_remap_sqlspec_error() -> None: + """Passing an already-mapped SQLSpecError through a sync handler should NOT re-map it.""" + from sqlspec.exceptions import IntegrityError + + handler = BaseSyncExceptionHandler() + original = IntegrityError("already mapped") + result = handler.__exit__(type(original), original, None) + assert result is False + assert handler.pending_exception is None + + +@pytest.mark.anyio +async def test_async_handler_does_not_remap_sqlspec_error() -> None: + """Passing an already-mapped SQLSpecError through an async handler should NOT re-map it.""" + from sqlspec.exceptions import UniqueViolationError + + handler = BaseAsyncExceptionHandler() + original = UniqueViolationError("already mapped") + result = await handler.__aexit__(type(original), original, None) + assert result is False + assert handler.pending_exception is None + + +def test_sync_adapter_handler_does_not_remap_sqlspec_error() -> None: + """Adapter-level sync handlers should also pass through SQLSpecError unchanged.""" + from sqlspec.adapters.sqlite.driver import SqliteExceptionHandler + from sqlspec.exceptions import ForeignKeyViolationError + + handler = SqliteExceptionHandler() + original = ForeignKeyViolationError("already mapped") + result = handler.__exit__(type(original), original, None) + assert result is False + assert handler.pending_exception is None + + +@pytest.mark.anyio +async def test_async_adapter_handler_does_not_remap_sqlspec_error() -> None: + """Adapter-level async handlers should also pass through SQLSpecError unchanged.""" + from sqlspec.adapters.aiosqlite.driver import AiosqliteExceptionHandler + from sqlspec.exceptions import CheckViolationError + + handler = AiosqliteExceptionHandler() + original = CheckViolationError("already mapped") + result = await handler.__aexit__(type(original), original, None) + assert result is False + assert handler.pending_exception is None + + +# ───────────────────────────────────────────────────────────────────────────── +# Adapter exception mapping tests (synthetic — no real DB connections) +# ───────────────────────────────────────────────────────────────────────────── + + +def test_sqlite_handler_maps_integrity_error() -> None: + """SQLite handler should map sqlite3.IntegrityError to a SQLSpecError.""" + import sqlite3 + + from sqlspec.adapters.sqlite.driver import SqliteExceptionHandler + from sqlspec.exceptions import SQLSpecError + + handler = SqliteExceptionHandler() + error = sqlite3.IntegrityError("UNIQUE constraint failed: users.email") + result = handler.__exit__(type(error), error, None) + assert result is True + assert handler.pending_exception is not None + assert isinstance(handler.pending_exception, SQLSpecError) + + +def test_sqlite_handler_maps_operational_error() -> None: + """SQLite handler should map sqlite3.OperationalError.""" + import sqlite3 + + from sqlspec.adapters.sqlite.driver import SqliteExceptionHandler + from sqlspec.exceptions import SQLSpecError + + handler = SqliteExceptionHandler() + error = sqlite3.OperationalError("no such table: users") + result = handler.__exit__(type(error), error, None) + assert result is True + assert handler.pending_exception is not None + assert isinstance(handler.pending_exception, SQLSpecError) + + +def test_sqlite_handler_ignores_non_sqlite_errors() -> None: + """SQLite handler should not map non-sqlite3 errors.""" + from sqlspec.adapters.sqlite.driver import SqliteExceptionHandler + + handler = SqliteExceptionHandler() + error = ValueError("not a db error") + result = handler.__exit__(type(error), error, None) + assert result is False + assert handler.pending_exception is None + + +@pytest.mark.anyio +async def test_aiosqlite_handler_maps_sqlite_error() -> None: + """AIOSQLite handler should map sqlite3.Error variants.""" + import sqlite3 + + from sqlspec.adapters.aiosqlite.driver import AiosqliteExceptionHandler + from sqlspec.exceptions import SQLSpecError + + handler = AiosqliteExceptionHandler() + error = sqlite3.IntegrityError("UNIQUE constraint failed") + result = await handler.__aexit__(type(error), error, None) + assert result is True + assert handler.pending_exception is not None + assert isinstance(handler.pending_exception, SQLSpecError) + + +def test_duckdb_handler_maps_constraint_exception(monkeypatch: pytest.MonkeyPatch) -> None: + """DuckDB handler should map constraint errors to specific violation types.""" + pytest.importorskip("duckdb") + from sqlspec.adapters.duckdb import core as duckdb_core + from sqlspec.adapters.duckdb.driver import DuckDBExceptionHandler + from sqlspec.exceptions import UniqueViolationError + + def fake_create_mapped_exception(exc_type: "type[BaseException]", exc_val: "BaseException") -> Exception: + return UniqueViolationError(str(exc_val)) + + monkeypatch.setattr(duckdb_core, "create_mapped_exception", fake_create_mapped_exception) + # Also patch the driver module's reference + from sqlspec.adapters.duckdb import driver as duckdb_driver + + monkeypatch.setattr(duckdb_driver, "create_mapped_exception", fake_create_mapped_exception) + + handler = DuckDBExceptionHandler() + error = RuntimeError("unique constraint violation") + result = handler.__exit__(type(error), error, None) + assert result is True + assert isinstance(handler.pending_exception, UniqueViolationError) + + +def test_psycopg_sync_handler_maps_psycopg_error(monkeypatch: pytest.MonkeyPatch) -> None: + """psycopg sync handler should map psycopg.Error to SQLSpecError.""" + psycopg = pytest.importorskip("psycopg") + from sqlspec.adapters.psycopg import driver as psycopg_driver + from sqlspec.adapters.psycopg.driver import PsycopgSyncExceptionHandler + from sqlspec.exceptions import SQLSpecError + + sentinel = SQLSpecError("mapped") + monkeypatch.setattr(psycopg_driver, "create_mapped_exception", lambda exc_val: sentinel) + + handler = PsycopgSyncExceptionHandler() + error = psycopg.Error("test error") + result = handler.__exit__(type(error), error, None) + assert result is True + assert handler.pending_exception is sentinel + + +@pytest.mark.anyio +async def test_psycopg_async_handler_maps_psycopg_error(monkeypatch: pytest.MonkeyPatch) -> None: + """psycopg async handler should map psycopg.Error to SQLSpecError.""" + psycopg = pytest.importorskip("psycopg") + from sqlspec.adapters.psycopg import driver as psycopg_driver + from sqlspec.adapters.psycopg.driver import PsycopgAsyncExceptionHandler + from sqlspec.exceptions import SQLSpecError + + sentinel = SQLSpecError("mapped") + monkeypatch.setattr(psycopg_driver, "create_mapped_exception", lambda exc_val: sentinel) + + handler = PsycopgAsyncExceptionHandler() + error = psycopg.Error("test error") + result = await handler.__aexit__(type(error), error, None) + assert result is True + assert handler.pending_exception is sentinel + + +@pytest.mark.anyio +async def test_asyncpg_handler_maps_postgres_error(monkeypatch: pytest.MonkeyPatch) -> None: + """asyncpg handler should map asyncpg.PostgresError to SQLSpecError.""" + pytest.importorskip("asyncpg") + from sqlspec.adapters.asyncpg import driver as asyncpg_driver + from sqlspec.adapters.asyncpg.driver import AsyncpgExceptionHandler + from sqlspec.exceptions import IntegrityError + + sentinel = IntegrityError("mapped") + monkeypatch.setattr(asyncpg_driver, "create_mapped_exception", lambda exc_val: sentinel) + monkeypatch.setattr(asyncpg_driver, "has_sqlstate", lambda exc: True) + + error = RuntimeError("postgres-like error") + error.sqlstate = "23505" # type: ignore[attr-defined] + handler = AsyncpgExceptionHandler() + result = await handler.__aexit__(type(error), error, None) + assert result is True + assert handler.pending_exception is sentinel + + +def test_oracledb_sync_handler_maps_database_error(monkeypatch: pytest.MonkeyPatch) -> None: + """oracledb sync handler should map oracledb.DatabaseError.""" + oracledb = pytest.importorskip("oracledb") + from sqlspec.adapters.oracledb import driver as oracledb_driver + from sqlspec.adapters.oracledb.driver import OracleSyncExceptionHandler + from sqlspec.exceptions import SQLSpecError + + sentinel = SQLSpecError("mapped") + monkeypatch.setattr(oracledb_driver, "create_mapped_exception", lambda exc_val: sentinel) + + handler = OracleSyncExceptionHandler() + error = oracledb.DatabaseError("ORA-00001: unique constraint violated") + result = handler.__exit__(type(error), error, None) + assert result is True + assert handler.pending_exception is sentinel + + +@pytest.mark.anyio +async def test_oracledb_async_handler_maps_database_error(monkeypatch: pytest.MonkeyPatch) -> None: + """oracledb async handler should map oracledb.DatabaseError.""" + oracledb = pytest.importorskip("oracledb") + from sqlspec.adapters.oracledb import driver as oracledb_driver + from sqlspec.adapters.oracledb.driver import OracleAsyncExceptionHandler + from sqlspec.exceptions import SQLSpecError + + sentinel = SQLSpecError("mapped") + monkeypatch.setattr(oracledb_driver, "create_mapped_exception", lambda exc_val: sentinel) + + handler = OracleAsyncExceptionHandler() + error = oracledb.DatabaseError("ORA-00001: unique constraint violated") + result = await handler.__aexit__(type(error), error, None) + assert result is True + assert handler.pending_exception is sentinel + + +def test_handler_none_exc_val_returns_false() -> None: + """All handlers should return False when exc_val is None (no exception).""" + handler = BaseSyncExceptionHandler() + assert handler.__exit__(None, None, None) is False + + +@pytest.mark.anyio +async def test_async_handler_none_exc_val_returns_false() -> None: + """All async handlers should return False when exc_val is None.""" + handler = BaseAsyncExceptionHandler() + assert await handler.__aexit__(None, None, None) is False + + +# ───────────────────────────────────────────────────────────────────────────── +# _check_pending_exception helper tests +# ───────────────────────────────────────────────────────────────────────────── + + +def test_check_pending_exception_raises_when_set() -> None: + """_check_pending_exception should raise the pending exception from None.""" + from sqlspec.exceptions import UniqueViolationError + + handler = BaseSyncExceptionHandler() + handler.pending_exception = UniqueViolationError("duplicate key") + + from sqlspec.driver._sync import SyncDriverAdapterBase + + with pytest.raises(UniqueViolationError, match="duplicate key"): + SyncDriverAdapterBase._check_pending_exception(handler) + + +def test_check_pending_exception_noop_when_none() -> None: + """_check_pending_exception should be a no-op when pending_exception is None.""" + handler = BaseSyncExceptionHandler() + assert handler.pending_exception is None + + from sqlspec.driver._sync import SyncDriverAdapterBase + + SyncDriverAdapterBase._check_pending_exception(handler) + + +@pytest.mark.anyio +async def test_async_check_pending_exception_raises_when_set() -> None: + """Async _check_pending_exception should raise the pending exception from None.""" + from sqlspec.exceptions import ForeignKeyViolationError + + handler = BaseAsyncExceptionHandler() + handler.pending_exception = ForeignKeyViolationError("fk violation") + + from sqlspec.driver._async import AsyncDriverAdapterBase + + with pytest.raises(ForeignKeyViolationError, match="fk violation"): + AsyncDriverAdapterBase._check_pending_exception(handler) + + +@pytest.mark.anyio +async def test_async_check_pending_exception_noop_when_none() -> None: + """Async _check_pending_exception should be a no-op when pending_exception is None.""" + handler = BaseAsyncExceptionHandler() + assert handler.pending_exception is None + + from sqlspec.driver._async import AsyncDriverAdapterBase + + AsyncDriverAdapterBase._check_pending_exception(handler) diff --git a/tests/unit/extensions/test_fastapi/test_providers.py b/tests/unit/extensions/test_fastapi/test_providers.py index c83ff913b..835d19a29 100644 --- a/tests/unit/extensions/test_fastapi/test_providers.py +++ b/tests/unit/extensions/test_fastapi/test_providers.py @@ -326,3 +326,45 @@ def test_provide_filters_search_without_value_excluded() -> None: # SearchFilter with None value should be excluded filters = provider(search_filter=SearchFilter(field_name={"name"}, value=None, ignore_case=False)) # type: ignore[arg-type] assert filters == [] + + +def test_provide_filters_in_fields_with_string_list() -> None: + """Test in_fields config with list of strings (issue #405).""" + config: FilterConfig = {"in_fields": [FieldNameType(name="status", type_hint=str)]} # type: ignore[typeddict-item] + provider = provide_filters(config) + + filters = provider(status_in_filter=InCollectionFilter(field_name="status", values=["active"])) + assert len(filters) == 1 + assert isinstance(filters[0], InCollectionFilter) + assert filters[0].field_name == "status" + + +def test_provide_filters_null_fields() -> None: + """Test null_fields filter generation.""" + from sqlspec.core import NullFilter + + config: FilterConfig = {"null_fields": {"email"}} + provider = provide_filters(config) + + # No filter + filters = provider(email_null_filter=None) + assert filters == [] + + # With filter + filters = provider(email_null_filter=NullFilter(field_name="email")) + assert len(filters) == 1 + assert isinstance(filters[0], NullFilter) + assert filters[0].field_name == "email" + + +def test_provide_filters_not_null_fields() -> None: + """Test not_null_fields filter generation.""" + from sqlspec.core import NotNullFilter + + config: FilterConfig = {"not_null_fields": {"email"}} + provider = provide_filters(config) + + filters = provider(email_not_null_filter=NotNullFilter(field_name="email")) + assert len(filters) == 1 + assert isinstance(filters[0], NotNullFilter) + assert filters[0].field_name == "email" diff --git a/tests/unit/extensions/test_litestar/test_providers.py b/tests/unit/extensions/test_litestar/test_providers.py new file mode 100644 index 000000000..e5304abbf --- /dev/null +++ b/tests/unit/extensions/test_litestar/test_providers.py @@ -0,0 +1,122 @@ +"""Unit tests for Litestar filter providers (issue #405).""" + +from typing import Any + +import pytest + +from sqlspec.core import InCollectionFilter, NotInCollectionFilter +from sqlspec.extensions.litestar.providers import ( + FieldNameType, + _create_filter_aggregate_function, + _create_statement_filters, + dep_cache, +) +from sqlspec.typing import LITESTAR_INSTALLED + +if not LITESTAR_INSTALLED: + pytest.skip("Litestar not installed", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def _clear_dependency_cache() -> Any: + dep_cache.dependencies.clear() + yield + dep_cache.dependencies.clear() + + +def test_in_fields_creates_provider_and_aggregate_param() -> None: + """in_fields config creates both individual provider and aggregate parameter.""" + config: dict[str, Any] = {"in_fields": {FieldNameType(name="status", type_hint=str)}} + deps = _create_statement_filters(config) + + assert "status_in_filter" in deps + assert "filters" in deps + + +def test_in_fields_provider_returns_filter_when_values_present() -> None: + """Individual in_fields provider returns InCollectionFilter when values are given.""" + config: dict[str, Any] = {"in_fields": {FieldNameType(name="status", type_hint=str)}} + deps = _create_statement_filters(config) + + provider = deps["status_in_filter"] + result = provider.dependency(values=["active", "archived"]) + + assert isinstance(result, InCollectionFilter) + assert result.field_name == "status" + assert list(result.values) == ["active", "archived"] # type: ignore[arg-type] + + +def test_in_fields_provider_returns_none_when_no_values() -> None: + """Individual in_fields provider returns None when values is None.""" + config: dict[str, Any] = {"in_fields": {FieldNameType(name="status", type_hint=str)}} + deps = _create_statement_filters(config) + + provider = deps["status_in_filter"] + result = provider.dependency(values=None) + assert result is None + + +def test_not_in_fields_provider_returns_filter_when_values_present() -> None: + """Individual not_in_fields provider returns NotInCollectionFilter when values are given.""" + config: dict[str, Any] = {"not_in_fields": {FieldNameType(name="status", type_hint=str)}} + deps = _create_statement_filters(config) + + provider = deps["status_not_in_filter"] + result = provider.dependency(values=["deleted", "archived"]) + + assert isinstance(result, NotInCollectionFilter) + assert result.field_name == "status" + assert list(result.values) == ["deleted", "archived"] # type: ignore[arg-type] + + +def test_aggregate_function_includes_in_filter() -> None: + """Aggregate function includes InCollectionFilter in results.""" + config: dict[str, Any] = {"in_fields": {FieldNameType(name="status", type_hint=str)}} + agg_fn = _create_filter_aggregate_function(config) + + in_filter = InCollectionFilter(field_name="status", values=["active", "pending"]) + result = agg_fn(status_in_filter=in_filter) + + assert len(result) == 1 + assert isinstance(result[0], InCollectionFilter) + assert result[0].field_name == "status" + + +def test_aggregate_function_excludes_none_in_filter() -> None: + """Aggregate function excludes None in_filter values.""" + config: dict[str, Any] = {"in_fields": {FieldNameType(name="status", type_hint=str)}} + agg_fn = _create_filter_aggregate_function(config) + + result = agg_fn(status_in_filter=None) + assert result == [] + + +def test_aggregate_function_multiple_in_fields() -> None: + """Aggregate function handles multiple in_fields.""" + config: dict[str, Any] = { + "in_fields": {FieldNameType(name="status", type_hint=str), FieldNameType(name="role", type_hint=str)} + } + agg_fn = _create_filter_aggregate_function(config) + + status_filter = InCollectionFilter(field_name="status", values=["active"]) + role_filter = InCollectionFilter(field_name="role", values=["admin", "user"]) + + result = agg_fn(status_in_filter=status_filter, role_in_filter=role_filter) + + assert len(result) == 2 + field_names = {f.field_name for f in result} + assert field_names == {"status", "role"} + + +def test_in_fields_with_string_config() -> None: + """in_fields config with list of strings works correctly.""" + config: dict[str, Any] = {"in_fields": ["status"]} + deps = _create_statement_filters(config) + + assert "status_in_filter" in deps + assert "filters" in deps + + provider = deps["status_in_filter"] + result = provider.dependency(values=["active"]) + assert isinstance(result, InCollectionFilter) + assert result.field_name == "status" diff --git a/uv.lock b/uv.lock index 10b797f1f..9f1f3835a 100644 --- a/uv.lock +++ b/uv.lock @@ -1011,7 +1011,7 @@ wheels = [ [[package]] name = "click-extra" -version = "7.8.0" +version = "7.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "boltons" }, @@ -1024,9 +1024,9 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "wcmatch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/cd/65d46aed5d64524e47fe39b2d6119e9295d23dbe65916ca64cd0f26efb05/click_extra-7.8.0.tar.gz", hash = "sha256:235ac590e893c6eee88ecbc22500c5f7d24ed5fdfbed7824dfb8d730a0759dc0", size = 107942, upload-time = "2026-03-09T16:08:02.409Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/12/6556edabd00a314de19573b4fee7a1441b2bc1258c143c5bab2fe556057a/click_extra-7.9.0.tar.gz", hash = "sha256:272e0063b94642d9ddb98e968c4b18bbcc692adeb45f5ef02825792aa3a2e04e", size = 113436, upload-time = "2026-03-31T14:47:24.54Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/04/6c0760d6eb91c944d4367d6775f7eb53ddf059a897cdd7325eb2f5813c3f/click_extra-7.8.0-py3-none-any.whl", hash = "sha256:31de6766c9460929bf944ba72d9b06784d93873189536a938fe05765b4af4a77", size = 121950, upload-time = "2026-03-09T16:08:00.991Z" }, + { url = "https://files.pythonhosted.org/packages/d9/1b/73473d4f99793a846a1d7a71cd799d462a97aac455b96a6ae43010856a23/click_extra-7.9.0-py3-none-any.whl", hash = "sha256:a2ddc2a9017bd132f13e003b4d85381b280f93e65d22c2cb5962af7bbbcc164c", size = 127329, upload-time = "2026-03-31T14:47:23.068Z" }, ] [package.optional-dependencies] @@ -1921,7 +1921,7 @@ wheels = [ [[package]] name = "google-api-core" -version = "2.30.0" +version = "2.30.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-auth" }, @@ -1930,9 +1930,9 @@ dependencies = [ { name = "protobuf" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/98/586ec94553b569080caef635f98a3723db36a38eac0e3d7eb3ea9d2e4b9a/google_api_core-2.30.0.tar.gz", hash = "sha256:02edfa9fab31e17fc0befb5f161b3bf93c9096d99aed584625f38065c511ad9b", size = 176959, upload-time = "2026-02-18T20:28:11.926Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/0b/b6e296aff70bef900766934cf4e83eaacc3f244adb61936b66d24b204080/google_api_core-2.30.1.tar.gz", hash = "sha256:7304ef3bd7e77fd26320a36eeb75868f9339532bfea21694964f4765b37574ee", size = 176742, upload-time = "2026-03-30T22:50:52.637Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/27/09c33d67f7e0dcf06d7ac17d196594e66989299374bfb0d4331d1038e76b/google_api_core-2.30.0-py3-none-any.whl", hash = "sha256:80be49ee937ff9aba0fd79a6eddfde35fe658b9953ab9b79c57dd7061afa8df5", size = 173288, upload-time = "2026-02-18T20:28:10.367Z" }, + { url = "https://files.pythonhosted.org/packages/43/86/a00ea4596780ef3f0721c1f073c0c5ae992da4f35cf12f0d8c92d19267a6/google_api_core-2.30.1-py3-none-any.whl", hash = "sha256:3be893babbb54a89c6807b598383ddf212112130e3d24d06c681b5d18f082e08", size = 173238, upload-time = "2026-03-30T22:48:50.586Z" }, ] [package.optional-dependencies] @@ -1980,15 +1980,15 @@ requests = [ [[package]] name = "google-auth-httplib2" -version = "0.3.0" +version = "0.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-auth" }, { name = "httplib2" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d5/ad/c1f2b1175096a8d04cf202ad5ea6065f108d26be6fc7215876bde4a7981d/google_auth_httplib2-0.3.0.tar.gz", hash = "sha256:177898a0175252480d5ed916aeea183c2df87c1f9c26705d74ae6b951c268b0b", size = 11134, upload-time = "2025-12-15T22:13:51.825Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/99/107612bef8d24b298bb5a7c8466f908ecda791d43f9466f5c3978f5b24c1/google_auth_httplib2-0.3.1.tar.gz", hash = "sha256:0af542e815784cb64159b4469aa5d71dd41069ba93effa006e1916b1dcd88e55", size = 11152, upload-time = "2026-03-30T22:50:26.766Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/d5/3c97526c8796d3caf5f4b3bed2b05e8a7102326f00a334e7a438237f3b22/google_auth_httplib2-0.3.0-py3-none-any.whl", hash = "sha256:426167e5df066e3f5a0fc7ea18768c08e7296046594ce4c8c409c2457dd1f776", size = 9529, upload-time = "2025-12-15T22:13:51.048Z" }, + { url = "https://files.pythonhosted.org/packages/97/e9/93afb14d23a949acaa3f4e7cc51a0024671174e116e35f42850764b99634/google_auth_httplib2-0.3.1-py3-none-any.whl", hash = "sha256:682356a90ef4ba3d06548c37e9112eea6fc00395a11b0303a644c1a86abc275c", size = 9534, upload-time = "2026-03-30T22:49:03.384Z" }, ] [[package]] @@ -2032,7 +2032,7 @@ agent-engines = [ [[package]] name = "google-cloud-alloydb" -version = "0.8.0" +version = "0.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -2042,9 +2042,9 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/4d/f79470b4ece1b46c423ea6529bcd96447af71c573052b6a21cc32cec62ec/google_cloud_alloydb-0.8.0.tar.gz", hash = "sha256:a8e5fb108a94b0eeea0634706cb4ff335cce82b56d2f66cc86951b9ab8322a92", size = 696060, upload-time = "2026-03-06T21:53:02.274Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/31/920111700f32dfc3fb12d41cac58cc68973b55d479dc299222cda58c0c4e/google_cloud_alloydb-0.9.0.tar.gz", hash = "sha256:c9339e0bc7fdc9968d9b4901c74bd328ac6d79fe6bbcd87ca7d97971271ca5dd", size = 700898, upload-time = "2026-03-30T22:51:24.508Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/3b/a5eafcf6a6e52fd56f5b1160d9c44eef33834f1c498bf8457c46a0c501e7/google_cloud_alloydb-0.8.0-py3-none-any.whl", hash = "sha256:545504c7cd1944f80665e0a14ac97ecdea2f8afd9052c412ab7a9b99787a0f1c", size = 533411, upload-time = "2026-03-06T21:52:32.36Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ea/2da8703ff5dce4e4d2319b8e046adb67cd311693818728dd16d6a6e527fd/google_cloud_alloydb-0.9.0-py3-none-any.whl", hash = "sha256:ee1e69a752fd26d36f5db686a71f8886f0c22b6b88b213f7e265c45568f766f4", size = 536940, upload-time = "2026-03-30T22:49:09.212Z" }, ] [[package]] @@ -2067,7 +2067,7 @@ wheels = [ [[package]] name = "google-cloud-appengine-logging" -version = "1.8.0" +version = "1.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -2076,27 +2076,27 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/65/38/89317773c64b5a7e9b56b9aecb2e39ac02d8d6d09fb5b276710c6892e690/google_cloud_appengine_logging-1.8.0.tar.gz", hash = "sha256:84b705a69e4109fc2f68dfe36ce3df6a34d5c3d989eee6d0ac1b024dda0ba6f5", size = 18071, upload-time = "2026-01-15T13:14:40.024Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/02/800897064ca6f1a26835cdf23939c4b93e38a30f3fb5c7cec7c01ae2edc2/google_cloud_appengine_logging-1.9.0.tar.gz", hash = "sha256:ff397f0bbc1485f979ab45767c38e0f676c9598c97c384f7412216e6ea22f805", size = 17963, upload-time = "2026-03-30T22:51:33.556Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/66/4a9be8afb1d0bf49472478cec20fefe4f4cb3a6e67be2231f097041e7339/google_cloud_appengine_logging-1.8.0-py3-none-any.whl", hash = "sha256:a4ce9ce94a9fd8c89ed07fa0b06fcf9ea3642f9532a1be1a8c7b5f82c0a70ec6", size = 18380, upload-time = "2026-01-09T14:52:58.154Z" }, + { url = "https://files.pythonhosted.org/packages/56/4a/304d42664ab2afbe7be39559c9eb3f81dd06e7ac9284f9f36f726f15939d/google_cloud_appengine_logging-1.9.0-py3-none-any.whl", hash = "sha256:bbf3a7e4dc171678f7f481259d1f68c3ae7d337530f1f2361f8a0b214dbcfe36", size = 18333, upload-time = "2026-03-30T22:49:39.045Z" }, ] [[package]] name = "google-cloud-audit-log" -version = "0.4.0" +version = "0.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c7/d2/ad96950410f8a05e921a6da2e1a6ba4aeca674bbb5dda8200c3c7296d7ad/google_cloud_audit_log-0.4.0.tar.gz", hash = "sha256:8467d4dcca9f3e6160520c24d71592e49e874838f174762272ec10e7950b6feb", size = 44682, upload-time = "2025-10-17T02:33:44.641Z" } +sdist = { url = "https://files.pythonhosted.org/packages/be/9f/3aedb3ce1d58c58ec7dd06b3964836eabfd17a16a95b60c8f609c0afff7f/google_cloud_audit_log-0.5.0.tar.gz", hash = "sha256:3b32d5e77db634c46fbd6c5e01f5bda836f420dfbb21d730501c75e9fab4e4a4", size = 44670, upload-time = "2026-03-30T22:50:42.295Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/25/532886995f11102ad6de290496de5db227bd3a73827702445928ad32edcb/google_cloud_audit_log-0.4.0-py3-none-any.whl", hash = "sha256:6b88e2349df45f8f4cc0993b687109b1388da1571c502dc1417efa4b66ec55e0", size = 44890, upload-time = "2025-10-17T02:30:55.11Z" }, + { url = "https://files.pythonhosted.org/packages/64/40/79fa535b6e3321d5e07b2a9ab4bb63860d3fea12230c765837881348003c/google_cloud_audit_log-0.5.0-py3-none-any.whl", hash = "sha256:3f4632f25bf67446fa9085c52868f3cb42fb1afbab9489ba8978e30991afc79f", size = 44862, upload-time = "2026-03-30T22:47:57.533Z" }, ] [[package]] name = "google-cloud-bigquery" -version = "3.40.1" +version = "3.41.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -2107,14 +2107,14 @@ dependencies = [ { name = "python-dateutil" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/11/0c/153ee546c288949fcc6794d58811ab5420f3ecad5fa7f9e73f78d9512a6e/google_cloud_bigquery-3.40.1.tar.gz", hash = "sha256:75afcfb6e007238fe1deefb2182105249321145ff921784fe7b1de2b4ba24506", size = 511761, upload-time = "2026-02-12T18:44:18.958Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/13/6515c7aab55a4a0cf708ffd309fb9af5bab54c13e32dc22c5acd6497193c/google_cloud_bigquery-3.41.0.tar.gz", hash = "sha256:2217e488b47ed576360c9b2cc07d59d883a54b83167c0ef37f915c26b01a06fe", size = 513434, upload-time = "2026-03-30T22:50:55.347Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/f5/081cf5b90adfe524ae0d671781b0d497a75a0f2601d075af518828e22d8f/google_cloud_bigquery-3.40.1-py3-none-any.whl", hash = "sha256:9082a6b8193aba87bed6a2c79cf1152b524c99bb7e7ac33a785e333c09eac868", size = 262018, upload-time = "2026-02-12T18:44:16.913Z" }, + { url = "https://files.pythonhosted.org/packages/40/33/1d3902efadef9194566d499d61507e1f038454e0b55499d2d7f8ab2a4fee/google_cloud_bigquery-3.41.0-py3-none-any.whl", hash = "sha256:2a5b5a737b401cbd824a6e5eac7554100b878668d908e6548836b5d8aaa4dcaa", size = 262343, upload-time = "2026-03-30T22:48:45.444Z" }, ] [[package]] name = "google-cloud-bigquery-storage" -version = "2.36.2" +version = "2.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -2123,9 +2123,9 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e0/fa/877e0059349369be38a64586b135c59ceadb87d0386084043d8c440ef929/google_cloud_bigquery_storage-2.36.2.tar.gz", hash = "sha256:ad49d8c09ad6cd82da4efe596fcfcdbc1458bf05b93915e3c5c00f1e700ae128", size = 308672, upload-time = "2026-02-19T16:03:10.544Z" } +sdist = { url = "https://files.pythonhosted.org/packages/13/31/5c6fa9e7b8e266a765ec80d13a2b2852cb0a6d3733572e7dbdc0cb39003c/google_cloud_bigquery_storage-2.37.0.tar.gz", hash = "sha256:f88ee7f1e49db1e639da3d9a8b79835ca4bc47afbb514fb2adfc0ccb41a7fd97", size = 310578, upload-time = "2026-03-30T22:51:13.418Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/07/62dbe78ef773569be0a1d2c1b845e9214889b404e506126519b4d33ee999/google_cloud_bigquery_storage-2.36.2-py3-none-any.whl", hash = "sha256:823a73db0c4564e8ad3eedcfd5049f3d5aa41775267863b5627211ec36be2dbf", size = 304398, upload-time = "2026-02-19T16:02:55.112Z" }, + { url = "https://files.pythonhosted.org/packages/74/0e/2950d4d0160300f51c7397a080b1685d3e25b40badb2c96f03d58d0ee868/google_cloud_bigquery_storage-2.37.0-py3-none-any.whl", hash = "sha256:1e319c27ef60fc31030f6e0b52e5e891e1cdd50551effe8c6f673a4c3c56fcb6", size = 306678, upload-time = "2026-03-30T22:47:42.333Z" }, ] [[package]] @@ -2148,15 +2148,15 @@ wheels = [ [[package]] name = "google-cloud-core" -version = "2.5.0" +version = "2.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core" }, { name = "google-auth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/03/ef0bc99d0e0faf4fdbe67ac445e18cdaa74824fd93cd069e7bb6548cb52d/google_cloud_core-2.5.0.tar.gz", hash = "sha256:7c1b7ef5c92311717bd05301aa1a91ffbc565673d3b0b4163a52d8413a186963", size = 36027, upload-time = "2025-10-29T23:17:39.513Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/24/6ca08b0a03c7b0c620427503ab00353a4ae806b848b93bcea18b6b76fde6/google_cloud_core-2.5.1.tar.gz", hash = "sha256:3dc94bdec9d05a31d9f355045ed0f369fbc0d8c665076c734f065d729800f811", size = 36078, upload-time = "2026-03-30T22:50:08.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/20/bfa472e327c8edee00f04beecc80baeddd2ab33ee0e86fd7654da49d45e9/google_cloud_core-2.5.0-py3-none-any.whl", hash = "sha256:67d977b41ae6c7211ee830c7912e41003ea8194bff15ae7d72fd6f51e57acabc", size = 29469, upload-time = "2025-10-29T23:17:38.548Z" }, + { url = "https://files.pythonhosted.org/packages/73/d9/5bb050cb32826466aa9b25f79e2ca2879fe66cb76782d4ed798dd7506151/google_cloud_core-2.5.1-py3-none-any.whl", hash = "sha256:ea62cdf502c20e3e14be8a32c05ed02113d7bef454e40ff3fab6fe1ec9f1f4e7", size = 29452, upload-time = "2026-03-30T22:48:31.567Z" }, ] [[package]] @@ -2171,6 +2171,7 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/ee/2b/c390bbe1f68015ea57eb9352e90ebbbf459c3139d9e5a8e6faa0b1abdc6e/google_cloud_dataplex-2.18.0.tar.gz", hash = "sha256:ae3f7f1b5c64675e8a4b66725d404eec864e12d29051323a2232bdb05797016d", size = 881810, upload-time = "2026-03-30T22:49:53.747Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b2/9a/8b096a6d772b7abf1c97dfbce17d47ba1d8a944ce8d7a239fd300a3ad8ae/google_cloud_dataplex-2.18.0-py3-none-any.whl", hash = "sha256:6e4ec95b24f64e95cec5f3753fbe7419f78ddb8b1ba90f8d955bc7613bb90764", size = 675743, upload-time = "2026-03-30T20:02:27.12Z" }, ] @@ -2202,6 +2203,7 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/62/e5/07d4f1daf85a2a0bd9f78ad865ea678d7b4e1227ed76f671c7167aae147f/google_cloud_iam-2.22.0.tar.gz", hash = "sha256:203ddfece17e014ee4fbc5c3244daa14a88b7ee57c8e3a7622d0f2a1a3b8d7f3", size = 502498, upload-time = "2026-03-30T22:51:28.878Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/aa/a8/d721ea11d0eb93803d14cb2e90d0442bb3b269a82f7cb5faff2b98022039/google_cloud_iam-2.22.0-py3-none-any.whl", hash = "sha256:c443b34b5a6a9e51d32cee397879bb781b900af68937c67a275def23bbc025f3", size = 463425, upload-time = "2026-03-30T20:02:42.967Z" }, ] @@ -2428,14 +2430,14 @@ wheels = [ [[package]] name = "google-resumable-media" -version = "2.8.0" +version = "2.8.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-crc32c" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/64/d7/520b62a35b23038ff005e334dba3ffc75fcf583bee26723f1fd8fd4b6919/google_resumable_media-2.8.0.tar.gz", hash = "sha256:f1157ed8b46994d60a1bc432544db62352043113684d4e030ee02e77ebe9a1ae", size = 2163265, upload-time = "2025-11-17T15:38:06.659Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/d1/b1ea14b93b6b78f57fc580125de44e9f593ab88dd2460f1a8a8d18f74754/google_resumable_media-2.8.2.tar.gz", hash = "sha256:f3354a182ebd193ae3f42e3ef95e6c9b10f128320de23ac7637236713b1acd70", size = 2164510, upload-time = "2026-03-30T23:34:25.369Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/0b/93afde9cfe012260e9fe1522f35c9b72d6ee222f316586b1f23ecf44d518/google_resumable_media-2.8.0-py3-none-any.whl", hash = "sha256:dd14a116af303845a8d932ddae161a26e86cc229645bc98b39f026f9b1717582", size = 81340, upload-time = "2025-11-17T15:38:05.594Z" }, + { url = "https://files.pythonhosted.org/packages/5e/f8/50bfaf4658431ff9de45c5c3935af7ab01157a4903c603cd0eee6e78e087/google_resumable_media-2.8.2-py3-none-any.whl", hash = "sha256:82b6d8ccd11765268cdd2a2123f417ec806b8eef3000a9a38dfe3033da5fb220", size = 81511, upload-time = "2026-03-30T23:34:09.671Z" }, ] [[package]] @@ -3794,7 +3796,7 @@ wheels = [ [[package]] name = "mypy" -version = "1.19.1" +version = "1.20.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, @@ -3803,39 +3805,51 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/63/e499890d8e39b1ff2df4c0c6ce5d371b6844ee22b8250687a99fd2f657a8/mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec", size = 13101333, upload-time = "2025-12-15T05:03:03.28Z" }, - { url = "https://files.pythonhosted.org/packages/72/4b/095626fc136fba96effc4fd4a82b41d688ab92124f8c4f7564bffe5cf1b0/mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b", size = 12164102, upload-time = "2025-12-15T05:02:33.611Z" }, - { url = "https://files.pythonhosted.org/packages/0c/5b/952928dd081bf88a83a5ccd49aaecfcd18fd0d2710c7ff07b8fb6f7032b9/mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6", size = 12765799, upload-time = "2025-12-15T05:03:28.44Z" }, - { url = "https://files.pythonhosted.org/packages/2a/0d/93c2e4a287f74ef11a66fb6d49c7a9f05e47b0a4399040e6719b57f500d2/mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74", size = 13522149, upload-time = "2025-12-15T05:02:36.011Z" }, - { url = "https://files.pythonhosted.org/packages/7b/0e/33a294b56aaad2b338d203e3a1d8b453637ac36cb278b45005e0901cf148/mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1", size = 13810105, upload-time = "2025-12-15T05:02:40.327Z" }, - { url = "https://files.pythonhosted.org/packages/0e/fd/3e82603a0cb66b67c5e7abababce6bf1a929ddf67bf445e652684af5c5a0/mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac", size = 10057200, upload-time = "2025-12-15T05:02:51.012Z" }, - { url = "https://files.pythonhosted.org/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288", size = 13028539, upload-time = "2025-12-15T05:03:44.129Z" }, - { url = "https://files.pythonhosted.org/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab", size = 12083163, upload-time = "2025-12-15T05:03:37.679Z" }, - { url = "https://files.pythonhosted.org/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6", size = 12687629, upload-time = "2025-12-15T05:02:31.526Z" }, - { url = "https://files.pythonhosted.org/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331", size = 13436933, upload-time = "2025-12-15T05:03:15.606Z" }, - { url = "https://files.pythonhosted.org/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925", size = 13661754, upload-time = "2025-12-15T05:02:26.731Z" }, - { url = "https://files.pythonhosted.org/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042", size = 10055772, upload-time = "2025-12-15T05:03:26.179Z" }, - { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, - { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, - { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, - { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, - { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, - { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, - { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, - { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, - { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, - { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, - { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, - { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, - { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, - { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, - { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/f8/5c/b0089fe7fef0a994ae5ee07029ced0526082c6cfaaa4c10d40a10e33b097/mypy-1.20.0.tar.gz", hash = "sha256:eb96c84efcc33f0b5e0e04beacf00129dd963b67226b01c00b9dfc8affb464c3", size = 3815028, upload-time = "2026-03-31T16:55:14.959Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/a2/a965c8c3fcd4fa8b84ba0d46606181b0d0a1d50f274c67877f3e9ed4882c/mypy-1.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d99f515f95fd03a90875fdb2cca12ff074aa04490db4d190905851bdf8a549a8", size = 14430138, upload-time = "2026-03-31T16:52:37.843Z" }, + { url = "https://files.pythonhosted.org/packages/53/6e/043477501deeb8eabbab7f1a2f6cac62cfb631806dc1d6862a04a7f5011b/mypy-1.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bd0212976dc57a5bfeede7c219e7cd66568a32c05c9129686dd487c059c1b88a", size = 13311282, upload-time = "2026-03-31T16:55:11.021Z" }, + { url = "https://files.pythonhosted.org/packages/65/aa/bd89b247b83128197a214f29f0632ff3c14f54d4cd70d144d157bd7d7d6e/mypy-1.20.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8426d4d75d68714abc17a4292d922f6ba2cfb984b72c2278c437f6dae797865", size = 13750889, upload-time = "2026-03-31T16:52:02.909Z" }, + { url = "https://files.pythonhosted.org/packages/fa/9d/2860be7355c45247ccc0be1501c91176318964c2a137bd4743f58ce6200e/mypy-1.20.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02cca0761c75b42a20a2757ae58713276605eb29a08dd8a6e092aa347c4115ca", size = 14619788, upload-time = "2026-03-31T16:50:48.928Z" }, + { url = "https://files.pythonhosted.org/packages/75/7f/3ef3e360c91f3de120f205c8ce405e9caf9fc52ef14b65d37073e322c114/mypy-1.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3a49064504be59e59da664c5e149edc1f26c67c4f8e8456f6ba6aba55033018", size = 14918849, upload-time = "2026-03-31T16:51:10.478Z" }, + { url = "https://files.pythonhosted.org/packages/ae/72/af970dfe167ef788df7c5e6109d2ed0229f164432ce828bc9741a4250e64/mypy-1.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:ebea00201737ad4391142808ed16e875add5c17f676e0912b387739f84991e13", size = 10822007, upload-time = "2026-03-31T16:50:25.268Z" }, + { url = "https://files.pythonhosted.org/packages/93/94/ba9065c2ebe5421619aff684b793d953e438a8bfe31a320dd6d1e0706e81/mypy-1.20.0-cp310-cp310-win_arm64.whl", hash = "sha256:e80cf77847d0d3e6e3111b7b25db32a7f8762fd4b9a3a72ce53fe16a2863b281", size = 9756158, upload-time = "2026-03-31T16:48:36.213Z" }, + { url = "https://files.pythonhosted.org/packages/6e/1c/74cb1d9993236910286865679d1c616b136b2eae468493aa939431eda410/mypy-1.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4525e7010b1b38334516181c5b81e16180b8e149e6684cee5a727c78186b4e3b", size = 14343972, upload-time = "2026-03-31T16:49:04.887Z" }, + { url = "https://files.pythonhosted.org/packages/d5/0d/01399515eca280386e308cf57901e68d3a52af18691941b773b3380c1df8/mypy-1.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a17c5d0bdcca61ce24a35beb828a2d0d323d3fcf387d7512206888c900193367", size = 13225007, upload-time = "2026-03-31T16:50:08.151Z" }, + { url = "https://files.pythonhosted.org/packages/56/ac/b4ba5094fb2d7fe9d2037cd8d18bbe02bcf68fd22ab9ff013f55e57ba095/mypy-1.20.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75ff57defcd0f1d6e006d721ccdec6c88d4f6a7816eb92f1c4890d979d9ee62", size = 13663752, upload-time = "2026-03-31T16:49:26.064Z" }, + { url = "https://files.pythonhosted.org/packages/db/a7/460678d3cf7da252d2288dad0c602294b6ec22a91932ec368cc11e44bb6e/mypy-1.20.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b503ab55a836136b619b5fc21c8803d810c5b87551af8600b72eecafb0059cb0", size = 14532265, upload-time = "2026-03-31T16:53:55.077Z" }, + { url = "https://files.pythonhosted.org/packages/a3/3e/051cca8166cf0438ae3ea80e0e7c030d7a8ab98dffc93f80a1aa3f23c1a2/mypy-1.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1973868d2adbb4584a3835780b27436f06d1dc606af5be09f187aaa25be1070f", size = 14768476, upload-time = "2026-03-31T16:50:34.587Z" }, + { url = "https://files.pythonhosted.org/packages/be/66/8e02ec184f852ed5c4abb805583305db475930854e09964b55e107cdcbc4/mypy-1.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:2fcedb16d456106e545b2bfd7ef9d24e70b38ec252d2a629823a4d07ebcdb69e", size = 10818226, upload-time = "2026-03-31T16:53:15.624Z" }, + { url = "https://files.pythonhosted.org/packages/13/4b/383ad1924b28f41e4879a74151e7a5451123330d45652da359f9183bcd45/mypy-1.20.0-cp311-cp311-win_arm64.whl", hash = "sha256:379edf079ce44ac8d2805bcf9b3dd7340d4f97aad3a5e0ebabbf9d125b84b442", size = 9750091, upload-time = "2026-03-31T16:54:12.162Z" }, + { url = "https://files.pythonhosted.org/packages/be/dd/3afa29b58c2e57c79116ed55d700721c3c3b15955e2b6251dd165d377c0e/mypy-1.20.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:002b613ae19f4ac7d18b7e168ffe1cb9013b37c57f7411984abbd3b817b0a214", size = 14509525, upload-time = "2026-03-31T16:55:01.824Z" }, + { url = "https://files.pythonhosted.org/packages/54/eb/227b516ab8cad9f2a13c5e7a98d28cd6aa75e9c83e82776ae6c1c4c046c7/mypy-1.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9336b5e6712f4adaf5afc3203a99a40b379049104349d747eb3e5a3aa23ac2e", size = 13326469, upload-time = "2026-03-31T16:51:41.23Z" }, + { url = "https://files.pythonhosted.org/packages/57/d4/1ddb799860c1b5ac6117ec307b965f65deeb47044395ff01ab793248a591/mypy-1.20.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f13b3e41bce9d257eded794c0f12878af3129d80aacd8a3ee0dee51f3a978651", size = 13705953, upload-time = "2026-03-31T16:48:55.69Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b7/54a720f565a87b893182a2a393370289ae7149e4715859e10e1c05e49154/mypy-1.20.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9804c3ad27f78e54e58b32e7cb532d128b43dbfb9f3f9f06262b821a0f6bd3f5", size = 14710363, upload-time = "2026-03-31T16:53:26.948Z" }, + { url = "https://files.pythonhosted.org/packages/b2/2a/74810274848d061f8a8ea4ac23aaad43bd3d8c1882457999c2e568341c57/mypy-1.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:697f102c5c1d526bdd761a69f17c6070f9892eebcb94b1a5963d679288c09e78", size = 14947005, upload-time = "2026-03-31T16:50:17.591Z" }, + { url = "https://files.pythonhosted.org/packages/77/91/21b8ba75f958bcda75690951ce6fa6b7138b03471618959529d74b8544e2/mypy-1.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ecd63f75fdd30327e4ad8b5704bd6d91fc6c1b2e029f8ee14705e1207212489", size = 10880616, upload-time = "2026-03-31T16:52:19.986Z" }, + { url = "https://files.pythonhosted.org/packages/8a/15/3d8198ef97c1ca03aea010cce4f1d4f3bc5d9849e8c0140111ca2ead9fdd/mypy-1.20.0-cp312-cp312-win_arm64.whl", hash = "sha256:f194db59657c58593a3c47c6dfd7bad4ef4ac12dbc94d01b3a95521f78177e33", size = 9813091, upload-time = "2026-03-31T16:53:44.385Z" }, + { url = "https://files.pythonhosted.org/packages/d6/a7/f64ea7bd592fa431cb597418b6dec4a47f7d0c36325fec7ac67bc8402b94/mypy-1.20.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b20c8b0fd5877abdf402e79a3af987053de07e6fb208c18df6659f708b535134", size = 14485344, upload-time = "2026-03-31T16:49:16.78Z" }, + { url = "https://files.pythonhosted.org/packages/bb/72/8927d84cfc90c6abea6e96663576e2e417589347eb538749a464c4c218a0/mypy-1.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:367e5c993ba34d5054d11937d0485ad6dfc60ba760fa326c01090fc256adf15c", size = 13327400, upload-time = "2026-03-31T16:53:08.02Z" }, + { url = "https://files.pythonhosted.org/packages/ab/4a/11ab99f9afa41aa350178d24a7d2da17043228ea10f6456523f64b5a6cf6/mypy-1.20.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f799d9db89fc00446f03281f84a221e50018fc40113a3ba9864b132895619ebe", size = 13706384, upload-time = "2026-03-31T16:52:28.577Z" }, + { url = "https://files.pythonhosted.org/packages/42/79/694ca73979cfb3535ebfe78733844cd5aff2e63304f59bf90585110d975a/mypy-1.20.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:555658c611099455b2da507582ea20d2043dfdfe7f5ad0add472b1c6238b433f", size = 14700378, upload-time = "2026-03-31T16:48:45.527Z" }, + { url = "https://files.pythonhosted.org/packages/84/24/a022ccab3a46e3d2cdf2e0e260648633640eb396c7e75d5a42818a8d3971/mypy-1.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:efe8d70949c3023698c3fca1e94527e7e790a361ab8116f90d11221421cd8726", size = 14932170, upload-time = "2026-03-31T16:49:36.038Z" }, + { url = "https://files.pythonhosted.org/packages/d8/9b/549228d88f574d04117e736f55958bd4908f980f9f5700a07aeb85df005b/mypy-1.20.0-cp313-cp313-win_amd64.whl", hash = "sha256:f49590891d2c2f8a9de15614e32e459a794bcba84693c2394291a2038bbaaa69", size = 10888526, upload-time = "2026-03-31T16:50:59.827Z" }, + { url = "https://files.pythonhosted.org/packages/91/17/15095c0e54a8bc04d22d4ff06b2139d5f142c2e87520b4e39010c4862771/mypy-1.20.0-cp313-cp313-win_arm64.whl", hash = "sha256:76a70bf840495729be47510856b978f1b0ec7d08f257ca38c9d932720bf6b43e", size = 9816456, upload-time = "2026-03-31T16:49:59.537Z" }, + { url = "https://files.pythonhosted.org/packages/4e/0e/6ca4a84cbed9e62384bc0b2974c90395ece5ed672393e553996501625fc5/mypy-1.20.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0f42dfaab7ec1baff3b383ad7af562ab0de573c5f6edb44b2dab016082b89948", size = 14483331, upload-time = "2026-03-31T16:52:57.999Z" }, + { url = "https://files.pythonhosted.org/packages/7d/c5/5fe9d8a729dd9605064691816243ae6c49fde0bd28f6e5e17f6a24203c43/mypy-1.20.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:31b5dbb55293c1bd27c0fc813a0d2bb5ceef9d65ac5afa2e58f829dab7921fd5", size = 13342047, upload-time = "2026-03-31T16:54:21.555Z" }, + { url = "https://files.pythonhosted.org/packages/4c/33/e18bcfa338ca4e6b2771c85d4c5203e627d0c69d9de5c1a2cf2ba13320ba/mypy-1.20.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49d11c6f573a5a08f77fad13faff2139f6d0730ebed2cfa9b3d2702671dd7188", size = 13719585, upload-time = "2026-03-31T16:51:53.89Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8d/93491ff7b79419edc7eabf95cb3b3f7490e2e574b2855c7c7e7394ff933f/mypy-1.20.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d3243c406773185144527f83be0e0aefc7bf4601b0b2b956665608bf7c98a83", size = 14685075, upload-time = "2026-03-31T16:54:04.464Z" }, + { url = "https://files.pythonhosted.org/packages/b5/9d/d924b38a4923f8d164bf2b4ec98bf13beaf6e10a5348b4b137eadae40a6e/mypy-1.20.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a79c1eba7ac4209f2d850f0edd0a2f8bba88cbfdfefe6fb76a19e9d4fe5e71a2", size = 14919141, upload-time = "2026-03-31T16:54:51.785Z" }, + { url = "https://files.pythonhosted.org/packages/59/98/1da9977016678c0b99d43afe52ed00bb3c1a0c4c995d3e6acca1a6ebb9b4/mypy-1.20.0-cp314-cp314-win_amd64.whl", hash = "sha256:00e047c74d3ec6e71a2eb88e9ea551a2edb90c21f993aefa9e0d2a898e0bb732", size = 11050925, upload-time = "2026-03-31T16:51:30.758Z" }, + { url = "https://files.pythonhosted.org/packages/5e/e3/ba0b7a3143e49a9c4f5967dde6ea4bf8e0b10ecbbcca69af84027160ee89/mypy-1.20.0-cp314-cp314-win_arm64.whl", hash = "sha256:931a7630bba591593dcf6e97224a21ff80fb357e7982628d25e3c618e7f598ef", size = 10001089, upload-time = "2026-03-31T16:49:43.632Z" }, + { url = "https://files.pythonhosted.org/packages/12/28/e617e67b3be9d213cda7277913269c874eb26472489f95d09d89765ce2d8/mypy-1.20.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:26c8b52627b6552f47ff11adb4e1509605f094e29815323e487fc0053ebe93d1", size = 15534710, upload-time = "2026-03-31T16:52:12.506Z" }, + { url = "https://files.pythonhosted.org/packages/6e/0c/3b5f2d3e45dc7169b811adce8451679d9430399d03b168f9b0489f43adaa/mypy-1.20.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:39362cdb4ba5f916e7976fccecaab1ba3a83e35f60fa68b64e9a70e221bb2436", size = 14393013, upload-time = "2026-03-31T16:54:41.186Z" }, + { url = "https://files.pythonhosted.org/packages/a3/49/edc8b0aa145cc09c1c74f7ce2858eead9329931dcbbb26e2ad40906daa4e/mypy-1.20.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34506397dbf40c15dc567635d18a21d33827e9ab29014fb83d292a8f4f8953b6", size = 15047240, upload-time = "2026-03-31T16:54:31.955Z" }, + { url = "https://files.pythonhosted.org/packages/42/37/a946bb416e37a57fa752b3100fd5ede0e28df94f92366d1716555d47c454/mypy-1.20.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:555493c44a4f5a1b58d611a43333e71a9981c6dbe26270377b6f8174126a0526", size = 15858565, upload-time = "2026-03-31T16:53:36.997Z" }, + { url = "https://files.pythonhosted.org/packages/2f/99/7690b5b5b552db1bd4ff362e4c0eb3107b98d680835e65823fbe888c8b78/mypy-1.20.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2721f0ce49cb74a38f00c50da67cb7d36317b5eda38877a49614dc018e91c787", size = 16087874, upload-time = "2026-03-31T16:52:48.313Z" }, + { url = "https://files.pythonhosted.org/packages/aa/76/53e893a498138066acd28192b77495c9357e5a58cc4be753182846b43315/mypy-1.20.0-cp314-cp314t-win_amd64.whl", hash = "sha256:47781555a7aa5fedcc2d16bcd72e0dc83eb272c10dd657f9fb3f9cc08e2e6abb", size = 12572380, upload-time = "2026-03-31T16:49:52.454Z" }, + { url = "https://files.pythonhosted.org/packages/76/9c/6dbdae21f01b7aacddc2c0bbf3c5557aa547827fdf271770fe1e521e7093/mypy-1.20.0-cp314-cp314t-win_arm64.whl", hash = "sha256:c70380fe5d64010f79fb863b9081c7004dd65225d2277333c219d93a10dad4dd", size = 10381174, upload-time = "2026-03-31T16:51:20.179Z" }, + { url = "https://files.pythonhosted.org/packages/21/66/4d734961ce167f0fd8380769b3b7c06dbdd6ff54c2190f3f2ecd22528158/mypy-1.20.0-py3-none-any.whl", hash = "sha256:a6e0641147cbfa7e4e94efdb95c2dab1aff8cfc159ded13e07f308ddccc8c48e", size = 2636365, upload-time = "2026-03-31T16:51:44.911Z" }, ] [[package]] @@ -4479,83 +4493,83 @@ wheels = [ [[package]] name = "orjson" -version = "3.11.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/45/b268004f745ede84e5798b48ee12b05129d19235d0e15267aa57dcdb400b/orjson-3.11.7.tar.gz", hash = "sha256:9b1a67243945819ce55d24a30b59d6a168e86220452d2c96f4d1f093e71c0c49", size = 6144992, upload-time = "2026-02-02T15:38:49.29Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/1a/a373746fa6d0e116dd9e54371a7b54622c44d12296d5d0f3ad5e3ff33490/orjson-3.11.7-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a02c833f38f36546ba65a452127633afce4cf0dd7296b753d3bb54e55e5c0174", size = 229140, upload-time = "2026-02-02T15:37:06.082Z" }, - { url = "https://files.pythonhosted.org/packages/52/a2/fa129e749d500f9b183e8a3446a193818a25f60261e9ce143ad61e975208/orjson-3.11.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b63c6e6738d7c3470ad01601e23376aa511e50e1f3931395b9f9c722406d1a67", size = 128670, upload-time = "2026-02-02T15:37:08.002Z" }, - { url = "https://files.pythonhosted.org/packages/08/93/1e82011cd1e0bd051ef9d35bed1aa7fb4ea1f0a055dc2c841b46b43a9ebd/orjson-3.11.7-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:043d3006b7d32c7e233b8cfb1f01c651013ea079e08dcef7189a29abd8befe11", size = 123832, upload-time = "2026-02-02T15:37:09.191Z" }, - { url = "https://files.pythonhosted.org/packages/fe/d8/a26b431ef962c7d55736674dddade876822f3e33223c1f47a36879350d04/orjson-3.11.7-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57036b27ac8a25d81112eb0cc9835cd4833c5b16e1467816adc0015f59e870dc", size = 129171, upload-time = "2026-02-02T15:37:11.112Z" }, - { url = "https://files.pythonhosted.org/packages/a7/19/f47819b84a580f490da260c3ee9ade214cf4cf78ac9ce8c1c758f80fdfc9/orjson-3.11.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:733ae23ada68b804b222c44affed76b39e30806d38660bf1eb200520d259cc16", size = 141967, upload-time = "2026-02-02T15:37:12.282Z" }, - { url = "https://files.pythonhosted.org/packages/5b/cd/37ece39a0777ba077fdcdbe4cccae3be8ed00290c14bf8afdc548befc260/orjson-3.11.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5fdfad2093bdd08245f2e204d977facd5f871c88c4a71230d5bcbd0e43bf6222", size = 130991, upload-time = "2026-02-02T15:37:13.465Z" }, - { url = "https://files.pythonhosted.org/packages/8f/ed/f2b5d66aa9b6b5c02ff5f120efc7b38c7c4962b21e6be0f00fd99a5c348e/orjson-3.11.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cededd6738e1c153530793998e31c05086582b08315db48ab66649768f326baa", size = 133674, upload-time = "2026-02-02T15:37:14.694Z" }, - { url = "https://files.pythonhosted.org/packages/c4/6e/baa83e68d1aa09fa8c3e5b2c087d01d0a0bd45256de719ed7bc22c07052d/orjson-3.11.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:14f440c7268c8f8633d1b3d443a434bd70cb15686117ea6beff8fdc8f5917a1e", size = 138722, upload-time = "2026-02-02T15:37:16.501Z" }, - { url = "https://files.pythonhosted.org/packages/0c/47/7f8ef4963b772cd56999b535e553f7eb5cd27e9dd6c049baee6f18bfa05d/orjson-3.11.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3a2479753bbb95b0ebcf7969f562cdb9668e6d12416a35b0dda79febf89cdea2", size = 409056, upload-time = "2026-02-02T15:37:17.895Z" }, - { url = "https://files.pythonhosted.org/packages/38/eb/2df104dd2244b3618f25325a656f85cc3277f74bbd91224752410a78f3c7/orjson-3.11.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:71924496986275a737f38e3f22b4e0878882b3f7a310d2ff4dc96e812789120c", size = 144196, upload-time = "2026-02-02T15:37:19.349Z" }, - { url = "https://files.pythonhosted.org/packages/b6/2a/ee41de0aa3a6686598661eae2b4ebdff1340c65bfb17fcff8b87138aab21/orjson-3.11.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4a9eefdc70bf8bf9857f0290f973dec534ac84c35cd6a7f4083be43e7170a8f", size = 134979, upload-time = "2026-02-02T15:37:20.906Z" }, - { url = "https://files.pythonhosted.org/packages/4c/fa/92fc5d3d402b87a8b28277a9ed35386218a6a5287c7fe5ee9b9f02c53fb2/orjson-3.11.7-cp310-cp310-win32.whl", hash = "sha256:ae9e0b37a834cef7ce8f99de6498f8fad4a2c0bf6bfc3d02abd8ed56aa15b2de", size = 127968, upload-time = "2026-02-02T15:37:23.178Z" }, - { url = "https://files.pythonhosted.org/packages/07/29/a576bf36d73d60df06904d3844a9df08e25d59eba64363aaf8ec2f9bff41/orjson-3.11.7-cp310-cp310-win_amd64.whl", hash = "sha256:d772afdb22555f0c58cfc741bdae44180122b3616faa1ecadb595cd526e4c993", size = 125128, upload-time = "2026-02-02T15:37:24.329Z" }, - { url = "https://files.pythonhosted.org/packages/37/02/da6cb01fc6087048d7f61522c327edf4250f1683a58a839fdcc435746dd5/orjson-3.11.7-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9487abc2c2086e7c8eb9a211d2ce8855bae0e92586279d0d27b341d5ad76c85c", size = 228664, upload-time = "2026-02-02T15:37:25.542Z" }, - { url = "https://files.pythonhosted.org/packages/c1/c2/5885e7a5881dba9a9af51bc564e8967225a642b3e03d089289a35054e749/orjson-3.11.7-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:79cacb0b52f6004caf92405a7e1f11e6e2de8bdf9019e4f76b44ba045125cd6b", size = 125344, upload-time = "2026-02-02T15:37:26.92Z" }, - { url = "https://files.pythonhosted.org/packages/a4/1d/4e7688de0a92d1caf600dfd5fb70b4c5bfff51dfa61ac555072ef2d0d32a/orjson-3.11.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2e85fe4698b6a56d5e2ebf7ae87544d668eb6bde1ad1226c13f44663f20ec9e", size = 128404, upload-time = "2026-02-02T15:37:28.108Z" }, - { url = "https://files.pythonhosted.org/packages/2f/b2/ec04b74ae03a125db7bd69cffd014b227b7f341e3261bf75b5eb88a1aa92/orjson-3.11.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b8d14b71c0b12963fe8a62aac87119f1afdf4cb88a400f61ca5ae581449efcb5", size = 123677, upload-time = "2026-02-02T15:37:30.287Z" }, - { url = "https://files.pythonhosted.org/packages/4c/69/f95bdf960605f08f827f6e3291fe243d8aa9c5c9ff017a8d7232209184c3/orjson-3.11.7-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91c81ef070c8f3220054115e1ef468b1c9ce8497b4e526cb9f68ab4dc0a7ac62", size = 128950, upload-time = "2026-02-02T15:37:31.595Z" }, - { url = "https://files.pythonhosted.org/packages/a4/1b/de59c57bae1d148ef298852abd31909ac3089cff370dfd4cd84cc99cbc42/orjson-3.11.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:411ebaf34d735e25e358a6d9e7978954a9c9d58cfb47bc6683cdc3964cd2f910", size = 141756, upload-time = "2026-02-02T15:37:32.985Z" }, - { url = "https://files.pythonhosted.org/packages/ee/9e/9decc59f4499f695f65c650f6cfa6cd4c37a3fbe8fa235a0a3614cb54386/orjson-3.11.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a16bcd08ab0bcdfc7e8801d9c4a9cc17e58418e4d48ddc6ded4e9e4b1a94062b", size = 130812, upload-time = "2026-02-02T15:37:34.204Z" }, - { url = "https://files.pythonhosted.org/packages/28/e6/59f932bcabd1eac44e334fe8e3281a92eacfcb450586e1f4bde0423728d8/orjson-3.11.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c0b51672e466fd7e56230ffbae7f1639e18d0ce023351fb75da21b71bc2c960", size = 133444, upload-time = "2026-02-02T15:37:35.446Z" }, - { url = "https://files.pythonhosted.org/packages/f1/36/b0f05c0eaa7ca30bc965e37e6a2956b0d67adb87a9872942d3568da846ae/orjson-3.11.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:136dcd6a2e796dfd9ffca9fc027d778567b0b7c9968d092842d3c323cef88aa8", size = 138609, upload-time = "2026-02-02T15:37:36.657Z" }, - { url = "https://files.pythonhosted.org/packages/b8/03/58ec7d302b8d86944c60c7b4b82975d5161fcce4c9bc8c6cb1d6741b6115/orjson-3.11.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:7ba61079379b0ae29e117db13bda5f28d939766e410d321ec1624afc6a0b0504", size = 408918, upload-time = "2026-02-02T15:37:38.076Z" }, - { url = "https://files.pythonhosted.org/packages/06/3a/868d65ef9a8b99be723bd510de491349618abd9f62c826cf206d962db295/orjson-3.11.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0527a4510c300e3b406591b0ba69b5dc50031895b0a93743526a3fc45f59d26e", size = 143998, upload-time = "2026-02-02T15:37:39.706Z" }, - { url = "https://files.pythonhosted.org/packages/5b/c7/1e18e1c83afe3349f4f6dc9e14910f0ae5f82eac756d1412ea4018938535/orjson-3.11.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a709e881723c9b18acddcfb8ba357322491ad553e277cf467e1e7e20e2d90561", size = 134802, upload-time = "2026-02-02T15:37:41.002Z" }, - { url = "https://files.pythonhosted.org/packages/d4/0b/ccb7ee1a65b37e8eeb8b267dc953561d72370e85185e459616d4345bab34/orjson-3.11.7-cp311-cp311-win32.whl", hash = "sha256:c43b8b5bab288b6b90dac410cca7e986a4fa747a2e8f94615aea407da706980d", size = 127828, upload-time = "2026-02-02T15:37:42.241Z" }, - { url = "https://files.pythonhosted.org/packages/af/9e/55c776dffda3f381e0f07d010a4f5f3902bf48eaba1bb7684d301acd4924/orjson-3.11.7-cp311-cp311-win_amd64.whl", hash = "sha256:6543001328aa857187f905308a028935864aefe9968af3848401b6fe80dbb471", size = 124941, upload-time = "2026-02-02T15:37:43.444Z" }, - { url = "https://files.pythonhosted.org/packages/aa/8e/424a620fa7d263b880162505fb107ef5e0afaa765b5b06a88312ac291560/orjson-3.11.7-cp311-cp311-win_arm64.whl", hash = "sha256:1ee5cc7160a821dfe14f130bc8e63e7611051f964b463d9e2a3a573204446a4d", size = 126245, upload-time = "2026-02-02T15:37:45.18Z" }, - { url = "https://files.pythonhosted.org/packages/80/bf/76f4f1665f6983385938f0e2a5d7efa12a58171b8456c252f3bae8a4cf75/orjson-3.11.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:bd03ea7606833655048dab1a00734a2875e3e86c276e1d772b2a02556f0d895f", size = 228545, upload-time = "2026-02-02T15:37:46.376Z" }, - { url = "https://files.pythonhosted.org/packages/79/53/6c72c002cb13b5a978a068add59b25a8bdf2800ac1c9c8ecdb26d6d97064/orjson-3.11.7-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:89e440ebc74ce8ab5c7bc4ce6757b4a6b1041becb127df818f6997b5c71aa60b", size = 125224, upload-time = "2026-02-02T15:37:47.697Z" }, - { url = "https://files.pythonhosted.org/packages/2c/83/10e48852865e5dd151bdfe652c06f7da484578ed02c5fca938e3632cb0b8/orjson-3.11.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ede977b5fe5ac91b1dffc0a517ca4542d2ec8a6a4ff7b2652d94f640796342a", size = 128154, upload-time = "2026-02-02T15:37:48.954Z" }, - { url = "https://files.pythonhosted.org/packages/6e/52/a66e22a2b9abaa374b4a081d410edab6d1e30024707b87eab7c734afe28d/orjson-3.11.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b7b1dae39230a393df353827c855a5f176271c23434cfd2db74e0e424e693e10", size = 123548, upload-time = "2026-02-02T15:37:50.187Z" }, - { url = "https://files.pythonhosted.org/packages/de/38/605d371417021359f4910c496f764c48ceb8997605f8c25bf1dfe58c0ebe/orjson-3.11.7-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed46f17096e28fb28d2975834836a639af7278aa87c84f68ab08fbe5b8bd75fa", size = 129000, upload-time = "2026-02-02T15:37:51.426Z" }, - { url = "https://files.pythonhosted.org/packages/44/98/af32e842b0ffd2335c89714d48ca4e3917b42f5d6ee5537832e069a4b3ac/orjson-3.11.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3726be79e36e526e3d9c1aceaadbfb4a04ee80a72ab47b3f3c17fefb9812e7b8", size = 141686, upload-time = "2026-02-02T15:37:52.607Z" }, - { url = "https://files.pythonhosted.org/packages/96/0b/fc793858dfa54be6feee940c1463370ece34b3c39c1ca0aa3845f5ba9892/orjson-3.11.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0724e265bc548af1dedebd9cb3d24b4e1c1e685a343be43e87ba922a5c5fff2f", size = 130812, upload-time = "2026-02-02T15:37:53.944Z" }, - { url = "https://files.pythonhosted.org/packages/dc/91/98a52415059db3f374757d0b7f0f16e3b5cd5976c90d1c2b56acaea039e6/orjson-3.11.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7745312efa9e11c17fbd3cb3097262d079da26930ae9ae7ba28fb738367cbad", size = 133440, upload-time = "2026-02-02T15:37:55.615Z" }, - { url = "https://files.pythonhosted.org/packages/dc/b6/cb540117bda61791f46381f8c26c8f93e802892830a6055748d3bb1925ab/orjson-3.11.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f904c24bdeabd4298f7a977ef14ca2a022ca921ed670b92ecd16ab6f3d01f867", size = 138386, upload-time = "2026-02-02T15:37:56.814Z" }, - { url = "https://files.pythonhosted.org/packages/63/1a/50a3201c334a7f17c231eee5f841342190723794e3b06293f26e7cf87d31/orjson-3.11.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b9fc4d0f81f394689e0814617aadc4f2ea0e8025f38c226cbf22d3b5ddbf025d", size = 408853, upload-time = "2026-02-02T15:37:58.291Z" }, - { url = "https://files.pythonhosted.org/packages/87/cd/8de1c67d0be44fdc22701e5989c0d015a2adf391498ad42c4dc589cd3013/orjson-3.11.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:849e38203e5be40b776ed2718e587faf204d184fc9a008ae441f9442320c0cab", size = 144130, upload-time = "2026-02-02T15:38:00.163Z" }, - { url = "https://files.pythonhosted.org/packages/0f/fe/d605d700c35dd55f51710d159fc54516a280923cd1b7e47508982fbb387d/orjson-3.11.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4682d1db3bcebd2b64757e0ddf9e87ae5f00d29d16c5cdf3a62f561d08cc3dd2", size = 134818, upload-time = "2026-02-02T15:38:01.507Z" }, - { url = "https://files.pythonhosted.org/packages/e4/e4/15ecc67edb3ddb3e2f46ae04475f2d294e8b60c1825fbe28a428b93b3fbd/orjson-3.11.7-cp312-cp312-win32.whl", hash = "sha256:f4f7c956b5215d949a1f65334cf9d7612dde38f20a95f2315deef167def91a6f", size = 127923, upload-time = "2026-02-02T15:38:02.75Z" }, - { url = "https://files.pythonhosted.org/packages/34/70/2e0855361f76198a3965273048c8e50a9695d88cd75811a5b46444895845/orjson-3.11.7-cp312-cp312-win_amd64.whl", hash = "sha256:bf742e149121dc5648ba0a08ea0871e87b660467ef168a3a5e53bc1fbd64bb74", size = 125007, upload-time = "2026-02-02T15:38:04.032Z" }, - { url = "https://files.pythonhosted.org/packages/68/40/c2051bd19fc467610fed469dc29e43ac65891571138f476834ca192bc290/orjson-3.11.7-cp312-cp312-win_arm64.whl", hash = "sha256:26c3b9132f783b7d7903bf1efb095fed8d4a3a85ec0d334ee8beff3d7a4749d5", size = 126089, upload-time = "2026-02-02T15:38:05.297Z" }, - { url = "https://files.pythonhosted.org/packages/89/25/6e0e52cac5aab51d7b6dcd257e855e1dec1c2060f6b28566c509b4665f62/orjson-3.11.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1d98b30cc1313d52d4af17d9c3d307b08389752ec5f2e5febdfada70b0f8c733", size = 228390, upload-time = "2026-02-02T15:38:06.8Z" }, - { url = "https://files.pythonhosted.org/packages/a5/29/a77f48d2fc8a05bbc529e5ff481fb43d914f9e383ea2469d4f3d51df3d00/orjson-3.11.7-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:d897e81f8d0cbd2abb82226d1860ad2e1ab3ff16d7b08c96ca00df9d45409ef4", size = 125189, upload-time = "2026-02-02T15:38:08.181Z" }, - { url = "https://files.pythonhosted.org/packages/89/25/0a16e0729a0e6a1504f9d1a13cdd365f030068aab64cec6958396b9969d7/orjson-3.11.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:814be4b49b228cfc0b3c565acf642dd7d13538f966e3ccde61f4f55be3e20785", size = 128106, upload-time = "2026-02-02T15:38:09.41Z" }, - { url = "https://files.pythonhosted.org/packages/66/da/a2e505469d60666a05ab373f1a6322eb671cb2ba3a0ccfc7d4bc97196787/orjson-3.11.7-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d06e5c5fed5caedd2e540d62e5b1c25e8c82431b9e577c33537e5fa4aa909539", size = 123363, upload-time = "2026-02-02T15:38:10.73Z" }, - { url = "https://files.pythonhosted.org/packages/23/bf/ed73f88396ea35c71b38961734ea4a4746f7ca0768bf28fd551d37e48dd0/orjson-3.11.7-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31c80ce534ac4ea3739c5ee751270646cbc46e45aea7576a38ffec040b4029a1", size = 129007, upload-time = "2026-02-02T15:38:12.138Z" }, - { url = "https://files.pythonhosted.org/packages/73/3c/b05d80716f0225fc9008fbf8ab22841dcc268a626aa550561743714ce3bf/orjson-3.11.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f50979824bde13d32b4320eedd513431c921102796d86be3eee0b58e58a3ecd1", size = 141667, upload-time = "2026-02-02T15:38:13.398Z" }, - { url = "https://files.pythonhosted.org/packages/61/e8/0be9b0addd9bf86abfc938e97441dcd0375d494594b1c8ad10fe57479617/orjson-3.11.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e54f3808e2b6b945078c41aa8d9b5834b28c50843846e97807e5adb75fa9705", size = 130832, upload-time = "2026-02-02T15:38:14.698Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ec/c68e3b9021a31d9ec15a94931db1410136af862955854ed5dd7e7e4f5bff/orjson-3.11.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12b80df61aab7b98b490fe9e4879925ba666fccdfcd175252ce4d9035865ace", size = 133373, upload-time = "2026-02-02T15:38:16.109Z" }, - { url = "https://files.pythonhosted.org/packages/d2/45/f3466739aaafa570cc8e77c6dbb853c48bf56e3b43738020e2661e08b0ac/orjson-3.11.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:996b65230271f1a97026fd0e6a753f51fbc0c335d2ad0c6201f711b0da32693b", size = 138307, upload-time = "2026-02-02T15:38:17.453Z" }, - { url = "https://files.pythonhosted.org/packages/e1/84/9f7f02288da1ffb31405c1be07657afd1eecbcb4b64ee2817b6fe0f785fa/orjson-3.11.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ab49d4b2a6a1d415ddb9f37a21e02e0d5dbfe10b7870b21bf779fc21e9156157", size = 408695, upload-time = "2026-02-02T15:38:18.831Z" }, - { url = "https://files.pythonhosted.org/packages/18/07/9dd2f0c0104f1a0295ffbe912bc8d63307a539b900dd9e2c48ef7810d971/orjson-3.11.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:390a1dce0c055ddf8adb6aa94a73b45a4a7d7177b5c584b8d1c1947f2ba60fb3", size = 144099, upload-time = "2026-02-02T15:38:20.28Z" }, - { url = "https://files.pythonhosted.org/packages/a5/66/857a8e4a3292e1f7b1b202883bcdeb43a91566cf59a93f97c53b44bd6801/orjson-3.11.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1eb80451a9c351a71dfaf5b7ccc13ad065405217726b59fdbeadbcc544f9d223", size = 134806, upload-time = "2026-02-02T15:38:22.186Z" }, - { url = "https://files.pythonhosted.org/packages/0a/5b/6ebcf3defc1aab3a338ca777214966851e92efb1f30dc7fc8285216e6d1b/orjson-3.11.7-cp313-cp313-win32.whl", hash = "sha256:7477aa6a6ec6139c5cb1cc7b214643592169a5494d200397c7fc95d740d5fcf3", size = 127914, upload-time = "2026-02-02T15:38:23.511Z" }, - { url = "https://files.pythonhosted.org/packages/00/04/c6f72daca5092e3117840a1b1e88dfc809cc1470cf0734890d0366b684a1/orjson-3.11.7-cp313-cp313-win_amd64.whl", hash = "sha256:b9f95dcdea9d4f805daa9ddf02617a89e484c6985fa03055459f90e87d7a0757", size = 124986, upload-time = "2026-02-02T15:38:24.836Z" }, - { url = "https://files.pythonhosted.org/packages/03/ba/077a0f6f1085d6b806937246860fafbd5b17f3919c70ee3f3d8d9c713f38/orjson-3.11.7-cp313-cp313-win_arm64.whl", hash = "sha256:800988273a014a0541483dc81021247d7eacb0c845a9d1a34a422bc718f41539", size = 126045, upload-time = "2026-02-02T15:38:26.216Z" }, - { url = "https://files.pythonhosted.org/packages/e9/1e/745565dca749813db9a093c5ebc4bac1a9475c64d54b95654336ac3ed961/orjson-3.11.7-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:de0a37f21d0d364954ad5de1970491d7fbd0fb1ef7417d4d56a36dc01ba0c0a0", size = 228391, upload-time = "2026-02-02T15:38:27.757Z" }, - { url = "https://files.pythonhosted.org/packages/46/19/e40f6225da4d3aa0c8dc6e5219c5e87c2063a560fe0d72a88deb59776794/orjson-3.11.7-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:c2428d358d85e8da9d37cba18b8c4047c55222007a84f97156a5b22028dfbfc0", size = 125188, upload-time = "2026-02-02T15:38:29.241Z" }, - { url = "https://files.pythonhosted.org/packages/9d/7e/c4de2babef2c0817fd1f048fd176aa48c37bec8aef53d2fa932983032cce/orjson-3.11.7-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c4bc6c6ac52cdaa267552544c73e486fecbd710b7ac09bc024d5a78555a22f6", size = 128097, upload-time = "2026-02-02T15:38:30.618Z" }, - { url = "https://files.pythonhosted.org/packages/eb/74/233d360632bafd2197f217eee7fb9c9d0229eac0c18128aee5b35b0014fe/orjson-3.11.7-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd0d68edd7dfca1b2eca9361a44ac9f24b078de3481003159929a0573f21a6bf", size = 123364, upload-time = "2026-02-02T15:38:32.363Z" }, - { url = "https://files.pythonhosted.org/packages/79/51/af79504981dd31efe20a9e360eb49c15f06df2b40e7f25a0a52d9ae888e8/orjson-3.11.7-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:623ad1b9548ef63886319c16fa317848e465a21513b31a6ad7b57443c3e0dcf5", size = 129076, upload-time = "2026-02-02T15:38:33.68Z" }, - { url = "https://files.pythonhosted.org/packages/67/e2/da898eb68b72304f8de05ca6715870d09d603ee98d30a27e8a9629abc64b/orjson-3.11.7-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6e776b998ac37c0396093d10290e60283f59cfe0fc3fccbd0ccc4bd04dd19892", size = 141705, upload-time = "2026-02-02T15:38:34.989Z" }, - { url = "https://files.pythonhosted.org/packages/c5/89/15364d92acb3d903b029e28d834edb8780c2b97404cbf7929aa6b9abdb24/orjson-3.11.7-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:652c6c3af76716f4a9c290371ba2e390ede06f6603edb277b481daf37f6f464e", size = 130855, upload-time = "2026-02-02T15:38:36.379Z" }, - { url = "https://files.pythonhosted.org/packages/c2/8b/ecdad52d0b38d4b8f514be603e69ccd5eacf4e7241f972e37e79792212ec/orjson-3.11.7-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a56df3239294ea5964adf074c54bcc4f0ccd21636049a2cf3ca9cf03b5d03cf1", size = 133386, upload-time = "2026-02-02T15:38:37.704Z" }, - { url = "https://files.pythonhosted.org/packages/b9/0e/45e1dcf10e17d0924b7c9162f87ec7b4ca79e28a0548acf6a71788d3e108/orjson-3.11.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bda117c4148e81f746655d5a3239ae9bd00cb7bc3ca178b5fc5a5997e9744183", size = 138295, upload-time = "2026-02-02T15:38:39.096Z" }, - { url = "https://files.pythonhosted.org/packages/63/d7/4d2e8b03561257af0450f2845b91fbd111d7e526ccdf737267108075e0ba/orjson-3.11.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:23d6c20517a97a9daf1d48b580fcdc6f0516c6f4b5038823426033690b4d2650", size = 408720, upload-time = "2026-02-02T15:38:40.634Z" }, - { url = "https://files.pythonhosted.org/packages/78/cf/d45343518282108b29c12a65892445fc51f9319dc3c552ceb51bb5905ed2/orjson-3.11.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8ff206156006da5b847c9304b6308a01e8cdbc8cce824e2779a5ba71c3def141", size = 144152, upload-time = "2026-02-02T15:38:42.262Z" }, - { url = "https://files.pythonhosted.org/packages/a9/3a/d6001f51a7275aacd342e77b735c71fa04125a3f93c36fee4526bc8c654e/orjson-3.11.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:962d046ee1765f74a1da723f4b33e3b228fe3a48bd307acce5021dfefe0e29b2", size = 134814, upload-time = "2026-02-02T15:38:43.627Z" }, - { url = "https://files.pythonhosted.org/packages/1d/d3/f19b47ce16820cc2c480f7f1723e17f6d411b3a295c60c8ad3aa9ff1c96a/orjson-3.11.7-cp314-cp314-win32.whl", hash = "sha256:89e13dd3f89f1c38a9c9eba5fbf7cdc2d1feca82f5f290864b4b7a6aac704576", size = 127997, upload-time = "2026-02-02T15:38:45.06Z" }, - { url = "https://files.pythonhosted.org/packages/12/df/172771902943af54bf661a8d102bdf2e7f932127968080632bda6054b62c/orjson-3.11.7-cp314-cp314-win_amd64.whl", hash = "sha256:845c3e0d8ded9c9271cd79596b9b552448b885b97110f628fb687aee2eed11c1", size = 124985, upload-time = "2026-02-02T15:38:46.388Z" }, - { url = "https://files.pythonhosted.org/packages/6f/1c/f2a8d8a1b17514660a614ce5f7aac74b934e69f5abc2700cc7ced882a009/orjson-3.11.7-cp314-cp314-win_arm64.whl", hash = "sha256:4a2e9c5be347b937a2e0203866f12bba36082e89b402ddb9e927d5822e43088d", size = 126038, upload-time = "2026-02-02T15:38:47.703Z" }, +version = "3.11.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/1b/2024d06792d0779f9dbc51531b61c24f76c75b9f4ce05e6f3377a1814cea/orjson-3.11.8.tar.gz", hash = "sha256:96163d9cdc5a202703e9ad1b9ae757d5f0ca62f4fa0cc93d1f27b0e180cc404e", size = 5603832, upload-time = "2026-03-31T16:16:27.878Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/90/5d81f61fe3e4270da80c71442864c091cee3003cc8984c75f413fe742a07/orjson-3.11.8-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e6693ff90018600c72fd18d3d22fa438be26076cd3c823da5f63f7bab28c11cb", size = 229663, upload-time = "2026-03-31T16:14:30.708Z" }, + { url = "https://files.pythonhosted.org/packages/6c/ef/85e06b0eb11de6fb424120fd5788a07035bd4c5e6bb7841ae9972a0526d1/orjson-3.11.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93de06bc920854552493c81f1f729fab7213b7db4b8195355db5fda02c7d1363", size = 132321, upload-time = "2026-03-31T16:14:32.317Z" }, + { url = "https://files.pythonhosted.org/packages/86/71/089338ee51b3132f050db0864a7df9bdd5e94c2a03820ab8a91e8f655618/orjson-3.11.8-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fe0b8c83e0f36247fc9431ce5425a5d95f9b3a689133d494831bdbd6f0bceb13", size = 130658, upload-time = "2026-03-31T16:14:33.935Z" }, + { url = "https://files.pythonhosted.org/packages/10/0d/f39d8802345d0ad65f7fd4374b29b9b59f98656dc30f21ca5c773265b2f0/orjson-3.11.8-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97d823831105c01f6c8029faf297633dbeb30271892bd430e9c24ceae3734744", size = 135708, upload-time = "2026-03-31T16:14:35.224Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b5/40aae576b3473511696dcffea84fde638b2b64774eb4dcb8b2c262729f8a/orjson-3.11.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c60c0423f15abb6cf78f56dff00168a1b582f7a1c23f114036e2bfc697814d5f", size = 147047, upload-time = "2026-03-31T16:14:36.489Z" }, + { url = "https://files.pythonhosted.org/packages/7b/f0/778a84458d1fdaa634b2e572e51ce0b354232f580b2327e1f00a8d88c38c/orjson-3.11.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01928d0476b216ad2201823b0a74000440360cef4fed1912d297b8d84718f277", size = 133072, upload-time = "2026-03-31T16:14:37.715Z" }, + { url = "https://files.pythonhosted.org/packages/bf/d3/1bbf2fc3ffcc4b829ade554b574af68cec898c9b5ad6420a923c75a073d3/orjson-3.11.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a4a639049c44d36a6d1ae0f4a94b271605c745aee5647fa8ffaabcdc01b69a6", size = 133867, upload-time = "2026-03-31T16:14:39.356Z" }, + { url = "https://files.pythonhosted.org/packages/08/94/6413da22edc99a69a8d0c2e83bf42973b8aa94d83ef52a6d39ac85da00bc/orjson-3.11.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3222adff1e1ff0dce93c16146b93063a7793de6c43d52309ae321234cdaf0f4d", size = 142268, upload-time = "2026-03-31T16:14:40.972Z" }, + { url = "https://files.pythonhosted.org/packages/4a/5f/aa5dbaa6136d7ba55f5461ac2e885efc6e6349424a428927fd46d68f4396/orjson-3.11.8-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3223665349bbfb68da234acd9846955b1a0808cbe5520ff634bf253a4407009b", size = 424008, upload-time = "2026-03-31T16:14:42.637Z" }, + { url = "https://files.pythonhosted.org/packages/fa/aa/2c1962d108c7fe5e27aa03a354b378caf56d8eafdef15fd83dec081ce45a/orjson-3.11.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:61c9d357a59465736022d5d9ba06687afb7611dfb581a9d2129b77a6fcf78e59", size = 147942, upload-time = "2026-03-31T16:14:44.256Z" }, + { url = "https://files.pythonhosted.org/packages/47/d1/65f404f4c47eb1b0b4476f03ec838cac0c4aa933920ff81e5dda4dee14e7/orjson-3.11.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58fb9b17b4472c7b1dcf1a54583629e62e23779b2331052f09a9249edf81675b", size = 136640, upload-time = "2026-03-31T16:14:45.884Z" }, + { url = "https://files.pythonhosted.org/packages/90/5f/7b784aea98bdb125a2f2da7c27d6c2d2f6d943d96ef0278bae596d563f85/orjson-3.11.8-cp310-cp310-win32.whl", hash = "sha256:b43dc2a391981d36c42fa57747a49dae793ef1d2e43898b197925b5534abd10a", size = 132066, upload-time = "2026-03-31T16:14:47.397Z" }, + { url = "https://files.pythonhosted.org/packages/92/ec/2e284af8d6c9478df5ef938917743f61d68f4c70d17f1b6e82f7e3b8dba1/orjson-3.11.8-cp310-cp310-win_amd64.whl", hash = "sha256:c98121237fea2f679480765abd566f7713185897f35c9e6c2add7e3a9900eb61", size = 127609, upload-time = "2026-03-31T16:14:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/67/41/5aa7fa3b0f4dc6b47dcafc3cea909299c37e40e9972feabc8b6a74e2730d/orjson-3.11.8-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:003646067cc48b7fcab2ae0c562491c9b5d2cbd43f1e5f16d98fd118c5522d34", size = 229229, upload-time = "2026-03-31T16:14:50.424Z" }, + { url = "https://files.pythonhosted.org/packages/0a/d7/57e7f2458e0a2c41694f39fc830030a13053a84f837a5b73423dca1f0938/orjson-3.11.8-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:ed193ce51d77a3830cad399a529cd4ef029968761f43ddc549e1bc62b40d88f8", size = 128871, upload-time = "2026-03-31T16:14:51.888Z" }, + { url = "https://files.pythonhosted.org/packages/53/4a/e0fdb9430983e6c46e0299559275025075568aad5d21dd606faee3703924/orjson-3.11.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30491bc4f862aa15744b9738517454f1e46e56c972a2be87d70d727d5b2a8f8", size = 132104, upload-time = "2026-03-31T16:14:53.142Z" }, + { url = "https://files.pythonhosted.org/packages/08/4a/2025a60ff3f5c8522060cda46612d9b1efa653de66ed2908591d8d82f22d/orjson-3.11.8-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6eda5b8b6be91d3f26efb7dc6e5e68ee805bc5617f65a328587b35255f138bf4", size = 130483, upload-time = "2026-03-31T16:14:54.605Z" }, + { url = "https://files.pythonhosted.org/packages/2d/3c/b9cde05bdc7b2385c66014e0620627da638d3d04e4954416ab48c31196c5/orjson-3.11.8-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee8db7bfb6fe03581bbab54d7c4124a6dd6a7f4273a38f7267197890f094675f", size = 135481, upload-time = "2026-03-31T16:14:55.901Z" }, + { url = "https://files.pythonhosted.org/packages/ff/f2/a8238e7734de7cb589fed319857a8025d509c89dc52fdcc88f39c6d03d5a/orjson-3.11.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d8b5231de76c528a46b57010bbd83fb51e056aa0220a372fd5065e978406f1c", size = 146819, upload-time = "2026-03-31T16:14:57.548Z" }, + { url = "https://files.pythonhosted.org/packages/db/10/dbf1e2a3cafea673b1b4350e371877b759060d6018a998643b7040e5de48/orjson-3.11.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58a4a208a6fbfdb7a7327b8f201c6014f189f721fd55d047cafc4157af1bc62a", size = 132846, upload-time = "2026-03-31T16:14:58.91Z" }, + { url = "https://files.pythonhosted.org/packages/f8/fc/55e667ec9c85694038fcff00573d221b085d50777368ee3d77f38668bf3c/orjson-3.11.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f8952d6d2505c003e8f0224ff7858d341fa4e33fef82b91c4ff0ef070f2393c", size = 133580, upload-time = "2026-03-31T16:15:00.519Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a6/c08c589a9aad0cb46c4831d17de212a2b6901f9d976814321ff8e69e8785/orjson-3.11.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0022bb50f90da04b009ce32c512dc1885910daa7cb10b7b0cba4505b16db82a8", size = 142042, upload-time = "2026-03-31T16:15:01.906Z" }, + { url = "https://files.pythonhosted.org/packages/5c/cc/2f78ea241d52b717d2efc38878615fe80425bf2beb6e68c984dde257a766/orjson-3.11.8-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ff51f9d657d1afb6f410cb435792ce4e1fe427aab23d2fcd727a2876e21d4cb6", size = 423845, upload-time = "2026-03-31T16:15:03.703Z" }, + { url = "https://files.pythonhosted.org/packages/70/07/c17dcf05dd8045457538428a983bf1f1127928df5bf328cb24d2b7cddacb/orjson-3.11.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6dbe9a97bdb4d8d9d5367b52a7c32549bba70b2739c58ef74a6964a6d05ae054", size = 147729, upload-time = "2026-03-31T16:15:05.203Z" }, + { url = "https://files.pythonhosted.org/packages/90/6c/0fb6e8a24e682e0958d71711ae6f39110e4b9cd8cab1357e2a89cb8e1951/orjson-3.11.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5c370674ebabe16c6ccac33ff80c62bf8a6e59439f5e9d40c1f5ab8fd2215b7", size = 136425, upload-time = "2026-03-31T16:15:07.052Z" }, + { url = "https://files.pythonhosted.org/packages/b2/35/4d3cc3a3d616035beb51b24a09bb872942dc452cf2df0c1d11ab35046d9f/orjson-3.11.8-cp311-cp311-win32.whl", hash = "sha256:0e32f7154299f42ae66f13488963269e5eccb8d588a65bc839ed986919fc9fac", size = 131870, upload-time = "2026-03-31T16:15:08.678Z" }, + { url = "https://files.pythonhosted.org/packages/13/26/9fe70f81d16b702f8c3a775e8731b50ad91d22dacd14c7599b60a0941cd1/orjson-3.11.8-cp311-cp311-win_amd64.whl", hash = "sha256:25e0c672a2e32348d2eb33057b41e754091f2835f87222e4675b796b92264f06", size = 127440, upload-time = "2026-03-31T16:15:09.994Z" }, + { url = "https://files.pythonhosted.org/packages/e8/c6/b038339f4145efd2859c1ca53097a52c0bb9cbdd24f947ebe146da1ad067/orjson-3.11.8-cp311-cp311-win_arm64.whl", hash = "sha256:9185589c1f2a944c17e26c9925dcdbc2df061cc4a145395c57f0c51f9b5dbfcd", size = 127399, upload-time = "2026-03-31T16:15:11.412Z" }, + { url = "https://files.pythonhosted.org/packages/01/f6/8d58b32ab32d9215973a1688aebd098252ee8af1766c0e4e36e7831f0295/orjson-3.11.8-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1cd0b77e77c95758f8e1100139844e99f3ccc87e71e6fc8e1c027e55807c549f", size = 229233, upload-time = "2026-03-31T16:15:12.762Z" }, + { url = "https://files.pythonhosted.org/packages/a9/8b/2ffe35e71f6b92622e8ea4607bf33ecf7dfb51b3619dcfabfd36cbe2d0a5/orjson-3.11.8-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:6a3d159d5ffa0e3961f353c4b036540996bf8b9697ccc38261c0eac1fd3347a6", size = 128772, upload-time = "2026-03-31T16:15:14.237Z" }, + { url = "https://files.pythonhosted.org/packages/27/d2/1f8682ae50d5c6897a563cb96bc106da8c9cb5b7b6e81a52e4cc086679b9/orjson-3.11.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76070a76e9c5ae661e2d9848f216980d8d533e0f8143e6ed462807b242e3c5e8", size = 131946, upload-time = "2026-03-31T16:15:15.607Z" }, + { url = "https://files.pythonhosted.org/packages/52/4b/5500f76f0eece84226e0689cb48dcde081104c2fa6e2483d17ca13685ffb/orjson-3.11.8-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:54153d21520a71a4c82a0dbb4523e468941d549d221dc173de0f019678cf3813", size = 130368, upload-time = "2026-03-31T16:15:17.066Z" }, + { url = "https://files.pythonhosted.org/packages/da/4e/58b927e08fbe9840e6c920d9e299b051ea667463b1f39a56e668669f8508/orjson-3.11.8-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:469ac2125611b7c5741a0b3798cd9e5786cbad6345f9f400c77212be89563bec", size = 135540, upload-time = "2026-03-31T16:15:18.404Z" }, + { url = "https://files.pythonhosted.org/packages/56/7c/ba7cb871cba1bcd5cd02ee34f98d894c6cea96353ad87466e5aef2429c60/orjson-3.11.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14778ffd0f6896aa613951a7fbf4690229aa7a543cb2bfbe9f358e08aafa9546", size = 146877, upload-time = "2026-03-31T16:15:19.833Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5d/eb9c25fc1386696c6a342cd361c306452c75e0b55e86ad602dd4827a7fd7/orjson-3.11.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea56a955056a6d6c550cf18b3348656a9d9a4f02e2d0c02cabf3c73f1055d506", size = 132837, upload-time = "2026-03-31T16:15:21.282Z" }, + { url = "https://files.pythonhosted.org/packages/37/87/5ddeb7fc1fbd9004aeccab08426f34c81a5b4c25c7061281862b015fce2b/orjson-3.11.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53a0f57e59a530d18a142f4d4ba6dfc708dc5fdedce45e98ff06b44930a2a48f", size = 133624, upload-time = "2026-03-31T16:15:22.641Z" }, + { url = "https://files.pythonhosted.org/packages/22/09/90048793db94ee4b2fcec4ac8e5ddb077367637d6650be896b3494b79bb7/orjson-3.11.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b48e274f8824567d74e2158199e269597edf00823a1b12b63d48462bbf5123e", size = 141904, upload-time = "2026-03-31T16:15:24.435Z" }, + { url = "https://files.pythonhosted.org/packages/c0/cf/eb284847487821a5d415e54149a6449ba9bfc5872ce63ab7be41b8ec401c/orjson-3.11.8-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3f262401086a3960586af06c054609365e98407151f5ea24a62893a40d80dbbb", size = 423742, upload-time = "2026-03-31T16:15:26.155Z" }, + { url = "https://files.pythonhosted.org/packages/44/09/e12423d327071c851c13e76936f144a96adacfc037394dec35ac3fc8d1e8/orjson-3.11.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8e8c6218b614badf8e229b697865df4301afa74b791b6c9ade01d19a9953a942", size = 147806, upload-time = "2026-03-31T16:15:27.909Z" }, + { url = "https://files.pythonhosted.org/packages/b3/6d/37c2589ba864e582ffe7611643314785c6afb1f83c701654ef05daa8fcc7/orjson-3.11.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:093d489fa039ddade2db541097dbb484999fcc65fc2b0ff9819141e2ab364f25", size = 136485, upload-time = "2026-03-31T16:15:29.749Z" }, + { url = "https://files.pythonhosted.org/packages/be/c9/135194a02ab76b04ed9a10f68624b7ebd238bbe55548878b11ff15a0f352/orjson-3.11.8-cp312-cp312-win32.whl", hash = "sha256:e0950ed1bcb9893f4293fd5c5a7ee10934fbf82c4101c70be360db23ce24b7d2", size = 131966, upload-time = "2026-03-31T16:15:31.687Z" }, + { url = "https://files.pythonhosted.org/packages/ed/9a/9796f8fbe3cf30ce9cb696748dbb535e5c87be4bf4fe2e9ca498ef1fa8cf/orjson-3.11.8-cp312-cp312-win_amd64.whl", hash = "sha256:3cf17c141617b88ced4536b2135c552490f07799f6ad565948ea07bef0dcb9a6", size = 127441, upload-time = "2026-03-31T16:15:33.333Z" }, + { url = "https://files.pythonhosted.org/packages/cc/47/5aaf54524a7a4a0dd09dd778f3fa65dd2108290615b652e23d944152bc8e/orjson-3.11.8-cp312-cp312-win_arm64.whl", hash = "sha256:48854463b0572cc87dac7d981aa72ed8bf6deedc0511853dc76b8bbd5482d36d", size = 127364, upload-time = "2026-03-31T16:15:34.748Z" }, + { url = "https://files.pythonhosted.org/packages/66/7f/95fba509bb2305fab0073558f1e8c3a2ec4b2afe58ed9fcb7d3b8beafe94/orjson-3.11.8-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:3f23426851d98478c8970da5991f84784a76682213cd50eb73a1da56b95239dc", size = 229180, upload-time = "2026-03-31T16:15:36.426Z" }, + { url = "https://files.pythonhosted.org/packages/f6/9d/b237215c743ca073697d759b5503abd2cb8a0d7b9c9e21f524bcf176ab66/orjson-3.11.8-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:ebaed4cef74a045b83e23537b52ef19a367c7e3f536751e355a2a394f8648559", size = 128754, upload-time = "2026-03-31T16:15:38.049Z" }, + { url = "https://files.pythonhosted.org/packages/42/3d/27d65b6d11e63f133781425f132807aef793ed25075fec686fc8e46dd528/orjson-3.11.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97c8f5d3b62380b70c36ffacb2a356b7c6becec86099b177f73851ba095ef623", size = 131877, upload-time = "2026-03-31T16:15:39.484Z" }, + { url = "https://files.pythonhosted.org/packages/dd/cc/faee30cd8f00421999e40ef0eba7332e3a625ce91a58200a2f52c7fef235/orjson-3.11.8-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:436c4922968a619fb7fef1ccd4b8b3a76c13b67d607073914d675026e911a65c", size = 130361, upload-time = "2026-03-31T16:15:41.274Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bb/a6c55896197f97b6d4b4e7c7fd77e7235517c34f5d6ad5aadd43c54c6d7c/orjson-3.11.8-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ab359aff0436d80bfe8a23b46b5fea69f1e18aaf1760a709b4787f1318b317f", size = 135521, upload-time = "2026-03-31T16:15:42.758Z" }, + { url = "https://files.pythonhosted.org/packages/9c/7c/ca3a3525aa32ff636ebb1778e77e3587b016ab2edb1b618b36ba96f8f2c0/orjson-3.11.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f89b6d0b3a8d81e1929d3ab3d92bbc225688bd80a770c49432543928fe09ac55", size = 146862, upload-time = "2026-03-31T16:15:44.341Z" }, + { url = "https://files.pythonhosted.org/packages/3c/0c/18a9d7f18b5edd37344d1fd5be17e94dc652c67826ab749c6e5948a78112/orjson-3.11.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c009e7a2ca9ad0ed1376ce20dd692146a5d9fe4310848904b6b4fee5c5c137", size = 132847, upload-time = "2026-03-31T16:15:46.368Z" }, + { url = "https://files.pythonhosted.org/packages/23/91/7e722f352ad67ca573cee44de2a58fb810d0f4eb4e33276c6a557979fd8a/orjson-3.11.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b895b781b3e395c067129d8551655642dfe9437273211d5404e87ac752b53", size = 133637, upload-time = "2026-03-31T16:15:48.123Z" }, + { url = "https://files.pythonhosted.org/packages/af/04/32845ce13ac5bd1046ddb02ac9432ba856cc35f6d74dde95864fe0ad5523/orjson-3.11.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:88006eda83858a9fdf73985ce3804e885c2befb2f506c9a3723cdeb5a2880e3e", size = 141906, upload-time = "2026-03-31T16:15:49.626Z" }, + { url = "https://files.pythonhosted.org/packages/02/5e/c551387ddf2d7106d9039369862245c85738b828844d13b99ccb8d61fd06/orjson-3.11.8-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:55120759e61309af7fcf9e961c6f6af3dde5921cdb3ee863ef63fd9db126cae6", size = 423722, upload-time = "2026-03-31T16:15:51.176Z" }, + { url = "https://files.pythonhosted.org/packages/00/a3/ecfe62434096f8a794d4976728cb59bcfc4a643977f21c2040545d37eb4c/orjson-3.11.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:98bdc6cb889d19bed01de46e67574a2eab61f5cc6b768ed50e8ac68e9d6ffab6", size = 147801, upload-time = "2026-03-31T16:15:52.939Z" }, + { url = "https://files.pythonhosted.org/packages/18/6d/0dce10b9f6643fdc59d99333871a38fa5a769d8e2fc34a18e5d2bfdee900/orjson-3.11.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:708c95f925a43ab9f34625e45dcdadf09ec8a6e7b664a938f2f8d5650f6c090b", size = 136460, upload-time = "2026-03-31T16:15:54.431Z" }, + { url = "https://files.pythonhosted.org/packages/01/d6/6dde4f31842d87099238f1f07b459d24edc1a774d20687187443ab044191/orjson-3.11.8-cp313-cp313-win32.whl", hash = "sha256:01c4e5a6695dc09098f2e6468a251bc4671c50922d4d745aff1a0a33a0cf5b8d", size = 131956, upload-time = "2026-03-31T16:15:56.081Z" }, + { url = "https://files.pythonhosted.org/packages/c1/f9/4e494a56e013db957fb77186b818b916d4695b8fa2aa612364974160e91b/orjson-3.11.8-cp313-cp313-win_amd64.whl", hash = "sha256:c154a35dd1330707450bb4d4e7dd1f17fa6f42267a40c1e8a1daa5e13719b4b8", size = 127410, upload-time = "2026-03-31T16:15:57.54Z" }, + { url = "https://files.pythonhosted.org/packages/57/7f/803203d00d6edb6e9e7eef421d4e1adbb5ea973e40b3533f3cfd9aeb374e/orjson-3.11.8-cp313-cp313-win_arm64.whl", hash = "sha256:4861bde57f4d253ab041e374f44023460e60e71efaa121f3c5f0ed457c3a701e", size = 127338, upload-time = "2026-03-31T16:15:59.106Z" }, + { url = "https://files.pythonhosted.org/packages/6d/35/b01910c3d6b85dc882442afe5060cbf719c7d1fc85749294beda23d17873/orjson-3.11.8-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ec795530a73c269a55130498842aaa762e4a939f6ce481a7e986eeaa790e9da4", size = 229171, upload-time = "2026-03-31T16:16:00.651Z" }, + { url = "https://files.pythonhosted.org/packages/c2/56/c9ec97bd11240abef39b9e5d99a15462809c45f677420fd148a6c5e6295e/orjson-3.11.8-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:c492a0e011c0f9066e9ceaa896fbc5b068c54d365fea5f3444b697ee01bc8625", size = 128746, upload-time = "2026-03-31T16:16:02.673Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e4/66d4f30a90de45e2f0cbd9623588e8ae71eef7679dbe2ae954ed6d66a41f/orjson-3.11.8-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:883206d55b1bd5f5679ad5e6ddd3d1a5e3cac5190482927fdb8c78fb699193b5", size = 131867, upload-time = "2026-03-31T16:16:04.342Z" }, + { url = "https://files.pythonhosted.org/packages/19/30/2a645fc9286b928675e43fa2a3a16fb7b6764aa78cc719dc82141e00f30b/orjson-3.11.8-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5774c1fdcc98b2259800b683b19599c133baeb11d60033e2095fd9d4667b82db", size = 124664, upload-time = "2026-03-31T16:16:05.837Z" }, + { url = "https://files.pythonhosted.org/packages/db/44/77b9a86d84a28d52ba3316d77737f6514e17118119ade3f91b639e859029/orjson-3.11.8-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7381c83dd3d4a6347e6635950aa448f54e7b8406a27c7ecb4a37e9f1ae08b", size = 129701, upload-time = "2026-03-31T16:16:07.407Z" }, + { url = "https://files.pythonhosted.org/packages/b3/ea/eff3d9bfe47e9bc6969c9181c58d9f71237f923f9c86a2d2f490cd898c82/orjson-3.11.8-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14439063aebcb92401c11afc68ee4e407258d2752e62d748b6942dad20d2a70d", size = 141202, upload-time = "2026-03-31T16:16:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/52/c8/90d4b4c60c84d62068d0cf9e4d8f0a4e05e76971d133ac0c60d818d4db20/orjson-3.11.8-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa72e71977bff96567b0f500fc5bfd2fdf915f34052c782a4c6ebbdaa97aa858", size = 127194, upload-time = "2026-03-31T16:16:11.02Z" }, + { url = "https://files.pythonhosted.org/packages/8d/c7/ea9e08d1f0ba981adffb629811148b44774d935171e7b3d780ae43c4c254/orjson-3.11.8-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7679bc2f01bb0d219758f1a5f87bb7c8a81c0a186824a393b366876b4948e14f", size = 133639, upload-time = "2026-03-31T16:16:13.434Z" }, + { url = "https://files.pythonhosted.org/packages/6c/8c/ddbbfd6ba59453c8fc7fe1d0e5983895864e264c37481b2a791db635f046/orjson-3.11.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:14f7b8fcb35ef403b42fa5ecfa4ed032332a91f3dc7368fbce4184d59e1eae0d", size = 141914, upload-time = "2026-03-31T16:16:14.955Z" }, + { url = "https://files.pythonhosted.org/packages/4e/31/dbfbefec9df060d34ef4962cd0afcb6fa7a9ec65884cb78f04a7859526c3/orjson-3.11.8-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c2bdf7b2facc80b5e34f48a2d557727d5c5c57a8a450de122ae81fa26a81c1bc", size = 423800, upload-time = "2026-03-31T16:16:16.594Z" }, + { url = "https://files.pythonhosted.org/packages/87/cf/f74e9ae9803d4ab46b163494adba636c6d7ea955af5cc23b8aaa94cfd528/orjson-3.11.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ccd7ba1b0605813a0715171d39ec4c314cb97a9c85893c2c5c0c3a3729df38bf", size = 147837, upload-time = "2026-03-31T16:16:18.585Z" }, + { url = "https://files.pythonhosted.org/packages/64/e6/9214f017b5db85e84e68602792f742e5dc5249e963503d1b356bee611e01/orjson-3.11.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbc8c9c02463fef4d3c53a9ba3336d05496ec8e1f1c53326a1e4acc11f5c600", size = 136441, upload-time = "2026-03-31T16:16:20.151Z" }, + { url = "https://files.pythonhosted.org/packages/24/dd/3590348818f58f837a75fb969b04cdf187ae197e14d60b5e5a794a38b79d/orjson-3.11.8-cp314-cp314-win32.whl", hash = "sha256:0b57f67710a8cd459e4e54eb96d5f77f3624eba0c661ba19a525807e42eccade", size = 131983, upload-time = "2026-03-31T16:16:21.823Z" }, + { url = "https://files.pythonhosted.org/packages/3f/0f/b6cb692116e05d058f31ceee819c70f097fa9167c82f67fabe7516289abc/orjson-3.11.8-cp314-cp314-win_amd64.whl", hash = "sha256:735e2262363dcbe05c35e3a8869898022af78f89dde9e256924dc02e99fe69ca", size = 127396, upload-time = "2026-03-31T16:16:23.685Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d1/facb5b5051fabb0ef9d26c6544d87ef19a939a9a001198655d0d891062dd/orjson-3.11.8-cp314-cp314-win_arm64.whl", hash = "sha256:6ccdea2c213cf9f3d9490cbd5d427693c870753df41e6cb375bd79bcbafc8817", size = 127330, upload-time = "2026-03-31T16:16:25.496Z" }, ] [[package]] @@ -4633,7 +4647,7 @@ wheels = [ [[package]] name = "pandas" -version = "3.0.1" +version = "3.0.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", @@ -4654,55 +4668,55 @@ dependencies = [ { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/0c/b28ed414f080ee0ad153f848586d61d1878f91689950f037f976ce15f6c8/pandas-3.0.1.tar.gz", hash = "sha256:4186a699674af418f655dbd420ed87f50d56b4cd6603784279d9eef6627823c8", size = 4641901, upload-time = "2026-02-17T22:20:16.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/07/c7087e003ceee9b9a82539b40414ec557aa795b584a1a346e89180853d79/pandas-3.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de09668c1bf3b925c07e5762291602f0d789eca1b3a781f99c1c78f6cac0e7ea", size = 10323380, upload-time = "2026-02-17T22:18:16.133Z" }, - { url = "https://files.pythonhosted.org/packages/c1/27/90683c7122febeefe84a56f2cde86a9f05f68d53885cebcc473298dfc33e/pandas-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:24ba315ba3d6e5806063ac6eb717504e499ce30bd8c236d8693a5fd3f084c796", size = 9923455, upload-time = "2026-02-17T22:18:19.13Z" }, - { url = "https://files.pythonhosted.org/packages/0e/f1/ed17d927f9950643bc7631aa4c99ff0cc83a37864470bc419345b656a41f/pandas-3.0.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:406ce835c55bac912f2a0dcfaf27c06d73c6b04a5dde45f1fd3169ce31337389", size = 10753464, upload-time = "2026-02-17T22:18:21.134Z" }, - { url = "https://files.pythonhosted.org/packages/2e/7c/870c7e7daec2a6c7ff2ac9e33b23317230d4e4e954b35112759ea4a924a7/pandas-3.0.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:830994d7e1f31dd7e790045235605ab61cff6c94defc774547e8b7fdfbff3dc7", size = 11255234, upload-time = "2026-02-17T22:18:24.175Z" }, - { url = "https://files.pythonhosted.org/packages/5c/39/3653fe59af68606282b989c23d1a543ceba6e8099cbcc5f1d506a7bae2aa/pandas-3.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a64ce8b0f2de1d2efd2ae40b0abe7f8ae6b29fbfb3812098ed5a6f8e235ad9bf", size = 11767299, upload-time = "2026-02-17T22:18:26.824Z" }, - { url = "https://files.pythonhosted.org/packages/9b/31/1daf3c0c94a849c7a8dab8a69697b36d313b229918002ba3e409265c7888/pandas-3.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9832c2c69da24b602c32e0c7b1b508a03949c18ba08d4d9f1c1033426685b447", size = 12333292, upload-time = "2026-02-17T22:18:28.996Z" }, - { url = "https://files.pythonhosted.org/packages/1f/67/af63f83cd6ca603a00fe8530c10a60f0879265b8be00b5930e8e78c5b30b/pandas-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:84f0904a69e7365f79a0c77d3cdfccbfb05bf87847e3a51a41e1426b0edb9c79", size = 9892176, upload-time = "2026-02-17T22:18:31.79Z" }, - { url = "https://files.pythonhosted.org/packages/79/ab/9c776b14ac4b7b4140788eca18468ea39894bc7340a408f1d1e379856a6b/pandas-3.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:4a68773d5a778afb31d12e34f7dd4612ab90de8c6fb1d8ffe5d4a03b955082a1", size = 9151328, upload-time = "2026-02-17T22:18:35.721Z" }, - { url = "https://files.pythonhosted.org/packages/37/51/b467209c08dae2c624873d7491ea47d2b47336e5403309d433ea79c38571/pandas-3.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:476f84f8c20c9f5bc47252b66b4bb25e1a9fc2fa98cead96744d8116cb85771d", size = 10344357, upload-time = "2026-02-17T22:18:38.262Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f1/e2567ffc8951ab371db2e40b2fe068e36b81d8cf3260f06ae508700e5504/pandas-3.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0ab749dfba921edf641d4036c4c21c0b3ea70fea478165cb98a998fb2a261955", size = 9884543, upload-time = "2026-02-17T22:18:41.476Z" }, - { url = "https://files.pythonhosted.org/packages/d7/39/327802e0b6d693182403c144edacbc27eb82907b57062f23ef5a4c4a5ea7/pandas-3.0.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8e36891080b87823aff3640c78649b91b8ff6eea3c0d70aeabd72ea43ab069b", size = 10396030, upload-time = "2026-02-17T22:18:43.822Z" }, - { url = "https://files.pythonhosted.org/packages/3d/fe/89d77e424365280b79d99b3e1e7d606f5165af2f2ecfaf0c6d24c799d607/pandas-3.0.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:532527a701281b9dd371e2f582ed9094f4c12dd9ffb82c0c54ee28d8ac9520c4", size = 10876435, upload-time = "2026-02-17T22:18:45.954Z" }, - { url = "https://files.pythonhosted.org/packages/b5/a6/2a75320849dd154a793f69c951db759aedb8d1dd3939eeacda9bdcfa1629/pandas-3.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:356e5c055ed9b0da1580d465657bc7d00635af4fd47f30afb23025352ba764d1", size = 11405133, upload-time = "2026-02-17T22:18:48.533Z" }, - { url = "https://files.pythonhosted.org/packages/58/53/1d68fafb2e02d7881df66aa53be4cd748d25cbe311f3b3c85c93ea5d30ca/pandas-3.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9d810036895f9ad6345b8f2a338dd6998a74e8483847403582cab67745bff821", size = 11932065, upload-time = "2026-02-17T22:18:50.837Z" }, - { url = "https://files.pythonhosted.org/packages/75/08/67cc404b3a966b6df27b38370ddd96b3b023030b572283d035181854aac5/pandas-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:536232a5fe26dd989bd633e7a0c450705fdc86a207fec7254a55e9a22950fe43", size = 9741627, upload-time = "2026-02-17T22:18:53.905Z" }, - { url = "https://files.pythonhosted.org/packages/86/4f/caf9952948fb00d23795f09b893d11f1cacb384e666854d87249530f7cbe/pandas-3.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:0f463ebfd8de7f326d38037c7363c6dacb857c5881ab8961fb387804d6daf2f7", size = 9052483, upload-time = "2026-02-17T22:18:57.31Z" }, - { url = "https://files.pythonhosted.org/packages/0b/48/aad6ec4f8d007534c091e9a7172b3ec1b1ee6d99a9cbb936b5eab6c6cf58/pandas-3.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5272627187b5d9c20e55d27caf5f2cd23e286aba25cadf73c8590e432e2b7262", size = 10317509, upload-time = "2026-02-17T22:18:59.498Z" }, - { url = "https://files.pythonhosted.org/packages/a8/14/5990826f779f79148ae9d3a2c39593dc04d61d5d90541e71b5749f35af95/pandas-3.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:661e0f665932af88c7877f31da0dc743fe9c8f2524bdffe23d24fdcb67ef9d56", size = 9860561, upload-time = "2026-02-17T22:19:02.265Z" }, - { url = "https://files.pythonhosted.org/packages/fa/80/f01ff54664b6d70fed71475543d108a9b7c888e923ad210795bef04ffb7d/pandas-3.0.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:75e6e292ff898679e47a2199172593d9f6107fd2dd3617c22c2946e97d5df46e", size = 10365506, upload-time = "2026-02-17T22:19:05.017Z" }, - { url = "https://files.pythonhosted.org/packages/f2/85/ab6d04733a7d6ff32bfc8382bf1b07078228f5d6ebec5266b91bfc5c4ff7/pandas-3.0.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1ff8cf1d2896e34343197685f432450ec99a85ba8d90cce2030c5eee2ef98791", size = 10873196, upload-time = "2026-02-17T22:19:07.204Z" }, - { url = "https://files.pythonhosted.org/packages/48/a9/9301c83d0b47c23ac5deab91c6b39fd98d5b5db4d93b25df8d381451828f/pandas-3.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eca8b4510f6763f3d37359c2105df03a7a221a508f30e396a51d0713d462e68a", size = 11370859, upload-time = "2026-02-17T22:19:09.436Z" }, - { url = "https://files.pythonhosted.org/packages/59/fe/0c1fc5bd2d29c7db2ab372330063ad555fb83e08422829c785f5ec2176ca/pandas-3.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:06aff2ad6f0b94a17822cf8b83bbb563b090ed82ff4fe7712db2ce57cd50d9b8", size = 11924584, upload-time = "2026-02-17T22:19:11.562Z" }, - { url = "https://files.pythonhosted.org/packages/d6/7d/216a1588b65a7aa5f4535570418a599d943c85afb1d95b0876fc00aa1468/pandas-3.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:9fea306c783e28884c29057a1d9baa11a349bbf99538ec1da44c8476563d1b25", size = 9742769, upload-time = "2026-02-17T22:19:13.926Z" }, - { url = "https://files.pythonhosted.org/packages/c4/cb/810a22a6af9a4e97c8ab1c946b47f3489c5bca5adc483ce0ffc84c9cc768/pandas-3.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:a8d37a43c52917427e897cb2e429f67a449327394396a81034a4449b99afda59", size = 9043855, upload-time = "2026-02-17T22:19:16.09Z" }, - { url = "https://files.pythonhosted.org/packages/92/fa/423c89086cca1f039cf1253c3ff5b90f157b5b3757314aa635f6bf3e30aa/pandas-3.0.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d54855f04f8246ed7b6fc96b05d4871591143c46c0b6f4af874764ed0d2d6f06", size = 10752673, upload-time = "2026-02-17T22:19:18.304Z" }, - { url = "https://files.pythonhosted.org/packages/22/23/b5a08ec1f40020397f0faba72f1e2c11f7596a6169c7b3e800abff0e433f/pandas-3.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e1b677accee34a09e0dc2ce5624e4a58a1870ffe56fc021e9caf7f23cd7668f", size = 10404967, upload-time = "2026-02-17T22:19:20.726Z" }, - { url = "https://files.pythonhosted.org/packages/5c/81/94841f1bb4afdc2b52a99daa895ac2c61600bb72e26525ecc9543d453ebc/pandas-3.0.1-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a9cabbdcd03f1b6cd254d6dda8ae09b0252524be1592594c00b7895916cb1324", size = 10320575, upload-time = "2026-02-17T22:19:24.919Z" }, - { url = "https://files.pythonhosted.org/packages/0a/8b/2ae37d66a5342a83adadfd0cb0b4bf9c3c7925424dd5f40d15d6cfaa35ee/pandas-3.0.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ae2ab1f166668b41e770650101e7090824fd34d17915dd9cd479f5c5e0065e9", size = 10710921, upload-time = "2026-02-17T22:19:27.181Z" }, - { url = "https://files.pythonhosted.org/packages/a2/61/772b2e2757855e232b7ccf7cb8079a5711becb3a97f291c953def15a833f/pandas-3.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6bf0603c2e30e2cafac32807b06435f28741135cb8697eae8b28c7d492fc7d76", size = 11334191, upload-time = "2026-02-17T22:19:29.411Z" }, - { url = "https://files.pythonhosted.org/packages/1b/08/b16c6df3ef555d8495d1d265a7963b65be166785d28f06a350913a4fac78/pandas-3.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6c426422973973cae1f4a23e51d4ae85974f44871b24844e4f7de752dd877098", size = 11782256, upload-time = "2026-02-17T22:19:32.34Z" }, - { url = "https://files.pythonhosted.org/packages/55/80/178af0594890dee17e239fca96d3d8670ba0f5ff59b7d0439850924a9c09/pandas-3.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b03f91ae8c10a85c1613102c7bef5229b5379f343030a3ccefeca8a33414cf35", size = 10485047, upload-time = "2026-02-17T22:19:34.605Z" }, - { url = "https://files.pythonhosted.org/packages/bb/8b/4bb774a998b97e6c2fd62a9e6cfdaae133b636fd1c468f92afb4ae9a447a/pandas-3.0.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:99d0f92ed92d3083d140bf6b97774f9f13863924cf3f52a70711f4e7588f9d0a", size = 10322465, upload-time = "2026-02-17T22:19:36.803Z" }, - { url = "https://files.pythonhosted.org/packages/72/3a/5b39b51c64159f470f1ca3b1c2a87da290657ca022f7cd11442606f607d1/pandas-3.0.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3b66857e983208654294bb6477b8a63dee26b37bdd0eb34d010556e91261784f", size = 9910632, upload-time = "2026-02-17T22:19:39.001Z" }, - { url = "https://files.pythonhosted.org/packages/4e/f7/b449ffb3f68c11da12fc06fbf6d2fa3a41c41e17d0284d23a79e1c13a7e4/pandas-3.0.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56cf59638bf24dc9bdf2154c81e248b3289f9a09a6d04e63608c159022352749", size = 10440535, upload-time = "2026-02-17T22:19:41.157Z" }, - { url = "https://files.pythonhosted.org/packages/55/77/6ea82043db22cb0f2bbfe7198da3544000ddaadb12d26be36e19b03a2dc5/pandas-3.0.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1a9f55e0f46951874b863d1f3906dcb57df2d9be5c5847ba4dfb55b2c815249", size = 10893940, upload-time = "2026-02-17T22:19:43.493Z" }, - { url = "https://files.pythonhosted.org/packages/03/30/f1b502a72468c89412c1b882a08f6eed8a4ee9dc033f35f65d0663df6081/pandas-3.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1849f0bba9c8a2fb0f691d492b834cc8dadf617e29015c66e989448d58d011ee", size = 11442711, upload-time = "2026-02-17T22:19:46.074Z" }, - { url = "https://files.pythonhosted.org/packages/0d/f0/ebb6ddd8fc049e98cabac5c2924d14d1dda26a20adb70d41ea2e428d3ec4/pandas-3.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c3d288439e11b5325b02ae6e9cc83e6805a62c40c5a6220bea9beb899c073b1c", size = 11963918, upload-time = "2026-02-17T22:19:48.838Z" }, - { url = "https://files.pythonhosted.org/packages/09/f8/8ce132104074f977f907442790eaae24e27bce3b3b454e82faa3237ff098/pandas-3.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:93325b0fe372d192965f4cca88d97667f49557398bbf94abdda3bf1b591dbe66", size = 9862099, upload-time = "2026-02-17T22:19:51.081Z" }, - { url = "https://files.pythonhosted.org/packages/e6/b7/6af9aac41ef2456b768ef0ae60acf8abcebb450a52043d030a65b4b7c9bd/pandas-3.0.1-cp314-cp314-win_arm64.whl", hash = "sha256:97ca08674e3287c7148f4858b01136f8bdfe7202ad25ad04fec602dd1d29d132", size = 9185333, upload-time = "2026-02-17T22:19:53.266Z" }, - { url = "https://files.pythonhosted.org/packages/66/fc/848bb6710bc6061cb0c5badd65b92ff75c81302e0e31e496d00029fe4953/pandas-3.0.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:58eeb1b2e0fb322befcf2bbc9ba0af41e616abadb3d3414a6bc7167f6cbfce32", size = 10772664, upload-time = "2026-02-17T22:19:55.806Z" }, - { url = "https://files.pythonhosted.org/packages/69/5c/866a9bbd0f79263b4b0db6ec1a341be13a1473323f05c122388e0f15b21d/pandas-3.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cd9af1276b5ca9e298bd79a26bda32fa9cc87ed095b2a9a60978d2ca058eaf87", size = 10421286, upload-time = "2026-02-17T22:19:58.091Z" }, - { url = "https://files.pythonhosted.org/packages/51/a4/2058fb84fb1cfbfb2d4a6d485e1940bb4ad5716e539d779852494479c580/pandas-3.0.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94f87a04984d6b63788327cd9f79dda62b7f9043909d2440ceccf709249ca988", size = 10342050, upload-time = "2026-02-17T22:20:01.376Z" }, - { url = "https://files.pythonhosted.org/packages/22/1b/674e89996cc4be74db3c4eb09240c4bb549865c9c3f5d9b086ff8fcfbf00/pandas-3.0.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85fe4c4df62e1e20f9db6ebfb88c844b092c22cd5324bdcf94bfa2fc1b391221", size = 10740055, upload-time = "2026-02-17T22:20:04.328Z" }, - { url = "https://files.pythonhosted.org/packages/d0/f8/e954b750764298c22fa4614376531fe63c521ef517e7059a51f062b87dca/pandas-3.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:331ca75a2f8672c365ae25c0b29e46f5ac0c6551fdace8eec4cd65e4fac271ff", size = 11357632, upload-time = "2026-02-17T22:20:06.647Z" }, - { url = "https://files.pythonhosted.org/packages/6d/02/c6e04b694ffd68568297abd03588b6d30295265176a5c01b7459d3bc35a3/pandas-3.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:15860b1fdb1973fffade772fdb931ccf9b2f400a3f5665aef94a00445d7d8dd5", size = 11810974, upload-time = "2026-02-17T22:20:08.946Z" }, - { url = "https://files.pythonhosted.org/packages/89/41/d7dfb63d2407f12055215070c42fc6ac41b66e90a2946cdc5e759058398b/pandas-3.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:44f1364411d5670efa692b146c748f4ed013df91ee91e9bec5677fb1fd58b937", size = 10884622, upload-time = "2026-02-17T22:20:11.711Z" }, - { url = "https://files.pythonhosted.org/packages/68/b0/34937815889fa982613775e4b97fddd13250f11012d769949c5465af2150/pandas-3.0.1-cp314-cp314t-win_arm64.whl", hash = "sha256:108dd1790337a494aa80e38def654ca3f0968cf4f362c85f44c15e471667102d", size = 9452085, upload-time = "2026-02-17T22:20:14.331Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/da/99/b342345300f13440fe9fe385c3c481e2d9a595ee3bab4d3219247ac94e9a/pandas-3.0.2.tar.gz", hash = "sha256:f4753e73e34c8d83221ba58f232433fca2748be8b18dbca02d242ed153945043", size = 4645855, upload-time = "2026-03-31T06:48:30.816Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/35/6411db530c618e0e0005187e35aa02ce60ae4c4c4d206964a2f978217c27/pandas-3.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a727a73cbdba2f7458dc82449e2315899d5140b449015d822f515749a46cbbe0", size = 10326926, upload-time = "2026-03-31T06:46:08.29Z" }, + { url = "https://files.pythonhosted.org/packages/c4/d3/b7da1d5d7dbdc5ef52ed7debd2b484313b832982266905315dad5a0bf0b1/pandas-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbbd4aa20ca51e63b53bbde6a0fa4254b1aaabb74d2f542df7a7959feb1d760c", size = 9926987, upload-time = "2026-03-31T06:46:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/52/77/9b1c2d6070b5dbe239a7bc889e21bfa58720793fb902d1e070695d87c6d0/pandas-3.0.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:339dda302bd8369dedeae979cb750e484d549b563c3f54f3922cb8ff4978c5eb", size = 10757067, upload-time = "2026-03-31T06:46:14.903Z" }, + { url = "https://files.pythonhosted.org/packages/20/17/ec40d981705654853726e7ac9aea9ddbb4a5d9cf54d8472222f4f3de06c2/pandas-3.0.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:61c2fd96d72b983a9891b2598f286befd4ad262161a609c92dc1652544b46b76", size = 11258787, upload-time = "2026-03-31T06:46:17.683Z" }, + { url = "https://files.pythonhosted.org/packages/90/e3/3f1126d43d3702ca8773871a81c9f15122a1f412342cc56284ffda5b1f70/pandas-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c934008c733b8bbea273ea308b73b3156f0181e5b72960790b09c18a2794fe1e", size = 11771616, upload-time = "2026-03-31T06:46:20.532Z" }, + { url = "https://files.pythonhosted.org/packages/2e/cf/0f4e268e1f5062e44a6bda9f925806721cd4c95c2b808a4c82ebe914f96b/pandas-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:60a80bb4feacbef5e1447a3f82c33209c8b7e07f28d805cfd1fb951e5cb443aa", size = 12337623, upload-time = "2026-03-31T06:46:23.754Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/97a6339859d4acb2536efb24feb6708e82f7d33b2ed7e036f2983fcced82/pandas-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:ed72cb3f45190874eb579c64fa92d9df74e98fd63e2be7f62bce5ace0ade61df", size = 9897372, upload-time = "2026-03-31T06:46:26.703Z" }, + { url = "https://files.pythonhosted.org/packages/8f/eb/781516b808a99ddf288143cec46b342b3016c3414d137da1fdc3290d8860/pandas-3.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:f12b1a9e332c01e09510586f8ca9b108fd631fd656af82e452d7315ef6df5f9f", size = 9154922, upload-time = "2026-03-31T06:46:30.284Z" }, + { url = "https://files.pythonhosted.org/packages/f3/b0/c20bd4d6d3f736e6bd6b55794e9cd0a617b858eaad27c8f410ea05d953b7/pandas-3.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:232a70ebb568c0c4d2db4584f338c1577d81e3af63292208d615907b698a0f18", size = 10347921, upload-time = "2026-03-31T06:46:33.36Z" }, + { url = "https://files.pythonhosted.org/packages/35/d0/4831af68ce30cc2d03c697bea8450e3225a835ef497d0d70f31b8cdde965/pandas-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:970762605cff1ca0d3f71ed4f3a769ea8f85fc8e6348f6e110b8fea7e6eb5a14", size = 9888127, upload-time = "2026-03-31T06:46:36.253Z" }, + { url = "https://files.pythonhosted.org/packages/61/a9/16ea9346e1fc4a96e2896242d9bc674764fb9049b0044c0132502f7a771e/pandas-3.0.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aff4e6f4d722e0652707d7bcb190c445fe58428500c6d16005b02401764b1b3d", size = 10399577, upload-time = "2026-03-31T06:46:39.224Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a8/3a61a721472959ab0ce865ef05d10b0d6bfe27ce8801c99f33d4fa996e65/pandas-3.0.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef8b27695c3d3dc78403c9a7d5e59a62d5464a7e1123b4e0042763f7104dc74f", size = 10880030, upload-time = "2026-03-31T06:46:42.412Z" }, + { url = "https://files.pythonhosted.org/packages/da/65/7225c0ea4d6ce9cb2160a7fb7f39804871049f016e74782e5dade4d14109/pandas-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f8d68083e49e16b84734eb1a4dcae4259a75c90fb6e2251ab9a00b61120c06ab", size = 11409468, upload-time = "2026-03-31T06:46:45.2Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5b/46e7c76032639f2132359b5cf4c785dd8cf9aea5ea64699eac752f02b9db/pandas-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:32cc41f310ebd4a296d93515fcac312216adfedb1894e879303987b8f1e2b97d", size = 11936381, upload-time = "2026-03-31T06:46:48.293Z" }, + { url = "https://files.pythonhosted.org/packages/7b/8b/721a9cff6fa6a91b162eb51019c6243b82b3226c71bb6c8ef4a9bd65cbc6/pandas-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:a4785e1d6547d8427c5208b748ae2efb64659a21bd82bf440d4262d02bfa02a4", size = 9744993, upload-time = "2026-03-31T06:46:51.488Z" }, + { url = "https://files.pythonhosted.org/packages/d5/18/7f0bd34ae27b28159aa80f2a6799f47fda34f7fb938a76e20c7b7fe3b200/pandas-3.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:08504503f7101300107ecdc8df73658e4347586db5cfdadabc1592e9d7e7a0fd", size = 9056118, upload-time = "2026-03-31T06:46:54.548Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ca/3e639a1ea6fcd0617ca4e8ca45f62a74de33a56ae6cd552735470b22c8d3/pandas-3.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5918ba197c951dec132b0c5929a00c0bf05d5942f590d3c10a807f6e15a57d3", size = 10321105, upload-time = "2026-03-31T06:46:57.327Z" }, + { url = "https://files.pythonhosted.org/packages/0b/77/dbc82ff2fb0e63c6564356682bf201edff0ba16c98630d21a1fb312a8182/pandas-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d606a041c89c0a474a4702d532ab7e73a14fe35c8d427b972a625c8e46373668", size = 9864088, upload-time = "2026-03-31T06:46:59.935Z" }, + { url = "https://files.pythonhosted.org/packages/5c/2b/341f1b04bbca2e17e13cd3f08c215b70ef2c60c5356ef1e8c6857449edc7/pandas-3.0.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:710246ba0616e86891b58ab95f2495143bb2bc83ab6b06747c74216f583a6ac9", size = 10369066, upload-time = "2026-03-31T06:47:02.792Z" }, + { url = "https://files.pythonhosted.org/packages/12/c5/cbb1ffefb20a93d3f0e1fdcda699fb84976210d411b008f97f48bf6ce27e/pandas-3.0.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d3cfe227c725b1f3dff4278b43d8c784656a42a9325b63af6b1492a8232209e", size = 10876780, upload-time = "2026-03-31T06:47:06.205Z" }, + { url = "https://files.pythonhosted.org/packages/98/fe/2249ae5e0a69bd0ddf17353d0a5d26611d70970111f5b3600cdc8be883e7/pandas-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c3b723df9087a9a9a840e263ebd9f88b64a12075d1bf2ea401a5a42f254f084d", size = 11375181, upload-time = "2026-03-31T06:47:09.383Z" }, + { url = "https://files.pythonhosted.org/packages/de/64/77a38b09e70b6464883b8d7584ab543e748e42c1b5d337a2ee088e0df741/pandas-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3096110bf9eac0070b7208465f2740e2d8a670d5cb6530b5bb884eca495fd39", size = 11928899, upload-time = "2026-03-31T06:47:12.686Z" }, + { url = "https://files.pythonhosted.org/packages/5e/52/42855bf626868413f761addd574acc6195880ae247a5346477a4361c3acb/pandas-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:07a10f5c36512eead51bc578eb3354ad17578b22c013d89a796ab5eee90cd991", size = 9746574, upload-time = "2026-03-31T06:47:15.64Z" }, + { url = "https://files.pythonhosted.org/packages/88/39/21304ae06a25e8bf9fc820d69b29b2c495b2ae580d1e143146c309941760/pandas-3.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:5fdbfa05931071aba28b408e59226186b01eb5e92bea2ab78b65863ca3228d84", size = 9047156, upload-time = "2026-03-31T06:47:18.595Z" }, + { url = "https://files.pythonhosted.org/packages/72/20/7defa8b27d4f330a903bb68eea33be07d839c5ea6bdda54174efcec0e1d2/pandas-3.0.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:dbc20dea3b9e27d0e66d74c42b2d0c1bed9c2ffe92adea33633e3bedeb5ac235", size = 10756238, upload-time = "2026-03-31T06:47:22.012Z" }, + { url = "https://files.pythonhosted.org/packages/e9/95/49433c14862c636afc0e9b2db83ff16b3ad92959364e52b2955e44c8e94c/pandas-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b75c347eff42497452116ce05ef461822d97ce5b9ff8df6edacb8076092c855d", size = 10408520, upload-time = "2026-03-31T06:47:25.197Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f8/462ad2b5881d6b8ec8e5f7ed2ea1893faa02290d13870a1600fe72ad8efc/pandas-3.0.2-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1478075142e83a5571782ad007fb201ed074bdeac7ebcc8890c71442e96adf7", size = 10324154, upload-time = "2026-03-31T06:47:28.097Z" }, + { url = "https://files.pythonhosted.org/packages/0a/65/d1e69b649cbcddda23ad6e4c40ef935340f6f652a006e5cbc3555ac8adb3/pandas-3.0.2-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5880314e69e763d4c8b27937090de570f1fb8d027059a7ada3f7f8e98bdcb677", size = 10714449, upload-time = "2026-03-31T06:47:30.85Z" }, + { url = "https://files.pythonhosted.org/packages/47/a4/85b59bc65b8190ea3689882db6cdf32a5003c0ccd5a586c30fdcc3ffc4fc/pandas-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b5329e26898896f06035241a626d7c335daa479b9bbc82be7c2742d048e41172", size = 11338475, upload-time = "2026-03-31T06:47:34.026Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c4/bc6966c6e38e5d9478b935272d124d80a589511ed1612a5d21d36f664c68/pandas-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:81526c4afd31971f8b62671442a4b2b51e0aa9acc3819c9f0f12a28b6fcf85f1", size = 11786568, upload-time = "2026-03-31T06:47:36.941Z" }, + { url = "https://files.pythonhosted.org/packages/e8/74/09298ca9740beed1d3504e073d67e128aa07e5ca5ca2824b0c674c0b8676/pandas-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:7cadd7e9a44ec13b621aec60f9150e744cfc7a3dd32924a7e2f45edff31823b0", size = 10488652, upload-time = "2026-03-31T06:47:40.612Z" }, + { url = "https://files.pythonhosted.org/packages/bb/40/c6ea527147c73b24fc15c891c3fcffe9c019793119c5742b8784a062c7db/pandas-3.0.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:db0dbfd2a6cdf3770aa60464d50333d8f3d9165b2f2671bcc299b72de5a6677b", size = 10326084, upload-time = "2026-03-31T06:47:43.834Z" }, + { url = "https://files.pythonhosted.org/packages/95/25/bdb9326c3b5455f8d4d3549fce7abcf967259de146fe2cf7a82368141948/pandas-3.0.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0555c5882688a39317179ab4a0ed41d3ebc8812ab14c69364bbee8fb7a3f6288", size = 9914146, upload-time = "2026-03-31T06:47:46.67Z" }, + { url = "https://files.pythonhosted.org/packages/8d/77/3a227ff3337aa376c60d288e1d61c5d097131d0ac71f954d90a8f369e422/pandas-3.0.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:01f31a546acd5574ef77fe199bc90b55527c225c20ccda6601cf6b0fd5ed597c", size = 10444081, upload-time = "2026-03-31T06:47:49.681Z" }, + { url = "https://files.pythonhosted.org/packages/15/88/3cdd54fa279341afa10acf8d2b503556b1375245dccc9315659f795dd2e9/pandas-3.0.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:deeca1b5a931fdf0c2212c8a659ade6d3b1edc21f0914ce71ef24456ca7a6535", size = 10897535, upload-time = "2026-03-31T06:47:53.033Z" }, + { url = "https://files.pythonhosted.org/packages/06/9d/98cc7a7624f7932e40f434299260e2917b090a579d75937cb8a57b9d2de3/pandas-3.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0f48afd9bb13300ffb5a3316973324c787054ba6665cda0da3fbd67f451995db", size = 11446992, upload-time = "2026-03-31T06:47:56.193Z" }, + { url = "https://files.pythonhosted.org/packages/9a/cd/19ff605cc3760e80602e6826ddef2824d8e7050ed80f2e11c4b079741dc3/pandas-3.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6c4d8458b97a35717b62469a4ea0e85abd5ed8687277f5ccfc67f8a5126f8c53", size = 11968257, upload-time = "2026-03-31T06:47:59.137Z" }, + { url = "https://files.pythonhosted.org/packages/db/60/aba6a38de456e7341285102bede27514795c1eaa353bc0e7638b6b785356/pandas-3.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:b35d14bb5d8285d9494fe93815a9e9307c0876e10f1e8e89ac5b88f728ec8dcf", size = 9865893, upload-time = "2026-03-31T06:48:02.038Z" }, + { url = "https://files.pythonhosted.org/packages/08/71/e5ec979dd2e8a093dacb8864598c0ff59a0cee0bbcdc0bfec16a51684d4f/pandas-3.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:63d141b56ef686f7f0d714cfb8de4e320475b86bf4b620aa0b7da89af8cbdbbb", size = 9188644, upload-time = "2026-03-31T06:48:05.045Z" }, + { url = "https://files.pythonhosted.org/packages/f1/6c/7b45d85db19cae1eb524f2418ceaa9d85965dcf7b764ed151386b7c540f0/pandas-3.0.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:140f0cffb1fa2524e874dde5b477d9defe10780d8e9e220d259b2c0874c89d9d", size = 10776246, upload-time = "2026-03-31T06:48:07.789Z" }, + { url = "https://files.pythonhosted.org/packages/a8/3e/7b00648b086c106e81766f25322b48aa8dfa95b55e621dbdf2fdd413a117/pandas-3.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae37e833ff4fed0ba352f6bdd8b73ba3ab3256a85e54edfd1ab51ae40cca0af8", size = 10424801, upload-time = "2026-03-31T06:48:10.897Z" }, + { url = "https://files.pythonhosted.org/packages/da/6e/558dd09a71b53b4008e7fc8a98ec6d447e9bfb63cdaeea10e5eb9b2dabe8/pandas-3.0.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d888a5c678a419a5bb41a2a93818e8ed9fd3172246555c0b37b7cc27027effd", size = 10345643, upload-time = "2026-03-31T06:48:13.7Z" }, + { url = "https://files.pythonhosted.org/packages/be/e3/921c93b4d9a280409451dc8d07b062b503bbec0531d2627e73a756e99a82/pandas-3.0.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b444dc64c079e84df91baa8bf613d58405645461cabca929d9178f2cd392398d", size = 10743641, upload-time = "2026-03-31T06:48:16.659Z" }, + { url = "https://files.pythonhosted.org/packages/56/ca/fd17286f24fa3b4d067965d8d5d7e14fe557dd4f979a0b068ac0deaf8228/pandas-3.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4544c7a54920de8eeacaa1466a6b7268ecfbc9bc64ab4dbb89c6bbe94d5e0660", size = 11361993, upload-time = "2026-03-31T06:48:19.475Z" }, + { url = "https://files.pythonhosted.org/packages/e4/a5/2f6ed612056819de445a433ca1f2821ac3dab7f150d569a59e9cc105de1d/pandas-3.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:734be7551687c00fbd760dc0522ed974f82ad230d4a10f54bf51b80d44a08702", size = 11815274, upload-time = "2026-03-31T06:48:22.695Z" }, + { url = "https://files.pythonhosted.org/packages/00/2f/b622683e99ec3ce00b0854bac9e80868592c5b051733f2cf3a868e5fea26/pandas-3.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:57a07209bebcbcf768d2d13c9b78b852f9a15978dac41b9e6421a81ad4cdd276", size = 10888530, upload-time = "2026-03-31T06:48:25.806Z" }, + { url = "https://files.pythonhosted.org/packages/cb/2b/f8434233fab2bd66a02ec014febe4e5adced20e2693e0e90a07d118ed30e/pandas-3.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:5371b72c2d4d415d08765f32d689217a43227484e81b2305b52076e328f6f482", size = 9455341, upload-time = "2026-03-31T06:48:28.418Z" }, ] [[package]] @@ -7247,7 +7261,7 @@ orjson = [ ] pandas = [ { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "pandas", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pandas", version = "3.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pyarrow" }, ] performance = [ From b13d17f10adc79446668044a1ea53d13ee110393 Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Thu, 2 Apr 2026 20:59:13 +0000 Subject: [PATCH 14/15] chore: top level export --- pyproject.toml | 6 - sqlspec/adapters/adbc/__init__.py | 12 +- sqlspec/adapters/adbc/config.py | 2 +- sqlspec/adapters/aiosqlite/config.py | 2 +- sqlspec/adapters/asyncmy/config.py | 2 +- sqlspec/adapters/asyncpg/__init__.py | 8 +- sqlspec/adapters/asyncpg/config.py | 2 +- sqlspec/adapters/bigquery/__init__.py | 3 +- sqlspec/adapters/bigquery/config.py | 2 +- sqlspec/adapters/duckdb/__init__.py | 4 + sqlspec/adapters/duckdb/config.py | 2 +- sqlspec/adapters/oracledb/__init__.py | 2 + sqlspec/adapters/oracledb/config.py | 4 +- sqlspec/adapters/psqlpy/config.py | 2 +- sqlspec/adapters/psycopg/__init__.py | 2 + sqlspec/adapters/psycopg/config.py | 4 +- sqlspec/adapters/sqlite/config.py | 2 +- sqlspec/dialects/postgres/_generators.py | 51 +- sqlspec/dialects/spanner/_generators.py | 137 ++--- sqlspec/extensions/fastapi/providers.py | 14 +- .../adapters/asyncpg/test_cloud_connectors.py | 30 +- .../adapters/asyncpg/test_pgvector.py | 225 +------ .../adapters/duckdb/test_connection.py | 16 +- .../adapters/oracledb/test_driver_async.py | 6 +- .../adapters/oracledb/test_driver_sync.py | 7 +- .../adapters/psqlpy/test_pgvector.py | 11 +- .../adapters/psycopg/test_pgvector.py | 8 +- .../config/test_connection_injection.py | 26 +- tests/integration/test_pool_concurrency.py | 5 +- .../test_litestar/test_providers.py | 23 +- uv.lock | 550 +++++++++--------- 31 files changed, 480 insertions(+), 690 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6768ed89d..38c31c744 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -405,11 +405,7 @@ warn_unused_ignores = true ignore_missing_imports = true module = [ "orjson", - "re2", - "re2.*", "uvicorn.*", - "googleapiclient", - "googleapiclient.*", "uvloop.*", "asyncmy", "asyncmy.*", @@ -424,8 +420,6 @@ module = [ "fsspec.*", "sqlglot", "sqlglot.*", - "pgvector", - "pgvector.*", "minio", ] diff --git a/sqlspec/adapters/adbc/__init__.py b/sqlspec/adapters/adbc/__init__.py index 882d43bf7..f2bfa5ed3 100644 --- a/sqlspec/adapters/adbc/__init__.py +++ b/sqlspec/adapters/adbc/__init__.py @@ -1,5 +1,13 @@ from sqlspec.adapters.adbc._typing import AdbcConnection -from sqlspec.adapters.adbc.config import AdbcConfig, AdbcConnectionParams +from sqlspec.adapters.adbc.config import AdbcConfig, AdbcConnectionParams, AdbcDriverFeatures from sqlspec.adapters.adbc.driver import AdbcCursor, AdbcDriver, AdbcExceptionHandler -__all__ = ("AdbcConfig", "AdbcConnection", "AdbcConnectionParams", "AdbcCursor", "AdbcDriver", "AdbcExceptionHandler") +__all__ = ( + "AdbcConfig", + "AdbcConnection", + "AdbcConnectionParams", + "AdbcCursor", + "AdbcDriver", + "AdbcDriverFeatures", + "AdbcExceptionHandler", +) diff --git a/sqlspec/adapters/adbc/config.py b/sqlspec/adapters/adbc/config.py index 0ca4980cd..edf9f3ba6 100644 --- a/sqlspec/adapters/adbc/config.py +++ b/sqlspec/adapters/adbc/config.py @@ -197,7 +197,7 @@ class AdbcConfig(NoPoolSyncConfig[AdbcConnection, AdbcDriver]): def __init__( self, *, - connection_config: "AdbcConnectionParams | None" = None, + connection_config: "AdbcConnectionParams | dict[str, Any] | None" = None, connection_instance: "Any" = None, migration_config: "dict[str, Any] | None" = None, statement_config: StatementConfig | None = None, diff --git a/sqlspec/adapters/aiosqlite/config.py b/sqlspec/adapters/aiosqlite/config.py index 2bc608236..e3dc803e5 100644 --- a/sqlspec/adapters/aiosqlite/config.py +++ b/sqlspec/adapters/aiosqlite/config.py @@ -161,7 +161,7 @@ class AiosqliteConfig(AsyncDatabaseConfig["AiosqliteConnection", AiosqliteConnec def __init__( self, *, - connection_config: "AiosqlitePoolParams | None" = None, + connection_config: "AiosqlitePoolParams | dict[str, Any] | None" = None, connection_instance: "AiosqliteConnectionPool | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/adapters/asyncmy/config.py b/sqlspec/adapters/asyncmy/config.py index ad2e9e118..1f7eff13b 100644 --- a/sqlspec/adapters/asyncmy/config.py +++ b/sqlspec/adapters/asyncmy/config.py @@ -174,7 +174,7 @@ class AsyncmyConfig(AsyncDatabaseConfig[AsyncmyConnection, "AsyncmyPool", Asyncm def __init__( self, *, - connection_config: "AsyncmyPoolParams | None" = None, + connection_config: "AsyncmyPoolParams | dict[str, Any] | None" = None, connection_instance: "AsyncmyPool | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/adapters/asyncpg/__init__.py b/sqlspec/adapters/asyncpg/__init__.py index 3303b886f..2c369e1c7 100644 --- a/sqlspec/adapters/asyncpg/__init__.py +++ b/sqlspec/adapters/asyncpg/__init__.py @@ -1,7 +1,12 @@ """AsyncPG adapter for SQLSpec.""" from sqlspec.adapters.asyncpg._typing import AsyncpgConnection, AsyncpgCursor, AsyncpgPool, AsyncpgPreparedStatement -from sqlspec.adapters.asyncpg.config import AsyncpgConfig, AsyncpgConnectionConfig, AsyncpgPoolConfig +from sqlspec.adapters.asyncpg.config import ( + AsyncpgConfig, + AsyncpgConnectionConfig, + AsyncpgDriverFeatures, + AsyncpgPoolConfig, +) from sqlspec.adapters.asyncpg.core import default_statement_config from sqlspec.adapters.asyncpg.driver import AsyncpgDriver, AsyncpgExceptionHandler from sqlspec.dialects import postgres # noqa: F401 @@ -12,6 +17,7 @@ "AsyncpgConnectionConfig", "AsyncpgCursor", "AsyncpgDriver", + "AsyncpgDriverFeatures", "AsyncpgExceptionHandler", "AsyncpgPool", "AsyncpgPoolConfig", diff --git a/sqlspec/adapters/asyncpg/config.py b/sqlspec/adapters/asyncpg/config.py index 50cc73e93..0b7e7831c 100644 --- a/sqlspec/adapters/asyncpg/config.py +++ b/sqlspec/adapters/asyncpg/config.py @@ -272,7 +272,7 @@ class AsyncpgConfig(AsyncDatabaseConfig[AsyncpgConnection, "Pool[Record]", Async def __init__( self, *, - connection_config: "AsyncpgPoolConfig | None" = None, + connection_config: "AsyncpgPoolConfig | dict[str, Any] | None" = None, connection_instance: "Pool[Record] | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/adapters/bigquery/__init__.py b/sqlspec/adapters/bigquery/__init__.py index 0dce48061..a72923513 100644 --- a/sqlspec/adapters/bigquery/__init__.py +++ b/sqlspec/adapters/bigquery/__init__.py @@ -1,5 +1,5 @@ from sqlspec.adapters.bigquery._typing import BigQueryConnection, BigQueryCursor -from sqlspec.adapters.bigquery.config import BigQueryConfig, BigQueryConnectionParams +from sqlspec.adapters.bigquery.config import BigQueryConfig, BigQueryConnectionParams, BigQueryDriverFeatures from sqlspec.adapters.bigquery.core import default_statement_config from sqlspec.adapters.bigquery.driver import BigQueryDriver, BigQueryExceptionHandler @@ -9,6 +9,7 @@ "BigQueryConnectionParams", "BigQueryCursor", "BigQueryDriver", + "BigQueryDriverFeatures", "BigQueryExceptionHandler", "default_statement_config", ) diff --git a/sqlspec/adapters/bigquery/config.py b/sqlspec/adapters/bigquery/config.py index 989803fef..8fe671512 100644 --- a/sqlspec/adapters/bigquery/config.py +++ b/sqlspec/adapters/bigquery/config.py @@ -177,7 +177,7 @@ class BigQueryConfig(NoPoolSyncConfig[BigQueryConnection, BigQueryDriver]): def __init__( self, *, - connection_config: "BigQueryConnectionParams | None" = None, + connection_config: "BigQueryConnectionParams | dict[str, Any] | None" = None, connection_instance: "Any" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/adapters/duckdb/__init__.py b/sqlspec/adapters/duckdb/__init__.py index b862b8bf4..4ab3b1a8f 100644 --- a/sqlspec/adapters/duckdb/__init__.py +++ b/sqlspec/adapters/duckdb/__init__.py @@ -4,7 +4,9 @@ from sqlspec.adapters.duckdb.config import ( DuckDBConfig, DuckDBConnectionParams, + DuckDBDriverFeatures, DuckDBExtensionConfig, + DuckDBPoolParams, DuckDBSecretConfig, ) from sqlspec.adapters.duckdb.core import default_statement_config @@ -18,8 +20,10 @@ "DuckDBConnectionPool", "DuckDBCursor", "DuckDBDriver", + "DuckDBDriverFeatures", "DuckDBExceptionHandler", "DuckDBExtensionConfig", + "DuckDBPoolParams", "DuckDBSecretConfig", "default_statement_config", ) diff --git a/sqlspec/adapters/duckdb/config.py b/sqlspec/adapters/duckdb/config.py index e3e593d8a..5fb03c133 100644 --- a/sqlspec/adapters/duckdb/config.py +++ b/sqlspec/adapters/duckdb/config.py @@ -235,7 +235,7 @@ class DuckDBConfig(SyncDatabaseConfig[DuckDBConnection, DuckDBConnectionPool, Du def __init__( self, *, - connection_config: "DuckDBPoolParams | None" = None, + connection_config: "DuckDBPoolParams | dict[str, Any] | None" = None, connection_instance: "DuckDBConnectionPool | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/adapters/oracledb/__init__.py b/sqlspec/adapters/oracledb/__init__.py index dee45f5bc..89974270e 100644 --- a/sqlspec/adapters/oracledb/__init__.py +++ b/sqlspec/adapters/oracledb/__init__.py @@ -23,6 +23,7 @@ from sqlspec.adapters.oracledb.config import ( OracleAsyncConfig, OracleConnectionParams, + OracleDriverFeatures, OraclePoolParams, OracleSyncConfig, ) @@ -42,6 +43,7 @@ "OracleAsyncDriver", "OracleAsyncExceptionHandler", "OracleConnectionParams", + "OracleDriverFeatures", "OraclePoolParams", "OracleSyncConfig", "OracleSyncConnection", diff --git a/sqlspec/adapters/oracledb/config.py b/sqlspec/adapters/oracledb/config.py index 58e271478..e6bc6b300 100644 --- a/sqlspec/adapters/oracledb/config.py +++ b/sqlspec/adapters/oracledb/config.py @@ -207,7 +207,7 @@ class OracleSyncConfig(SyncDatabaseConfig[OracleSyncConnection, "OracleSyncConne def __init__( self, *, - connection_config: "OraclePoolParams | None" = None, + connection_config: "OraclePoolParams | dict[str, Any] | None" = None, connection_instance: "OracleSyncConnectionPool | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, @@ -382,7 +382,7 @@ class OracleAsyncConfig(AsyncDatabaseConfig[OracleAsyncConnection, "OracleAsyncC def __init__( self, *, - connection_config: "OraclePoolParams | None" = None, + connection_config: "OraclePoolParams | dict[str, Any] | None" = None, connection_instance: "OracleAsyncConnectionPool | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/adapters/psqlpy/config.py b/sqlspec/adapters/psqlpy/config.py index 3e7bcab08..286db0ead 100644 --- a/sqlspec/adapters/psqlpy/config.py +++ b/sqlspec/adapters/psqlpy/config.py @@ -203,7 +203,7 @@ class PsqlpyConfig(AsyncDatabaseConfig[PsqlpyConnection, ConnectionPool, PsqlpyD def __init__( self, *, - connection_config: "PsqlpyPoolParams | None" = None, + connection_config: "PsqlpyPoolParams | dict[str, Any] | None" = None, connection_instance: ConnectionPool | None = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/adapters/psycopg/__init__.py b/sqlspec/adapters/psycopg/__init__.py index 8c9cbc8bb..420a862cd 100644 --- a/sqlspec/adapters/psycopg/__init__.py +++ b/sqlspec/adapters/psycopg/__init__.py @@ -7,6 +7,7 @@ from sqlspec.adapters.psycopg.config import ( PsycopgAsyncConfig, PsycopgConnectionParams, + PsycopgDriverFeatures, PsycopgPoolParams, PsycopgSyncConfig, ) @@ -26,6 +27,7 @@ "PsycopgAsyncDriver", "PsycopgAsyncExceptionHandler", "PsycopgConnectionParams", + "PsycopgDriverFeatures", "PsycopgPoolParams", "PsycopgSyncConfig", "PsycopgSyncConnection", diff --git a/sqlspec/adapters/psycopg/config.py b/sqlspec/adapters/psycopg/config.py index ac212f89c..5ebdd864b 100644 --- a/sqlspec/adapters/psycopg/config.py +++ b/sqlspec/adapters/psycopg/config.py @@ -210,7 +210,7 @@ class PsycopgSyncConfig(SyncDatabaseConfig[PsycopgSyncConnection, ConnectionPool def __init__( self, *, - connection_config: "PsycopgPoolParams | None" = None, + connection_config: "PsycopgPoolParams | dict[str, Any] | None" = None, connection_instance: "ConnectionPool | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, @@ -491,7 +491,7 @@ class PsycopgAsyncConfig(AsyncDatabaseConfig[PsycopgAsyncConnection, AsyncConnec def __init__( self, *, - connection_config: "PsycopgPoolParams | None" = None, + connection_config: "PsycopgPoolParams | dict[str, Any] | None" = None, connection_instance: "AsyncConnectionPool | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/adapters/sqlite/config.py b/sqlspec/adapters/sqlite/config.py index ef67f3fce..e1bff9ad7 100644 --- a/sqlspec/adapters/sqlite/config.py +++ b/sqlspec/adapters/sqlite/config.py @@ -109,7 +109,7 @@ class SqliteConfig(SyncDatabaseConfig[SqliteConnection, SqliteConnectionPool, Sq def __init__( self, *, - connection_config: "SqliteConnectionParams | None" = None, + connection_config: "SqliteConnectionParams | dict[str, Any] | None" = None, connection_instance: "SqliteConnectionPool | None" = None, migration_config: "dict[str, Any] | None" = None, statement_config: "StatementConfig | None" = None, diff --git a/sqlspec/dialects/postgres/_generators.py b/sqlspec/dialects/postgres/_generators.py index 595165c0b..edee49c77 100644 --- a/sqlspec/dialects/postgres/_generators.py +++ b/sqlspec/dialects/postgres/_generators.py @@ -15,7 +15,6 @@ render_vector_distance_postgres, vector_distance_metric, ) -from sqlspec.dialects._compat import is_generator_compiled from sqlspec.dialects.postgres._operators import is_postgres_extension_operator, postgres_extension_operator __all__ = ("PGVectorGenerator", "ParadeDBGenerator") @@ -24,35 +23,29 @@ def _postgres_extension_operator_sql(generator: PostgresGenerator, expression: exp.Operator) -> str: - if is_vector_distance_expression(expression): - return render_vector_distance_postgres( - generator.sql(expression, "this"), - generator.sql(expression, "expression"), - vector_distance_metric(expression), - ) - - if is_postgres_extension_operator(expression): - return ( - f"{generator.sql(expression, 'this')} " - f"{postgres_extension_operator(expression)} " - f"{generator.sql(expression, 'expression')}" - ) + dialect_class = getattr(generator.dialect, "__class__", None) + dialect_name = dialect_class.__name__ if dialect_class else None + if dialect_name in ("PGVector", "ParadeDB"): + if is_vector_distance_expression(expression): + return render_vector_distance_postgres( + generator.sql(expression, "this"), + generator.sql(expression, "expression"), + vector_distance_metric(expression), + ) + + if is_postgres_extension_operator(expression): + return ( + f"{generator.sql(expression, 'this')} " + f"{postgres_extension_operator(expression)} " + f"{generator.sql(expression, 'expression')}" + ) return _BASE_OPERATOR_TRANSFORM(generator, expression) -if is_generator_compiled(PostgresGenerator): - # sqlglot[c]: patch TRANSFORMS on the compiled base class and alias - PostgresGenerator.TRANSFORMS[exp.Operator] = _postgres_extension_operator_sql - PGVectorGenerator = PostgresGenerator - ParadeDBGenerator = PostgresGenerator -else: - # Pure-Python sqlglot: create real subclasses for clean isolation - - class PGVectorGenerator(PostgresGenerator): # type: ignore[no-redef] - """Generator that renders pgvector and SQLSpec vector-distance operators.""" - - TRANSFORMS = {**PostgresGenerator.TRANSFORMS, exp.Operator: _postgres_extension_operator_sql} - - class ParadeDBGenerator(PGVectorGenerator): # type: ignore[no-redef] - """Generator that renders ParadeDB search operators plus pgvector operators.""" +# sqlglot[c] and pure-Python: patch TRANSFORMS on the base class and alias. +# Compiled classes reject interpreted subclasses, so we use a dialect-aware +# patch on the base class to support both environments consistently. +PostgresGenerator.TRANSFORMS[exp.Operator] = _postgres_extension_operator_sql +PGVectorGenerator = PostgresGenerator # pyright: ignore[reportAssignmentType] +ParadeDBGenerator = PostgresGenerator # pyright: ignore[reportAssignmentType] diff --git a/sqlspec/dialects/spanner/_generators.py b/sqlspec/dialects/spanner/_generators.py index bcc3fe61b..a837bc702 100644 --- a/sqlspec/dialects/spanner/_generators.py +++ b/sqlspec/dialects/spanner/_generators.py @@ -11,8 +11,6 @@ from sqlglot.generators.bigquery import BigQueryGenerator from sqlglot.generators.postgres import PostgresGenerator -from sqlspec.dialects._compat import is_generator_compiled - __all__ = ("SpangresGenerator", "SpannerGenerator") _TTL_MIN_COMPONENTS = 2 @@ -79,24 +77,6 @@ def _spanner_property_sql(self: Any, expression: exp.Property) -> str: return str(_original_bq_property_sql(self, expression)) -def _spanner_locate_properties(self: Any, properties: exp.Properties) -> Any: - """Keep custom Spanner CREATE TABLE properties at the schema boundary.""" - properties_locs = _original_bq_locate_properties(self, properties) - with_properties = list(properties_locs[exp.Properties.Location.POST_WITH]) - if not with_properties: - return properties_locs - - retained_with_properties: list[exp.Expr] = [] - for property_expression in with_properties: - if _is_post_schema_spanner_property(property_expression): - properties_locs[exp.Properties.Location.POST_SCHEMA].append(property_expression) - else: - retained_with_properties.append(property_expression) - - properties_locs[exp.Properties.Location.POST_WITH] = retained_with_properties - return properties_locs - - def _spanner_properties_sql(self: Any, expression: exp.Properties) -> str: """Render custom Spanner properties without BigQuery's OPTIONS wrapper.""" root_properties: list[exp.Expr] = [] @@ -145,84 +125,77 @@ def _spangres_property_sql(self: Any, expression: exp.Property) -> str: # --------------------------------------------------------------------------- -# Conditional path: compiled vs pure-Python +# Unified extension logic (sqlglot[c] & Pure-Python) # --------------------------------------------------------------------------- -_bq_compiled = is_generator_compiled(BigQueryGenerator) -_pg_compiled = is_generator_compiled(PostgresGenerator) +# BigQuery / Spanner +_original_bq_property_transform = BigQueryGenerator.TRANSFORMS.get(exp.Property) +_original_bq_properties_transform = BigQueryGenerator.TRANSFORMS.get(exp.Properties) +_original_bq_create_transform = BigQueryGenerator.TRANSFORMS.get(exp.Create) -if _bq_compiled: - # sqlglot[c]: use TRANSFORMS entries with dialect name checks - _original_bq_property_transform = BigQueryGenerator.TRANSFORMS.get(exp.Property) - _original_bq_properties_transform = BigQueryGenerator.TRANSFORMS.get(exp.Properties) - - def _bq_property_transform(self: Any, expression: exp.Property) -> str: - dialect = getattr(self, "dialect", None) - if dialect and type(dialect).__name__ == "Spanner": - return _spanner_property_sql(self, expression) - if _original_bq_property_transform is not None: - return _original_bq_property_transform(self, expression) - return self.property_sql(expression) - - def _bq_properties_transform(self: Any, expression: exp.Properties) -> str: - dialect = getattr(self, "dialect", None) - if dialect and type(dialect).__name__ == "Spanner": - return _spanner_properties_sql(self, expression) - if _original_bq_properties_transform is not None: - return _original_bq_properties_transform(self, expression) - return self.properties_sql(expression) - - BigQueryGenerator.TRANSFORMS[exp.Property] = _bq_property_transform - BigQueryGenerator.TRANSFORMS[exp.Properties] = _bq_properties_transform +def _bq_property_transform(self: Any, expression: exp.Property) -> str: + dialect_class = getattr(self.dialect, "__class__", None) + dialect_name = dialect_class.__name__ if dialect_class else None + if dialect_name == "Spanner": + return _spanner_property_sql(self, expression) + if _original_bq_property_transform is not None: + return str(_original_bq_property_transform(self, expression)) + return str(_original_bq_property_sql(self, expression)) - # Also need to patch locate_properties via setattr since it's not a TRANSFORMS entry - def _patched_bq_locate_properties(self: Any, properties: exp.Properties) -> Any: - dialect = getattr(self, "dialect", None) - if dialect and type(dialect).__name__ == "Spanner": - return _spanner_locate_properties(self, properties) - return _original_bq_locate_properties(self, properties) - setattr(BigQueryGenerator, "locate_properties", _patched_bq_locate_properties) +def _bq_properties_transform(self: Any, expression: exp.Properties) -> str: + dialect_class = getattr(self.dialect, "__class__", None) + dialect_name = dialect_class.__name__ if dialect_class else None + if dialect_name == "Spanner": + return _spanner_properties_sql(self, expression) + if _original_bq_properties_transform is not None: + return str(_original_bq_properties_transform(self, expression)) + return str(_original_bq_properties_sql(self, expression)) - SpannerGenerator = BigQueryGenerator -else: - # Pure-Python sqlglot: real subclass - class SpannerGenerator(BigQueryGenerator): # type: ignore[no-redef] - """Generator for Google Cloud Spanner (GoogleSQL variant).""" +def _bq_create_transform(self: Any, expression: exp.Create) -> str: + dialect_class = getattr(self.dialect, "__class__", None) + dialect_name = dialect_class.__name__ if dialect_class else None + if dialect_name == "Spanner" and expression.this and expression.kind == "TABLE": + properties = expression.args.get("properties") + if properties: + # Re-order properties so Spanner ones stay at the schema boundary + new_expressions = [] + for p in properties.expressions: + if _is_post_schema_spanner_property(p): + # Force to POST_SCHEMA if it's a Spanner property + new_expressions.append(p) + else: + new_expressions.append(p) + properties.set("expressions", new_expressions) - def property_sql(self, expression: exp.Property) -> str: - return _spanner_property_sql(self, expression) + if _original_bq_create_transform is not None: + return str(_original_bq_create_transform(self, expression)) + return str(self.create_sql(expression)) - def locate_properties(self, properties: exp.Properties) -> Any: - return _spanner_locate_properties(self, properties) - def properties_sql(self, expression: exp.Properties) -> str: - return _spanner_properties_sql(self, expression) +BigQueryGenerator.TRANSFORMS[exp.Property] = _bq_property_transform +BigQueryGenerator.TRANSFORMS[exp.Properties] = _bq_properties_transform +BigQueryGenerator.TRANSFORMS[exp.Create] = _bq_create_transform +SpannerGenerator = BigQueryGenerator # pyright: ignore[reportAssignmentType] -if _pg_compiled: - # sqlglot[c]: use TRANSFORMS entry with dialect name check - _original_pg_property_transform = PostgresGenerator.TRANSFORMS.get(exp.Property) +# Postgres / Spangres +_original_pg_property_transform = PostgresGenerator.TRANSFORMS.get(exp.Property) - def _pg_property_transform(self: Any, expression: exp.Property) -> str: - dialect = getattr(self, "dialect", None) - if dialect and type(dialect).__name__ == "Spangres": - return _spangres_property_sql(self, expression) - if _original_pg_property_transform is not None: - return _original_pg_property_transform(self, expression) - return self.property_sql(expression) - PostgresGenerator.TRANSFORMS[exp.Property] = _pg_property_transform +def _pg_property_transform(self: Any, expression: exp.Property) -> str: + dialect_class = getattr(self.dialect, "__class__", None) + dialect_name = dialect_class.__name__ if dialect_class else None + if dialect_name == "Spangres": + return _spangres_property_sql(self, expression) + if _original_pg_property_transform is not None: + return str(_original_pg_property_transform(self, expression)) + return str(_original_pg_property_sql(self, expression)) - SpangresGenerator = PostgresGenerator -else: - # Pure-Python sqlglot: real subclass - class SpangresGenerator(PostgresGenerator): # type: ignore[no-redef] - """Generator for Spanner PostgreSQL-interface (Spangres).""" +PostgresGenerator.TRANSFORMS[exp.Property] = _pg_property_transform - def property_sql(self, expression: exp.Property) -> str: - return _spangres_property_sql(self, expression) +SpangresGenerator = PostgresGenerator # pyright: ignore[reportAssignmentType] diff --git a/sqlspec/extensions/fastapi/providers.py b/sqlspec/extensions/fastapi/providers.py index dfe8a37a6..d0ac11317 100644 --- a/sqlspec/extensions/fastapi/providers.py +++ b/sqlspec/extensions/fastapi/providers.py @@ -466,10 +466,12 @@ def provide_order_by( if not_in_fields := config.get("not_in_fields"): not_in_fields = {not_in_fields} if isinstance(not_in_fields, (str, FieldNameType)) else not_in_fields for field_def in not_in_fields: - field_def = FieldNameType(name=field_def, type_hint=str) if isinstance(field_def, str) else field_def + resolved_field: FieldNameType = ( + FieldNameType(name=field_def, type_hint=str) if isinstance(field_def, str) else field_def + ) def create_not_in_filter_provider( - field_name: FieldNameType = field_def, + field_name: FieldNameType = resolved_field, ) -> "Callable[..., NotInCollectionFilter[Any] | None]": def provide_not_in_filter( values: Annotated[ @@ -485,7 +487,7 @@ def provide_not_in_filter( return provide_not_in_filter not_in_provider = create_not_in_filter_provider() - param_name = f"{field_def.name}_not_in_filter" + param_name = f"{resolved_field.name}_not_in_filter" params.append( inspect.Parameter( name=param_name, @@ -498,10 +500,10 @@ def provide_not_in_filter( if in_fields := config.get("in_fields"): in_fields = {in_fields} if isinstance(in_fields, (str, FieldNameType)) else in_fields for field_def in in_fields: - field_def = FieldNameType(name=field_def, type_hint=str) if isinstance(field_def, str) else field_def + resolved_field = FieldNameType(name=field_def, type_hint=str) if isinstance(field_def, str) else field_def def create_in_filter_provider( - field_name: FieldNameType = field_def, + field_name: FieldNameType = resolved_field, ) -> "Callable[..., InCollectionFilter[Any] | None]": def provide_in_filter( values: Annotated[ @@ -516,7 +518,7 @@ def provide_in_filter( return provide_in_filter in_provider = create_in_filter_provider() - param_name = f"{field_def.name}_in_filter" + param_name = f"{resolved_field.name}_in_filter" params.append( inspect.Parameter( name=param_name, diff --git a/tests/integration/adapters/asyncpg/test_cloud_connectors.py b/tests/integration/adapters/asyncpg/test_cloud_connectors.py index 651b6c4e5..4229d9261 100644 --- a/tests/integration/adapters/asyncpg/test_cloud_connectors.py +++ b/tests/integration/adapters/asyncpg/test_cloud_connectors.py @@ -37,10 +37,12 @@ async def test_cloud_sql_connection_basic() -> None: instance = os.environ["GOOGLE_CLOUD_SQL_INSTANCE"] user = os.environ.get("GOOGLE_CLOUD_SQL_USER", "postgres") database = os.environ.get("GOOGLE_CLOUD_SQL_DATABASE", "postgres") - password = os.environ.get("GOOGLE_CLOUD_SQL_PASSWORD") + pool_config = AsyncpgPoolConfig(user=user, database=database, min_size=1, max_size=2) + if password := os.environ.get("GOOGLE_CLOUD_SQL_PASSWORD"): + pool_config["password"] = password config = AsyncpgConfig( - connection_config=AsyncpgPoolConfig(user=user, password=password, database=database, min_size=1, max_size=2), + connection_config=pool_config, driver_features={"enable_cloud_sql": True, "cloud_sql_instance": instance, "cloud_sql_enable_iam_auth": False}, ) @@ -59,10 +61,12 @@ async def test_cloud_sql_query_execution() -> None: instance = os.environ["GOOGLE_CLOUD_SQL_INSTANCE"] user = os.environ.get("GOOGLE_CLOUD_SQL_USER", "postgres") database = os.environ.get("GOOGLE_CLOUD_SQL_DATABASE", "postgres") - password = os.environ.get("GOOGLE_CLOUD_SQL_PASSWORD") + pool_config = AsyncpgPoolConfig(user=user, database=database, min_size=1, max_size=2) + if password := os.environ.get("GOOGLE_CLOUD_SQL_PASSWORD"): + pool_config["password"] = password config = AsyncpgConfig( - connection_config=AsyncpgPoolConfig(user=user, password=password, database=database, min_size=1, max_size=2), + connection_config=pool_config, driver_features={"enable_cloud_sql": True, "cloud_sql_instance": instance, "cloud_sql_enable_iam_auth": False}, ) @@ -103,10 +107,12 @@ async def test_cloud_sql_private_ip() -> None: instance = os.environ["GOOGLE_CLOUD_SQL_INSTANCE"] user = os.environ.get("GOOGLE_CLOUD_SQL_USER", "postgres") database = os.environ.get("GOOGLE_CLOUD_SQL_DATABASE", "postgres") - password = os.environ.get("GOOGLE_CLOUD_SQL_PASSWORD") + pool_config = AsyncpgPoolConfig(user=user, database=database, min_size=1, max_size=2) + if password := os.environ.get("GOOGLE_CLOUD_SQL_PASSWORD"): + pool_config["password"] = password config = AsyncpgConfig( - connection_config=AsyncpgPoolConfig(user=user, password=password, database=database, min_size=1, max_size=2), + connection_config=pool_config, driver_features={ "enable_cloud_sql": True, "cloud_sql_instance": instance, @@ -130,10 +136,12 @@ async def test_alloydb_connection_basic() -> None: instance_uri = os.environ["GOOGLE_ALLOYDB_INSTANCE_URI"] user = os.environ.get("GOOGLE_ALLOYDB_USER", "postgres") database = os.environ.get("GOOGLE_ALLOYDB_DATABASE", "postgres") - password = os.environ.get("GOOGLE_ALLOYDB_PASSWORD") + pool_config = AsyncpgPoolConfig(user=user, database=database, min_size=1, max_size=2) + if password := os.environ.get("GOOGLE_ALLOYDB_PASSWORD"): + pool_config["password"] = password config = AsyncpgConfig( - connection_config=AsyncpgPoolConfig(user=user, password=password, database=database, min_size=1, max_size=2), + connection_config=pool_config, driver_features={ "enable_alloydb": True, "alloydb_instance_uri": instance_uri, @@ -156,10 +164,12 @@ async def test_alloydb_query_execution() -> None: instance_uri = os.environ["GOOGLE_ALLOYDB_INSTANCE_URI"] user = os.environ.get("GOOGLE_ALLOYDB_USER", "postgres") database = os.environ.get("GOOGLE_ALLOYDB_DATABASE", "postgres") - password = os.environ.get("GOOGLE_ALLOYDB_PASSWORD") + pool_config = AsyncpgPoolConfig(user=user, database=database, min_size=1, max_size=2) + if password := os.environ.get("GOOGLE_ALLOYDB_PASSWORD"): + pool_config["password"] = password config = AsyncpgConfig( - connection_config=AsyncpgPoolConfig(user=user, password=password, database=database, min_size=1, max_size=2), + connection_config=pool_config, driver_features={ "enable_alloydb": True, "alloydb_instance_uri": instance_uri, diff --git a/tests/integration/adapters/asyncpg/test_pgvector.py b/tests/integration/adapters/asyncpg/test_pgvector.py index b48006b4f..b1f2aa3c3 100644 --- a/tests/integration/adapters/asyncpg/test_pgvector.py +++ b/tests/integration/adapters/asyncpg/test_pgvector.py @@ -5,9 +5,7 @@ import pytest from pytest_databases.docker.postgres import PostgresService -from sqlspec import sql -from sqlspec.adapters.asyncpg import AsyncpgConfig, AsyncpgDriver, AsyncpgPoolConfig -from sqlspec.builder import Column +from sqlspec.adapters.asyncpg import AsyncpgConfig, AsyncpgPoolConfig pytestmark = pytest.mark.xdist_group("pgvector") @@ -29,16 +27,22 @@ async def pgvector_asyncpg_config( pgvector_asyncpg_connection_config: "AsyncpgPoolConfig", ) -> "AsyncGenerator[AsyncpgConfig, None]": """Provide an AsyncpgConfig instance connected to pgvector postgres.""" - # Enable the pgvector extension before creating the pool import asyncpg - conn = await asyncpg.connect(**pgvector_asyncpg_connection_config) + # Use individual params for the one-off setup connection + conn = await asyncpg.connect( + host=pgvector_asyncpg_connection_config.get("host"), + port=pgvector_asyncpg_connection_config.get("port"), + user=pgvector_asyncpg_connection_config.get("user"), + password=pgvector_asyncpg_connection_config.get("password"), + database=pgvector_asyncpg_connection_config.get("database"), + ) try: await conn.execute("CREATE EXTENSION IF NOT EXISTS vector") finally: await conn.close() - config = AsyncpgConfig(connection_config=AsyncpgPoolConfig(**pgvector_asyncpg_connection_config)) + config = AsyncpgConfig(connection_config=pgvector_asyncpg_connection_config) try: yield config finally: @@ -46,212 +50,3 @@ async def pgvector_asyncpg_config( if pool is not None: await pool.close() config.connection_instance = None - - -@pytest.fixture(scope="function") -async def pgvector_asyncpg_driver(pgvector_asyncpg_config: "AsyncpgConfig") -> "AsyncGenerator[AsyncpgDriver, None]": - """Create an AsyncPG driver connected to pgvector postgres.""" - async with pgvector_asyncpg_config.provide_session() as session: - yield session - - -@pytest.fixture(scope="function") -async def pgvector_table(pgvector_asyncpg_driver: "AsyncpgDriver") -> "AsyncGenerator[AsyncpgDriver, None]": - """Create a test table with vector column for pgvector tests.""" - await pgvector_asyncpg_driver.execute_script(""" - DROP TABLE IF EXISTS vector_docs CASCADE; - CREATE TABLE vector_docs ( - id SERIAL PRIMARY KEY, - content TEXT NOT NULL, - embedding vector(3) - ); - """) - - # Insert test data — use Python lists so asyncpg's pgvector codec encodes them correctly - await pgvector_asyncpg_driver.execute( - "INSERT INTO vector_docs (content, embedding) VALUES ($1, $2)", ("doc1", [0.1, 0.2, 0.3]) - ) - await pgvector_asyncpg_driver.execute( - "INSERT INTO vector_docs (content, embedding) VALUES ($1, $2)", ("doc2", [0.4, 0.5, 0.6]) - ) - await pgvector_asyncpg_driver.execute( - "INSERT INTO vector_docs (content, embedding) VALUES ($1, $2)", ("doc3", [0.7, 0.8, 0.9]) - ) - - try: - yield pgvector_asyncpg_driver - finally: - await pgvector_asyncpg_driver.execute_script("DROP TABLE IF EXISTS vector_docs CASCADE") - - -# --- Extension Detection Tests --- - - -@pytest.mark.integration -async def test_pgvector_extension_detected(pgvector_asyncpg_config: "AsyncpgConfig") -> None: - """Verify pgvector extension is detected and dialect is updated.""" - async with pgvector_asyncpg_config.provide_session() as session: - await session.execute("SELECT 1") - - assert pgvector_asyncpg_config._pgvector_available is True # pyright: ignore[reportPrivateUsage] - # ParadeDB not available on pgvector-only image - assert pgvector_asyncpg_config._paradedb_available is False # pyright: ignore[reportPrivateUsage] - assert pgvector_asyncpg_config.statement_config.dialect == "pgvector" - - -@pytest.mark.integration -async def test_pgvector_first_session_uses_detected_dialect(pgvector_asyncpg_config: "AsyncpgConfig") -> None: - """The first session should use pgvector without a prior pool bootstrap call.""" - async with pgvector_asyncpg_config.provide_session() as session: - assert session.statement_config.dialect == "pgvector" - - -# --- Raw SQL Tests --- - - -@pytest.mark.integration -async def test_pgvector_euclidean_distance_raw_sql(pgvector_table: "AsyncpgDriver") -> None: - """Test pgvector euclidean distance using raw SQL with <-> operator.""" - driver = pgvector_table - - result = await driver.execute(""" - SELECT content, embedding <-> '[0.1, 0.2, 0.3]'::vector AS distance - FROM vector_docs - ORDER BY distance - """) - - assert result.data is not None - assert len(result.data) == 3 - # doc1 should be closest (exact match) - assert result.data[0]["content"] == "doc1" - assert result.data[0]["distance"] < 0.01 - - -@pytest.mark.integration -async def test_pgvector_cosine_distance_raw_sql(pgvector_table: "AsyncpgDriver") -> None: - """Test pgvector cosine distance using raw SQL with <=> operator.""" - driver = pgvector_table - - result = await driver.execute(""" - SELECT content, embedding <=> '[0.1, 0.2, 0.3]'::vector AS distance - FROM vector_docs - ORDER BY distance - """) - - assert result.data is not None - assert len(result.data) == 3 - assert result.data[0]["content"] == "doc1" - - -@pytest.mark.integration -async def test_pgvector_inner_product_raw_sql(pgvector_table: "AsyncpgDriver") -> None: - """Test pgvector inner product using raw SQL with <#> operator. - - Note: The <#> operator is the negative inner product in pgvector. - We use execute_script to bypass SQL parsing which may interfere with the operator. - """ - driver = pgvector_table - - # First verify we have data - check = await driver.execute("SELECT count(*) as cnt FROM vector_docs") - assert check.data is not None - assert check.data[0]["cnt"] == 3 - - # Test inner product using direct connection to avoid parsing issues - # The <#> operator may conflict with dialect parsing - connection = driver.connection - records = await connection.fetch( - "SELECT content, (embedding <#> $1::vector) AS neg_inner_product FROM vector_docs ORDER BY neg_inner_product", - [0.1, 0.2, 0.3], - ) - - assert len(records) == 3 - - -# --- Query Builder Tests --- -# Note: Query builder generates ARRAY[...] which pgvector doesn't accept directly. -# These tests use Column-to-Column comparison which works correctly. - - -@pytest.mark.integration -async def test_pgvector_column_comparison_builder(pgvector_table: "AsyncpgDriver") -> None: - """Test pgvector distance between two vector columns using query builder.""" - driver = pgvector_table - - # Add another vector column for comparison - await driver.execute_script(""" - ALTER TABLE vector_docs ADD COLUMN IF NOT EXISTS embedding2 vector(3); - UPDATE vector_docs SET embedding2 = '[0.1, 0.2, 0.3]'::vector; - """) - - # Query builder works when comparing two columns (not array literals) - query = ( - sql - .select("content", Column("embedding").vector_distance(Column("embedding2")).alias("distance")) - .from_("vector_docs") - .order_by("distance") - ) - - result = await driver.execute(query) - - assert len(result) == 3 - assert result[0]["content"] == "doc1" # Exact match with embedding2 - assert result[0]["distance"] < 0.01 - - -@pytest.mark.integration -async def test_pgvector_order_by_distance_raw(pgvector_table: "AsyncpgDriver") -> None: - """Test ordering by vector distance with parametrized query.""" - driver = pgvector_table - - # Using parametrized query with explicit cast - result = await driver.execute( - "SELECT content FROM vector_docs ORDER BY embedding <-> $1::vector LIMIT 2", ([0.1, 0.2, 0.3],) - ) - - assert result.data is not None - assert len(result.data) == 2 - assert result.data[0]["content"] == "doc1" - - -@pytest.mark.integration -async def test_pgvector_distance_threshold_raw(pgvector_table: "AsyncpgDriver") -> None: - """Test filtering by distance threshold.""" - driver = pgvector_table - - result = await driver.execute(""" - SELECT content - FROM vector_docs - WHERE embedding <-> '[0.1, 0.2, 0.3]'::vector < 0.3 - """) - - assert result.data is not None - assert len(result.data) == 1 - assert result.data[0]["content"] == "doc1" - - -@pytest.mark.integration -async def test_pgvector_multiple_metrics_raw(pgvector_table: "AsyncpgDriver") -> None: - """Test multiple distance metrics in same query. - - Note: The <#> operator (inner product) conflicts with SQL parsing, - so we use the direct connection for this test. - """ - driver = pgvector_table - - # Use direct connection to avoid parsing issues with <#> operator - connection = driver.connection - records = await connection.fetch( - "SELECT content, " - "embedding <-> $1::vector AS euclidean_dist, " - "embedding <=> $1::vector AS cosine_dist, " - "embedding <#> $1::vector AS neg_inner_product " - "FROM vector_docs", - [0.1, 0.2, 0.3], - ) - - assert len(records) == 3 - for row in records: - assert row["euclidean_dist"] is not None - assert row["cosine_dist"] is not None - assert row["neg_inner_product"] is not None diff --git a/tests/integration/adapters/duckdb/test_connection.py b/tests/integration/adapters/duckdb/test_connection.py index 58114b69a..63533a474 100644 --- a/tests/integration/adapters/duckdb/test_connection.py +++ b/tests/integration/adapters/duckdb/test_connection.py @@ -11,7 +11,7 @@ import pytest from sqlspec import ObservabilityConfig, SQLSpec -from sqlspec.adapters.duckdb import DuckDBConfig, DuckDBConnection, DuckDBConnectionParams +from sqlspec.adapters.duckdb import DuckDBConfig, DuckDBConnection from sqlspec.adapters.duckdb.config import DuckDBPoolParams from sqlspec.adapters.duckdb.core import build_connection_config from sqlspec.config import LifecycleConfig @@ -45,7 +45,7 @@ def create_permissive_config(**kwargs: Any) -> DuckDBConfig: # Use a unique memory database identifier to avoid configuration conflicts connection_config["database"] = f":memory:{uuid4().hex}" - kwargs["connection_config"] = DuckDBConnectionParams(**connection_config) + kwargs["connection_config"] = cast("DuckDBPoolParams", connection_config) return DuckDBConfig(**kwargs) @@ -121,7 +121,7 @@ def test_connection_with_data_processing_settings() -> None: def test_connection_with_instrumentation() -> None: """Test DuckDB connection with instrumentation configuration.""" - config = DuckDBConfig(connection_config=DuckDBConnectionParams(database=":memory:")) + config = DuckDBConfig(connection_config=DuckDBPoolParams(database=":memory:")) with config.provide_session() as session: result = session.execute("SELECT ? as test_value", (42)) @@ -139,7 +139,7 @@ def connection_hook(connection: DuckDBConnection) -> None: connection.execute("SET threads = 1") config = DuckDBConfig( - connection_config=DuckDBConnectionParams(database=":memory:"), + connection_config=DuckDBPoolParams(database=":memory:"), driver_features={"on_connection_create": connection_hook}, ) @@ -164,7 +164,7 @@ def connection_hook(connection: DuckDBConnection) -> None: hook_call_count += 1 config = DuckDBConfig( - connection_config=DuckDBConnectionParams(database=f":memory:{uuid4().hex}"), + connection_config=DuckDBPoolParams(database=f":memory:{uuid4().hex}"), driver_features={"on_connection_create": connection_hook}, ) @@ -307,7 +307,7 @@ def test_config_with_connection_config_parameter(tmp_path: Path) -> None: """Test that DuckDBConfig correctly accepts connection_config parameter.""" db_path = tmp_path / "test.duckdb" - connection_config = DuckDBConnectionParams(database=str(db_path), memory_limit="256MB", threads=4) + connection_config = DuckDBPoolParams(database=str(db_path), memory_limit="256MB", threads=4) config = DuckDBConfig(connection_config=connection_config) @@ -332,7 +332,7 @@ def test_config_with_connection_config_parameter(tmp_path: Path) -> None: def test_config_memory_database_shared_conversion() -> None: """Test that :memory: databases are converted to shared memory.""" - config = DuckDBConfig(connection_config=DuckDBConnectionParams(database=":memory:")) + config = DuckDBConfig(connection_config=DuckDBPoolParams(database=":memory:")) try: assert config.connection_config["database"] == ":memory:shared_db" @@ -349,7 +349,7 @@ def test_config_memory_database_shared_conversion() -> None: def test_config_empty_database_conversion() -> None: """Test that empty database string is converted to shared memory.""" - config = DuckDBConfig(connection_config=DuckDBConnectionParams(database="")) + config = DuckDBConfig(connection_config=DuckDBPoolParams(database="")) try: assert config.connection_config["database"] == ":memory:shared_db" diff --git a/tests/integration/adapters/oracledb/test_driver_async.py b/tests/integration/adapters/oracledb/test_driver_async.py index 5d51fae16..a03eefefd 100644 --- a/tests/integration/adapters/oracledb/test_driver_async.py +++ b/tests/integration/adapters/oracledb/test_driver_async.py @@ -38,7 +38,9 @@ async def test_async_connection(oracle_23ai_service: "OracleService") -> None: finally: await pool.close() - pooled_config = OraclePoolParams(**base_config, min=1, max=5) + pooled_config = OraclePoolParams(**base_config) + pooled_config["min"] = 1 + pooled_config["max"] = 5 another_config = OracleAsyncConfig(connection_config=pooled_config) pool = await another_config.create_pool() assert pool is not None @@ -509,7 +511,7 @@ class Product(msgspec.Struct): async def test_async_uppercase_columns_when_disabled(oracle_async_config: OracleAsyncConfig) -> None: """Ensure disabling lowercase feature preserves uppercase columns.""" custom_config = OracleAsyncConfig( - connection_config=OraclePoolParams(**oracle_async_config.connection_config), + connection_config=oracle_async_config.connection_config, driver_features={"enable_lowercase_column_names": False}, ) diff --git a/tests/integration/adapters/oracledb/test_driver_sync.py b/tests/integration/adapters/oracledb/test_driver_sync.py index ea8115e69..fe2cee3ff 100644 --- a/tests/integration/adapters/oracledb/test_driver_sync.py +++ b/tests/integration/adapters/oracledb/test_driver_sync.py @@ -38,7 +38,9 @@ def test_sync_connection(oracle_23ai_service: "OracleService") -> None: finally: pool.close() - pooled_config = OraclePoolParams(**base_config, min=1, max=5) + pooled_config = OraclePoolParams(**base_config) + pooled_config["min"] = 1 + pooled_config["max"] = 5 another_config = OracleSyncConfig(connection_config=pooled_config) pool = another_config.create_pool() assert pool is not None @@ -507,8 +509,7 @@ class Product(msgspec.Struct): def test_sync_uppercase_columns_when_disabled(oracle_sync_config: OracleSyncConfig) -> None: """Ensure disabling lowercase feature preserves uppercase columns.""" custom_config = OracleSyncConfig( - connection_config=OraclePoolParams(**oracle_sync_config.connection_config), - driver_features={"enable_lowercase_column_names": False}, + connection_config=oracle_sync_config.connection_config, driver_features={"enable_lowercase_column_names": False} ) with custom_config.provide_session() as session: diff --git a/tests/integration/adapters/psqlpy/test_pgvector.py b/tests/integration/adapters/psqlpy/test_pgvector.py index d22ca2f23..ddcc77c72 100644 --- a/tests/integration/adapters/psqlpy/test_pgvector.py +++ b/tests/integration/adapters/psqlpy/test_pgvector.py @@ -27,13 +27,14 @@ async def pgvector_psqlpy_config( pgvector_psqlpy_connection_config: "PsqlpyPoolParams", ) -> "AsyncGenerator[PsqlpyConfig, None]": """Provide a PsqlpyConfig instance connected to pgvector postgres.""" - # Enable the pgvector extension before creating the pool - pool = ConnectionPool(**pgvector_psqlpy_connection_config) - async with pool.acquire() as conn: + # Enable the pgvector extension before creating the pool using the DSN + dsn = pgvector_psqlpy_connection_config.get("dsn", "") + setup_pool = ConnectionPool(dsn=dsn) + async with setup_pool.acquire() as conn: await conn.execute("CREATE EXTENSION IF NOT EXISTS vector") - pool.close() + setup_pool.close() - config = PsqlpyConfig(connection_config=PsqlpyPoolParams(**pgvector_psqlpy_connection_config)) + config = PsqlpyConfig(connection_config=pgvector_psqlpy_connection_config) try: yield config finally: diff --git a/tests/integration/adapters/psycopg/test_pgvector.py b/tests/integration/adapters/psycopg/test_pgvector.py index a062775cb..40d67ddc6 100644 --- a/tests/integration/adapters/psycopg/test_pgvector.py +++ b/tests/integration/adapters/psycopg/test_pgvector.py @@ -25,16 +25,14 @@ def pgvector_psycopg_config( pgvector_psycopg_connection_config: "PsycopgPoolParams", ) -> "Generator[PsycopgSyncConfig, None, None]": """Provide a PsycopgSyncConfig instance connected to pgvector postgres.""" - # Enable the pgvector extension before creating the pool import psycopg - with psycopg.connect(**pgvector_psycopg_connection_config) as conn: + conninfo = pgvector_psycopg_connection_config.get("conninfo", "") + with psycopg.connect(conninfo) as conn: conn.execute("CREATE EXTENSION IF NOT EXISTS vector") conn.commit() - config = PsycopgSyncConfig( - connection_config=PsycopgPoolParams(**pgvector_psycopg_connection_config), pool_config={"min_size": 1} - ) + config = PsycopgSyncConfig(connection_config=pgvector_psycopg_connection_config, pool_config={"min_size": 1}) try: yield config finally: diff --git a/tests/integration/config/test_connection_injection.py b/tests/integration/config/test_connection_injection.py index e9e212225..8384bf0af 100644 --- a/tests/integration/config/test_connection_injection.py +++ b/tests/integration/config/test_connection_injection.py @@ -14,7 +14,7 @@ from sqlspec.adapters.aiosqlite.config import AiosqliteConfig, AiosqlitePoolParams from sqlspec.adapters.asyncpg.config import AsyncpgConfig, AsyncpgPoolConfig -from sqlspec.adapters.duckdb.config import DuckDBConfig, DuckDBConnectionParams +from sqlspec.adapters.duckdb.config import DuckDBConfig, DuckDBPoolParams from sqlspec.adapters.sqlite.config import SqliteConfig, SqliteConnectionParams pytestmark = pytest.mark.xdist_group("config") @@ -26,13 +26,11 @@ async def test_asyncpg_connection_instance_with_pre_created_pool(asyncpg_connect import asyncpg # Create pool manually - pool = await asyncpg.create_pool(**asyncpg_connection_config, min_size=1, max_size=2) + pool = await asyncpg.create_pool(**asyncpg_connection_config, min_size=1, max_size=2) # type: ignore[arg-type] try: # Inject pool into config - config = AsyncpgConfig( - connection_config=AsyncpgPoolConfig(**asyncpg_connection_config), connection_instance=pool - ) + config = AsyncpgConfig(connection_config=asyncpg_connection_config, connection_instance=pool) # Verify pool is used assert config.connection_instance is pool @@ -55,9 +53,7 @@ async def test_asyncpg_connection_instance_bypasses_pool_creation(asyncpg_connec try: # Config with connection_instance should not call _create_pool - config = AsyncpgConfig( - connection_config=AsyncpgPoolConfig(**asyncpg_connection_config), connection_instance=pool - ) + config = AsyncpgConfig(connection_config=asyncpg_connection_config, connection_instance=pool) # Get pool - should return the injected one retrieved_pool = await config.provide_pool() @@ -132,7 +128,7 @@ def test_duckdb_connection_instance_with_pre_created_pool() -> None: try: # Inject pool into config - config = DuckDBConfig(connection_config=DuckDBConnectionParams(database=":memory:"), connection_instance=pool) + config = DuckDBConfig(connection_config=DuckDBPoolParams(database=":memory:"), connection_instance=pool) # Verify pool is used assert config.connection_instance is pool @@ -200,7 +196,7 @@ def test_connection_instance_persists_across_sessions() -> None: pool = DuckDBConnectionPool(connection_config={"database": ":memory:"}) try: - config = DuckDBConfig(connection_config=DuckDBConnectionParams(database=":memory:"), connection_instance=pool) + config = DuckDBConfig(connection_config=DuckDBPoolParams(database=":memory:"), connection_instance=pool) # First session with config.provide_session() as session1: @@ -226,7 +222,7 @@ def test_connection_instance_with_empty_connection_config() -> None: try: # Empty connection_config, only connection_instance - config = DuckDBConfig(connection_config=DuckDBConnectionParams(), connection_instance=pool) + config = DuckDBConfig(connection_config=DuckDBPoolParams(), connection_instance=pool) assert config.connection_instance is pool # DuckDB adds default database parameter @@ -248,11 +244,11 @@ async def test_asyncpg_connection_instance_overrides_connection_config_pool_para import asyncpg # Create pool with specific size - pool = await asyncpg.create_pool(**asyncpg_connection_config, min_size=3, max_size=5) + pool = await asyncpg.create_pool(**asyncpg_connection_config, min_size=3, max_size=5) # type: ignore[arg-type] try: # Config has different pool params but connection_instance should take precedence - merged_config = AsyncpgPoolConfig(**asyncpg_connection_config, min_size=10, max_size=20) + merged_config = {**asyncpg_connection_config, "min_size": 10, "max_size": 20} config = AsyncpgConfig(connection_config=merged_config, connection_instance=pool) # The injected pool should be used, not a new one with config params @@ -274,7 +270,7 @@ def test_connection_instance_manual_close() -> None: pool = DuckDBConnectionPool(connection_config={"database": ":memory:"}) - config = DuckDBConfig(connection_config=DuckDBConnectionParams(database=":memory:"), connection_instance=pool) + config = DuckDBConfig(connection_config=DuckDBPoolParams(database=":memory:"), connection_instance=pool) # Use the config with config.provide_session() as session: @@ -323,7 +319,7 @@ def test_connection_instance_with_mock_pool() -> None: mock_pool = MagicMock() mock_pool.acquire = MagicMock() - config = DuckDBConfig(connection_config=DuckDBConnectionParams(database=":memory:"), connection_instance=mock_pool) + config = DuckDBConfig(connection_config=DuckDBPoolParams(database=":memory:"), connection_instance=mock_pool) assert config.connection_instance is mock_pool diff --git a/tests/integration/test_pool_concurrency.py b/tests/integration/test_pool_concurrency.py index c03c13023..c59a998fc 100644 --- a/tests/integration/test_pool_concurrency.py +++ b/tests/integration/test_pool_concurrency.py @@ -7,7 +7,8 @@ import pytest from sqlspec.adapters.asyncpg import AsyncpgConfig, AsyncpgPoolConfig -from sqlspec.adapters.duckdb import DuckDBConfig, DuckDBConnectionParams +from sqlspec.adapters.duckdb import DuckDBConfig +from sqlspec.adapters.duckdb.config import DuckDBPoolParams if TYPE_CHECKING: from pytest_databases.docker.postgres import PostgresService @@ -50,7 +51,7 @@ async def get_pool() -> AsyncpgPool: def test_duckdb_pool_concurrency() -> None: """Verify that multiple concurrent calls to provide_pool result in a single pool (Sync).""" # Use shared memory db for valid concurrency test - config = DuckDBConfig(connection_config=DuckDBConnectionParams(database=":memory:")) + config = DuckDBConfig(connection_config=DuckDBPoolParams(database=":memory:")) # We need to capture results from threads results: list[DuckDBConnectionPool | None] = [None] * 50 diff --git a/tests/unit/extensions/test_litestar/test_providers.py b/tests/unit/extensions/test_litestar/test_providers.py index e5304abbf..f24f5be41 100644 --- a/tests/unit/extensions/test_litestar/test_providers.py +++ b/tests/unit/extensions/test_litestar/test_providers.py @@ -7,6 +7,7 @@ from sqlspec.core import InCollectionFilter, NotInCollectionFilter from sqlspec.extensions.litestar.providers import ( FieldNameType, + FilterConfig, _create_filter_aggregate_function, _create_statement_filters, dep_cache, @@ -26,7 +27,7 @@ def _clear_dependency_cache() -> Any: def test_in_fields_creates_provider_and_aggregate_param() -> None: """in_fields config creates both individual provider and aggregate parameter.""" - config: dict[str, Any] = {"in_fields": {FieldNameType(name="status", type_hint=str)}} + config = FilterConfig(in_fields={FieldNameType(name="status", type_hint=str)}) deps = _create_statement_filters(config) assert "status_in_filter" in deps @@ -35,7 +36,7 @@ def test_in_fields_creates_provider_and_aggregate_param() -> None: def test_in_fields_provider_returns_filter_when_values_present() -> None: """Individual in_fields provider returns InCollectionFilter when values are given.""" - config: dict[str, Any] = {"in_fields": {FieldNameType(name="status", type_hint=str)}} + config = FilterConfig(in_fields={FieldNameType(name="status", type_hint=str)}) deps = _create_statement_filters(config) provider = deps["status_in_filter"] @@ -48,7 +49,7 @@ def test_in_fields_provider_returns_filter_when_values_present() -> None: def test_in_fields_provider_returns_none_when_no_values() -> None: """Individual in_fields provider returns None when values is None.""" - config: dict[str, Any] = {"in_fields": {FieldNameType(name="status", type_hint=str)}} + config = FilterConfig(in_fields={FieldNameType(name="status", type_hint=str)}) deps = _create_statement_filters(config) provider = deps["status_in_filter"] @@ -58,7 +59,7 @@ def test_in_fields_provider_returns_none_when_no_values() -> None: def test_not_in_fields_provider_returns_filter_when_values_present() -> None: """Individual not_in_fields provider returns NotInCollectionFilter when values are given.""" - config: dict[str, Any] = {"not_in_fields": {FieldNameType(name="status", type_hint=str)}} + config = FilterConfig(not_in_fields={FieldNameType(name="status", type_hint=str)}) deps = _create_statement_filters(config) provider = deps["status_not_in_filter"] @@ -71,7 +72,7 @@ def test_not_in_fields_provider_returns_filter_when_values_present() -> None: def test_aggregate_function_includes_in_filter() -> None: """Aggregate function includes InCollectionFilter in results.""" - config: dict[str, Any] = {"in_fields": {FieldNameType(name="status", type_hint=str)}} + config = FilterConfig(in_fields={FieldNameType(name="status", type_hint=str)}) agg_fn = _create_filter_aggregate_function(config) in_filter = InCollectionFilter(field_name="status", values=["active", "pending"]) @@ -84,7 +85,7 @@ def test_aggregate_function_includes_in_filter() -> None: def test_aggregate_function_excludes_none_in_filter() -> None: """Aggregate function excludes None in_filter values.""" - config: dict[str, Any] = {"in_fields": {FieldNameType(name="status", type_hint=str)}} + config = FilterConfig(in_fields={FieldNameType(name="status", type_hint=str)}) agg_fn = _create_filter_aggregate_function(config) result = agg_fn(status_in_filter=None) @@ -93,9 +94,9 @@ def test_aggregate_function_excludes_none_in_filter() -> None: def test_aggregate_function_multiple_in_fields() -> None: """Aggregate function handles multiple in_fields.""" - config: dict[str, Any] = { - "in_fields": {FieldNameType(name="status", type_hint=str), FieldNameType(name="role", type_hint=str)} - } + config = FilterConfig( + in_fields={FieldNameType(name="status", type_hint=str), FieldNameType(name="role", type_hint=str)} + ) agg_fn = _create_filter_aggregate_function(config) status_filter = InCollectionFilter(field_name="status", values=["active"]) @@ -104,13 +105,13 @@ def test_aggregate_function_multiple_in_fields() -> None: result = agg_fn(status_in_filter=status_filter, role_in_filter=role_filter) assert len(result) == 2 - field_names = {f.field_name for f in result} + field_names = {f.field_name for f in result if isinstance(f, (InCollectionFilter, NotInCollectionFilter))} assert field_names == {"status", "role"} def test_in_fields_with_string_config() -> None: """in_fields config with list of strings works correctly.""" - config: dict[str, Any] = {"in_fields": ["status"]} + config = FilterConfig(in_fields=["status"]) deps = _create_statement_filters(config) assert "status_in_filter" in deps diff --git a/uv.lock b/uv.lock index 9f1f3835a..177203391 100644 --- a/uv.lock +++ b/uv.lock @@ -174,7 +174,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.13.4" +version = "3.13.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -186,110 +186,110 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/45/4a/064321452809dae953c1ed6e017504e72551a26b6f5708a5a80e4bf556ff/aiohttp-3.13.4.tar.gz", hash = "sha256:d97a6d09c66087890c2ab5d49069e1e570583f7ac0314ecf98294c1b6aaebd38", size = 7859748, upload-time = "2026-03-28T17:19:40.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/05/6817e0390eb47b0867cf8efdb535298191662192281bc3ca62a0cb7973eb/aiohttp-3.13.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6290fe12fe8cefa6ea3c1c5b969d32c010dfe191d4392ff9b599a3f473cbe722", size = 753094, upload-time = "2026-03-28T17:14:59.928Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c1/e5b7f25f6dd1ab57da92aa9d226b2c8b56f223dd20475d3ddfddaba86ab8/aiohttp-3.13.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7520d92c0e8fbbe63f36f20a5762db349ff574ad38ad7bc7732558a650439845", size = 505213, upload-time = "2026-03-28T17:15:01.989Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e5/8f42033c7ce98b54dfd3791f03e60231cfe4a2db4471b5fc188df2b8a6ad/aiohttp-3.13.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d2710ae1e1b81d0f187883b6e9d66cecf8794b50e91aa1e73fc78bfb5503b5d9", size = 498580, upload-time = "2026-03-28T17:15:03.879Z" }, - { url = "https://files.pythonhosted.org/packages/8c/a4/bbc989f5362066b81930da1a66084a859a971d03faab799dc59a3ce3a220/aiohttp-3.13.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:717d17347567ded1e273aa09918650dfd6fd06f461549204570c7973537d4123", size = 1692718, upload-time = "2026-03-28T17:15:05.541Z" }, - { url = "https://files.pythonhosted.org/packages/1c/72/3775116969931f151be116689d2ae6ddafff2ec2887d8f9b4e7043f32e74/aiohttp-3.13.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:383880f7b8de5ac208fa829c7038d08e66377283b2de9e791b71e06e803153c2", size = 1660714, upload-time = "2026-03-28T17:15:08.23Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e8/d2f1a2da2743e32fe348ebf8a4c59caad14a92f5f18af616fd33381275e1/aiohttp-3.13.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1867087e2c1963db1216aedf001efe3b129835ed2b05d97d058176a6d08b5726", size = 1744152, upload-time = "2026-03-28T17:15:10.828Z" }, - { url = "https://files.pythonhosted.org/packages/4c/a6/575886f417ac3c08e462f2ca237cc49f436bd992ca3f7ff95b7dd9c44205/aiohttp-3.13.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6234bf416a38d687c3ab7f79934d7fb2a42117a5b9813aca07de0a5398489023", size = 1836278, upload-time = "2026-03-28T17:15:12.537Z" }, - { url = "https://files.pythonhosted.org/packages/4a/4c/0051d4550fb9e8b5ca4e0fe1ccd58652340915180c5164999e6741bf2083/aiohttp-3.13.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3cdd3393130bf6588962441ffd5bde1d3ea2d63a64afa7119b3f3ba349cebbe7", size = 1687953, upload-time = "2026-03-28T17:15:14.248Z" }, - { url = "https://files.pythonhosted.org/packages/c9/54/841e87b8c51c2adc01a3ceb9919dc45c7899fe4c21deb70aada734ea5a38/aiohttp-3.13.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0d0dbc6c76befa76865373d6aa303e480bb8c3486e7763530f7f6e527b471118", size = 1572484, upload-time = "2026-03-28T17:15:15.911Z" }, - { url = "https://files.pythonhosted.org/packages/da/f1/21cbf5f7fa1e267af6301f886cab9b314f085e4d0097668d189d165cd7da/aiohttp-3.13.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10fb7b53262cf4144a083c9db0d2b4d22823d6708270a9970c4627b248c6064c", size = 1662851, upload-time = "2026-03-28T17:15:17.822Z" }, - { url = "https://files.pythonhosted.org/packages/40/15/bcad6b68d7bef27ae7443288215767263c7753ede164267cf6cf63c94a87/aiohttp-3.13.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:eb10ce8c03850e77f4d9518961c227be569e12f71525a7e90d17bca04299921d", size = 1671984, upload-time = "2026-03-28T17:15:19.561Z" }, - { url = "https://files.pythonhosted.org/packages/ff/fa/ab316931afc7a73c7f493bb1b30fbd61e28ec2d3ea50353336e76293e8ec/aiohttp-3.13.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:7c65738ac5ae32b8feef699a4ed0dc91a0c8618b347781b7461458bbcaaac7eb", size = 1713880, upload-time = "2026-03-28T17:15:21.589Z" }, - { url = "https://files.pythonhosted.org/packages/1c/45/314e8e64c7f328174964b6db511dd5e9e60c9121ab5457bc2c908b7d03a4/aiohttp-3.13.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:6b335919ffbaf98df8ff3c74f7a6decb8775882632952fd1810a017e38f15aee", size = 1560315, upload-time = "2026-03-28T17:15:23.66Z" }, - { url = "https://files.pythonhosted.org/packages/18/e7/93d5fa06fe00219a81466577dacae9e3732f3b4f767b12b2e2cc8c35c970/aiohttp-3.13.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ec75fc18cb9f4aca51c2cbace20cf6716e36850f44189644d2d69a875d5e0532", size = 1735115, upload-time = "2026-03-28T17:15:25.77Z" }, - { url = "https://files.pythonhosted.org/packages/19/9f/f64b95392ddd4e204fd9ab7cd33dd18d14ac9e4b86866f1f6a69b7cda83d/aiohttp-3.13.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:463fa18a95c5a635d2b8c09babe240f9d7dbf2a2010a6c0b35d8c4dff2a0e819", size = 1673916, upload-time = "2026-03-28T17:15:27.526Z" }, - { url = "https://files.pythonhosted.org/packages/52/c1/bb33be79fd285c69f32e5b074b299cae8847f748950149c3965c1b3b3adf/aiohttp-3.13.4-cp310-cp310-win32.whl", hash = "sha256:13168f5645d9045522c6cef818f54295376257ed8d02513a37c2ef3046fc7a97", size = 440277, upload-time = "2026-03-28T17:15:29.173Z" }, - { url = "https://files.pythonhosted.org/packages/23/f9/7cf1688da4dd0885f914ee40bc8e1dce776df98fe6518766de975a570538/aiohttp-3.13.4-cp310-cp310-win_amd64.whl", hash = "sha256:a7058af1f53209fdf07745579ced525d38d481650a989b7aa4a3b484b901cdab", size = 463015, upload-time = "2026-03-28T17:15:30.802Z" }, - { url = "https://files.pythonhosted.org/packages/d4/7e/cb94129302d78c46662b47f9897d642fd0b33bdfef4b73b20c6ced35aa4c/aiohttp-3.13.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8ea0c64d1bcbf201b285c2246c51a0c035ba3bbd306640007bc5844a3b4658c1", size = 760027, upload-time = "2026-03-28T17:15:33.022Z" }, - { url = "https://files.pythonhosted.org/packages/5e/cd/2db3c9397c3bd24216b203dd739945b04f8b87bb036c640da7ddb63c75ef/aiohttp-3.13.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6f742e1fa45c0ed522b00ede565e18f97e4cf8d1883a712ac42d0339dfb0cce7", size = 508325, upload-time = "2026-03-28T17:15:34.714Z" }, - { url = "https://files.pythonhosted.org/packages/36/a3/d28b2722ec13107f2e37a86b8a169897308bab6a3b9e071ecead9d67bd9b/aiohttp-3.13.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dcfb50ee25b3b7a1222a9123be1f9f89e56e67636b561441f0b304e25aaef8f", size = 502402, upload-time = "2026-03-28T17:15:36.409Z" }, - { url = "https://files.pythonhosted.org/packages/fa/d6/acd47b5f17c4430e555590990a4746efbcb2079909bb865516892bf85f37/aiohttp-3.13.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3262386c4ff370849863ea93b9ea60fd59c6cf56bf8f93beac625cf4d677c04d", size = 1771224, upload-time = "2026-03-28T17:15:38.223Z" }, - { url = "https://files.pythonhosted.org/packages/98/af/af6e20113ba6a48fd1cd9e5832c4851e7613ef50c7619acdaee6ec5f1aff/aiohttp-3.13.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:473bb5aa4218dd254e9ae4834f20e31f5a0083064ac0136a01a62ddbae2eaa42", size = 1731530, upload-time = "2026-03-28T17:15:39.988Z" }, - { url = "https://files.pythonhosted.org/packages/81/16/78a2f5d9c124ad05d5ce59a9af94214b6466c3491a25fb70760e98e9f762/aiohttp-3.13.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e56423766399b4c77b965f6aaab6c9546617b8994a956821cc507d00b91d978c", size = 1827925, upload-time = "2026-03-28T17:15:41.944Z" }, - { url = "https://files.pythonhosted.org/packages/2a/1f/79acf0974ced805e0e70027389fccbb7d728e6f30fcac725fb1071e63075/aiohttp-3.13.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8af249343fafd5ad90366a16d230fc265cf1149f26075dc9fe93cfd7c7173942", size = 1923579, upload-time = "2026-03-28T17:15:44.071Z" }, - { url = "https://files.pythonhosted.org/packages/af/53/29f9e2054ea6900413f3b4c3eb9d8331f60678ec855f13ba8714c47fd48d/aiohttp-3.13.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bc0a5cf4f10ef5a2c94fdde488734b582a3a7a000b131263e27c9295bd682d9", size = 1767655, upload-time = "2026-03-28T17:15:45.911Z" }, - { url = "https://files.pythonhosted.org/packages/f3/57/462fe1d3da08109ba4aa8590e7aed57c059af2a7e80ec21f4bac5cfe1094/aiohttp-3.13.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5c7ff1028e3c9fc5123a865ce17df1cb6424d180c503b8517afbe89aa566e6be", size = 1630439, upload-time = "2026-03-28T17:15:48.11Z" }, - { url = "https://files.pythonhosted.org/packages/d7/4b/4813344aacdb8127263e3eec343d24e973421143826364fa9fc847f6283f/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ba5cf98b5dcb9bddd857da6713a503fa6d341043258ca823f0f5ab7ab4a94ee8", size = 1745557, upload-time = "2026-03-28T17:15:50.13Z" }, - { url = "https://files.pythonhosted.org/packages/d4/01/1ef1adae1454341ec50a789f03cfafe4c4ac9c003f6a64515ecd32fe4210/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d85965d3ba21ee4999e83e992fecb86c4614d6920e40705501c0a1f80a583c12", size = 1741796, upload-time = "2026-03-28T17:15:52.351Z" }, - { url = "https://files.pythonhosted.org/packages/22/04/8cdd99af988d2aa6922714d957d21383c559835cbd43fbf5a47ddf2e0f05/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:49f0b18a9b05d79f6f37ddd567695943fcefb834ef480f17a4211987302b2dc7", size = 1805312, upload-time = "2026-03-28T17:15:54.407Z" }, - { url = "https://files.pythonhosted.org/packages/fb/7f/b48d5577338d4b25bbdbae35c75dbfd0493cb8886dc586fbfb2e90862239/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7f78cb080c86fbf765920e5f1ef35af3f24ec4314d6675d0a21eaf41f6f2679c", size = 1621751, upload-time = "2026-03-28T17:15:56.564Z" }, - { url = "https://files.pythonhosted.org/packages/bc/89/4eecad8c1858e6d0893c05929e22343e0ebe3aec29a8a399c65c3cc38311/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:67a3ec705534a614b68bbf1c70efa777a21c3da3895d1c44510a41f5a7ae0453", size = 1826073, upload-time = "2026-03-28T17:15:58.489Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5c/9dc8293ed31b46c39c9c513ac7ca152b3c3d38e0ea111a530ad12001b827/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d6630ec917e85c5356b2295744c8a97d40f007f96a1c76bf1928dc2e27465393", size = 1760083, upload-time = "2026-03-28T17:16:00.677Z" }, - { url = "https://files.pythonhosted.org/packages/1e/19/8bbf6a4994205d96831f97b7d21a0feed120136e6267b5b22d229c6dc4dc/aiohttp-3.13.4-cp311-cp311-win32.whl", hash = "sha256:54049021bc626f53a5394c29e8c444f726ee5a14b6e89e0ad118315b1f90f5e3", size = 439690, upload-time = "2026-03-28T17:16:02.902Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f5/ac409ecd1007528d15c3e8c3a57d34f334c70d76cfb7128a28cffdebd4c1/aiohttp-3.13.4-cp311-cp311-win_amd64.whl", hash = "sha256:c033f2bc964156030772d31cbf7e5defea181238ce1f87b9455b786de7d30145", size = 463824, upload-time = "2026-03-28T17:16:05.058Z" }, - { url = "https://files.pythonhosted.org/packages/1e/bd/ede278648914cabbabfdf95e436679b5d4156e417896a9b9f4587169e376/aiohttp-3.13.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ee62d4471ce86b108b19c3364db4b91180d13fe3510144872d6bad5401957360", size = 752158, upload-time = "2026-03-28T17:16:06.901Z" }, - { url = "https://files.pythonhosted.org/packages/90/de/581c053253c07b480b03785196ca5335e3c606a37dc73e95f6527f1591fe/aiohttp-3.13.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c0fd8f41b54b58636402eb493afd512c23580456f022c1ba2db0f810c959ed0d", size = 501037, upload-time = "2026-03-28T17:16:08.82Z" }, - { url = "https://files.pythonhosted.org/packages/fa/f9/a5ede193c08f13cc42c0a5b50d1e246ecee9115e4cf6e900d8dbd8fd6acb/aiohttp-3.13.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4baa48ce49efd82d6b1a0be12d6a36b35e5594d1dd42f8bfba96ea9f8678b88c", size = 501556, upload-time = "2026-03-28T17:16:10.63Z" }, - { url = "https://files.pythonhosted.org/packages/d6/10/88ff67cd48a6ec36335b63a640abe86135791544863e0cfe1f065d6cef7a/aiohttp-3.13.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d738ebab9f71ee652d9dbd0211057690022201b11197f9a7324fd4dba128aa97", size = 1757314, upload-time = "2026-03-28T17:16:12.498Z" }, - { url = "https://files.pythonhosted.org/packages/8b/15/fdb90a5cf5a1f52845c276e76298c75fbbcc0ac2b4a86551906d54529965/aiohttp-3.13.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0ce692c3468fa831af7dceed52edf51ac348cebfc8d3feb935927b63bd3e8576", size = 1731819, upload-time = "2026-03-28T17:16:14.558Z" }, - { url = "https://files.pythonhosted.org/packages/ec/df/28146785a007f7820416be05d4f28cc207493efd1e8c6c1068e9bdc29198/aiohttp-3.13.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8e08abcfe752a454d2cb89ff0c08f2d1ecd057ae3e8cc6d84638de853530ebab", size = 1793279, upload-time = "2026-03-28T17:16:16.594Z" }, - { url = "https://files.pythonhosted.org/packages/10/47/689c743abf62ea7a77774d5722f220e2c912a77d65d368b884d9779ef41b/aiohttp-3.13.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5977f701b3fff36367a11087f30ea73c212e686d41cd363c50c022d48b011d8d", size = 1891082, upload-time = "2026-03-28T17:16:18.71Z" }, - { url = "https://files.pythonhosted.org/packages/b0/b6/f7f4f318c7e58c23b761c9b13b9a3c9b394e0f9d5d76fbc6622fa98509f6/aiohttp-3.13.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54203e10405c06f8b6020bd1e076ae0fe6c194adcee12a5a78af3ffa3c57025e", size = 1773938, upload-time = "2026-03-28T17:16:21.125Z" }, - { url = "https://files.pythonhosted.org/packages/aa/06/f207cb3121852c989586a6fc16ff854c4fcc8651b86c5d3bd1fc83057650/aiohttp-3.13.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:358a6af0145bc4dda037f13167bef3cce54b132087acc4c295c739d05d16b1c3", size = 1579548, upload-time = "2026-03-28T17:16:23.588Z" }, - { url = "https://files.pythonhosted.org/packages/6c/58/e1289661a32161e24c1fe479711d783067210d266842523752869cc1d9c2/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:898ea1850656d7d61832ef06aa9846ab3ddb1621b74f46de78fbc5e1a586ba83", size = 1714669, upload-time = "2026-03-28T17:16:25.713Z" }, - { url = "https://files.pythonhosted.org/packages/96/0a/3e86d039438a74a86e6a948a9119b22540bae037d6ba317a042ae3c22711/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7bc30cceb710cf6a44e9617e43eebb6e3e43ad855a34da7b4b6a73537d8a6763", size = 1754175, upload-time = "2026-03-28T17:16:28.18Z" }, - { url = "https://files.pythonhosted.org/packages/f4/30/e717fc5df83133ba467a560b6d8ef20197037b4bb5d7075b90037de1018e/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4a31c0c587a8a038f19a4c7e60654a6c899c9de9174593a13e7cc6e15ff271f9", size = 1762049, upload-time = "2026-03-28T17:16:30.941Z" }, - { url = "https://files.pythonhosted.org/packages/e4/28/8f7a2d4492e336e40005151bdd94baf344880a4707573378579f833a64c1/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2062f675f3fe6e06d6113eb74a157fb9df58953ffed0cdb4182554b116545758", size = 1570861, upload-time = "2026-03-28T17:16:32.953Z" }, - { url = "https://files.pythonhosted.org/packages/78/45/12e1a3d0645968b1c38de4b23fdf270b8637735ea057d4f84482ff918ad9/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d1ba8afb847ff80626d5e408c1fdc99f942acc877d0702fe137015903a220a9", size = 1790003, upload-time = "2026-03-28T17:16:35.468Z" }, - { url = "https://files.pythonhosted.org/packages/eb/0f/60374e18d590de16dcb39d6ff62f39c096c1b958e6f37727b5870026ea30/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b08149419994cdd4d5eecf7fd4bc5986b5a9380285bcd01ab4c0d6bfca47b79d", size = 1737289, upload-time = "2026-03-28T17:16:38.187Z" }, - { url = "https://files.pythonhosted.org/packages/02/bf/535e58d886cfbc40a8b0013c974afad24ef7632d645bca0b678b70033a60/aiohttp-3.13.4-cp312-cp312-win32.whl", hash = "sha256:fc432f6a2c4f720180959bc19aa37259651c1a4ed8af8afc84dd41c60f15f791", size = 434185, upload-time = "2026-03-28T17:16:40.735Z" }, - { url = "https://files.pythonhosted.org/packages/1e/1a/d92e3325134ebfff6f4069f270d3aac770d63320bd1fcd0eca023e74d9a8/aiohttp-3.13.4-cp312-cp312-win_amd64.whl", hash = "sha256:6148c9ae97a3e8bff9a1fc9c757fa164116f86c100468339730e717590a3fb77", size = 461285, upload-time = "2026-03-28T17:16:42.713Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ac/892f4162df9b115b4758d615f32ec63d00f3084c705ff5526630887b9b42/aiohttp-3.13.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:63dd5e5b1e43b8fb1e91b79b7ceba1feba588b317d1edff385084fcc7a0a4538", size = 745744, upload-time = "2026-03-28T17:16:44.67Z" }, - { url = "https://files.pythonhosted.org/packages/97/a9/c5b87e4443a2f0ea88cb3000c93a8fdad1ee63bffc9ded8d8c8e0d66efc6/aiohttp-3.13.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:746ac3cc00b5baea424dacddea3ec2c2702f9590de27d837aa67004db1eebc6e", size = 498178, upload-time = "2026-03-28T17:16:46.766Z" }, - { url = "https://files.pythonhosted.org/packages/94/42/07e1b543a61250783650df13da8ddcdc0d0a5538b2bd15cef6e042aefc61/aiohttp-3.13.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bda8f16ea99d6a6705e5946732e48487a448be874e54a4f73d514660ff7c05d3", size = 498331, upload-time = "2026-03-28T17:16:48.9Z" }, - { url = "https://files.pythonhosted.org/packages/20/d6/492f46bf0328534124772d0cf58570acae5b286ea25006900650f69dae0e/aiohttp-3.13.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b061e7b5f840391e3f64d0ddf672973e45c4cfff7a0feea425ea24e51530fc2", size = 1744414, upload-time = "2026-03-28T17:16:50.968Z" }, - { url = "https://files.pythonhosted.org/packages/e2/4d/e02627b2683f68051246215d2d62b2d2f249ff7a285e7a858dc47d6b6a14/aiohttp-3.13.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b252e8d5cd66184b570d0d010de742736e8a4fab22c58299772b0c5a466d4b21", size = 1719226, upload-time = "2026-03-28T17:16:53.173Z" }, - { url = "https://files.pythonhosted.org/packages/7b/6c/5d0a3394dd2b9f9aeba6e1b6065d0439e4b75d41f1fb09a3ec010b43552b/aiohttp-3.13.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:20af8aad61d1803ff11152a26146d8d81c266aa8c5aa9b4504432abb965c36a0", size = 1782110, upload-time = "2026-03-28T17:16:55.362Z" }, - { url = "https://files.pythonhosted.org/packages/0d/2d/c20791e3437700a7441a7edfb59731150322424f5aadf635602d1d326101/aiohttp-3.13.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:13a5cc924b59859ad2adb1478e31f410a7ed46e92a2a619d6d1dd1a63c1a855e", size = 1884809, upload-time = "2026-03-28T17:16:57.734Z" }, - { url = "https://files.pythonhosted.org/packages/c8/94/d99dbfbd1924a87ef643833932eb2a3d9e5eee87656efea7d78058539eff/aiohttp-3.13.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:534913dfb0a644d537aebb4123e7d466d94e3be5549205e6a31f72368980a81a", size = 1764938, upload-time = "2026-03-28T17:17:00.221Z" }, - { url = "https://files.pythonhosted.org/packages/49/61/3ce326a1538781deb89f6cf5e094e2029cd308ed1e21b2ba2278b08426f6/aiohttp-3.13.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:320e40192a2dcc1cf4b5576936e9652981ab596bf81eb309535db7e2f5b5672f", size = 1570697, upload-time = "2026-03-28T17:17:02.985Z" }, - { url = "https://files.pythonhosted.org/packages/b6/77/4ab5a546857bb3028fbaf34d6eea180267bdab022ee8b1168b1fcde4bfdd/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9e587fcfce2bcf06526a43cb705bdee21ac089096f2e271d75de9c339db3100c", size = 1702258, upload-time = "2026-03-28T17:17:05.28Z" }, - { url = "https://files.pythonhosted.org/packages/79/63/d8f29021e39bc5af8e5d5e9da1b07976fb9846487a784e11e4f4eeda4666/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9eb9c2eea7278206b5c6c1441fdd9dc420c278ead3f3b2cc87f9b693698cc500", size = 1740287, upload-time = "2026-03-28T17:17:07.712Z" }, - { url = "https://files.pythonhosted.org/packages/55/3a/cbc6b3b124859a11bc8055d3682c26999b393531ef926754a3445b99dfef/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:29be00c51972b04bf9d5c8f2d7f7314f48f96070ca40a873a53056e652e805f7", size = 1753011, upload-time = "2026-03-28T17:17:10.053Z" }, - { url = "https://files.pythonhosted.org/packages/e0/30/836278675205d58c1368b21520eab9572457cf19afd23759216c04483048/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:90c06228a6c3a7c9f776fe4fc0b7ff647fffd3bed93779a6913c804ae00c1073", size = 1566359, upload-time = "2026-03-28T17:17:12.433Z" }, - { url = "https://files.pythonhosted.org/packages/50/b4/8032cc9b82d17e4277704ba30509eaccb39329dc18d6a35f05e424439e32/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a533ec132f05fd9a1d959e7f34184cd7d5e8511584848dab85faefbaac573069", size = 1785537, upload-time = "2026-03-28T17:17:14.721Z" }, - { url = "https://files.pythonhosted.org/packages/17/7d/5873e98230bde59f493bf1f7c3e327486a4b5653fa401144704df5d00211/aiohttp-3.13.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1c946f10f413836f82ea4cfb90200d2a59578c549f00857e03111cf45ad01ca5", size = 1740752, upload-time = "2026-03-28T17:17:17.387Z" }, - { url = "https://files.pythonhosted.org/packages/7b/f2/13e46e0df051494d7d3c68b7f72d071f48c384c12716fc294f75d5b1a064/aiohttp-3.13.4-cp313-cp313-win32.whl", hash = "sha256:48708e2706106da6967eff5908c78ca3943f005ed6bcb75da2a7e4da94ef8c70", size = 433187, upload-time = "2026-03-28T17:17:19.523Z" }, - { url = "https://files.pythonhosted.org/packages/ea/c0/649856ee655a843c8f8664592cfccb73ac80ede6a8c8db33a25d810c12db/aiohttp-3.13.4-cp313-cp313-win_amd64.whl", hash = "sha256:74a2eb058da44fa3a877a49e2095b591d4913308bb424c418b77beb160c55ce3", size = 459778, upload-time = "2026-03-28T17:17:21.964Z" }, - { url = "https://files.pythonhosted.org/packages/6d/29/6657cc37ae04cacc2dbf53fb730a06b6091cc4cbe745028e047c53e6d840/aiohttp-3.13.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:e0a2c961fc92abeff61d6444f2ce6ad35bb982db9fc8ff8a47455beacf454a57", size = 749363, upload-time = "2026-03-28T17:17:24.044Z" }, - { url = "https://files.pythonhosted.org/packages/90/7f/30ccdf67ca3d24b610067dc63d64dcb91e5d88e27667811640644aa4a85d/aiohttp-3.13.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:153274535985a0ff2bff1fb6c104ed547cec898a09213d21b0f791a44b14d933", size = 499317, upload-time = "2026-03-28T17:17:26.199Z" }, - { url = "https://files.pythonhosted.org/packages/93/13/e372dd4e68ad04ee25dafb050c7f98b0d91ea643f7352757e87231102555/aiohttp-3.13.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:351f3171e2458da3d731ce83f9e6b9619e325c45cbd534c7759750cabf453ad7", size = 500477, upload-time = "2026-03-28T17:17:28.279Z" }, - { url = "https://files.pythonhosted.org/packages/e5/fe/ee6298e8e586096fb6f5eddd31393d8544f33ae0792c71ecbb4c2bef98ac/aiohttp-3.13.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f989ac8bc5595ff761a5ccd32bdb0768a117f36dd1504b1c2c074ed5d3f4df9c", size = 1737227, upload-time = "2026-03-28T17:17:30.587Z" }, - { url = "https://files.pythonhosted.org/packages/b0/b9/a7a0463a09e1a3fe35100f74324f23644bfc3383ac5fd5effe0722a5f0b7/aiohttp-3.13.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d36fc1709110ec1e87a229b201dd3ddc32aa01e98e7868083a794609b081c349", size = 1694036, upload-time = "2026-03-28T17:17:33.29Z" }, - { url = "https://files.pythonhosted.org/packages/57/7c/8972ae3fb7be00a91aee6b644b2a6a909aedb2c425269a3bfd90115e6f8f/aiohttp-3.13.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42adaeea83cbdf069ab94f5103ce0787c21fb1a0153270da76b59d5578302329", size = 1786814, upload-time = "2026-03-28T17:17:36.035Z" }, - { url = "https://files.pythonhosted.org/packages/93/01/c81e97e85c774decbaf0d577de7d848934e8166a3a14ad9f8aa5be329d28/aiohttp-3.13.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:92deb95469928cc41fd4b42a95d8012fa6df93f6b1c0a83af0ffbc4a5e218cde", size = 1866676, upload-time = "2026-03-28T17:17:38.441Z" }, - { url = "https://files.pythonhosted.org/packages/5a/5f/5b46fe8694a639ddea2cd035bf5729e4677ea882cb251396637e2ef1590d/aiohttp-3.13.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c0c7c07c4257ef3a1df355f840bc62d133bcdef5c1c5ba75add3c08553e2eed", size = 1740842, upload-time = "2026-03-28T17:17:40.783Z" }, - { url = "https://files.pythonhosted.org/packages/20/a2/0d4b03d011cca6b6b0acba8433193c1e484efa8d705ea58295590fe24203/aiohttp-3.13.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f062c45de8a1098cb137a1898819796a2491aec4e637a06b03f149315dff4d8f", size = 1566508, upload-time = "2026-03-28T17:17:43.235Z" }, - { url = "https://files.pythonhosted.org/packages/98/17/e689fd500da52488ec5f889effd6404dece6a59de301e380f3c64f167beb/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:76093107c531517001114f0ebdb4f46858ce818590363e3e99a4a2280334454a", size = 1700569, upload-time = "2026-03-28T17:17:46.165Z" }, - { url = "https://files.pythonhosted.org/packages/d8/0d/66402894dbcf470ef7db99449e436105ea862c24f7ea4c95c683e635af35/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:6f6ec32162d293b82f8b63a16edc80769662fbd5ae6fbd4936d3206a2c2cc63b", size = 1707407, upload-time = "2026-03-28T17:17:48.825Z" }, - { url = "https://files.pythonhosted.org/packages/2f/eb/af0ab1a3650092cbd8e14ef29e4ab0209e1460e1c299996c3f8288b3f1ff/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5903e2db3d202a00ad9f0ec35a122c005e85d90c9836ab4cda628f01edf425e2", size = 1752214, upload-time = "2026-03-28T17:17:51.206Z" }, - { url = "https://files.pythonhosted.org/packages/5a/bf/72326f8a98e4c666f292f03c385545963cc65e358835d2a7375037a97b57/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2d5bea57be7aca98dbbac8da046d99b5557c5cf4e28538c4c786313078aca09e", size = 1562162, upload-time = "2026-03-28T17:17:53.634Z" }, - { url = "https://files.pythonhosted.org/packages/67/9f/13b72435f99151dd9a5469c96b3b5f86aa29b7e785ca7f35cf5e538f74c0/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:bcf0c9902085976edc0232b75006ef38f89686901249ce14226b6877f88464fb", size = 1768904, upload-time = "2026-03-28T17:17:55.991Z" }, - { url = "https://files.pythonhosted.org/packages/18/bc/28d4970e7d5452ac7776cdb5431a1164a0d9cf8bd2fffd67b4fb463aa56d/aiohttp-3.13.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c3295f98bfeed2e867cab588f2a146a9db37a85e3ae9062abf46ba062bd29165", size = 1723378, upload-time = "2026-03-28T17:17:58.348Z" }, - { url = "https://files.pythonhosted.org/packages/53/74/b32458ca1a7f34d65bdee7aef2036adbe0438123d3d53e2b083c453c24dd/aiohttp-3.13.4-cp314-cp314-win32.whl", hash = "sha256:a598a5c5767e1369d8f5b08695cab1d8160040f796c4416af76fd773d229b3c9", size = 438711, upload-time = "2026-03-28T17:18:00.728Z" }, - { url = "https://files.pythonhosted.org/packages/40/b2/54b487316c2df3e03a8f3435e9636f8a81a42a69d942164830d193beb56a/aiohttp-3.13.4-cp314-cp314-win_amd64.whl", hash = "sha256:c555db4bc7a264bead5a7d63d92d41a1122fcd39cc62a4db815f45ad46f9c2c8", size = 464977, upload-time = "2026-03-28T17:18:03.367Z" }, - { url = "https://files.pythonhosted.org/packages/47/fb/e41b63c6ce71b07a59243bb8f3b457ee0c3402a619acb9d2c0d21ef0e647/aiohttp-3.13.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45abbbf09a129825d13c18c7d3182fecd46d9da3cfc383756145394013604ac1", size = 781549, upload-time = "2026-03-28T17:18:05.779Z" }, - { url = "https://files.pythonhosted.org/packages/97/53/532b8d28df1e17e44c4d9a9368b78dcb6bf0b51037522136eced13afa9e8/aiohttp-3.13.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:74c80b2bc2c2adb7b3d1941b2b60701ee2af8296fc8aad8b8bc48bc25767266c", size = 514383, upload-time = "2026-03-28T17:18:08.096Z" }, - { url = "https://files.pythonhosted.org/packages/1b/1f/62e5d400603e8468cd635812d99cb81cfdc08127a3dc474c647615f31339/aiohttp-3.13.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c97989ae40a9746650fa196894f317dafc12227c808c774929dda0ff873a5954", size = 518304, upload-time = "2026-03-28T17:18:10.642Z" }, - { url = "https://files.pythonhosted.org/packages/90/57/2326b37b10896447e3c6e0cbef4fe2486d30913639a5cfd1332b5d870f82/aiohttp-3.13.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dae86be9811493f9990ef44fff1685f5c1a3192e9061a71a109d527944eed551", size = 1893433, upload-time = "2026-03-28T17:18:13.121Z" }, - { url = "https://files.pythonhosted.org/packages/d2/b4/a24d82112c304afdb650167ef2fe190957d81cbddac7460bedd245f765aa/aiohttp-3.13.4-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1db491abe852ca2fa6cc48a3341985b0174b3741838e1341b82ac82c8bd9e871", size = 1755901, upload-time = "2026-03-28T17:18:16.21Z" }, - { url = "https://files.pythonhosted.org/packages/9e/2d/0883ef9d878d7846287f036c162a951968f22aabeef3ac97b0bea6f76d5d/aiohttp-3.13.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0e5d701c0aad02a7dce72eef6b93226cf3734330f1a31d69ebbf69f33b86666e", size = 1876093, upload-time = "2026-03-28T17:18:18.703Z" }, - { url = "https://files.pythonhosted.org/packages/ad/52/9204bb59c014869b71971addad6778f005daa72a96eed652c496789d7468/aiohttp-3.13.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8ac32a189081ae0a10ba18993f10f338ec94341f0d5df8fff348043962f3c6f8", size = 1970815, upload-time = "2026-03-28T17:18:21.858Z" }, - { url = "https://files.pythonhosted.org/packages/d6/b5/e4eb20275a866dde0f570f411b36c6b48f7b53edfe4f4071aa1b0728098a/aiohttp-3.13.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98e968cdaba43e45c73c3f306fca418c8009a957733bac85937c9f9cf3f4de27", size = 1816223, upload-time = "2026-03-28T17:18:24.729Z" }, - { url = "https://files.pythonhosted.org/packages/d8/23/e98075c5bb146aa61a1239ee1ac7714c85e814838d6cebbe37d3fe19214a/aiohttp-3.13.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca114790c9144c335d538852612d3e43ea0f075288f4849cf4b05d6cd2238ce7", size = 1649145, upload-time = "2026-03-28T17:18:27.269Z" }, - { url = "https://files.pythonhosted.org/packages/d6/c1/7bad8be33bb06c2bb224b6468874346026092762cbec388c3bdb65a368ee/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ea2e071661ba9cfe11eabbc81ac5376eaeb3061f6e72ec4cc86d7cdd1ffbdbbb", size = 1816562, upload-time = "2026-03-28T17:18:29.847Z" }, - { url = "https://files.pythonhosted.org/packages/5c/10/c00323348695e9a5e316825969c88463dcc24c7e9d443244b8a2c9cf2eae/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:34e89912b6c20e0fd80e07fa401fd218a410aa1ce9f1c2f1dad6db1bd0ce0927", size = 1800333, upload-time = "2026-03-28T17:18:32.269Z" }, - { url = "https://files.pythonhosted.org/packages/84/43/9b2147a1df3559f49bd723e22905b46a46c068a53adb54abdca32c4de180/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0e217cf9f6a42908c52b46e42c568bd57adc39c9286ced31aaace614b6087965", size = 1820617, upload-time = "2026-03-28T17:18:35.238Z" }, - { url = "https://files.pythonhosted.org/packages/a9/7f/b3481a81e7a586d02e99387b18c6dafff41285f6efd3daa2124c01f87eae/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:0c296f1221e21ba979f5ac1964c3b78cfde15c5c5f855ffd2caab337e9cd9182", size = 1643417, upload-time = "2026-03-28T17:18:37.949Z" }, - { url = "https://files.pythonhosted.org/packages/8f/72/07181226bc99ce1124e0f89280f5221a82d3ae6a6d9d1973ce429d48e52b/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d99a9d168ebaffb74f36d011750e490085ac418f4db926cce3989c8fe6cb6b1b", size = 1849286, upload-time = "2026-03-28T17:18:40.534Z" }, - { url = "https://files.pythonhosted.org/packages/1a/e6/1b3566e103eca6da5be4ae6713e112a053725c584e96574caf117568ffef/aiohttp-3.13.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cb19177205d93b881f3f89e6081593676043a6828f59c78c17a0fd6c1fbed2ba", size = 1782635, upload-time = "2026-03-28T17:18:43.073Z" }, - { url = "https://files.pythonhosted.org/packages/37/58/1b11c71904b8d079eb0c39fe664180dd1e14bebe5608e235d8bfbadc8929/aiohttp-3.13.4-cp314-cp314t-win32.whl", hash = "sha256:c606aa5656dab6552e52ca368e43869c916338346bfaf6304e15c58fb113ea30", size = 472537, upload-time = "2026-03-28T17:18:46.286Z" }, - { url = "https://files.pythonhosted.org/packages/bc/8f/87c56a1a1977d7dddea5b31e12189665a140fdb48a71e9038ff90bb564ec/aiohttp-3.13.4-cp314-cp314t-win_amd64.whl", hash = "sha256:014dcc10ec8ab8db681f0d68e939d1e9286a5aa2b993cbbdb0db130853e02144", size = 506381, upload-time = "2026-03-28T17:18:48.74Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/85/cebc47ee74d8b408749073a1a46c6fcba13d170dc8af7e61996c6c9394ac/aiohttp-3.13.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:02222e7e233295f40e011c1b00e3b0bd451f22cf853a0304c3595633ee47da4b", size = 750547, upload-time = "2026-03-31T21:56:30.024Z" }, + { url = "https://files.pythonhosted.org/packages/05/98/afd308e35b9d3d8c9ec54c0918f1d722c86dc17ddfec272fcdbcce5a3124/aiohttp-3.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bace460460ed20614fa6bc8cb09966c0b8517b8c58ad8046828c6078d25333b5", size = 503535, upload-time = "2026-03-31T21:56:31.935Z" }, + { url = "https://files.pythonhosted.org/packages/6f/4d/926c183e06b09d5270a309eb50fbde7b09782bfd305dec1e800f329834fb/aiohttp-3.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f546a4dc1e6a5edbb9fd1fd6ad18134550e096a5a43f4ad74acfbd834fc6670", size = 497830, upload-time = "2026-03-31T21:56:33.654Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d6/f47d1c690f115a5c2a5e8938cce4a232a5be9aac5c5fb2647efcbbbda333/aiohttp-3.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c86969d012e51b8e415a8c6ce96f7857d6a87d6207303ab02d5d11ef0cad2274", size = 1682474, upload-time = "2026-03-31T21:56:35.513Z" }, + { url = "https://files.pythonhosted.org/packages/01/44/056fd37b1bb52eac760303e5196acc74d9d546631b035704ae5927f7b4ac/aiohttp-3.13.5-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b6f6cd1560c5fa427e3b6074bb24d2c64e225afbb7165008903bd42e4e33e28a", size = 1655259, upload-time = "2026-03-31T21:56:37.843Z" }, + { url = "https://files.pythonhosted.org/packages/91/9f/78eb1a20c1c28ae02f6a3c0f4d7b0dcc66abce5290cadd53d78ce3084175/aiohttp-3.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:636bc362f0c5bbc7372bc3ae49737f9e3030dbce469f0f422c8f38079780363d", size = 1736204, upload-time = "2026-03-31T21:56:39.822Z" }, + { url = "https://files.pythonhosted.org/packages/de/6c/d20d7de23f0b52b8c1d9e2033b2db1ac4dacbb470bb74c56de0f5f86bb4f/aiohttp-3.13.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a7cbeb06d1070f1d14895eeeed4dac5913b22d7b456f2eb969f11f4b3993796", size = 1826198, upload-time = "2026-03-31T21:56:41.378Z" }, + { url = "https://files.pythonhosted.org/packages/2f/86/a6f3ff1fd795f49545a7c74b2c92f62729135d73e7e4055bf74da5a26c82/aiohttp-3.13.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca9ef7517fd7874a1a08970ae88f497bf5c984610caa0bf40bd7e8450852b95", size = 1681329, upload-time = "2026-03-31T21:56:43.374Z" }, + { url = "https://files.pythonhosted.org/packages/fb/68/84cd3dab6b7b4f3e6fe9459a961acb142aaab846417f6e8905110d7027e5/aiohttp-3.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:019a67772e034a0e6b9b17c13d0a8fe56ad9fb150fc724b7f3ffd3724288d9e5", size = 1560023, upload-time = "2026-03-31T21:56:45.031Z" }, + { url = "https://files.pythonhosted.org/packages/41/2c/db61b64b0249e30f954a65ab4cb4970ced57544b1de2e3c98ee5dc24165f/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f34ecee82858e41dd217734f0c41a532bd066bcaab636ad830f03a30b2a96f2a", size = 1652372, upload-time = "2026-03-31T21:56:47.075Z" }, + { url = "https://files.pythonhosted.org/packages/25/6f/e96988a6c982d047810c772e28c43c64c300c943b0ed5c1c0c4ce1e1027c/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4eac02d9af4813ee289cd63a361576da36dba57f5a1ab36377bc2600db0cbb73", size = 1662031, upload-time = "2026-03-31T21:56:48.835Z" }, + { url = "https://files.pythonhosted.org/packages/b7/26/a56feace81f3d347b4052403a9d03754a0ab23f7940780dada0849a38c92/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4beac52e9fe46d6abf98b0176a88154b742e878fdf209d2248e99fcdf73cd297", size = 1708118, upload-time = "2026-03-31T21:56:50.833Z" }, + { url = "https://files.pythonhosted.org/packages/78/6e/b6173a8ff03d01d5e1a694bc06764b5dad1df2d4ed8f0ceec12bb3277936/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c180f480207a9b2475f2b8d8bd7204e47aec952d084b2a2be58a782ffcf96074", size = 1548667, upload-time = "2026-03-31T21:56:52.81Z" }, + { url = "https://files.pythonhosted.org/packages/16/13/13296ffe2c132d888b3fe2c195c8b9c0c24c89c3fa5cc2c44464dc23b22e/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2837fb92951564d6339cedae4a7231692aa9f73cbc4fb2e04263b96844e03b4e", size = 1724490, upload-time = "2026-03-31T21:56:54.541Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1f1c287f4a79782ef36e5a6e62954c85343bc30470d862d30bd5f26c9fa2/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9010032a0b9710f58012a1e9c222528763d860ba2ee1422c03473eab47703e7", size = 1667109, upload-time = "2026-03-31T21:56:56.21Z" }, + { url = "https://files.pythonhosted.org/packages/ef/42/8461a2aaf60a8f4ea4549a4056be36b904b0eb03d97ca9a8a2604681a500/aiohttp-3.13.5-cp310-cp310-win32.whl", hash = "sha256:7c4b6668b2b2b9027f209ddf647f2a4407784b5d88b8be4efcc72036f365baf9", size = 439478, upload-time = "2026-03-31T21:56:58.292Z" }, + { url = "https://files.pythonhosted.org/packages/e5/71/06956304cb5ee439dfe8d86e1b2e70088bd88ed1ced1f42fb29e5d855f0e/aiohttp-3.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:cd3db5927bf9167d5a6157ddb2f036f6b6b0ad001ac82355d43e97a4bde76d76", size = 462047, upload-time = "2026-03-31T21:57:00.257Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/a20c4ac64aeaef1679e25c9983573618ff765d7aa829fa2b84ae7573169e/aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6", size = 757513, upload-time = "2026-03-31T21:57:02.146Z" }, + { url = "https://files.pythonhosted.org/packages/75/0a/39fa6c6b179b53fcb3e4b3d2b6d6cad0180854eda17060c7218540102bef/aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d", size = 506748, upload-time = "2026-03-31T21:57:04.275Z" }, + { url = "https://files.pythonhosted.org/packages/87/ec/e38ce072e724fd7add6243613f8d1810da084f54175353d25ccf9f9c7e5a/aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c", size = 501673, upload-time = "2026-03-31T21:57:06.208Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ba/3bc7525d7e2beaa11b309a70d48b0d3cfc3c2089ec6a7d0820d59c657053/aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb", size = 1763757, upload-time = "2026-03-31T21:57:07.882Z" }, + { url = "https://files.pythonhosted.org/packages/5e/ab/e87744cf18f1bd78263aba24924d4953b41086bd3a31d22452378e9028a0/aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6", size = 1720152, upload-time = "2026-03-31T21:57:09.946Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f3/ed17a6f2d742af17b50bae2d152315ed1b164b07a5fd5cc1754d99e4dfa5/aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13", size = 1818010, upload-time = "2026-03-31T21:57:12.157Z" }, + { url = "https://files.pythonhosted.org/packages/53/06/ecbc63dc937192e2a5cb46df4d3edb21deb8225535818802f210a6ea5816/aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174", size = 1907251, upload-time = "2026-03-31T21:57:14.023Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a5/0521aa32c1ddf3aa1e71dcc466be0b7db2771907a13f18cddaa45967d97b/aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc", size = 1759969, upload-time = "2026-03-31T21:57:16.146Z" }, + { url = "https://files.pythonhosted.org/packages/f6/78/a38f8c9105199dd3b9706745865a8a59d0041b6be0ca0cc4b2ccf1bab374/aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6", size = 1616871, upload-time = "2026-03-31T21:57:17.856Z" }, + { url = "https://files.pythonhosted.org/packages/6f/41/27392a61ead8ab38072105c71aa44ff891e71653fe53d576a7067da2b4e8/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49", size = 1739844, upload-time = "2026-03-31T21:57:19.679Z" }, + { url = "https://files.pythonhosted.org/packages/6e/55/5564e7ae26d94f3214250009a0b1c65a0c6af4bf88924ccb6fdab901de28/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8", size = 1731969, upload-time = "2026-03-31T21:57:22.006Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c5/705a3929149865fc941bcbdd1047b238e4a72bcb215a9b16b9d7a2e8d992/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d", size = 1795193, upload-time = "2026-03-31T21:57:24.256Z" }, + { url = "https://files.pythonhosted.org/packages/a6/19/edabed62f718d02cff7231ca0db4ef1c72504235bc467f7b67adb1679f48/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c", size = 1606477, upload-time = "2026-03-31T21:57:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/de/fc/76f80ef008675637d88d0b21584596dc27410a990b0918cb1e5776545b5b/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac", size = 1813198, upload-time = "2026-03-31T21:57:28.316Z" }, + { url = "https://files.pythonhosted.org/packages/e5/67/5b3ac26b80adb20ea541c487f73730dc8fa107d632c998f25bbbab98fcda/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3", size = 1752321, upload-time = "2026-03-31T21:57:30.549Z" }, + { url = "https://files.pythonhosted.org/packages/88/06/e4a2e49255ea23fa4feeb5ab092d90240d927c15e47b5b5c48dff5a9ce29/aiohttp-3.13.5-cp311-cp311-win32.whl", hash = "sha256:77dfa48c9f8013271011e51c00f8ada19851f013cde2c48fca1ba5e0caf5bb06", size = 439069, upload-time = "2026-03-31T21:57:32.388Z" }, + { url = "https://files.pythonhosted.org/packages/c0/43/8c7163a596dab4f8be12c190cf467a1e07e4734cf90eebb39f7f5d53fc6a/aiohttp-3.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:d3a4834f221061624b8887090637db9ad4f61752001eae37d56c52fddade2dc8", size = 462859, upload-time = "2026-03-31T21:57:34.455Z" }, + { url = "https://files.pythonhosted.org/packages/be/6f/353954c29e7dcce7cf00280a02c75f30e133c00793c7a2ed3776d7b2f426/aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9", size = 748876, upload-time = "2026-03-31T21:57:36.319Z" }, + { url = "https://files.pythonhosted.org/packages/f5/1b/428a7c64687b3b2e9cd293186695affc0e1e54a445d0361743b231f11066/aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416", size = 499557, upload-time = "2026-03-31T21:57:38.236Z" }, + { url = "https://files.pythonhosted.org/packages/29/47/7be41556bfbb6917069d6a6634bb7dd5e163ba445b783a90d40f5ac7e3a7/aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2", size = 500258, upload-time = "2026-03-31T21:57:39.923Z" }, + { url = "https://files.pythonhosted.org/packages/67/84/c9ecc5828cb0b3695856c07c0a6817a99d51e2473400f705275a2b3d9239/aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4", size = 1749199, upload-time = "2026-03-31T21:57:41.938Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d3/3c6d610e66b495657622edb6ae7c7fd31b2e9086b4ec50b47897ad6042a9/aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9", size = 1721013, upload-time = "2026-03-31T21:57:43.904Z" }, + { url = "https://files.pythonhosted.org/packages/49/a0/24409c12217456df0bae7babe3b014e460b0b38a8e60753d6cb339f6556d/aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5", size = 1781501, upload-time = "2026-03-31T21:57:46.285Z" }, + { url = "https://files.pythonhosted.org/packages/98/9d/b65ec649adc5bccc008b0957a9a9c691070aeac4e41cea18559fef49958b/aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e", size = 1878981, upload-time = "2026-03-31T21:57:48.734Z" }, + { url = "https://files.pythonhosted.org/packages/57/d8/8d44036d7eb7b6a8ec4c5494ea0c8c8b94fbc0ed3991c1a7adf230df03bf/aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1", size = 1767934, upload-time = "2026-03-31T21:57:51.171Z" }, + { url = "https://files.pythonhosted.org/packages/31/04/d3f8211f273356f158e3464e9e45484d3fb8c4ce5eb2f6fe9405c3273983/aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286", size = 1566671, upload-time = "2026-03-31T21:57:53.326Z" }, + { url = "https://files.pythonhosted.org/packages/41/db/073e4ebe00b78e2dfcacff734291651729a62953b48933d765dc513bf798/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9", size = 1705219, upload-time = "2026-03-31T21:57:55.385Z" }, + { url = "https://files.pythonhosted.org/packages/48/45/7dfba71a2f9fd97b15c95c06819de7eb38113d2cdb6319669195a7d64270/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88", size = 1743049, upload-time = "2026-03-31T21:57:57.341Z" }, + { url = "https://files.pythonhosted.org/packages/18/71/901db0061e0f717d226386a7f471bb59b19566f2cae5f0d93874b017271f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3", size = 1749557, upload-time = "2026-03-31T21:57:59.626Z" }, + { url = "https://files.pythonhosted.org/packages/08/d5/41eebd16066e59cd43728fe74bce953d7402f2b4ddfdfef2c0e9f17ca274/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b", size = 1558931, upload-time = "2026-03-31T21:58:01.972Z" }, + { url = "https://files.pythonhosted.org/packages/30/e6/4a799798bf05740e66c3a1161079bda7a3dd8e22ca392481d7a7f9af82a6/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe", size = 1774125, upload-time = "2026-03-31T21:58:04.007Z" }, + { url = "https://files.pythonhosted.org/packages/84/63/7749337c90f92bc2cb18f9560d67aa6258c7060d1397d21529b8004fcf6f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14", size = 1732427, upload-time = "2026-03-31T21:58:06.337Z" }, + { url = "https://files.pythonhosted.org/packages/98/de/cf2f44ff98d307e72fb97d5f5bbae3bfcb442f0ea9790c0bf5c5c2331404/aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3", size = 433534, upload-time = "2026-03-31T21:58:08.712Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ca/eadf6f9c8fa5e31d40993e3db153fb5ed0b11008ad5d9de98a95045bed84/aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1", size = 460446, upload-time = "2026-03-31T21:58:10.945Z" }, + { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930, upload-time = "2026-03-31T21:58:13.155Z" }, + { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927, upload-time = "2026-03-31T21:58:15.073Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141, upload-time = "2026-03-31T21:58:17.009Z" }, + { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476, upload-time = "2026-03-31T21:58:18.925Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507, upload-time = "2026-03-31T21:58:21.094Z" }, + { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465, upload-time = "2026-03-31T21:58:23.159Z" }, + { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523, upload-time = "2026-03-31T21:58:25.59Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113, upload-time = "2026-03-31T21:58:27.624Z" }, + { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351, upload-time = "2026-03-31T21:58:29.918Z" }, + { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205, upload-time = "2026-03-31T21:58:32.214Z" }, + { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618, upload-time = "2026-03-31T21:58:34.728Z" }, + { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185, upload-time = "2026-03-31T21:58:36.909Z" }, + { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311, upload-time = "2026-03-31T21:58:39.38Z" }, + { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147, upload-time = "2026-03-31T21:58:41.476Z" }, + { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356, upload-time = "2026-03-31T21:58:44.049Z" }, + { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637, upload-time = "2026-03-31T21:58:46.167Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896, upload-time = "2026-03-31T21:58:48.119Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721, upload-time = "2026-03-31T21:58:50.229Z" }, + { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663, upload-time = "2026-03-31T21:58:52.232Z" }, + { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094, upload-time = "2026-03-31T21:58:54.566Z" }, + { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701, upload-time = "2026-03-31T21:58:56.864Z" }, + { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360, upload-time = "2026-03-31T21:58:59.072Z" }, + { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023, upload-time = "2026-03-31T21:59:01.776Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795, upload-time = "2026-03-31T21:59:04.568Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405, upload-time = "2026-03-31T21:59:07.221Z" }, + { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082, upload-time = "2026-03-31T21:59:09.484Z" }, + { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346, upload-time = "2026-03-31T21:59:12.068Z" }, + { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891, upload-time = "2026-03-31T21:59:14.552Z" }, + { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113, upload-time = "2026-03-31T21:59:17.068Z" }, + { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088, upload-time = "2026-03-31T21:59:19.541Z" }, + { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976, upload-time = "2026-03-31T21:59:22.311Z" }, + { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444, upload-time = "2026-03-31T21:59:24.635Z" }, + { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128, upload-time = "2026-03-31T21:59:27.291Z" }, + { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029, upload-time = "2026-03-31T21:59:29.429Z" }, + { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758, upload-time = "2026-03-31T21:59:31.547Z" }, + { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883, upload-time = "2026-03-31T21:59:34.098Z" }, + { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668, upload-time = "2026-03-31T21:59:36.497Z" }, + { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461, upload-time = "2026-03-31T21:59:38.723Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661, upload-time = "2026-03-31T21:59:41.187Z" }, + { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800, upload-time = "2026-03-31T21:59:43.84Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382, upload-time = "2026-03-31T21:59:46.187Z" }, + { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724, upload-time = "2026-03-31T21:59:48.656Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027, upload-time = "2026-03-31T21:59:51.284Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644, upload-time = "2026-03-31T21:59:53.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630, upload-time = "2026-03-31T21:59:56.239Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403, upload-time = "2026-03-31T21:59:59.103Z" }, + { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924, upload-time = "2026-03-31T22:00:02.116Z" }, + { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119, upload-time = "2026-03-31T22:00:04.756Z" }, + { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072, upload-time = "2026-03-31T22:00:07.494Z" }, + { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819, upload-time = "2026-03-31T22:00:10.277Z" }, + { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441, upload-time = "2026-03-31T22:00:12.791Z" }, ] [[package]] @@ -894,107 +894,107 @@ wheels = [ [[package]] name = "charset-normalizer" -version = "3.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/60/e3bec1881450851b087e301bedc3daa9377a4d45f1c26aa90b0b235e38aa/charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6", size = 143363, upload-time = "2026-03-15T18:53:25.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/8c/2c56124c6dc53a774d435f985b5973bc592f42d437be58c0c92d65ae7296/charset_normalizer-3.4.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e1d8ca8611099001949d1cdfaefc510cf0f212484fe7c565f735b68c78c3c95", size = 298751, upload-time = "2026-03-15T18:50:00.003Z" }, - { url = "https://files.pythonhosted.org/packages/86/2a/2a7db6b314b966a3bcad8c731c0719c60b931b931de7ae9f34b2839289ee/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e25369dc110d58ddf29b949377a93e0716d72a24f62bad72b2b39f155949c1fd", size = 200027, upload-time = "2026-03-15T18:50:01.702Z" }, - { url = "https://files.pythonhosted.org/packages/68/f2/0fe775c74ae25e2a3b07b01538fc162737b3e3f795bada3bc26f4d4d495c/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:259695e2ccc253feb2a016303543d691825e920917e31f894ca1a687982b1de4", size = 220741, upload-time = "2026-03-15T18:50:03.194Z" }, - { url = "https://files.pythonhosted.org/packages/10/98/8085596e41f00b27dd6aa1e68413d1ddda7e605f34dd546833c61fddd709/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dda86aba335c902b6149a02a55b38e96287157e609200811837678214ba2b1db", size = 215802, upload-time = "2026-03-15T18:50:05.859Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ce/865e4e09b041bad659d682bbd98b47fb490b8e124f9398c9448065f64fee/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51fb3c322c81d20567019778cb5a4a6f2dc1c200b886bc0d636238e364848c89", size = 207908, upload-time = "2026-03-15T18:50:07.676Z" }, - { url = "https://files.pythonhosted.org/packages/a8/54/8c757f1f7349262898c2f169e0d562b39dcb977503f18fdf0814e923db78/charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:4482481cb0572180b6fd976a4d5c72a30263e98564da68b86ec91f0fe35e8565", size = 194357, upload-time = "2026-03-15T18:50:09.327Z" }, - { url = "https://files.pythonhosted.org/packages/6f/29/e88f2fac9218907fc7a70722b393d1bbe8334c61fe9c46640dba349b6e66/charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39f5068d35621da2881271e5c3205125cc456f54e9030d3f723288c873a71bf9", size = 205610, upload-time = "2026-03-15T18:50:10.732Z" }, - { url = "https://files.pythonhosted.org/packages/4c/c5/21d7bb0cb415287178450171d130bed9d664211fdd59731ed2c34267b07d/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8bea55c4eef25b0b19a0337dc4e3f9a15b00d569c77211fa8cde38684f234fb7", size = 203512, upload-time = "2026-03-15T18:50:12.535Z" }, - { url = "https://files.pythonhosted.org/packages/a4/be/ce52f3c7fdb35cc987ad38a53ebcef52eec498f4fb6c66ecfe62cfe57ba2/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f0cdaecd4c953bfae0b6bb64910aaaca5a424ad9c72d85cb88417bb9814f7550", size = 195398, upload-time = "2026-03-15T18:50:14.236Z" }, - { url = "https://files.pythonhosted.org/packages/81/a0/3ab5dd39d4859a3555e5dadfc8a9fa7f8352f8c183d1a65c90264517da0e/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:150b8ce8e830eb7ccb029ec9ca36022f756986aaaa7956aad6d9ec90089338c0", size = 221772, upload-time = "2026-03-15T18:50:15.581Z" }, - { url = "https://files.pythonhosted.org/packages/04/6e/6a4e41a97ba6b2fa87f849c41e4d229449a586be85053c4d90135fe82d26/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e68c14b04827dd76dcbd1aeea9e604e3e4b78322d8faf2f8132c7138efa340a8", size = 205759, upload-time = "2026-03-15T18:50:17.047Z" }, - { url = "https://files.pythonhosted.org/packages/db/3b/34a712a5ee64a6957bf355b01dc17b12de457638d436fdb05d01e463cd1c/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3778fd7d7cd04ae8f54651f4a7a0bd6e39a0cf20f801720a4c21d80e9b7ad6b0", size = 216938, upload-time = "2026-03-15T18:50:18.44Z" }, - { url = "https://files.pythonhosted.org/packages/cb/05/5bd1e12da9ab18790af05c61aafd01a60f489778179b621ac2a305243c62/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dad6e0f2e481fffdcf776d10ebee25e0ef89f16d691f1e5dee4b586375fdc64b", size = 210138, upload-time = "2026-03-15T18:50:19.852Z" }, - { url = "https://files.pythonhosted.org/packages/bd/8e/3cb9e2d998ff6b21c0a1860343cb7b83eba9cdb66b91410e18fc4969d6ab/charset_normalizer-3.4.6-cp310-cp310-win32.whl", hash = "sha256:74a2e659c7ecbc73562e2a15e05039f1e22c75b7c7618b4b574a3ea9118d1557", size = 144137, upload-time = "2026-03-15T18:50:21.505Z" }, - { url = "https://files.pythonhosted.org/packages/d8/8f/78f5489ffadb0db3eb7aff53d31c24531d33eb545f0c6f6567c25f49a5ff/charset_normalizer-3.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:aa9cccf4a44b9b62d8ba8b4dd06c649ba683e4bf04eea606d2e94cfc2d6ff4d6", size = 154244, upload-time = "2026-03-15T18:50:22.81Z" }, - { url = "https://files.pythonhosted.org/packages/e4/74/e472659dffb0cadb2f411282d2d76c60da1fc94076d7fffed4ae8a93ec01/charset_normalizer-3.4.6-cp310-cp310-win_arm64.whl", hash = "sha256:e985a16ff513596f217cee86c21371b8cd011c0f6f056d0920aa2d926c544058", size = 143312, upload-time = "2026-03-15T18:50:24.074Z" }, - { url = "https://files.pythonhosted.org/packages/62/28/ff6f234e628a2de61c458be2779cb182bc03f6eec12200d4a525bbfc9741/charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e", size = 293582, upload-time = "2026-03-15T18:50:25.454Z" }, - { url = "https://files.pythonhosted.org/packages/1c/b7/b1a117e5385cbdb3205f6055403c2a2a220c5ea80b8716c324eaf75c5c95/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9", size = 197240, upload-time = "2026-03-15T18:50:27.196Z" }, - { url = "https://files.pythonhosted.org/packages/a1/5f/2574f0f09f3c3bc1b2f992e20bce6546cb1f17e111c5be07308dc5427956/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d", size = 217363, upload-time = "2026-03-15T18:50:28.601Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d1/0ae20ad77bc949ddd39b51bf383b6ca932f2916074c95cad34ae465ab71f/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de", size = 212994, upload-time = "2026-03-15T18:50:30.102Z" }, - { url = "https://files.pythonhosted.org/packages/60/ac/3233d262a310c1b12633536a07cde5ddd16985e6e7e238e9f3f9423d8eb9/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73", size = 204697, upload-time = "2026-03-15T18:50:31.654Z" }, - { url = "https://files.pythonhosted.org/packages/25/3c/8a18fc411f085b82303cfb7154eed5bd49c77035eb7608d049468b53f87c/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c", size = 191673, upload-time = "2026-03-15T18:50:33.433Z" }, - { url = "https://files.pythonhosted.org/packages/ff/a7/11cfe61d6c5c5c7438d6ba40919d0306ed83c9ab957f3d4da2277ff67836/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc", size = 201120, upload-time = "2026-03-15T18:50:35.105Z" }, - { url = "https://files.pythonhosted.org/packages/b5/10/cf491fa1abd47c02f69687046b896c950b92b6cd7337a27e6548adbec8e4/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f", size = 200911, upload-time = "2026-03-15T18:50:36.819Z" }, - { url = "https://files.pythonhosted.org/packages/28/70/039796160b48b18ed466fde0af84c1b090c4e288fae26cd674ad04a2d703/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef", size = 192516, upload-time = "2026-03-15T18:50:38.228Z" }, - { url = "https://files.pythonhosted.org/packages/ff/34/c56f3223393d6ff3124b9e78f7de738047c2d6bc40a4f16ac0c9d7a1cb3c/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398", size = 218795, upload-time = "2026-03-15T18:50:39.664Z" }, - { url = "https://files.pythonhosted.org/packages/e8/3b/ce2d4f86c5282191a041fdc5a4ce18f1c6bd40a5bd1f74cf8625f08d51c1/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e", size = 201833, upload-time = "2026-03-15T18:50:41.552Z" }, - { url = "https://files.pythonhosted.org/packages/3b/9b/b6a9f76b0fd7c5b5ec58b228ff7e85095370282150f0bd50b3126f5506d6/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed", size = 213920, upload-time = "2026-03-15T18:50:43.33Z" }, - { url = "https://files.pythonhosted.org/packages/ae/98/7bc23513a33d8172365ed30ee3a3b3fe1ece14a395e5fc94129541fc6003/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021", size = 206951, upload-time = "2026-03-15T18:50:44.789Z" }, - { url = "https://files.pythonhosted.org/packages/32/73/c0b86f3d1458468e11aec870e6b3feac931facbe105a894b552b0e518e79/charset_normalizer-3.4.6-cp311-cp311-win32.whl", hash = "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e", size = 143703, upload-time = "2026-03-15T18:50:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/c6/e3/76f2facfe8eddee0bbd38d2594e709033338eae44ebf1738bcefe0a06185/charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4", size = 153857, upload-time = "2026-03-15T18:50:47.563Z" }, - { url = "https://files.pythonhosted.org/packages/e2/dc/9abe19c9b27e6cd3636036b9d1b387b78c40dedbf0b47f9366737684b4b0/charset_normalizer-3.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316", size = 142751, upload-time = "2026-03-15T18:50:49.234Z" }, - { url = "https://files.pythonhosted.org/packages/e5/62/c0815c992c9545347aeea7859b50dc9044d147e2e7278329c6e02ac9a616/charset_normalizer-3.4.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab", size = 295154, upload-time = "2026-03-15T18:50:50.88Z" }, - { url = "https://files.pythonhosted.org/packages/a8/37/bdca6613c2e3c58c7421891d80cc3efa1d32e882f7c4a7ee6039c3fc951a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21", size = 199191, upload-time = "2026-03-15T18:50:52.658Z" }, - { url = "https://files.pythonhosted.org/packages/6c/92/9934d1bbd69f7f398b38c5dae1cbf9cc672e7c34a4adf7b17c0a9c17d15d/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2", size = 218674, upload-time = "2026-03-15T18:50:54.102Z" }, - { url = "https://files.pythonhosted.org/packages/af/90/25f6ab406659286be929fd89ab0e78e38aa183fc374e03aa3c12d730af8a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff", size = 215259, upload-time = "2026-03-15T18:50:55.616Z" }, - { url = "https://files.pythonhosted.org/packages/4e/ef/79a463eb0fff7f96afa04c1d4c51f8fc85426f918db467854bfb6a569ce3/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5", size = 207276, upload-time = "2026-03-15T18:50:57.054Z" }, - { url = "https://files.pythonhosted.org/packages/f7/72/d0426afec4b71dc159fa6b4e68f868cd5a3ecd918fec5813a15d292a7d10/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0", size = 195161, upload-time = "2026-03-15T18:50:58.686Z" }, - { url = "https://files.pythonhosted.org/packages/bf/18/c82b06a68bfcb6ce55e508225d210c7e6a4ea122bfc0748892f3dc4e8e11/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a", size = 203452, upload-time = "2026-03-15T18:51:00.196Z" }, - { url = "https://files.pythonhosted.org/packages/44/d6/0c25979b92f8adafdbb946160348d8d44aa60ce99afdc27df524379875cb/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2", size = 202272, upload-time = "2026-03-15T18:51:01.703Z" }, - { url = "https://files.pythonhosted.org/packages/2e/3d/7fea3e8fe84136bebbac715dd1221cc25c173c57a699c030ab9b8900cbb7/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5", size = 195622, upload-time = "2026-03-15T18:51:03.526Z" }, - { url = "https://files.pythonhosted.org/packages/57/8a/d6f7fd5cb96c58ef2f681424fbca01264461336d2a7fc875e4446b1f1346/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6", size = 220056, upload-time = "2026-03-15T18:51:05.269Z" }, - { url = "https://files.pythonhosted.org/packages/16/50/478cdda782c8c9c3fb5da3cc72dd7f331f031e7f1363a893cdd6ca0f8de0/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d", size = 203751, upload-time = "2026-03-15T18:51:06.858Z" }, - { url = "https://files.pythonhosted.org/packages/75/fc/cc2fcac943939c8e4d8791abfa139f685e5150cae9f94b60f12520feaa9b/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2", size = 216563, upload-time = "2026-03-15T18:51:08.564Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b7/a4add1d9a5f68f3d037261aecca83abdb0ab15960a3591d340e829b37298/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923", size = 209265, upload-time = "2026-03-15T18:51:10.312Z" }, - { url = "https://files.pythonhosted.org/packages/6c/18/c094561b5d64a24277707698e54b7f67bd17a4f857bbfbb1072bba07c8bf/charset_normalizer-3.4.6-cp312-cp312-win32.whl", hash = "sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4", size = 144229, upload-time = "2026-03-15T18:51:11.694Z" }, - { url = "https://files.pythonhosted.org/packages/ab/20/0567efb3a8fd481b8f34f739ebddc098ed062a59fed41a8d193a61939e8f/charset_normalizer-3.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb", size = 154277, upload-time = "2026-03-15T18:51:13.004Z" }, - { url = "https://files.pythonhosted.org/packages/15/57/28d79b44b51933119e21f65479d0864a8d5893e494cf5daab15df0247c17/charset_normalizer-3.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4", size = 142817, upload-time = "2026-03-15T18:51:14.408Z" }, - { url = "https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f", size = 294823, upload-time = "2026-03-15T18:51:15.755Z" }, - { url = "https://files.pythonhosted.org/packages/47/7b/20e809b89c69d37be748d98e84dce6820bf663cf19cf6b942c951a3e8f41/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843", size = 198527, upload-time = "2026-03-15T18:51:17.177Z" }, - { url = "https://files.pythonhosted.org/packages/37/a6/4f8d27527d59c039dce6f7622593cdcd3d70a8504d87d09eb11e9fdc6062/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf", size = 218388, upload-time = "2026-03-15T18:51:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/f6/9b/4770ccb3e491a9bacf1c46cc8b812214fe367c86a96353ccc6daf87b01ec/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8", size = 214563, upload-time = "2026-03-15T18:51:20.374Z" }, - { url = "https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9", size = 206587, upload-time = "2026-03-15T18:51:21.807Z" }, - { url = "https://files.pythonhosted.org/packages/7e/70/3def227f1ec56f5c69dfc8392b8bd63b11a18ca8178d9211d7cc5e5e4f27/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88", size = 194724, upload-time = "2026-03-15T18:51:23.508Z" }, - { url = "https://files.pythonhosted.org/packages/58/ab/9318352e220c05efd31c2779a23b50969dc94b985a2efa643ed9077bfca5/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84", size = 202956, upload-time = "2026-03-15T18:51:25.239Z" }, - { url = "https://files.pythonhosted.org/packages/75/13/f3550a3ac25b70f87ac98c40d3199a8503676c2f1620efbf8d42095cfc40/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd", size = 201923, upload-time = "2026-03-15T18:51:26.682Z" }, - { url = "https://files.pythonhosted.org/packages/1b/db/c5c643b912740b45e8eec21de1bbab8e7fc085944d37e1e709d3dcd9d72f/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c", size = 195366, upload-time = "2026-03-15T18:51:28.129Z" }, - { url = "https://files.pythonhosted.org/packages/5a/67/3b1c62744f9b2448443e0eb160d8b001c849ec3fef591e012eda6484787c/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194", size = 219752, upload-time = "2026-03-15T18:51:29.556Z" }, - { url = "https://files.pythonhosted.org/packages/f6/98/32ffbaf7f0366ffb0445930b87d103f6b406bc2c271563644bde8a2b1093/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc", size = 203296, upload-time = "2026-03-15T18:51:30.921Z" }, - { url = "https://files.pythonhosted.org/packages/41/12/5d308c1bbe60cabb0c5ef511574a647067e2a1f631bc8634fcafaccd8293/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f", size = 215956, upload-time = "2026-03-15T18:51:32.399Z" }, - { url = "https://files.pythonhosted.org/packages/53/e9/5f85f6c5e20669dbe56b165c67b0260547dea97dba7e187938833d791687/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2", size = 208652, upload-time = "2026-03-15T18:51:34.214Z" }, - { url = "https://files.pythonhosted.org/packages/f1/11/897052ea6af56df3eef3ca94edafee410ca699ca0c7b87960ad19932c55e/charset_normalizer-3.4.6-cp313-cp313-win32.whl", hash = "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d", size = 143940, upload-time = "2026-03-15T18:51:36.15Z" }, - { url = "https://files.pythonhosted.org/packages/a1/5c/724b6b363603e419829f561c854b87ed7c7e31231a7908708ac086cdf3e2/charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389", size = 154101, upload-time = "2026-03-15T18:51:37.876Z" }, - { url = "https://files.pythonhosted.org/packages/01/a5/7abf15b4c0968e47020f9ca0935fb3274deb87cb288cd187cad92e8cdffd/charset_normalizer-3.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f", size = 143109, upload-time = "2026-03-15T18:51:39.565Z" }, - { url = "https://files.pythonhosted.org/packages/25/6f/ffe1e1259f384594063ea1869bfb6be5cdb8bc81020fc36c3636bc8302a1/charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8", size = 294458, upload-time = "2026-03-15T18:51:41.134Z" }, - { url = "https://files.pythonhosted.org/packages/56/60/09bb6c13a8c1016c2ed5c6a6488e4ffef506461aa5161662bd7636936fb1/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421", size = 199277, upload-time = "2026-03-15T18:51:42.953Z" }, - { url = "https://files.pythonhosted.org/packages/00/50/dcfbb72a5138bbefdc3332e8d81a23494bf67998b4b100703fd15fa52d81/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2", size = 218758, upload-time = "2026-03-15T18:51:44.339Z" }, - { url = "https://files.pythonhosted.org/packages/03/b3/d79a9a191bb75f5aa81f3aaaa387ef29ce7cb7a9e5074ba8ea095cc073c2/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30", size = 215299, upload-time = "2026-03-15T18:51:45.871Z" }, - { url = "https://files.pythonhosted.org/packages/76/7e/bc8911719f7084f72fd545f647601ea3532363927f807d296a8c88a62c0d/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db", size = 206811, upload-time = "2026-03-15T18:51:47.308Z" }, - { url = "https://files.pythonhosted.org/packages/e2/40/c430b969d41dda0c465aa36cc7c2c068afb67177bef50905ac371b28ccc7/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8", size = 193706, upload-time = "2026-03-15T18:51:48.849Z" }, - { url = "https://files.pythonhosted.org/packages/48/15/e35e0590af254f7df984de1323640ef375df5761f615b6225ba8deb9799a/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815", size = 202706, upload-time = "2026-03-15T18:51:50.257Z" }, - { url = "https://files.pythonhosted.org/packages/5e/bd/f736f7b9cc5e93a18b794a50346bb16fbfd6b37f99e8f306f7951d27c17c/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a", size = 202497, upload-time = "2026-03-15T18:51:52.012Z" }, - { url = "https://files.pythonhosted.org/packages/9d/ba/2cc9e3e7dfdf7760a6ed8da7446d22536f3d0ce114ac63dee2a5a3599e62/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43", size = 193511, upload-time = "2026-03-15T18:51:53.723Z" }, - { url = "https://files.pythonhosted.org/packages/9e/cb/5be49b5f776e5613be07298c80e1b02a2d900f7a7de807230595c85a8b2e/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0", size = 220133, upload-time = "2026-03-15T18:51:55.333Z" }, - { url = "https://files.pythonhosted.org/packages/83/43/99f1b5dad345accb322c80c7821071554f791a95ee50c1c90041c157ae99/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1", size = 203035, upload-time = "2026-03-15T18:51:56.736Z" }, - { url = "https://files.pythonhosted.org/packages/87/9a/62c2cb6a531483b55dddff1a68b3d891a8b498f3ca555fbcf2978e804d9d/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f", size = 216321, upload-time = "2026-03-15T18:51:58.17Z" }, - { url = "https://files.pythonhosted.org/packages/6e/79/94a010ff81e3aec7c293eb82c28f930918e517bc144c9906a060844462eb/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815", size = 208973, upload-time = "2026-03-15T18:51:59.998Z" }, - { url = "https://files.pythonhosted.org/packages/2a/57/4ecff6d4ec8585342f0c71bc03efaa99cb7468f7c91a57b105bcd561cea8/charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d", size = 144610, upload-time = "2026-03-15T18:52:02.213Z" }, - { url = "https://files.pythonhosted.org/packages/80/94/8434a02d9d7f168c25767c64671fead8d599744a05d6a6c877144c754246/charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f", size = 154962, upload-time = "2026-03-15T18:52:03.658Z" }, - { url = "https://files.pythonhosted.org/packages/46/4c/48f2cdbfd923026503dfd67ccea45c94fd8fe988d9056b468579c66ed62b/charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e", size = 143595, upload-time = "2026-03-15T18:52:05.123Z" }, - { url = "https://files.pythonhosted.org/packages/31/93/8878be7569f87b14f1d52032946131bcb6ebbd8af3e20446bc04053dc3f1/charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866", size = 314828, upload-time = "2026-03-15T18:52:06.831Z" }, - { url = "https://files.pythonhosted.org/packages/06/b6/fae511ca98aac69ecc35cde828b0a3d146325dd03d99655ad38fc2cc3293/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc", size = 208138, upload-time = "2026-03-15T18:52:08.239Z" }, - { url = "https://files.pythonhosted.org/packages/54/57/64caf6e1bf07274a1e0b7c160a55ee9e8c9ec32c46846ce59b9c333f7008/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e", size = 224679, upload-time = "2026-03-15T18:52:10.043Z" }, - { url = "https://files.pythonhosted.org/packages/aa/cb/9ff5a25b9273ef160861b41f6937f86fae18b0792fe0a8e75e06acb08f1d/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077", size = 223475, upload-time = "2026-03-15T18:52:11.854Z" }, - { url = "https://files.pythonhosted.org/packages/fc/97/440635fc093b8d7347502a377031f9605a1039c958f3cd18dcacffb37743/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f", size = 215230, upload-time = "2026-03-15T18:52:13.325Z" }, - { url = "https://files.pythonhosted.org/packages/cd/24/afff630feb571a13f07c8539fbb502d2ab494019492aaffc78ef41f1d1d0/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e", size = 199045, upload-time = "2026-03-15T18:52:14.752Z" }, - { url = "https://files.pythonhosted.org/packages/e5/17/d1399ecdaf7e0498c327433e7eefdd862b41236a7e484355b8e0e5ebd64b/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484", size = 211658, upload-time = "2026-03-15T18:52:16.278Z" }, - { url = "https://files.pythonhosted.org/packages/b5/38/16baa0affb957b3d880e5ac2144caf3f9d7de7bc4a91842e447fbb5e8b67/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7", size = 210769, upload-time = "2026-03-15T18:52:17.782Z" }, - { url = "https://files.pythonhosted.org/packages/05/34/c531bc6ac4c21da9ddfddb3107be2287188b3ea4b53b70fc58f2a77ac8d8/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff", size = 201328, upload-time = "2026-03-15T18:52:19.553Z" }, - { url = "https://files.pythonhosted.org/packages/fa/73/a5a1e9ca5f234519c1953608a03fe109c306b97fdfb25f09182babad51a7/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e", size = 225302, upload-time = "2026-03-15T18:52:21.043Z" }, - { url = "https://files.pythonhosted.org/packages/ba/f6/cd782923d112d296294dea4bcc7af5a7ae0f86ab79f8fefbda5526b6cfc0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659", size = 211127, upload-time = "2026-03-15T18:52:22.491Z" }, - { url = "https://files.pythonhosted.org/packages/0e/c5/0b6898950627af7d6103a449b22320372c24c6feda91aa24e201a478d161/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602", size = 222840, upload-time = "2026-03-15T18:52:24.113Z" }, - { url = "https://files.pythonhosted.org/packages/7d/25/c4bba773bef442cbdc06111d40daa3de5050a676fa26e85090fc54dd12f0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407", size = 216890, upload-time = "2026-03-15T18:52:25.541Z" }, - { url = "https://files.pythonhosted.org/packages/35/1a/05dacadb0978da72ee287b0143097db12f2e7e8d3ffc4647da07a383b0b7/charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579", size = 155379, upload-time = "2026-03-15T18:52:27.05Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7a/d269d834cb3a76291651256f3b9a5945e81d0a49ab9f4a498964e83c0416/charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4", size = 169043, upload-time = "2026-03-15T18:52:28.502Z" }, - { url = "https://files.pythonhosted.org/packages/23/06/28b29fba521a37a8932c6a84192175c34d49f84a6d4773fa63d05f9aff22/charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c", size = 148523, upload-time = "2026-03-15T18:52:29.956Z" }, - { url = "https://files.pythonhosted.org/packages/2a/68/687187c7e26cb24ccbd88e5069f5ef00eba804d36dde11d99aad0838ab45/charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", size = 61455, upload-time = "2026-03-15T18:53:23.833Z" }, +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/08/0f303cb0b529e456bb116f2d50565a482694fbb94340bf56d44677e7ed03/charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d", size = 315182, upload-time = "2026-04-02T09:25:40.673Z" }, + { url = "https://files.pythonhosted.org/packages/24/47/b192933e94b546f1b1fe4df9cc1f84fcdbf2359f8d1081d46dd029b50207/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8", size = 209329, upload-time = "2026-04-02T09:25:42.354Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b4/01fa81c5ca6141024d89a8fc15968002b71da7f825dd14113207113fabbd/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790", size = 231230, upload-time = "2026-04-02T09:25:44.281Z" }, + { url = "https://files.pythonhosted.org/packages/20/f7/7b991776844dfa058017e600e6e55ff01984a063290ca5622c0b63162f68/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc", size = 225890, upload-time = "2026-04-02T09:25:45.475Z" }, + { url = "https://files.pythonhosted.org/packages/20/e7/bed0024a0f4ab0c8a9c64d4445f39b30c99bd1acd228291959e3de664247/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393", size = 216930, upload-time = "2026-04-02T09:25:46.58Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ab/b18f0ab31cdd7b3ddb8bb76c4a414aeb8160c9810fdf1bc62f269a539d87/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153", size = 202109, upload-time = "2026-04-02T09:25:48.031Z" }, + { url = "https://files.pythonhosted.org/packages/82/e5/7e9440768a06dfb3075936490cb82dbf0ee20a133bf0dd8551fa096914ec/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af", size = 214684, upload-time = "2026-04-02T09:25:49.245Z" }, + { url = "https://files.pythonhosted.org/packages/71/94/8c61d8da9f062fdf457c80acfa25060ec22bf1d34bbeaca4350f13bcfd07/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34", size = 212785, upload-time = "2026-04-02T09:25:50.671Z" }, + { url = "https://files.pythonhosted.org/packages/66/cd/6e9889c648e72c0ab2e5967528bb83508f354d706637bc7097190c874e13/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1", size = 203055, upload-time = "2026-04-02T09:25:51.802Z" }, + { url = "https://files.pythonhosted.org/packages/92/2e/7a951d6a08aefb7eb8e1b54cdfb580b1365afdd9dd484dc4bee9e5d8f258/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752", size = 232502, upload-time = "2026-04-02T09:25:53.388Z" }, + { url = "https://files.pythonhosted.org/packages/58/d5/abcf2d83bf8e0a1286df55cd0dc1d49af0da4282aa77e986df343e7de124/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53", size = 214295, upload-time = "2026-04-02T09:25:54.765Z" }, + { url = "https://files.pythonhosted.org/packages/47/3a/7d4cd7ed54be99973a0dc176032cba5cb1f258082c31fa6df35cff46acfc/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616", size = 227145, upload-time = "2026-04-02T09:25:55.904Z" }, + { url = "https://files.pythonhosted.org/packages/1d/98/3a45bf8247889cf28262ebd3d0872edff11565b2a1e3064ccb132db3fbb0/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a", size = 218884, upload-time = "2026-04-02T09:25:57.074Z" }, + { url = "https://files.pythonhosted.org/packages/ad/80/2e8b7f8915ed5c9ef13aa828d82738e33888c485b65ebf744d615040c7ea/charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374", size = 148343, upload-time = "2026-04-02T09:25:58.199Z" }, + { url = "https://files.pythonhosted.org/packages/35/1b/3b8c8c77184af465ee9ad88b5aea46ea6b2e1f7b9dc9502891e37af21e30/charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943", size = 159174, upload-time = "2026-04-02T09:25:59.322Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/feb40dca40dbb21e0a908801782d9288c64fc8d8e562c2098e9994c8c21b/charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008", size = 147805, upload-time = "2026-04-02T09:26:00.756Z" }, + { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" }, + { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" }, + { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" }, + { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" }, + { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" }, + { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" }, + { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" }, + { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" }, + { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" }, + { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" }, + { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" }, + { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" }, + { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" }, + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, ] [[package]] @@ -1011,7 +1011,7 @@ wheels = [ [[package]] name = "click-extra" -version = "7.9.0" +version = "7.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "boltons" }, @@ -1024,9 +1024,9 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "wcmatch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4f/12/6556edabd00a314de19573b4fee7a1441b2bc1258c143c5bab2fe556057a/click_extra-7.9.0.tar.gz", hash = "sha256:272e0063b94642d9ddb98e968c4b18bbcc692adeb45f5ef02825792aa3a2e04e", size = 113436, upload-time = "2026-03-31T14:47:24.54Z" } +sdist = { url = "https://files.pythonhosted.org/packages/44/b1/ea97a8300989ecc54021f2fe7480b6b8f12ec5261d1cd44aeb5854f8c184/click_extra-7.10.0.tar.gz", hash = "sha256:f2e7725460983adf47fa6b491459434abd3305ac5715c727fef67529f13c49fd", size = 116556, upload-time = "2026-04-02T14:27:04.464Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/1b/73473d4f99793a846a1d7a71cd799d462a97aac455b96a6ae43010856a23/click_extra-7.9.0-py3-none-any.whl", hash = "sha256:a2ddc2a9017bd132f13e003b4d85381b280f93e65d22c2cb5962af7bbbcc164c", size = 127329, upload-time = "2026-03-31T14:47:23.068Z" }, + { url = "https://files.pythonhosted.org/packages/a1/8b/645b58f13a06cae46ba6ce07278a5b188fc0589b6c4f00d38848f60f509a/click_extra-7.10.0-py3-none-any.whl", hash = "sha256:c132db0c3317af1af3a0cd0856e2b51f9f5969025f17a191f0023759854ec578", size = 130434, upload-time = "2026-04-02T14:27:05.787Z" }, ] [package.optional-dependencies] @@ -1587,7 +1587,7 @@ wheels = [ [[package]] name = "fastapi" -version = "0.135.2" +version = "0.135.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -1596,9 +1596,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/73/5903c4b13beae98618d64eb9870c3fac4f605523dd0312ca5c80dadbd5b9/fastapi-0.135.2.tar.gz", hash = "sha256:88a832095359755527b7f63bb4c6bc9edb8329a026189eed83d6c1afcf419d56", size = 395833, upload-time = "2026-03-23T14:12:41.697Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/e6/7adb4c5fa231e82c35b8f5741a9f2d055f520c29af5546fd70d3e8e1cd2e/fastapi-0.135.3.tar.gz", hash = "sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654", size = 396524, upload-time = "2026-04-01T16:23:58.188Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/ea/18f6d0457f9efb2fc6fa594857f92810cadb03024975726db6546b3d6fcf/fastapi-0.135.2-py3-none-any.whl", hash = "sha256:0af0447d541867e8db2a6a25c23a8c4bd80e2394ac5529bd87501bbb9e240ca5", size = 117407, upload-time = "2026-03-23T14:12:43.284Z" }, + { url = "https://files.pythonhosted.org/packages/84/a4/5caa2de7f917a04ada20018eccf60d6cc6145b0199d55ca3711b0fc08312/fastapi-0.135.3-py3-none-any.whl", hash = "sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98", size = 117734, upload-time = "2026-04-01T16:23:59.328Z" }, ] [[package]] @@ -1993,7 +1993,7 @@ wheels = [ [[package]] name = "google-cloud-aiplatform" -version = "1.143.0" +version = "1.145.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docstring-parser" }, @@ -2009,9 +2009,9 @@ dependencies = [ { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a7/08/939fb05870fdf155410a927e22f5b053d49f18e215618e102fba1d8bb147/google_cloud_aiplatform-1.143.0.tar.gz", hash = "sha256:1f0124a89795a6b473deb28724dd37d95334205df3a9c9c48d0b8d7a3d5d5cc4", size = 10215389, upload-time = "2026-03-25T18:30:15.444Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/e5/6442d9d2c019456638825d4665b1e87ec4eaf1d182950ba426d0f0210eab/google_cloud_aiplatform-1.145.0.tar.gz", hash = "sha256:7894c4f3d2684bdb60e9a122004c01678e3b585174a27298ae7a3ed1e5eaf3bd", size = 10222904, upload-time = "2026-04-02T14:06:58.322Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/90/14/16323e604e79dc63b528268f97a841c2c29dd8eb16395de6bf530c1a5ebe/google_cloud_aiplatform-1.143.0-py2.py3-none-any.whl", hash = "sha256:78df97d044859f743a9cc48b89a260d33579b0d548b1589bb3ae9f4c2afc0c5a", size = 8392705, upload-time = "2026-03-25T18:30:11.496Z" }, + { url = "https://files.pythonhosted.org/packages/3d/c6/23e98d3407d5e2416a3dfaecb0a053da899848c50db69e5f2b61a555ce06/google_cloud_aiplatform-1.145.0-py2.py3-none-any.whl", hash = "sha256:4d1c31797a8bd8f3342ed5f186dd30d1f6bca73ddbee2bde452777100d2ddc11", size = 8396640, upload-time = "2026-04-02T14:06:54.125Z" }, ] [package.optional-dependencies] @@ -2301,7 +2301,7 @@ wheels = [ [[package]] name = "google-cloud-spanner" -version = "3.63.0" +version = "3.64.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -2318,9 +2318,9 @@ dependencies = [ { name = "protobuf" }, { name = "sqlparse" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/82/ee/9ae0794d32ec271b2b2326f17d977d29801e5b960e7a0f03d721aeffe824/google_cloud_spanner-3.63.0.tar.gz", hash = "sha256:e2a4fb3bdbad4688645f455d498705d3f935b7c9011f5c94c137b77569b47a62", size = 729522, upload-time = "2026-02-13T07:35:13.593Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/67/573b14674bd74c8f0630125e13fd52791c76e6a34f21862358913fa41742/google_cloud_spanner-3.64.0.tar.gz", hash = "sha256:02c26601eaaef6abba78efe5c55187b16550aeab0671ed0a65ab2d78bf7c019e", size = 884721, upload-time = "2026-04-01T16:14:38.479Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/72/e16c4fe5a7058c5526461ade670a4bec0922bc02c2690df27300e9955925/google_cloud_spanner-3.63.0-py3-none-any.whl", hash = "sha256:6ffae0ed589bbbd2d8831495e266198f3d069005cfe65c664448c9a727c88e7b", size = 518799, upload-time = "2026-02-13T07:35:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/23/93/0ae1f0edfb9d9a0fc85d234b085b1cd7a3c5444f5bb85f1315f76c654313/google_cloud_spanner-3.64.0-py3-none-any.whl", hash = "sha256:9dd8b268c511def6bef118f9d8d9cbea98509727d13388a8365d5b72e13acf7c", size = 607319, upload-time = "2026-04-01T16:14:36.224Z" }, ] [[package]] @@ -2409,7 +2409,7 @@ wheels = [ [[package]] name = "google-genai" -version = "1.69.0" +version = "1.70.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2423,9 +2423,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/5e/c0a5e6ff60d18d3f19819a9b1fbd6a1ef2162d025696d8660550739168dc/google_genai-1.69.0.tar.gz", hash = "sha256:5f1a6a478e0c5851506a3d337534bab27b3c33120e27bf9174507ea79dfb8673", size = 519538, upload-time = "2026-03-28T15:33:27.308Z" } +sdist = { url = "https://files.pythonhosted.org/packages/74/dd/28e4682904b183acbfad3fe6409f13a42f69bb8eab6e882d3bcbea1dde01/google_genai-1.70.0.tar.gz", hash = "sha256:36b67b0fc6f319e08d1f1efd808b790107b1809c8743a05d55dfcf9d9fad7719", size = 519550, upload-time = "2026-04-01T10:52:46.487Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/58/ef0586019f54b2ebb36deed7608ccb5efe1377564d2aaea6b1e295d1fadc/google_genai-1.69.0-py3-none-any.whl", hash = "sha256:252e714d724aba74949647b9de511a6a6f7804b3b317ab39ddee9cc2f001cacc", size = 760551, upload-time = "2026-03-28T15:33:24.957Z" }, + { url = "https://files.pythonhosted.org/packages/36/a3/d4564c8a9beaf6a3cef8d70fa6354318572cebfee65db4f01af0d41f45ba/google_genai-1.70.0-py3-none-any.whl", hash = "sha256:b74c24549d8b4208f4c736fd11857374788e1ffffc725de45d706e35c97fceee", size = 760584, upload-time = "2026-04-01T10:52:44.349Z" }, ] [[package]] @@ -2528,16 +2528,16 @@ wheels = [ [[package]] name = "grpc-google-iam-v1" -version = "0.14.3" +version = "0.14.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos", extra = ["grpc"] }, { name = "grpcio" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/1e/1011451679a983f2f5c6771a1682542ecb027776762ad031fd0d7129164b/grpc_google_iam_v1-0.14.3.tar.gz", hash = "sha256:879ac4ef33136c5491a6300e27575a9ec760f6cdf9a2518798c1b8977a5dc389", size = 23745, upload-time = "2025-10-15T21:14:53.318Z" } +sdist = { url = "https://files.pythonhosted.org/packages/44/4f/d098419ad0bfc06c9ce440575f05aa22d8973b6c276e86ac7890093d3c37/grpc_google_iam_v1-0.14.4.tar.gz", hash = "sha256:392b3796947ed6334e61171d9ab06bf7eb357f554e5fc7556ad7aab6d0e17038", size = 23706, upload-time = "2026-04-01T01:57:49.813Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/bd/330a1bbdb1afe0b96311249e699b6dc9cfc17916394fd4503ac5aca2514b/grpc_google_iam_v1-0.14.3-py3-none-any.whl", hash = "sha256:7a7f697e017a067206a3dfef44e4c634a34d3dee135fe7d7a4613fe3e59217e6", size = 32690, upload-time = "2025-10-15T21:14:51.72Z" }, + { url = "https://files.pythonhosted.org/packages/89/22/c2dd50c09bf679bd38173656cd4402d2511e563b33bc88f90009cf50613c/grpc_google_iam_v1-0.14.4-py3-none-any.whl", hash = "sha256:412facc320fcbd94034b4df3d557662051d4d8adfa86e0ddb4dca70a3f739964", size = 32675, upload-time = "2026-04-01T01:57:47.69Z" }, ] [[package]] @@ -3334,7 +3334,7 @@ wheels = [ [[package]] name = "mcp" -version = "1.26.0" +version = "1.27.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -3352,9 +3352,9 @@ dependencies = [ { name = "typing-inspection" }, { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/eb/c0cfc62075dc6e1ec1c64d352ae09ac051d9334311ed226f1f425312848a/mcp-1.27.0.tar.gz", hash = "sha256:d3dc35a7eec0d458c1da4976a48f982097ddaab87e278c5511d5a4a56e852b83", size = 607509, upload-time = "2026-04-02T14:48:08.88Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, + { url = "https://files.pythonhosted.org/packages/9c/46/f6b4ad632c67ef35209a66127e4bddc95759649dd595f71f13fba11bdf9a/mcp-1.27.0-py3-none-any.whl", hash = "sha256:5ce1fa81614958e267b21fb2aa34e0aea8e2c6ede60d52aba45fd47246b4d741", size = 215967, upload-time = "2026-04-02T14:48:07.24Z" }, ] [[package]] @@ -3523,11 +3523,11 @@ wheels = [ [[package]] name = "more-itertools" -version = "10.8.0" +version = "11.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/24/e0acc4bf54cba50c1d432c70a72a3df96db4a321b2c4c68432a60759044f/more_itertools-11.0.1.tar.gz", hash = "sha256:fefaf25b7ab08f0b45fa9f1892cae93b9fc0089ef034d39213bce15f1cc9e199", size = 144739, upload-time = "2026-04-02T16:17:45.061Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" }, + { url = "https://files.pythonhosted.org/packages/d8/f4/5e52c7319b8087acef603ed6e50dc325c02eaa999355414830468611f13c/more_itertools-11.0.1-py3-none-any.whl", hash = "sha256:eaf287826069452a8f61026c597eae2428b2d1ba2859083abbf240b46842ce6d", size = 72182, upload-time = "2026-04-02T16:17:43.724Z" }, ] [[package]] @@ -7120,11 +7120,11 @@ wheels = [ [[package]] name = "sqlglot" -version = "30.1.0" +version = "30.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/ae/afee950eff42a9c8ceab4a2e25abfeaa8278c578f967201824287cf530ce/sqlglot-30.1.0.tar.gz", hash = "sha256:7593aea85349c577b269d540ba245024f91464afdcf61c6ef7765f4691c46ef8", size = 5812093, upload-time = "2026-03-26T19:25:45.065Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/5a/b8149963cd479a7e5b71f43d11d678f5e4b8633ab4de5b25e7a5d6eefa20/sqlglot-30.2.1.tar.gz", hash = "sha256:ef4a67cc6f66a8043085eb8ea95fa9541c1625dffa9145ad4e9815a7ba60a199", size = 5820630, upload-time = "2026-04-02T11:47:22.061Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/31/f1cad1972a8eb4b1a9bc904e4a8d440af1eef064160fe10ba0ae81f4693f/sqlglot-30.1.0-py3-none-any.whl", hash = "sha256:6c2d58d0cc68b5f96900058e8866ef4959f89f9e66e4096e0ba746830dda4f40", size = 665823, upload-time = "2026-03-26T19:25:42.794Z" }, + { url = "https://files.pythonhosted.org/packages/69/13/f8c5dd59b119feee28cce53f23361d955cd46d0612697d49db0070f41ea9/sqlglot-30.2.1-py3-none-any.whl", hash = "sha256:f23d9ee9427ef9d20df15f9b0ffa57d9eb45e52b012219a349d1e6b50ed926d1", size = 668564, upload-time = "2026-04-02T11:47:19.34Z" }, ] [package.optional-dependencies] @@ -7134,30 +7134,30 @@ c = [ [[package]] name = "sqlglotc" -version = "30.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b4/53/abd9c353be84baca2272027e274c916786dc99abec3a5cefcd7295670f49/sqlglotc-30.1.0.tar.gz", hash = "sha256:c1e3ca25b0f9f81977862956fbb38dcf7e9f7bf00082ae123ff8d5c93ddec520", size = 433098, upload-time = "2026-03-26T19:24:43.676Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/08/00e1da79529c277e560f665836939543990dd004f2eff152f191eeb6db0e/sqlglotc-30.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e9087ce9ade1d3be91c8025e30b26d3ccc4742795cae0c2736f3de292e684db", size = 17741441, upload-time = "2026-03-26T19:23:57.415Z" }, - { url = "https://files.pythonhosted.org/packages/20/9f/1180591e7d9d15e53c52a13965701dc7aa1b57dcf99ddc950e022dee3357/sqlglotc-30.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2187e5ddbe5f7ddbcd4fc9577fa83edd36266464b7b6736341f28397b5770e14", size = 11800449, upload-time = "2026-03-26T19:23:59.801Z" }, - { url = "https://files.pythonhosted.org/packages/6a/b8/24372b052b8b7fdf1fa63b03e1ea4090289db5edced8fedb09ed507e71c5/sqlglotc-30.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d5f7dcfc8b3761fb3432461fc13983aea1463a8eb6196e3b66e9c3affc45db0", size = 12392798, upload-time = "2026-03-26T19:24:02.215Z" }, - { url = "https://files.pythonhosted.org/packages/61/95/8f6c142e930688e8a3b3147238f34c7217883a9fb7331285caf30b5aa9ac/sqlglotc-30.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:584fcab537f5c845d32dd17ee096df7a5e539a5a65c0bda657767953a7543692", size = 7707373, upload-time = "2026-03-26T19:24:04.49Z" }, - { url = "https://files.pythonhosted.org/packages/2c/47/f4bc1d4a854521b6942e15c7418f087a6c2ddd02ca8f7a663ecbda281193/sqlglotc-30.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aed0e1e5cf1c0cd09bb4d2c89738e2e5da76a07b46e62695e9b4c97843c21044", size = 17666122, upload-time = "2026-03-26T19:24:05.868Z" }, - { url = "https://files.pythonhosted.org/packages/87/28/f1d637e2a02887f0f1e8a5cbaf46e17b096109bbc53598fd44cb69511ad5/sqlglotc-30.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:787bc5860424955aa2c7ff3d1a041faacc85e3b048ede83fb5c470b0fefa68b3", size = 11761274, upload-time = "2026-03-26T19:24:07.881Z" }, - { url = "https://files.pythonhosted.org/packages/d8/d0/3ca532e921f4b0bb45e159fcff0c99ac8a60b2e1440db9dad2afb24c4c78/sqlglotc-30.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b0b0ddfe2033af693ddf2e2f979e37fa7949a2f282b06ade985328470c899fd", size = 12338512, upload-time = "2026-03-26T19:24:09.905Z" }, - { url = "https://files.pythonhosted.org/packages/46/12/5faae564487186af2e0f76d5f07b1d779a23c0236349708a1054031c03ad/sqlglotc-30.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:6a25690f97103687b1ac36576ba677e6ea169c1092bb23670a411d789acecd6a", size = 7720318, upload-time = "2026-03-26T19:24:11.903Z" }, - { url = "https://files.pythonhosted.org/packages/8f/7a/0250322354b18c2e5fba84017d4c3f0c97d27d216786742379775aa9a5d7/sqlglotc-30.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:07fe6a1ae40c42d9b3f00531f93798c2bb3168c01a32d6ed61952207f24b53de", size = 17735285, upload-time = "2026-03-26T19:24:13.573Z" }, - { url = "https://files.pythonhosted.org/packages/1c/1c/271ec81c35a0a966508e8d2c1d81396e8f41eb4c5746905421cfde5fd83a/sqlglotc-30.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dac443012ae7866505d40752005a11a861ad1ef942fdc093abad51d45b86f48b", size = 12407907, upload-time = "2026-03-26T19:24:15.647Z" }, - { url = "https://files.pythonhosted.org/packages/27/6d/6bdb1982b6c8cb35095b39e39320bdedc3888d4df2da18b5b244d86a7ab6/sqlglotc-30.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6919cae44dd21f476444ec851e3bf9579801740401b394e77a1d0e6fc6e22cb", size = 12954625, upload-time = "2026-03-26T19:24:17.452Z" }, - { url = "https://files.pythonhosted.org/packages/4d/0d/036be5ca7c613864241bbe375f0cd6fdb4d58ef2c16343fb14de7a85b518/sqlglotc-30.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:471550fe9725af978129bb37161e48f16bac7659afe2df65b153cd84376d422c", size = 7930443, upload-time = "2026-03-26T19:24:19.074Z" }, - { url = "https://files.pythonhosted.org/packages/7d/be/6befe0a641bfd2698e2552723a4b502b5bad67a80ba87dfa1da4cf968fc9/sqlglotc-30.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:91deaabad97680ab69d02e9878bd82bc3cabbc1709ab2f5fa5fb19a7b29e8521", size = 17671102, upload-time = "2026-03-26T19:24:20.586Z" }, - { url = "https://files.pythonhosted.org/packages/f9/fa/74d938a0f3a3975ab8e2934ea0a958f16c4444bf65f2ac268c74b34b533b/sqlglotc-30.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:75db34b844abdc308015893950828a3c38362009ca6d0dd4a7ab41bc7ab83570", size = 12306463, upload-time = "2026-03-26T19:24:22.739Z" }, - { url = "https://files.pythonhosted.org/packages/7d/59/0fe37835dba5a6eac6193e886c73bfbe20e78a71bae933c7ccf39ffd5c1d/sqlglotc-30.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:040a8540be799688837a8912b05557531952a27747c7bd612e341dff01979118", size = 12871619, upload-time = "2026-03-26T19:24:24.94Z" }, - { url = "https://files.pythonhosted.org/packages/b1/0e/0abdcb2f90bf670431454d041a5ba0d8e656a71be261d1e2fb959c35b96e/sqlglotc-30.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:ca8444caea04c6b817cf8ce039f1c9f429fe358ea81c50e6dd710cf2a9167648", size = 7938367, upload-time = "2026-03-26T19:24:27.421Z" }, - { url = "https://files.pythonhosted.org/packages/c4/52/a7885f87212444372ea0fa4e7db4170169237f0b801df411b41e4d10d563/sqlglotc-30.1.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:83dd3106320f1ce2b94866be0ea32c4bde2528f391ef0247ee80cb5fd7e30cb8", size = 17600245, upload-time = "2026-03-26T19:24:28.879Z" }, - { url = "https://files.pythonhosted.org/packages/25/08/253a66baf42563eb72de33a8d32f08ac41404fda5d3c51eb9d355bf55d23/sqlglotc-30.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:159d1dff253606af9bb15d198cd7f65b7d77924785f0b53c16239a8c261cc318", size = 12297805, upload-time = "2026-03-26T19:24:30.688Z" }, - { url = "https://files.pythonhosted.org/packages/5a/22/e491682a25fb389e528754c0cc1fe09bf94ea4d78538ac49c52de3e266aa/sqlglotc-30.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3182a183fbf3e205cd50d0ed038302e2cd2abd60e6ba0eaf9c909c455e495146", size = 12829341, upload-time = "2026-03-26T19:24:33.264Z" }, - { url = "https://files.pythonhosted.org/packages/48/c9/e25794baa777575f130b35486e814884996e6889581c7e9fd4264f2b6e05/sqlglotc-30.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:1e7a6cbf0b15d8859e9865cf97f2bf9f9a09e53d03f2e0718df560d132f7eacb", size = 8102173, upload-time = "2026-03-26T19:24:34.945Z" }, +version = "30.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/c7/f2ea63cd9a42f9e3ed1d0cd5f36d5a00b3b9f405ab86f3f4979fb5a7b464/sqlglotc-30.2.1.tar.gz", hash = "sha256:2ffe527bc8664b03cc936bae7ebf965f482beb4acee7a815c2ec2d9aea720b4e", size = 455522, upload-time = "2026-04-02T11:46:30.016Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/3d/0777183989451b2839b51f1ad531c4e7ef8e9aa21fdcedfe52e627940b57/sqlglotc-30.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f33c7d1646ff6531cb9b07f0740b2939f3ecaa31efebfbec8adb6b275f1a45f2", size = 18605519, upload-time = "2026-04-02T11:45:39.29Z" }, + { url = "https://files.pythonhosted.org/packages/a2/f6/e994d75fb462bf5b6dbeb53f91624da2993c0f867e7ff8eef27bfcbeda87/sqlglotc-30.2.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9a1fc7b1ff3b51d0d03a391768a79964f68541b4c2f294a25a6f14e6670ffab", size = 12353322, upload-time = "2026-04-02T11:45:41.628Z" }, + { url = "https://files.pythonhosted.org/packages/eb/4f/9414d98fb307eac8e3de1595cb8e93862586d67bf47e8e2005fef88263a9/sqlglotc-30.2.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fae4edad0b7c5f9f963bd63452f722f0d7f77a436c2d334b555b31722f9573ad", size = 12994260, upload-time = "2026-04-02T11:45:43.984Z" }, + { url = "https://files.pythonhosted.org/packages/6c/02/0d8811da9adcf5256b0e2d820a471a2079b0541efc24d193cc642901dc79/sqlglotc-30.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:b17e3002ed10747388367621b2ecf39c06d5fdc6b3c31a8c32be2f5ef546fc0b", size = 7973074, upload-time = "2026-04-02T11:45:45.806Z" }, + { url = "https://files.pythonhosted.org/packages/7e/36/92a57d3861ffd92574c4911cbc63ea65d723713e65d46577938b00936b0f/sqlglotc-30.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:052cd7bb41fc9b841eb268d4dd601eb6b5954b7c6d5656795d4350a0f8020d53", size = 18536998, upload-time = "2026-04-02T11:45:47.704Z" }, + { url = "https://files.pythonhosted.org/packages/bb/05/a940cbab1e273b6b3e09536baeee109df10702190a42094a43107b473ebd/sqlglotc-30.2.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507935a971e0a9e5d4ac7ca14df479f8e270502b44904f71d95c0aaed066006f", size = 12321847, upload-time = "2026-04-02T11:45:49.779Z" }, + { url = "https://files.pythonhosted.org/packages/b4/95/cf52238080914cea9de1cb26dc2000d41b21176304958fd3317b83b00227/sqlglotc-30.2.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b5fe8adc1a1e2fb819e014e94974a274f30dbf9684ceed9f171fb0889f80f0b", size = 12970399, upload-time = "2026-04-02T11:45:51.994Z" }, + { url = "https://files.pythonhosted.org/packages/48/9b/9263856c9dfcf012179427f40ecff8bea362b982c5b80baa328df5e2765c/sqlglotc-30.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:d577e1635e127febb7012bc42fa1c3b958076e59a1a116ade20048c572a1be42", size = 7976402, upload-time = "2026-04-02T11:45:54.183Z" }, + { url = "https://files.pythonhosted.org/packages/9a/ce/5e31a253284b7de16aaefd1e7931b1ad947e2933fe45ac2038847211a280/sqlglotc-30.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8f063af733cbcc51686380470e7f3f80b589b8c58084baa138efb3b8ca821597", size = 18609250, upload-time = "2026-04-02T11:45:56.123Z" }, + { url = "https://files.pythonhosted.org/packages/16/d0/8fe4687e589e155399f4e1e0c3d2226f02f45d1d12676dfb27113cfd12c3/sqlglotc-30.2.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1a004086ab871be0cc97766f7b6fb8866729f09dd7272254fd31c05107f3fdc8", size = 13029408, upload-time = "2026-04-02T11:45:58.121Z" }, + { url = "https://files.pythonhosted.org/packages/74/cc/f37e409ec2d19723286e751c43578891c0b06e090884760c5bb6284b079f/sqlglotc-30.2.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:058f0e9aed2b8dff87dc893b8793e514204c8dfef699b7d3d1704dfbdd949f2b", size = 13660814, upload-time = "2026-04-02T11:46:00.05Z" }, + { url = "https://files.pythonhosted.org/packages/9d/62/1c745e6c3d6bc6aaf0d10174ad013a3da40a4ab25e00bdf4f0bf754aab4b/sqlglotc-30.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:de884dd224220002c3e940ca5bdceb27ef9638e5f02493db133ffb8ae88b5610", size = 8191516, upload-time = "2026-04-02T11:46:02.296Z" }, + { url = "https://files.pythonhosted.org/packages/ec/73/1f83757df26df54d5b4513020d38ffc23b3dd330974c51f205c8f1369989/sqlglotc-30.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:4aa90e08f53409b1857572836e57a31835ed20e32521c6fafdc6af96199baff7", size = 18545729, upload-time = "2026-04-02T11:46:04.211Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f0/dd29be608858e079db3d2d6159efd928db5d3156fb3ccb82fdca1a78b741/sqlglotc-30.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:585bb610fde3e3dd1d7e5ff3cce14f70fbd53ced6769cd104679adf8b5c4ab5b", size = 12899705, upload-time = "2026-04-02T11:46:06.476Z" }, + { url = "https://files.pythonhosted.org/packages/63/b2/9da4086877e95c0bdff9d67e240e272e1d9c15d5e7c85af6cd77969af079/sqlglotc-30.2.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc292cd73e0c447253877c27f00454a2d09b71324a130ad4c58c145ab753889e", size = 13555880, upload-time = "2026-04-02T11:46:08.816Z" }, + { url = "https://files.pythonhosted.org/packages/89/8b/2a4ebf791b246762eaf479c652ef45bc98a92a26ffb7576477ca7e68ee97/sqlglotc-30.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:13f8f68808777ba7d845bc908bf09f72a0c9899a19811483dc52f0fa48b38d5a", size = 8199520, upload-time = "2026-04-02T11:46:10.701Z" }, + { url = "https://files.pythonhosted.org/packages/14/04/1b4955eb05d99b603e6d6146f5c2596a1401dc3806f3c0563bb7453a9221/sqlglotc-30.2.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:0e6be524252894c0fa98d25d4e60dfae6485ba66ca1abd40bf05f16a9cf26baf", size = 18453317, upload-time = "2026-04-02T11:46:12.616Z" }, + { url = "https://files.pythonhosted.org/packages/66/a1/518093e9163572adecf9dcf645bc8a1eef280c1c45c72df5614d2c650fdd/sqlglotc-30.2.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:850e7517dd4739cad9af65bcb9699825f9202e5971407bf955e3248fe4814f96", size = 12896480, upload-time = "2026-04-02T11:46:14.622Z" }, + { url = "https://files.pythonhosted.org/packages/63/c5/88b329a6565592472c3b555791d0e14cb08eaa5455b9be45d3f89db8ea1f/sqlglotc-30.2.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:feefc0ab7606d1fe284d23bef09ea4829ce4fad679936959c29324310f23e081", size = 13490650, upload-time = "2026-04-02T11:46:16.854Z" }, + { url = "https://files.pythonhosted.org/packages/17/ce/5e09bdf6d6037621eea07fbabc498f0932fcb693046dfabb199c0c078923/sqlglotc-30.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:515e092ab8fb522b256fa8a34f471e9b187bb8a50a7c0226a65b036a07d6d188", size = 8370358, upload-time = "2026-04-02T11:46:18.732Z" }, ] [[package]] @@ -7853,14 +7853,14 @@ wheels = [ [[package]] name = "types-cffi" -version = "2.0.0.20260316" +version = "2.0.0.20260402" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "types-setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/07/4c/805b40b094eb3fd60f8d17fa7b3c58a33781311a95d0e6a74da0751ce294/types_cffi-2.0.0.20260316.tar.gz", hash = "sha256:8fb06ed4709675c999853689941133affcd2250cd6121cc11fd22c0d81ad510c", size = 17399, upload-time = "2026-03-16T07:54:43.059Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/85/3896bfcb4e7c32904f762c36ff0afa96d3e39bfce5a95a41635af79c8761/types_cffi-2.0.0.20260402.tar.gz", hash = "sha256:47e1320c009f630c59c55c8e3d2b8c501e280babf52e92f6109cbfb0864ba367", size = 17476, upload-time = "2026-04-02T04:21:09.332Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/5e/9f1a709225ad9d0e1d7a6e4366ff285f0113c749e882d6cbeb40eab32e75/types_cffi-2.0.0.20260316-py3-none-any.whl", hash = "sha256:dd504698029db4c580385f679324621cc64d886e6a23e9821d52bc5169251302", size = 20096, upload-time = "2026-03-16T07:54:41.994Z" }, + { url = "https://files.pythonhosted.org/packages/ae/26/aacfef05841e31c65f889ae4225c6bce6b84cd5d3882c42a3661030f29ee/types_cffi-2.0.0.20260402-py3-none-any.whl", hash = "sha256:f647a400fba0a31d603479169d82ee5359db79bd1136e41dc7e6489296e3a2b2", size = 20103, upload-time = "2026-04-02T04:21:08.199Z" }, ] [[package]] @@ -7883,32 +7883,32 @@ wheels = [ [[package]] name = "types-protobuf" -version = "6.32.1.20260221" +version = "7.34.1.20260402" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5f/e2/9aa4a3b2469508bd7b4e2ae11cbedaf419222a09a1b94daffcd5efca4023/types_protobuf-6.32.1.20260221.tar.gz", hash = "sha256:6d5fb060a616bfb076cbb61b4b3c3969f5fc8bec5810f9a2f7e648ee5cbcbf6e", size = 64408, upload-time = "2026-02-21T03:55:13.916Z" } +sdist = { url = "https://files.pythonhosted.org/packages/40/43/19261b537cfc8db9f39f92b31dba961462a034412aafafa630aa54191db7/types_protobuf-7.34.1.20260402.tar.gz", hash = "sha256:7270594b4bc28dfe85602d97849b28a2fe7905d77b1d91b418924ed6f83f857c", size = 68767, upload-time = "2026-04-02T04:18:25.025Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/e8/1fd38926f9cf031188fbc5a96694203ea6f24b0e34bd64a225ec6f6291ba/types_protobuf-6.32.1.20260221-py3-none-any.whl", hash = "sha256:da7cdd947975964a93c30bfbcc2c6841ee646b318d3816b033adc2c4eb6448e4", size = 77956, upload-time = "2026-02-21T03:55:12.894Z" }, + { url = "https://files.pythonhosted.org/packages/15/34/9bc4fcb26be938a4d678a1e5364e6a6b36e84c1d9b6aaf4d6bb5082ad86a/types_protobuf-7.34.1.20260402-py3-none-any.whl", hash = "sha256:5a1094907ca0d19202f7bca6db06ff69779873db695ddd73f2dde718cd8df1fa", size = 86007, upload-time = "2026-04-02T04:18:23.673Z" }, ] [[package]] name = "types-psutil" -version = "7.2.2.20260130" +version = "7.2.2.20260402" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/14/fc5fb0a6ddfadf68c27e254a02ececd4d5c7fdb0efcb7e7e917a183497fb/types_psutil-7.2.2.20260130.tar.gz", hash = "sha256:15b0ab69c52841cf9ce3c383e8480c620a4d13d6a8e22b16978ebddac5590950", size = 26535, upload-time = "2026-01-30T03:58:14.116Z" } +sdist = { url = "https://files.pythonhosted.org/packages/31/a2/a608db0caf0d71bd231305dc3ab3f5d65624d77761003696a3ca8c6fad40/types_psutil-7.2.2.20260402.tar.gz", hash = "sha256:9f36eebf15ad8487f8004ed67c8e008b84b63ba00cfb709a3f60275058217329", size = 26522, upload-time = "2026-04-02T04:18:47.916Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/d7/60974b7e31545d3768d1770c5fe6e093182c3bfd819429b33133ba6b3e89/types_psutil-7.2.2.20260130-py3-none-any.whl", hash = "sha256:15523a3caa7b3ff03ac7f9b78a6470a59f88f48df1d74a39e70e06d2a99107da", size = 32876, upload-time = "2026-01-30T03:58:13.172Z" }, + { url = "https://files.pythonhosted.org/packages/81/8a/f4b3ca3154e8a77df91eb7a28c208af721d48f8a4aca667f582523a0beff/types_psutil-7.2.2.20260402-py3-none-any.whl", hash = "sha256:653d1fd908e68cc0666754b16a0cee28efbded0c401caa5314d2aeea67f227cd", size = 32860, upload-time = "2026-04-02T04:18:46.671Z" }, ] [[package]] name = "types-pygments" -version = "2.19.0.20251121" +version = "2.19.0.20260402" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "types-docutils" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/3b/cd650700ce9e26b56bd1a6aa4af397bbbc1784e22a03971cb633cdb0b601/types_pygments-2.19.0.20251121.tar.gz", hash = "sha256:eef114fde2ef6265365522045eac0f8354978a566852f69e75c531f0553822b1", size = 18590, upload-time = "2025-11-21T03:03:46.623Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/a8/5834c55d900ce31b31367eedb82e664347dfa551a957b74a0ce0cd9f4f9a/types_pygments-2.19.0.20260402.tar.gz", hash = "sha256:bd26e1f662c9a3b8ea56668ddc099b809ffd54931bee97b15853bc4a8e5d4250", size = 18808, upload-time = "2026-04-02T04:21:13.058Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/8a/9244b21f1d60dcc62e261435d76b02f1853b4771663d7ec7d287e47a9ba9/types_pygments-2.19.0.20251121-py3-none-any.whl", hash = "sha256:cb3bfde34eb75b984c98fb733ce4f795213bd3378f855c32e75b49318371bb25", size = 25674, upload-time = "2025-11-21T03:03:45.72Z" }, + { url = "https://files.pythonhosted.org/packages/f7/f2/b23659df4219fc4f39a45ede527cc209d961c7ff957678536a7a9d9ac9c5/types_pygments-2.19.0.20260402-py3-none-any.whl", hash = "sha256:5b0d863cec1c43ba38c946fb6e89d389c1cf287806f72360336fba4482f8daeb", size = 25672, upload-time = "2026-04-02T04:21:11.916Z" }, ] [[package]] @@ -7922,20 +7922,20 @@ wheels = [ [[package]] name = "types-pytz" -version = "2026.1.1.20260304" +version = "2026.1.1.20260402" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/56/2f12a15ea8c5615c8fb896c4fbbb527ab1c0f776ed5860c6fc9ec26ea2c7/types_pytz-2026.1.1.20260304.tar.gz", hash = "sha256:0c3542d8e9b0160b424233440c52b83d6f58cae4b85333d54e4f961cf013e117", size = 11198, upload-time = "2026-03-04T03:57:24.445Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/ff/52b895d4cb5f51f4ae50de8ca6a0b4098a71fa174b63f14d80ba86fa0692/types_pytz-2026.1.1.20260402.tar.gz", hash = "sha256:79209aa51dc003a4a6a764234d92b14e5c09a1b7f24e0f00c493929fd33618e8", size = 10726, upload-time = "2026-04-02T04:17:52.603Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/b8/e77c355f179dc89d44e7ca6dbf7a46e650806df1d356a5462e5829fccea5/types_pytz-2026.1.1.20260304-py3-none-any.whl", hash = "sha256:175332c1cf7bd6b1cc56b877f70bf02def1a3f75e5adcc05385ce2c3c70e6500", size = 10126, upload-time = "2026-03-04T03:57:23.481Z" }, + { url = "https://files.pythonhosted.org/packages/79/ce/93d4fc3ba66be6cd50f3f22bbdb6fd953a02689398ea2c4a733e023c9227/types_pytz-2026.1.1.20260402-py3-none-any.whl", hash = "sha256:0d9a60ed1c6ad4fce7c6395b5bd2d9827db41d4b83de7c0322cf85869c2bfda3", size = 10122, upload-time = "2026-04-02T04:17:51.729Z" }, ] [[package]] name = "types-setuptools" -version = "82.0.0.20260210" +version = "82.0.0.20260402" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4b/90/796ac8c774a7f535084aacbaa6b7053d16fff5c630eff87c3ecff7896c37/types_setuptools-82.0.0.20260210.tar.gz", hash = "sha256:d9719fbbeb185254480ade1f25327c4654f8c00efda3fec36823379cebcdee58", size = 44768, upload-time = "2026-02-10T04:22:02.107Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e9/f8/74f8a76b4311e70772c0df8f2d432040a3b0facd7bcce6b72b0b26e1746b/types_setuptools-82.0.0.20260402.tar.gz", hash = "sha256:63d2b10ba7958396ad79bbc24d2f6311484e452daad4637ffd40407983a27069", size = 44805, upload-time = "2026-04-02T04:17:49.229Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/54/3489432b1d9bc713c9d8aa810296b8f5b0088403662959fb63a8acdbd4fc/types_setuptools-82.0.0.20260210-py3-none-any.whl", hash = "sha256:5124a7daf67f195c6054e0f00f1d97c69caad12fdcf9113eba33eff0bce8cd2b", size = 68433, upload-time = "2026-02-10T04:22:00.876Z" }, + { url = "https://files.pythonhosted.org/packages/0e/e9/22451997f70ac2c5f18dc5f988750c986011fb049d9021767277119e63fa/types_setuptools-82.0.0.20260402-py3-none-any.whl", hash = "sha256:4b9a9f6c3c4c65107a3956ad6a6acbccec38e398ff6d5f78d5df7f103dadb8d6", size = 68429, upload-time = "2026-04-02T04:17:48.11Z" }, ] [[package]] From 913c090d2faa577e9390a79b05b88d3730496981 Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Thu, 2 Apr 2026 21:32:28 +0000 Subject: [PATCH 15/15] fix: update dependencies and correct import paths in asyncmy and postgres dialects --- .pre-commit-config.yaml | 2 +- sqlspec/adapters/asyncmy/events/store.py | 4 +- sqlspec/dialects/postgres/_generators.py | 2 +- uv.lock | 66 ++++++++++++------------ 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index af836ca5e..af0cd5666 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: - id: mixed-line-ending - id: trailing-whitespace - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: "v0.15.8" + rev: "v0.15.9" hooks: - id: ruff args: ["--fix"] diff --git a/sqlspec/adapters/asyncmy/events/store.py b/sqlspec/adapters/asyncmy/events/store.py index 95f718c4d..db80affdf 100644 --- a/sqlspec/adapters/asyncmy/events/store.py +++ b/sqlspec/adapters/asyncmy/events/store.py @@ -9,7 +9,7 @@ from typing import Final -from sqlspec.adapters.asyncmy import AsyncmyConfig +from sqlspec.adapters.asyncmy.config import AsyncmyConfig from sqlspec.extensions.events import BaseEventQueueStore __all__ = ("AsyncmyEventQueueStore",) @@ -32,7 +32,7 @@ class AsyncmyEventQueueStore(BaseEventQueueStore[AsyncmyConfig]): - queue_table: Table name (default: "sqlspec_event_queue") Example: - from sqlspec.adapters.asyncmy import AsyncmyConfig + from sqlspec.adapters.asyncmy.config import AsyncmyConfig from sqlspec.adapters.asyncmy.events import AsyncmyEventQueueStore config = AsyncmyConfig( diff --git a/sqlspec/dialects/postgres/_generators.py b/sqlspec/dialects/postgres/_generators.py index edee49c77..56c07cbfd 100644 --- a/sqlspec/dialects/postgres/_generators.py +++ b/sqlspec/dialects/postgres/_generators.py @@ -25,7 +25,7 @@ def _postgres_extension_operator_sql(generator: PostgresGenerator, expression: exp.Operator) -> str: dialect_class = getattr(generator.dialect, "__class__", None) dialect_name = dialect_class.__name__ if dialect_class else None - if dialect_name in ("PGVector", "ParadeDB"): + if dialect_name in {"PGVector", "ParadeDB"}: if is_vector_distance_expression(expression): return render_vector_distance_postgres( generator.sql(expression, "this"), diff --git a/uv.lock b/uv.lock index 177203391..71adcdac8 100644 --- a/uv.lock +++ b/uv.lock @@ -1921,7 +1921,7 @@ wheels = [ [[package]] name = "google-api-core" -version = "2.30.1" +version = "2.30.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-auth" }, @@ -1930,9 +1930,9 @@ dependencies = [ { name = "protobuf" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f7/0b/b6e296aff70bef900766934cf4e83eaacc3f244adb61936b66d24b204080/google_api_core-2.30.1.tar.gz", hash = "sha256:7304ef3bd7e77fd26320a36eeb75868f9339532bfea21694964f4765b37574ee", size = 176742, upload-time = "2026-03-30T22:50:52.637Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/2e/83ca41eb400eb228f9279ec14ed66f6475218b59af4c6daec2d5a509fe83/google_api_core-2.30.2.tar.gz", hash = "sha256:9a8113e1a88bdc09a7ff629707f2214d98d61c7f6ceb0ea38c42a095d02dc0f9", size = 176862, upload-time = "2026-04-02T21:23:44.876Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/86/a00ea4596780ef3f0721c1f073c0c5ae992da4f35cf12f0d8c92d19267a6/google_api_core-2.30.1-py3-none-any.whl", hash = "sha256:3be893babbb54a89c6807b598383ddf212112130e3d24d06c681b5d18f082e08", size = 173238, upload-time = "2026-03-30T22:48:50.586Z" }, + { url = "https://files.pythonhosted.org/packages/84/e1/ebd5100cbb202e561c0c8b59e485ef3bd63fa9beb610f3fdcaea443f0288/google_api_core-2.30.2-py3-none-any.whl", hash = "sha256:a4c226766d6af2580577db1f1a51bf53cd262f722b49731ce7414c43068a9594", size = 173236, upload-time = "2026-04-02T21:23:06.395Z" }, ] [package.optional-dependencies] @@ -2130,7 +2130,7 @@ wheels = [ [[package]] name = "google-cloud-bigtable" -version = "2.35.0" +version = "2.36.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core", extra = ["grpc"] }, @@ -2141,9 +2141,9 @@ dependencies = [ { name = "proto-plus" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/57/c9/aceae21411b1a77fb4d3cde6e6f461321ee33c65fb8dc53480d4e47e1a55/google_cloud_bigtable-2.35.0.tar.gz", hash = "sha256:f5699012c5fea4bd4bdf7e80e5e3a812a847eb8f41bf8dc2f43095d6d876b83b", size = 775613, upload-time = "2025-12-17T15:18:14.303Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/f5/ad2a48306a7e8d5e47b5203703ce9c343389e60f025b5ea3f0c62ba92129/google_cloud_bigtable-2.36.0.tar.gz", hash = "sha256:d5987733c2f60c739f93f259d2037858411cc994ac37cdfbccb6bb159f3ca43e", size = 796035, upload-time = "2026-04-02T21:23:33.248Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/69/03eed134d71f6117ffd9efac2d1033bb2fa2522e9e82545a0828061d32f4/google_cloud_bigtable-2.35.0-py3-none-any.whl", hash = "sha256:f355bfce1f239453ec2bb3839b0f4f9937cf34ef06ef29e1ca63d58fd38d0c50", size = 540341, upload-time = "2025-12-17T15:18:12.176Z" }, + { url = "https://files.pythonhosted.org/packages/d1/19/1cc695fa8489ef446a70ee9e983c12f4b47e0649005758035530eaec4b1c/google_cloud_bigtable-2.36.0-py3-none-any.whl", hash = "sha256:21b2f41231b7368a550b44d5b493b811b3507fcb23eb26d00005cd3f205f2207", size = 552799, upload-time = "2026-04-02T21:23:20.475Z" }, ] [[package]] @@ -2442,14 +2442,14 @@ wheels = [ [[package]] name = "googleapis-common-protos" -version = "1.73.1" +version = "1.74.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/c0/4a54c386282c13449eca8bbe2ddb518181dc113e78d240458a68856b4d69/googleapis_common_protos-1.73.1.tar.gz", hash = "sha256:13114f0e9d2391756a0194c3a8131974ed7bffb06086569ba193364af59163b6", size = 147506, upload-time = "2026-03-26T22:17:38.451Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/18/a746c8344152d368a5aac738d4c857012f2c5d1fd2eac7e17b647a7861bd/googleapis_common_protos-1.74.0.tar.gz", hash = "sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1", size = 151254, upload-time = "2026-04-02T21:23:26.679Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/82/fcb6520612bec0c39b973a6c0954b6a0d948aadfe8f7e9487f60ceb8bfa6/googleapis_common_protos-1.73.1-py3-none-any.whl", hash = "sha256:e51f09eb0a43a8602f5a915870972e6b4a394088415c79d79605a46d8e826ee8", size = 297556, upload-time = "2026-03-26T22:15:58.455Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b0/be5d3329badb9230b765de6eea66b73abd5944bdeb5afb3562ddcd80ae84/googleapis_common_protos-1.74.0-py3-none-any.whl", hash = "sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5", size = 300743, upload-time = "2026-04-02T21:22:49.108Z" }, ] [package.optional-dependencies] @@ -6381,27 +6381,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/14/b0/73cf7550861e2b4824950b8b52eebdcc5adc792a00c514406556c5b80817/ruff-0.15.8.tar.gz", hash = "sha256:995f11f63597ee362130d1d5a327a87cb6f3f5eae3094c620bcc632329a4d26e", size = 4610921, upload-time = "2026-03-26T18:39:38.675Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/92/c445b0cd6da6e7ae51e954939cb69f97e008dbe750cfca89b8cedc081be7/ruff-0.15.8-py3-none-linux_armv6l.whl", hash = "sha256:cbe05adeba76d58162762d6b239c9056f1a15a55bd4b346cfd21e26cd6ad7bc7", size = 10527394, upload-time = "2026-03-26T18:39:41.566Z" }, - { url = "https://files.pythonhosted.org/packages/eb/92/f1c662784d149ad1414cae450b082cf736430c12ca78367f20f5ed569d65/ruff-0.15.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570", size = 10905693, upload-time = "2026-03-26T18:39:30.364Z" }, - { url = "https://files.pythonhosted.org/packages/ca/f2/7a631a8af6d88bcef997eb1bf87cc3da158294c57044aafd3e17030613de/ruff-0.15.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3", size = 10323044, upload-time = "2026-03-26T18:39:33.37Z" }, - { url = "https://files.pythonhosted.org/packages/67/18/1bf38e20914a05e72ef3b9569b1d5c70a7ef26cd188d69e9ca8ef588d5bf/ruff-0.15.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdce027ada77baa448077ccc6ebb2fa9c3c62fd110d8659d601cf2f475858d94", size = 10629135, upload-time = "2026-03-26T18:39:44.142Z" }, - { url = "https://files.pythonhosted.org/packages/d2/e9/138c150ff9af60556121623d41aba18b7b57d95ac032e177b6a53789d279/ruff-0.15.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12e617fc01a95e5821648a6df341d80456bd627bfab8a829f7cfc26a14a4b4a3", size = 10348041, upload-time = "2026-03-26T18:39:52.178Z" }, - { url = "https://files.pythonhosted.org/packages/02/f1/5bfb9298d9c323f842c5ddeb85f1f10ef51516ac7a34ba446c9347d898df/ruff-0.15.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:432701303b26416d22ba696c39f2c6f12499b89093b61360abc34bcc9bf07762", size = 11121987, upload-time = "2026-03-26T18:39:55.195Z" }, - { url = "https://files.pythonhosted.org/packages/10/11/6da2e538704e753c04e8d86b1fc55712fdbdcc266af1a1ece7a51fff0d10/ruff-0.15.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d910ae974b7a06a33a057cb87d2a10792a3b2b3b35e33d2699fdf63ec8f6b17a", size = 11951057, upload-time = "2026-03-26T18:39:19.18Z" }, - { url = "https://files.pythonhosted.org/packages/83/f0/c9208c5fd5101bf87002fed774ff25a96eea313d305f1e5d5744698dc314/ruff-0.15.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2033f963c43949d51e6fdccd3946633c6b37c484f5f98c3035f49c27395a8ab8", size = 11464613, upload-time = "2026-03-26T18:40:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/f8/22/d7f2fabdba4fae9f3b570e5605d5eb4500dcb7b770d3217dca4428484b17/ruff-0.15.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1", size = 11257557, upload-time = "2026-03-26T18:39:57.972Z" }, - { url = "https://files.pythonhosted.org/packages/71/8c/382a9620038cf6906446b23ce8632ab8c0811b8f9d3e764f58bedd0c9a6f/ruff-0.15.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:ac51d486bf457cdc985a412fb1801b2dfd1bd8838372fc55de64b1510eff4bec", size = 11169440, upload-time = "2026-03-26T18:39:22.205Z" }, - { url = "https://files.pythonhosted.org/packages/4d/0d/0994c802a7eaaf99380085e4e40c845f8e32a562e20a38ec06174b52ef24/ruff-0.15.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c9861eb959edab053c10ad62c278835ee69ca527b6dcd72b47d5c1e5648964f6", size = 10605963, upload-time = "2026-03-26T18:39:46.682Z" }, - { url = "https://files.pythonhosted.org/packages/19/aa/d624b86f5b0aad7cef6bbf9cd47a6a02dfdc4f72c92a337d724e39c9d14b/ruff-0.15.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8d9a5b8ea13f26ae90838afc33f91b547e61b794865374f114f349e9036835fb", size = 10357484, upload-time = "2026-03-26T18:39:49.176Z" }, - { url = "https://files.pythonhosted.org/packages/35/c3/e0b7835d23001f7d999f3895c6b569927c4d39912286897f625736e1fd04/ruff-0.15.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c2a33a529fb3cbc23a7124b5c6ff121e4d6228029cba374777bd7649cc8598b8", size = 10830426, upload-time = "2026-03-26T18:40:03.702Z" }, - { url = "https://files.pythonhosted.org/packages/f0/51/ab20b322f637b369383adc341d761eaaa0f0203d6b9a7421cd6e783d81b9/ruff-0.15.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:75e5cd06b1cf3f47a3996cfc999226b19aa92e7cce682dcd62f80d7035f98f49", size = 11345125, upload-time = "2026-03-26T18:39:27.799Z" }, - { url = "https://files.pythonhosted.org/packages/37/e6/90b2b33419f59d0f2c4c8a48a4b74b460709a557e8e0064cf33ad894f983/ruff-0.15.8-py3-none-win32.whl", hash = "sha256:bc1f0a51254ba21767bfa9a8b5013ca8149dcf38092e6a9eb704d876de94dc34", size = 10571959, upload-time = "2026-03-26T18:39:36.117Z" }, - { url = "https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl", hash = "sha256:04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89", size = 11743893, upload-time = "2026-03-26T18:39:25.01Z" }, - { url = "https://files.pythonhosted.org/packages/15/e2/77be4fff062fa78d9b2a4dea85d14785dac5f1d0c1fb58ed52331f0ebe28/ruff-0.15.8-py3-none-win_arm64.whl", hash = "sha256:cf891fa8e3bb430c0e7fac93851a5978fc99c8fa2c053b57b118972866f8e5f2", size = 11048175, upload-time = "2026-03-26T18:40:01.06Z" }, +version = "0.15.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/97/e9f1ca355108ef7194e38c812ef40ba98c7208f47b13ad78d023caa583da/ruff-0.15.9.tar.gz", hash = "sha256:29cbb1255a9797903f6dde5ba0188c707907ff44a9006eb273b5a17bfa0739a2", size = 4617361, upload-time = "2026-04-02T18:17:20.829Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/1f/9cdfd0ac4b9d1e5a6cf09bedabdf0b56306ab5e333c85c87281273e7b041/ruff-0.15.9-py3-none-linux_armv6l.whl", hash = "sha256:6efbe303983441c51975c243e26dff328aca11f94b70992f35b093c2e71801e1", size = 10511206, upload-time = "2026-04-02T18:16:41.574Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f6/32bfe3e9c136b35f02e489778d94384118bb80fd92c6d92e7ccd97db12ce/ruff-0.15.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4965bac6ac9ea86772f4e23587746f0b7a395eccabb823eb8bfacc3fa06069f7", size = 10923307, upload-time = "2026-04-02T18:17:08.645Z" }, + { url = "https://files.pythonhosted.org/packages/ca/25/de55f52ab5535d12e7aaba1de37a84be6179fb20bddcbe71ec091b4a3243/ruff-0.15.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eaf05aad70ca5b5a0a4b0e080df3a6b699803916d88f006efd1f5b46302daab8", size = 10316722, upload-time = "2026-04-02T18:16:44.206Z" }, + { url = "https://files.pythonhosted.org/packages/48/11/690d75f3fd6278fe55fff7c9eb429c92d207e14b25d1cae4064a32677029/ruff-0.15.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9439a342adb8725f32f92732e2bafb6d5246bd7a5021101166b223d312e8fc59", size = 10623674, upload-time = "2026-04-02T18:16:50.951Z" }, + { url = "https://files.pythonhosted.org/packages/bd/ec/176f6987be248fc5404199255522f57af1b4a5a1b57727e942479fec98ad/ruff-0.15.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c5e6faf9d97c8edc43877c3f406f47446fc48c40e1442d58cfcdaba2acea745", size = 10351516, upload-time = "2026-04-02T18:16:57.206Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fc/51cffbd2b3f240accc380171d51446a32aa2ea43a40d4a45ada67368fbd2/ruff-0.15.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b34a9766aeec27a222373d0b055722900fbc0582b24f39661aa96f3fe6ad901", size = 11150202, upload-time = "2026-04-02T18:17:06.452Z" }, + { url = "https://files.pythonhosted.org/packages/d6/d4/25292a6dfc125f6b6528fe6af31f5e996e19bf73ca8e3ce6eb7fa5b95885/ruff-0.15.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89dd695bc72ae76ff484ae54b7e8b0f6b50f49046e198355e44ea656e521fef9", size = 11988891, upload-time = "2026-04-02T18:17:18.575Z" }, + { url = "https://files.pythonhosted.org/packages/13/e1/1eebcb885c10e19f969dcb93d8413dfee8172578709d7ee933640f5e7147/ruff-0.15.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce187224ef1de1bd225bc9a152ac7102a6171107f026e81f317e4257052916d5", size = 11480576, upload-time = "2026-04-02T18:16:52.986Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6b/a1548ac378a78332a4c3dcf4a134c2475a36d2a22ddfa272acd574140b50/ruff-0.15.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b0c7c341f68adb01c488c3b7d4b49aa8ea97409eae6462d860a79cf55f431b6", size = 11254525, upload-time = "2026-04-02T18:17:02.041Z" }, + { url = "https://files.pythonhosted.org/packages/42/aa/4bb3af8e61acd9b1281db2ab77e8b2c3c5e5599bf2a29d4a942f1c62b8d6/ruff-0.15.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:55cc15eee27dc0eebdfcb0d185a6153420efbedc15eb1d38fe5e685657b0f840", size = 11204072, upload-time = "2026-04-02T18:17:13.581Z" }, + { url = "https://files.pythonhosted.org/packages/69/48/d550dc2aa6e423ea0bcc1d0ff0699325ffe8a811e2dba156bd80750b86dc/ruff-0.15.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a6537f6eed5cda688c81073d46ffdfb962a5f29ecb6f7e770b2dc920598997ed", size = 10594998, upload-time = "2026-04-02T18:16:46.369Z" }, + { url = "https://files.pythonhosted.org/packages/63/47/321167e17f5344ed5ec6b0aa2cff64efef5f9e985af8f5622cfa6536043f/ruff-0.15.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6d3fcbca7388b066139c523bda744c822258ebdcfbba7d24410c3f454cc9af71", size = 10359769, upload-time = "2026-04-02T18:17:10.994Z" }, + { url = "https://files.pythonhosted.org/packages/67/5e/074f00b9785d1d2c6f8c22a21e023d0c2c1817838cfca4c8243200a1fa87/ruff-0.15.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:058d8e99e1bfe79d8a0def0b481c56059ee6716214f7e425d8e737e412d69677", size = 10850236, upload-time = "2026-04-02T18:16:48.749Z" }, + { url = "https://files.pythonhosted.org/packages/76/37/804c4135a2a2caf042925d30d5f68181bdbd4461fd0d7739da28305df593/ruff-0.15.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8e1ddb11dbd61d5983fa2d7d6370ef3eb210951e443cace19594c01c72abab4c", size = 11358343, upload-time = "2026-04-02T18:16:55.068Z" }, + { url = "https://files.pythonhosted.org/packages/88/3d/1364fcde8656962782aa9ea93c92d98682b1ecec2f184e625a965ad3b4a6/ruff-0.15.9-py3-none-win32.whl", hash = "sha256:bde6ff36eaf72b700f32b7196088970bf8fdb2b917b7accd8c371bfc0fd573ec", size = 10583382, upload-time = "2026-04-02T18:17:04.261Z" }, + { url = "https://files.pythonhosted.org/packages/4c/56/5c7084299bd2cacaa07ae63a91c6f4ba66edc08bf28f356b24f6b717c799/ruff-0.15.9-py3-none-win_amd64.whl", hash = "sha256:45a70921b80e1c10cf0b734ef09421f71b5aa11d27404edc89d7e8a69505e43d", size = 11744969, upload-time = "2026-04-02T18:16:59.611Z" }, + { url = "https://files.pythonhosted.org/packages/03/36/76704c4f312257d6dbaae3c959add2a622f63fcca9d864659ce6d8d97d3d/ruff-0.15.9-py3-none-win_arm64.whl", hash = "sha256:0694e601c028fd97dc5c6ee244675bc241aeefced7ef80cd9c6935a871078f53", size = 11005870, upload-time = "2026-04-02T18:17:15.773Z" }, ] [[package]] @@ -8283,14 +8283,14 @@ wheels = [ [[package]] name = "werkzeug" -version = "3.1.7" +version = "3.1.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/43/76ded108b296a49f52de6bac5192ca1c4be84e886f9b5c9ba8427d9694fd/werkzeug-3.1.7.tar.gz", hash = "sha256:fb8c01fe6ab13b9b7cdb46892b99b1d66754e1d7ab8e542e865ec13f526b5351", size = 875700, upload-time = "2026-03-24T01:08:07.687Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd117872481b6e378f85c957dd7c5bca38897b08f765fd/werkzeug-3.1.8.tar.gz", hash = "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44", size = 875852, upload-time = "2026-04-02T18:49:14.268Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/b2/0bba9bbb4596d2d2f285a16c2ab04118f6b957d8441566e1abb892e6a6b2/werkzeug-3.1.7-py3-none-any.whl", hash = "sha256:4b314d81163a3e1a169b6a0be2a000a0e204e8873c5de6586f453c55688d422f", size = 226295, upload-time = "2026-03-24T01:08:06.133Z" }, + { url = "https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl", hash = "sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50", size = 226459, upload-time = "2026-04-02T18:49:12.72Z" }, ] [[package]]