diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d2902497..355d9618 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,7 +23,7 @@ repos: - id: unasyncd additional_dependencies: ["ruff"] - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: "v0.4.7" + rev: "v0.4.8" hooks: - id: ruff args: ["--fix"] diff --git a/advanced_alchemy/config/asyncio.py b/advanced_alchemy/config/asyncio.py index 6412372c..64bcda64 100644 --- a/advanced_alchemy/config/asyncio.py +++ b/advanced_alchemy/config/asyncio.py @@ -48,16 +48,16 @@ class AlembicAsyncConfig(GenericAlembicConfig): @dataclass -class SQLAlchemyAsyncConfig(GenericSQLAlchemyConfig[AsyncEngine, AsyncSession, async_sessionmaker]): +class SQLAlchemyAsyncConfig(GenericSQLAlchemyConfig[AsyncEngine, AsyncSession, async_sessionmaker[AsyncSession]]): """Async SQLAlchemy Configuration.""" create_engine_callable: Callable[[str], AsyncEngine] = create_async_engine """Callable that creates an :class:`AsyncEngine ` instance or instance of its subclass. """ - session_config: AsyncSessionConfig = field(default_factory=AsyncSessionConfig) + session_config: AsyncSessionConfig = field(default_factory=AsyncSessionConfig) # pyright: ignore[reportIncompatibleVariableOverride] """Configuration options for the :class:`async_sessionmaker`.""" - session_maker_class: type[async_sessionmaker] = async_sessionmaker + session_maker_class: type[async_sessionmaker[AsyncSession]] = async_sessionmaker # pyright: ignore[reportIncompatibleVariableOverride] """Sessionmaker class to use.""" alembic_config: AlembicAsyncConfig = field(default_factory=AlembicAsyncConfig) """Configuration for the SQLAlchemy Alembic migrations. diff --git a/advanced_alchemy/config/common.py b/advanced_alchemy/config/common.py index 6e2e9487..78109cba 100644 --- a/advanced_alchemy/config/common.py +++ b/advanced_alchemy/config/common.py @@ -2,7 +2,7 @@ from dataclasses import dataclass, field from pathlib import Path -from typing import TYPE_CHECKING, Callable, Generic, TypeVar +from typing import TYPE_CHECKING, Callable, Generic, TypeVar, cast from advanced_alchemy.base import orm_registry from advanced_alchemy.config.engine import EngineConfig @@ -27,6 +27,7 @@ ) ALEMBIC_TEMPLATE_PATH = f"{Path(__file__).parent.parent}/alembic/templates" + """Path to the Alembic templates.""" ConnectionT = TypeVar("ConnectionT", bound="Connection | AsyncConnection") """Type variable for a SQLAlchemy connection.""" @@ -34,7 +35,7 @@ """Type variable for a SQLAlchemy engine.""" SessionT = TypeVar("SessionT", bound="Session | AsyncSession") """Type variable for a SQLAlchemy session.""" -SessionMakerT = TypeVar("SessionMakerT", bound="sessionmaker | async_sessionmaker") +SessionMakerT = TypeVar("SessionMakerT", bound="sessionmaker[Session] | async_sessionmaker[AsyncSession]") """Type variable for a SQLAlchemy sessionmaker.""" @@ -50,7 +51,7 @@ class GenericSessionConfig(Generic[ConnectionT, EngineT, SessionT]): bind: EngineT | ConnectionT | None | EmptyType = Empty """The :class:`Engine ` or :class:`Connection ` that new :class:`Session ` objects will be bound to.""" - binds: dict[type[Any] | Mapper | TableClause | str, EngineT | ConnectionT] | None | EmptyType = Empty + binds: dict[type[Any] | Mapper | TableClause | str, EngineT | ConnectionT] | None | EmptyType = Empty # pyright: ignore[reportMissingTypeArgument] """A dictionary which may specify any number of :class:`Engine ` or :class:`Connection ` objects as the source of connectivity for SQL operations on a per-entity basis. The keys of the dictionary consist of any series of mapped classes, arbitrary Python classes that are bases for mapped @@ -68,7 +69,7 @@ class GenericSessionConfig(Generic[ConnectionT, EngineT, SessionT]): """Describes the transactional behavior to take when a given bind is a Connection that has already begun a transaction outside the scope of this Session; in other words the :attr:`Connection.in_transaction() ` method returns True.""" - query_cls: type[Query] | None | EmptyType = Empty + query_cls: type[Query] | None | EmptyType = Empty # pyright: ignore[reportMissingTypeArgument] """Class which should be used to create new Query objects, as returned by the :attr:`Session.query() ` method.""" twophase: bool | EmptyType = Empty @@ -92,7 +93,7 @@ class GenericSQLAlchemyConfig(Generic[EngineT, SessionT, SessionMakerT]): """Configuration options for either the :class:`async_sessionmaker ` or :class:`sessionmaker `. """ - session_maker_class: type[sessionmaker | async_sessionmaker] + session_maker_class: type[sessionmaker[Session] | async_sessionmaker[AsyncSession]] """Sessionmaker class to use.""" connection_string: str | None = field(default=None) """Database connection string in one of the formats supported by SQLAlchemy. @@ -196,7 +197,7 @@ def create_session_maker(self) -> Callable[[], SessionT]: session_kws = self.session_config_dict if session_kws.get("bind") is None: session_kws["bind"] = self.get_engine() - return self.session_maker_class(**session_kws) + return cast("Callable[[], SessionT]", self.session_maker_class(**session_kws)) @dataclass diff --git a/advanced_alchemy/config/sync.py b/advanced_alchemy/config/sync.py index b8838d2f..938fbe50 100644 --- a/advanced_alchemy/config/sync.py +++ b/advanced_alchemy/config/sync.py @@ -33,16 +33,16 @@ class AlembicSyncConfig(GenericAlembicConfig): @dataclass -class SQLAlchemySyncConfig(GenericSQLAlchemyConfig[Engine, Session, sessionmaker]): +class SQLAlchemySyncConfig(GenericSQLAlchemyConfig[Engine, Session, sessionmaker[Session]]): """Sync SQLAlchemy Configuration.""" create_engine_callable: Callable[[str], Engine] = create_engine """Callable that creates an :class:`AsyncEngine ` instance or instance of its subclass. """ - session_config: SyncSessionConfig = field(default_factory=SyncSessionConfig) # pyright:ignore # noqa: PGH003 + session_config: SyncSessionConfig = field(default_factory=SyncSessionConfig) # pyright: ignore[reportIncompatibleVariableOverride] """Configuration options for the :class:`sessionmaker`.""" - session_maker_class: type[sessionmaker] = sessionmaker + session_maker_class: type[sessionmaker[Session]] = sessionmaker # pyright: ignore[reportIncompatibleVariableOverride] """Sessionmaker class to use.""" alembic_config: AlembicSyncConfig = field(default_factory=AlembicSyncConfig) """Configuration for the SQLAlchemy Alembic migrations. diff --git a/advanced_alchemy/filters.py b/advanced_alchemy/filters.py index 49c8e672..d3b243a1 100644 --- a/advanced_alchemy/filters.py +++ b/advanced_alchemy/filters.py @@ -327,7 +327,7 @@ def append_to_lambda_statement( model: type[ModelT], ) -> StatementLambdaElement: where_clause = self._operator(*self.get_search_clauses(model)) - statement += lambda s: s.where(where_clause) + statement += lambda s: s.where(where_clause) # pyright: ignore[reportUnknownLambdaType,reportUnknownMemberType] return statement diff --git a/advanced_alchemy/repository/__init__.py b/advanced_alchemy/repository/__init__.py index 63909287..f8b22896 100644 --- a/advanced_alchemy/repository/__init__.py +++ b/advanced_alchemy/repository/__init__.py @@ -1,17 +1,31 @@ from advanced_alchemy.repository._async import ( SQLAlchemyAsyncQueryRepository, SQLAlchemyAsyncRepository, + SQLAlchemyAsyncRepositoryProtocol, SQLAlchemyAsyncSlugRepository, + SQLAlchemyAsyncSlugRepositoryProtocol, ) from advanced_alchemy.repository._sync import ( SQLAlchemySyncQueryRepository, SQLAlchemySyncRepository, + SQLAlchemySyncRepositoryProtocol, SQLAlchemySyncSlugRepository, + SQLAlchemySyncSlugRepositoryProtocol, +) +from advanced_alchemy.repository._util import ( + FilterableRepositoryProtocol, + LoadSpec, + get_instrumented_attr, + model_from_dict, ) -from advanced_alchemy.repository._util import LoadSpec, get_instrumented_attr, model_from_dict __all__ = ( "SQLAlchemyAsyncRepository", + "SQLAlchemyAsyncRepositoryProtocol", + "SQLAlchemyAsyncSlugRepositoryProtocol", + "FilterableRepositoryProtocol", + "SQLAlchemySyncRepositoryProtocol", + "SQLAlchemySyncSlugRepositoryProtocol", "SQLAlchemyAsyncQueryRepository", "SQLAlchemyAsyncSlugRepository", "SQLAlchemySyncSlugRepository", diff --git a/advanced_alchemy/repository/_async.py b/advanced_alchemy/repository/_async.py index 54f8b539..c03323d6 100644 --- a/advanced_alchemy/repository/_async.py +++ b/advanced_alchemy/repository/_async.py @@ -2,7 +2,7 @@ import random import string -from typing import TYPE_CHECKING, Any, Final, Iterable, List, Literal, Sequence, cast +from typing import TYPE_CHECKING, Any, Final, Iterable, List, Literal, Protocol, Sequence, cast, runtime_checkable from sqlalchemy import ( Result, @@ -25,6 +25,7 @@ from advanced_alchemy.operations import Merge from advanced_alchemy.repository._util import ( FilterableRepository, + FilterableRepositoryProtocol, LoadSpec, get_abstract_loader_options, get_instrumented_attr, @@ -46,7 +47,263 @@ POSTGRES_VERSION_SUPPORTING_MERGE: Final = 15 -class SQLAlchemyAsyncRepository(FilterableRepository[ModelT]): +@runtime_checkable +class SQLAlchemyAsyncRepositoryProtocol(FilterableRepositoryProtocol[ModelT], Protocol[ModelT]): + """Base Protocol""" + + id_attribute: Any + match_fields: list[str] | str | None = None + statement: Select[tuple[ModelT]] | StatementLambdaElement + session: AsyncSession | async_scoped_session[AsyncSession] + auto_expunge: bool + auto_refresh: bool + auto_commit: bool + + def __init__( + self, + *, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + session: AsyncSession | async_scoped_session[AsyncSession], + auto_expunge: bool = False, + auto_refresh: bool = True, + auto_commit: bool = False, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> None: ... + + @classmethod + def get_id_attribute_value( + cls, + item: ModelT | type[ModelT], + id_attribute: str | InstrumentedAttribute[Any] | None = None, + ) -> Any: ... + + @classmethod + def set_id_attribute_value( + cls, + item_id: Any, + item: ModelT, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + ) -> ModelT: ... + + @staticmethod + def check_not_found(item_or_none: ModelT | None) -> ModelT: ... + + async def add( + self, + data: ModelT, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + auto_refresh: bool | None = None, + ) -> ModelT: ... + + async def add_many( + self, + data: list[ModelT], + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + ) -> Sequence[ModelT]: ... + + async def delete( + self, + item_id: Any, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> ModelT: ... + + async def delete_many( + self, + item_ids: list[Any], + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + chunk_size: int | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> Sequence[ModelT]: ... + + async def delete_where( + self, + *filters: StatementFilter | ColumnElement[bool], + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + sanity_check: bool = True, + **kwargs: Any, + ) -> Sequence[ModelT]: ... + + async def exists( + self, + *filters: StatementFilter | ColumnElement[bool], + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> bool: ... + + async def get( + self, + item_id: Any, + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> ModelT: ... + + async def get_one( + self, + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> ModelT: ... + + async def get_one_or_none( + self, + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> ModelT | None: ... + + async def get_or_upsert( + self, + match_fields: list[str] | str | None = None, + upsert: bool = True, + attribute_names: Iterable[str] | None = None, + with_for_update: bool | None = None, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + auto_refresh: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> tuple[ModelT, bool]: ... + + async def get_and_update( + self, + match_fields: list[str] | str | None = None, + attribute_names: Iterable[str] | None = None, + with_for_update: bool | None = None, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + auto_refresh: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> tuple[ModelT, bool]: ... + + async def count( + self, + *filters: StatementFilter | ColumnElement[bool], + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> int: ... + + async def update( + self, + data: ModelT, + attribute_names: Iterable[str] | None = None, + with_for_update: bool | None = None, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + auto_refresh: bool | None = None, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + ) -> ModelT: ... + + async def update_many( + self, + data: list[ModelT], + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> list[ModelT]: ... + + def _get_update_many_statement( + self, + model_type: type[ModelT], + supports_returning: bool, + loader_options: list[_AbstractLoad] | None, + execution_options: dict[str, Any] | None, + ) -> StatementLambdaElement: ... + + async def upsert( + self, + data: ModelT, + attribute_names: Iterable[str] | None = None, + with_for_update: bool | None = None, + auto_expunge: bool | None = None, + auto_commit: bool | None = None, + auto_refresh: bool | None = None, + match_fields: list[str] | str | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> ModelT: ... + + async def upsert_many( + self, + data: list[ModelT], + auto_expunge: bool | None = None, + auto_commit: bool | None = None, + no_merge: bool = False, + match_fields: list[str] | str | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> list[ModelT]: ... + + async def list_and_count( + self, + *filters: StatementFilter | ColumnElement[bool], + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + force_basic_query_mode: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> tuple[list[ModelT], int]: ... + + async def list( + self, + *filters: StatementFilter | ColumnElement[bool], + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> list[ModelT]: ... + + @classmethod + async def check_health(cls, session: AsyncSession | async_scoped_session[AsyncSession]) -> bool: ... + + +@runtime_checkable +class SQLAlchemyAsyncSlugRepositoryProtocol(SQLAlchemyAsyncRepositoryProtocol[ModelT], Protocol[ModelT]): + async def get_by_slug( + self, + slug: str, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> ModelT | None: ... + + async def get_available_slug( + self, + value_to_slugify: str, + **kwargs: Any, + ) -> str: ... + + +class SQLAlchemyAsyncRepository(SQLAlchemyAsyncRepositoryProtocol[ModelT], FilterableRepository[ModelT]): """SQLAlchemy based implementation of the repository interface.""" id_attribute: Any = "id" @@ -83,7 +340,6 @@ def __init__( **kwargs: Additional arguments. """ - super().__init__(**kwargs) self.auto_expunge = auto_expunge self.auto_refresh = auto_refresh self.auto_commit = auto_commit @@ -384,7 +640,7 @@ async def delete_where( with wrap_sqlalchemy_exception(): loader_options, _loader_options_have_wildcard = self._get_loader_options(load) model_type = self.model_type - statement = lambda_stmt(lambda: delete(model_type)) + statement = lambda_stmt(lambda: delete(model_type)) # pyright: ignore[reportUnknownLambdaType] if loader_options: statement = statement.options(*loader_options) if execution_options: @@ -1472,7 +1728,7 @@ def filter_collection_by_kwargs( have the property that their attribute named `key` has value equal to `value`. """ with wrap_sqlalchemy_exception(): - if isinstance(collection, Select): + if not isinstance(collection, StatementLambdaElement): collection = lambda_stmt(lambda: collection) collection += lambda s: s.filter_by(**kwargs) # pyright: ignore[reportUnknownLambdaType,reportUnknownMemberType] return collection @@ -1542,7 +1798,10 @@ async def _execute( return result -class SQLAlchemyAsyncSlugRepository(SQLAlchemyAsyncRepository[ModelT]): +class SQLAlchemyAsyncSlugRepository( + SQLAlchemyAsyncRepository[ModelT], + SQLAlchemyAsyncSlugRepositoryProtocol[ModelT], +): """Extends the repository to include slug model features..""" async def get_by_slug( diff --git a/advanced_alchemy/repository/_sync.py b/advanced_alchemy/repository/_sync.py index d7518b23..0ac59978 100644 --- a/advanced_alchemy/repository/_sync.py +++ b/advanced_alchemy/repository/_sync.py @@ -4,7 +4,7 @@ import random import string -from typing import TYPE_CHECKING, Any, Final, Iterable, List, Literal, Sequence, cast +from typing import TYPE_CHECKING, Any, Final, Iterable, List, Literal, Protocol, Sequence, cast, runtime_checkable from sqlalchemy import ( Result, @@ -27,6 +27,7 @@ from advanced_alchemy.operations import Merge from advanced_alchemy.repository._util import ( FilterableRepository, + FilterableRepositoryProtocol, LoadSpec, get_abstract_loader_options, get_instrumented_attr, @@ -47,7 +48,263 @@ POSTGRES_VERSION_SUPPORTING_MERGE: Final = 15 -class SQLAlchemySyncRepository(FilterableRepository[ModelT]): +@runtime_checkable +class SQLAlchemySyncRepositoryProtocol(FilterableRepositoryProtocol[ModelT], Protocol[ModelT]): + """Base Protocol""" + + id_attribute: Any + match_fields: list[str] | str | None = None + statement: Select[tuple[ModelT]] | StatementLambdaElement + session: Session | scoped_session[Session] + auto_expunge: bool + auto_refresh: bool + auto_commit: bool + + def __init__( + self, + *, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + session: Session | scoped_session[Session], + auto_expunge: bool = False, + auto_refresh: bool = True, + auto_commit: bool = False, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> None: ... + + @classmethod + def get_id_attribute_value( + cls, + item: ModelT | type[ModelT], + id_attribute: str | InstrumentedAttribute[Any] | None = None, + ) -> Any: ... + + @classmethod + def set_id_attribute_value( + cls, + item_id: Any, + item: ModelT, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + ) -> ModelT: ... + + @staticmethod + def check_not_found(item_or_none: ModelT | None) -> ModelT: ... + + def add( + self, + data: ModelT, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + auto_refresh: bool | None = None, + ) -> ModelT: ... + + def add_many( + self, + data: list[ModelT], + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + ) -> Sequence[ModelT]: ... + + def delete( + self, + item_id: Any, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> ModelT: ... + + def delete_many( + self, + item_ids: list[Any], + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + chunk_size: int | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> Sequence[ModelT]: ... + + def delete_where( + self, + *filters: StatementFilter | ColumnElement[bool], + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + sanity_check: bool = True, + **kwargs: Any, + ) -> Sequence[ModelT]: ... + + def exists( + self, + *filters: StatementFilter | ColumnElement[bool], + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> bool: ... + + def get( + self, + item_id: Any, + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> ModelT: ... + + def get_one( + self, + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> ModelT: ... + + def get_one_or_none( + self, + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> ModelT | None: ... + + def get_or_upsert( + self, + match_fields: list[str] | str | None = None, + upsert: bool = True, + attribute_names: Iterable[str] | None = None, + with_for_update: bool | None = None, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + auto_refresh: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> tuple[ModelT, bool]: ... + + def get_and_update( + self, + match_fields: list[str] | str | None = None, + attribute_names: Iterable[str] | None = None, + with_for_update: bool | None = None, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + auto_refresh: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> tuple[ModelT, bool]: ... + + def count( + self, + *filters: StatementFilter | ColumnElement[bool], + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> int: ... + + def update( + self, + data: ModelT, + attribute_names: Iterable[str] | None = None, + with_for_update: bool | None = None, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + auto_refresh: bool | None = None, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + ) -> ModelT: ... + + def update_many( + self, + data: list[ModelT], + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> list[ModelT]: ... + + def _get_update_many_statement( + self, + model_type: type[ModelT], + supports_returning: bool, + loader_options: list[_AbstractLoad] | None, + execution_options: dict[str, Any] | None, + ) -> StatementLambdaElement: ... + + def upsert( + self, + data: ModelT, + attribute_names: Iterable[str] | None = None, + with_for_update: bool | None = None, + auto_expunge: bool | None = None, + auto_commit: bool | None = None, + auto_refresh: bool | None = None, + match_fields: list[str] | str | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> ModelT: ... + + def upsert_many( + self, + data: list[ModelT], + auto_expunge: bool | None = None, + auto_commit: bool | None = None, + no_merge: bool = False, + match_fields: list[str] | str | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> list[ModelT]: ... + + def list_and_count( + self, + *filters: StatementFilter | ColumnElement[bool], + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + force_basic_query_mode: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> tuple[list[ModelT], int]: ... + + def list( + self, + *filters: StatementFilter | ColumnElement[bool], + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> list[ModelT]: ... + + @classmethod + def check_health(cls, session: Session | scoped_session[Session]) -> bool: ... + + +@runtime_checkable +class SQLAlchemySyncSlugRepositoryProtocol(SQLAlchemySyncRepositoryProtocol[ModelT], Protocol[ModelT]): + def get_by_slug( + self, + slug: str, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> ModelT | None: ... + + def get_available_slug( + self, + value_to_slugify: str, + **kwargs: Any, + ) -> str: ... + + +class SQLAlchemySyncRepository(SQLAlchemySyncRepositoryProtocol[ModelT], FilterableRepository[ModelT]): """SQLAlchemy based implementation of the repository interface.""" id_attribute: Any = "id" @@ -84,7 +341,6 @@ def __init__( **kwargs: Additional arguments. """ - super().__init__(**kwargs) self.auto_expunge = auto_expunge self.auto_refresh = auto_refresh self.auto_commit = auto_commit @@ -385,7 +641,7 @@ def delete_where( with wrap_sqlalchemy_exception(): loader_options, _loader_options_have_wildcard = self._get_loader_options(load) model_type = self.model_type - statement = lambda_stmt(lambda: delete(model_type)) + statement = lambda_stmt(lambda: delete(model_type)) # pyright: ignore[reportUnknownLambdaType] if loader_options: statement = statement.options(*loader_options) if execution_options: @@ -1473,7 +1729,7 @@ def filter_collection_by_kwargs( have the property that their attribute named `key` has value equal to `value`. """ with wrap_sqlalchemy_exception(): - if isinstance(collection, Select): + if not isinstance(collection, StatementLambdaElement): collection = lambda_stmt(lambda: collection) collection += lambda s: s.filter_by(**kwargs) # pyright: ignore[reportUnknownLambdaType,reportUnknownMemberType] return collection @@ -1543,7 +1799,10 @@ def _execute( return result -class SQLAlchemySyncSlugRepository(SQLAlchemySyncRepository[ModelT]): +class SQLAlchemySyncSlugRepository( + SQLAlchemySyncRepository[ModelT], + SQLAlchemySyncSlugRepositoryProtocol[ModelT], +): """Extends the repository to include slug model features..""" def get_by_slug( diff --git a/advanced_alchemy/repository/_util.py b/advanced_alchemy/repository/_util.py index 56459f1c..7483377c 100644 --- a/advanced_alchemy/repository/_util.py +++ b/advanced_alchemy/repository/_util.py @@ -1,6 +1,17 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Generic, Iterable, List, Literal, Sequence, Tuple, Union, cast +from typing import ( + TYPE_CHECKING, + Any, + Iterable, + List, + Literal, + Protocol, + Sequence, + Tuple, + Union, + cast, +) from sqlalchemy.orm import InstrumentedAttribute, MapperProperty, RelationshipProperty, joinedload, selectinload from sqlalchemy.orm.strategy_options import ( @@ -52,14 +63,14 @@ def get_instrumented_attr( return key -def model_from_dict(model: ModelT, **kwargs: Any) -> ModelT: +def model_from_dict(model: type[ModelT], **kwargs: Any) -> ModelT: """Return ORM Object from Dictionary.""" data = { column_name: kwargs[column_name] for column_name in model.__mapper__.columns.keys() # noqa: SIM118 # pyright: ignore[reportUnknownMemberType] if column_name in kwargs } - return cast("ModelT", model(**data)) # type: ignore[operator] + return model(**data) def get_abstract_loader_options( @@ -106,7 +117,11 @@ def get_abstract_loader_options( return (loads, options_have_wildcards) -class FilterableRepository(Generic[ModelT]): +class FilterableRepositoryProtocol(Protocol[ModelT]): + model_type: type[ModelT] + + +class FilterableRepository(FilterableRepositoryProtocol[ModelT]): model_type: type[ModelT] _prefer_any: bool = False prefer_any_dialects: tuple[str] | None = ("postgresql",) diff --git a/advanced_alchemy/repository/memory/_async.py b/advanced_alchemy/repository/memory/_async.py index 9809ae78..51d83386 100644 --- a/advanced_alchemy/repository/memory/_async.py +++ b/advanced_alchemy/repository/memory/_async.py @@ -3,11 +3,15 @@ import random import re import string -from typing import TYPE_CHECKING, Any, Generic, Iterable, List, cast, overload +from typing import TYPE_CHECKING, Any, Iterable, List, cast, overload from unittest.mock import create_autospec -from sqlalchemy import ColumnElement, Dialect, Select -from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession +from sqlalchemy import ( + ColumnElement, + Dialect, + Select, + StatementLambdaElement, +) from sqlalchemy.orm import InstrumentedAttribute from advanced_alchemy.exceptions import IntegrityError, NotFoundError, RepositoryError @@ -22,6 +26,7 @@ SearchFilter, StatementFilter, ) +from advanced_alchemy.repository._async import SQLAlchemyAsyncRepositoryProtocol, SQLAlchemyAsyncSlugRepositoryProtocol from advanced_alchemy.repository.memory.base import ( AnyObject, CollectionT, @@ -37,10 +42,16 @@ from collections import abc from datetime import datetime + from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio.scoping import async_scoped_session + from sqlalchemy.orm.strategy_options import _AbstractLoad # pyright: ignore[reportPrivateUsage] + + from advanced_alchemy.repository._util import ( + LoadSpec, + ) -class SQLAlchemyAsyncMockRepository(Generic[ModelT]): +class SQLAlchemyAsyncMockRepository(SQLAlchemyAsyncRepositoryProtocol[ModelT]): """In memory repository.""" __database__: SQLAlchemyMultiStore[ModelT] = SQLAlchemyMultiStore(SQLAlchemyInMemoryStore) @@ -63,10 +74,23 @@ class SQLAlchemyAsyncMockRepository(Generic[ModelT]): "execution_options", } - def __init__(self, **kwargs: Any) -> None: - self.session = create_autospec(AsyncSession, instance=True) - self.session.bind = create_autospec(AsyncEngine, instance=True) - self.statement: Select[Any] = create_autospec(Select, instance=True) + def __init__( + self, + *, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + session: AsyncSession | async_scoped_session[AsyncSession], + auto_expunge: bool = False, + auto_refresh: bool = True, + auto_commit: bool = False, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> None: + self.session = session + self.statement = create_autospec("Select[tuple[ModelT]]", instance=True) + self.auto_expunge = auto_expunge + self.auto_refresh = auto_refresh + self.auto_commit = auto_commit self._dialect: Dialect = create_autospec(Dialect, instance=True) self._dialect.name = "mock" self.__filtered_store__: InMemoryStore[ModelT] = self.__database__.store_type() @@ -347,17 +371,48 @@ def _find_one_or_raise_error(self, result: list[ModelT]) -> ModelT: raise IntegrityError(msg) return result[0] + def _get_update_many_statement( + self, + model_type: type[ModelT], + supports_returning: bool, + loader_options: list[_AbstractLoad] | None, + execution_options: dict[str, Any] | None, + ) -> StatementLambdaElement: + return cast("StatementLambdaElement", self.statement) + @classmethod async def check_health(cls, session: AsyncSession | async_scoped_session[AsyncSession]) -> bool: # noqa: ARG003 return True - async def get(self, item_id: Any, **_: Any) -> ModelT: + async def get( + self, + item_id: Any, + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> ModelT: return self._find_or_raise_not_found(item_id) - async def get_one(self, **kwargs: Any) -> ModelT: + async def get_one( + self, + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> ModelT: return self.check_not_found(await self.get_one_or_none(**kwargs)) - async def get_one_or_none(self, **kwargs: Any) -> ModelT | None: + async def get_one_or_none( + self, + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> ModelT | None: result = self._filter_result_by_kwargs(self.__collection__().list(), kwargs) if len(result) > 1: msg = "Multiple objects when one was expected" @@ -377,6 +432,13 @@ async def get_or_upsert( self, match_fields: list[str] | str | None = None, upsert: bool = True, + attribute_names: Iterable[str] | None = None, + with_for_update: bool | None = None, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + auto_refresh: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, **kwargs: Any, ) -> tuple[ModelT, bool]: kwargs_ = self._exclude_unused_kwargs(kwargs) @@ -400,7 +462,18 @@ async def get_or_upsert( existing = await self.update(existing) return existing, False - async def get_and_update(self, match_fields: list[str] | str | None = None, **kwargs: Any) -> tuple[ModelT, bool]: + async def get_and_update( + self, + match_fields: list[str] | str | None = None, + attribute_names: Iterable[str] | None = None, + with_for_update: bool | None = None, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + auto_refresh: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> tuple[ModelT, bool]: kwargs_ = self._exclude_unused_kwargs(kwargs) if match_fields := self._get_match_fields(match_fields=match_fields): match_filter = { @@ -429,7 +502,13 @@ async def count(self, *filters: StatementFilter | ColumnElement[bool], **kwargs: result = self._apply_filters(self.__collection__().list(), *filters) return len(self._filter_result_by_kwargs(result, kwargs)) - async def add(self, data: ModelT, **_: Any) -> ModelT: + async def add( + self, + data: ModelT, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + auto_refresh: bool | None = None, + ) -> ModelT: try: self.__database__.add(self.model_type, data) except KeyError as exc: @@ -437,25 +516,63 @@ async def add(self, data: ModelT, **_: Any) -> ModelT: raise IntegrityError(msg) from exc return data - async def add_many(self, data: list[ModelT], **_: Any) -> list[ModelT]: + async def add_many( + self, + data: list[ModelT], + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + ) -> list[ModelT]: for obj in data: await self.add(obj) return data - async def update(self, data: ModelT, **_: Any) -> ModelT: + async def update( + self, + data: ModelT, + attribute_names: Iterable[str] | None = None, + with_for_update: bool | None = None, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + auto_refresh: bool | None = None, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + ) -> ModelT: self._find_or_raise_not_found(self.__collection__().key(data)) return self.__collection__().update(data) - async def update_many(self, data: list[ModelT], **_: Any) -> list[ModelT]: + async def update_many( + self, + data: list[ModelT], + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> list[ModelT]: return [self.__collection__().update(obj) for obj in data if obj in self.__collection__()] - async def delete(self, item_id: Any, **_: Any) -> ModelT: + async def delete( + self, + item_id: Any, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> ModelT: try: return self._find_or_raise_not_found(item_id) finally: self.__collection__().remove(item_id) - async def delete_many(self, item_ids: list[Any], **_: Any) -> list[ModelT]: + async def delete_many( + self, + item_ids: list[Any], + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + chunk_size: int | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> list[ModelT]: deleted: list[ModelT] = [] for id_ in item_ids: if obj := self.__collection__().get_or_none(id_): @@ -463,25 +580,59 @@ async def delete_many(self, item_ids: list[Any], **_: Any) -> list[ModelT]: self.__collection__().remove(id_) return deleted - async def delete_where(self, *filters: StatementFilter | ColumnElement[bool], **kwargs: Any) -> list[ModelT]: + async def delete_where( + self, + *filters: StatementFilter | ColumnElement[bool], + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + sanity_check: bool = True, + **kwargs: Any, + ) -> list[ModelT]: result = self.__collection__().list() result = self._apply_filters(result, *filters) models = self._filter_result_by_kwargs(result, kwargs) item_ids = [getattr(model, self.id_attribute) for model in models] return await self.delete_many(item_ids=item_ids) - async def upsert(self, data: ModelT, **_: Any) -> ModelT: + async def upsert( + self, + data: ModelT, + attribute_names: Iterable[str] | None = None, + with_for_update: bool | None = None, + auto_expunge: bool | None = None, + auto_commit: bool | None = None, + auto_refresh: bool | None = None, + match_fields: list[str] | str | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> ModelT: # sourcery skip: assign-if-exp, reintroduce-else if data in self.__collection__(): return await self.update(data) return await self.add(data) - async def upsert_many(self, data: list[ModelT], **_: Any) -> list[ModelT]: + async def upsert_many( + self, + data: list[ModelT], + auto_expunge: bool | None = None, + auto_commit: bool | None = None, + no_merge: bool = False, + match_fields: list[str] | str | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> list[ModelT]: return [await self.upsert(item) for item in data] async def list_and_count( self, *filters: StatementFilter | ColumnElement[bool], + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + force_basic_query_mode: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, **kwargs: Any, ) -> tuple[list[ModelT], int]: return await self._list_and_count_basic(*filters, **kwargs) @@ -497,10 +648,15 @@ async def list(self, *filters: StatementFilter | ColumnElement[bool], **kwargs: return self._filter_result_by_kwargs(result, kwargs) -class SQLAlchemyAsyncMockSlugRepository(SQLAlchemyAsyncMockRepository[ModelT]): +class SQLAlchemyAsyncMockSlugRepository( + SQLAlchemyAsyncMockRepository[ModelT], + SQLAlchemyAsyncSlugRepositoryProtocol[ModelT], +): async def get_by_slug( self, slug: str, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, **kwargs: Any, ) -> ModelT | None: """Select record by slug value.""" diff --git a/advanced_alchemy/repository/memory/_sync.py b/advanced_alchemy/repository/memory/_sync.py index 88320577..044b0031 100644 --- a/advanced_alchemy/repository/memory/_sync.py +++ b/advanced_alchemy/repository/memory/_sync.py @@ -5,11 +5,15 @@ import random import re import string -from typing import TYPE_CHECKING, Any, Generic, Iterable, List, cast, overload +from typing import TYPE_CHECKING, Any, Iterable, List, cast, overload from unittest.mock import create_autospec -from sqlalchemy import ColumnElement, Dialect, Select -from sqlalchemy.ext.asyncio import AsyncEngine +from sqlalchemy import ( + ColumnElement, + Dialect, + Select, + StatementLambdaElement, +) from sqlalchemy.orm import InstrumentedAttribute, Session from advanced_alchemy.exceptions import IntegrityError, NotFoundError, RepositoryError @@ -24,6 +28,7 @@ SearchFilter, StatementFilter, ) +from advanced_alchemy.repository._sync import SQLAlchemySyncRepositoryProtocol, SQLAlchemySyncSlugRepositoryProtocol from advanced_alchemy.repository.memory.base import ( AnyObject, CollectionT, @@ -40,9 +45,14 @@ from datetime import datetime from sqlalchemy.orm.scoping import scoped_session + from sqlalchemy.orm.strategy_options import _AbstractLoad # pyright: ignore[reportPrivateUsage] + + from advanced_alchemy.repository._util import ( + LoadSpec, + ) -class SQLAlchemySyncMockRepository(Generic[ModelT]): +class SQLAlchemySyncMockRepository(SQLAlchemySyncRepositoryProtocol[ModelT]): """In memory repository.""" __database__: SQLAlchemyMultiStore[ModelT] = SQLAlchemyMultiStore(SQLAlchemyInMemoryStore) @@ -65,10 +75,23 @@ class SQLAlchemySyncMockRepository(Generic[ModelT]): "execution_options", } - def __init__(self, **kwargs: Any) -> None: - self.session = create_autospec(Session, instance=True) - self.session.bind = create_autospec(AsyncEngine, instance=True) - self.statement: Select[Any] = create_autospec(Select, instance=True) + def __init__( + self, + *, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + session: Session | scoped_session[Session], + auto_expunge: bool = False, + auto_refresh: bool = True, + auto_commit: bool = False, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> None: + self.session = session + self.statement = create_autospec("Select[tuple[ModelT]]", instance=True) + self.auto_expunge = auto_expunge + self.auto_refresh = auto_refresh + self.auto_commit = auto_commit self._dialect: Dialect = create_autospec(Dialect, instance=True) self._dialect.name = "mock" self.__filtered_store__: InMemoryStore[ModelT] = self.__database__.store_type() @@ -349,17 +372,48 @@ def _find_one_or_raise_error(self, result: list[ModelT]) -> ModelT: raise IntegrityError(msg) return result[0] + def _get_update_many_statement( + self, + model_type: type[ModelT], + supports_returning: bool, + loader_options: list[_AbstractLoad] | None, + execution_options: dict[str, Any] | None, + ) -> StatementLambdaElement: + return cast("StatementLambdaElement", self.statement) + @classmethod def check_health(cls, session: Session | scoped_session[Session]) -> bool: # noqa: ARG003 return True - def get(self, item_id: Any, **_: Any) -> ModelT: + def get( + self, + item_id: Any, + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> ModelT: return self._find_or_raise_not_found(item_id) - def get_one(self, **kwargs: Any) -> ModelT: + def get_one( + self, + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> ModelT: return self.check_not_found(self.get_one_or_none(**kwargs)) - def get_one_or_none(self, **kwargs: Any) -> ModelT | None: + def get_one_or_none( + self, + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> ModelT | None: result = self._filter_result_by_kwargs(self.__collection__().list(), kwargs) if len(result) > 1: msg = "Multiple objects when one was expected" @@ -379,6 +433,13 @@ def get_or_upsert( self, match_fields: list[str] | str | None = None, upsert: bool = True, + attribute_names: Iterable[str] | None = None, + with_for_update: bool | None = None, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + auto_refresh: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, **kwargs: Any, ) -> tuple[ModelT, bool]: kwargs_ = self._exclude_unused_kwargs(kwargs) @@ -402,7 +463,18 @@ def get_or_upsert( existing = self.update(existing) return existing, False - def get_and_update(self, match_fields: list[str] | str | None = None, **kwargs: Any) -> tuple[ModelT, bool]: + def get_and_update( + self, + match_fields: list[str] | str | None = None, + attribute_names: Iterable[str] | None = None, + with_for_update: bool | None = None, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + auto_refresh: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + **kwargs: Any, + ) -> tuple[ModelT, bool]: kwargs_ = self._exclude_unused_kwargs(kwargs) if match_fields := self._get_match_fields(match_fields=match_fields): match_filter = { @@ -431,7 +503,13 @@ def count(self, *filters: StatementFilter | ColumnElement[bool], **kwargs: Any) result = self._apply_filters(self.__collection__().list(), *filters) return len(self._filter_result_by_kwargs(result, kwargs)) - def add(self, data: ModelT, **_: Any) -> ModelT: + def add( + self, + data: ModelT, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + auto_refresh: bool | None = None, + ) -> ModelT: try: self.__database__.add(self.model_type, data) except KeyError as exc: @@ -439,25 +517,63 @@ def add(self, data: ModelT, **_: Any) -> ModelT: raise IntegrityError(msg) from exc return data - def add_many(self, data: list[ModelT], **_: Any) -> list[ModelT]: + def add_many( + self, + data: list[ModelT], + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + ) -> list[ModelT]: for obj in data: self.add(obj) return data - def update(self, data: ModelT, **_: Any) -> ModelT: + def update( + self, + data: ModelT, + attribute_names: Iterable[str] | None = None, + with_for_update: bool | None = None, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + auto_refresh: bool | None = None, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + ) -> ModelT: self._find_or_raise_not_found(self.__collection__().key(data)) return self.__collection__().update(data) - def update_many(self, data: list[ModelT], **_: Any) -> list[ModelT]: + def update_many( + self, + data: list[ModelT], + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> list[ModelT]: return [self.__collection__().update(obj) for obj in data if obj in self.__collection__()] - def delete(self, item_id: Any, **_: Any) -> ModelT: + def delete( + self, + item_id: Any, + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> ModelT: try: return self._find_or_raise_not_found(item_id) finally: self.__collection__().remove(item_id) - def delete_many(self, item_ids: list[Any], **_: Any) -> list[ModelT]: + def delete_many( + self, + item_ids: list[Any], + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + id_attribute: str | InstrumentedAttribute[Any] | None = None, + chunk_size: int | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> list[ModelT]: deleted: list[ModelT] = [] for id_ in item_ids: if obj := self.__collection__().get_or_none(id_): @@ -465,25 +581,59 @@ def delete_many(self, item_ids: list[Any], **_: Any) -> list[ModelT]: self.__collection__().remove(id_) return deleted - def delete_where(self, *filters: StatementFilter | ColumnElement[bool], **kwargs: Any) -> list[ModelT]: + def delete_where( + self, + *filters: StatementFilter | ColumnElement[bool], + auto_commit: bool | None = None, + auto_expunge: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + sanity_check: bool = True, + **kwargs: Any, + ) -> list[ModelT]: result = self.__collection__().list() result = self._apply_filters(result, *filters) models = self._filter_result_by_kwargs(result, kwargs) item_ids = [getattr(model, self.id_attribute) for model in models] return self.delete_many(item_ids=item_ids) - def upsert(self, data: ModelT, **_: Any) -> ModelT: + def upsert( + self, + data: ModelT, + attribute_names: Iterable[str] | None = None, + with_for_update: bool | None = None, + auto_expunge: bool | None = None, + auto_commit: bool | None = None, + auto_refresh: bool | None = None, + match_fields: list[str] | str | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> ModelT: # sourcery skip: assign-if-exp, reintroduce-else if data in self.__collection__(): return self.update(data) return self.add(data) - def upsert_many(self, data: list[ModelT], **_: Any) -> list[ModelT]: + def upsert_many( + self, + data: list[ModelT], + auto_expunge: bool | None = None, + auto_commit: bool | None = None, + no_merge: bool = False, + match_fields: list[str] | str | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, + ) -> list[ModelT]: return [self.upsert(item) for item in data] def list_and_count( self, *filters: StatementFilter | ColumnElement[bool], + auto_expunge: bool | None = None, + statement: Select[tuple[ModelT]] | StatementLambdaElement | None = None, + force_basic_query_mode: bool | None = None, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, **kwargs: Any, ) -> tuple[list[ModelT], int]: return self._list_and_count_basic(*filters, **kwargs) @@ -499,10 +649,15 @@ def list(self, *filters: StatementFilter | ColumnElement[bool], **kwargs: Any) - return self._filter_result_by_kwargs(result, kwargs) -class SQLAlchemySyncMockSlugRepository(SQLAlchemySyncMockRepository[ModelT]): +class SQLAlchemySyncMockSlugRepository( + SQLAlchemySyncMockRepository[ModelT], + SQLAlchemySyncSlugRepositoryProtocol[ModelT], +): def get_by_slug( self, slug: str, + load: LoadSpec | None = None, + execution_options: dict[str, Any] | None = None, **kwargs: Any, ) -> ModelT | None: """Select record by slug value.""" diff --git a/advanced_alchemy/repository/typing.py b/advanced_alchemy/repository/typing.py index 503904c7..260a81e5 100644 --- a/advanced_alchemy/repository/typing.py +++ b/advanced_alchemy/repository/typing.py @@ -23,7 +23,10 @@ RowMappingT = TypeVar("RowMappingT", bound="RowMapping") ModelOrRowMappingT = TypeVar("ModelOrRowMappingT", bound="Union[base.ModelProtocol, RowMapping]") SQLAlchemySyncRepositoryT = TypeVar("SQLAlchemySyncRepositoryT", bound="SQLAlchemySyncRepository[Any]") -SQLAlchemyAsyncRepositoryT = TypeVar("SQLAlchemyAsyncRepositoryT", bound="SQLAlchemyAsyncRepository[Any]") +SQLAlchemyAsyncRepositoryT = TypeVar( + "SQLAlchemyAsyncRepositoryT", + bound="SQLAlchemyAsyncRepository[Any]", +) class _MISSING: diff --git a/advanced_alchemy/service/_async.py b/advanced_alchemy/service/_async.py index 8dbb4f52..017820db 100644 --- a/advanced_alchemy/service/_async.py +++ b/advanced_alchemy/service/_async.py @@ -13,7 +13,11 @@ from typing_extensions import Self from advanced_alchemy.exceptions import AdvancedAlchemyError, RepositoryError -from advanced_alchemy.repository._async import SQLAlchemyAsyncQueryRepository +from advanced_alchemy.repository import ( + SQLAlchemyAsyncQueryRepository, + SQLAlchemyAsyncRepositoryProtocol, + SQLAlchemyAsyncSlugRepositoryProtocol, +) from advanced_alchemy.repository._util import ( LoadSpec, model_from_dict, @@ -32,8 +36,6 @@ from advanced_alchemy.config.asyncio import SQLAlchemyAsyncConfig from advanced_alchemy.filters import StatementFilter - from advanced_alchemy.repository import SQLAlchemyAsyncRepository - from advanced_alchemy.repository.memory import SQLAlchemyAsyncMockRepository class SQLAlchemyAsyncQueryService(ResultConverter): @@ -81,10 +83,10 @@ async def new( ) -class SQLAlchemyAsyncRepositoryReadService(Generic[ModelT], ResultConverter): +class SQLAlchemyAsyncRepositoryReadService(ResultConverter, Generic[ModelT]): """Service object that operates on a repository object.""" - repository_type: type[SQLAlchemyAsyncRepository[ModelT] | SQLAlchemyAsyncMockRepository[ModelT]] + repository_type: type[SQLAlchemyAsyncRepositoryProtocol[ModelT] | SQLAlchemyAsyncSlugRepositoryProtocol[ModelT]] match_fields: list[str] | str | None = None def __init__( @@ -275,7 +277,7 @@ async def to_model(self, data: ModelT | dict[str, Any], operation: str | None = Representation of created instances. """ if isinstance(data, dict): - return model_from_dict(model=self.repository.model_type, **data) # type: ignore # noqa: PGH003 + return model_from_dict(model=self.repository.model_type, **data) return data async def list_and_count( diff --git a/advanced_alchemy/service/_converters.py b/advanced_alchemy/service/_converters.py deleted file mode 100644 index d991d389..00000000 --- a/advanced_alchemy/service/_converters.py +++ /dev/null @@ -1,165 +0,0 @@ -from __future__ import annotations - -from functools import partial -from pathlib import Path, PurePath -from typing import ( - TYPE_CHECKING, - Any, - Callable, - List, - Sequence, - cast, -) -from uuid import UUID - -from advanced_alchemy.exceptions import AdvancedAlchemyError -from advanced_alchemy.filters import FilterTypes, LimitOffset -from advanced_alchemy.repository.typing import ModelOrRowMappingT -from advanced_alchemy.service.pagination import OffsetPagination - -if TYPE_CHECKING: - from sqlalchemy import ColumnElement - - from advanced_alchemy.service.typing import FilterTypeT, ModelDTOT - -try: - from msgspec import Struct, convert # pyright: ignore[reportAssignmentType] -except ImportError: # pragma: nocover - - class Struct: # type: ignore[no-redef] - """Placeholder Implementation""" - - def convert(*args: Any, **kwargs: Any) -> Any: # type: ignore[no-redef] # noqa: ARG001 - """Placeholder implementation""" - return {} - - -try: - from pydantic import BaseModel # pyright: ignore[reportAssignmentType] - from pydantic.type_adapter import TypeAdapter # pyright: ignore[reportAssignmentType] -except ImportError: # pragma: nocover - - class BaseModel: # type: ignore[no-redef] - """Placeholder Implementation""" - - class TypeAdapter: # type: ignore[no-redef] - """Placeholder Implementation""" - - -def _default_deserializer( - target_type: Any, - value: Any, - type_decoders: Sequence[tuple[Callable[[Any], bool], Callable[[Any, Any], Any]]] | None = None, -) -> Any: # pragma: no cover - """Transform values non-natively supported by ``msgspec`` - - Args: - target_type: Encountered type - value: Value to coerce - type_decoders: Optional sequence of type decoders - - Returns: - A ``msgspec``-supported type - """ - - if isinstance(value, target_type): - return value - - if type_decoders: - for predicate, decoder in type_decoders: - if predicate(target_type): - return decoder(target_type, value) - - if issubclass(target_type, (Path, PurePath, UUID)): - return target_type(value) - - msg = f"Unsupported type: {type(value)!r}" - raise TypeError(msg) - - -def _find_filter( - filter_type: type[FilterTypeT], - filters: Sequence[FilterTypes | ColumnElement[bool]] | Sequence[FilterTypes], -) -> FilterTypeT | None: - """Get the filter specified by filter type from the filters. - - Args: - filter_type: The type of filter to find. - filters: filter types to apply to the query - - Returns: - The match filter instance or None - """ - return next( - (cast("FilterTypeT | None", filter_) for filter_ in filters if isinstance(filter_, filter_type)), - None, - ) - - -def to_schema( - data: ModelOrRowMappingT | Sequence[ModelOrRowMappingT], - total: int | None = None, - filters: Sequence[FilterTypes | ColumnElement[bool]] | Sequence[FilterTypes] | None = None, - schema_type: type[ModelDTOT] | None = None, -) -> ModelOrRowMappingT | OffsetPagination[ModelOrRowMappingT] | ModelDTOT | OffsetPagination[ModelDTOT]: - if filters is None: - filters = [] - if schema_type is None: - if not issubclass(type(data), Sequence): - return cast("ModelOrRowMappingT", data) - limit_offset = _find_filter(LimitOffset, filters=filters) - total = total or len(data) # type: ignore[arg-type] - limit_offset = limit_offset if limit_offset is not None else LimitOffset(limit=len(data), offset=0) # type: ignore[arg-type] - return OffsetPagination[ModelOrRowMappingT]( - items=cast("List[ModelOrRowMappingT]", data), - limit=limit_offset.limit, - offset=limit_offset.offset, - total=total, - ) - if issubclass(schema_type, Struct): - if not isinstance(data, Sequence): - return convert( # type: ignore # noqa: PGH003 - obj=data, - type=schema_type, - from_attributes=True, - dec_hook=partial( - _default_deserializer, - type_decoders=[ - (lambda x: x is UUID, lambda t, v: t(v.hex)), - ], - ), - ) - limit_offset = _find_filter(LimitOffset, filters=filters) - total = total or len(data) - limit_offset = limit_offset if limit_offset is not None else LimitOffset(limit=len(data), offset=0) - return OffsetPagination[schema_type]( # type: ignore[valid-type] - items=convert( - obj=data, - type=List[schema_type], # type: ignore[valid-type] - from_attributes=True, - dec_hook=partial( - _default_deserializer, - type_decoders=[ - (lambda x: x is UUID, lambda t, v: t(v.hex)), - ], - ), - ), - limit=limit_offset.limit, - offset=limit_offset.offset, - total=total, - ) - - if issubclass(schema_type, BaseModel): - if not isinstance(data, Sequence): - return TypeAdapter(schema_type).validate_python(data, from_attributes=True) # type: ignore[return-value] # pyright: ignore[reportUnknownVariableType,reportUnknownMemberType,reportAttributeAccessIssue,reportCallIssue] - limit_offset = _find_filter(LimitOffset, filters=filters) - total = total if total else len(data) - limit_offset = limit_offset if limit_offset is not None else LimitOffset(limit=len(data), offset=0) - return OffsetPagination[schema_type]( # type: ignore[valid-type] - items=TypeAdapter(List[schema_type]).validate_python(data, from_attributes=True), # type: ignore[valid-type] - limit=limit_offset.limit, - offset=limit_offset.offset, - total=total, - ) - msg = "`schema_type` should be a valid Pydantic or Msgspec schema" - raise AdvancedAlchemyError(msg) diff --git a/advanced_alchemy/service/_sync.py b/advanced_alchemy/service/_sync.py index 4e19b4f8..e78cfd51 100644 --- a/advanced_alchemy/service/_sync.py +++ b/advanced_alchemy/service/_sync.py @@ -15,7 +15,11 @@ from typing_extensions import Self from advanced_alchemy.exceptions import AdvancedAlchemyError, RepositoryError -from advanced_alchemy.repository._sync import SQLAlchemySyncQueryRepository +from advanced_alchemy.repository import ( + SQLAlchemySyncQueryRepository, + SQLAlchemySyncRepositoryProtocol, + SQLAlchemySyncSlugRepositoryProtocol, +) from advanced_alchemy.repository._util import ( LoadSpec, model_from_dict, @@ -33,8 +37,6 @@ from advanced_alchemy.config.sync import SQLAlchemySyncConfig from advanced_alchemy.filters import StatementFilter - from advanced_alchemy.repository import SQLAlchemySyncRepository - from advanced_alchemy.repository.memory import SQLAlchemySyncMockRepository class SQLAlchemySyncQueryService(ResultConverter): @@ -82,10 +84,10 @@ def new( ) -class SQLAlchemySyncRepositoryReadService(Generic[ModelT], ResultConverter): +class SQLAlchemySyncRepositoryReadService(ResultConverter, Generic[ModelT]): """Service object that operates on a repository object.""" - repository_type: type[SQLAlchemySyncRepository[ModelT] | SQLAlchemySyncMockRepository[ModelT]] + repository_type: type[SQLAlchemySyncRepositoryProtocol[ModelT] | SQLAlchemySyncSlugRepositoryProtocol[ModelT]] match_fields: list[str] | str | None = None def __init__( @@ -276,7 +278,7 @@ def to_model(self, data: ModelT | dict[str, Any], operation: str | None = None) Representation of created instances. """ if isinstance(data, dict): - return model_from_dict(model=self.repository.model_type, **data) # type: ignore # noqa: PGH003 + return model_from_dict(model=self.repository.model_type, **data) return data def list_and_count( diff --git a/advanced_alchemy/service/_util.py b/advanced_alchemy/service/_util.py index 82d4f145..d609186f 100644 --- a/advanced_alchemy/service/_util.py +++ b/advanced_alchemy/service/_util.py @@ -6,21 +6,86 @@ from __future__ import annotations -from typing import TYPE_CHECKING, overload - -from advanced_alchemy.service._converters import to_schema +from functools import partial +from pathlib import Path, PurePath +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Sequence, + cast, + overload, +) +from uuid import UUID + +from advanced_alchemy.exceptions import AdvancedAlchemyError +from advanced_alchemy.filters import LimitOffset +from advanced_alchemy.repository.typing import ModelOrRowMappingT +from advanced_alchemy.service.pagination import OffsetPagination +from advanced_alchemy.service.typing import ( # type: ignore[attr-defined] + BaseModel, + ModelDTOT, + Struct, + TypeAdapter, + convert, +) if TYPE_CHECKING: - from collections.abc import Sequence - - from sqlalchemy import RowMapping - from sqlalchemy.sql import ColumnElement + from sqlalchemy import ColumnElement, RowMapping from advanced_alchemy.base import ModelProtocol - from advanced_alchemy.filters import FilterTypes - from advanced_alchemy.repository.typing import ModelOrRowMappingT - from advanced_alchemy.service.pagination import OffsetPagination - from advanced_alchemy.service.typing import ModelDTOT + from advanced_alchemy.filters import StatementFilter + from advanced_alchemy.service.typing import FilterTypeT + + +def _default_msgspec_deserializer( + target_type: Any, + value: Any, + type_decoders: Sequence[tuple[Callable[[Any], bool], Callable[[Any, Any], Any]]] | None = None, +) -> Any: # pragma: no cover + """Transform values non-natively supported by ``msgspec`` + + Args: + target_type: Encountered type + value: Value to coerce + type_decoders: Optional sequence of type decoders + + Returns: + A ``msgspec``-supported type + """ + + if isinstance(value, target_type): + return value + + if type_decoders: + for predicate, decoder in type_decoders: + if predicate(target_type): + return decoder(target_type, value) + + if issubclass(target_type, (Path, PurePath, UUID)): + return target_type(value) + + msg = f"Unsupported type: {type(value)!r}" + raise TypeError(msg) + + +def find_filter( + filter_type: type[FilterTypeT], + filters: Sequence[StatementFilter | ColumnElement[bool]] | Sequence[StatementFilter], +) -> FilterTypeT | None: + """Get the filter specified by filter type from the filters. + + Args: + filter_type: The type of filter to find. + filters: filter types to apply to the query + + Returns: + The match filter instance or None + """ + return next( + (cast("FilterTypeT | None", filter_) for filter_ in filters if isinstance(filter_, filter_type)), + None, + ) class ResultConverter: @@ -31,7 +96,7 @@ def to_schema( self, data: ModelOrRowMappingT, total: int | None = None, - filters: Sequence[FilterTypes | ColumnElement[bool]] | Sequence[FilterTypes] = ..., + filters: Sequence[StatementFilter | ColumnElement[bool]] | Sequence[StatementFilter] | None = None, ) -> ModelOrRowMappingT: ... @overload @@ -39,7 +104,27 @@ def to_schema( self, data: Sequence[ModelOrRowMappingT], total: int | None = None, - filters: Sequence[FilterTypes | ColumnElement[bool]] | Sequence[FilterTypes] | None = None, + filters: Sequence[StatementFilter | ColumnElement[bool]] | Sequence[StatementFilter] | None = None, + ) -> OffsetPagination[ModelOrRowMappingT]: ... + + @overload + def to_schema( + self, + data: ModelOrRowMappingT, + total: int | None = None, + filters: Sequence[StatementFilter | ColumnElement[bool]] | Sequence[StatementFilter] | None = None, + *, + schema_type: None = None, + ) -> ModelOrRowMappingT: ... + + @overload + def to_schema( + self, + data: Sequence[ModelOrRowMappingT], + total: int | None = None, + filters: Sequence[StatementFilter | ColumnElement[bool]] | Sequence[StatementFilter] | None = None, + *, + schema_type: None = None, ) -> OffsetPagination[ModelOrRowMappingT]: ... @overload @@ -47,7 +132,7 @@ def to_schema( self, data: ModelProtocol | RowMapping, total: int | None = None, - filters: Sequence[FilterTypes | ColumnElement[bool]] | Sequence[FilterTypes] | None = None, + filters: Sequence[StatementFilter | ColumnElement[bool]] | Sequence[StatementFilter] | None = None, *, schema_type: type[ModelDTOT], ) -> ModelDTOT: ... @@ -55,18 +140,23 @@ def to_schema( @overload def to_schema( self, - data: Sequence[ModelOrRowMappingT], + data: Sequence[ModelProtocol] | Sequence[RowMapping], total: int | None = None, - filters: Sequence[FilterTypes | ColumnElement[bool]] | Sequence[FilterTypes] | None = None, + filters: Sequence[StatementFilter | ColumnElement[bool]] | Sequence[StatementFilter] | None = None, *, schema_type: type[ModelDTOT], ) -> OffsetPagination[ModelDTOT]: ... def to_schema( self, - data: ModelOrRowMappingT | Sequence[ModelOrRowMappingT], + data: ModelOrRowMappingT + | Sequence[ModelOrRowMappingT] + | ModelProtocol + | Sequence[ModelProtocol] + | RowMapping + | Sequence[RowMapping], total: int | None = None, - filters: Sequence[FilterTypes | ColumnElement[bool]] | Sequence[FilterTypes] | None = None, + filters: Sequence[StatementFilter | ColumnElement[bool]] | Sequence[StatementFilter] | None = None, *, schema_type: type[ModelDTOT] | None = None, ) -> ModelOrRowMappingT | OffsetPagination[ModelOrRowMappingT] | ModelDTOT | OffsetPagination[ModelDTOT]: @@ -81,4 +171,67 @@ def to_schema( Returns: The list of instances retrieved from the repository. """ - return to_schema(data=data, total=total, filters=filters, schema_type=schema_type) + if filters is None: + filters = [] + if schema_type is None: + if not isinstance(data, Sequence): + return cast("ModelOrRowMappingT", data) + limit_offset = find_filter(LimitOffset, filters=filters) + total = total or len(data) + limit_offset = limit_offset if limit_offset is not None else LimitOffset(limit=len(data), offset=0) + return OffsetPagination[ModelOrRowMappingT]( + items=cast("Sequence[ModelOrRowMappingT]", data), + limit=limit_offset.limit, + offset=limit_offset.offset, + total=total, + ) + if issubclass(schema_type, Struct): + if not isinstance(data, Sequence): + return cast( + "ModelDTOT", + convert( + obj=data, + type=schema_type, + from_attributes=True, + dec_hook=partial( + _default_msgspec_deserializer, + type_decoders=[ + (lambda x: x is UUID, lambda t, v: t(v.hex)), + ], + ), + ), + ) + limit_offset = find_filter(LimitOffset, filters=filters) + total = total or len(data) + limit_offset = limit_offset if limit_offset is not None else LimitOffset(limit=len(data), offset=0) + return OffsetPagination[ModelDTOT]( + items=convert( + obj=data, + type=Sequence[schema_type], # type: ignore[valid-type] + from_attributes=True, + dec_hook=partial( + _default_msgspec_deserializer, + type_decoders=[ + (lambda x: x is UUID, lambda t, v: t(v.hex)), + ], + ), + ), + limit=limit_offset.limit, + offset=limit_offset.offset, + total=total, + ) + + if issubclass(schema_type, BaseModel): + if not isinstance(data, Sequence): + return cast("ModelDTOT", TypeAdapter(schema_type).validate_python(data, from_attributes=True)) # pyright: ignore[reportUnknownVariableType,reportUnknownMemberType,reportAttributeAccessIssue,reportCallIssue] + limit_offset = find_filter(LimitOffset, filters=filters) + total = total if total else len(data) + limit_offset = limit_offset if limit_offset is not None else LimitOffset(limit=len(data), offset=0) + return OffsetPagination[ModelDTOT]( + items=TypeAdapter(Sequence[schema_type]).validate_python(data, from_attributes=True), # type: ignore[valid-type] # pyright: ignore[reportUnknownArgumentType] + limit=limit_offset.limit, + offset=limit_offset.offset, + total=total, + ) + msg = "`schema_type` should be a valid Pydantic or Msgspec schema" + raise AdvancedAlchemyError(msg) diff --git a/advanced_alchemy/service/pagination.py b/advanced_alchemy/service/pagination.py index a4b3514e..48d00aa1 100644 --- a/advanced_alchemy/service/pagination.py +++ b/advanced_alchemy/service/pagination.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Generic, List, TypeVar +from typing import Generic, Sequence, TypeVar from uuid import UUID T = TypeVar("T") @@ -16,7 +16,7 @@ class OffsetPagination(Generic[T]): __slots__ = ("items", "limit", "offset", "total") - items: List[T] # noqa: UP006 + items: Sequence[T] """List of data being sent as part of the response.""" limit: int """Maximal number of items to send.""" diff --git a/advanced_alchemy/service/typing.py b/advanced_alchemy/service/typing.py index 10845a97..78f4f9e9 100644 --- a/advanced_alchemy/service/typing.py +++ b/advanced_alchemy/service/typing.py @@ -6,34 +6,55 @@ from __future__ import annotations -from typing import Any, TypeVar +from typing import ( + Any, + Generic, + Protocol, + TypeVar, + cast, +) from typing_extensions import TypeAlias -from advanced_alchemy.filters import FilterTypes +from advanced_alchemy.filters import StatementFilter # noqa: TCH001 from advanced_alchemy.repository.typing import ModelT # noqa: TCH001 try: - from msgspec import Struct # pyright: ignore[reportAssignmentType,reportUnknownVariableType,reportMissingImports] + from msgspec import Struct, convert # pyright: ignore[reportAssignmentType,reportUnusedImport] except ImportError: # pragma: nocover - class Struct: # type: ignore[no-redef] + class Struct(Protocol): # type: ignore[no-redef] # pragma: nocover """Placeholder Implementation""" + def convert(*args: Any, **kwargs: Any) -> Any: # type: ignore[no-redef] # noqa: ARG001 # pragma: nocover + """Placeholder implementation""" + return {} + try: - from pydantic import ( # pyright: ignore[reportAssignmentType,reportUnknownVariableType,reportMissingImports] - BaseModel, # pyright: ignore[reportAssignmentType,reportUnknownVariableType,reportMissingImports] - ) + from pydantic import BaseModel # pyright: ignore[reportAssignmentType] + from pydantic.type_adapter import TypeAdapter # pyright: ignore[reportUnusedImport, reportAssignmentType] except ImportError: # pragma: nocover - class BaseModel: # type: ignore[no-redef] + class BaseModel(Protocol): # type: ignore[no-redef] # pragma: nocover + """Placeholder Implementation""" + + T = TypeVar("T") # pragma: nocover + + class TypeAdapter(Generic[T]): # type: ignore[no-redef] # pragma: nocover """Placeholder Implementation""" + def __init__(self, *args: Any, **kwargs: Any) -> None: # pragma: nocover + super().__init__() + + def validate_python(self, data: Any, *args: Any, **kwargs: Any) -> T: # pragma: nocover + """Stub""" + return cast("T", data) + ModelDictT: TypeAlias = "dict[str, Any] | ModelT" ModelDictListT: TypeAlias = "list[ModelT | dict[str, Any]] | list[dict[str, Any]]" -FilterTypeT = TypeVar("FilterTypeT", bound=FilterTypes) +FilterTypeT = TypeVar("FilterTypeT", bound="StatementFilter") ModelDTOT = TypeVar("ModelDTOT", bound="Struct | BaseModel") PydanticModelDTOT = TypeVar("PydanticModelDTOT", bound="BaseModel") StructModelDTOT = TypeVar("StructModelDTOT", bound="Struct") diff --git a/advanced_alchemy/utils/dataclass.py b/advanced_alchemy/utils/dataclass.py index 5f7010b0..ce777f0c 100644 --- a/advanced_alchemy/utils/dataclass.py +++ b/advanced_alchemy/utils/dataclass.py @@ -114,7 +114,7 @@ def simple_asdict( Returns: A dictionary of key/value pairs. """ - ret = {} + ret: dict[str, Any] = {} for field in extract_dataclass_fields(obj, exclude_none, exclude_empty, exclude=exclude): value = getattr(obj, field.name) if is_dataclass_instance(value) and convert_nested: @@ -133,7 +133,7 @@ def is_dataclass_instance(obj: Any) -> TypeGuard[DataclassProtocol]: Returns: True if the object is a dataclass instance. """ - return hasattr(type(obj), "__dataclass_fields__") + return hasattr(type(obj), "__dataclass_fields__") # pyright: ignore[reportUnknownArgumentType] def is_dataclass_class(annotation: Any) -> TypeGuard[type[DataclassProtocol]]: diff --git a/pdm.lock b/pdm.lock index fa17ca41..019b1794 100644 --- a/pdm.lock +++ b/pdm.lock @@ -77,7 +77,7 @@ files = [ [[package]] name = "annotated-types" -version = "0.6.0" +version = "0.7.0" requires_python = ">=3.8" summary = "Reusable constraint types to use with typing.Annotated" groups = ["dev", "extensions"] @@ -85,13 +85,13 @@ dependencies = [ "typing-extensions>=4.0.0; python_version < \"3.9\"", ] files = [ - {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, - {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] [[package]] name = "anyio" -version = "4.3.0" +version = "4.4.0" requires_python = ">=3.8" summary = "High level compatibility layer for multiple asynchronous event loop implementations" groups = ["dev", "extensions", "test"] @@ -102,8 +102,8 @@ dependencies = [ "typing-extensions>=4.1; python_version < \"3.11\"", ] files = [ - {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, - {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, + {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, + {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, ] [[package]] @@ -487,13 +487,13 @@ files = [ [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.6.2" requires_python = ">=3.6" summary = "Python package for providing Mozilla's CA Bundle." groups = ["dev", "docs", "extensions", "test"] files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, + {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, ] [[package]] @@ -812,7 +812,7 @@ files = [ [[package]] name = "cryptography" -version = "42.0.7" +version = "42.0.8" requires_python = ">=3.7" summary = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." groups = ["dev"] @@ -820,49 +820,52 @@ dependencies = [ "cffi>=1.12; platform_python_implementation != \"PyPy\"", ] files = [ - {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477"}, - {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a"}, - {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604"}, - {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8"}, - {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55"}, - {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc"}, - {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2"}, - {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13"}, - {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da"}, - {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7"}, - {file = "cryptography-42.0.7-cp37-abi3-win32.whl", hash = "sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b"}, - {file = "cryptography-42.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678"}, - {file = "cryptography-42.0.7-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4"}, - {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858"}, - {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785"}, - {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda"}, - {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9"}, - {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e"}, - {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f"}, - {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1"}, - {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886"}, - {file = "cryptography-42.0.7-cp39-abi3-win32.whl", hash = "sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda"}, - {file = "cryptography-42.0.7-cp39-abi3-win_amd64.whl", hash = "sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b"}, - {file = "cryptography-42.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82"}, - {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60"}, - {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd"}, - {file = "cryptography-42.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582"}, - {file = "cryptography-42.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562"}, - {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14"}, - {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9"}, - {file = "cryptography-42.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68"}, - {file = "cryptography-42.0.7.tar.gz", hash = "sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2"}, + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e"}, + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7"}, + {file = "cryptography-42.0.8-cp37-abi3-win32.whl", hash = "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2"}, + {file = "cryptography-42.0.8-cp37-abi3-win_amd64.whl", hash = "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba"}, + {file = "cryptography-42.0.8-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14"}, + {file = "cryptography-42.0.8-cp39-abi3-win32.whl", hash = "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c"}, + {file = "cryptography-42.0.8-cp39-abi3-win_amd64.whl", hash = "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad"}, + {file = "cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2"}, ] [[package]] name = "cssutils" -version = "2.10.2" +version = "2.11.1" requires_python = ">=3.8" summary = "A CSS Cascading Style Sheets library for Python" groups = ["docs"] +dependencies = [ + "more-itertools", +] files = [ - {file = "cssutils-2.10.2-py3-none-any.whl", hash = "sha256:4ad7d2f29270b22cf199f65a6b5e795f2c3130f3b9fb50c3d45e5054ef86e41a"}, - {file = "cssutils-2.10.2.tar.gz", hash = "sha256:93cf92a350b1c123b17feff042e212f94d960975a3ed145743d84ebe8ccec7ab"}, + {file = "cssutils-2.11.1-py3-none-any.whl", hash = "sha256:a67bfdfdff4f3867fab43698ec4897c1a828eca5973f4073321b3bccaf1199b1"}, + {file = "cssutils-2.11.1.tar.gz", hash = "sha256:0563a76513b6af6eebbe788c3bf3d01c920e46b3f90c8416738c5cfc773ff8e2"}, ] [[package]] @@ -914,7 +917,7 @@ files = [ [[package]] name = "domdf-python-tools" -version = "3.8.0.post2" +version = "3.8.1" requires_python = ">=3.6" summary = "Helpful functions for Python 🐍 🛠️" groups = ["docs"] @@ -924,63 +927,63 @@ dependencies = [ "typing-extensions>=3.7.4.1", ] files = [ - {file = "domdf_python_tools-3.8.0.post2-py3-none-any.whl", hash = "sha256:ad2c763c8d00850a7fa92ad95e9891a1918281ea25322c4dbb1734fd32f905dd"}, - {file = "domdf_python_tools-3.8.0.post2.tar.gz", hash = "sha256:a1fd255ea29f767b08de462d2da39d360262304389227d980bc307ee8aa3366a"}, + {file = "domdf_python_tools-3.8.1-py3-none-any.whl", hash = "sha256:9821d76505e16c0fab60b37be90b8acf401c9604f7119cf6bce314f848461c7e"}, + {file = "domdf_python_tools-3.8.1.tar.gz", hash = "sha256:f45e34cf4d3363af59c32da28a9de9480ed916eff06bd0cf9a1644b6b460fb88"}, ] [[package]] name = "duckdb" -version = "0.10.3" +version = "1.0.0" requires_python = ">=3.7.0" summary = "DuckDB in-process database" groups = ["dev"] files = [ - {file = "duckdb-0.10.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd25cc8d001c09a19340739ba59d33e12a81ab285b7a6bed37169655e1cefb31"}, - {file = "duckdb-0.10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f9259c637b917ca0f4c63887e8d9b35ec248f5d987c886dfc4229d66a791009"}, - {file = "duckdb-0.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b48f5f1542f1e4b184e6b4fc188f497be8b9c48127867e7d9a5f4a3e334f88b0"}, - {file = "duckdb-0.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e327f7a3951ea154bb56e3fef7da889e790bd9a67ca3c36afc1beb17d3feb6d6"}, - {file = "duckdb-0.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d8b20ed67da004b4481973f4254fd79a0e5af957d2382eac8624b5c527ec48c"}, - {file = "duckdb-0.10.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d37680b8d7be04e4709db3a66c8b3eb7ceba2a5276574903528632f2b2cc2e60"}, - {file = "duckdb-0.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d34b86d6a2a6dfe8bb757f90bfe7101a3bd9e3022bf19dbddfa4b32680d26a9"}, - {file = "duckdb-0.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:73b1cb283ca0f6576dc18183fd315b4e487a545667ffebbf50b08eb4e8cdc143"}, - {file = "duckdb-0.10.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d917dde19fcec8cadcbef1f23946e85dee626ddc133e1e3f6551f15a61a03c61"}, - {file = "duckdb-0.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46757e0cf5f44b4cb820c48a34f339a9ccf83b43d525d44947273a585a4ed822"}, - {file = "duckdb-0.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:338c14d8ac53ac4aa9ec03b6f1325ecfe609ceeb72565124d489cb07f8a1e4eb"}, - {file = "duckdb-0.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:651fcb429602b79a3cf76b662a39e93e9c3e6650f7018258f4af344c816dab72"}, - {file = "duckdb-0.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3ae3c73b98b6215dab93cc9bc936b94aed55b53c34ba01dec863c5cab9f8e25"}, - {file = "duckdb-0.10.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56429b2cfe70e367fb818c2be19f59ce2f6b080c8382c4d10b4f90ba81f774e9"}, - {file = "duckdb-0.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b46c02c2e39e3676b1bb0dc7720b8aa953734de4fd1b762e6d7375fbeb1b63af"}, - {file = "duckdb-0.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:bcd460feef56575af2c2443d7394d405a164c409e9794a4d94cb5fdaa24a0ba4"}, - {file = "duckdb-0.10.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e229a7c6361afbb0d0ab29b1b398c10921263c52957aefe3ace99b0426fdb91e"}, - {file = "duckdb-0.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:732b1d3b6b17bf2f32ea696b9afc9e033493c5a3b783c292ca4b0ee7cc7b0e66"}, - {file = "duckdb-0.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5380d4db11fec5021389fb85d614680dc12757ef7c5881262742250e0b58c75"}, - {file = "duckdb-0.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:468a4e0c0b13c55f84972b1110060d1b0f854ffeb5900a178a775259ec1562db"}, - {file = "duckdb-0.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fa1e7ff8d18d71defa84e79f5c86aa25d3be80d7cb7bc259a322de6d7cc72da"}, - {file = "duckdb-0.10.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed1063ed97c02e9cf2e7fd1d280de2d1e243d72268330f45344c69c7ce438a01"}, - {file = "duckdb-0.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:22f2aad5bb49c007f3bfcd3e81fdedbc16a2ae41f2915fc278724ca494128b0c"}, - {file = "duckdb-0.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:8f9e2bb00a048eb70b73a494bdc868ce7549b342f7ffec88192a78e5a4e164bd"}, - {file = "duckdb-0.10.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d75d67024fc22c8edfd47747c8550fb3c34fb1cbcbfd567e94939ffd9c9e3ca7"}, - {file = "duckdb-0.10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f3796e9507c02d0ddbba2e84c994fae131da567ce3d9cbb4cbcd32fadc5fbb26"}, - {file = "duckdb-0.10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:78e539d85ebd84e3e87ec44d28ad912ca4ca444fe705794e0de9be3dd5550c11"}, - {file = "duckdb-0.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a99b67ac674b4de32073e9bc604b9c2273d399325181ff50b436c6da17bf00a"}, - {file = "duckdb-0.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1209a354a763758c4017a1f6a9f9b154a83bed4458287af9f71d84664ddb86b6"}, - {file = "duckdb-0.10.3-cp38-cp38-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b735cea64aab39b67c136ab3a571dbf834067f8472ba2f8bf0341bc91bea820"}, - {file = "duckdb-0.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:816ffb9f758ed98eb02199d9321d592d7a32a6cb6aa31930f4337eb22cfc64e2"}, - {file = "duckdb-0.10.3-cp38-cp38-win_amd64.whl", hash = "sha256:1631184b94c3dc38b13bce4045bf3ae7e1b0ecbfbb8771eb8d751d8ffe1b59b3"}, - {file = "duckdb-0.10.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fb98c35fc8dd65043bc08a2414dd9f59c680d7e8656295b8969f3f2061f26c52"}, - {file = "duckdb-0.10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e75c9f5b6a92b2a6816605c001d30790f6d67ce627a2b848d4d6040686efdf9"}, - {file = "duckdb-0.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ae786eddf1c2fd003466e13393b9348a44b6061af6fe7bcb380a64cac24e7df7"}, - {file = "duckdb-0.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9387da7b7973707b0dea2588749660dd5dd724273222680e985a2dd36787668"}, - {file = "duckdb-0.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:538f943bf9fa8a3a7c4fafa05f21a69539d2c8a68e557233cbe9d989ae232899"}, - {file = "duckdb-0.10.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6930608f35025a73eb94252964f9f19dd68cf2aaa471da3982cf6694866cfa63"}, - {file = "duckdb-0.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:03bc54a9cde5490918aad82d7d2a34290e3dfb78d5b889c6626625c0f141272a"}, - {file = "duckdb-0.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:372b6e3901d85108cafe5df03c872dfb6f0dbff66165a0cf46c47246c1957aa0"}, - {file = "duckdb-0.10.3.tar.gz", hash = "sha256:c5bd84a92bc708d3a6adffe1f554b94c6e76c795826daaaf482afc3d9c636971"}, + {file = "duckdb-1.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:4a8ce2d1f9e1c23b9bab3ae4ca7997e9822e21563ff8f646992663f66d050211"}, + {file = "duckdb-1.0.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:19797670f20f430196e48d25d082a264b66150c264c1e8eae8e22c64c2c5f3f5"}, + {file = "duckdb-1.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:b71c342090fe117b35d866a91ad6bffce61cd6ff3e0cff4003f93fc1506da0d8"}, + {file = "duckdb-1.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25dd69f44ad212c35ae2ea736b0e643ea2b70f204b8dff483af1491b0e2a4cec"}, + {file = "duckdb-1.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8da5f293ecb4f99daa9a9352c5fd1312a6ab02b464653a0c3a25ab7065c45d4d"}, + {file = "duckdb-1.0.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3207936da9967ddbb60644ec291eb934d5819b08169bc35d08b2dedbe7068c60"}, + {file = "duckdb-1.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1128d6c9c33e883b1f5df6b57c1eb46b7ab1baf2650912d77ee769aaa05111f9"}, + {file = "duckdb-1.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:02310d263474d0ac238646677feff47190ffb82544c018b2ff732a4cb462c6ef"}, + {file = "duckdb-1.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:75586791ab2702719c284157b65ecefe12d0cca9041da474391896ddd9aa71a4"}, + {file = "duckdb-1.0.0-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:83bb415fc7994e641344f3489e40430ce083b78963cb1057bf714ac3a58da3ba"}, + {file = "duckdb-1.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:bee2e0b415074e84c5a2cefd91f6b5ebeb4283e7196ba4ef65175a7cef298b57"}, + {file = "duckdb-1.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa5a4110d2a499312609544ad0be61e85a5cdad90e5b6d75ad16b300bf075b90"}, + {file = "duckdb-1.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fa389e6a382d4707b5f3d1bc2087895925ebb92b77e9fe3bfb23c9b98372fdc"}, + {file = "duckdb-1.0.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ede6f5277dd851f1a4586b0c78dc93f6c26da45e12b23ee0e88c76519cbdbe0"}, + {file = "duckdb-1.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0b88cdbc0d5c3e3d7545a341784dc6cafd90fc035f17b2f04bf1e870c68456e5"}, + {file = "duckdb-1.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd1693cdd15375156f7fff4745debc14e5c54928589f67b87fb8eace9880c370"}, + {file = "duckdb-1.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:c65a7fe8a8ce21b985356ee3ec0c3d3b3b2234e288e64b4cfb03356dbe6e5583"}, + {file = "duckdb-1.0.0-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:e5a8eda554379b3a43b07bad00968acc14dd3e518c9fbe8f128b484cf95e3d16"}, + {file = "duckdb-1.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:a1b6acdd54c4a7b43bd7cb584975a1b2ff88ea1a31607a2b734b17960e7d3088"}, + {file = "duckdb-1.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a677bb1b6a8e7cab4a19874249d8144296e6e39dae38fce66a80f26d15e670df"}, + {file = "duckdb-1.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:752e9d412b0a2871bf615a2ede54be494c6dc289d076974eefbf3af28129c759"}, + {file = "duckdb-1.0.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3aadb99d098c5e32d00dc09421bc63a47134a6a0de9d7cd6abf21780b678663c"}, + {file = "duckdb-1.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83b7091d4da3e9301c4f9378833f5ffe934fb1ad2b387b439ee067b2c10c8bb0"}, + {file = "duckdb-1.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:6a8058d0148b544694cb5ea331db44f6c2a00a7b03776cc4dd1470735c3d5ff7"}, + {file = "duckdb-1.0.0-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:1bea713c1925918714328da76e79a1f7651b2b503511498ccf5e007a7e67d49e"}, + {file = "duckdb-1.0.0-cp38-cp38-macosx_12_0_universal2.whl", hash = "sha256:bfe67f3bcf181edbf6f918b8c963eb060e6aa26697d86590da4edc5707205450"}, + {file = "duckdb-1.0.0-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:dbc6093a75242f002be1d96a6ace3fdf1d002c813e67baff52112e899de9292f"}, + {file = "duckdb-1.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba1881a2b11c507cee18f8fd9ef10100be066fddaa2c20fba1f9a664245cd6d8"}, + {file = "duckdb-1.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:445d0bb35087c522705c724a75f9f1c13f1eb017305b694d2686218d653c8142"}, + {file = "duckdb-1.0.0-cp38-cp38-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:224553432e84432ffb9684f33206572477049b371ce68cc313a01e214f2fbdda"}, + {file = "duckdb-1.0.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d3914032e47c4e76636ad986d466b63fdea65e37be8a6dfc484ed3f462c4fde4"}, + {file = "duckdb-1.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:af9128a2eb7e1bb50cd2c2020d825fb2946fdad0a2558920cd5411d998999334"}, + {file = "duckdb-1.0.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:dd2659a5dbc0df0de68f617a605bf12fe4da85ba24f67c08730984a0892087e8"}, + {file = "duckdb-1.0.0-cp39-cp39-macosx_12_0_universal2.whl", hash = "sha256:ac5a4afb0bc20725e734e0b2c17e99a274de4801aff0d4e765d276b99dad6d90"}, + {file = "duckdb-1.0.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:2c5a53bee3668d6e84c0536164589d5127b23d298e4c443d83f55e4150fafe61"}, + {file = "duckdb-1.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b980713244d7708b25ee0a73de0c65f0e5521c47a0e907f5e1b933d79d972ef6"}, + {file = "duckdb-1.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cbd4f9fe7b7a56eff96c3f4d6778770dd370469ca2212eddbae5dd63749db5"}, + {file = "duckdb-1.0.0-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed228167c5d49888c5ef36f6f9cbf65011c2daf9dcb53ea8aa7a041ce567b3e4"}, + {file = "duckdb-1.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:46d8395fbcea7231fd5032a250b673cc99352fef349b718a23dea2c0dd2b8dec"}, + {file = "duckdb-1.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:6ad1fc1a4d57e7616944166a5f9417bdbca1ea65c490797e3786e3a42e162d8a"}, + {file = "duckdb-1.0.0.tar.gz", hash = "sha256:a2a059b77bc7d5b76ae9d88e267372deff19c291048d59450c431e166233d453"}, ] [[package]] name = "duckdb-engine" -version = "0.12.1" +version = "0.13.0" requires_python = "<4,>=3.8" summary = "SQLAlchemy driver for duckdb" groups = ["dev"] @@ -990,8 +993,8 @@ dependencies = [ "sqlalchemy>=1.3.22", ] files = [ - {file = "duckdb_engine-0.12.1-py3-none-any.whl", hash = "sha256:2449b61db4f7cf928ebbbb6b897a839bc3df353878533c1300818aa9094ee0e8"}, - {file = "duckdb_engine-0.12.1.tar.gz", hash = "sha256:8ee3b672f5d3abc85ea6290cde59a58a72462cdd671826db4b7d3d50d8ab49ba"}, + {file = "duckdb_engine-0.13.0-py3-none-any.whl", hash = "sha256:58ac839d6730d07c11148123aa16001623d780bcd7369eabc4bf1594b79ab0d0"}, + {file = "duckdb_engine-0.13.0.tar.gz", hash = "sha256:8e934091b2922a60c2faaac057f88b669fe4925cc26a3adaea8be7f27ca60bab"}, ] [[package]] @@ -1043,7 +1046,7 @@ files = [ [[package]] name = "faker" -version = "25.2.0" +version = "25.8.0" requires_python = ">=3.8" summary = "Faker is a Python package that generates fake data for you." groups = ["dev", "extensions"] @@ -1051,8 +1054,8 @@ dependencies = [ "python-dateutil>=2.4", ] files = [ - {file = "Faker-25.2.0-py3-none-any.whl", hash = "sha256:cfe97c4857c4c36ee32ea4aaabef884895992e209bae4cbd26807cf3e05c6918"}, - {file = "Faker-25.2.0.tar.gz", hash = "sha256:45b84f47ff1ef86e3d1a8d11583ca871ecf6730fad0660edadc02576583a2423"}, + {file = "Faker-25.8.0-py3-none-any.whl", hash = "sha256:4c40b34a9c569018d4f9d6366d71a4da8a883d5ddf2b23197be5370f29b7e1b6"}, + {file = "Faker-25.8.0.tar.gz", hash = "sha256:bdec5f2fb057d244ebef6e0ed318fea4dcbdf32c3a1a010766fc45f5d68fc68d"}, ] [[package]] @@ -1081,18 +1084,16 @@ files = [ [[package]] name = "fastapi-cli" -version = "0.0.3" +version = "0.0.4" requires_python = ">=3.8" summary = "Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀" groups = ["dev", "extensions"] dependencies = [ - "fastapi", "typer>=0.12.3", - "uvicorn[standard]>=0.15.0", ] files = [ - {file = "fastapi_cli-0.0.3-py3-none-any.whl", hash = "sha256:ae233115f729945479044917d949095e829d2d84f56f55ce1ca17627872825a5"}, - {file = "fastapi_cli-0.0.3.tar.gz", hash = "sha256:3b6e4d2c4daee940fb8db59ebbfd60a72c4b962bcf593e263e4cc69da4ea3d7f"}, + {file = "fastapi_cli-0.0.4-py3-none-any.whl", hash = "sha256:a2552f3a7ae64058cdbb530be6fa6dbfc975dc165e4fa66d224c3d396e25e809"}, + {file = "fastapi_cli-0.0.4.tar.gz", hash = "sha256:e2e9ffaffc1f7767f488d6da34b6f5a377751c996f397902eb6abb99a67bde32"}, ] [[package]] @@ -1142,21 +1143,21 @@ files = [ [[package]] name = "git-cliff" -version = "2.2.2" +version = "2.3.0" requires_python = ">=3.7" summary = "A highly customizable changelog generator ⛰️" groups = ["docs"] files = [ - {file = "git_cliff-2.2.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3a3af4cb34b11875c9c2939d5eae41def33e1cc2bb837069ab61ac2bdec47ad2"}, - {file = "git_cliff-2.2.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8f631e193ea668d4527cf50a258a8e1597058e46021e3ec44a33b4c343e0fee9"}, - {file = "git_cliff-2.2.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65e7aac3357c75aaaccfe737a38772cada41d734ca86c2ca496ca60fa1e3f2ee"}, - {file = "git_cliff-2.2.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a59730f49e212c4583a967e9ddea7f6c507cee01d461f4c4602a366999e3be73"}, - {file = "git_cliff-2.2.2-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:0f411a58df635ce779d0048db19d347a0eeb37f3d061e08335659c587518b4cc"}, - {file = "git_cliff-2.2.2-py3-none-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6def651f6c44b527f33822c8e063383f2596eab7f0796d06213a31c22302d2c4"}, - {file = "git_cliff-2.2.2-py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:aa3888fc975a0d55a8434ef0ad0d481e918ad761c191a64b29f3ed9337843538"}, - {file = "git_cliff-2.2.2-py3-none-win32.whl", hash = "sha256:96155b457dec726dcebed2c5a3fecb3994c7991a4ce0779515907fd53945c13d"}, - {file = "git_cliff-2.2.2-py3-none-win_amd64.whl", hash = "sha256:fa5017b3e204f5cc7c7b4442a082fa9ae6f40736e55353649fcc12e6bb7632be"}, - {file = "git_cliff-2.2.2.tar.gz", hash = "sha256:1ff34e6659cac3f9fa01645e1edf5bce333d22738a6579be7b8e79c636a6c25f"}, + {file = "git_cliff-2.3.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:fd88441c1894cee85fdd501dbbe92cf9c00b66ad880bdfd05c0df91847ed7159"}, + {file = "git_cliff-2.3.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:014dd730091a2222b0748272c07a282f7093ec5da45f8a9fabcfb2771a13fa10"}, + {file = "git_cliff-2.3.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d45b17f94345e022ddc2796bfb5b78ad94d005730affb3ba2a42dd8f8e3d29c0"}, + {file = "git_cliff-2.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca115c75f49251c539e1234d6a034275c9bdc5f828ed72b8ac31b5137ff1c7bb"}, + {file = "git_cliff-2.3.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:ebbfa6f1346d92d5304fb5e23be096a9ecfcba1bd4195eb411884e98fd497316"}, + {file = "git_cliff-2.3.0-py3-none-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5c8f7a7d57daaeb8529fe716ebfd5a6434a5b752ae1f40b51e7a5ac6a73eca33"}, + {file = "git_cliff-2.3.0-py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:033f07e33039f60c525e500e6e9b1bfadd8a1b487d9a0c0cabe33359c4163715"}, + {file = "git_cliff-2.3.0-py3-none-win32.whl", hash = "sha256:b1e564c0519aa5727bb17e42712bc23a195bd264264eb60e0e15fd380859d708"}, + {file = "git_cliff-2.3.0-py3-none-win_amd64.whl", hash = "sha256:606c7c135d6daf628b4a369da895a0a4405cde2da500e1ed692ad69a22cacbc6"}, + {file = "git_cliff-2.3.0.tar.gz", hash = "sha256:1fd5bae53b2deee5ba855131d7fa8930e77a272de8fa1376b5aab2706494c979"}, ] [[package]] @@ -1198,7 +1199,7 @@ files = [ [[package]] name = "google-auth" -version = "2.29.0" +version = "2.30.0" requires_python = ">=3.7" summary = "Google Authentication Library" groups = ["dev"] @@ -1208,8 +1209,8 @@ dependencies = [ "rsa<5,>=3.1.4", ] files = [ - {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, - {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, + {file = "google-auth-2.30.0.tar.gz", hash = "sha256:ab630a1320f6720909ad76a7dbdb6841cdf5c66b328d690027e4867bdfb16688"}, + {file = "google_auth-2.30.0-py2.py3-none-any.whl", hash = "sha256:8df7da660f62757388b8a7f249df13549b3373f24388cb5d2f1dd91cc18180b5"}, ] [[package]] @@ -1229,7 +1230,7 @@ files = [ [[package]] name = "google-cloud-spanner" -version = "3.46.0" +version = "3.47.0" requires_python = ">=3.7" summary = "Google Cloud Spanner API client library" groups = ["dev"] @@ -1240,42 +1241,41 @@ dependencies = [ "grpc-interceptor>=0.15.4", "proto-plus<2.0.0dev,>=1.22.0", "proto-plus<2.0.0dev,>=1.22.2; python_version >= \"3.11\"", - "protobuf!=3.20.0,!=3.20.1,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<5.0.0dev,>=3.19.5", + "protobuf!=3.20.0,!=3.20.1,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<5.0.0dev,>=3.20.2", "sqlparse>=0.4.4", ] files = [ - {file = "google-cloud-spanner-3.46.0.tar.gz", hash = "sha256:b0987f54ba9e55b8a6800bc8daf74d16fbc5094e90f83ad0178027f20820d06f"}, - {file = "google_cloud_spanner-3.46.0-py2.py3-none-any.whl", hash = "sha256:8680a29c90c519194ec7bc5732cae17c2435f147fba4220efc731632e9ba9230"}, + {file = "google_cloud_spanner-3.47.0-py2.py3-none-any.whl", hash = "sha256:b05fa4ffccf08af3f32c3c5c77edd1486a76ddbd42db8ad8865830fab3cfda80"}, ] [[package]] name = "googleapis-common-protos" -version = "1.63.0" +version = "1.63.1" requires_python = ">=3.7" summary = "Common protobufs used in Google APIs" groups = ["dev"] dependencies = [ - "protobuf!=3.20.0,!=3.20.1,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<5.0.0.dev0,>=3.19.5", + "protobuf!=3.20.0,!=3.20.1,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<6.0.0.dev0,>=3.19.5", ] files = [ - {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, - {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, + {file = "googleapis-common-protos-1.63.1.tar.gz", hash = "sha256:c6442f7a0a6b2a80369457d79e6672bb7dcbaab88e0848302497e3ec80780a6a"}, + {file = "googleapis_common_protos-1.63.1-py2.py3-none-any.whl", hash = "sha256:0e1c2cdfcbc354b76e4a211a35ea35d6926a835cba1377073c4861db904a1877"}, ] [[package]] name = "googleapis-common-protos" -version = "1.63.0" +version = "1.63.1" extras = ["grpc"] requires_python = ">=3.7" summary = "Common protobufs used in Google APIs" groups = ["dev"] dependencies = [ - "googleapis-common-protos==1.63.0", + "googleapis-common-protos==1.63.1", "grpcio<2.0.0.dev0,>=1.44.0", ] files = [ - {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, - {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, + {file = "googleapis-common-protos-1.63.1.tar.gz", hash = "sha256:c6442f7a0a6b2a80369457d79e6672bb7dcbaab88e0848302497e3ec80780a6a"}, + {file = "googleapis_common_protos-1.63.1-py2.py3-none-any.whl", hash = "sha256:0e1c2cdfcbc354b76e4a211a35ea35d6926a835cba1377073c4861db904a1877"}, ] [[package]] @@ -1368,57 +1368,57 @@ files = [ [[package]] name = "grpcio" -version = "1.63.0" +version = "1.64.1" requires_python = ">=3.8" summary = "HTTP/2-based RPC framework" groups = ["dev"] files = [ - {file = "grpcio-1.63.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:2e93aca840c29d4ab5db93f94ed0a0ca899e241f2e8aec6334ab3575dc46125c"}, - {file = "grpcio-1.63.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:91b73d3f1340fefa1e1716c8c1ec9930c676d6b10a3513ab6c26004cb02d8b3f"}, - {file = "grpcio-1.63.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b3afbd9d6827fa6f475a4f91db55e441113f6d3eb9b7ebb8fb806e5bb6d6bd0d"}, - {file = "grpcio-1.63.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f3f6883ce54a7a5f47db43289a0a4c776487912de1a0e2cc83fdaec9685cc9f"}, - {file = "grpcio-1.63.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf8dae9cc0412cb86c8de5a8f3be395c5119a370f3ce2e69c8b7d46bb9872c8d"}, - {file = "grpcio-1.63.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:08e1559fd3b3b4468486b26b0af64a3904a8dbc78d8d936af9c1cf9636eb3e8b"}, - {file = "grpcio-1.63.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5c039ef01516039fa39da8a8a43a95b64e288f79f42a17e6c2904a02a319b357"}, - {file = "grpcio-1.63.0-cp310-cp310-win32.whl", hash = "sha256:ad2ac8903b2eae071055a927ef74121ed52d69468e91d9bcbd028bd0e554be6d"}, - {file = "grpcio-1.63.0-cp310-cp310-win_amd64.whl", hash = "sha256:b2e44f59316716532a993ca2966636df6fbe7be4ab6f099de6815570ebe4383a"}, - {file = "grpcio-1.63.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:f28f8b2db7b86c77916829d64ab21ff49a9d8289ea1564a2b2a3a8ed9ffcccd3"}, - {file = "grpcio-1.63.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:65bf975639a1f93bee63ca60d2e4951f1b543f498d581869922910a476ead2f5"}, - {file = "grpcio-1.63.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b5194775fec7dc3dbd6a935102bb156cd2c35efe1685b0a46c67b927c74f0cfb"}, - {file = "grpcio-1.63.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4cbb2100ee46d024c45920d16e888ee5d3cf47c66e316210bc236d5bebc42b3"}, - {file = "grpcio-1.63.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ff737cf29b5b801619f10e59b581869e32f400159e8b12d7a97e7e3bdeee6a2"}, - {file = "grpcio-1.63.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cd1e68776262dd44dedd7381b1a0ad09d9930ffb405f737d64f505eb7f77d6c7"}, - {file = "grpcio-1.63.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:93f45f27f516548e23e4ec3fbab21b060416007dbe768a111fc4611464cc773f"}, - {file = "grpcio-1.63.0-cp311-cp311-win32.whl", hash = "sha256:878b1d88d0137df60e6b09b74cdb73db123f9579232c8456f53e9abc4f62eb3c"}, - {file = "grpcio-1.63.0-cp311-cp311-win_amd64.whl", hash = "sha256:756fed02dacd24e8f488f295a913f250b56b98fb793f41d5b2de6c44fb762434"}, - {file = "grpcio-1.63.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:93a46794cc96c3a674cdfb59ef9ce84d46185fe9421baf2268ccb556f8f81f57"}, - {file = "grpcio-1.63.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a7b19dfc74d0be7032ca1eda0ed545e582ee46cd65c162f9e9fc6b26ef827dc6"}, - {file = "grpcio-1.63.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8064d986d3a64ba21e498b9a376cbc5d6ab2e8ab0e288d39f266f0fca169b90d"}, - {file = "grpcio-1.63.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:219bb1848cd2c90348c79ed0a6b0ea51866bc7e72fa6e205e459fedab5770172"}, - {file = "grpcio-1.63.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2d60cd1d58817bc5985fae6168d8b5655c4981d448d0f5b6194bbcc038090d2"}, - {file = "grpcio-1.63.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9e350cb096e5c67832e9b6e018cf8a0d2a53b2a958f6251615173165269a91b0"}, - {file = "grpcio-1.63.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:56cdf96ff82e3cc90dbe8bac260352993f23e8e256e063c327b6cf9c88daf7a9"}, - {file = "grpcio-1.63.0-cp312-cp312-win32.whl", hash = "sha256:3a6d1f9ea965e750db7b4ee6f9fdef5fdf135abe8a249e75d84b0a3e0c668a1b"}, - {file = "grpcio-1.63.0-cp312-cp312-win_amd64.whl", hash = "sha256:d2497769895bb03efe3187fb1888fc20e98a5f18b3d14b606167dacda5789434"}, - {file = "grpcio-1.63.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:fdf348ae69c6ff484402cfdb14e18c1b0054ac2420079d575c53a60b9b2853ae"}, - {file = "grpcio-1.63.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a3abfe0b0f6798dedd2e9e92e881d9acd0fdb62ae27dcbbfa7654a57e24060c0"}, - {file = "grpcio-1.63.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6ef0ad92873672a2a3767cb827b64741c363ebaa27e7f21659e4e31f4d750280"}, - {file = "grpcio-1.63.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b416252ac5588d9dfb8a30a191451adbf534e9ce5f56bb02cd193f12d8845b7f"}, - {file = "grpcio-1.63.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3b77eaefc74d7eb861d3ffbdf91b50a1bb1639514ebe764c47773b833fa2d91"}, - {file = "grpcio-1.63.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b005292369d9c1f80bf70c1db1c17c6c342da7576f1c689e8eee4fb0c256af85"}, - {file = "grpcio-1.63.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cdcda1156dcc41e042d1e899ba1f5c2e9f3cd7625b3d6ebfa619806a4c1aadda"}, - {file = "grpcio-1.63.0-cp38-cp38-win32.whl", hash = "sha256:01799e8649f9e94ba7db1aeb3452188048b0019dc37696b0f5ce212c87c560c3"}, - {file = "grpcio-1.63.0-cp38-cp38-win_amd64.whl", hash = "sha256:6a1a3642d76f887aa4009d92f71eb37809abceb3b7b5a1eec9c554a246f20e3a"}, - {file = "grpcio-1.63.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:75f701ff645858a2b16bc8c9fc68af215a8bb2d5a9b647448129de6e85d52bce"}, - {file = "grpcio-1.63.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cacdef0348a08e475a721967f48206a2254a1b26ee7637638d9e081761a5ba86"}, - {file = "grpcio-1.63.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:0697563d1d84d6985e40ec5ec596ff41b52abb3fd91ec240e8cb44a63b895094"}, - {file = "grpcio-1.63.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6426e1fb92d006e47476d42b8f240c1d916a6d4423c5258ccc5b105e43438f61"}, - {file = "grpcio-1.63.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48cee31bc5f5a31fb2f3b573764bd563aaa5472342860edcc7039525b53e46a"}, - {file = "grpcio-1.63.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:50344663068041b34a992c19c600236e7abb42d6ec32567916b87b4c8b8833b3"}, - {file = "grpcio-1.63.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:259e11932230d70ef24a21b9fb5bb947eb4703f57865a404054400ee92f42f5d"}, - {file = "grpcio-1.63.0-cp39-cp39-win32.whl", hash = "sha256:a44624aad77bf8ca198c55af811fd28f2b3eaf0a50ec5b57b06c034416ef2d0a"}, - {file = "grpcio-1.63.0-cp39-cp39-win_amd64.whl", hash = "sha256:166e5c460e5d7d4656ff9e63b13e1f6029b122104c1633d5f37eaea348d7356d"}, - {file = "grpcio-1.63.0.tar.gz", hash = "sha256:f3023e14805c61bc439fb40ca545ac3d5740ce66120a678a3c6c2c55b70343d1"}, + {file = "grpcio-1.64.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:55697ecec192bc3f2f3cc13a295ab670f51de29884ca9ae6cd6247df55df2502"}, + {file = "grpcio-1.64.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3b64ae304c175671efdaa7ec9ae2cc36996b681eb63ca39c464958396697daff"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:bac71b4b28bc9af61efcdc7630b166440bbfbaa80940c9a697271b5e1dabbc61"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c024ffc22d6dc59000faf8ad781696d81e8e38f4078cb0f2630b4a3cf231a90"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7cd5c1325f6808b8ae31657d281aadb2a51ac11ab081ae335f4f7fc44c1721d"}, + {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0a2813093ddb27418a4c99f9b1c223fab0b053157176a64cc9db0f4557b69bd9"}, + {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2981c7365a9353f9b5c864595c510c983251b1ab403e05b1ccc70a3d9541a73b"}, + {file = "grpcio-1.64.1-cp310-cp310-win32.whl", hash = "sha256:1262402af5a511c245c3ae918167eca57342c72320dffae5d9b51840c4b2f86d"}, + {file = "grpcio-1.64.1-cp310-cp310-win_amd64.whl", hash = "sha256:19264fc964576ddb065368cae953f8d0514ecc6cb3da8903766d9fb9d4554c33"}, + {file = "grpcio-1.64.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:58b1041e7c870bb30ee41d3090cbd6f0851f30ae4eb68228955d973d3efa2e61"}, + {file = "grpcio-1.64.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bbc5b1d78a7822b0a84c6f8917faa986c1a744e65d762ef6d8be9d75677af2ca"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5841dd1f284bd1b3d8a6eca3a7f062b06f1eec09b184397e1d1d43447e89a7ae"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8caee47e970b92b3dd948371230fcceb80d3f2277b3bf7fbd7c0564e7d39068e"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73819689c169417a4f978e562d24f2def2be75739c4bed1992435d007819da1b"}, + {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6503b64c8b2dfad299749cad1b595c650c91e5b2c8a1b775380fcf8d2cbba1e9"}, + {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1de403fc1305fd96cfa75e83be3dee8538f2413a6b1685b8452301c7ba33c294"}, + {file = "grpcio-1.64.1-cp311-cp311-win32.whl", hash = "sha256:d4d29cc612e1332237877dfa7fe687157973aab1d63bd0f84cf06692f04c0367"}, + {file = "grpcio-1.64.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e56462b05a6f860b72f0fa50dca06d5b26543a4e88d0396259a07dc30f4e5aa"}, + {file = "grpcio-1.64.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:4657d24c8063e6095f850b68f2d1ba3b39f2b287a38242dcabc166453e950c59"}, + {file = "grpcio-1.64.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:62b4e6eb7bf901719fce0ca83e3ed474ae5022bb3827b0a501e056458c51c0a1"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:ee73a2f5ca4ba44fa33b4d7d2c71e2c8a9e9f78d53f6507ad68e7d2ad5f64a22"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:198908f9b22e2672a998870355e226a725aeab327ac4e6ff3a1399792ece4762"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39b9d0acaa8d835a6566c640f48b50054f422d03e77e49716d4c4e8e279665a1"}, + {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5e42634a989c3aa6049f132266faf6b949ec2a6f7d302dbb5c15395b77d757eb"}, + {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1a82e0b9b3022799c336e1fc0f6210adc019ae84efb7321d668129d28ee1efb"}, + {file = "grpcio-1.64.1-cp312-cp312-win32.whl", hash = "sha256:55260032b95c49bee69a423c2f5365baa9369d2f7d233e933564d8a47b893027"}, + {file = "grpcio-1.64.1-cp312-cp312-win_amd64.whl", hash = "sha256:c1a786ac592b47573a5bb7e35665c08064a5d77ab88a076eec11f8ae86b3e3f6"}, + {file = "grpcio-1.64.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:a011ac6c03cfe162ff2b727bcb530567826cec85eb8d4ad2bfb4bd023287a52d"}, + {file = "grpcio-1.64.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4d6dab6124225496010bd22690f2d9bd35c7cbb267b3f14e7a3eb05c911325d4"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:a5e771d0252e871ce194d0fdcafd13971f1aae0ddacc5f25615030d5df55c3a2"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c3c1b90ab93fed424e454e93c0ed0b9d552bdf1b0929712b094f5ecfe7a23ad"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20405cb8b13fd779135df23fabadc53b86522d0f1cba8cca0e87968587f50650"}, + {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0cc79c982ccb2feec8aad0e8fb0d168bcbca85bc77b080d0d3c5f2f15c24ea8f"}, + {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a3a035c37ce7565b8f4f35ff683a4db34d24e53dc487e47438e434eb3f701b2a"}, + {file = "grpcio-1.64.1-cp38-cp38-win32.whl", hash = "sha256:1257b76748612aca0f89beec7fa0615727fd6f2a1ad580a9638816a4b2eb18fd"}, + {file = "grpcio-1.64.1-cp38-cp38-win_amd64.whl", hash = "sha256:0a12ddb1678ebc6a84ec6b0487feac020ee2b1659cbe69b80f06dbffdb249122"}, + {file = "grpcio-1.64.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:75dbbf415026d2862192fe1b28d71f209e2fd87079d98470db90bebe57b33179"}, + {file = "grpcio-1.64.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e3d9f8d1221baa0ced7ec7322a981e28deb23749c76eeeb3d33e18b72935ab62"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:5f8b75f64d5d324c565b263c67dbe4f0af595635bbdd93bb1a88189fc62ed2e5"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c84ad903d0d94311a2b7eea608da163dace97c5fe9412ea311e72c3684925602"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:940e3ec884520155f68a3b712d045e077d61c520a195d1a5932c531f11883489"}, + {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f10193c69fc9d3d726e83bbf0f3d316f1847c3071c8c93d8090cf5f326b14309"}, + {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac15b6c2c80a4d1338b04d42a02d376a53395ddf0ec9ab157cbaf44191f3ffdd"}, + {file = "grpcio-1.64.1-cp39-cp39-win32.whl", hash = "sha256:03b43d0ccf99c557ec671c7dede64f023c7da9bb632ac65dbc57f166e4970040"}, + {file = "grpcio-1.64.1-cp39-cp39-win_amd64.whl", hash = "sha256:ed6091fa0adcc7e4ff944090cf203a52da35c37a130efa564ded02b7aff63bcd"}, + {file = "grpcio-1.64.1.tar.gz", hash = "sha256:8d51dd1c59d5fa0f34266b80a3805ec29a1f26425c2a54736133f6d87fc4968a"}, ] [[package]] @@ -1666,7 +1666,7 @@ files = [ [[package]] name = "litestar" -version = "2.8.3" +version = "2.9.0" requires_python = "<4.0,>=3.8" summary = "Litestar - A production-ready, highly performant, extensible ASGI API Framework" groups = ["dev", "extensions"] @@ -1686,8 +1686,8 @@ dependencies = [ "typing-extensions", ] files = [ - {file = "litestar-2.8.3-py3-none-any.whl", hash = "sha256:bef5e8592b1afba24306f9a5834d84cef89826b12f58a97d3099cf49e2f3767d"}, - {file = "litestar-2.8.3.tar.gz", hash = "sha256:3764eef3885b331bfb15c478a2b9e2bc1980741ff58bd5512d599436f669549c"}, + {file = "litestar-2.9.0-py3-none-any.whl", hash = "sha256:25235ad99f08807e633347fa509dcf5989153bde9b6a82e521b1f056af33becc"}, + {file = "litestar-2.9.0.tar.gz", hash = "sha256:bea384c9ddae74bcb0d96ecb6dc1b51f8a13d50173cdfd56bfd2627ede505154"}, ] [[package]] @@ -1711,20 +1711,20 @@ dependencies = [ [[package]] name = "litestar" -version = "2.8.3" +version = "2.9.0" extras = ["cli"] requires_python = "<4.0,>=3.8" summary = "Litestar - A production-ready, highly performant, extensible ASGI API Framework" groups = ["extensions"] dependencies = [ "jsbeautifier", - "litestar==2.8.3", + "litestar==2.9.0", "uvicorn[standard]", "uvloop>=0.18.0; sys_platform != \"win32\"", ] files = [ - {file = "litestar-2.8.3-py3-none-any.whl", hash = "sha256:bef5e8592b1afba24306f9a5834d84cef89826b12f58a97d3099cf49e2f3767d"}, - {file = "litestar-2.8.3.tar.gz", hash = "sha256:3764eef3885b331bfb15c478a2b9e2bc1980741ff58bd5512d599436f669549c"}, + {file = "litestar-2.9.0-py3-none-any.whl", hash = "sha256:25235ad99f08807e633347fa509dcf5989153bde9b6a82e521b1f056af33becc"}, + {file = "litestar-2.9.0.tar.gz", hash = "sha256:bea384c9ddae74bcb0d96ecb6dc1b51f8a13d50173cdfd56bfd2627ede505154"}, ] [[package]] @@ -1743,7 +1743,7 @@ files = [ [[package]] name = "mako" -version = "1.3.3" +version = "1.3.5" requires_python = ">=3.8" summary = "A super-fast templating language that borrows the best ideas from the existing templating languages." groups = ["default", "dev"] @@ -1751,8 +1751,8 @@ dependencies = [ "MarkupSafe>=0.9.2", ] files = [ - {file = "Mako-1.3.3-py3-none-any.whl", hash = "sha256:5324b88089a8978bf76d1629774fcc2f1c07b82acdf00f4c5dd8ceadfffc4b40"}, - {file = "Mako-1.3.3.tar.gz", hash = "sha256:e16c01d9ab9c11f7290eef1cfefc093fb5a45ee4a3da09e2fec2e4d1bae54e73"}, + {file = "Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a"}, + {file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"}, ] [[package]] @@ -1840,6 +1840,17 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "more-itertools" +version = "10.2.0" +requires_python = ">=3.8" +summary = "More routines for operating on iterables, beyond itertools" +groups = ["docs"] +files = [ + {file = "more-itertools-10.2.0.tar.gz", hash = "sha256:8fccb480c43d3e99a00087634c06dd02b0d50fbf088b380de5a41a015ec239e1"}, + {file = "more_itertools-10.2.0-py3-none-any.whl", hash = "sha256:686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684"}, +] + [[package]] name = "msgpack" version = "1.0.8" @@ -2101,16 +2112,13 @@ files = [ [[package]] name = "nodeenv" -version = "1.8.0" -requires_python = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +version = "1.9.1" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" summary = "Node.js virtual environment builder" groups = ["linting"] -dependencies = [ - "setuptools", -] files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, ] [[package]] @@ -2230,13 +2238,13 @@ files = [ [[package]] name = "platformdirs" -version = "4.2.1" +version = "4.2.2" requires_python = ">=3.8" summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." groups = ["docs", "linting"] files = [ - {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, - {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [[package]] @@ -2536,23 +2544,23 @@ files = [ [[package]] name = "pydantic" -version = "2.7.1" +version = "2.7.3" requires_python = ">=3.8" summary = "Data validation using Python type hints" groups = ["dev", "extensions"] dependencies = [ "annotated-types>=0.4.0", - "pydantic-core==2.18.2", + "pydantic-core==2.18.4", "typing-extensions>=4.6.1", ] files = [ - {file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"}, - {file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"}, + {file = "pydantic-2.7.3-py3-none-any.whl", hash = "sha256:ea91b002777bf643bb20dd717c028ec43216b24a6001a280f83877fd2655d0b4"}, + {file = "pydantic-2.7.3.tar.gz", hash = "sha256:c46c76a40bb1296728d7a8b99aa73dd70a48c3510111ff290034f860c99c419e"}, ] [[package]] name = "pydantic-core" -version = "2.18.2" +version = "2.18.4" requires_python = ">=3.8" summary = "Core functionality for Pydantic validation and serialization" groups = ["dev", "extensions"] @@ -2560,85 +2568,85 @@ dependencies = [ "typing-extensions!=4.7.0,>=4.6.0", ] files = [ - {file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"}, - {file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"}, - {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"}, - {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"}, - {file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"}, - {file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"}, - {file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"}, - {file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"}, - {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"}, - {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"}, - {file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"}, - {file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"}, - {file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"}, - {file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"}, - {file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"}, - {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"}, - {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"}, - {file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"}, - {file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"}, - {file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"}, - {file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"}, - {file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"}, - {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"}, - {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"}, - {file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"}, - {file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"}, - {file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"}, - {file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"}, - {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"}, - {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"}, - {file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"}, - {file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"}, - {file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"}, + {file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"}, + {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"}, + {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"}, + {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"}, + {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"}, + {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"}, + {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"}, + {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"}, + {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"}, + {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"}, + {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"}, + {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"}, + {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"}, + {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"}, + {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"}, + {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"}, + {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"}, + {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"}, + {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"}, + {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"}, + {file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"}, + {file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"}, + {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"}, + {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"}, + {file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"}, + {file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"}, + {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"}, + {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"}, + {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"}, + {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"}, + {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"}, + {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"}, + {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"}, ] [[package]] @@ -2694,7 +2702,7 @@ files = [ [[package]] name = "pyright" -version = "1.1.365" +version = "1.1.366" requires_python = ">=3.7" summary = "Command line wrapper for pyright" groups = ["linting"] @@ -2702,8 +2710,8 @@ dependencies = [ "nodeenv>=1.6.0", ] files = [ - {file = "pyright-1.1.365-py3-none-any.whl", hash = "sha256:194d767a039f9034376b7ec8423841880ac6efdd061f3e283b4ad9fcd484a659"}, - {file = "pyright-1.1.365.tar.gz", hash = "sha256:d7e69000939aed4bf823707086c30c84c005bdd39fac2dfb370f0e5be16c2ef2"}, + {file = "pyright-1.1.366-py3-none-any.whl", hash = "sha256:c09e73ccc894976bcd6d6a5784aa84d724dbd9ceb7b873b39d475ca61c2de071"}, + {file = "pyright-1.1.366.tar.gz", hash = "sha256:10e4d60be411f6d960cd39b0b58bf2ff76f2c83b9aeb102ffa9d9fda2e1303cb"}, ] [[package]] @@ -2921,7 +2929,7 @@ files = [ [[package]] name = "redis" -version = "5.0.4" +version = "5.0.5" requires_python = ">=3.7" summary = "Python client for Redis database and key-value store" groups = ["dev"] @@ -2929,14 +2937,14 @@ dependencies = [ "async-timeout>=4.0.3; python_full_version < \"3.11.3\"", ] files = [ - {file = "redis-5.0.4-py3-none-any.whl", hash = "sha256:7adc2835c7a9b5033b7ad8f8918d09b7344188228809c98df07af226d39dec91"}, - {file = "redis-5.0.4.tar.gz", hash = "sha256:ec31f2ed9675cc54c21ba854cfe0462e6faf1d83c8ce5944709db8a4700b9c61"}, + {file = "redis-5.0.5-py3-none-any.whl", hash = "sha256:30b47d4ebb6b7a0b9b40c1275a19b87bb6f46b3bed82a89012cf56dea4024ada"}, + {file = "redis-5.0.5.tar.gz", hash = "sha256:3417688621acf6ee368dec4a04dd95881be24efd34c79f00d31f62bb528800ae"}, ] [[package]] name = "requests" -version = "2.31.0" -requires_python = ">=3.7" +version = "2.32.3" +requires_python = ">=3.8" summary = "Python HTTP for Humans." groups = ["dev", "docs"] dependencies = [ @@ -2946,8 +2954,8 @@ dependencies = [ "urllib3<3,>=1.21.1", ] files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [[package]] @@ -2968,7 +2976,7 @@ files = [ [[package]] name = "rich-click" -version = "1.8.1" +version = "1.8.2" requires_python = ">=3.7" summary = "Format click help output nicely with rich" groups = ["dev", "extensions"] @@ -2978,8 +2986,8 @@ dependencies = [ "typing-extensions", ] files = [ - {file = "rich_click-1.8.1-py3-none-any.whl", hash = "sha256:0cf0bf84404e78379bd2722db88cb07ffd0535440e20a05943d5b02249d90f8a"}, - {file = "rich_click-1.8.1.tar.gz", hash = "sha256:73c2ec88a66d7bf6b8c32783539d1c9c92c7c75847f14186092d27f83b206e8a"}, + {file = "rich_click-1.8.2-py3-none-any.whl", hash = "sha256:b57856f304e4fe0394b82d7ce0784450758f8c8b4e201ccc4320501cc201806b"}, + {file = "rich_click-1.8.2.tar.gz", hash = "sha256:8e29bdede858b59aa2859a1ab1c4ccbd39ed7ed5870262dae756fba6b5dc72e8"}, ] [[package]] @@ -3063,28 +3071,28 @@ files = [ [[package]] name = "ruff" -version = "0.4.7" +version = "0.4.8" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["docs", "linting"] files = [ - {file = "ruff-0.4.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e089371c67892a73b6bb1525608e89a2aca1b77b5440acf7a71dda5dac958f9e"}, - {file = "ruff-0.4.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:10f973d521d910e5f9c72ab27e409e839089f955be8a4c8826601a6323a89753"}, - {file = "ruff-0.4.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59c3d110970001dfa494bcd95478e62286c751126dfb15c3c46e7915fc49694f"}, - {file = "ruff-0.4.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa9773c6c00f4958f73b317bc0fd125295110c3776089f6ef318f4b775f0abe4"}, - {file = "ruff-0.4.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07fc80bbb61e42b3b23b10fda6a2a0f5a067f810180a3760c5ef1b456c21b9db"}, - {file = "ruff-0.4.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:fa4dafe3fe66d90e2e2b63fa1591dd6e3f090ca2128daa0be33db894e6c18648"}, - {file = "ruff-0.4.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7c0083febdec17571455903b184a10026603a1de078428ba155e7ce9358c5f6"}, - {file = "ruff-0.4.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad1b20e66a44057c326168437d680a2166c177c939346b19c0d6b08a62a37589"}, - {file = "ruff-0.4.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbf5d818553add7511c38b05532d94a407f499d1a76ebb0cad0374e32bc67202"}, - {file = "ruff-0.4.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:50e9651578b629baec3d1513b2534de0ac7ed7753e1382272b8d609997e27e83"}, - {file = "ruff-0.4.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8874a9df7766cb956b218a0a239e0a5d23d9e843e4da1e113ae1d27ee420877a"}, - {file = "ruff-0.4.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b9de9a6e49f7d529decd09381c0860c3f82fa0b0ea00ea78409b785d2308a567"}, - {file = "ruff-0.4.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:13a1768b0691619822ae6d446132dbdfd568b700ecd3652b20d4e8bc1e498f78"}, - {file = "ruff-0.4.7-py3-none-win32.whl", hash = "sha256:769e5a51df61e07e887b81e6f039e7ed3573316ab7dd9f635c5afaa310e4030e"}, - {file = "ruff-0.4.7-py3-none-win_amd64.whl", hash = "sha256:9e3ab684ad403a9ed1226894c32c3ab9c2e0718440f6f50c7c5829932bc9e054"}, - {file = "ruff-0.4.7-py3-none-win_arm64.whl", hash = "sha256:10f2204b9a613988e3484194c2c9e96a22079206b22b787605c255f130db5ed7"}, - {file = "ruff-0.4.7.tar.gz", hash = "sha256:2331d2b051dc77a289a653fcc6a42cce357087c5975738157cd966590b18b5e1"}, + {file = "ruff-0.4.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7663a6d78f6adb0eab270fa9cf1ff2d28618ca3a652b60f2a234d92b9ec89066"}, + {file = "ruff-0.4.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eeceb78da8afb6de0ddada93112869852d04f1cd0f6b80fe464fd4e35c330913"}, + {file = "ruff-0.4.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aad360893e92486662ef3be0a339c5ca3c1b109e0134fcd37d534d4be9fb8de3"}, + {file = "ruff-0.4.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:284c2e3f3396fb05f5f803c9fffb53ebbe09a3ebe7dda2929ed8d73ded736deb"}, + {file = "ruff-0.4.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7354f921e3fbe04d2a62d46707e569f9315e1a613307f7311a935743c51a764"}, + {file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:72584676164e15a68a15778fd1b17c28a519e7a0622161eb2debdcdabdc71883"}, + {file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9678d5c9b43315f323af2233a04d747409d1e3aa6789620083a82d1066a35199"}, + {file = "ruff-0.4.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704977a658131651a22b5ebeb28b717ef42ac6ee3b11e91dc87b633b5d83142b"}, + {file = "ruff-0.4.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05f8d6f0c3cce5026cecd83b7a143dcad503045857bc49662f736437380ad45"}, + {file = "ruff-0.4.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ea874950daca5697309d976c9afba830d3bf0ed66887481d6bca1673fc5b66a"}, + {file = "ruff-0.4.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fc95aac2943ddf360376be9aa3107c8cf9640083940a8c5bd824be692d2216dc"}, + {file = "ruff-0.4.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:384154a1c3f4bf537bac69f33720957ee49ac8d484bfc91720cc94172026ceed"}, + {file = "ruff-0.4.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e9d5ce97cacc99878aa0d084c626a15cd21e6b3d53fd6f9112b7fc485918e1fa"}, + {file = "ruff-0.4.8-py3-none-win32.whl", hash = "sha256:6d795d7639212c2dfd01991259460101c22aabf420d9b943f153ab9d9706e6a9"}, + {file = "ruff-0.4.8-py3-none-win_amd64.whl", hash = "sha256:e14a3a095d07560a9d6769a72f781d73259655919d9b396c650fc98a8157555d"}, + {file = "ruff-0.4.8-py3-none-win_arm64.whl", hash = "sha256:14019a06dbe29b608f6b7cbcec300e3170a8d86efaddb7b23405cb7f7dcaf780"}, + {file = "ruff-0.4.8.tar.gz", hash = "sha256:16d717b1d57b2e2fd68bd0bf80fb43931b79d05a7131aa477d66fc40fbd86268"}, ] [[package]] @@ -3164,13 +3172,13 @@ files = [ [[package]] name = "setuptools" -version = "69.5.1" +version = "70.0.0" requires_python = ">=3.8" summary = "Easily download, build, install, upgrade, and uninstall Python packages" -groups = ["docs", "linting"] +groups = ["docs"] files = [ - {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, - {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, ] [[package]] @@ -3186,7 +3194,7 @@ files = [ [[package]] name = "shibuya" -version = "2024.5.10" +version = "2024.6.1" requires_python = ">=3.7" summary = "A clean, responsive, and customizable Sphinx documentation theme with light/dark mode." groups = ["docs"] @@ -3194,8 +3202,8 @@ dependencies = [ "Sphinx", ] files = [ - {file = "shibuya-2024.5.10-py3-none-any.whl", hash = "sha256:a1868bc5cd4605742b3cb75d0da2f8ee933873ee052398b8a7d1b2aba51159de"}, - {file = "shibuya-2024.5.10.tar.gz", hash = "sha256:3ff7cf4c6af90bb7c6d29322afc9f1fb76f0af8a86045f80f93cb32f3e496f8a"}, + {file = "shibuya-2024.6.1-py3-none-any.whl", hash = "sha256:25aa62476ed6235111c4ed8ed75b8d87c281f81060ecf733894ee7a83c870074"}, + {file = "shibuya-2024.6.1.tar.gz", hash = "sha256:9807f47349237b617ac29c809cfda7fa287ac01a03ecd9ade0faf7ec35ee4403"}, ] [[package]] @@ -3727,23 +3735,23 @@ files = [ [[package]] name = "tornado" -version = "6.4" -requires_python = ">= 3.8" +version = "6.4.1" +requires_python = ">=3.8" summary = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." groups = ["docs"] marker = "python_version > \"2.7\"" files = [ - {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, - {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, - {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, - {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, - {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, - {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, - {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, - {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, - {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, - {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, - {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, + {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"}, + {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"}, + {file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"}, + {file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"}, + {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, ] [[package]] @@ -3810,13 +3818,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.12.1" +version = "4.12.2" requires_python = ">=3.8" summary = "Backported and Experimental Type Hints for Python 3.8+" groups = ["default", "dev", "docs", "extensions", "linting", "test"] files = [ - {file = "typing_extensions-4.12.1-py3-none-any.whl", hash = "sha256:6024b58b69089e5a89c347397254e35f1bf02a907728ec7fee9bf0fe837d203a"}, - {file = "typing_extensions-4.12.1.tar.gz", hash = "sha256:915f5e35ff76f56588223f15fdd5938f9a1cf9195c0de25130c627e4d597f6d1"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] @@ -3833,76 +3841,89 @@ files = [ [[package]] name = "ujson" -version = "5.9.0" +version = "5.10.0" requires_python = ">=3.8" summary = "Ultra fast JSON encoder and decoder for Python" groups = ["dev", "extensions"] files = [ - {file = "ujson-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab71bf27b002eaf7d047c54a68e60230fbd5cd9da60de7ca0aa87d0bccead8fa"}, - {file = "ujson-5.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a365eac66f5aa7a7fdf57e5066ada6226700884fc7dce2ba5483538bc16c8c5"}, - {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e015122b337858dba5a3dc3533af2a8fc0410ee9e2374092f6a5b88b182e9fcc"}, - {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:779a2a88c53039bebfbccca934430dabb5c62cc179e09a9c27a322023f363e0d"}, - {file = "ujson-5.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10ca3c41e80509fd9805f7c149068fa8dbee18872bbdc03d7cca928926a358d5"}, - {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a566e465cb2fcfdf040c2447b7dd9718799d0d90134b37a20dff1e27c0e9096"}, - {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f833c529e922577226a05bc25b6a8b3eb6c4fb155b72dd88d33de99d53113124"}, - {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b68a0caab33f359b4cbbc10065c88e3758c9f73a11a65a91f024b2e7a1257106"}, - {file = "ujson-5.9.0-cp310-cp310-win32.whl", hash = "sha256:7cc7e605d2aa6ae6b7321c3ae250d2e050f06082e71ab1a4200b4ae64d25863c"}, - {file = "ujson-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6d3f10eb8ccba4316a6b5465b705ed70a06011c6f82418b59278fbc919bef6f"}, - {file = "ujson-5.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b23bbb46334ce51ddb5dded60c662fbf7bb74a37b8f87221c5b0fec1ec6454b"}, - {file = "ujson-5.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6974b3a7c17bbf829e6c3bfdc5823c67922e44ff169851a755eab79a3dd31ec0"}, - {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5964ea916edfe24af1f4cc68488448fbb1ec27a3ddcddc2b236da575c12c8ae"}, - {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ba7cac47dd65ff88571eceeff48bf30ed5eb9c67b34b88cb22869b7aa19600d"}, - {file = "ujson-5.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bbd91a151a8f3358c29355a491e915eb203f607267a25e6ab10531b3b157c5e"}, - {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:829a69d451a49c0de14a9fecb2a2d544a9b2c884c2b542adb243b683a6f15908"}, - {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a807ae73c46ad5db161a7e883eec0fbe1bebc6a54890152ccc63072c4884823b"}, - {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8fc2aa18b13d97b3c8ccecdf1a3c405f411a6e96adeee94233058c44ff92617d"}, - {file = "ujson-5.9.0-cp311-cp311-win32.whl", hash = "sha256:70e06849dfeb2548be48fdd3ceb53300640bc8100c379d6e19d78045e9c26120"}, - {file = "ujson-5.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:7309d063cd392811acc49b5016728a5e1b46ab9907d321ebbe1c2156bc3c0b99"}, - {file = "ujson-5.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:20509a8c9f775b3a511e308bbe0b72897ba6b800767a7c90c5cca59d20d7c42c"}, - {file = "ujson-5.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b28407cfe315bd1b34f1ebe65d3bd735d6b36d409b334100be8cdffae2177b2f"}, - {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d302bd17989b6bd90d49bade66943c78f9e3670407dbc53ebcf61271cadc399"}, - {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f21315f51e0db8ee245e33a649dd2d9dce0594522de6f278d62f15f998e050e"}, - {file = "ujson-5.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5635b78b636a54a86fdbf6f027e461aa6c6b948363bdf8d4fbb56a42b7388320"}, - {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82b5a56609f1235d72835ee109163c7041b30920d70fe7dac9176c64df87c164"}, - {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5ca35f484622fd208f55041b042d9d94f3b2c9c5add4e9af5ee9946d2d30db01"}, - {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:829b824953ebad76d46e4ae709e940bb229e8999e40881338b3cc94c771b876c"}, - {file = "ujson-5.9.0-cp312-cp312-win32.whl", hash = "sha256:25fa46e4ff0a2deecbcf7100af3a5d70090b461906f2299506485ff31d9ec437"}, - {file = "ujson-5.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:60718f1720a61560618eff3b56fd517d107518d3c0160ca7a5a66ac949c6cf1c"}, - {file = "ujson-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d581db9db9e41d8ea0b2705c90518ba623cbdc74f8d644d7eb0d107be0d85d9c"}, - {file = "ujson-5.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ff741a5b4be2d08fceaab681c9d4bc89abf3c9db600ab435e20b9b6d4dfef12e"}, - {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdcb02cabcb1e44381221840a7af04433c1dc3297af76fde924a50c3054c708c"}, - {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e208d3bf02c6963e6ef7324dadf1d73239fb7008491fdf523208f60be6437402"}, - {file = "ujson-5.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4b3917296630a075e04d3d07601ce2a176479c23af838b6cf90a2d6b39b0d95"}, - {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0c4d6adb2c7bb9eb7c71ad6f6f612e13b264942e841f8cc3314a21a289a76c4e"}, - {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0b159efece9ab5c01f70b9d10bbb77241ce111a45bc8d21a44c219a2aec8ddfd"}, - {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0cb4a7814940ddd6619bdce6be637a4b37a8c4760de9373bac54bb7b229698b"}, - {file = "ujson-5.9.0-cp38-cp38-win32.whl", hash = "sha256:dc80f0f5abf33bd7099f7ac94ab1206730a3c0a2d17549911ed2cb6b7aa36d2d"}, - {file = "ujson-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:506a45e5fcbb2d46f1a51fead991c39529fc3737c0f5d47c9b4a1d762578fc30"}, - {file = "ujson-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0fd2eba664a22447102062814bd13e63c6130540222c0aa620701dd01f4be81"}, - {file = "ujson-5.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bdf7fc21a03bafe4ba208dafa84ae38e04e5d36c0e1c746726edf5392e9f9f36"}, - {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2f909bc08ce01f122fd9c24bc6f9876aa087188dfaf3c4116fe6e4daf7e194f"}, - {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd4ea86c2afd41429751d22a3ccd03311c067bd6aeee2d054f83f97e41e11d8f"}, - {file = "ujson-5.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:63fb2e6599d96fdffdb553af0ed3f76b85fda63281063f1cb5b1141a6fcd0617"}, - {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:32bba5870c8fa2a97f4a68f6401038d3f1922e66c34280d710af00b14a3ca562"}, - {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:37ef92e42535a81bf72179d0e252c9af42a4ed966dc6be6967ebfb929a87bc60"}, - {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f69f16b8f1c69da00e38dc5f2d08a86b0e781d0ad3e4cc6a13ea033a439c4844"}, - {file = "ujson-5.9.0-cp39-cp39-win32.whl", hash = "sha256:3382a3ce0ccc0558b1c1668950008cece9bf463ebb17463ebf6a8bfc060dae34"}, - {file = "ujson-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:6adef377ed583477cf005b58c3025051b5faa6b8cc25876e594afbb772578f21"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ffdfebd819f492e48e4f31c97cb593b9c1a8251933d8f8972e81697f00326ff1"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4eec2ddc046360d087cf35659c7ba0cbd101f32035e19047013162274e71fcf"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbb90aa5c23cb3d4b803c12aa220d26778c31b6e4b7a13a1f49971f6c7d088e"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0823cb70866f0d6a4ad48d998dd338dce7314598721bc1b7986d054d782dfd"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4e35d7885ed612feb6b3dd1b7de28e89baaba4011ecdf995e88be9ac614765e9"}, - {file = "ujson-5.9.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b048aa93eace8571eedbd67b3766623e7f0acbf08ee291bef7d8106210432427"}, - {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:323279e68c195110ef85cbe5edce885219e3d4a48705448720ad925d88c9f851"}, - {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ac92d86ff34296f881e12aa955f7014d276895e0e4e868ba7fddebbde38e378"}, - {file = "ujson-5.9.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6eecbd09b316cea1fd929b1e25f70382917542ab11b692cb46ec9b0a26c7427f"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:473fb8dff1d58f49912323d7cb0859df5585cfc932e4b9c053bf8cf7f2d7c5c4"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f91719c6abafe429c1a144cfe27883eace9fb1c09a9c5ef1bcb3ae80a3076a4e"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1c0991c4fe256f5fdb19758f7eac7f47caac29a6c57d0de16a19048eb86bad"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a8ea0f55a1396708e564595aaa6696c0d8af532340f477162ff6927ecc46e21"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:07e0cfdde5fd91f54cd2d7ffb3482c8ff1bf558abf32a8b953a5d169575ae1cd"}, - {file = "ujson-5.9.0.tar.gz", hash = "sha256:89cc92e73d5501b8a7f48575eeb14ad27156ad092c2e9fc7e3cf949f07e75532"}, + {file = "ujson-5.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2601aa9ecdbee1118a1c2065323bda35e2c5a2cf0797ef4522d485f9d3ef65bd"}, + {file = "ujson-5.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:348898dd702fc1c4f1051bc3aacbf894caa0927fe2c53e68679c073375f732cf"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22cffecf73391e8abd65ef5f4e4dd523162a3399d5e84faa6aebbf9583df86d6"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26b0e2d2366543c1bb4fbd457446f00b0187a2bddf93148ac2da07a53fe51569"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:caf270c6dba1be7a41125cd1e4fc7ba384bf564650beef0df2dd21a00b7f5770"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a245d59f2ffe750446292b0094244df163c3dc96b3ce152a2c837a44e7cda9d1"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94a87f6e151c5f483d7d54ceef83b45d3a9cca7a9cb453dbdbb3f5a6f64033f5"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:29b443c4c0a113bcbb792c88bea67b675c7ca3ca80c3474784e08bba01c18d51"}, + {file = "ujson-5.10.0-cp310-cp310-win32.whl", hash = "sha256:c18610b9ccd2874950faf474692deee4223a994251bc0a083c114671b64e6518"}, + {file = "ujson-5.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:924f7318c31874d6bb44d9ee1900167ca32aa9b69389b98ecbde34c1698a250f"}, + {file = "ujson-5.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a5b366812c90e69d0f379a53648be10a5db38f9d4ad212b60af00bd4048d0f00"}, + {file = "ujson-5.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:502bf475781e8167f0f9d0e41cd32879d120a524b22358e7f205294224c71126"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b91b5d0d9d283e085e821651184a647699430705b15bf274c7896f23fe9c9d8"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:129e39af3a6d85b9c26d5577169c21d53821d8cf68e079060602e861c6e5da1b"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f77b74475c462cb8b88680471193064d3e715c7c6074b1c8c412cb526466efe9"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ec0ca8c415e81aa4123501fee7f761abf4b7f386aad348501a26940beb1860f"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab13a2a9e0b2865a6c6db9271f4b46af1c7476bfd51af1f64585e919b7c07fd4"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57aaf98b92d72fc70886b5a0e1a1ca52c2320377360341715dd3933a18e827b1"}, + {file = "ujson-5.10.0-cp311-cp311-win32.whl", hash = "sha256:2987713a490ceb27edff77fb184ed09acdc565db700ee852823c3dc3cffe455f"}, + {file = "ujson-5.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:f00ea7e00447918ee0eff2422c4add4c5752b1b60e88fcb3c067d4a21049a720"}, + {file = "ujson-5.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98ba15d8cbc481ce55695beee9f063189dce91a4b08bc1d03e7f0152cd4bbdd5"}, + {file = "ujson-5.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9d2edbf1556e4f56e50fab7d8ff993dbad7f54bac68eacdd27a8f55f433578e"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6627029ae4f52d0e1a2451768c2c37c0c814ffc04f796eb36244cf16b8e57043"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ccb77b3e40b151e20519c6ae6d89bfe3f4c14e8e210d910287f778368bb3d1"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3caf9cd64abfeb11a3b661329085c5e167abbe15256b3b68cb5d914ba7396f3"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6e32abdce572e3a8c3d02c886c704a38a1b015a1fb858004e03d20ca7cecbb21"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a65b6af4d903103ee7b6f4f5b85f1bfd0c90ba4eeac6421aae436c9988aa64a2"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:604a046d966457b6cdcacc5aa2ec5314f0e8c42bae52842c1e6fa02ea4bda42e"}, + {file = "ujson-5.10.0-cp312-cp312-win32.whl", hash = "sha256:6dea1c8b4fc921bf78a8ff00bbd2bfe166345f5536c510671bccececb187c80e"}, + {file = "ujson-5.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:38665e7d8290188b1e0d57d584eb8110951a9591363316dd41cf8686ab1d0abc"}, + {file = "ujson-5.10.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:618efd84dc1acbd6bff8eaa736bb6c074bfa8b8a98f55b61c38d4ca2c1f7f287"}, + {file = "ujson-5.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38d5d36b4aedfe81dfe251f76c0467399d575d1395a1755de391e58985ab1c2e"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67079b1f9fb29ed9a2914acf4ef6c02844b3153913eb735d4bf287ee1db6e557"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d0e0ceeb8fe2468c70ec0c37b439dd554e2aa539a8a56365fd761edb418988"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59e02cd37bc7c44d587a0ba45347cc815fb7a5fe48de16bf05caa5f7d0d2e816"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a890b706b64e0065f02577bf6d8ca3b66c11a5e81fb75d757233a38c07a1f20"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:621e34b4632c740ecb491efc7f1fcb4f74b48ddb55e65221995e74e2d00bbff0"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9500e61fce0cfc86168b248104e954fead61f9be213087153d272e817ec7b4f"}, + {file = "ujson-5.10.0-cp313-cp313-win32.whl", hash = "sha256:4c4fc16f11ac1612f05b6f5781b384716719547e142cfd67b65d035bd85af165"}, + {file = "ujson-5.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:4573fd1695932d4f619928fd09d5d03d917274381649ade4328091ceca175539"}, + {file = "ujson-5.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a984a3131da7f07563057db1c3020b1350a3e27a8ec46ccbfbf21e5928a43050"}, + {file = "ujson-5.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73814cd1b9db6fc3270e9d8fe3b19f9f89e78ee9d71e8bd6c9a626aeaeaf16bd"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61e1591ed9376e5eddda202ec229eddc56c612b61ac6ad07f96b91460bb6c2fb"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c75269f8205b2690db4572a4a36fe47cd1338e4368bc73a7a0e48789e2e35a"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7223f41e5bf1f919cd8d073e35b229295aa8e0f7b5de07ed1c8fddac63a6bc5d"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc2fd6b3067c0782e7002ac3b38cf48608ee6366ff176bbd02cf969c9c20fe"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:232cc85f8ee3c454c115455195a205074a56ff42608fd6b942aa4c378ac14dd7"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cc6139531f13148055d691e442e4bc6601f6dba1e6d521b1585d4788ab0bfad4"}, + {file = "ujson-5.10.0-cp38-cp38-win32.whl", hash = "sha256:e7ce306a42b6b93ca47ac4a3b96683ca554f6d35dd8adc5acfcd55096c8dfcb8"}, + {file = "ujson-5.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:e82d4bb2138ab05e18f089a83b6564fee28048771eb63cdecf4b9b549de8a2cc"}, + {file = "ujson-5.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfef2814c6b3291c3c5f10065f745a1307d86019dbd7ea50e83504950136ed5b"}, + {file = "ujson-5.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4734ee0745d5928d0ba3a213647f1c4a74a2a28edc6d27b2d6d5bd9fa4319e27"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47ebb01bd865fdea43da56254a3930a413f0c5590372a1241514abae8aa7c76"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dee5e97c2496874acbf1d3e37b521dd1f307349ed955e62d1d2f05382bc36dd5"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7490655a2272a2d0b072ef16b0b58ee462f4973a8f6bbe64917ce5e0a256f9c0"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba17799fcddaddf5c1f75a4ba3fd6441f6a4f1e9173f8a786b42450851bd74f1"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2aff2985cef314f21d0fecc56027505804bc78802c0121343874741650a4d3d1"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad88ac75c432674d05b61184178635d44901eb749786c8eb08c102330e6e8996"}, + {file = "ujson-5.10.0-cp39-cp39-win32.whl", hash = "sha256:2544912a71da4ff8c4f7ab5606f947d7299971bdd25a45e008e467ca638d13c9"}, + {file = "ujson-5.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:3ff201d62b1b177a46f113bb43ad300b424b7847f9c5d38b1b4ad8f75d4a282a"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5b6fee72fa77dc172a28f21693f64d93166534c263adb3f96c413ccc85ef6e64"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:61d0af13a9af01d9f26d2331ce49bb5ac1fb9c814964018ac8df605b5422dcb3"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecb24f0bdd899d368b715c9e6664166cf694d1e57be73f17759573a6986dd95a"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbd8fd427f57a03cff3ad6574b5e299131585d9727c8c366da4624a9069ed746"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beeaf1c48e32f07d8820c705ff8e645f8afa690cca1544adba4ebfa067efdc88"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:baed37ea46d756aca2955e99525cc02d9181de67f25515c468856c38d52b5f3b"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7663960f08cd5a2bb152f5ee3992e1af7690a64c0e26d31ba7b3ff5b2ee66337"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8640fb4072d36b08e95a3a380ba65779d356b2fee8696afeb7794cf0902d0a1"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78778a3aa7aafb11e7ddca4e29f46bc5139131037ad628cc10936764282d6753"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0111b27f2d5c820e7f2dbad7d48e3338c824e7ac4d2a12da3dc6061cc39c8e6"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:c66962ca7565605b355a9ed478292da628b8f18c0f2793021ca4425abf8b01e5"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ba43cc34cce49cf2d4bc76401a754a81202d8aa926d0e2b79f0ee258cb15d3a4"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac56eb983edce27e7f51d05bc8dd820586c6e6be1c5216a6809b0c668bb312b8"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44bd4b23a0e723bf8b10628288c2c7c335161d6840013d4d5de20e48551773b"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c10f4654e5326ec14a46bcdeb2b685d4ada6911050aa8baaf3501e57024b804"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0de4971a89a762398006e844ae394bd46991f7c385d7a6a3b93ba229e6dac17e"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e1402f0564a97d2a52310ae10a64d25bcef94f8dd643fcf5d310219d915484f7"}, + {file = "ujson-5.10.0.tar.gz", hash = "sha256:b3cd8f3c5d8c7738257f1018880444f7b7d9b66232c64649f562d7ba86ad4bc1"}, ] [[package]] @@ -4010,7 +4031,7 @@ files = [ [[package]] name = "uvicorn" -version = "0.29.0" +version = "0.30.1" requires_python = ">=3.8" summary = "The lightning-fast ASGI server." groups = ["dev", "extensions"] @@ -4020,13 +4041,13 @@ dependencies = [ "typing-extensions>=4.0; python_version < \"3.11\"", ] files = [ - {file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"}, - {file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"}, + {file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"}, + {file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"}, ] [[package]] name = "uvicorn" -version = "0.29.0" +version = "0.30.1" extras = ["standard"] requires_python = ">=3.8" summary = "The lightning-fast ASGI server." @@ -4036,14 +4057,14 @@ dependencies = [ "httptools>=0.5.0", "python-dotenv>=0.13", "pyyaml>=5.1", - "uvicorn==0.29.0", + "uvicorn==0.30.1", "uvloop!=0.15.0,!=0.15.1,>=0.14.0; (sys_platform != \"cygwin\" and sys_platform != \"win32\") and platform_python_implementation != \"PyPy\"", "watchfiles>=0.13", "websockets>=10.4", ] files = [ - {file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"}, - {file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"}, + {file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"}, + {file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"}, ] [[package]] @@ -4105,7 +4126,7 @@ files = [ [[package]] name = "watchfiles" -version = "0.21.0" +version = "0.22.0" requires_python = ">=3.8" summary = "Simple, modern and high performance file watching and code reload in python." groups = ["dev", "extensions"] @@ -4113,81 +4134,81 @@ dependencies = [ "anyio>=3.0.0", ] files = [ - {file = "watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa"}, - {file = "watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c"}, - {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9"}, - {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9"}, - {file = "watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293"}, - {file = "watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235"}, - {file = "watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7"}, - {file = "watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7"}, - {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0"}, - {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365"}, - {file = "watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400"}, - {file = "watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe"}, - {file = "watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078"}, - {file = "watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a"}, - {file = "watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c"}, - {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235"}, - {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7"}, - {file = "watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3"}, - {file = "watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094"}, - {file = "watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6"}, - {file = "watchfiles-0.21.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:4ea10a29aa5de67de02256a28d1bf53d21322295cb00bd2d57fcd19b850ebd99"}, - {file = "watchfiles-0.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:40bca549fdc929b470dd1dbfcb47b3295cb46a6d2c90e50588b0a1b3bd98f429"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9b37a7ba223b2f26122c148bb8d09a9ff312afca998c48c725ff5a0a632145f7"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec8c8900dc5c83650a63dd48c4d1d245343f904c4b64b48798c67a3767d7e165"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ad3fe0a3567c2f0f629d800409cd528cb6251da12e81a1f765e5c5345fd0137"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d353c4cfda586db2a176ce42c88f2fc31ec25e50212650c89fdd0f560ee507b"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83a696da8922314ff2aec02987eefb03784f473281d740bf9170181829133765"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a03651352fc20975ee2a707cd2d74a386cd303cc688f407296064ad1e6d1562"}, - {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ad692bc7792be8c32918c699638b660c0de078a6cbe464c46e1340dadb94c19"}, - {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06247538e8253975bdb328e7683f8515ff5ff041f43be6c40bff62d989b7d0b0"}, - {file = "watchfiles-0.21.0-cp38-none-win32.whl", hash = "sha256:9a0aa47f94ea9a0b39dd30850b0adf2e1cd32a8b4f9c7aa443d852aacf9ca214"}, - {file = "watchfiles-0.21.0-cp38-none-win_amd64.whl", hash = "sha256:8d5f400326840934e3507701f9f7269247f7c026d1b6cfd49477d2be0933cfca"}, - {file = "watchfiles-0.21.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7f762a1a85a12cc3484f77eee7be87b10f8c50b0b787bb02f4e357403cad0c0e"}, - {file = "watchfiles-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6e9be3ef84e2bb9710f3f777accce25556f4a71e15d2b73223788d528fcc2052"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4c48a10d17571d1275701e14a601e36959ffada3add8cdbc9e5061a6e3579a5d"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c889025f59884423428c261f212e04d438de865beda0b1e1babab85ef4c0f01"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66fac0c238ab9a2e72d026b5fb91cb902c146202bbd29a9a1a44e8db7b710b6f"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4a21f71885aa2744719459951819e7bf5a906a6448a6b2bbce8e9cc9f2c8128"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c9198c989f47898b2c22201756f73249de3748e0fc9de44adaf54a8b259cc0c"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f57c4461cd24fda22493109c45b3980863c58a25b8bec885ca8bea6b8d4b28"}, - {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:853853cbf7bf9408b404754b92512ebe3e3a83587503d766d23e6bf83d092ee6"}, - {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d5b1dc0e708fad9f92c296ab2f948af403bf201db8fb2eb4c8179db143732e49"}, - {file = "watchfiles-0.21.0-cp39-none-win32.whl", hash = "sha256:59137c0c6826bd56c710d1d2bda81553b5e6b7c84d5a676747d80caf0409ad94"}, - {file = "watchfiles-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:6cb8fdc044909e2078c248986f2fc76f911f72b51ea4a4fbbf472e01d14faa58"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:08dca260e85ffae975448e344834d765983237ad6dc308231aa16e7933db763e"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ccceb50c611c433145502735e0370877cced72a6c70fd2410238bcbc7fe51d8"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57d430f5fb63fea141ab71ca9c064e80de3a20b427ca2febcbfcef70ff0ce895"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd5fad9b9c0dd89904bbdea978ce89a2b692a7ee8a0ce19b940e538c88a809c"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:be6dd5d52b73018b21adc1c5d28ac0c68184a64769052dfeb0c5d9998e7f56a2"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b3cab0e06143768499384a8a5efb9c4dc53e19382952859e4802f294214f36ec"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6ed10c2497e5fedadf61e465b3ca12a19f96004c15dcffe4bd442ebadc2d85"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43babacef21c519bc6631c5fce2a61eccdfc011b4bcb9047255e9620732c8097"}, - {file = "watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3"}, + {file = "watchfiles-0.22.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:da1e0a8caebf17976e2ffd00fa15f258e14749db5e014660f53114b676e68538"}, + {file = "watchfiles-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61af9efa0733dc4ca462347becb82e8ef4945aba5135b1638bfc20fad64d4f0e"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d9188979a58a096b6f8090e816ccc3f255f137a009dd4bbec628e27696d67c1"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2bdadf6b90c099ca079d468f976fd50062905d61fae183f769637cb0f68ba59a"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:067dea90c43bf837d41e72e546196e674f68c23702d3ef80e4e816937b0a3ffd"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbf8a20266136507abf88b0df2328e6a9a7c7309e8daff124dda3803306a9fdb"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1235c11510ea557fe21be5d0e354bae2c655a8ee6519c94617fe63e05bca4171"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2444dc7cb9d8cc5ab88ebe792a8d75709d96eeef47f4c8fccb6df7c7bc5be71"}, + {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c5af2347d17ab0bd59366db8752d9e037982e259cacb2ba06f2c41c08af02c39"}, + {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9624a68b96c878c10437199d9a8b7d7e542feddda8d5ecff58fdc8e67b460848"}, + {file = "watchfiles-0.22.0-cp310-none-win32.whl", hash = "sha256:4b9f2a128a32a2c273d63eb1fdbf49ad64852fc38d15b34eaa3f7ca2f0d2b797"}, + {file = "watchfiles-0.22.0-cp310-none-win_amd64.whl", hash = "sha256:2627a91e8110b8de2406d8b2474427c86f5a62bf7d9ab3654f541f319ef22bcb"}, + {file = "watchfiles-0.22.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8c39987a1397a877217be1ac0fb1d8b9f662c6077b90ff3de2c05f235e6a8f96"}, + {file = "watchfiles-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a927b3034d0672f62fb2ef7ea3c9fc76d063c4b15ea852d1db2dc75fe2c09696"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052d668a167e9fc345c24203b104c313c86654dd6c0feb4b8a6dfc2462239249"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e45fb0d70dda1623a7045bd00c9e036e6f1f6a85e4ef2c8ae602b1dfadf7550"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c49b76a78c156979759d759339fb62eb0549515acfe4fd18bb151cc07366629c"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a65474fd2b4c63e2c18ac67a0c6c66b82f4e73e2e4d940f837ed3d2fd9d4da"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc0cba54f47c660d9fa3218158b8963c517ed23bd9f45fe463f08262a4adae1"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ebe84a035993bb7668f58a0ebf998174fb723a39e4ef9fce95baabb42b787f"}, + {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e0f0a874231e2839abbf473256efffe577d6ee2e3bfa5b540479e892e47c172d"}, + {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:213792c2cd3150b903e6e7884d40660e0bcec4465e00563a5fc03f30ea9c166c"}, + {file = "watchfiles-0.22.0-cp311-none-win32.whl", hash = "sha256:b44b70850f0073b5fcc0b31ede8b4e736860d70e2dbf55701e05d3227a154a67"}, + {file = "watchfiles-0.22.0-cp311-none-win_amd64.whl", hash = "sha256:00f39592cdd124b4ec5ed0b1edfae091567c72c7da1487ae645426d1b0ffcad1"}, + {file = "watchfiles-0.22.0-cp311-none-win_arm64.whl", hash = "sha256:3218a6f908f6a276941422b035b511b6d0d8328edd89a53ae8c65be139073f84"}, + {file = "watchfiles-0.22.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c7b978c384e29d6c7372209cbf421d82286a807bbcdeb315427687f8371c340a"}, + {file = "watchfiles-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd4c06100bce70a20c4b81e599e5886cf504c9532951df65ad1133e508bf20be"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:425440e55cd735386ec7925f64d5dde392e69979d4c8459f6bb4e920210407f2"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68fe0c4d22332d7ce53ad094622b27e67440dacefbaedd29e0794d26e247280c"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8a31bfd98f846c3c284ba694c6365620b637debdd36e46e1859c897123aa232"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc2e8fe41f3cac0660197d95216c42910c2b7e9c70d48e6d84e22f577d106fc1"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b7cc10261c2786c41d9207193a85c1db1b725cf87936df40972aab466179b6"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28585744c931576e535860eaf3f2c0ec7deb68e3b9c5a85ca566d69d36d8dd27"}, + {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00095dd368f73f8f1c3a7982a9801190cc88a2f3582dd395b289294f8975172b"}, + {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:52fc9b0dbf54d43301a19b236b4a4614e610605f95e8c3f0f65c3a456ffd7d35"}, + {file = "watchfiles-0.22.0-cp312-none-win32.whl", hash = "sha256:581f0a051ba7bafd03e17127735d92f4d286af941dacf94bcf823b101366249e"}, + {file = "watchfiles-0.22.0-cp312-none-win_amd64.whl", hash = "sha256:aec83c3ba24c723eac14225194b862af176d52292d271c98820199110e31141e"}, + {file = "watchfiles-0.22.0-cp312-none-win_arm64.whl", hash = "sha256:c668228833c5619f6618699a2c12be057711b0ea6396aeaece4ded94184304ea"}, + {file = "watchfiles-0.22.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d47e9ef1a94cc7a536039e46738e17cce058ac1593b2eccdede8bf72e45f372a"}, + {file = "watchfiles-0.22.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28f393c1194b6eaadcdd8f941307fc9bbd7eb567995232c830f6aef38e8a6e88"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd64f3a4db121bc161644c9e10a9acdb836853155a108c2446db2f5ae1778c3d"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2abeb79209630da981f8ebca30a2c84b4c3516a214451bfc5f106723c5f45843"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cc382083afba7918e32d5ef12321421ef43d685b9a67cc452a6e6e18920890e"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d048ad5d25b363ba1d19f92dcf29023988524bee6f9d952130b316c5802069cb"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:103622865599f8082f03af4214eaff90e2426edff5e8522c8f9e93dc17caee13"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e1f3cf81f1f823e7874ae563457828e940d75573c8fbf0ee66818c8b6a9099"}, + {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8597b6f9dc410bdafc8bb362dac1cbc9b4684a8310e16b1ff5eee8725d13dcd6"}, + {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0b04a2cbc30e110303baa6d3ddce8ca3664bc3403be0f0ad513d1843a41c97d1"}, + {file = "watchfiles-0.22.0-cp38-none-win32.whl", hash = "sha256:b610fb5e27825b570554d01cec427b6620ce9bd21ff8ab775fc3a32f28bba63e"}, + {file = "watchfiles-0.22.0-cp38-none-win_amd64.whl", hash = "sha256:fe82d13461418ca5e5a808a9e40f79c1879351fcaeddbede094028e74d836e86"}, + {file = "watchfiles-0.22.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3973145235a38f73c61474d56ad6199124e7488822f3a4fc97c72009751ae3b0"}, + {file = "watchfiles-0.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:280a4afbc607cdfc9571b9904b03a478fc9f08bbeec382d648181c695648202f"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a0d883351a34c01bd53cfa75cd0292e3f7e268bacf2f9e33af4ecede7e21d1d"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9165bcab15f2b6d90eedc5c20a7f8a03156b3773e5fb06a790b54ccecdb73385"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc1b9b56f051209be458b87edb6856a449ad3f803315d87b2da4c93b43a6fe72"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc1fc25a1dedf2dd952909c8e5cb210791e5f2d9bc5e0e8ebc28dd42fed7562"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc92d2d2706d2b862ce0568b24987eba51e17e14b79a1abcd2edc39e48e743c8"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97b94e14b88409c58cdf4a8eaf0e67dfd3ece7e9ce7140ea6ff48b0407a593ec"}, + {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:96eec15e5ea7c0b6eb5bfffe990fc7c6bd833acf7e26704eb18387fb2f5fd087"}, + {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:28324d6b28bcb8d7c1041648d7b63be07a16db5510bea923fc80b91a2a6cbed6"}, + {file = "watchfiles-0.22.0-cp39-none-win32.whl", hash = "sha256:8c3e3675e6e39dc59b8fe5c914a19d30029e36e9f99468dddffd432d8a7b1c93"}, + {file = "watchfiles-0.22.0-cp39-none-win_amd64.whl", hash = "sha256:25c817ff2a86bc3de3ed2df1703e3d24ce03479b27bb4527c57e722f8554d971"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b810a2c7878cbdecca12feae2c2ae8af59bea016a78bc353c184fa1e09f76b68"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7e1f9c5d1160d03b93fc4b68a0aeb82fe25563e12fbcdc8507f8434ab6f823c"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:030bc4e68d14bcad2294ff68c1ed87215fbd9a10d9dea74e7cfe8a17869785ab"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace7d060432acde5532e26863e897ee684780337afb775107c0a90ae8dbccfd2"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5834e1f8b71476a26df97d121c0c0ed3549d869124ed2433e02491553cb468c2"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0bc3b2f93a140df6806c8467c7f51ed5e55a931b031b5c2d7ff6132292e803d6"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fdebb655bb1ba0122402352b0a4254812717a017d2dc49372a1d47e24073795"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8e0aa0e8cc2a43561e0184c0513e291ca891db13a269d8d47cb9841ced7c71"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2f350cbaa4bb812314af5dab0eb8d538481e2e2279472890864547f3fe2281ed"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7a74436c415843af2a769b36bf043b6ccbc0f8d784814ba3d42fc961cdb0a9dc"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00ad0bcd399503a84cc688590cdffbe7a991691314dde5b57b3ed50a41319a31"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a44e9481afc7a5ee3291b09c419abab93b7e9c306c9ef9108cb76728ca58d2"}, + {file = "watchfiles-0.22.0.tar.gz", hash = "sha256:988e981aaab4f3955209e7e28c7794acdb690be1efa7f16f8ea5aba7ffdadacb"}, ] [[package]] @@ -4308,12 +4329,12 @@ files = [ [[package]] name = "zipp" -version = "3.18.1" +version = "3.19.2" requires_python = ">=3.8" summary = "Backport of pathlib-compatible object wrapper for zip files" groups = ["default", "dev", "docs", "extensions"] marker = "python_version < \"3.10\"" files = [ - {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, - {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, + {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, + {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, ] diff --git a/pyproject.toml b/pyproject.toml index d3295cf5..235cc281 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -383,7 +383,9 @@ update_docstrings = true SQLAlchemyAsyncMockRepository = "SQLAlchemySyncMockRepository" "SQLAlchemyAsyncQueryRepository" = "SQLAlchemySyncQueryRepository" SQLAlchemyAsyncRepository = "SQLAlchemySyncRepository" +SQLAlchemyAsyncRepositoryProtocol = "SQLAlchemySyncRepositoryProtocol" "SQLAlchemyAsyncSlugRepository" = "SQLAlchemySyncSlugRepository" +SQLAlchemyAsyncSlugRepositoryProtocol = "SQLAlchemySyncSlugRepositoryProtocol" "async_scoped_session" = "scoped_session" "sqlalchemy.ext.asyncio.AsyncSession" = "sqlalchemy.orm.Session" "sqlalchemy.ext.asyncio.scoping.async_scoped_session" = "sqlalchemy.orm.scoping.scoped_session" @@ -392,8 +394,13 @@ SQLAlchemyAsyncRepository = "SQLAlchemySyncRepository" SQLAlchemyAsyncMockRepository = "SQLAlchemySyncMockRepository" "SQLAlchemyAsyncMockSlugRepository" = "SQLAlchemySyncMockSlugRepository" SQLAlchemyAsyncRepository = "SQLAlchemySyncRepository" +SQLAlchemyAsyncRepositoryProtocol = "SQLAlchemySyncRepositoryProtocol" "SQLAlchemyAsyncSlugRepository" = "SQLAlchemySyncSlugRepository" +SQLAlchemyAsyncSlugRepositoryProtocol = "SQLAlchemySyncSlugRepositoryProtocol" +"advanced_alchemy.repository._async.SQLAlchemyAsyncRepositoryProtocol" = "advanced_alchemy.repository._sync.SQLAlchemySyncRepositoryProtocol" +"advanced_alchemy.repository._async.SQLAlchemyAsyncSlugRepositoryProtocol" = "advanced_alchemy.repository._sync.SQLAlchemySyncSlugRepositoryProtocol" "async_scoped_session" = "scoped_session" +"sqlalchemy.ext.asyncio.AsyncEngine" = "sqlalchemy.Engine" "sqlalchemy.ext.asyncio.AsyncSession" = "sqlalchemy.orm.Session" "sqlalchemy.ext.asyncio.scoping.async_scoped_session" = "sqlalchemy.orm.scoping.scoped_session" @@ -401,14 +408,20 @@ SQLAlchemyAsyncRepository = "SQLAlchemySyncRepository" "AsyncIterator" = "Iterator" "SQLAlchemyAsyncConfigT" = "SQLAlchemySyncConfigT" SQLAlchemyAsyncMockRepository = "SQLAlchemySyncMockRepository" +SQLAlchemyAsyncMockSlugRepository = "SQLAlchemySyncMockSlugRepository" SQLAlchemyAsyncQueryService = "SQLAlchemySyncQueryService" SQLAlchemyAsyncRepository = "SQLAlchemySyncRepository" SQLAlchemyAsyncRepositoryReadService = "SQLAlchemySyncRepositoryReadService" SQLAlchemyAsyncRepositoryService = "SQLAlchemySyncRepositoryService" +SQLAlchemyAsyncSlugRepository = "SQLAlchemySyncSlugRepository" "advanced_alchemy.config.asyncio.SQLAlchemyAsyncConfig" = "advanced_alchemy.config.sync.SQLAlchemySyncConfig" +"advanced_alchemy.repository.SQLAlchemyAsyncQueryRepository" = "advanced_alchemy.repository.SQLAlchemySyncQueryRepository" "advanced_alchemy.repository.SQLAlchemyAsyncRepository" = "advanced_alchemy.repository.SQLAlchemySyncRepository" -"advanced_alchemy.repository._async.SQLAlchemyAsyncQueryRepository" = "advanced_alchemy.repository._sync.SQLAlchemySyncQueryRepository" +"advanced_alchemy.repository.SQLAlchemyAsyncRepositoryProtocol" = "advanced_alchemy.repository.SQLAlchemySyncRepositoryProtocol" +"advanced_alchemy.repository.SQLAlchemyAsyncSlugRepository" = "advanced_alchemy.repository.SQLAlchemySyncSlugRepository" +"advanced_alchemy.repository.SQLAlchemyAsyncSlugRepositoryProtocol" = "advanced_alchemy.repository.SQLAlchemySyncSlugRepositoryProtocol" "advanced_alchemy.repository.memory.SQLAlchemyAsyncMockRepository" = "advanced_alchemy.repository.memory.SQLAlchemySyncMockRepository" +"advanced_alchemy.repository.memory.SQLAlchemyAsyncMockSlugRepository" = "advanced_alchemy.repository.memory.SQLAlchemySyncMockSlugRepository" "async_scoped_session" = "scoped_session" "collections.abc.AsyncIterator" = "collections.abc.Iterator" "sqlalchemy.ext.asyncio.AsyncSession" = "sqlalchemy.orm.Session" diff --git a/sqlite.db b/sqlite.db new file mode 100644 index 00000000..7c945e4d Binary files /dev/null and b/sqlite.db differ diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/fixtures/bigint/__init__.py b/tests/fixtures/bigint/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/fixtures/bigint/models.py b/tests/fixtures/bigint/models.py new file mode 100644 index 00000000..39b777db --- /dev/null +++ b/tests/fixtures/bigint/models.py @@ -0,0 +1,111 @@ +"""Example domain objects for testing.""" + +from __future__ import annotations + +from datetime import date, datetime +from typing import List + +from sqlalchemy import Column, FetchedValue, ForeignKey, String, Table, func +from sqlalchemy.orm import Mapped, declared_attr, mapped_column, relationship +from sqlalchemy.orm.decl_base import _TableArgsType as TableArgsType # pyright: ignore[reportPrivateUsage] + +from advanced_alchemy.base import BigIntAuditBase, BigIntBase, SlugKey, merge_table_arguments +from advanced_alchemy.types import EncryptedString +from advanced_alchemy.types.encrypted_string import EncryptedText + + +class BigIntAuthor(BigIntAuditBase): + """The Author domain object.""" + + name: Mapped[str] = mapped_column(String(length=100)) + string_field: Mapped[str] = mapped_column(String(20), default="static value", nullable=True) + dob: Mapped[date] = mapped_column(nullable=True) + books: Mapped[List[BigIntBook]] = relationship( + lazy="selectin", + back_populates="author", + cascade="all, delete", + ) + + +class BigIntBook(BigIntBase): + """The Book domain object.""" + + title: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore + author_id: Mapped[int] = mapped_column(ForeignKey("big_int_author.id")) # pyright: ignore + author: Mapped[BigIntAuthor] = relationship( # pyright: ignore + lazy="joined", + innerjoin=True, + back_populates="books", + ) + + +class BigIntSlugBook(BigIntBase, SlugKey): + """The Book domain object with a slug key.""" + + title: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore + author_id: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore + + @declared_attr.directive + @classmethod + def __table_args__(cls) -> TableArgsType: + return merge_table_arguments( + cls, + table_args={"comment": "Slugbook"}, + ) + + +class BigIntEventLog(BigIntAuditBase): + """The event log domain object.""" + + logged_at: Mapped[datetime] = mapped_column(default=datetime.now()) # pyright: ignore + payload: Mapped[dict] = mapped_column(default=lambda: {}) # pyright: ignore + + +class BigIntModelWithFetchedValue(BigIntBase): + """The ModelWithFetchedValue BigIntBase.""" + + val: Mapped[int] # pyright: ignore + updated: Mapped[datetime] = mapped_column( # pyright: ignore + server_default=func.current_timestamp(), + onupdate=func.current_timestamp(), + server_onupdate=FetchedValue(), + ) + + +bigint_item_tag = Table( + "bigint_item_tag", + BigIntBase.metadata, + Column("item_id", ForeignKey("big_int_item.id"), primary_key=True), # pyright: ignore + Column("tag_id", ForeignKey("big_int_tag.id"), primary_key=True), # pyright: ignore +) + + +class BigIntItem(BigIntBase): + name: Mapped[str] = mapped_column(String(length=50)) # pyright: ignore + description: Mapped[str] = mapped_column(String(length=100), nullable=True) # pyright: ignore + tags: Mapped[List[BigIntTag]] = relationship(secondary=lambda: bigint_item_tag, back_populates="items") + + +class BigIntTag(BigIntBase): + """The event log domain object.""" + + name: Mapped[str] = mapped_column(String(length=50)) # pyright: ignore + items: Mapped[List[BigIntItem]] = relationship(secondary=lambda: bigint_item_tag, back_populates="tags") + + +class BigIntRule(BigIntAuditBase): + """The rule domain object.""" + + name: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore + config: Mapped[dict] = mapped_column(default=lambda: {}) # pyright: ignore + + +class BigIntSecret(BigIntBase): + """The secret domain model.""" + + secret: Mapped[str] = mapped_column( + EncryptedString(key="super_secret"), + ) + long_secret: Mapped[str] = mapped_column( + EncryptedText(key="super_secret"), + ) diff --git a/tests/fixtures/bigint/repositories.py b/tests/fixtures/bigint/repositories.py new file mode 100644 index 00000000..06cc6295 --- /dev/null +++ b/tests/fixtures/bigint/repositories.py @@ -0,0 +1,217 @@ +"""Example domain objects for testing.""" + +from __future__ import annotations + +from advanced_alchemy.repository import ( + SQLAlchemyAsyncRepository, + SQLAlchemyAsyncSlugRepository, + SQLAlchemySyncRepository, + SQLAlchemySyncSlugRepository, +) +from advanced_alchemy.repository.memory import ( + SQLAlchemyAsyncMockRepository, + SQLAlchemyAsyncMockSlugRepository, + SQLAlchemySyncMockRepository, + SQLAlchemySyncMockSlugRepository, +) +from tests.fixtures.bigint.models import ( + BigIntAuthor, + BigIntBook, + BigIntEventLog, + BigIntItem, + BigIntModelWithFetchedValue, + BigIntRule, + BigIntSecret, + BigIntSlugBook, + BigIntTag, +) + + +class RuleAsyncRepository(SQLAlchemyAsyncRepository[BigIntRule]): + """Rule repository.""" + + model_type = BigIntRule + + +class RuleAsyncMockRepository(SQLAlchemyAsyncMockRepository[BigIntRule]): + """Rule repository.""" + + model_type = BigIntRule + + +class RuleSyncMockRepository(SQLAlchemySyncMockRepository[BigIntRule]): + """Rule repository.""" + + model_type = BigIntRule + + +class AuthorAsyncRepository(SQLAlchemyAsyncRepository[BigIntAuthor]): + """Author repository.""" + + model_type = BigIntAuthor + + +class AuthorAsyncMockRepository(SQLAlchemyAsyncMockRepository[BigIntAuthor]): + model_type = BigIntAuthor + + +class AuthorSyncMockRepository(SQLAlchemySyncMockRepository[BigIntAuthor]): + model_type = BigIntAuthor + + +class BookAsyncRepository(SQLAlchemyAsyncRepository[BigIntBook]): + """Book repository.""" + + model_type = BigIntBook + + +class BookAsyncMockRepository(SQLAlchemyAsyncMockRepository[BigIntBook]): + """Book repository.""" + + model_type = BigIntBook + + +class BookSyncMockRepository(SQLAlchemySyncMockRepository[BigIntBook]): + """Book repository.""" + + model_type = BigIntBook + + +class EventLogAsyncRepository(SQLAlchemyAsyncRepository[BigIntEventLog]): + """Event log repository.""" + + model_type = BigIntEventLog + + +class ModelWithFetchedValueAsyncRepository(SQLAlchemyAsyncRepository[BigIntModelWithFetchedValue]): + """BigIntModelWithFetchedValue repository.""" + + model_type = BigIntModelWithFetchedValue + + +class SecretAsyncRepository(SQLAlchemyAsyncRepository[BigIntSecret]): + """Secret repository.""" + + model_type = BigIntSecret + + +class TagAsyncRepository(SQLAlchemyAsyncRepository[BigIntTag]): + """Tag repository.""" + + model_type = BigIntTag + + +class TagAsyncMockRepository(SQLAlchemyAsyncMockRepository[BigIntTag]): + """Tag repository.""" + + model_type = BigIntTag + + +class TagSyncMockRepository(SQLAlchemySyncMockRepository[BigIntTag]): + """Tag repository.""" + + model_type = BigIntTag + + +class SecretSyncRepository(SQLAlchemySyncRepository[BigIntSecret]): + """Secret repository.""" + + model_type = BigIntSecret + + +class ItemAsyncRepository(SQLAlchemyAsyncRepository[BigIntItem]): + """Item repository.""" + + model_type = BigIntItem + + +class ItemAsyncMockRepository(SQLAlchemyAsyncMockRepository[BigIntItem]): + """Item repository.""" + + model_type = BigIntItem + + +class ItemSyncMockRepository(SQLAlchemySyncMockRepository[BigIntItem]): + """Item repository.""" + + model_type = BigIntItem + + +class SecretAsyncMockRepository(SQLAlchemyAsyncMockRepository[BigIntSecret]): + """Secret repository.""" + + model_type = BigIntSecret + + +class SecretSyncMockRepository(SQLAlchemySyncMockRepository[BigIntSecret]): + """Secret repository.""" + + model_type = BigIntSecret + + +class AuthorSyncRepository(SQLAlchemySyncRepository[BigIntAuthor]): + """Author repository.""" + + model_type = BigIntAuthor + + +class BookSyncRepository(SQLAlchemySyncRepository[BigIntBook]): + """Book repository.""" + + model_type = BigIntBook + + +class EventLogSyncRepository(SQLAlchemySyncRepository[BigIntEventLog]): + """Event log repository.""" + + model_type = BigIntEventLog + + +class RuleSyncRepository(SQLAlchemySyncRepository[BigIntRule]): + """Rule repository.""" + + model_type = BigIntRule + + +class ModelWithFetchedValueSyncRepository(SQLAlchemySyncRepository[BigIntModelWithFetchedValue]): + """ModelWithFetchedValue repository.""" + + model_type = BigIntModelWithFetchedValue + + +class TagSyncRepository(SQLAlchemySyncRepository[BigIntTag]): + """Tag repository.""" + + model_type = BigIntTag + + +class ItemSyncRepository(SQLAlchemySyncRepository[BigIntItem]): + """Item repository.""" + + model_type = BigIntItem + + +class SlugBookAsyncRepository(SQLAlchemyAsyncSlugRepository[BigIntSlugBook]): + """Slug Book repository.""" + + _uniquify_results = True + model_type = BigIntSlugBook + + +class SlugBookSyncRepository(SQLAlchemySyncSlugRepository[BigIntSlugBook]): + """Slug Book repository.""" + + _uniquify_results = True + model_type = BigIntSlugBook + + +class SlugBookAsyncMockRepository(SQLAlchemyAsyncMockSlugRepository[BigIntSlugBook]): + """Book repository.""" + + model_type = BigIntSlugBook + + +class SlugBookSyncMockRepository(SQLAlchemySyncMockSlugRepository[BigIntSlugBook]): + """Book repository.""" + + model_type = BigIntSlugBook diff --git a/tests/fixtures/bigint/services.py b/tests/fixtures/bigint/services.py new file mode 100644 index 00000000..7ae71556 --- /dev/null +++ b/tests/fixtures/bigint/services.py @@ -0,0 +1,281 @@ +"""Example domain objects for testing.""" + +from __future__ import annotations + +from typing import Any + +from advanced_alchemy.service import ( + SQLAlchemyAsyncRepositoryService, + SQLAlchemySyncRepositoryService, +) +from tests.fixtures.bigint.models import ( + BigIntAuthor, + BigIntBook, + BigIntEventLog, + BigIntItem, + BigIntModelWithFetchedValue, + BigIntRule, + BigIntSecret, + BigIntSlugBook, + BigIntTag, +) +from tests.fixtures.bigint.repositories import ( + AuthorAsyncMockRepository, + AuthorAsyncRepository, + AuthorSyncMockRepository, + AuthorSyncRepository, + BookAsyncMockRepository, + BookAsyncRepository, + BookSyncMockRepository, + BookSyncRepository, + EventLogAsyncRepository, + EventLogSyncRepository, + ItemAsyncMockRepository, + ItemAsyncRepository, + ItemSyncMockRepository, + ItemSyncRepository, + ModelWithFetchedValueAsyncRepository, + ModelWithFetchedValueSyncRepository, + RuleAsyncMockRepository, + RuleAsyncRepository, + RuleSyncMockRepository, + RuleSyncRepository, + SecretAsyncRepository, + SecretSyncRepository, + SlugBookAsyncMockRepository, + SlugBookAsyncRepository, + SlugBookSyncMockRepository, + SlugBookSyncRepository, + TagAsyncMockRepository, + TagAsyncRepository, + TagSyncMockRepository, + TagSyncRepository, +) + + +# Services +class SecretAsyncService(SQLAlchemyAsyncRepositoryService[BigIntSecret]): + """Rule repository.""" + + repository_type = SecretAsyncRepository + + +class RuleAsyncService(SQLAlchemyAsyncRepositoryService[BigIntRule]): + """Rule repository.""" + + repository_type = RuleAsyncRepository + + +class RuleAsyncMockService(SQLAlchemyAsyncRepositoryService[BigIntRule]): + """Rule repository.""" + + repository_type = RuleAsyncMockRepository + + +class RuleSyncMockService(SQLAlchemySyncRepositoryService[BigIntRule]): + """Rule repository.""" + + repository_type = RuleSyncMockRepository + + +class AuthorAsyncService(SQLAlchemyAsyncRepositoryService[BigIntAuthor]): + """Author repository.""" + + repository_type = AuthorAsyncRepository + + +class AuthorAsyncMockService(SQLAlchemyAsyncRepositoryService[BigIntAuthor]): + """Author repository.""" + + repository_type = AuthorAsyncMockRepository + + +class AuthorSyncMockService(SQLAlchemySyncRepositoryService[BigIntAuthor]): + """Author repository.""" + + repository_type = AuthorSyncMockRepository + + +class BookAsyncService(SQLAlchemyAsyncRepositoryService[BigIntBook]): + """Book repository.""" + + repository_type = BookAsyncRepository + + +class BookAsyncMockService(SQLAlchemyAsyncRepositoryService[BigIntBook]): + """Book repository.""" + + repository_type = BookAsyncMockRepository + + +class BookSyncMockService(SQLAlchemySyncRepositoryService[BigIntBook]): + """Book repository.""" + + repository_type = BookSyncMockRepository + + +class EventLogAsyncService(SQLAlchemyAsyncRepositoryService[BigIntEventLog]): + """Event log repository.""" + + repository_type = EventLogAsyncRepository + + +class ModelWithFetchedValueAsyncService(SQLAlchemyAsyncRepositoryService[BigIntModelWithFetchedValue]): + """BigIntModelWithFetchedValue repository.""" + + repository_type = ModelWithFetchedValueAsyncRepository + + +class TagAsyncService(SQLAlchemyAsyncRepositoryService[BigIntTag]): + """Tag repository.""" + + repository_type = TagAsyncRepository + + +class TagAsyncMockService(SQLAlchemyAsyncRepositoryService[BigIntTag]): + """Tag repository.""" + + repository_type = TagAsyncMockRepository + + +class TagSyncMockService(SQLAlchemySyncRepositoryService[BigIntTag]): + """Tag repository.""" + + repository_type = TagSyncMockRepository + + +class ItemAsyncService(SQLAlchemyAsyncRepositoryService[BigIntItem]): + """Item repository.""" + + repository_type = ItemAsyncRepository + + +class ItemAsyncMockService(SQLAlchemyAsyncRepositoryService[BigIntItem]): + """Item repository.""" + + repository_type = ItemAsyncMockRepository + + +class ItemSyncMockService(SQLAlchemySyncRepositoryService[BigIntItem]): + """Item repository.""" + + repository_type = ItemSyncMockRepository + + +class RuleSyncService(SQLAlchemySyncRepositoryService[BigIntRule]): + """Rule repository.""" + + repository_type = RuleSyncRepository + + +class AuthorSyncService(SQLAlchemySyncRepositoryService[BigIntAuthor]): + """Author repository.""" + + repository_type = AuthorSyncRepository + + +class BookSyncService(SQLAlchemySyncRepositoryService[BigIntBook]): + """Book repository.""" + + repository_type = BookSyncRepository + + +class EventLogSyncService(SQLAlchemySyncRepositoryService[BigIntEventLog]): + """Event log repository.""" + + repository_type = EventLogSyncRepository + + +class ModelWithFetchedValueSyncService(SQLAlchemySyncRepositoryService[BigIntModelWithFetchedValue]): + """BigIntModelWithFetchedValue repository.""" + + repository_type = ModelWithFetchedValueSyncRepository + + +class SecretSyncService(SQLAlchemySyncRepositoryService[BigIntSecret]): + """Rule repository.""" + + repository_type = SecretSyncRepository + + +class TagSyncService(SQLAlchemySyncRepositoryService[BigIntTag]): + """Tag repository.""" + + repository_type = TagSyncRepository + + +class ItemSyncService(SQLAlchemySyncRepositoryService[BigIntItem]): + """Item repository.""" + + repository_type = ItemSyncRepository + + +# Slug book + + +class SlugBookAsyncService(SQLAlchemyAsyncRepositoryService[BigIntSlugBook]): + """Book repository.""" + + repository_type = SlugBookAsyncRepository + match_fields = ["title"] + + def __init__(self, **repo_kwargs: Any) -> None: + self.repository: SlugBookAsyncRepository = self.repository_type(**repo_kwargs) # pyright: ignore + + async def to_model(self, data: BigIntSlugBook | dict[str, Any], operation: str | None = None) -> BigIntSlugBook: + if isinstance(data, dict) and "slug" not in data and operation == "create": + data["slug"] = await self.repository.get_available_slug(data["title"]) + if isinstance(data, dict) and "slug" not in data and "title" in data and operation == "update": + data["slug"] = await self.repository.get_available_slug(data["title"]) + return await super().to_model(data, operation) + + +class SlugBookSyncService(SQLAlchemySyncRepositoryService[BigIntSlugBook]): + """Book repository.""" + + repository_type = SlugBookSyncRepository + match_fields = ["title"] + + def __init__(self, **repo_kwargs: Any) -> None: + self.repository = SlugBookSyncRepository(**repo_kwargs) + + def to_model(self, data: BigIntSlugBook | dict[str, Any], operation: str | None = None) -> BigIntSlugBook: + if isinstance(data, dict) and "slug" not in data and operation == "create": + data["slug"] = self.repository.get_available_slug(data["title"]) + if isinstance(data, dict) and "slug" not in data and "title" in data and operation == "update": + data["slug"] = self.repository.get_available_slug(data["title"]) + return super().to_model(data, operation) + + +class SlugBookAsyncMockService(SQLAlchemyAsyncRepositoryService[BigIntSlugBook]): + """Book repository.""" + + repository_type = SlugBookAsyncMockRepository + match_fields = ["title"] + + def __init__(self, **repo_kwargs: Any) -> None: + self.repository = SlugBookAsyncMockRepository(**repo_kwargs) + + async def to_model(self, data: BigIntSlugBook | dict[str, Any], operation: str | None = None) -> BigIntSlugBook: + if isinstance(data, dict) and "slug" not in data and operation == "create": + data["slug"] = await self.repository.get_available_slug(data["title"]) + if isinstance(data, dict) and "slug" not in data and "title" in data and operation == "update": + data["slug"] = await self.repository.get_available_slug(data["title"]) + return await super().to_model(data, operation) + + +class SlugBookSyncMockService(SQLAlchemySyncRepositoryService[BigIntSlugBook]): + """Book repository.""" + + repository_type = SlugBookSyncMockRepository + match_fields = ["title"] + + def __init__(self, **repo_kwargs: Any) -> None: + self.repository: SlugBookSyncMockRepository = self.repository_type(**repo_kwargs) # pyright: ignore + + def to_model(self, data: BigIntSlugBook | dict[str, Any], operation: str | None = None) -> BigIntSlugBook: + if isinstance(data, dict) and "slug" not in data and operation == "create": + data["slug"] = self.repository.get_available_slug(data["title"]) + if isinstance(data, dict) and "slug" not in data and "title" in data and operation == "update": + data["slug"] = self.repository.get_available_slug(data["title"]) + return super().to_model(data, operation) diff --git a/tests/fixtures/uuid/__init__.py b/tests/fixtures/uuid/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/fixtures/uuid/models.py b/tests/fixtures/uuid/models.py new file mode 100644 index 00000000..f218123b --- /dev/null +++ b/tests/fixtures/uuid/models.py @@ -0,0 +1,114 @@ +"""Example domain objects for testing.""" + +from __future__ import annotations + +from datetime import date, datetime +from typing import List +from uuid import UUID + +from sqlalchemy import Column, FetchedValue, ForeignKey, String, Table, func +from sqlalchemy.orm import Mapped, declared_attr, mapped_column, relationship + +from advanced_alchemy.base import ( + SlugKey, + TableArgsType, # pyright: ignore[reportPrivateUsage] + UUIDAuditBase, + UUIDBase, + UUIDv6Base, + UUIDv7Base, + merge_table_arguments, +) +from advanced_alchemy.types.encrypted_string import EncryptedString, EncryptedText + + +class UUIDAuthor(UUIDAuditBase): + """The UUIDAuthor domain object.""" + + name: Mapped[str] = mapped_column(String(length=100)) # pyright: ignore + string_field: Mapped[str] = mapped_column(String(20), default="static value", nullable=True) # pyright: ignore + dob: Mapped[date] = mapped_column(nullable=True) # pyright: ignore + books: Mapped[List[UUIDBook]] = relationship( + lazy="selectin", + back_populates="author", + cascade="all, delete", + ) + + +class UUIDBook(UUIDBase): + """The Book domain object.""" + + title: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore + author_id: Mapped[UUID] = mapped_column(ForeignKey("uuid_author.id")) # pyright: ignore + author: Mapped[UUIDAuthor] = relationship(lazy="joined", innerjoin=True, back_populates="books") # pyright: ignore + + +class UUIDSlugBook(UUIDBase, SlugKey): + """The Book domain object with a slug key.""" + + title: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore + author_id: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore + + @declared_attr.directive + @classmethod + def __table_args__(cls) -> TableArgsType: + return merge_table_arguments( + cls, + table_args={"comment": "Slugbook"}, + ) + + +class UUIDEventLog(UUIDAuditBase): + """The event log domain object.""" + + logged_at: Mapped[datetime] = mapped_column(default=datetime.now()) # pyright: ignore + payload: Mapped[dict] = mapped_column(default={}) # pyright: ignore + + +class UUIDSecret(UUIDv7Base): + """The secret domain model.""" + + secret: Mapped[str] = mapped_column( + EncryptedString(key="super_secret"), + ) + long_secret: Mapped[str] = mapped_column( + EncryptedText(key="super_secret"), + ) + + +class UUIDModelWithFetchedValue(UUIDv6Base): + """The ModelWithFetchedValue UUIDBase.""" + + val: Mapped[int] # pyright: ignore + updated: Mapped[datetime] = mapped_column( # pyright: ignore + server_default=func.current_timestamp(), + onupdate=func.current_timestamp(), + server_onupdate=FetchedValue(), + ) + + +uuid_item_tag = Table( + "uuid_item_tag", + UUIDBase.metadata, + Column("item_id", ForeignKey("uuid_item.id"), primary_key=True), # pyright: ignore + Column("tag_id", ForeignKey("uuid_tag.id"), primary_key=True), # pyright: ignore +) + + +class UUIDItem(UUIDBase): + name: Mapped[str] = mapped_column(String(length=50)) # pyright: ignore + description: Mapped[str] = mapped_column(String(length=100), nullable=True) # pyright: ignore + tags: Mapped[List[UUIDTag]] = relationship(secondary=lambda: uuid_item_tag, back_populates="items") + + +class UUIDTag(UUIDAuditBase): + """The event log domain object.""" + + name: Mapped[str] = mapped_column(String(length=50)) # pyright: ignore + items: Mapped[List[UUIDItem]] = relationship(secondary=lambda: uuid_item_tag, back_populates="tags") + + +class UUIDRule(UUIDAuditBase): + """The rule domain object.""" + + name: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore + config: Mapped[dict] = mapped_column(default=lambda: {}) # pyright: ignore diff --git a/tests/fixtures/uuid/repositories.py b/tests/fixtures/uuid/repositories.py new file mode 100644 index 00000000..69a9d3b2 --- /dev/null +++ b/tests/fixtures/uuid/repositories.py @@ -0,0 +1,217 @@ +"""Example domain objects for testing.""" + +from __future__ import annotations + +from advanced_alchemy.repository import ( + SQLAlchemyAsyncRepository, + SQLAlchemyAsyncSlugRepository, + SQLAlchemySyncRepository, + SQLAlchemySyncSlugRepository, +) +from advanced_alchemy.repository.memory import ( + SQLAlchemyAsyncMockRepository, + SQLAlchemyAsyncMockSlugRepository, + SQLAlchemySyncMockRepository, + SQLAlchemySyncMockSlugRepository, +) +from tests.fixtures.uuid.models import ( + UUIDAuthor, + UUIDBook, + UUIDEventLog, + UUIDItem, + UUIDModelWithFetchedValue, + UUIDRule, + UUIDSecret, + UUIDSlugBook, + UUIDTag, +) + + +class SecretAsyncRepository(SQLAlchemyAsyncRepository[UUIDSecret]): + """Secret repository.""" + + model_type = UUIDSecret + + +class RuleAsyncRepository(SQLAlchemyAsyncRepository[UUIDRule]): + """Rule repository.""" + + model_type = UUIDRule + + +class RuleAsyncMockRepository(SQLAlchemyAsyncMockRepository[UUIDRule]): + """Rule repository.""" + + model_type = UUIDRule + + +class RuleSyncMockRepository(SQLAlchemySyncMockRepository[UUIDRule]): + """Rule repository.""" + + model_type = UUIDRule + + +class AuthorAsyncRepository(SQLAlchemyAsyncRepository[UUIDAuthor]): + """Author repository.""" + + model_type = UUIDAuthor + + +class AuthorAsyncMockRepository(SQLAlchemyAsyncMockRepository[UUIDAuthor]): + model_type = UUIDAuthor + + +class AuthorSyncMockRepository(SQLAlchemySyncMockRepository[UUIDAuthor]): + model_type = UUIDAuthor + + +class BookAsyncRepository(SQLAlchemyAsyncRepository[UUIDBook]): + """Book repository.""" + + model_type = UUIDBook + + +class BookAsyncMockRepository(SQLAlchemyAsyncMockRepository[UUIDBook]): + """Book repository.""" + + model_type = UUIDBook + + +class BookSyncMockRepository(SQLAlchemySyncMockRepository[UUIDBook]): + """Book repository.""" + + model_type = UUIDBook + + +class SlugBookAsyncRepository(SQLAlchemyAsyncSlugRepository[UUIDSlugBook]): + """Book repository.""" + + _uniquify_results = True + model_type = UUIDSlugBook + + +class SlugBookSyncRepository(SQLAlchemySyncSlugRepository[UUIDSlugBook]): + """Slug Book repository.""" + + _uniquify_results = True + model_type = UUIDSlugBook + + +class SlugBookAsyncMockRepository(SQLAlchemyAsyncMockSlugRepository[UUIDSlugBook]): + """Book repository.""" + + model_type = UUIDSlugBook + + +class SlugBookSyncMockRepository(SQLAlchemySyncMockSlugRepository[UUIDSlugBook]): + """Book repository.""" + + model_type = UUIDSlugBook + + +class EventLogAsyncRepository(SQLAlchemyAsyncRepository[UUIDEventLog]): + """Event log repository.""" + + model_type = UUIDEventLog + + +class ModelWithFetchedValueAsyncRepository(SQLAlchemyAsyncRepository[UUIDModelWithFetchedValue]): + """ModelWithFetchedValue repository.""" + + model_type = UUIDModelWithFetchedValue + + +class TagAsyncRepository(SQLAlchemyAsyncRepository[UUIDTag]): + """Tag repository.""" + + model_type = UUIDTag + + +class TagAsyncMockRepository(SQLAlchemyAsyncMockRepository[UUIDTag]): + """Tag repository.""" + + model_type = UUIDTag + + +class TagSyncMockRepository(SQLAlchemySyncMockRepository[UUIDTag]): + """Tag repository.""" + + model_type = UUIDTag + + +class ItemAsyncRepository(SQLAlchemyAsyncRepository[UUIDItem]): + """Item repository.""" + + model_type = UUIDItem + + +class ItemAsyncMockRepository(SQLAlchemyAsyncMockRepository[UUIDItem]): + """Item repository.""" + + model_type = UUIDItem + + +class ItemSyncMockRepository(SQLAlchemySyncMockRepository[UUIDItem]): + """Item repository.""" + + model_type = UUIDItem + + +class SecretAsyncMockRepository(SQLAlchemyAsyncMockRepository[UUIDSecret]): + """Secret repository.""" + + model_type = UUIDSecret + + +class SecretSyncMockRepository(SQLAlchemySyncMockRepository[UUIDSecret]): + """Secret repository.""" + + model_type = UUIDSecret + + +class AuthorSyncRepository(SQLAlchemySyncRepository[UUIDAuthor]): + """Author repository.""" + + model_type = UUIDAuthor + + +class BookSyncRepository(SQLAlchemySyncRepository[UUIDBook]): + """Book repository.""" + + model_type = UUIDBook + + +class SecretSyncRepository(SQLAlchemySyncRepository[UUIDSecret]): + """Secret repository.""" + + model_type = UUIDSecret + + +class EventLogSyncRepository(SQLAlchemySyncRepository[UUIDEventLog]): + """Event log repository.""" + + model_type = UUIDEventLog + + +class RuleSyncRepository(SQLAlchemySyncRepository[UUIDRule]): + """Rule repository.""" + + model_type = UUIDRule + + +class ModelWithFetchedValueSyncRepository(SQLAlchemySyncRepository[UUIDModelWithFetchedValue]): + """ModelWithFetchedValue repository.""" + + model_type = UUIDModelWithFetchedValue + + +class TagSyncRepository(SQLAlchemySyncRepository[UUIDTag]): + """Tag repository.""" + + model_type = UUIDTag + + +class ItemSyncRepository(SQLAlchemySyncRepository[UUIDItem]): + """Item repository.""" + + model_type = UUIDItem diff --git a/tests/fixtures/uuid/services.py b/tests/fixtures/uuid/services.py new file mode 100644 index 00000000..d33bb636 --- /dev/null +++ b/tests/fixtures/uuid/services.py @@ -0,0 +1,276 @@ +"""Example domain objects for testing.""" + +from __future__ import annotations + +from typing import Any + +from advanced_alchemy.service import ( + SQLAlchemyAsyncRepositoryService, + SQLAlchemySyncRepositoryService, +) +from tests.fixtures.uuid.models import ( + UUIDAuthor, + UUIDBook, + UUIDEventLog, + UUIDItem, + UUIDModelWithFetchedValue, + UUIDRule, + UUIDSecret, + UUIDSlugBook, + UUIDTag, +) +from tests.fixtures.uuid.repositories import ( + AuthorAsyncMockRepository, + AuthorAsyncRepository, + AuthorSyncMockRepository, + AuthorSyncRepository, + BookAsyncMockRepository, + BookAsyncRepository, + BookSyncMockRepository, + BookSyncRepository, + EventLogAsyncRepository, + EventLogSyncRepository, + ItemAsyncMockRepository, + ItemAsyncRepository, + ItemSyncMockRepository, + ItemSyncRepository, + ModelWithFetchedValueAsyncRepository, + ModelWithFetchedValueSyncRepository, + RuleAsyncMockRepository, + RuleAsyncRepository, + RuleSyncMockRepository, + RuleSyncRepository, + SecretAsyncRepository, + SecretSyncRepository, + SlugBookAsyncMockRepository, + SlugBookAsyncRepository, + SlugBookSyncMockRepository, + SlugBookSyncRepository, + TagAsyncMockRepository, + TagAsyncRepository, + TagSyncMockRepository, + TagSyncRepository, +) + + +class SecretAsyncService(SQLAlchemyAsyncRepositoryService[UUIDSecret]): + """Rule repository.""" + + repository_type = SecretAsyncRepository + + +class RuleAsyncService(SQLAlchemyAsyncRepositoryService[UUIDRule]): + """Rule repository.""" + + repository_type = RuleAsyncRepository + + +class RuleAsyncMockService(SQLAlchemyAsyncRepositoryService[UUIDRule]): + """Rule repository.""" + + repository_type = RuleAsyncMockRepository + + +class RuleSyncMockService(SQLAlchemySyncRepositoryService[UUIDRule]): + """Rule repository.""" + + repository_type = RuleSyncMockRepository + + +class AuthorAsyncService(SQLAlchemyAsyncRepositoryService[UUIDAuthor]): + """Author repository.""" + + repository_type = AuthorAsyncRepository + + +class AuthorAsyncMockService(SQLAlchemyAsyncRepositoryService[UUIDAuthor]): + """Author repository.""" + + repository_type = AuthorAsyncMockRepository + + +class AuthorSyncMockService(SQLAlchemySyncRepositoryService[UUIDAuthor]): + """Author repository.""" + + repository_type = AuthorSyncMockRepository + + +class BookAsyncService(SQLAlchemyAsyncRepositoryService[UUIDBook]): + """Book repository.""" + + repository_type = BookAsyncRepository + + +class BookAsyncMockService(SQLAlchemyAsyncRepositoryService[UUIDBook]): + """Book repository.""" + + repository_type = BookAsyncMockRepository + + +class BookSyncMockService(SQLAlchemySyncRepositoryService[UUIDBook]): + """Book repository.""" + + repository_type = BookSyncMockRepository + + +class EventLogAsyncService(SQLAlchemyAsyncRepositoryService[UUIDEventLog]): + """Event log repository.""" + + repository_type = EventLogAsyncRepository + + +class ModelWithFetchedValueAsyncService(SQLAlchemyAsyncRepositoryService[UUIDModelWithFetchedValue]): + """UUIDModelWithFetchedValue repository.""" + + repository_type = ModelWithFetchedValueAsyncRepository + + +class TagAsyncService(SQLAlchemyAsyncRepositoryService[UUIDTag]): + """Tag repository.""" + + repository_type = TagAsyncRepository + + +class TagAsyncMockService(SQLAlchemyAsyncRepositoryService[UUIDTag]): + """Tag repository.""" + + repository_type = TagAsyncMockRepository + + +class TagSyncMockService(SQLAlchemySyncRepositoryService[UUIDTag]): + """Tag repository.""" + + repository_type = TagSyncMockRepository + + +class ItemAsyncService(SQLAlchemyAsyncRepositoryService[UUIDItem]): + """Item repository.""" + + repository_type = ItemAsyncRepository + + +class ItemAsyncMockService(SQLAlchemyAsyncRepositoryService[UUIDItem]): + """Item repository.""" + + repository_type = ItemAsyncMockRepository + + +class ItemSyncMockService(SQLAlchemySyncRepositoryService[UUIDItem]): + """Item repository.""" + + repository_type = ItemSyncMockRepository + + +class RuleSyncService(SQLAlchemySyncRepositoryService[UUIDRule]): + """Rule repository.""" + + repository_type = RuleSyncRepository + + +class AuthorSyncService(SQLAlchemySyncRepositoryService[UUIDAuthor]): + """Author repository.""" + + repository_type = AuthorSyncRepository + + +class BookSyncService(SQLAlchemySyncRepositoryService[UUIDBook]): + """Book repository.""" + + repository_type = BookSyncRepository + + +class EventLogSyncService(SQLAlchemySyncRepositoryService[UUIDEventLog]): + """Event log repository.""" + + repository_type = EventLogSyncRepository + + +class ModelWithFetchedValueSyncService(SQLAlchemySyncRepositoryService[UUIDModelWithFetchedValue]): + """UUIDModelWithFetchedValue repository.""" + + repository_type = ModelWithFetchedValueSyncRepository + + +class TagSyncService(SQLAlchemySyncRepositoryService[UUIDTag]): + """Tag repository.""" + + repository_type = TagSyncRepository + + +class ItemSyncService(SQLAlchemySyncRepositoryService[UUIDItem]): + """Item repository.""" + + repository_type = ItemSyncRepository + + +class SecretSyncService(SQLAlchemySyncRepositoryService[UUIDSecret]): + """Rule repository.""" + + repository_type = SecretSyncRepository + + +class SlugBookAsyncService(SQLAlchemyAsyncRepositoryService[UUIDSlugBook]): + """Book repository.""" + + repository_type = SlugBookAsyncRepository + match_fields = ["title"] + + def __init__(self, **repo_kwargs: Any) -> None: + self.repository: SlugBookAsyncRepository = self.repository_type(**repo_kwargs) # pyright: ignore + + async def to_model(self, data: UUIDSlugBook | dict[str, Any], operation: str | None = None) -> UUIDSlugBook: + if isinstance(data, dict) and "slug" not in data and operation == "create": + data["slug"] = await self.repository.get_available_slug(data["title"]) + if isinstance(data, dict) and "slug" not in data and "title" in data and operation == "update": + data["slug"] = await self.repository.get_available_slug(data["title"]) + return await super().to_model(data, operation) + + +class SlugBookSyncService(SQLAlchemySyncRepositoryService[UUIDSlugBook]): + """Book repository.""" + + repository_type = SlugBookSyncRepository + + def __init__(self, **repo_kwargs: Any) -> None: + self.repository: SlugBookSyncRepository = self.repository_type(**repo_kwargs) # pyright: ignore + + def to_model(self, data: UUIDSlugBook | dict[str, Any], operation: str | None = None) -> UUIDSlugBook: + if isinstance(data, dict) and "slug" not in data and operation == "create": + data["slug"] = self.repository.get_available_slug(data["title"]) + if isinstance(data, dict) and "slug" not in data and "title" in data and operation == "update": + data["slug"] = self.repository.get_available_slug(data["title"]) + return super().to_model(data, operation) + + +class SlugBookAsyncMockService(SQLAlchemyAsyncRepositoryService[UUIDSlugBook]): + """Book repository.""" + + repository_type = SlugBookAsyncMockRepository + match_fields = ["title"] + + def __init__(self, **repo_kwargs: Any) -> None: + self.repository: SlugBookAsyncMockRepository = self.repository_type(**repo_kwargs) # pyright: ignore + + async def to_model(self, data: UUIDSlugBook | dict[str, Any], operation: str | None = None) -> UUIDSlugBook: + if isinstance(data, dict) and "slug" not in data and operation == "create": + data["slug"] = await self.repository.get_available_slug(data["title"]) + if isinstance(data, dict) and "slug" not in data and "title" in data and operation == "update": + data["slug"] = await self.repository.get_available_slug(data["title"]) + return await super().to_model(data, operation) + + +class SlugBookSyncMockService(SQLAlchemySyncRepositoryService[UUIDSlugBook]): + """Book repository.""" + + repository_type = SlugBookSyncMockRepository + match_fields = ["title"] + + def __init__(self, **repo_kwargs: Any) -> None: + self.repository: SlugBookSyncMockRepository = self.repository_type(**repo_kwargs) # pyright: ignore + + def to_model(self, data: UUIDSlugBook | dict[str, Any], operation: str | None = None) -> UUIDSlugBook: + if isinstance(data, dict) and "slug" not in data and operation == "create": + data["slug"] = self.repository.get_available_slug(data["title"]) + if isinstance(data, dict) and "slug" not in data and "title" in data and operation == "update": + data["slug"] = self.repository.get_available_slug(data["title"]) + return super().to_model(data, operation) diff --git a/tests/helpers.py b/tests/helpers.py index ce2c39c9..abd37e6b 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -19,8 +19,8 @@ class _ContextManagerWrapper: def __init__(self, cm: ContextManager[T]) -> None: self._cm = cm - async def __aenter__(self) -> T: - return self._cm.__enter__() + async def __aenter__(self) -> T: # pyright: ignore + return self._cm.__enter__() # pyright: ignore async def __aexit__( self, @@ -54,6 +54,6 @@ def wrap_sync(fn: Callable[P, T]) -> Callable[P, Awaitable[T]]: return fn async def wrapped(*args: P.args, **kwargs: P.kwargs) -> T: - return await anyio.to_thread.run_sync(partial(fn, *args, **kwargs)) + return await anyio.to_thread.run_sync(partial(fn, *args, **kwargs)) # pyright: ignore return wrapped diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 71e8c899..6e689a75 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -16,7 +16,7 @@ @pytest.fixture(autouse=True) -def _patch_bases(monkeypatch: MonkeyPatch) -> None: +def _patch_bases(monkeypatch: MonkeyPatch) -> None: # pyright: ignore[reportUnusedFunction] """Ensure new registry state for every test. This prevents errors such as "Table '...' is already defined for @@ -287,13 +287,18 @@ def engine(request: FixtureRequest) -> Engine: @pytest.fixture() -def session(engine: Engine) -> Generator[Session, None, None]: - session = sessionmaker(bind=engine, expire_on_commit=False)() - try: +def session(engine: Engine, request: FixtureRequest) -> Generator[Session, None, None]: + if "mock_sync_engine" in request.fixturenames: + session = create_autospec(Session, instance=True) + session.bind = engine yield session - finally: - session.rollback() - session.close() + else: + session = sessionmaker(bind=engine, expire_on_commit=False)() + try: + yield session + finally: + session.rollback() + session.close() @pytest.fixture() @@ -448,7 +453,10 @@ async def oracle23c_async_engine(docker_ip: str, oracle23c_service: None) -> Asy @pytest.fixture() async def mock_async_engine() -> NonCallableMagicMock: """Return a mocked AsyncEngine instance.""" - return cast(NonCallableMagicMock, create_autospec(AsyncEngine, instance=True)) + mock = cast(NonCallableMagicMock, create_autospec(AsyncEngine, instance=True)) + mock.dialect = create_autospec(Dialect, instance=True) + mock.dialect.name = "mock" + return mock @pytest.fixture( @@ -535,13 +543,19 @@ def async_engine(request: FixtureRequest) -> AsyncEngine: @pytest.fixture() async def async_session( async_engine: AsyncEngine, + request: FixtureRequest, ) -> AsyncGenerator[AsyncSession, None]: - session = async_sessionmaker(bind=async_engine, expire_on_commit=False)() - try: + if "mock_async_engine" in request.fixturenames: + session = create_autospec(AsyncSession, instance=True) + session.bind = async_engine yield session - finally: - await session.rollback() - await session.close() + else: + session = async_sessionmaker(bind=async_engine, expire_on_commit=False)() + try: + yield session + finally: + await session.rollback() + await session.close() # @pytest.fixture() diff --git a/tests/integration/test_alembic_commands.py b/tests/integration/test_alembic_commands.py index c6a8cf7c..6af51be2 100644 --- a/tests/integration/test_alembic_commands.py +++ b/tests/integration/test_alembic_commands.py @@ -14,7 +14,7 @@ from advanced_alchemy.alembic import commands from advanced_alchemy.config import SQLAlchemyAsyncConfig, SQLAlchemySyncConfig from alembic.util.exc import CommandError -from tests import models_uuid +from tests.fixtures.uuid import models as models_uuid AuthorModel = Type[models_uuid.UUIDAuthor] RuleModel = Type[models_uuid.UUIDRule] diff --git a/tests/integration/test_oracledb_json.py b/tests/integration/test_oracledb_json.py index db95ddc5..9aea6d37 100644 --- a/tests/integration/test_oracledb_json.py +++ b/tests/integration/test_oracledb_json.py @@ -9,7 +9,7 @@ from sqlalchemy.dialects import oracle from sqlalchemy.schema import CreateTable -from tests.models_uuid import UUIDEventLog +from tests.fixtures.uuid.models import UUIDEventLog if TYPE_CHECKING: from sqlalchemy import Engine diff --git a/tests/integration/test_repository.py b/tests/integration/test_repository.py index a8491aa1..fd98ea2e 100644 --- a/tests/integration/test_repository.py +++ b/tests/integration/test_repository.py @@ -7,7 +7,7 @@ import os from datetime import date, datetime, timedelta, timezone from typing import TYPE_CHECKING, Any, Dict, Generator, Iterator, List, Literal, Type, Union, cast -from unittest.mock import NonCallableMagicMock, create_autospec +from unittest.mock import NonCallableMagicMock from uuid import UUID, uuid4 import pytest @@ -34,6 +34,7 @@ from advanced_alchemy.repository._util import get_instrumented_attr, model_from_dict from advanced_alchemy.repository.memory import ( SQLAlchemyAsyncMockRepository, + SQLAlchemyAsyncMockSlugRepository, SQLAlchemySyncMockRepository, SQLAlchemySyncMockSlugRepository, ) @@ -42,7 +43,12 @@ ) from advanced_alchemy.service.pagination import OffsetPagination from advanced_alchemy.utils.text import slugify -from tests import models_bigint, models_uuid +from tests.fixtures.bigint import models as models_bigint +from tests.fixtures.bigint import repositories as repositories_bigint +from tests.fixtures.bigint import services as services_bigint +from tests.fixtures.uuid import models as models_uuid +from tests.fixtures.uuid import repositories as repositories_uuid +from tests.fixtures.uuid import services as services_uuid from tests.helpers import maybe_async from tests.integration.helpers import update_raw_records @@ -658,14 +664,14 @@ def session( if engine.dialect.name.startswith("spanner"): try: - author_repo = models_uuid.AuthorSyncRepository(session=session) + author_repo = repositories_uuid.AuthorSyncRepository(session=session) for author in raw_authors: _ = author_repo.get_or_upsert(match_fields="name", **author) - secret_repo = models_uuid.SecretSyncRepository(session=session) + secret_repo = repositories_uuid.SecretSyncRepository(session=session) for secret in raw_secrets: _ = secret_repo.get_or_upsert(match_fields="id", **secret) if not bool(os.environ.get("SPANNER_EMULATOR_HOST")): - rule_repo = models_uuid.RuleSyncRepository(session=session) + rule_repo = repositories_uuid.RuleSyncRepository(session=session) for rule in raw_rules: _ = rule_repo.add(models_uuid.UUIDRule(**rule)) yield session @@ -839,7 +845,14 @@ async def any_engine( def repository_module(repository_pk_type: RepositoryPKType, request: FixtureRequest) -> Any: if repository_pk_type == "bigint" and mock_engines.intersection(set(request.fixturenames)): pytest.skip("Skipping additional bigint mock repository tests") - return models_uuid if repository_pk_type == "uuid" else models_bigint + return repositories_uuid if repository_pk_type == "uuid" else repositories_bigint + + +@pytest.fixture() +def service_module(repository_pk_type: RepositoryPKType, request: FixtureRequest) -> Any: + if repository_pk_type == "bigint" and mock_engines.intersection(set(request.fixturenames)): + pytest.skip("Skipping additional bigint mock repository tests") + return services_uuid if repository_pk_type == "uuid" else services_bigint @pytest.fixture() @@ -850,9 +863,9 @@ def author_repo( ) -> AuthorRepository: """Return an AuthorAsyncRepository or AuthorSyncRepository based on the current PK and session type""" if "mock_async_engine" in request.fixturenames: - repo = repository_module.AuthorAsyncMockRepository() + repo = repository_module.AuthorAsyncMockRepository(session=any_session) elif "mock_sync_engine" in request.fixturenames: - repo = repository_module.AuthorSyncMockRepository() + repo = repository_module.AuthorSyncMockRepository(session=any_session) elif isinstance(any_session, AsyncSession): repo = repository_module.AuthorAsyncRepository(session=any_session) else: @@ -868,9 +881,9 @@ def secret_repo( ) -> SecretRepository: """Return an SecretAsyncRepository or SecretSyncRepository based on the current PK and session type""" if "mock_async_engine" in request.fixturenames: - repo = repository_module.SecretAsyncMockRepository() + repo = repository_module.SecretAsyncMockRepository(session=any_session) elif "mock_sync_engine" in request.fixturenames: - repo = repository_module.SecretSyncMockRepository() + repo = repository_module.SecretSyncMockRepository(session=any_session) elif isinstance(any_session, AsyncSession): repo = repository_module.SecretAsyncRepository(session=any_session) else: @@ -881,18 +894,18 @@ def secret_repo( @pytest.fixture() def author_service( any_session: AsyncSession | Session, - repository_module: Any, + service_module: Any, request: FixtureRequest, ) -> AuthorService: """Return an AuthorAsyncService or AuthorSyncService based on the current PK and session type""" if "mock_async_engine" in request.fixturenames: - repo = repository_module.AuthorAsyncMockService(session=create_autospec(any_session, instance=True)) + repo = service_module.AuthorAsyncMockService(session=any_session) elif "mock_sync_engine" in request.fixturenames: - repo = repository_module.AuthorSyncMockService(session=create_autospec(any_session, instance=True)) + repo = service_module.AuthorSyncMockService(session=any_session) elif isinstance(any_session, AsyncSession): - repo = repository_module.AuthorAsyncService(session=any_session) + repo = service_module.AuthorAsyncService(session=any_session) else: - repo = repository_module.AuthorSyncService(session=any_session) + repo = service_module.AuthorSyncService(session=any_session) return cast(AuthorService, repo) @@ -900,9 +913,9 @@ def author_service( def rule_repo(any_session: AsyncSession | Session, repository_module: Any, request: FixtureRequest) -> RuleRepository: """Return an RuleAsyncRepository or RuleSyncRepository based on the current PK and session type""" if "mock_async_engine" in request.fixturenames: - repo = repository_module.RuleAsyncMockRepository() + repo = repository_module.RuleAsyncMockRepository(session=any_session) elif "mock_sync_engine" in request.fixturenames: - repo = repository_module.RuleSyncMockRepository() + repo = repository_module.RuleSyncMockRepository(session=any_session) elif isinstance(any_session, AsyncSession): repo = repository_module.RuleAsyncRepository(session=any_session) else: @@ -911,16 +924,16 @@ def rule_repo(any_session: AsyncSession | Session, repository_module: Any, reque @pytest.fixture() -def rule_service(any_session: AsyncSession | Session, repository_module: Any, request: FixtureRequest) -> RuleService: +def rule_service(any_session: AsyncSession | Session, service_module: Any, request: FixtureRequest) -> RuleService: """Return an RuleAsyncService or RuleSyncService based on the current PK and session type""" if "mock_async_engine" in request.fixturenames: - repo = repository_module.RuleAsyncMockService(session=create_autospec(any_session, instance=True)) + repo = service_module.RuleAsyncMockService(session=any_session) elif "mock_sync_engine" in request.fixturenames: - repo = repository_module.RuleSyncMockService(session=create_autospec(any_session, instance=True)) + repo = service_module.RuleSyncMockService(session=any_session) elif isinstance(any_session, AsyncSession): - repo = repository_module.RuleAsyncService(session=any_session) + repo = service_module.RuleAsyncService(session=any_session) else: - repo = repository_module.RuleSyncService(session=any_session) + repo = service_module.RuleSyncService(session=any_session) return cast(RuleService, repo) @@ -928,9 +941,9 @@ def rule_service(any_session: AsyncSession | Session, repository_module: Any, re def book_repo(any_session: AsyncSession | Session, repository_module: Any, request: FixtureRequest) -> BookRepository: """Return an BookAsyncRepository or BookSyncRepository based on the current PK and session type""" if "mock_async_engine" in request.fixturenames: - repo = repository_module.BookAsyncMockRepository() + repo = repository_module.BookAsyncMockRepository(session=any_session) elif "mock_sync_engine" in request.fixturenames: - repo = repository_module.BookSyncMockRepository() + repo = repository_module.BookSyncMockRepository(session=any_session) elif isinstance(any_session, AsyncSession): repo = repository_module.BookAsyncRepository(session=any_session) else: @@ -939,16 +952,16 @@ def book_repo(any_session: AsyncSession | Session, repository_module: Any, reque @pytest.fixture() -def book_service(any_session: AsyncSession | Session, repository_module: Any, request: FixtureRequest) -> BookService: +def book_service(any_session: AsyncSession | Session, service_module: Any, request: FixtureRequest) -> BookService: """Return an BookAsyncService or BookSyncService based on the current PK and session type""" if "mock_async_engine" in request.fixturenames: - repo = repository_module.BookAsyncMockService(session=create_autospec(any_session, instance=True)) + repo = service_module.BookAsyncMockService(session=any_session) elif "mock_sync_engine" in request.fixturenames: - repo = repository_module.BookSyncMockService(session=create_autospec(any_session, instance=True)) + repo = service_module.BookSyncMockService(session=any_session) elif isinstance(any_session, AsyncSession): - repo = repository_module.BookAsyncService(session=any_session) + repo = service_module.BookAsyncService(session=any_session) else: - repo = repository_module.BookSyncService(session=any_session) + repo = service_module.BookSyncService(session=any_session) return cast(BookService, repo) @@ -960,41 +973,41 @@ def slug_book_repo( ) -> SlugBookRepository: """Return an SlugBookAsyncRepository or SlugBookSyncRepository based on the current PK and session type""" if "mock_async_engine" in request.fixturenames: - repo = repository_module.SlugBookAsyncMockRepository() + repo = repository_module.SlugBookAsyncMockRepository(session=any_session) elif "mock_sync_engine" in request.fixturenames: - repo = repository_module.SlugBookSyncMockRepository() + repo = repository_module.SlugBookSyncMockRepository(session=any_session) elif isinstance(any_session, AsyncSession): repo = repository_module.SlugBookAsyncRepository(session=any_session) else: repo = repository_module.SlugBookSyncRepository(session=any_session) - return cast(SlugBookRepository, repo) + return cast("SlugBookRepository", repo) @pytest.fixture() def slug_book_service( any_session: AsyncSession | Session, - repository_module: Any, + service_module: Any, request: FixtureRequest, ) -> SlugBookService: """Return an SlugBookAsyncService or SlugBookSyncService based on the current PK and session type""" if "mock_async_engine" in request.fixturenames: - repo = repository_module.SlugBookAsyncMockService(session=create_autospec(any_session, instance=True)) + svc = service_module.SlugBookAsyncMockService(session=any_session) elif "mock_sync_engine" in request.fixturenames: - repo = repository_module.SlugBookSyncMockService(session=create_autospec(any_session, instance=True)) + svc = service_module.SlugBookSyncMockService(session=any_session) elif isinstance(any_session, AsyncSession): - repo = repository_module.SlugBookAsyncService(session=any_session) + svc = service_module.SlugBookAsyncService(session=any_session) else: - repo = repository_module.SlugBookSyncService(session=any_session) - return cast(SlugBookService, repo) + svc = service_module.SlugBookSyncService(session=any_session) + return cast("SlugBookService", svc) @pytest.fixture() def tag_repo(any_session: AsyncSession | Session, repository_module: Any, request: FixtureRequest) -> ItemRepository: """Return an TagAsyncRepository or TagSyncRepository based on the current PK and session type""" if "mock_async_engine" in request.fixturenames: - repo = repository_module.TagAsyncMockRepository() + repo = repository_module.TagAsyncMockRepository(session=any_session) elif "mock_sync_engine" in request.fixturenames: - repo = repository_module.TagSyncMockRepository() + repo = repository_module.TagSyncMockRepository(session=any_session) elif isinstance(any_session, AsyncSession): repo = repository_module.TagAsyncRepository(session=any_session) else: @@ -1004,16 +1017,16 @@ def tag_repo(any_session: AsyncSession | Session, repository_module: Any, reques @pytest.fixture() -def tag_service(any_session: AsyncSession | Session, repository_module: Any, request: FixtureRequest) -> TagService: +def tag_service(any_session: AsyncSession | Session, service_module: Any, request: FixtureRequest) -> TagService: """Return an TagAsyncService or TagSyncService based on the current PK and session type""" if "mock_async_engine" in request.fixturenames: - repo = repository_module.TagAsyncMockService(session=create_autospec(any_session, instance=True)) + repo = service_module.TagAsyncMockService(session=any_session) elif "mock_sync_engine" in request.fixturenames: - repo = repository_module.TagSyncMockService(session=create_autospec(any_session, instance=True)) + repo = service_module.TagSyncMockService(session=any_session) elif isinstance(any_session, AsyncSession): - repo = repository_module.TagAsyncService(session=any_session) + repo = service_module.TagAsyncService(session=any_session) else: - repo = repository_module.TagSyncService(session=any_session) + repo = service_module.TagSyncService(session=any_session) return cast(TagService, repo) @@ -1021,9 +1034,9 @@ def tag_service(any_session: AsyncSession | Session, repository_module: Any, req def item_repo(any_session: AsyncSession | Session, repository_module: Any, request: FixtureRequest) -> ItemRepository: """Return an ItemAsyncRepository or ItemSyncRepository based on the current PK and session type""" if "mock_async_engine" in request.fixturenames: - repo = repository_module.ItemAsyncMockRepository() + repo = repository_module.ItemAsyncMockRepository(session=any_session) elif "mock_sync_engine" in request.fixturenames: - repo = repository_module.ItemSyncMockRepository() + repo = repository_module.ItemSyncMockRepository(session=any_session) elif isinstance(any_session, AsyncSession): repo = repository_module.ItemAsyncRepository(session=any_session) else: @@ -1033,16 +1046,16 @@ def item_repo(any_session: AsyncSession | Session, repository_module: Any, reque @pytest.fixture() -def item_service(any_session: AsyncSession | Session, repository_module: Any, request: FixtureRequest) -> ItemService: +def item_service(any_session: AsyncSession | Session, service_module: Any, request: FixtureRequest) -> ItemService: """Return an ItemAsyncService or ItemSyncService based on the current PK and session type""" if "mock_async_engine" in request.fixturenames: - repo = repository_module.ItemAsyncMockService(session=create_autospec(any_session, instance=True)) + repo = service_module.ItemAsyncMockService(session=any_session) elif "mock_sync_engine" in request.fixturenames: - repo = repository_module.ItemSyncMockService(session=create_autospec(any_session, instance=True)) + repo = service_module.ItemSyncMockService(session=any_session) elif isinstance(any_session, AsyncSession): - repo = repository_module.ItemAsyncService(session=any_session) + repo = service_module.ItemAsyncService(session=any_session) else: - repo = repository_module.ItemSyncService(session=any_session) + repo = service_module.ItemSyncService(session=any_session) return cast(ItemService, repo) @@ -1173,7 +1186,7 @@ async def test_repo_list_and_count_method_empty(book_repo: BookRepository) -> No @pytest.fixture() def frozen_datetime() -> Generator[Coordinates, None, None]: - with travel(datetime.utcnow, tick=False) as frozen: # pyright: ignore[reportDeprecated] + with travel(datetime.utcnow, tick=False) as frozen: # pyright: ignore[reportDeprecated,reportCallIssue] yield frozen @@ -1793,25 +1806,25 @@ async def test_repo_json_methods( assert exp_count == count assert isinstance(obj, rule_model) assert new_rule.name == obj.name - assert new_rule.config == obj.config + assert new_rule.config == obj.config # pyright: ignore assert obj.id is not None obj.config = {"the": "update"} updated = await maybe_async(rule_repo.update(obj)) - assert obj.config == updated.config + assert obj.config == updated.config # pyright: ignore get_obj, get_created = await maybe_async( rule_repo.get_or_upsert(match_fields=["name"], name="Secondary loading rule.", config={"another": "object"}), ) assert get_created is False assert get_obj.id is not None - assert get_obj.config == {"another": "object"} + assert get_obj.config == {"another": "object"} # pyright: ignore new_obj, new_created = await maybe_async( rule_repo.get_or_upsert(match_fields=["name"], name="New rule.", config={"new": "object"}), ) assert new_created is True assert new_obj.id is not None - assert new_obj.config == {"new": "object"} + assert new_obj.config == {"new": "object"} # pyright: ignore async def test_repo_fetched_value( @@ -1858,8 +1871,8 @@ async def test_lazy_load( "id": first_item_id, } tags_to_add = await maybe_async(tag_repo.list(CollectionFilter("name", update_data.pop("tag_names", [])))) # type: ignore - assert len(tags_to_add) > 0 - assert tags_to_add[0].id is not None + assert len(tags_to_add) > 0 # pyright: ignore + assert tags_to_add[0].id is not None # pyright: ignore update_data["tags"] = tags_to_add # type: ignore[assignment] updated_obj = await maybe_async(item_repo.update(item_model(**update_data), auto_refresh=False)) await maybe_async(item_repo.session.commit()) @@ -1872,6 +1885,51 @@ async def test_repo_health_check(author_repo: AnyAuthorRepository) -> None: assert healthy +async def test_repo_custom_statement(author_repo: AnyAuthorRepository, author_service: AuthorService) -> None: + """Test Repo with custom statement + + Args: + author_repo: The author mock repository + """ + service_type = type(author_service) + new_service = service_type(session=author_repo.session, statement=select(author_repo.model_type)) + assert await maybe_async(new_service.count()) == 2 + + +async def test_repo_get_or_create_deprecation(author_repo: AnyAuthorRepository, first_author_id: Any) -> None: + with pytest.deprecated_call(): + existing_obj, existing_created = await maybe_async(author_repo.get_or_create(name="Agatha Christie")) + assert str(existing_obj.id) == str(first_author_id) + assert existing_created is False + + +async def test_repo_encrypted_methods( + raw_secrets_uuid: RawRecordData, + secret_repo: SecretRepository, + raw_secrets: RawRecordData, + first_secret_id: Any, + secret_model: SecretModel, +) -> None: + existing_obj = await maybe_async(secret_repo.get(first_secret_id)) + assert existing_obj.secret == raw_secrets[0]["secret"] + assert existing_obj.long_secret == raw_secrets[0]["long_secret"] + + exp_count = len(raw_secrets_uuid) + 1 + new_secret = secret_model(secret="hidden data", long_secret="another longer secret") + obj = await maybe_async(secret_repo.add(new_secret)) + count = await maybe_async(secret_repo.count()) + assert exp_count == count + assert isinstance(obj, secret_model) + assert new_secret.secret == obj.secret + assert new_secret.long_secret == obj.long_secret + assert obj.id is not None + obj.secret = "new secret value" + obj.long_secret = "new long secret value" + updated = await maybe_async(secret_repo.update(obj)) + assert obj.secret == updated.secret + assert obj.long_secret == updated.long_secret + + # service tests async def test_service_filter_search(author_service: AuthorService) -> None: existing_obj = await maybe_async( @@ -2066,7 +2124,7 @@ async def test_service_create_many_method( async def test_service_update_many_method(author_service: AuthorService) -> None: - if author_service.repository._dialect.name.startswith("spanner") and os.environ.get("SPANNER_EMULATOR_HOST"): # pyright: ignore[reportPrivateUsage] + if author_service.repository._dialect.name.startswith("spanner") and os.environ.get("SPANNER_EMULATOR_HOST"): # pyright: ignore[reportPrivateUsage,reportUnknownMemberType,reportAttributeAccessIssue] pytest.skip("Skipped on emulator") objs = await maybe_async(author_service.list()) @@ -2140,7 +2198,7 @@ async def test_service_delete_many_method(author_service: AuthorService, author_ all_objs = await maybe_async(author_service.list()) ids_to_delete = [existing_obj.id for existing_obj in all_objs] objs = await maybe_async(author_service.delete_many(ids_to_delete)) - await maybe_async(author_service.repository.session.commit()) + await maybe_async(author_service.repository.session.commit()) # pyright: ignore[reportUnknownArgumentType,reportUnknownMemberType,reportAttributeAccessIssue] assert len(objs) > 0 data, count = await maybe_async(author_service.list_and_count()) assert data == [] @@ -2250,7 +2308,7 @@ async def test_service_upsert_method_match( author_model: AuthorModel, new_pk_id: Any, ) -> None: - if author_service.repository._dialect.name.startswith("spanner") and os.environ.get("SPANNER_EMULATOR_HOST"): # pyright: ignore[reportPrivateUsage] + if author_service.repository._dialect.name.startswith("spanner") and os.environ.get("SPANNER_EMULATOR_HOST"): # pyright: ignore[reportPrivateUsage,reportUnknownMemberType,reportAttributeAccessIssue] pytest.skip( "Skipped on emulator. See the following: https://github.com/GoogleCloudPlatform/cloud-spanner-emulator/issues/73", ) @@ -2281,7 +2339,7 @@ async def test_service_upsert_many_method( author_service: AuthorService, author_model: AuthorModel, ) -> None: - if author_service.repository._dialect.name.startswith("spanner") and os.environ.get("SPANNER_EMULATOR_HOST"): # pyright: ignore[reportPrivateUsage] + if author_service.repository._dialect.name.startswith("spanner") and os.environ.get("SPANNER_EMULATOR_HOST"): # pyright: ignore[reportPrivateUsage,reportUnknownMemberType,reportAttributeAccessIssue] pytest.skip( "Skipped on emulator. See the following: https://github.com/GoogleCloudPlatform/cloud-spanner-emulator/issues/73", ) @@ -2309,7 +2367,7 @@ async def test_service_upsert_many_method_match_fields_id( author_service: AuthorService, author_model: AuthorModel, ) -> None: - if author_service.repository._dialect.name.startswith("spanner") and os.environ.get("SPANNER_EMULATOR_HOST"): # pyright: ignore[reportPrivateUsage] + if author_service.repository._dialect.name.startswith("spanner") and os.environ.get("SPANNER_EMULATOR_HOST"): # pyright: ignore[reportPrivateUsage,reportUnknownMemberType,reportAttributeAccessIssue] pytest.skip( "Skipped on emulator. See the following: https://github.com/GoogleCloudPlatform/cloud-spanner-emulator/issues/73", ) @@ -2338,7 +2396,7 @@ async def test_service_upsert_many_method_match_fields_non_id( author_service: AuthorService, author_model: AuthorModel, ) -> None: - if author_service.repository._dialect.name.startswith("spanner") and os.environ.get("SPANNER_EMULATOR_HOST"): # pyright: ignore[reportPrivateUsage] + if author_service.repository._dialect.name.startswith("spanner") and os.environ.get("SPANNER_EMULATOR_HOST"): # pyright: ignore[reportPrivateUsage,reportUnknownMemberType,reportAttributeAccessIssue] pytest.skip( "Skipped on emulator. See the following: https://github.com/GoogleCloudPlatform/cloud-spanner-emulator/issues/73", ) @@ -2360,56 +2418,11 @@ async def test_service_upsert_many_method_match_fields_non_id( assert existing_count_now > existing_count -async def test_repo_custom_statement(author_repo: AnyAuthorRepository, author_service: AuthorService) -> None: - """Test Repo with custom statement - - Args: - author_repo: The author mock repository - """ - service_type = type(author_service) - new_service = service_type(session=author_repo.session, statement=select(author_repo.model_type)) - assert await maybe_async(new_service.count()) == 2 - - -async def test_repo_get_or_create_deprecation(author_repo: AnyAuthorRepository, first_author_id: Any) -> None: - with pytest.deprecated_call(): - existing_obj, existing_created = await maybe_async(author_repo.get_or_create(name="Agatha Christie")) - assert str(existing_obj.id) == str(first_author_id) - assert existing_created is False - - async def test_service_update_no_pk(author_service: AuthorService) -> None: with pytest.raises(RepositoryError): _existing_obj = await maybe_async(author_service.update(data={"name": "Agatha Christie"})) -async def test_repo_encrypted_methods( - raw_secrets_uuid: RawRecordData, - secret_repo: SecretRepository, - raw_secrets: RawRecordData, - first_secret_id: Any, - secret_model: SecretModel, -) -> None: - existing_obj = await maybe_async(secret_repo.get(first_secret_id)) - assert existing_obj.secret == raw_secrets[0]["secret"] - assert existing_obj.long_secret == raw_secrets[0]["long_secret"] - - exp_count = len(raw_secrets_uuid) + 1 - new_secret = secret_model(secret="hidden data", long_secret="another longer secret") - obj = await maybe_async(secret_repo.add(new_secret)) - count = await maybe_async(secret_repo.count()) - assert exp_count == count - assert isinstance(obj, secret_model) - assert new_secret.secret == obj.secret - assert new_secret.long_secret == obj.long_secret - assert obj.id is not None - obj.secret = "new secret value" - obj.long_secret = "new long secret value" - updated = await maybe_async(secret_repo.update(obj)) - assert obj.secret == updated.secret - assert obj.long_secret == updated.long_secret - - async def test_service_create_method_slug( raw_slug_books: RawRecordData, slug_book_service: SlugBookService, @@ -2428,13 +2441,13 @@ async def test_service_create_method_slug_existing( slug_book_service: SlugBookService, slug_book_model: SlugBookModel, ) -> None: - if issubclass( + if isinstance( slug_book_service.repository_type, ( SQLAlchemySyncMockSlugRepository, + SQLAlchemyAsyncMockSlugRepository, SQLAlchemyAsyncMockRepository, - SQLAlchemyAsyncMockRepository, - SQLAlchemyAsyncMockRepository, + SQLAlchemySyncMockRepository, ), ): pytest.skip("Skipping additional bigint mock repository tests") @@ -2496,10 +2509,10 @@ async def test_service_paginated_to_schema(raw_authors: RawRecordData, author_se assert isinstance(model_dto.items[0].name, str) assert model_dto.total == exp_count assert isinstance(pydantic_dto, OffsetPagination) - assert isinstance(pydantic_dto.items[0].name, str) + assert isinstance(pydantic_dto.items[0].name, str) # pyright: ignore assert pydantic_dto.total == exp_count assert isinstance(msgspec_dto, OffsetPagination) - assert isinstance(msgspec_dto.items[0].name, str) + assert isinstance(msgspec_dto.items[0].name, str) # pyright: ignore assert msgspec_dto.total == exp_count @@ -2517,6 +2530,10 @@ async def test_service_to_schema( model_dto = author_service.to_schema(data=obj) pydantic_dto = author_service.to_schema(data=obj, schema_type=AuthorBaseModel) msgspec_dto = author_service.to_schema(data=obj, schema_type=AuthorStruct) + assert issubclass(AuthorStruct, Struct) + assert issubclass(AuthorBaseModel, BaseModel) assert isinstance(model_dto.name, str) - assert isinstance(pydantic_dto.name, str) - assert isinstance(msgspec_dto.name, str) + assert isinstance(pydantic_dto, BaseModel) + assert isinstance(msgspec_dto, Struct) + assert isinstance(pydantic_dto.name, str) # pyright: ignore + assert isinstance(msgspec_dto.name, str) # pyright: ignore diff --git a/tests/integration/test_sqlquery_service.py b/tests/integration/test_sqlquery_service.py index e6f2af9f..5efa8c08 100644 --- a/tests/integration/test_sqlquery_service.py +++ b/tests/integration/test_sqlquery_service.py @@ -82,7 +82,7 @@ def test_sync_fixture_and_query() -> None: repo = USStateSyncRepository(session=session) query_service = SQLAlchemySyncQueryService(session=session) fixture = open_fixture(fixture_path, USStateSyncRepository.model_type.__tablename__) # type: ignore[has-type] - _add_objs = repo.add_many([USStateSyncRepository.model_type(**raw_obj) for raw_obj in fixture]) + _add_objs = repo.add_many([USState(**raw_obj) for raw_obj in fixture]) query_count = query_service.repository.count(statement=select(StateQuery)) assert query_count > 0 list_query_objs, list_query_count = query_service.repository.list_and_count( diff --git a/tests/models_bigint.py b/tests/models_bigint.py deleted file mode 100644 index 74ab9629..00000000 --- a/tests/models_bigint.py +++ /dev/null @@ -1,545 +0,0 @@ -"""Example domain objects for testing.""" - -from __future__ import annotations - -from datetime import date, datetime -from typing import Any, List - -from sqlalchemy import Column, FetchedValue, ForeignKey, String, Table, func -from sqlalchemy.orm import Mapped, declared_attr, mapped_column, relationship -from sqlalchemy.orm.decl_base import _TableArgsType as TableArgsType # pyright: ignore[reportPrivateUsage] - -from advanced_alchemy.base import BigIntAuditBase, BigIntBase, SlugKey, merge_table_arguments -from advanced_alchemy.repository import ( - SQLAlchemyAsyncRepository, - SQLAlchemyAsyncSlugRepository, - SQLAlchemySyncRepository, - SQLAlchemySyncSlugRepository, -) -from advanced_alchemy.repository.memory import ( - SQLAlchemyAsyncMockRepository, - SQLAlchemyAsyncMockSlugRepository, - SQLAlchemySyncMockRepository, - SQLAlchemySyncMockSlugRepository, -) -from advanced_alchemy.service import ( - SQLAlchemyAsyncRepositoryService, - SQLAlchemySyncRepositoryService, -) -from advanced_alchemy.types import EncryptedString -from advanced_alchemy.types.encrypted_string import EncryptedText - - -class BigIntAuthor(BigIntAuditBase): - """The Author domain object.""" - - name: Mapped[str] = mapped_column(String(length=100)) # pyright: ignore - string_field: Mapped[str] = mapped_column(String(20), default="static value", nullable=True) # pyright: ignore - dob: Mapped[date] = mapped_column(nullable=True) # pyright: ignore - books: Mapped[List[BigIntBook]] = relationship( - lazy="selectin", - back_populates="author", - cascade="all, delete", - ) - - -class BigIntBook(BigIntBase): - """The Book domain object.""" - - title: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore - author_id: Mapped[int] = mapped_column(ForeignKey("big_int_author.id")) # pyright: ignore - author: Mapped[BigIntAuthor] = relationship( # pyright: ignore - lazy="joined", - innerjoin=True, - back_populates="books", - ) - - -class BigIntSlugBook(BigIntBase, SlugKey): - """The Book domain object with a slug key.""" - - title: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore - author_id: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore - - @declared_attr.directive - @classmethod - def __table_args__(cls) -> TableArgsType: - return merge_table_arguments( - cls, - table_args={"comment": "Slugbook"}, - ) - - -class BigIntEventLog(BigIntAuditBase): - """The event log domain object.""" - - logged_at: Mapped[datetime] = mapped_column(default=datetime.now()) # pyright: ignore - payload: Mapped[dict] = mapped_column(default=lambda: {}) # pyright: ignore - - -class BigIntModelWithFetchedValue(BigIntBase): - """The ModelWithFetchedValue BigIntBase.""" - - val: Mapped[int] # pyright: ignore - updated: Mapped[datetime] = mapped_column( # pyright: ignore - server_default=func.current_timestamp(), - onupdate=func.current_timestamp(), - server_onupdate=FetchedValue(), - ) - - -bigint_item_tag = Table( - "bigint_item_tag", - BigIntBase.metadata, - Column("item_id", ForeignKey("big_int_item.id"), primary_key=True), # pyright: ignore - Column("tag_id", ForeignKey("big_int_tag.id"), primary_key=True), # pyright: ignore -) - - -class BigIntItem(BigIntBase): - name: Mapped[str] = mapped_column(String(length=50)) # pyright: ignore - description: Mapped[str] = mapped_column(String(length=100), nullable=True) # pyright: ignore - tags: Mapped[List[BigIntTag]] = relationship(secondary=lambda: bigint_item_tag, back_populates="items") - - -class BigIntTag(BigIntBase): - """The event log domain object.""" - - name: Mapped[str] = mapped_column(String(length=50)) # pyright: ignore - items: Mapped[List[BigIntItem]] = relationship(secondary=lambda: bigint_item_tag, back_populates="tags") - - -class BigIntRule(BigIntAuditBase): - """The rule domain object.""" - - name: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore - config: Mapped[dict] = mapped_column(default=lambda: {}) # pyright: ignore - - -class BigIntSecret(BigIntBase): - """The secret domain model.""" - - secret: Mapped[str] = mapped_column( - EncryptedString(key="super_secret"), - ) - long_secret: Mapped[str] = mapped_column( - EncryptedText(key="super_secret"), - ) - - -class RuleAsyncRepository(SQLAlchemyAsyncRepository[BigIntRule]): - """Rule repository.""" - - model_type = BigIntRule - - -class RuleAsyncMockRepository(SQLAlchemyAsyncMockRepository[BigIntRule]): - """Rule repository.""" - - model_type = BigIntRule - - -class RuleSyncMockRepository(SQLAlchemySyncMockRepository[BigIntRule]): - """Rule repository.""" - - model_type = BigIntRule - - -class AuthorAsyncRepository(SQLAlchemyAsyncRepository[BigIntAuthor]): - """Author repository.""" - - model_type = BigIntAuthor - - -class AuthorAsyncMockRepository(SQLAlchemyAsyncMockRepository[BigIntAuthor]): - model_type = BigIntAuthor - - -class AuthorSyncMockRepository(SQLAlchemySyncMockRepository[BigIntAuthor]): - model_type = BigIntAuthor - - -class BookAsyncRepository(SQLAlchemyAsyncRepository[BigIntBook]): - """Book repository.""" - - model_type = BigIntBook - - -class BookAsyncMockRepository(SQLAlchemyAsyncMockRepository[BigIntBook]): - """Book repository.""" - - model_type = BigIntBook - - -class BookSyncMockRepository(SQLAlchemySyncMockRepository[BigIntBook]): - """Book repository.""" - - model_type = BigIntBook - - -class EventLogAsyncRepository(SQLAlchemyAsyncRepository[BigIntEventLog]): - """Event log repository.""" - - model_type = BigIntEventLog - - -class ModelWithFetchedValueAsyncRepository(SQLAlchemyAsyncRepository[BigIntModelWithFetchedValue]): - """BigIntModelWithFetchedValue repository.""" - - model_type = BigIntModelWithFetchedValue - - -class SecretAsyncRepository(SQLAlchemyAsyncRepository[BigIntSecret]): - """Secret repository.""" - - model_type = BigIntSecret - - -class TagAsyncRepository(SQLAlchemyAsyncRepository[BigIntTag]): - """Tag repository.""" - - model_type = BigIntTag - - -class TagAsyncMockRepository(SQLAlchemyAsyncMockRepository[BigIntTag]): - """Tag repository.""" - - model_type = BigIntTag - - -class TagSyncMockRepository(SQLAlchemySyncMockRepository[BigIntTag]): - """Tag repository.""" - - model_type = BigIntTag - - -class SecretSyncRepository(SQLAlchemySyncRepository[BigIntSecret]): - """Secret repository.""" - - model_type = BigIntSecret - - -class ItemAsyncRepository(SQLAlchemyAsyncRepository[BigIntItem]): - """Item repository.""" - - model_type = BigIntItem - - -class ItemAsyncMockRepository(SQLAlchemyAsyncMockRepository[BigIntItem]): - """Item repository.""" - - model_type = BigIntItem - - -class ItemSyncMockRepository(SQLAlchemySyncMockRepository[BigIntItem]): - """Item repository.""" - - model_type = BigIntItem - - -class SecretAsyncMockRepository(SQLAlchemyAsyncMockRepository[BigIntSecret]): - """Secret repository.""" - - model_type = BigIntSecret - - -class SecretSyncMockRepository(SQLAlchemySyncMockRepository[BigIntSecret]): - """Secret repository.""" - - model_type = BigIntSecret - - -class AuthorSyncRepository(SQLAlchemySyncRepository[BigIntAuthor]): - """Author repository.""" - - model_type = BigIntAuthor - - -class BookSyncRepository(SQLAlchemySyncRepository[BigIntBook]): - """Book repository.""" - - model_type = BigIntBook - - -class EventLogSyncRepository(SQLAlchemySyncRepository[BigIntEventLog]): - """Event log repository.""" - - model_type = BigIntEventLog - - -class RuleSyncRepository(SQLAlchemySyncRepository[BigIntRule]): - """Rule repository.""" - - model_type = BigIntRule - - -class ModelWithFetchedValueSyncRepository(SQLAlchemySyncRepository[BigIntModelWithFetchedValue]): - """ModelWithFetchedValue repository.""" - - model_type = BigIntModelWithFetchedValue - - -class TagSyncRepository(SQLAlchemySyncRepository[BigIntTag]): - """Tag repository.""" - - model_type = BigIntTag - - -class ItemSyncRepository(SQLAlchemySyncRepository[BigIntItem]): - """Item repository.""" - - model_type = BigIntItem - - -# Services -class SecretAsyncService(SQLAlchemyAsyncRepositoryService[BigIntSecret]): - """Rule repository.""" - - repository_type = SecretAsyncRepository - - -class RuleAsyncService(SQLAlchemyAsyncRepositoryService[BigIntRule]): - """Rule repository.""" - - repository_type = RuleAsyncRepository - - -class RuleAsyncMockService(SQLAlchemyAsyncRepositoryService[BigIntRule]): - """Rule repository.""" - - repository_type = RuleAsyncMockRepository - - -class RuleSyncMockService(SQLAlchemySyncRepositoryService[BigIntRule]): - """Rule repository.""" - - repository_type = RuleSyncMockRepository - - -class AuthorAsyncService(SQLAlchemyAsyncRepositoryService[BigIntAuthor]): - """Author repository.""" - - repository_type = AuthorAsyncRepository - - -class AuthorAsyncMockService(SQLAlchemyAsyncRepositoryService[BigIntAuthor]): - """Author repository.""" - - repository_type = AuthorAsyncMockRepository - - -class AuthorSyncMockService(SQLAlchemySyncRepositoryService[BigIntAuthor]): - """Author repository.""" - - repository_type = AuthorSyncMockRepository - - -class BookAsyncService(SQLAlchemyAsyncRepositoryService[BigIntBook]): - """Book repository.""" - - repository_type = BookAsyncRepository - - -class BookAsyncMockService(SQLAlchemyAsyncRepositoryService[BigIntBook]): - """Book repository.""" - - repository_type = BookAsyncMockRepository - - -class BookSyncMockService(SQLAlchemySyncRepositoryService[BigIntBook]): - """Book repository.""" - - repository_type = BookSyncMockRepository - - -class EventLogAsyncService(SQLAlchemyAsyncRepositoryService[BigIntEventLog]): - """Event log repository.""" - - repository_type = EventLogAsyncRepository - - -class ModelWithFetchedValueAsyncService(SQLAlchemyAsyncRepositoryService[BigIntModelWithFetchedValue]): - """BigIntModelWithFetchedValue repository.""" - - repository_type = ModelWithFetchedValueAsyncRepository - - -class TagAsyncService(SQLAlchemyAsyncRepositoryService[BigIntTag]): - """Tag repository.""" - - repository_type = TagAsyncRepository - - -class TagAsyncMockService(SQLAlchemyAsyncRepositoryService[BigIntTag]): - """Tag repository.""" - - repository_type = TagAsyncMockRepository - - -class TagSyncMockService(SQLAlchemySyncRepositoryService[BigIntTag]): - """Tag repository.""" - - repository_type = TagSyncMockRepository - - -class ItemAsyncService(SQLAlchemyAsyncRepositoryService[BigIntItem]): - """Item repository.""" - - repository_type = ItemAsyncRepository - - -class ItemAsyncMockService(SQLAlchemyAsyncRepositoryService[BigIntItem]): - """Item repository.""" - - repository_type = ItemAsyncMockRepository - - -class ItemSyncMockService(SQLAlchemySyncRepositoryService[BigIntItem]): - """Item repository.""" - - repository_type = ItemSyncMockRepository - - -class RuleSyncService(SQLAlchemySyncRepositoryService[BigIntRule]): - """Rule repository.""" - - repository_type = RuleSyncRepository - - -class AuthorSyncService(SQLAlchemySyncRepositoryService[BigIntAuthor]): - """Author repository.""" - - repository_type = AuthorSyncRepository - - -class BookSyncService(SQLAlchemySyncRepositoryService[BigIntBook]): - """Book repository.""" - - repository_type = BookSyncRepository - - -class EventLogSyncService(SQLAlchemySyncRepositoryService[BigIntEventLog]): - """Event log repository.""" - - repository_type = EventLogSyncRepository - - -class ModelWithFetchedValueSyncService(SQLAlchemySyncRepositoryService[BigIntModelWithFetchedValue]): - """BigIntModelWithFetchedValue repository.""" - - repository_type = ModelWithFetchedValueSyncRepository - - -class SecretSyncService(SQLAlchemySyncRepositoryService[BigIntSecret]): - """Rule repository.""" - - repository_type = SecretSyncRepository - - -class TagSyncService(SQLAlchemySyncRepositoryService[BigIntTag]): - """Tag repository.""" - - repository_type = TagSyncRepository - - -class ItemSyncService(SQLAlchemySyncRepositoryService[BigIntItem]): - """Item repository.""" - - repository_type = ItemSyncRepository - - -# Slug book - - -class SlugBookAsyncRepository(SQLAlchemyAsyncSlugRepository[BigIntSlugBook]): - """Slug Book repository.""" - - _uniquify_results = True - model_type = BigIntSlugBook - - -class SlugBookSyncRepository(SQLAlchemySyncSlugRepository[BigIntSlugBook]): - """Slug Book repository.""" - - _uniquify_results = True - model_type = BigIntSlugBook - - -class SlugBookAsyncMockRepository(SQLAlchemyAsyncMockSlugRepository[BigIntSlugBook]): - """Book repository.""" - - model_type = BigIntSlugBook - - -class SlugBookSyncMockRepository(SQLAlchemySyncMockSlugRepository[BigIntSlugBook]): - """Book repository.""" - - model_type = BigIntSlugBook - - -class SlugBookAsyncService(SQLAlchemyAsyncRepositoryService[BigIntSlugBook]): - """Book repository.""" - - repository_type = SlugBookAsyncRepository - match_fields = ["title"] - - def __init__(self, **repo_kwargs: Any) -> None: - self.repository: SlugBookAsyncRepository = self.repository_type(**repo_kwargs) # pyright: ignore - - async def to_model(self, data: BigIntSlugBook | dict[str, Any], operation: str | None = None) -> BigIntSlugBook: - if isinstance(data, dict) and "slug" not in data and operation == "create": - data["slug"] = await self.repository.get_available_slug(data["title"]) - if isinstance(data, dict) and "slug" not in data and "title" in data and operation == "update": - data["slug"] = await self.repository.get_available_slug(data["title"]) - return await super().to_model(data, operation) - - -class SlugBookSyncService(SQLAlchemySyncRepositoryService[BigIntSlugBook]): - """Book repository.""" - - repository_type = SlugBookSyncRepository - match_fields = ["title"] - - def __init__(self, **repo_kwargs: Any) -> None: - self.repository: SlugBookSyncRepository = self.repository_type(**repo_kwargs) # pyright: ignore - - def to_model(self, data: BigIntSlugBook | dict[str, Any], operation: str | None = None) -> BigIntSlugBook: - if isinstance(data, dict) and "slug" not in data and operation == "create": - data["slug"] = self.repository.get_available_slug(data["title"]) - if isinstance(data, dict) and "slug" not in data and "title" in data and operation == "update": - data["slug"] = self.repository.get_available_slug(data["title"]) - return super().to_model(data, operation) - - -class SlugBookAsyncMockService(SQLAlchemyAsyncRepositoryService[BigIntSlugBook]): - """Book repository.""" - - repository_type = SlugBookAsyncMockRepository - match_fields = ["title"] - - def __init__(self, **repo_kwargs: Any) -> None: - self.repository: SlugBookAsyncMockRepository = self.repository_type(**repo_kwargs) # pyright: ignore - - async def to_model(self, data: BigIntSlugBook | dict[str, Any], operation: str | None = None) -> BigIntSlugBook: - if isinstance(data, dict) and "slug" not in data and operation == "create": - data["slug"] = await self.repository.get_available_slug(data["title"]) - if isinstance(data, dict) and "slug" not in data and "title" in data and operation == "update": - data["slug"] = await self.repository.get_available_slug(data["title"]) - return await super().to_model(data, operation) - - -class SlugBookSyncMockService(SQLAlchemySyncRepositoryService[BigIntSlugBook]): - """Book repository.""" - - repository_type = SlugBookSyncMockRepository - match_fields = ["title"] - - def __init__(self, **repo_kwargs: Any) -> None: - self.repository: SlugBookSyncMockRepository = self.repository_type(**repo_kwargs) # pyright: ignore - - def to_model(self, data: BigIntSlugBook | dict[str, Any], operation: str | None = None) -> BigIntSlugBook: - if isinstance(data, dict) and "slug" not in data and operation == "create": - data["slug"] = self.repository.get_available_slug(data["title"]) - if isinstance(data, dict) and "slug" not in data and "title" in data and operation == "update": - data["slug"] = self.repository.get_available_slug(data["title"]) - return super().to_model(data, operation) diff --git a/tests/models_uuid.py b/tests/models_uuid.py deleted file mode 100644 index 2a2b9362..00000000 --- a/tests/models_uuid.py +++ /dev/null @@ -1,546 +0,0 @@ -"""Example domain objects for testing.""" - -from __future__ import annotations - -from datetime import date, datetime -from typing import Any, List -from uuid import UUID - -from sqlalchemy import Column, FetchedValue, ForeignKey, String, Table, func -from sqlalchemy.orm import Mapped, declared_attr, mapped_column, relationship - -from advanced_alchemy.base import ( - SlugKey, - TableArgsType, # pyright: ignore[reportPrivateUsage] - UUIDAuditBase, - UUIDBase, - UUIDv6Base, - UUIDv7Base, - merge_table_arguments, -) -from advanced_alchemy.repository import ( - SQLAlchemyAsyncRepository, - SQLAlchemyAsyncSlugRepository, - SQLAlchemySyncRepository, - SQLAlchemySyncSlugRepository, -) -from advanced_alchemy.repository.memory import ( - SQLAlchemyAsyncMockRepository, - SQLAlchemyAsyncMockSlugRepository, - SQLAlchemySyncMockRepository, - SQLAlchemySyncMockSlugRepository, -) -from advanced_alchemy.service import ( - SQLAlchemyAsyncRepositoryService, - SQLAlchemySyncRepositoryService, -) -from advanced_alchemy.types.encrypted_string import EncryptedString, EncryptedText - - -class UUIDAuthor(UUIDAuditBase): - """The UUIDAuthor domain object.""" - - name: Mapped[str] = mapped_column(String(length=100)) # pyright: ignore - string_field: Mapped[str] = mapped_column(String(20), default="static value", nullable=True) # pyright: ignore - dob: Mapped[date] = mapped_column(nullable=True) # pyright: ignore - books: Mapped[List[UUIDBook]] = relationship( - lazy="selectin", - back_populates="author", - cascade="all, delete", - ) - - -class UUIDBook(UUIDBase): - """The Book domain object.""" - - title: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore - author_id: Mapped[UUID] = mapped_column(ForeignKey("uuid_author.id")) # pyright: ignore - author: Mapped[UUIDAuthor] = relationship(lazy="joined", innerjoin=True, back_populates="books") # pyright: ignore - - -class UUIDSlugBook(UUIDBase, SlugKey): - """The Book domain object with a slug key.""" - - title: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore - author_id: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore - - @declared_attr.directive - @classmethod - def __table_args__(cls) -> TableArgsType: - return merge_table_arguments( - cls, - table_args={"comment": "Slugbook"}, - ) - - -class UUIDEventLog(UUIDAuditBase): - """The event log domain object.""" - - logged_at: Mapped[datetime] = mapped_column(default=datetime.now()) # pyright: ignore - payload: Mapped[dict] = mapped_column(default={}) # pyright: ignore - - -class UUIDSecret(UUIDv7Base): - """The secret domain model.""" - - secret: Mapped[str] = mapped_column( - EncryptedString(key="super_secret"), - ) - long_secret: Mapped[str] = mapped_column( - EncryptedText(key="super_secret"), - ) - - -class UUIDModelWithFetchedValue(UUIDv6Base): - """The ModelWithFetchedValue UUIDBase.""" - - val: Mapped[int] # pyright: ignore - updated: Mapped[datetime] = mapped_column( # pyright: ignore - server_default=func.current_timestamp(), - onupdate=func.current_timestamp(), - server_onupdate=FetchedValue(), - ) - - -uuid_item_tag = Table( - "uuid_item_tag", - UUIDBase.metadata, - Column("item_id", ForeignKey("uuid_item.id"), primary_key=True), - Column("tag_id", ForeignKey("uuid_tag.id"), primary_key=True), -) - - -class UUIDItem(UUIDBase): - name: Mapped[str] = mapped_column(String(length=50)) # pyright: ignore - description: Mapped[str] = mapped_column(String(length=100), nullable=True) # pyright: ignore - tags: Mapped[List[UUIDTag]] = relationship(secondary=lambda: uuid_item_tag, back_populates="items") - - -class UUIDTag(UUIDAuditBase): - """The event log domain object.""" - - name: Mapped[str] = mapped_column(String(length=50)) # pyright: ignore - items: Mapped[List[UUIDItem]] = relationship(secondary=lambda: uuid_item_tag, back_populates="tags") - - -class UUIDRule(UUIDAuditBase): - """The rule domain object.""" - - name: Mapped[str] = mapped_column(String(length=250)) # pyright: ignore - config: Mapped[dict] = mapped_column(default=lambda: {}) # pyright: ignore - - -class SecretAsyncRepository(SQLAlchemyAsyncRepository[UUIDSecret]): - """Secret repository.""" - - model_type = UUIDSecret - - -class RuleAsyncRepository(SQLAlchemyAsyncRepository[UUIDRule]): - """Rule repository.""" - - model_type = UUIDRule - - -class RuleAsyncMockRepository(SQLAlchemyAsyncMockRepository[UUIDRule]): - """Rule repository.""" - - model_type = UUIDRule - - -class RuleSyncMockRepository(SQLAlchemySyncMockRepository[UUIDRule]): - """Rule repository.""" - - model_type = UUIDRule - - -class AuthorAsyncRepository(SQLAlchemyAsyncRepository[UUIDAuthor]): - """Author repository.""" - - model_type = UUIDAuthor - - -class AuthorAsyncMockRepository(SQLAlchemyAsyncMockRepository[UUIDAuthor]): - model_type = UUIDAuthor - - -class AuthorSyncMockRepository(SQLAlchemySyncMockRepository[UUIDAuthor]): - model_type = UUIDAuthor - - -class BookAsyncRepository(SQLAlchemyAsyncRepository[UUIDBook]): - """Book repository.""" - - model_type = UUIDBook - - -class BookAsyncMockRepository(SQLAlchemyAsyncMockRepository[UUIDBook]): - """Book repository.""" - - model_type = UUIDBook - - -class BookSyncMockRepository(SQLAlchemySyncMockRepository[UUIDBook]): - """Book repository.""" - - model_type = UUIDBook - - -class SlugBookAsyncRepository(SQLAlchemyAsyncSlugRepository[UUIDSlugBook]): - """Book repository.""" - - _uniquify_results = True - model_type = UUIDSlugBook - - -class SlugBookSyncRepository(SQLAlchemySyncSlugRepository[UUIDSlugBook]): - """Slug Book repository.""" - - _uniquify_results = True - model_type = UUIDSlugBook - - -class SlugBookAsyncMockRepository(SQLAlchemyAsyncMockSlugRepository[UUIDSlugBook]): - """Book repository.""" - - model_type = UUIDSlugBook - - -class SlugBookSyncMockRepository(SQLAlchemySyncMockSlugRepository[UUIDSlugBook]): - """Book repository.""" - - model_type = UUIDSlugBook - - -class EventLogAsyncRepository(SQLAlchemyAsyncRepository[UUIDEventLog]): - """Event log repository.""" - - model_type = UUIDEventLog - - -class ModelWithFetchedValueAsyncRepository(SQLAlchemyAsyncRepository[UUIDModelWithFetchedValue]): - """ModelWithFetchedValue repository.""" - - model_type = UUIDModelWithFetchedValue - - -class TagAsyncRepository(SQLAlchemyAsyncRepository[UUIDTag]): - """Tag repository.""" - - model_type = UUIDTag - - -class TagAsyncMockRepository(SQLAlchemyAsyncMockRepository[UUIDTag]): - """Tag repository.""" - - model_type = UUIDTag - - -class TagSyncMockRepository(SQLAlchemySyncMockRepository[UUIDTag]): - """Tag repository.""" - - model_type = UUIDTag - - -class ItemAsyncRepository(SQLAlchemyAsyncRepository[UUIDItem]): - """Item repository.""" - - model_type = UUIDItem - - -class ItemAsyncMockRepository(SQLAlchemyAsyncMockRepository[UUIDItem]): - """Item repository.""" - - model_type = UUIDItem - - -class ItemSyncMockRepository(SQLAlchemySyncMockRepository[UUIDItem]): - """Item repository.""" - - model_type = UUIDItem - - -class SecretAsyncMockRepository(SQLAlchemyAsyncMockRepository[UUIDSecret]): - """Secret repository.""" - - model_type = UUIDSecret - - -class SecretSyncMockRepository(SQLAlchemySyncMockRepository[UUIDSecret]): - """Secret repository.""" - - model_type = UUIDSecret - - -class AuthorSyncRepository(SQLAlchemySyncRepository[UUIDAuthor]): - """Author repository.""" - - model_type = UUIDAuthor - - -class BookSyncRepository(SQLAlchemySyncRepository[UUIDBook]): - """Book repository.""" - - model_type = UUIDBook - - -class SecretSyncRepository(SQLAlchemySyncRepository[UUIDSecret]): - """Secret repository.""" - - model_type = UUIDSecret - - -class EventLogSyncRepository(SQLAlchemySyncRepository[UUIDEventLog]): - """Event log repository.""" - - model_type = UUIDEventLog - - -class RuleSyncRepository(SQLAlchemySyncRepository[UUIDRule]): - """Rule repository.""" - - model_type = UUIDRule - - -class ModelWithFetchedValueSyncRepository(SQLAlchemySyncRepository[UUIDModelWithFetchedValue]): - """ModelWithFetchedValue repository.""" - - model_type = UUIDModelWithFetchedValue - - -class TagSyncRepository(SQLAlchemySyncRepository[UUIDTag]): - """Tag repository.""" - - model_type = UUIDTag - - -class ItemSyncRepository(SQLAlchemySyncRepository[UUIDItem]): - """Item repository.""" - - model_type = UUIDItem - - -# Services - - -class SecretAsyncService(SQLAlchemyAsyncRepositoryService[UUIDSecret]): - """Rule repository.""" - - repository_type = SecretAsyncRepository - - -class RuleAsyncService(SQLAlchemyAsyncRepositoryService[UUIDRule]): - """Rule repository.""" - - repository_type = RuleAsyncRepository - - -class RuleAsyncMockService(SQLAlchemyAsyncRepositoryService[UUIDRule]): - """Rule repository.""" - - repository_type = RuleAsyncMockRepository - - -class RuleSyncMockService(SQLAlchemySyncRepositoryService[UUIDRule]): - """Rule repository.""" - - repository_type = RuleSyncMockRepository - - -class AuthorAsyncService(SQLAlchemyAsyncRepositoryService[UUIDAuthor]): - """Author repository.""" - - repository_type = AuthorAsyncRepository - - -class AuthorAsyncMockService(SQLAlchemyAsyncRepositoryService[UUIDAuthor]): - """Author repository.""" - - repository_type = AuthorAsyncMockRepository - - -class AuthorSyncMockService(SQLAlchemySyncRepositoryService[UUIDAuthor]): - """Author repository.""" - - repository_type = AuthorSyncMockRepository - - -class BookAsyncService(SQLAlchemyAsyncRepositoryService[UUIDBook]): - """Book repository.""" - - repository_type = BookAsyncRepository - - -class BookAsyncMockService(SQLAlchemyAsyncRepositoryService[UUIDBook]): - """Book repository.""" - - repository_type = BookAsyncMockRepository - - -class BookSyncMockService(SQLAlchemySyncRepositoryService[UUIDBook]): - """Book repository.""" - - repository_type = BookSyncMockRepository - - -class EventLogAsyncService(SQLAlchemyAsyncRepositoryService[UUIDEventLog]): - """Event log repository.""" - - repository_type = EventLogAsyncRepository - - -class ModelWithFetchedValueAsyncService(SQLAlchemyAsyncRepositoryService[UUIDModelWithFetchedValue]): - """UUIDModelWithFetchedValue repository.""" - - repository_type = ModelWithFetchedValueAsyncRepository - - -class TagAsyncService(SQLAlchemyAsyncRepositoryService[UUIDTag]): - """Tag repository.""" - - repository_type = TagAsyncRepository - - -class TagAsyncMockService(SQLAlchemyAsyncRepositoryService[UUIDTag]): - """Tag repository.""" - - repository_type = TagAsyncMockRepository - - -class TagSyncMockService(SQLAlchemySyncRepositoryService[UUIDTag]): - """Tag repository.""" - - repository_type = TagSyncMockRepository - - -class ItemAsyncService(SQLAlchemyAsyncRepositoryService[UUIDItem]): - """Item repository.""" - - repository_type = ItemAsyncRepository - - -class ItemAsyncMockService(SQLAlchemyAsyncRepositoryService[UUIDItem]): - """Item repository.""" - - repository_type = ItemAsyncMockRepository - - -class ItemSyncMockService(SQLAlchemySyncRepositoryService[UUIDItem]): - """Item repository.""" - - repository_type = ItemSyncMockRepository - - -class RuleSyncService(SQLAlchemySyncRepositoryService[UUIDRule]): - """Rule repository.""" - - repository_type = RuleSyncRepository - - -class AuthorSyncService(SQLAlchemySyncRepositoryService[UUIDAuthor]): - """Author repository.""" - - repository_type = AuthorSyncRepository - - -class BookSyncService(SQLAlchemySyncRepositoryService[UUIDBook]): - """Book repository.""" - - repository_type = BookSyncRepository - - -class EventLogSyncService(SQLAlchemySyncRepositoryService[UUIDEventLog]): - """Event log repository.""" - - repository_type = EventLogSyncRepository - - -class ModelWithFetchedValueSyncService(SQLAlchemySyncRepositoryService[UUIDModelWithFetchedValue]): - """UUIDModelWithFetchedValue repository.""" - - repository_type = ModelWithFetchedValueSyncRepository - - -class TagSyncService(SQLAlchemySyncRepositoryService[UUIDTag]): - """Tag repository.""" - - repository_type = TagSyncRepository - - -class ItemSyncService(SQLAlchemySyncRepositoryService[UUIDItem]): - """Item repository.""" - - repository_type = ItemSyncRepository - - -class SecretSyncService(SQLAlchemySyncRepositoryService[UUIDSecret]): - """Rule repository.""" - - repository_type = SecretSyncRepository - - -class SlugBookAsyncService(SQLAlchemyAsyncRepositoryService[UUIDSlugBook]): - """Book repository.""" - - repository_type = SlugBookAsyncRepository - match_fields = ["title"] - - def __init__(self, **repo_kwargs: Any) -> None: - self.repository: SlugBookAsyncRepository = self.repository_type(**repo_kwargs) - - async def to_model(self, data: UUIDSlugBook | dict[str, Any], operation: str | None = None) -> UUIDSlugBook: - if isinstance(data, dict) and "slug" not in data and operation == "create": - data["slug"] = await self.repository.get_available_slug(data["title"]) - if isinstance(data, dict) and "slug" not in data and "title" in data and operation == "update": - data["slug"] = await self.repository.get_available_slug(data["title"]) - return await super().to_model(data, operation) - - -class SlugBookSyncService(SQLAlchemySyncRepositoryService[UUIDSlugBook]): - """Book repository.""" - - repository_type = SlugBookSyncRepository - - def __init__(self, **repo_kwargs: Any) -> None: - self.repository: SlugBookSyncRepository = self.repository_type(**repo_kwargs) - - def to_model(self, data: UUIDSlugBook | dict[str, Any], operation: str | None = None) -> UUIDSlugBook: - if isinstance(data, dict) and "slug" not in data and operation == "create": - data["slug"] = self.repository.get_available_slug(data["title"]) - if isinstance(data, dict) and "slug" not in data and "title" in data and operation == "update": - data["slug"] = self.repository.get_available_slug(data["title"]) - return super().to_model(data, operation) - - -class SlugBookAsyncMockService(SQLAlchemyAsyncRepositoryService[UUIDSlugBook]): - """Book repository.""" - - repository_type = SlugBookAsyncMockRepository - match_fields = ["title"] - - def __init__(self, **repo_kwargs: Any) -> None: - self.repository: SlugBookAsyncMockRepository = self.repository_type(**repo_kwargs) - - async def to_model(self, data: UUIDSlugBook | dict[str, Any], operation: str | None = None) -> UUIDSlugBook: - if isinstance(data, dict) and "slug" not in data and operation == "create": - data["slug"] = await self.repository.get_available_slug(data["title"]) - if isinstance(data, dict) and "slug" not in data and "title" in data and operation == "update": - data["slug"] = await self.repository.get_available_slug(data["title"]) - return await super().to_model(data, operation) - - -class SlugBookSyncMockService(SQLAlchemySyncRepositoryService[UUIDSlugBook]): - """Book repository.""" - - repository_type = SlugBookSyncMockRepository - match_fields = ["title"] - - def __init__(self, **repo_kwargs: Any) -> None: - self.repository: SlugBookSyncMockRepository = self.repository_type(**repo_kwargs) - - def to_model(self, data: UUIDSlugBook | dict[str, Any], operation: str | None = None) -> UUIDSlugBook: - if isinstance(data, dict) and "slug" not in data and operation == "create": - data["slug"] = self.repository.get_available_slug(data["title"]) - if isinstance(data, dict) and "slug" not in data and "title" in data and operation == "update": - data["slug"] = self.repository.get_available_slug(data["title"]) - return super().to_model(data, operation) diff --git a/tests/unit/test_repository.py b/tests/unit/test_repository.py index 34a4b5a3..cd213f57 100644 --- a/tests/unit/test_repository.py +++ b/tests/unit/test_repository.py @@ -57,7 +57,7 @@ def async_mock_repo() -> SQLAlchemyAsyncRepository[MagicMock]: class Repo(SQLAlchemyAsyncRepository[MagicMock]): """Repo with mocked out stuff.""" - model_type = MagicMock() # pyright:ignore[reportGeneralTypeIssues] + model_type = MagicMock() # pyright:ignore[reportGeneralTypeIssues,reportAssignmentType] return Repo(session=AsyncMock(spec=AsyncSession, bind=MagicMock()), statement=MagicMock()) @@ -69,7 +69,7 @@ def sync_mock_repo() -> SQLAlchemySyncRepository[MagicMock]: class Repo(SQLAlchemySyncRepository[MagicMock]): """Repo with mocked out stuff.""" - model_type = MagicMock() # pyright:ignore[reportGeneralTypeIssues] + model_type = MagicMock() # pyright:ignore[reportGeneralTypeIssues,reportAssignmentType] return Repo(session=MagicMock(spec=Session, bind=MagicMock()), statement=MagicMock()) @@ -194,11 +194,11 @@ async def test_sqlalchemy_repo_add(mock_repo: SQLAlchemyAsyncRepository[Any]) -> instance = await maybe_async(mock_repo.add(mock_instance)) assert instance is mock_instance - mock_repo.session.add.assert_called_once_with(mock_instance) - mock_repo.session.flush.assert_called_once() - mock_repo.session.refresh.assert_called_once_with(mock_instance, attribute_names=None, with_for_update=None) - mock_repo.session.expunge.assert_not_called() - mock_repo.session.commit.assert_not_called() + mock_repo.session.add.assert_called_once_with(mock_instance) # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.flush.assert_called_once() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.refresh.assert_called_once_with(mock_instance, attribute_names=None, with_for_update=None) # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.expunge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_add_many( @@ -218,8 +218,8 @@ async def test_sqlalchemy_repo_add_many( assert len(instances) == 3 for row in instances: assert row.id is not None - mock_repo.session.expunge.assert_not_called() - mock_repo.session.commit.assert_not_called() + mock_repo.session.expunge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_update_many( @@ -239,8 +239,8 @@ async def test_sqlalchemy_repo_update_many( for row in instances: assert row.id is not None - mock_repo.session.flush.assert_called_once() - mock_repo.session.commit.assert_not_called() + mock_repo.session.flush.assert_called_once() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_upsert_many( @@ -264,7 +264,7 @@ async def test_sqlalchemy_repo_upsert_many( for row in instances: assert row.id is not None - mock_repo.session.commit.assert_not_called() + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_delete(mock_repo: SQLAlchemyAsyncRepository[Any], mocker: MockerFixture) -> None: @@ -275,10 +275,10 @@ async def test_sqlalchemy_repo_delete(mock_repo: SQLAlchemyAsyncRepository[Any], assert instance is mock_instance - mock_repo.session.delete.assert_called_once_with(mock_instance) - mock_repo.session.flush.assert_called_once() - mock_repo.session.expunge.assert_not_called() - mock_repo.session.commit.assert_not_called() + mock_repo.session.delete.assert_called_once_with(mock_instance) # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.flush.assert_called_once() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.expunge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_delete_many_uuid( @@ -301,8 +301,8 @@ async def test_sqlalchemy_repo_delete_many_uuid( instances = await maybe_async(mock_repo.delete_many([obj.id for obj in added_instances])) assert len(instances) == len(mock_instances) - mock_repo.session.flush.assert_called() - mock_repo.session.commit.assert_not_called() + mock_repo.session.flush.assert_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_delete_many_bigint( @@ -326,8 +326,8 @@ async def test_sqlalchemy_repo_delete_many_bigint( instances = await maybe_async(mock_repo.delete_many([obj.id for obj in added_instances])) assert len(instances) == len(mock_instances) - mock_repo.session.flush.assert_called() - mock_repo.session.commit.assert_not_called() + mock_repo.session.flush.assert_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_get_member( @@ -342,8 +342,8 @@ async def test_sqlalchemy_repo_get_member( instance = await maybe_async(mock_repo.get("instance-id")) assert instance is mock_instance - mock_repo.session.expunge.assert_not_called() - mock_repo.session.commit.assert_not_called() + mock_repo.session.expunge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_get_one_member( @@ -358,8 +358,8 @@ async def test_sqlalchemy_repo_get_one_member( instance = await maybe_async(mock_repo.get_one(id="instance-id")) assert instance is mock_instance - mock_repo.session.expunge.assert_not_called() - mock_repo.session.commit.assert_not_called() + mock_repo.session.expunge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_get_or_upsert_member_existing( @@ -377,9 +377,9 @@ async def test_sqlalchemy_repo_get_or_upsert_member_existing( assert instance is mock_instance assert created is False - mock_repo.session.expunge.assert_not_called() - mock_repo.session.merge.assert_not_called() - mock_repo.session.refresh.assert_not_called() + mock_repo.session.expunge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.merge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.refresh.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_get_or_upsert_member_existing_upsert( @@ -399,10 +399,10 @@ async def test_sqlalchemy_repo_get_or_upsert_member_existing_upsert( assert instance is mock_instance assert created is False - mock_repo.session.expunge.assert_not_called() - mock_repo._attach_to_session.assert_called_once() - mock_repo.session.flush.assert_called_once() - mock_repo.session.refresh.assert_called_once_with(mock_instance, attribute_names=None, with_for_update=None) + mock_repo.session.expunge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo._attach_to_session.assert_called_once() # pyright: ignore[reportFunctionMemberAccess,reportPrivateUsage] + mock_repo.session.flush.assert_called_once() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.refresh.assert_called_once_with(mock_instance, attribute_names=None, with_for_update=None) # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_get_or_upsert_member_existing_no_upsert( @@ -420,9 +420,9 @@ async def test_sqlalchemy_repo_get_or_upsert_member_existing_no_upsert( assert instance is mock_instance assert created is False - mock_repo.session.expunge.assert_not_called() - mock_repo.session.add.assert_not_called() - mock_repo.session.refresh.assert_not_called() + mock_repo.session.expunge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.add.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.refresh.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_get_or_upsert_member_created( @@ -437,9 +437,9 @@ async def test_sqlalchemy_repo_get_or_upsert_member_created( assert instance is not None assert created is True - mock_repo.session.expunge.assert_not_called() - mock_repo.session.add.assert_called_once_with(instance) - mock_repo.session.refresh.assert_called_once_with(instance, attribute_names=None, with_for_update=None) + mock_repo.session.expunge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.add.assert_called_once_with(instance) # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.refresh.assert_called_once_with(instance, attribute_names=None, with_for_update=None) # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_get_one_or_none_member( @@ -454,8 +454,8 @@ async def test_sqlalchemy_repo_get_one_or_none_member( instance = await maybe_async(mock_repo.get_one_or_none(id="instance-id")) assert instance is mock_instance - mock_repo.session.expunge.assert_not_called() - mock_repo.session.commit.assert_not_called() + mock_repo.session.expunge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_get_one_or_none_not_found( @@ -470,8 +470,8 @@ async def test_sqlalchemy_repo_get_one_or_none_not_found( instance = await maybe_async(mock_repo.get_one_or_none(id="instance-id")) assert instance is None - mock_repo.session.expunge.assert_not_called() - mock_repo.session.commit.assert_not_called() + mock_repo.session.expunge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_list( @@ -486,8 +486,8 @@ async def test_sqlalchemy_repo_list( instances = await maybe_async(mock_repo.list()) assert instances == mock_instances - mock_repo.session.expunge.assert_not_called() - mock_repo.session.commit.assert_not_called() + mock_repo.session.expunge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_list_and_count(mock_repo: SQLAlchemyAsyncRepository[Any], mocker: MockerFixture) -> None: @@ -501,8 +501,8 @@ async def test_sqlalchemy_repo_list_and_count(mock_repo: SQLAlchemyAsyncReposito assert instances == mock_instances assert instance_count == mock_count - mock_repo.session.expunge.assert_not_called() - mock_repo.session.commit.assert_not_called() + mock_repo.session.expunge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_list_and_count_basic( @@ -519,8 +519,8 @@ async def test_sqlalchemy_repo_list_and_count_basic( assert instances == mock_instances assert instance_count == mock_count - mock_repo.session.expunge.assert_not_called() - mock_repo.session.commit.assert_not_called() + mock_repo.session.expunge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_exists( @@ -535,7 +535,7 @@ async def test_sqlalchemy_repo_exists( exists = await maybe_async(mock_repo.exists(id="my-id")) assert exists - mock_repo.session.commit.assert_not_called() + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_exists_with_filter( @@ -551,7 +551,7 @@ async def test_sqlalchemy_repo_exists_with_filter( exists = await maybe_async(mock_repo.exists(limit_filter, id="my-id")) assert exists - mock_repo.session.commit.assert_not_called() + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_count( @@ -566,7 +566,7 @@ async def test_sqlalchemy_repo_count( count = await maybe_async(mock_repo.count()) assert count == 1 - mock_repo.session.commit.assert_not_called() + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_list_with_pagination( @@ -581,7 +581,7 @@ async def test_sqlalchemy_repo_list_with_pagination( mocker.patch.object(LimitOffset, "append_to_lambda_statement", return_value=statement) mock_repo_execute.return_value = MagicMock() await maybe_async(mock_repo.list(LimitOffset(2, 3))) - mock_repo._execute.assert_called_with(statement, uniquify=False) + mock_repo._execute.assert_called_with(statement, uniquify=False) # pyright: ignore[reportFunctionMemberAccess,reportPrivateUsage] async def test_sqlalchemy_repo_list_with_before_after_filter( @@ -596,7 +596,7 @@ async def test_sqlalchemy_repo_list_with_before_after_filter( mocker.patch.object(BeforeAfter, "append_to_lambda_statement", return_value=statement) mock_repo_execute.return_value = MagicMock() await maybe_async(mock_repo.list(BeforeAfter("updated_at", datetime.max, datetime.min))) - mock_repo._execute.assert_called_with(statement, uniquify=False) + mock_repo._execute.assert_called_with(statement, uniquify=False) # pyright: ignore[reportFunctionMemberAccess,reportPrivateUsage] async def test_sqlalchemy_repo_list_with_on_before_after_filter( @@ -613,7 +613,7 @@ async def test_sqlalchemy_repo_list_with_on_before_after_filter( mock_repo_execute.return_value = MagicMock() await maybe_async(mock_repo.list(OnBeforeAfter("updated_at", datetime.max, datetime.min))) - mock_repo._execute.assert_called_with(statement, uniquify=False) + mock_repo._execute.assert_called_with(statement, uniquify=False) # pyright: ignore[reportFunctionMemberAccess,reportPrivateUsage] async def test_sqlalchemy_repo_list_with_collection_filter( @@ -625,11 +625,11 @@ async def test_sqlalchemy_repo_list_with_collection_filter( """Test behavior of list operation given CollectionFilter.""" field_name = "id" mock_repo_execute.return_value = MagicMock() - mock_repo.statement.where.return_value = mock_repo.statement + mock_repo.statement.where.return_value = mock_repo.statement # pyright: ignore[reportFunctionMemberAccess] mocker.patch.object(CollectionFilter, "append_to_lambda_statement", return_value=mock_repo.statement) values = [1, 2, 3] await maybe_async(mock_repo.list(CollectionFilter(field_name, values))) - mock_repo._execute.assert_called_with(mock_repo.statement, uniquify=False) + mock_repo._execute.assert_called_with(mock_repo.statement, uniquify=False) # pyright: ignore[reportFunctionMemberAccess,reportPrivateUsage] async def test_sqlalchemy_repo_list_with_null_collection_filter( @@ -641,14 +641,14 @@ async def test_sqlalchemy_repo_list_with_null_collection_filter( """Test behavior of list operation given CollectionFilter.""" field_name = "id" mock_repo_execute.return_value = MagicMock() - mock_repo.statement.where.return_value = mock_repo.statement + mock_repo.statement.where.return_value = mock_repo.statement # pyright: ignore[reportFunctionMemberAccess] monkeypatch.setattr( CollectionFilter, "append_to_lambda_statement", MagicMock(return_value=mock_repo.statement), ) - await maybe_async(mock_repo.list(CollectionFilter(field_name, None))) - mock_repo._execute.assert_called_with(mock_repo.statement, uniquify=False) + await maybe_async(mock_repo.list(CollectionFilter(field_name, None))) # pyright: ignore[reportFunctionMemberAccess,reportUnknownArgumentType] + mock_repo._execute.assert_called_with(mock_repo.statement, uniquify=False) # pyright: ignore[reportFunctionMemberAccess,reportPrivateUsage] async def test_sqlalchemy_repo_empty_list_with_collection_filter( @@ -660,7 +660,7 @@ async def test_sqlalchemy_repo_empty_list_with_collection_filter( """Test behavior of list operation given CollectionFilter.""" field_name = "id" mock_repo_execute.return_value = MagicMock() - mock_repo.statement.where.return_value = mock_repo.statement + mock_repo.statement.where.return_value = mock_repo.statement # pyright: ignore[reportFunctionMemberAccess] values: Collection[Any] = [] await maybe_async(mock_repo.list(CollectionFilter(field_name, values))) monkeypatch.setattr( @@ -669,7 +669,7 @@ async def test_sqlalchemy_repo_empty_list_with_collection_filter( MagicMock(return_value=mock_repo.statement), ) await maybe_async(mock_repo.list(CollectionFilter(field_name, values))) - mock_repo._execute.assert_called_with(mock_repo.statement, uniquify=False) + mock_repo._execute.assert_called_with(mock_repo.statement, uniquify=False) # pyright: ignore[reportFunctionMemberAccess,reportPrivateUsage] async def test_sqlalchemy_repo_list_with_not_in_collection_filter( @@ -681,7 +681,7 @@ async def test_sqlalchemy_repo_list_with_not_in_collection_filter( """Test behavior of list operation given CollectionFilter.""" field_name = "id" mock_repo_execute.return_value = MagicMock() - mock_repo.statement.where.return_value = mock_repo.statement + mock_repo.statement.where.return_value = mock_repo.statement # pyright: ignore[reportFunctionMemberAccess] monkeypatch.setattr( NotInCollectionFilter, "append_to_lambda_statement", @@ -689,7 +689,7 @@ async def test_sqlalchemy_repo_list_with_not_in_collection_filter( ) values = [1, 2, 3] await maybe_async(mock_repo.list(NotInCollectionFilter(field_name, values))) - mock_repo._execute.assert_called_with(mock_repo.statement, uniquify=False) + mock_repo._execute.assert_called_with(mock_repo.statement, uniquify=False) # pyright: ignore[reportFunctionMemberAccess,reportPrivateUsage] async def test_sqlalchemy_repo_list_with_null_not_in_collection_filter( @@ -701,14 +701,14 @@ async def test_sqlalchemy_repo_list_with_null_not_in_collection_filter( """Test behavior of list operation given CollectionFilter.""" field_name = "id" mock_repo_execute.return_value = MagicMock() - mock_repo.statement.where.return_value = mock_repo.statement + mock_repo.statement.where.return_value = mock_repo.statement # pyright: ignore[reportFunctionMemberAccess] monkeypatch.setattr( NotInCollectionFilter, "append_to_lambda_statement", MagicMock(return_value=mock_repo.statement), ) - await maybe_async(mock_repo.list(NotInCollectionFilter(field_name, None))) - mock_repo._execute.assert_called_with(mock_repo.statement, uniquify=False) + await maybe_async(mock_repo.list(NotInCollectionFilter[str](field_name, None))) # pyright: ignore[reportFunctionMemberAccess] + mock_repo._execute.assert_called_with(mock_repo.statement, uniquify=False) # pyright: ignore[reportFunctionMemberAccess,reportPrivateUsage] async def test_sqlalchemy_repo_unknown_filter_type_raises(mock_repo: SQLAlchemyAsyncRepository[Any]) -> None: @@ -727,32 +727,32 @@ async def test_sqlalchemy_repo_update( mock_instance = MagicMock() mocker.patch.object(mock_repo, "get_id_attribute_value", return_value=id_) mocker.patch.object(mock_repo, "get") - mock_repo.session.merge.return_value = mock_instance + mock_repo.session.merge.return_value = mock_instance # pyright: ignore[reportFunctionMemberAccess] instance = await maybe_async(mock_repo.update(mock_instance)) assert instance is mock_instance - mock_repo.session.merge.assert_called_once_with(mock_instance, load=True) - mock_repo.session.flush.assert_called_once() - mock_repo.session.expunge.assert_not_called() - mock_repo.session.commit.assert_not_called() - mock_repo.session.refresh.assert_called_once_with(mock_instance, attribute_names=None, with_for_update=None) + mock_repo.session.merge.assert_called_once_with(mock_instance, load=True) # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.flush.assert_called_once() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.expunge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.refresh.assert_called_once_with(mock_instance, attribute_names=None, with_for_update=None) # pyright: ignore[reportFunctionMemberAccess] async def test_sqlalchemy_repo_upsert(mock_repo: SQLAlchemyAsyncRepository[Any], mocker: MockerFixture) -> None: """Test the sequence of repo calls for upsert operation.""" mock_instance = MagicMock() - mock_repo.session.merge.return_value = mock_instance + mock_repo.session.merge.return_value = mock_instance # pyright: ignore[reportFunctionMemberAccess] instance = await maybe_async(mock_repo.upsert(mock_instance)) mocker.patch.object(mock_repo, "exists", return_value=True) mocker.patch.object(mock_repo, "count", return_value=1) assert instance is mock_instance - mock_repo.session.flush.assert_called_once() - mock_repo.session.expunge.assert_not_called() - mock_repo.session.commit.assert_not_called() - mock_repo.session.refresh.assert_called_once_with(mock_instance, attribute_names=None, with_for_update=None) + mock_repo.session.flush.assert_called_once() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.expunge.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.commit.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] + mock_repo.session.refresh.assert_called_once_with(mock_instance, attribute_names=None, with_for_update=None) # pyright: ignore[reportFunctionMemberAccess] async def test_attach_to_session_unexpected_strategy_raises_valueerror( @@ -765,8 +765,8 @@ async def test_attach_to_session_unexpected_strategy_raises_valueerror( async def test_execute(mock_repo: SQLAlchemyAsyncRepository[Any]) -> None: """Simple test of the abstraction over `AsyncSession.execute()`""" - _ = await maybe_async(mock_repo._execute(mock_repo.statement)) - mock_repo.session.execute.assert_called_once_with(mock_repo.statement) + _ = await maybe_async(mock_repo._execute(mock_repo.statement)) # pyright: ignore[reportFunctionMemberAccess,reportPrivateUsage] + mock_repo.session.execute.assert_called_once_with(mock_repo.statement) # pyright: ignore[reportFunctionMemberAccess] def test_filter_in_collection_noop_if_collection_empty(mock_repo: SQLAlchemyAsyncRepository[Any]) -> None: @@ -774,7 +774,7 @@ def test_filter_in_collection_noop_if_collection_empty(mock_repo: SQLAlchemyAsyn statement = MagicMock() filter = CollectionFilter(field_name="id", values=[]) # type:ignore[var-annotated] statement = filter.append_to_lambda_statement(statement, MagicMock()) # type:ignore[assignment] - mock_repo.statement.where.assert_not_called() + mock_repo.statement.where.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] @pytest.mark.parametrize( @@ -789,17 +789,17 @@ def test_filter_on_datetime_field(before: datetime, after: datetime, mock_repo: """Test through branches of _filter_on_datetime_field()""" field_mock = MagicMock() statement = MagicMock() - field_mock.__gt__ = field_mock.__lt__ = lambda self, other: True + field_mock.__gt__ = field_mock.__lt__ = lambda self, other: True # pyright: ignore[reportFunctionMemberAccess,reportUnknownLambdaType] filter = BeforeAfter(field_name="updated_at", before=before, after=after) statement = filter.append_to_lambda_statement(statement, MagicMock()) # type:ignore[assignment] mock_repo.model_type.updated_at = field_mock - mock_repo.statement.where.assert_not_called() + mock_repo.statement.where.assert_not_called() # pyright: ignore[reportFunctionMemberAccess] def test_filter_collection_by_kwargs(mock_repo: SQLAlchemyAsyncRepository[Any], mocker: MockerFixture) -> None: """Test `filter_by()` called with kwargs.""" - mock_repo_execute.return_value = MagicMock() - statement = mock_repo._to_lambda_stmt(mock_repo.statement) + mock_repo_execute.return_value = MagicMock() # pyright: ignore[reportFunctionMemberAccess] + statement = mock_repo._to_lambda_stmt(mock_repo.statement) # pyright: ignore[reportFunctionMemberAccess,reportPrivateUsage] mocker.patch.object(mock_repo, "filter_collection_by_kwargs", return_value=statement) _ = mock_repo.filter_collection_by_kwargs(statement, a=1, b=2) - mock_repo.filter_collection_by_kwargs.assert_called_once_with(statement, a=1, b=2) + mock_repo.filter_collection_by_kwargs.assert_called_once_with(statement, a=1, b=2) # pyright: ignore[reportFunctionMemberAccess]