From 653218adc725bec1505b6939d34acb95ac879e37 Mon Sep 17 00:00:00 2001 From: Damien Garros Date: Wed, 18 Sep 2024 14:00:43 +0200 Subject: [PATCH 1/6] Sync with develop --- infrahub_sdk/batch.py | 6 +- infrahub_sdk/client.py | 684 ++++++++++++++++++++++-- infrahub_sdk/code_generator.py | 123 +++++ infrahub_sdk/ctl/cli_commands.py | 134 ++--- infrahub_sdk/ctl/constants.py | 49 ++ infrahub_sdk/ctl/schema.py | 2 +- infrahub_sdk/protocols.py | 54 ++ infrahub_sdk/protocols_base.py | 70 ++- infrahub_sdk/schema.py | 50 +- infrahub_sdk/store.py | 48 +- infrahub_sdk/transfer/importer/json.py | 2 + infrahub_sdk/uuidt.py | 2 +- pyproject.toml | 2 +- tests/integration/test_export_import.py | 23 +- tests/unit/sdk/conftest.py | 18 + 15 files changed, 1076 insertions(+), 191 deletions(-) create mode 100644 infrahub_sdk/code_generator.py diff --git a/infrahub_sdk/batch.py b/infrahub_sdk/batch.py index 81348888..6cfd8f43 100644 --- a/infrahub_sdk/batch.py +++ b/infrahub_sdk/batch.py @@ -10,7 +10,7 @@ class BatchTask: task: Callable[[Any], Awaitable[Any]] args: tuple[Any, ...] kwargs: dict[str, Any] - node: Optional[InfrahubNode] = None + node: Optional[Any] = None async def execute_batch_task_in_pool( @@ -43,9 +43,7 @@ def __init__( def num_tasks(self) -> int: return len(self._tasks) - def add( - self, *args: Any, task: Callable[[Any], Awaitable[Any]], node: Optional[InfrahubNode] = None, **kwargs: Any - ) -> None: + def add(self, *args: Any, task: Callable, node: Optional[Any] = None, **kwargs: Any) -> None: self._tasks.append(BatchTask(task=task, node=node, args=args, kwargs=kwargs)) async def execute(self) -> AsyncGenerator: diff --git a/infrahub_sdk/client.py b/infrahub_sdk/client.py index 8833f04b..972c0d73 100644 --- a/infrahub_sdk/client.py +++ b/infrahub_sdk/client.py @@ -6,7 +6,19 @@ import warnings from functools import wraps from time import sleep -from typing import TYPE_CHECKING, Any, Callable, Coroutine, MutableMapping, Optional, TypedDict, Union +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Coroutine, + Literal, + MutableMapping, + Optional, + TypedDict, + TypeVar, + Union, + overload, +) import httpx import ujson @@ -36,6 +48,7 @@ InfrahubNodeSync, ) from infrahub_sdk.object_store import ObjectStore, ObjectStoreSync +from infrahub_sdk.protocols_base import CoreNode, CoreNodeSync from infrahub_sdk.queries import get_commit_update_mutation from infrahub_sdk.query_groups import InfrahubGroupContext, InfrahubGroupContextSync from infrahub_sdk.schema import InfrahubSchema, InfrahubSchemaSync, NodeSchema @@ -47,8 +60,12 @@ if TYPE_CHECKING: from types import TracebackType + # pylint: disable=redefined-builtin disable=too-many-lines +SchemaType = TypeVar("SchemaType", bound=CoreNode) +SchemaTypeSync = TypeVar("SchemaTypeSync", bound=CoreNodeSync) + class NodeDiff(ExtensionTypedDict): branch: str @@ -301,14 +318,33 @@ async def init( ) return cls(address=address, config=config) + @overload async def create( self, kind: str, + data: Optional[dict] = ..., + branch: Optional[str] = ..., + **kwargs: Any, + ) -> InfrahubNode: ... + + @overload + async def create( + self, + kind: type[SchemaType], + data: Optional[dict] = ..., + branch: Optional[str] = ..., + **kwargs: Any, + ) -> SchemaType: ... + + async def create( + self, + kind: Union[str, type[SchemaType]], data: Optional[dict] = None, branch: Optional[str] = None, **kwargs: Any, - ) -> InfrahubNode: + ) -> Union[InfrahubNode, SchemaType]: branch = branch or self.default_branch + schema = await self.schema.get(kind=kind, branch=branch) if not data and not kwargs: @@ -316,16 +352,48 @@ async def create( return InfrahubNode(client=self, schema=schema, branch=branch, data=data or kwargs) - async def delete(self, kind: str, id: str, branch: Optional[str] = None) -> None: + async def delete(self, kind: Union[str, type[SchemaType]], id: str, branch: Optional[str] = None) -> None: branch = branch or self.default_branch schema = await self.schema.get(kind=kind, branch=branch) node = InfrahubNode(client=self, schema=schema, branch=branch, data={"id": id}) await node.delete() + @overload + async def get( + self, + kind: type[SchemaType], + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + id: Optional[str] = ..., + hfid: Optional[list[str]] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + populate_store: bool = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + **kwargs: Any, + ) -> SchemaType: ... + + @overload async def get( self, kind: str, + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + id: Optional[str] = ..., + hfid: Optional[list[str]] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + populate_store: bool = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + **kwargs: Any, + ) -> InfrahubNode: ... + + async def get( + self, + kind: Union[str, type[SchemaType]], at: Optional[Timestamp] = None, branch: Optional[str] = None, id: Optional[str] = None, @@ -336,7 +404,7 @@ async def get( fragment: bool = False, prefetch_relationships: bool = False, **kwargs: Any, - ) -> InfrahubNode: + ) -> Union[InfrahubNode, SchemaType]: branch = branch or self.default_branch schema = await self.schema.get(kind=kind, branch=branch) @@ -370,7 +438,7 @@ async def get( ) if len(results) == 0: - raise NodeNotFoundError(branch_name=branch, node_type=kind, identifier=filters) + raise NodeNotFoundError(branch_name=branch, node_type=schema.kind, identifier=filters) if len(results) > 1: raise IndexError("More than 1 node returned") @@ -405,9 +473,39 @@ async def _process_nodes_and_relationships( return ProcessRelationsNode(nodes=nodes, related_nodes=related_nodes) + @overload + async def all( + self, + kind: type[SchemaType], + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + populate_store: bool = ..., + offset: Optional[int] = ..., + limit: Optional[int] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + ) -> list[SchemaType]: ... + + @overload async def all( self, kind: str, + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + populate_store: bool = ..., + offset: Optional[int] = ..., + limit: Optional[int] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + ) -> list[InfrahubNode]: ... + + async def all( + self, + kind: Union[str, type[SchemaType]], at: Optional[Timestamp] = None, branch: Optional[str] = None, populate_store: bool = False, @@ -417,7 +515,7 @@ async def all( exclude: Optional[list[str]] = None, fragment: bool = False, prefetch_relationships: bool = False, - ) -> list[InfrahubNode]: + ) -> Union[list[InfrahubNode], list[SchemaType]]: """Retrieve all nodes of a given kind Args: @@ -448,9 +546,43 @@ async def all( prefetch_relationships=prefetch_relationships, ) + @overload + async def filters( + self, + kind: type[SchemaType], + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + populate_store: bool = ..., + offset: Optional[int] = ..., + limit: Optional[int] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + partial_match: bool = ..., + **kwargs: Any, + ) -> list[SchemaType]: ... + + @overload async def filters( self, kind: str, + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + populate_store: bool = ..., + offset: Optional[int] = ..., + limit: Optional[int] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + partial_match: bool = ..., + **kwargs: Any, + ) -> list[InfrahubNode]: ... + + async def filters( + self, + kind: Union[str, type[SchemaType]], at: Optional[Timestamp] = None, branch: Optional[str] = None, populate_store: bool = False, @@ -462,7 +594,7 @@ async def filters( prefetch_relationships: bool = False, partial_match: bool = False, **kwargs: Any, - ) -> list[InfrahubNode]: + ) -> Union[list[InfrahubNode], list[SchemaType]]: """Retrieve nodes of a given kind based on provided filters. Args: @@ -858,9 +990,100 @@ async def get_diff_summary( ) return response["DiffSummary"] + @overload + async def allocate_next_ip_address( + self, + resource_pool: CoreNode, + kind: type[SchemaType], + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + address_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: Literal[True] = True, + ) -> SchemaType: ... + + @overload + async def allocate_next_ip_address( + self, + resource_pool: CoreNode, + kind: type[SchemaType], + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + address_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: Literal[False] = False, + ) -> Optional[SchemaType]: ... + + @overload + async def allocate_next_ip_address( + self, + resource_pool: CoreNode, + kind: type[SchemaType], + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + address_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: bool = ..., + ) -> SchemaType: ... + + @overload + async def allocate_next_ip_address( + self, + resource_pool: CoreNode, + kind: Literal[None] = ..., + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + address_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: Literal[True] = True, + ) -> CoreNode: ... + + @overload + async def allocate_next_ip_address( + self, + resource_pool: CoreNode, + kind: Literal[None] = ..., + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + address_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: Literal[False] = False, + ) -> Optional[CoreNode]: ... + + @overload + async def allocate_next_ip_address( + self, + resource_pool: CoreNode, + kind: Literal[None] = ..., + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + address_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: bool = ..., + ) -> Optional[CoreNode]: ... + async def allocate_next_ip_address( self, - resource_pool: InfrahubNode, + resource_pool: CoreNode, + kind: Optional[type[SchemaType]] = None, # pylint: disable=unused-argument identifier: Optional[str] = None, prefix_length: Optional[int] = None, address_type: Optional[str] = None, @@ -869,7 +1092,7 @@ async def allocate_next_ip_address( timeout: Optional[int] = None, tracker: Optional[str] = None, raise_for_error: bool = True, - ) -> Optional[InfrahubNode]: + ) -> Optional[Union[CoreNode, SchemaType]]: """Allocate a new IP address by using the provided resource pool. Args: @@ -911,9 +1134,106 @@ async def allocate_next_ip_address( return await self.get(kind=resource_details["kind"], id=resource_details["id"], branch=branch) return None + @overload async def allocate_next_ip_prefix( self, - resource_pool: InfrahubNode, + resource_pool: CoreNode, + kind: type[SchemaType], + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + member_type: Optional[str] = ..., + prefix_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: Literal[True] = True, + ) -> SchemaType: ... + + @overload + async def allocate_next_ip_prefix( + self, + resource_pool: CoreNode, + kind: type[SchemaType], + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + member_type: Optional[str] = ..., + prefix_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: Literal[False] = False, + ) -> Optional[SchemaType]: ... + + @overload + async def allocate_next_ip_prefix( + self, + resource_pool: CoreNode, + kind: type[SchemaType], + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + member_type: Optional[str] = ..., + prefix_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: bool = ..., + ) -> SchemaType: ... + + @overload + async def allocate_next_ip_prefix( + self, + resource_pool: CoreNode, + kind: Literal[None] = ..., + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + member_type: Optional[str] = ..., + prefix_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: Literal[True] = True, + ) -> CoreNode: ... + + @overload + async def allocate_next_ip_prefix( + self, + resource_pool: CoreNode, + kind: Literal[None] = ..., + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + member_type: Optional[str] = ..., + prefix_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: Literal[False] = False, + ) -> Optional[CoreNode]: ... + + @overload + async def allocate_next_ip_prefix( + self, + resource_pool: CoreNode, + kind: Literal[None] = ..., + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + member_type: Optional[str] = ..., + prefix_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: bool = ..., + ) -> Optional[CoreNode]: ... + + async def allocate_next_ip_prefix( + self, + resource_pool: CoreNode, + kind: Optional[type[SchemaType]] = None, # pylint: disable=unused-argument identifier: Optional[str] = None, prefix_length: Optional[int] = None, member_type: Optional[str] = None, @@ -923,20 +1243,20 @@ async def allocate_next_ip_prefix( timeout: Optional[int] = None, tracker: Optional[str] = None, raise_for_error: bool = True, - ) -> Optional[InfrahubNode]: + ) -> Optional[Union[CoreNode, SchemaType]]: """Allocate a new IP prefix by using the provided resource pool. Args: - resource_pool (InfrahubNode): Node corresponding to the pool to allocate resources from. - identifier (str, optional): Value to perform idempotent allocation, the same resource will be returned for a given identifier. - prefix_length (int, optional): Length of the prefix to allocate. - member_type (str, optional): Member type of the prefix to allocate. - prefix_type (str, optional): Kind of the prefix to allocate. - data (dict, optional): A key/value map to use to set attributes values on the allocated prefix. - branch (str, optional): Name of the branch to allocate from. Defaults to default_branch. - timeout (int, optional): Flag to indicate whether to populate the store with the retrieved nodes. - tracker (str, optional): The offset for pagination. - raise_for_error (bool, optional): The limit for pagination. + resource_pool: Node corresponding to the pool to allocate resources from. + identifier: Value to perform idempotent allocation, the same resource will be returned for a given identifier. + prefix_length: Length of the prefix to allocate. + member_type: Member type of the prefix to allocate. + prefix_type: Kind of the prefix to allocate. + data: A key/value map to use to set attributes values on the allocated prefix. + branch: Name of the branch to allocate from. Defaults to default_branch. + timeout: Flag to indicate whether to populate the store with the retrieved nodes. + tracker: The offset for pagination. + raise_for_error: The limit for pagination. Returns: InfrahubNode: Node corresponding to the allocated resource. """ @@ -1056,13 +1376,31 @@ def init( ) return cls(address=address, config=config) + @overload def create( self, kind: str, + data: Optional[dict] = ..., + branch: Optional[str] = ..., + **kwargs: Any, + ) -> InfrahubNodeSync: ... + + @overload + def create( + self, + kind: type[SchemaTypeSync], + data: Optional[dict] = ..., + branch: Optional[str] = ..., + **kwargs: Any, + ) -> SchemaTypeSync: ... + + def create( + self, + kind: Union[str, type[SchemaTypeSync]], data: Optional[dict] = None, branch: Optional[str] = None, **kwargs: Any, - ) -> InfrahubNodeSync: + ) -> Union[InfrahubNodeSync, SchemaTypeSync]: branch = branch or self.default_branch schema = self.schema.get(kind=kind, branch=branch) @@ -1071,7 +1409,7 @@ def create( return InfrahubNodeSync(client=self, schema=schema, branch=branch, data=data or kwargs) - def delete(self, kind: str, id: str, branch: Optional[str] = None) -> None: + def delete(self, kind: Union[str, type[SchemaTypeSync]], id: str, branch: Optional[str] = None) -> None: branch = branch or self.default_branch schema = self.schema.get(kind=kind, branch=branch) @@ -1163,9 +1501,39 @@ def execute_graphql( # TODO add a special method to execute mutation that will check if the method returned OK + @overload + def all( + self, + kind: type[SchemaTypeSync], + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + populate_store: bool = ..., + offset: Optional[int] = ..., + limit: Optional[int] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + ) -> list[SchemaTypeSync]: ... + + @overload def all( self, kind: str, + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + populate_store: bool = ..., + offset: Optional[int] = ..., + limit: Optional[int] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + ) -> list[InfrahubNodeSync]: ... + + def all( + self, + kind: Union[str, type[SchemaTypeSync]], at: Optional[Timestamp] = None, branch: Optional[str] = None, populate_store: bool = False, @@ -1175,7 +1543,7 @@ def all( exclude: Optional[list[str]] = None, fragment: bool = False, prefetch_relationships: bool = False, - ) -> list[InfrahubNodeSync]: + ) -> Union[list[InfrahubNodeSync], list[SchemaTypeSync]]: """Retrieve all nodes of a given kind Args: @@ -1235,9 +1603,43 @@ def _process_nodes_and_relationships( return ProcessRelationsNodeSync(nodes=nodes, related_nodes=related_nodes) + @overload + def filters( + self, + kind: type[SchemaTypeSync], + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + populate_store: bool = ..., + offset: Optional[int] = ..., + limit: Optional[int] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + partial_match: bool = ..., + **kwargs: Any, + ) -> list[SchemaTypeSync]: ... + + @overload def filters( self, kind: str, + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + populate_store: bool = ..., + offset: Optional[int] = ..., + limit: Optional[int] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + partial_match: bool = ..., + **kwargs: Any, + ) -> list[InfrahubNodeSync]: ... + + def filters( + self, + kind: Union[str, type[SchemaTypeSync]], at: Optional[Timestamp] = None, branch: Optional[str] = None, populate_store: bool = False, @@ -1249,7 +1651,7 @@ def filters( prefetch_relationships: bool = False, partial_match: bool = False, **kwargs: Any, - ) -> list[InfrahubNodeSync]: + ) -> Union[list[InfrahubNodeSync], list[SchemaTypeSync]]: """Retrieve nodes of a given kind based on provided filters. Args: @@ -1331,9 +1733,41 @@ def filters( return nodes + @overload + def get( + self, + kind: type[SchemaTypeSync], + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + id: Optional[str] = ..., + hfid: Optional[list[str]] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + populate_store: bool = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + **kwargs: Any, + ) -> SchemaTypeSync: ... + + @overload def get( self, kind: str, + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + id: Optional[str] = ..., + hfid: Optional[list[str]] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + populate_store: bool = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + **kwargs: Any, + ) -> InfrahubNodeSync: ... + + def get( + self, + kind: Union[str, type[SchemaTypeSync]], at: Optional[Timestamp] = None, branch: Optional[str] = None, id: Optional[str] = None, @@ -1344,7 +1778,7 @@ def get( fragment: bool = False, prefetch_relationships: bool = False, **kwargs: Any, - ) -> InfrahubNodeSync: + ) -> Union[InfrahubNodeSync, SchemaTypeSync]: branch = branch or self.default_branch schema = self.schema.get(kind=kind, branch=branch) @@ -1378,7 +1812,7 @@ def get( ) if len(results) == 0: - raise NodeNotFoundError(branch_name=branch, node_type=kind, identifier=filters) + raise NodeNotFoundError(branch_name=branch, node_type=schema.kind, identifier=filters) if len(results) > 1: raise IndexError("More than 1 node returned") @@ -1490,9 +1924,100 @@ def get_diff_summary( ) return response["DiffSummary"] + @overload + def allocate_next_ip_address( + self, + resource_pool: CoreNodeSync, + kind: type[SchemaTypeSync], + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + address_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: Literal[True] = True, + ) -> SchemaTypeSync: ... + + @overload + def allocate_next_ip_address( + self, + resource_pool: CoreNodeSync, + kind: type[SchemaTypeSync], + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + address_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: Literal[False] = False, + ) -> Optional[SchemaTypeSync]: ... + + @overload + def allocate_next_ip_address( + self, + resource_pool: CoreNodeSync, + kind: type[SchemaTypeSync], + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + address_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: bool = ..., + ) -> SchemaTypeSync: ... + + @overload + def allocate_next_ip_address( + self, + resource_pool: CoreNodeSync, + kind: Literal[None] = ..., + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + address_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: Literal[True] = True, + ) -> CoreNodeSync: ... + + @overload + def allocate_next_ip_address( + self, + resource_pool: CoreNodeSync, + kind: Literal[None] = ..., + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + address_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: Literal[False] = False, + ) -> Optional[CoreNodeSync]: ... + + @overload + def allocate_next_ip_address( + self, + resource_pool: CoreNodeSync, + kind: Literal[None] = ..., + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + address_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: bool = ..., + ) -> Optional[CoreNodeSync]: ... + def allocate_next_ip_address( self, - resource_pool: InfrahubNodeSync, + resource_pool: CoreNodeSync, + kind: Optional[type[SchemaTypeSync]] = None, # pylint: disable=unused-argument identifier: Optional[str] = None, prefix_length: Optional[int] = None, address_type: Optional[str] = None, @@ -1501,7 +2026,7 @@ def allocate_next_ip_address( timeout: Optional[int] = None, tracker: Optional[str] = None, raise_for_error: bool = True, - ) -> Optional[InfrahubNodeSync]: + ) -> Optional[Union[CoreNodeSync, SchemaTypeSync]]: """Allocate a new IP address by using the provided resource pool. Args: @@ -1539,9 +2064,106 @@ def allocate_next_ip_address( return self.get(kind=resource_details["kind"], id=resource_details["id"], branch=branch) return None + @overload + def allocate_next_ip_prefix( + self, + resource_pool: CoreNodeSync, + kind: type[SchemaTypeSync], + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + member_type: Optional[str] = ..., + prefix_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: Literal[True] = True, + ) -> SchemaTypeSync: ... + + @overload + def allocate_next_ip_prefix( + self, + resource_pool: CoreNodeSync, + kind: type[SchemaTypeSync], + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + member_type: Optional[str] = ..., + prefix_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: Literal[False] = False, + ) -> Optional[SchemaTypeSync]: ... + + @overload + def allocate_next_ip_prefix( + self, + resource_pool: CoreNodeSync, + kind: type[SchemaTypeSync], + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + member_type: Optional[str] = ..., + prefix_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: bool = ..., + ) -> SchemaTypeSync: ... + + @overload + def allocate_next_ip_prefix( + self, + resource_pool: CoreNodeSync, + kind: Literal[None] = ..., + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + member_type: Optional[str] = ..., + prefix_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: Literal[True] = True, + ) -> CoreNodeSync: ... + + @overload + def allocate_next_ip_prefix( + self, + resource_pool: CoreNodeSync, + kind: Literal[None] = ..., + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + member_type: Optional[str] = ..., + prefix_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: Literal[False] = False, + ) -> Optional[CoreNodeSync]: ... + + @overload + def allocate_next_ip_prefix( + self, + resource_pool: CoreNodeSync, + kind: Literal[None] = ..., + identifier: Optional[str] = ..., + prefix_length: Optional[int] = ..., + member_type: Optional[str] = ..., + prefix_type: Optional[str] = ..., + data: Optional[dict[str, Any]] = ..., + branch: Optional[str] = ..., + timeout: Optional[int] = ..., + tracker: Optional[str] = ..., + raise_for_error: bool = ..., + ) -> Optional[CoreNodeSync]: ... + def allocate_next_ip_prefix( self, - resource_pool: InfrahubNodeSync, + resource_pool: CoreNodeSync, + kind: Optional[type[SchemaTypeSync]] = None, # pylint: disable=unused-argument identifier: Optional[str] = None, prefix_length: Optional[int] = None, member_type: Optional[str] = None, @@ -1551,7 +2173,7 @@ def allocate_next_ip_prefix( timeout: Optional[int] = None, tracker: Optional[str] = None, raise_for_error: bool = True, - ) -> Optional[InfrahubNodeSync]: + ) -> Optional[Union[CoreNodeSync, SchemaTypeSync]]: """Allocate a new IP prefix by using the provided resource pool. Args: diff --git a/infrahub_sdk/code_generator.py b/infrahub_sdk/code_generator.py new file mode 100644 index 00000000..0bfb4e34 --- /dev/null +++ b/infrahub_sdk/code_generator.py @@ -0,0 +1,123 @@ +from typing import Any, Mapping, Optional + +import jinja2 + +from infrahub_sdk import protocols as sdk_protocols +from infrahub_sdk.ctl.constants import PROTOCOLS_TEMPLATE +from infrahub_sdk.schema import ( + AttributeSchema, + GenericSchema, + MainSchemaTypes, + NodeSchema, + ProfileSchema, + RelationshipSchema, +) + + +class CodeGenerator: + def __init__(self, schema: dict[str, MainSchemaTypes]): + self.generics: dict[str, GenericSchema] = {} + self.nodes: dict[str, NodeSchema] = {} + self.profiles: dict[str, ProfileSchema] = {} + + for name, schema_type in schema.items(): + if isinstance(schema_type, GenericSchema): + self.generics[name] = schema_type + if isinstance(schema_type, NodeSchema): + self.nodes[name] = schema_type + if isinstance(schema_type, ProfileSchema): + self.profiles[name] = schema_type + + self.base_protocols = [ + e + for e in dir(sdk_protocols) + if not e.startswith("__") + and not e.endswith("__") + and e + not in ("TYPE_CHECKING", "CoreNode", "Optional", "Protocol", "Union", "annotations", "runtime_checkable") + ] + + self.sorted_generics = self._sort_and_filter_models(self.generics, filters=["CoreNode"] + self.base_protocols) + self.sorted_nodes = self._sort_and_filter_models(self.nodes, filters=["CoreNode"] + self.base_protocols) + self.sorted_profiles = self._sort_and_filter_models( + self.profiles, filters=["CoreProfile"] + self.base_protocols + ) + + def render(self, sync: bool = True) -> str: + jinja2_env = jinja2.Environment(loader=jinja2.BaseLoader(), trim_blocks=True, lstrip_blocks=True) + jinja2_env.filters["inheritance"] = self._jinja2_filter_inheritance + jinja2_env.filters["render_attribute"] = self._jinja2_filter_render_attribute + jinja2_env.filters["render_relationship"] = self._jinja2_filter_render_relationship + + template = jinja2_env.from_string(PROTOCOLS_TEMPLATE) + return template.render( + generics=self.sorted_generics, + nodes=self.sorted_nodes, + profiles=self.sorted_profiles, + base_protocols=self.base_protocols, + sync=sync, + ) + + @staticmethod + def _jinja2_filter_inheritance(value: dict[str, Any]) -> str: + inherit_from: list[str] = value.get("inherit_from", []) + + if not inherit_from: + return "CoreNode" + return ", ".join(inherit_from) + + @staticmethod + def _jinja2_filter_render_attribute(value: AttributeSchema) -> str: + attribute_kind_map = { + "boolean": "Boolean", + "datetime": "DateTime", + "dropdown": "Dropdown", + "hashedpassword": "HashedPassword", + "iphost": "IPHost", + "ipnetwork": "IPNetwork", + "json": "JSONAttribute", + "list": "ListAttribute", + "number": "Integer", + "password": "String", + "text": "String", + "textarea": "String", + "url": "URL", + } + + name = value.name + kind = value.kind + + attribute_kind = attribute_kind_map[kind.lower()] + if value.optional: + attribute_kind = f"{attribute_kind}Optional" + + return f"{name}: {attribute_kind}" + + @staticmethod + def _jinja2_filter_render_relationship(value: RelationshipSchema, sync: bool = False) -> str: + name = value.name + cardinality = value.cardinality + + type_ = "RelatedNode" + if cardinality == "many": + type_ = "RelationshipManager" + + if sync: + type_ += "Sync" + + return f"{name}: {type_}" + + @staticmethod + def _sort_and_filter_models( + models: Mapping[str, MainSchemaTypes], filters: Optional[list[str]] = None + ) -> list[MainSchemaTypes]: + if filters is None: + filters = ["CoreNode"] + + filtered: list[MainSchemaTypes] = [] + for name, model in models.items(): + if name in filters: + continue + filtered.append(model) + + return sorted(filtered, key=lambda k: k.name) diff --git a/infrahub_sdk/ctl/cli_commands.py b/infrahub_sdk/ctl/cli_commands.py index 8779b0f9..498e7b0d 100644 --- a/infrahub_sdk/ctl/cli_commands.py +++ b/infrahub_sdk/ctl/cli_commands.py @@ -4,7 +4,7 @@ import logging import sys from pathlib import Path -from typing import Any, Callable, Optional, Union +from typing import Any, Callable, Optional import jinja2 import typer @@ -14,25 +14,29 @@ from rich.traceback import Traceback from infrahub_sdk import __version__ as sdk_version -from infrahub_sdk import protocols as sdk_protocols from infrahub_sdk.async_typer import AsyncTyper +from infrahub_sdk.code_generator import CodeGenerator from infrahub_sdk.ctl import config from infrahub_sdk.ctl.branch import app as branch_app from infrahub_sdk.ctl.check import run as run_check from infrahub_sdk.ctl.client import initialize_client, initialize_client_sync -from infrahub_sdk.ctl.constants import PROTOCOLS_TEMPLATE from infrahub_sdk.ctl.exceptions import QueryNotFoundError from infrahub_sdk.ctl.generator import run as run_generator from infrahub_sdk.ctl.render import list_jinja2_transforms from infrahub_sdk.ctl.repository import app as repository_app from infrahub_sdk.ctl.repository import get_repository_config -from infrahub_sdk.ctl.schema import app as schema +from infrahub_sdk.ctl.schema import app as schema_app +from infrahub_sdk.ctl.schema import load_schemas_from_disk_and_exit from infrahub_sdk.ctl.transform import list_transforms from infrahub_sdk.ctl.utils import catch_exception, execute_graphql_query, parse_cli_vars from infrahub_sdk.ctl.validate import app as validate_app from infrahub_sdk.exceptions import GraphQLError, InfrahubTransformNotFoundError from infrahub_sdk.jinja2 import identify_faulty_jinja_code -from infrahub_sdk.schema import AttributeSchema, GenericSchema, InfrahubRepositoryConfig, NodeSchema, RelationshipSchema +from infrahub_sdk.schema import ( + InfrahubRepositoryConfig, + MainSchemaTypes, + SchemaRoot, +) from infrahub_sdk.transforms import get_transform_class_instance from infrahub_sdk.utils import get_branch, write_to_file @@ -43,7 +47,7 @@ app = AsyncTyper(pretty_exceptions_show_locals=False) app.add_typer(branch_app, name="branch") -app.add_typer(schema, name="schema") +app.add_typer(schema_app, name="schema") app.add_typer(validate_app, name="validate") app.add_typer(repository_app, name="repository") app.command(name="dump")(dump) @@ -323,114 +327,36 @@ def transform( @app.command(name="protocols") @catch_exception(console=console) def protocols( # noqa: PLR0915 + schemas: list[Path] = typer.Option(None, help="List of schemas or directory to load."), branch: str = typer.Option(None, help="Branch of schema to export Python protocols for."), + sync: bool = typer.Option(False, help="Generate for sync or async."), _: str = CONFIG_PARAM, out: str = typer.Option("schema_protocols.py", help="Path to a file to save the result."), ) -> None: """Export Python protocols corresponding to a schema.""" - def _jinja2_filter_inheritance(value: dict[str, Any]) -> str: - inherit_from: list[str] = value.get("inherit_from", []) - - if not inherit_from: - return "CoreNode" - return ", ".join(inherit_from) - - def _jinja2_filter_render_attribute(value: AttributeSchema) -> str: - attribute_kind_map = { - "boolean": "bool", - "datetime": "datetime", - "dropdown": "str", - "hashedpassword": "str", - "iphost": "str", - "ipnetwork": "str", - "json": "dict", - "list": "list", - "number": "int", - "password": "str", - "text": "str", - "textarea": "str", - "url": "str", - } - - name = value.name - kind = value.kind - - attribute_kind = attribute_kind_map[kind.lower()] - if value.optional: - attribute_kind = f"Optional[{attribute_kind}]" - - return f"{name}: {attribute_kind}" - - def _jinja2_filter_render_relationship(value: RelationshipSchema, sync: bool = False) -> str: - name = value.name - cardinality = value.cardinality - - type_ = "RelatedNode" - if cardinality == "many": - type_ = "RelationshipManager" - - if sync: - type_ += "Sync" - - return f"{name}: {type_}" - - def _sort_and_filter_models( - models: dict[str, Union[GenericSchema, NodeSchema]], filters: Optional[list[str]] = None - ) -> list[Union[GenericSchema, NodeSchema]]: - if filters is None: - filters = ["CoreNode"] - - filtered: list[Union[GenericSchema, NodeSchema]] = [] - for name, model in models.items(): - if name in filters: - continue - filtered.append(model) - - return sorted(filtered, key=lambda k: k.name) + schema: dict[str, MainSchemaTypes] = {} - client = initialize_client_sync() - current_schema = client.schema.all(branch=branch) - - generics: dict[str, GenericSchema] = {} - nodes: dict[str, NodeSchema] = {} - - for name, schema_type in current_schema.items(): - if isinstance(schema_type, GenericSchema): - generics[name] = schema_type - if isinstance(schema_type, NodeSchema): - nodes[name] = schema_type - - base_protocols = [ - e - for e in dir(sdk_protocols) - if not e.startswith("__") - and not e.endswith("__") - and e not in ("TYPE_CHECKING", "CoreNode", "Optional", "Protocol", "Union", "annotations", "runtime_checkable") - ] - sorted_generics = _sort_and_filter_models(generics, filters=["CoreNode"] + base_protocols) - sorted_nodes = _sort_and_filter_models(nodes, filters=["CoreNode"] + base_protocols) - - jinja2_env = jinja2.Environment(loader=jinja2.BaseLoader, trim_blocks=True, lstrip_blocks=True) - jinja2_env.filters["inheritance"] = _jinja2_filter_inheritance - jinja2_env.filters["render_attribute"] = _jinja2_filter_render_attribute - jinja2_env.filters["render_relationship"] = _jinja2_filter_render_relationship - - template = jinja2_env.from_string(PROTOCOLS_TEMPLATE) - rendered = template.render(generics=sorted_generics, nodes=sorted_nodes, base_protocols=base_protocols, sync=False) - rendered_sync = template.render( - generics=sorted_generics, nodes=sorted_nodes, base_protocols=base_protocols, sync=True - ) - output_file = Path(out) - output_file_sync = Path(output_file.stem + "_sync" + output_file.suffix) + if schemas: + schemas_data = load_schemas_from_disk_and_exit(schemas=schemas) + + for data in schemas_data: + data.load_content() + schema_root = SchemaRoot(**data.content) + schema.update({item.kind: item for item in schema_root.nodes + schema_root.generics}) + + else: + client = initialize_client_sync() + schema.update(client.schema.fetch(branch=branch)) + + code_generator = CodeGenerator(schema=schema) if out: - write_to_file(output_file, rendered) - write_to_file(output_file_sync, rendered_sync) - console.print(f"Python protocols exported in {output_file} and {output_file_sync}") + output_file = Path(out) + write_to_file(output_file, code_generator.render(sync=sync)) + console.print(f"Python protocols exported in {output_file}") else: - console.print(rendered) - console.print(rendered_sync) + console.print(code_generator.render(sync=sync)) @app.command(name="version") diff --git a/infrahub_sdk/ctl/constants.py b/infrahub_sdk/ctl/constants.py index 24887197..5c82c8a4 100644 --- a/infrahub_sdk/ctl/constants.py +++ b/infrahub_sdk/ctl/constants.py @@ -14,6 +14,30 @@ {% else %} from infrahub_sdk.node import RelatedNode, RelationshipManager {% endif %} + from infrahub_sdk.protocols_base import ( + String, + StringOptional, + Integer, + IntegerOptional, + Boolean, + BooleanOptional, + DateTime, + DateTimeOptional, + Dropdown, + DropdownOptional, + HashedPassword, + HashedPasswordOptional, + IPHost, + IPHostOptional, + IPNetwork, + IPNetworkOptional, + JSONAttribute, + JSONAttributeOptional, + ListAttribute, + ListAttributeOptional, + URL, + URLOptional, + ) {% for generic in generics %} @@ -36,6 +60,7 @@ class {{ generic.namespace + generic.name }}(CoreNode): children: RelationshipManager {% endif %} {% endif %} + {% endfor %} @@ -59,5 +84,29 @@ class {{ node.namespace + node.name }}({{ node.inherit_from | join(", ") or "Cor children: RelationshipManager {% endif %} {% endif %} + +{% endfor %} + +{% for node in profiles %} +class {{ node.namespace + node.name }}({{ node.inherit_from | join(", ") or "CoreNode" }}): + {% if not node.attributes|default([]) and not node.relationships|default([]) %} + pass + {% endif %} + {% for attribute in node.attributes|default([]) %} + {{ attribute | render_attribute }} + {% endfor %} + {% for relationship in node.relationships|default([]) %} + {{ relationship | render_relationship(sync) }} + {% endfor %} + {% if node.hierarchical | default(false) %} + {% if sync %} + parent: RelatedNodeSync + children: RelationshipManagerSync + {% else %} + parent: RelatedNode + children: RelationshipManager + {% endif %} + {% endif %} + {% endfor %} """ diff --git a/infrahub_sdk/ctl/schema.py b/infrahub_sdk/ctl/schema.py index f6f2cda3..57ecc379 100644 --- a/infrahub_sdk/ctl/schema.py +++ b/infrahub_sdk/ctl/schema.py @@ -49,7 +49,7 @@ def load_schemas_from_disk(schemas: list[Path]) -> list[SchemaFile]: return schemas_data -def load_schemas_from_disk_and_exit(schemas: list[Path]): +def load_schemas_from_disk_and_exit(schemas: list[Path]) -> list[SchemaFile]: has_error = False try: schemas_data = load_schemas_from_disk(schemas=schemas) diff --git a/infrahub_sdk/protocols.py b/infrahub_sdk/protocols.py index 9e66873f..7f2816ce 100644 --- a/infrahub_sdk/protocols.py +++ b/infrahub_sdk/protocols.py @@ -72,6 +72,10 @@ class CoreArtifactTarget(CoreNode): artifacts: RelationshipManager +class CoreBasePermission(CoreNode): + roles: RelationshipManager + + class CoreCheck(CoreNode): name: StringOptional label: StringOptional @@ -317,6 +321,11 @@ class CoreGeneratorValidator(CoreValidator): definition: RelatedNode +class CoreGlobalPermission(CoreBasePermission): + name: String + action: Dropdown + + class CoreGraphQLQuery(CoreNode): name: String description: StringOptional @@ -357,6 +366,14 @@ class CoreNumberPool(CoreResourcePool, LineageSource): end_range: Integer +class CoreObjectPermission(CoreBasePermission): + branch: String + namespace: String + name: String + action: Enum + decision: Enum + + class CoreObjectThread(CoreThread): object_path: String @@ -428,6 +445,16 @@ class CoreTransformPython(CoreTransformation): class_name: String +class CoreUserGroup(CoreGroup): + roles: RelationshipManager + + +class CoreUserRole(CoreNode): + name: String + groups: RelationshipManager + permissions: RelationshipManager + + class CoreUserValidator(CoreValidator): check_definition: RelatedNode repository: RelatedNode @@ -490,6 +517,10 @@ class CoreArtifactTargetSync(CoreNodeSync): artifacts: RelationshipManagerSync +class CoreBasePermissionSync(CoreNodeSync): + roles: RelationshipManagerSync + + class CoreCheckSync(CoreNodeSync): name: StringOptional label: StringOptional @@ -735,6 +766,11 @@ class CoreGeneratorValidatorSync(CoreValidatorSync): definition: RelatedNodeSync +class CoreGlobalPermissionSync(CoreBasePermissionSync): + name: String + action: Dropdown + + class CoreGraphQLQuerySync(CoreNodeSync): name: String description: StringOptional @@ -775,6 +811,14 @@ class CoreNumberPoolSync(CoreResourcePoolSync, LineageSourceSync): end_range: Integer +class CoreObjectPermissionSync(CoreBasePermissionSync): + branch: String + namespace: String + name: String + action: Enum + decision: Enum + + class CoreObjectThreadSync(CoreThreadSync): object_path: String @@ -846,6 +890,16 @@ class CoreTransformPythonSync(CoreTransformationSync): class_name: String +class CoreUserGroupSync(CoreGroupSync): + roles: RelationshipManagerSync + + +class CoreUserRoleSync(CoreNodeSync): + name: String + groups: RelationshipManagerSync + permissions: RelationshipManagerSync + + class CoreUserValidatorSync(CoreValidatorSync): check_definition: RelatedNodeSync repository: RelatedNodeSync diff --git a/infrahub_sdk/protocols_base.py b/infrahub_sdk/protocols_base.py index 44f07927..b23b15c2 100644 --- a/infrahub_sdk/protocols_base.py +++ b/infrahub_sdk/protocols_base.py @@ -1,11 +1,18 @@ from __future__ import annotations -from typing import Any, Optional, Protocol, runtime_checkable +from typing import TYPE_CHECKING, Any, Optional, Protocol, Union, runtime_checkable +if TYPE_CHECKING: + import ipaddress + from infrahub_sdk.schema import MainSchemaTypes + + +@runtime_checkable class RelatedNode(Protocol): ... +@runtime_checkable class RelatedNodeSync(Protocol): ... @@ -79,19 +86,19 @@ class DropdownOptional(Attribute): class IPNetwork(Attribute): - value: str + value: Union[ipaddress.IPv4Network, ipaddress.IPv6Network] class IPNetworkOptional(Attribute): - value: Optional[str] + value: Optional[Union[ipaddress.IPv4Network, ipaddress.IPv6Network]] class IPHost(Attribute): - value: str + value: Union[ipaddress.IPv4Address, ipaddress.IPv6Address] class IPHostOptional(Attribute): - value: Optional[str] + value: Optional[Union[ipaddress.IPv4Address, ipaddress.IPv6Address]] class HashedPassword(Attribute): @@ -118,29 +125,60 @@ class ListAttributeOptional(Attribute): value: Optional[list[Any]] +@runtime_checkable class CoreNodeBase(Protocol): + _schema: MainSchemaTypes id: str display_label: Optional[str] - hfid: Optional[list[str]] - hfid_str: Optional[str] + @property + def hfid(self) -> Optional[list[str]]: ... + + @property + def hfid_str(self) -> Optional[str]: ... + + def get_human_friendly_id_as_string(self, include_kind: bool = False) -> Optional[str]: ... -class CoreNode(CoreNodeBase, Protocol): def get_kind(self) -> str: ... - async def save(self) -> None: ... + def is_ip_prefix(self) -> bool: ... + + def is_ip_address(self) -> bool: ... + + def is_resource_pool(self) -> bool: ... + + def get_raw_graphql_data(self) -> Optional[dict]: ... + + def validate_filters(self, filters: Optional[dict[str, Any]] = None) -> bool: ... + + def extract(self, params: dict[str, str]) -> dict[str, Any]: ... + + +@runtime_checkable +class CoreNode(CoreNodeBase, Protocol): + async def save(self, allow_upsert: bool = False, update_group_context: Optional[bool] = None) -> None: ... + + async def delete(self) -> None: ... async def update(self, do_full_update: bool) -> None: ... + async def create(self, allow_upsert: bool = False) -> None: ... -class CoreNodeSync(CoreNodeBase, Protocol): - id: str - display_label: Optional[str] - hfid: Optional[list[str]] - hfid_str: Optional[str] + async def add_relationships(self, relation_to_update: str, related_nodes: list[str]) -> None: ... - def get_kind(self) -> str: ... + async def remove_relationships(self, relation_to_update: str, related_nodes: list[str]) -> None: ... + + +@runtime_checkable +class CoreNodeSync(CoreNodeBase, Protocol): + def save(self, allow_upsert: bool = False, update_group_context: Optional[bool] = None) -> None: ... - def save(self) -> None: ... + def delete(self) -> None: ... def update(self, do_full_update: bool) -> None: ... + + def create(self, allow_upsert: bool = False) -> None: ... + + def add_relationships(self, relation_to_update: str, related_nodes: list[str]) -> None: ... + + def remove_relationships(self, relation_to_update: str, related_nodes: list[str]) -> None: ... diff --git a/infrahub_sdk/schema.py b/infrahub_sdk/schema.py index 58dd06be..d7d4fc15 100644 --- a/infrahub_sdk/schema.py +++ b/infrahub_sdk/schema.py @@ -17,7 +17,7 @@ from infrahub_sdk.utils import duplicates if TYPE_CHECKING: - from infrahub_sdk.client import InfrahubClient, InfrahubClientSync + from infrahub_sdk.client import InfrahubClient, InfrahubClientSync, SchemaType, SchemaTypeSync from infrahub_sdk.node import InfrahubNode, InfrahubNodeSync InfrahubNodeTypes = Union[InfrahubNode, InfrahubNodeSync] @@ -511,30 +511,47 @@ def _validate_load_schema_response(response: httpx.Response) -> SchemaLoadRespon raise InvalidResponseError(message=f"Invalid response received from server HTTP {response.status_code}") + @staticmethod + def _get_schema_name(schema: Union[type[Union[SchemaType, SchemaTypeSync]], str]) -> str: + if hasattr(schema, "_is_runtime_protocol") and schema._is_runtime_protocol: # type: ignore[union-attr] + return schema.__name__ # type: ignore[union-attr] + + if isinstance(schema, str): + return schema + + raise ValueError("schema must be a protocol or a string") + class InfrahubSchema(InfrahubSchemaBase): def __init__(self, client: InfrahubClient): self.client = client self.cache: dict = defaultdict(lambda: dict) - async def get(self, kind: str, branch: Optional[str] = None, refresh: bool = False) -> MainSchemaTypes: + async def get( + self, + kind: Union[type[Union[SchemaType, SchemaTypeSync]], str], + branch: Optional[str] = None, + refresh: bool = False, + ) -> MainSchemaTypes: branch = branch or self.client.default_branch + kind_str = self._get_schema_name(schema=kind) + if refresh: self.cache[branch] = await self.fetch(branch=branch) - if branch in self.cache and kind in self.cache[branch]: - return self.cache[branch][kind] + if branch in self.cache and kind_str in self.cache[branch]: + return self.cache[branch][kind_str] # Fetching the latest schema from the server if we didn't fetch it earlier # because we coulnd't find the object on the local cache if not refresh: self.cache[branch] = await self.fetch(branch=branch) - if branch in self.cache and kind in self.cache[branch]: - return self.cache[branch][kind] + if branch in self.cache and kind_str in self.cache[branch]: + return self.cache[branch][kind_str] - raise SchemaNotFoundError(identifier=kind) + raise SchemaNotFoundError(identifier=kind_str) async def all( self, branch: Optional[str] = None, refresh: bool = False, namespaces: Optional[list[str]] = None @@ -760,24 +777,31 @@ def all( return self.cache[branch] - def get(self, kind: str, branch: Optional[str] = None, refresh: bool = False) -> MainSchemaTypes: + def get( + self, + kind: Union[type[Union[SchemaType, SchemaTypeSync]], str], + branch: Optional[str] = None, + refresh: bool = False, + ) -> MainSchemaTypes: branch = branch or self.client.default_branch + kind_str = self._get_schema_name(schema=kind) + if refresh: self.cache[branch] = self.fetch(branch=branch) - if branch in self.cache and kind in self.cache[branch]: - return self.cache[branch][kind] + if branch in self.cache and kind_str in self.cache[branch]: + return self.cache[branch][kind_str] # Fetching the latest schema from the server if we didn't fetch it earlier # because we coulnd't find the object on the local cache if not refresh: self.cache[branch] = self.fetch(branch=branch) - if branch in self.cache and kind in self.cache[branch]: - return self.cache[branch][kind] + if branch in self.cache and kind_str in self.cache[branch]: + return self.cache[branch][kind_str] - raise SchemaNotFoundError(identifier=kind) + raise SchemaNotFoundError(identifier=kind_str) def _get_kind_and_attribute_schema( self, kind: Union[str, InfrahubNodeTypes], attribute: str, branch: Optional[str] = None diff --git a/infrahub_sdk/store.py b/infrahub_sdk/store.py index 1384bbd8..2289ce48 100644 --- a/infrahub_sdk/store.py +++ b/infrahub_sdk/store.py @@ -6,9 +6,20 @@ from infrahub_sdk.exceptions import NodeNotFoundError if TYPE_CHECKING: + from infrahub_sdk.client import SchemaType from infrahub_sdk.node import InfrahubNode, InfrahubNodeSync +def get_schema_name(schema: Optional[Union[str, type[SchemaType]]] = None) -> Optional[str]: + if isinstance(schema, str): + return schema + + if hasattr(schema, "_is_runtime_protocol") and schema._is_runtime_protocol: # type: ignore[union-attr] + return schema.__name__ # type: ignore[union-attr] + + return None + + class NodeStoreBase: """Internal Store for InfrahubNode objects. @@ -20,7 +31,7 @@ def __init__(self) -> None: self._store: dict[str, dict] = defaultdict(dict) self._store_by_hfid: dict[str, Any] = defaultdict(dict) - def _set(self, node: Union[InfrahubNode, InfrahubNodeSync], key: Optional[str] = None) -> None: + def _set(self, node: Union[InfrahubNode, InfrahubNodeSync, SchemaType], key: Optional[str] = None) -> None: hfid = node.get_human_friendly_id_as_string(include_kind=True) if not key and not hfid: @@ -33,18 +44,19 @@ def _set(self, node: Union[InfrahubNode, InfrahubNodeSync], key: Optional[str] = if hfid: self._store_by_hfid[hfid] = node - def _get(self, key: str, kind: Optional[str] = None, raise_when_missing: bool = True): # type: ignore[no-untyped-def] - if kind and kind not in self._store and key not in self._store[kind]: # type: ignore[attr-defined] + def _get(self, key: str, kind: Optional[Union[str, type[SchemaType]]] = None, raise_when_missing: bool = True): # type: ignore[no-untyped-def] + kind_name = get_schema_name(schema=kind) + if kind_name and kind_name not in self._store and key not in self._store[kind_name]: # type: ignore[attr-defined] if not raise_when_missing: return None raise NodeNotFoundError( - node_type=kind, + node_type=kind_name, identifier={"key": [key]}, message="Unable to find the node in the Store", ) - if kind and kind in self._store and key in self._store[kind]: # type: ignore[attr-defined] - return self._store[kind][key] # type: ignore[attr-defined] + if kind_name and kind_name in self._store and key in self._store[kind_name]: # type: ignore[attr-defined] + return self._store[kind_name][key] # type: ignore[attr-defined] for _, item in self._store.items(): # type: ignore[attr-defined] if key in item: @@ -73,14 +85,30 @@ def _get_by_hfid(self, key: str, raise_when_missing: bool = True): # type: igno class NodeStore(NodeStoreBase): @overload - def get(self, key: str, kind: Optional[str] = None, raise_when_missing: Literal[True] = True) -> InfrahubNode: ... + def get(self, key: str, kind: type[SchemaType], raise_when_missing: Literal[True] = True) -> SchemaType: ... @overload def get( - self, key: str, kind: Optional[str] = None, raise_when_missing: Literal[False] = False + self, key: str, kind: type[SchemaType], raise_when_missing: Literal[False] = False + ) -> Optional[SchemaType]: ... + + @overload + def get(self, key: str, kind: type[SchemaType], raise_when_missing: bool = ...) -> SchemaType: ... + + @overload + def get( + self, key: str, kind: Optional[str] = ..., raise_when_missing: Literal[False] = False ) -> Optional[InfrahubNode]: ... - def get(self, key: str, kind: Optional[str] = None, raise_when_missing: bool = True) -> Optional[InfrahubNode]: + @overload + def get(self, key: str, kind: Optional[str] = ..., raise_when_missing: Literal[True] = True) -> InfrahubNode: ... + + @overload + def get(self, key: str, kind: Optional[str] = ..., raise_when_missing: bool = ...) -> InfrahubNode: ... + + def get( + self, key: str, kind: Optional[Union[str, type[SchemaType]]] = None, raise_when_missing: bool = True + ) -> Optional[Union[InfrahubNode, SchemaType]]: return self._get(key=key, kind=kind, raise_when_missing=raise_when_missing) @overload @@ -92,7 +120,7 @@ def get_by_hfid(self, key: str, raise_when_missing: Literal[False] = False) -> O def get_by_hfid(self, key: str, raise_when_missing: bool = True) -> Optional[InfrahubNode]: return self._get_by_hfid(key=key, raise_when_missing=raise_when_missing) - def set(self, node: InfrahubNode, key: Optional[str] = None) -> None: + def set(self, node: Any, key: Optional[str] = None) -> None: return self._set(node=node, key=key) diff --git a/infrahub_sdk/transfer/importer/json.py b/infrahub_sdk/transfer/importer/json.py index 20b1ac2a..f6ceeeda 100644 --- a/infrahub_sdk/transfer/importer/json.py +++ b/infrahub_sdk/transfer/importer/json.py @@ -129,6 +129,8 @@ async def update_optional_relationships(self) -> None: # Check if we are in a many-many relationship, ignore importing it if it is if relationship_schema.cardinality == "many": + if relationship_schema.peer not in self.schemas_by_kind: + continue for peer_relationship in self.schemas_by_kind[relationship_schema.peer].relationships: if peer_relationship.cardinality == "many" and peer_relationship.peer == node_kind: ignore = True diff --git a/infrahub_sdk/uuidt.py b/infrahub_sdk/uuidt.py index cd3d553d..d0f6418d 100644 --- a/infrahub_sdk/uuidt.py +++ b/infrahub_sdk/uuidt.py @@ -40,7 +40,7 @@ def __init__( timestamp: Optional[int] = None, hostname: Optional[str] = None, random_chars: Optional[str] = None, - ): + ) -> None: self.namespace = namespace or DEFAULT_NAMESPACE self.timestamp = timestamp or time.time_ns() self.hostname = hostname or HOSTNAME diff --git a/pyproject.toml b/pyproject.toml index a1929ec8..e5c964d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "infrahub-sdk" -version = "0.13.1-dev0" +version = "0.14.0-dev0" description = "Python Client to interact with Infrahub" authors = ["OpsMill "] readme = "README.md" diff --git a/tests/integration/test_export_import.py b/tests/integration/test_export_import.py index d6603816..df2247b6 100644 --- a/tests/integration/test_export_import.py +++ b/tests/integration/test_export_import.py @@ -207,13 +207,14 @@ async def test_step01_export_no_schema(self, client: InfrahubClient, temporary_d assert relationships_file.exists() # Verify that only the admin account has been exported - admin_account_node_dump = ujson.loads(nodes_file.read_text()) + with nodes_file.open() as f: + admin_account_node_dump = ujson.loads(f.readline()) assert admin_account_node_dump assert admin_account_node_dump["kind"] == "CoreAccount" assert ujson.loads(admin_account_node_dump["graphql_json"])["name"]["value"] == "admin" relationships_dump = ujson.loads(relationships_file.read_text()) - assert not relationships_dump + assert relationships_dump async def test_step02_import_no_schema(self, client: InfrahubClient, temporary_directory: Path): importer = LineDelimitedJSONImporter(client=client, topological_sorter=InfrahubSchemaTopologicalSorter()) @@ -241,13 +242,14 @@ async def test_step03_export_empty_dataset(self, client: InfrahubClient, tempora assert relationships_file.exists() # Verify that only the admin account has been exported - admin_account_node_dump = ujson.loads(nodes_file.read_text()) + with nodes_file.open() as f: + admin_account_node_dump = ujson.loads(f.readline()) assert admin_account_node_dump assert admin_account_node_dump["kind"] == "CoreAccount" assert ujson.loads(admin_account_node_dump["graphql_json"])["name"]["value"] == "admin" relationships_dump = ujson.loads(relationships_file.read_text()) - assert not relationships_dump + assert relationships_dump async def test_step04_import_empty_dataset(self, client: InfrahubClient, temporary_directory: Path, schema): await client.schema.load(schemas=[schema]) @@ -280,10 +282,10 @@ async def test_step05_export_initial_dataset( with nodes_file.open() as reader: while line := reader.readline(): nodes_dump.append(ujson.loads(line)) - assert len(nodes_dump) == len(initial_dataset) + 1 + assert len(nodes_dump) == len(initial_dataset) + 5 # add number to account for default data relationships_dump = ujson.loads(relationships_file.read_text()) - assert not relationships_dump + assert relationships_dump async def test_step06_import_initial_dataset(self, client: InfrahubClient, temporary_directory: Path, schema): await client.schema.load(schemas=[schema]) @@ -366,6 +368,7 @@ def schema_car_base(self) -> Dict[str, Any]: "optional": True, "peer": "TestingPool", "cardinality": "many", + "identifier": "car__pool", }, { "name": "manufacturer", @@ -490,7 +493,7 @@ async def test_step01_export_initial_dataset( with nodes_file.open() as reader: while line := reader.readline(): nodes_dump.append(ujson.loads(line)) - assert len(nodes_dump) == len(initial_dataset) + 1 + assert len(nodes_dump) == len(initial_dataset) + 5 # add number to account for default data # Make sure there are as many relationships as there are in the database relationship_count = 0 @@ -498,7 +501,7 @@ async def test_step01_export_initial_dataset( await node.cars.fetch() relationship_count += len(node.cars.peers) relationships_dump = ujson.loads(relationships_file.read_text()) - assert len(relationships_dump) == relationship_count + assert len(relationships_dump) == relationship_count + 1 # add number to account for default data async def test_step02_import_initial_dataset(self, client: InfrahubClient, temporary_directory: Path, schema): await client.schema.load(schemas=[schema]) @@ -517,7 +520,7 @@ async def test_step02_import_initial_dataset(self, client: InfrahubClient, tempo relationship_count += len(node.cars.peers) relationships_file = temporary_directory / "relationships.json" relationships_dump = ujson.loads(relationships_file.read_text()) - assert len(relationships_dump) == relationship_count + assert len(relationships_dump) == relationship_count + 1 # add number to account for default data async def test_step03_import_initial_dataset_with_existing_data( self, client: InfrahubClient, temporary_directory: Path, initial_dataset @@ -536,7 +539,7 @@ async def test_step03_import_initial_dataset_with_existing_data( relationship_count += len(node.cars.peers) relationships_file = temporary_directory / "relationships.json" relationships_dump = ujson.loads(relationships_file.read_text()) - assert len(relationships_dump) == relationship_count + assert len(relationships_dump) == relationship_count + 1 # add number to account for default data # Cleanup for next tests self.reset_export_directory(temporary_directory) diff --git a/tests/unit/sdk/conftest.py b/tests/unit/sdk/conftest.py index a870cfdb..63cf6356 100644 --- a/tests/unit/sdk/conftest.py +++ b/tests/unit/sdk/conftest.py @@ -59,10 +59,19 @@ def replace_async_return_annotation(): def replace_annotation(annotation: str) -> str: replacements = { + "type[SchemaType]": "type[SchemaTypeSync]", + "SchemaType": "SchemaTypeSync", + "CoreNode": "CoreNodeSync", + "Optional[CoreNode]": "Optional[CoreNodeSync]", + "Union[str, type[SchemaType]]": "Union[str, type[SchemaTypeSync]]", + "Union[InfrahubNode, SchemaType]": "Union[InfrahubNodeSync, SchemaTypeSync]", + "Union[list[InfrahubNode], list[SchemaType]]": "Union[list[InfrahubNodeSync], list[SchemaTypeSync]]", "InfrahubClient": "InfrahubClientSync", "InfrahubNode": "InfrahubNodeSync", "list[InfrahubNode]": "list[InfrahubNodeSync]", "Optional[InfrahubNode]": "Optional[InfrahubNodeSync]", + "Optional[type[SchemaType]]": "Optional[type[SchemaTypeSync]]", + "Optional[Union[CoreNode, SchemaType]]": "Optional[Union[CoreNodeSync, SchemaTypeSync]]", } return replacements.get(annotation) or annotation @@ -89,10 +98,19 @@ def replace_sync_return_annotation() -> str: def replace_annotation(annotation: str) -> str: replacements = { + "type[SchemaTypeSync]": "type[SchemaType]", + "SchemaTypeSync": "SchemaType", + "CoreNodeSync": "CoreNode", + "Optional[CoreNodeSync]": "Optional[CoreNode]", + "Union[str, type[SchemaTypeSync]]": "Union[str, type[SchemaType]]", + "Union[InfrahubNodeSync, SchemaTypeSync]": "Union[InfrahubNode, SchemaType]", + "Union[list[InfrahubNodeSync], list[SchemaTypeSync]]": "Union[list[InfrahubNode], list[SchemaType]]", "InfrahubClientSync": "InfrahubClient", "InfrahubNodeSync": "InfrahubNode", "list[InfrahubNodeSync]": "list[InfrahubNode]", "Optional[InfrahubNodeSync]": "Optional[InfrahubNode]", + "Optional[type[SchemaTypeSync]]": "Optional[type[SchemaType]]", + "Optional[Union[CoreNodeSync, SchemaTypeSync]]": "Optional[Union[CoreNode, SchemaType]]", } return replacements.get(annotation) or annotation From f5408000f7344afa02b532ab5eba13084708a854 Mon Sep 17 00:00:00 2001 From: Damien Garros Date: Thu, 19 Sep 2024 20:04:45 +0200 Subject: [PATCH 2/6] Addd CoreAccountRole and CoreAccountGroup protocols --- infrahub_sdk/protocols.py | 40 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/infrahub_sdk/protocols.py b/infrahub_sdk/protocols.py index 7f2816ce..048ec982 100644 --- a/infrahub_sdk/protocols.py +++ b/infrahub_sdk/protocols.py @@ -204,6 +204,16 @@ class CoreAccount(LineageOwner, LineageSource, CoreGenericAccount): pass +class CoreAccountGroup(CoreGroup): + roles: RelationshipManager + + +class CoreAccountRole(CoreNode): + name: String + groups: RelationshipManager + permissions: RelationshipManager + + class CoreArtifact(CoreTaskTarget): name: String status: Enum @@ -445,16 +455,6 @@ class CoreTransformPython(CoreTransformation): class_name: String -class CoreUserGroup(CoreGroup): - roles: RelationshipManager - - -class CoreUserRole(CoreNode): - name: String - groups: RelationshipManager - permissions: RelationshipManager - - class CoreUserValidator(CoreValidator): check_definition: RelatedNode repository: RelatedNode @@ -649,6 +649,16 @@ class CoreAccountSync(LineageOwnerSync, LineageSourceSync, CoreGenericAccountSyn pass +class CoreAccountGroupSync(CoreGroupSync): + roles: RelationshipManagerSync + + +class CoreAccountRoleSync(CoreNodeSync): + name: String + groups: RelationshipManagerSync + permissions: RelationshipManagerSync + + class CoreArtifactSync(CoreTaskTargetSync): name: String status: Enum @@ -890,16 +900,6 @@ class CoreTransformPythonSync(CoreTransformationSync): class_name: String -class CoreUserGroupSync(CoreGroupSync): - roles: RelationshipManagerSync - - -class CoreUserRoleSync(CoreNodeSync): - name: String - groups: RelationshipManagerSync - permissions: RelationshipManagerSync - - class CoreUserValidatorSync(CoreValidatorSync): check_definition: RelatedNodeSync repository: RelatedNodeSync From 937da7fbb87b0662a9c03728912852b79703b959 Mon Sep 17 00:00:00 2001 From: Damien Garros Date: Sat, 21 Sep 2024 15:18:09 +0200 Subject: [PATCH 3/6] Add parameter raise_when_missing on client.get to return None instead of raising an exception --- changelog/11.changed.md | 7 ++ infrahub_sdk/client.py | 154 +++++++++++++++++++++++++++++++++- tests/unit/sdk/conftest.py | 2 + tests/unit/sdk/test_client.py | 14 ++++ 4 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 changelog/11.changed.md diff --git a/changelog/11.changed.md b/changelog/11.changed.md new file mode 100644 index 00000000..a72fa814 --- /dev/null +++ b/changelog/11.changed.md @@ -0,0 +1,7 @@ +Method client.get() can now return `None` instead of raising an exception when `raise_when_missing` is set to False + +```python +response = await clients.get( + kind="CoreRepository", name__value="infrahub-demo", raise_when_missing=False +) +``` \ No newline at end of file diff --git a/infrahub_sdk/client.py b/infrahub_sdk/client.py index 972c0d73..20e3457f 100644 --- a/infrahub_sdk/client.py +++ b/infrahub_sdk/client.py @@ -363,6 +363,41 @@ async def delete(self, kind: Union[str, type[SchemaType]], id: str, branch: Opti async def get( self, kind: type[SchemaType], + raise_when_missing: Literal[False], + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + id: Optional[str] = ..., + hfid: Optional[list[str]] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + populate_store: bool = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + **kwargs: Any, + ) -> Optional[SchemaType]: ... + + @overload + async def get( + self, + kind: type[SchemaType], + raise_when_missing: Literal[True], + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + id: Optional[str] = ..., + hfid: Optional[list[str]] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + populate_store: bool = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + **kwargs: Any, + ) -> SchemaType: ... + + @overload + async def get( + self, + kind: type[SchemaType], + raise_when_missing: bool = ..., at: Optional[Timestamp] = ..., branch: Optional[str] = ..., id: Optional[str] = ..., @@ -379,6 +414,41 @@ async def get( async def get( self, kind: str, + raise_when_missing: Literal[False], + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + id: Optional[str] = ..., + hfid: Optional[list[str]] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + populate_store: bool = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + **kwargs: Any, + ) -> Optional[InfrahubNode]: ... + + @overload + async def get( + self, + kind: str, + raise_when_missing: Literal[True], + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + id: Optional[str] = ..., + hfid: Optional[list[str]] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + populate_store: bool = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + **kwargs: Any, + ) -> InfrahubNode: ... + + @overload + async def get( + self, + kind: str, + raise_when_missing: bool = ..., at: Optional[Timestamp] = ..., branch: Optional[str] = ..., id: Optional[str] = ..., @@ -394,6 +464,7 @@ async def get( async def get( self, kind: Union[str, type[SchemaType]], + raise_when_missing: bool = True, at: Optional[Timestamp] = None, branch: Optional[str] = None, id: Optional[str] = None, @@ -404,7 +475,7 @@ async def get( fragment: bool = False, prefetch_relationships: bool = False, **kwargs: Any, - ) -> Union[InfrahubNode, SchemaType]: + ) -> Union[InfrahubNode, SchemaType, None]: branch = branch or self.default_branch schema = await self.schema.get(kind=kind, branch=branch) @@ -437,8 +508,10 @@ async def get( **filters, ) - if len(results) == 0: + if len(results) == 0 and raise_when_missing: raise NodeNotFoundError(branch_name=branch, node_type=schema.kind, identifier=filters) + if len(results) == 0 and not raise_when_missing: + return None if len(results) > 1: raise IndexError("More than 1 node returned") @@ -1737,6 +1810,41 @@ def filters( def get( self, kind: type[SchemaTypeSync], + raise_when_missing: Literal[False], + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + id: Optional[str] = ..., + hfid: Optional[list[str]] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + populate_store: bool = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + **kwargs: Any, + ) -> Optional[SchemaTypeSync]: ... + + @overload + def get( + self, + kind: type[SchemaTypeSync], + raise_when_missing: Literal[True], + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + id: Optional[str] = ..., + hfid: Optional[list[str]] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + populate_store: bool = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + **kwargs: Any, + ) -> SchemaTypeSync: ... + + @overload + def get( + self, + kind: type[SchemaTypeSync], + raise_when_missing: bool = ..., at: Optional[Timestamp] = ..., branch: Optional[str] = ..., id: Optional[str] = ..., @@ -1753,6 +1861,41 @@ def get( def get( self, kind: str, + raise_when_missing: Literal[False], + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + id: Optional[str] = ..., + hfid: Optional[list[str]] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + populate_store: bool = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + **kwargs: Any, + ) -> Optional[InfrahubNodeSync]: ... + + @overload + def get( + self, + kind: str, + raise_when_missing: Literal[True], + at: Optional[Timestamp] = ..., + branch: Optional[str] = ..., + id: Optional[str] = ..., + hfid: Optional[list[str]] = ..., + include: Optional[list[str]] = ..., + exclude: Optional[list[str]] = ..., + populate_store: bool = ..., + fragment: bool = ..., + prefetch_relationships: bool = ..., + **kwargs: Any, + ) -> InfrahubNodeSync: ... + + @overload + def get( + self, + kind: str, + raise_when_missing: bool = ..., at: Optional[Timestamp] = ..., branch: Optional[str] = ..., id: Optional[str] = ..., @@ -1768,6 +1911,7 @@ def get( def get( self, kind: Union[str, type[SchemaTypeSync]], + raise_when_missing: bool = True, at: Optional[Timestamp] = None, branch: Optional[str] = None, id: Optional[str] = None, @@ -1778,7 +1922,7 @@ def get( fragment: bool = False, prefetch_relationships: bool = False, **kwargs: Any, - ) -> Union[InfrahubNodeSync, SchemaTypeSync]: + ) -> Union[InfrahubNodeSync, SchemaTypeSync, None]: branch = branch or self.default_branch schema = self.schema.get(kind=kind, branch=branch) @@ -1811,8 +1955,10 @@ def get( **filters, ) - if len(results) == 0: + if len(results) == 0 and raise_when_missing: raise NodeNotFoundError(branch_name=branch, node_type=schema.kind, identifier=filters) + if len(results) == 0 and not raise_when_missing: + return None if len(results) > 1: raise IndexError("More than 1 node returned") diff --git a/tests/unit/sdk/conftest.py b/tests/unit/sdk/conftest.py index 63cf6356..5afe7ef2 100644 --- a/tests/unit/sdk/conftest.py +++ b/tests/unit/sdk/conftest.py @@ -65,6 +65,7 @@ def replace_annotation(annotation: str) -> str: "Optional[CoreNode]": "Optional[CoreNodeSync]", "Union[str, type[SchemaType]]": "Union[str, type[SchemaTypeSync]]", "Union[InfrahubNode, SchemaType]": "Union[InfrahubNodeSync, SchemaTypeSync]", + "Union[InfrahubNode, SchemaType, None]": "Union[InfrahubNodeSync, SchemaTypeSync, None]", "Union[list[InfrahubNode], list[SchemaType]]": "Union[list[InfrahubNodeSync], list[SchemaTypeSync]]", "InfrahubClient": "InfrahubClientSync", "InfrahubNode": "InfrahubNodeSync", @@ -104,6 +105,7 @@ def replace_annotation(annotation: str) -> str: "Optional[CoreNodeSync]": "Optional[CoreNode]", "Union[str, type[SchemaTypeSync]]": "Union[str, type[SchemaType]]", "Union[InfrahubNodeSync, SchemaTypeSync]": "Union[InfrahubNode, SchemaType]", + "Union[InfrahubNodeSync, SchemaTypeSync, None]": "Union[InfrahubNode, SchemaType, None]", "Union[list[InfrahubNodeSync], list[SchemaTypeSync]]": "Union[list[InfrahubNode], list[SchemaType]]", "InfrahubClientSync": "InfrahubClient", "InfrahubNodeSync": "InfrahubNode", diff --git a/tests/unit/sdk/test_client.py b/tests/unit/sdk/test_client.py index 50dc0b34..e80152d2 100644 --- a/tests/unit/sdk/test_client.py +++ b/tests/unit/sdk/test_client.py @@ -314,6 +314,20 @@ async def test_method_get_not_found(httpx_mock: HTTPXMock, clients, mock_query_r clients.sync.get(kind="CoreRepository", name__value="infrahub-demo-core") +@pytest.mark.parametrize("client_type", client_types) +async def test_method_get_not_found_none( + httpx_mock: HTTPXMock, clients, mock_query_repository_page1_empty, client_type +): # pylint: disable=unused-argument + if client_type == "standard": + response = await clients.standard.get( + kind="CoreRepository", name__value="infrahub-demo-core", raise_when_missing=False + ) + else: + response = clients.sync.get(kind="CoreRepository", name__value="infrahub-demo-core", raise_when_missing=False) + + assert response is None + + @pytest.mark.parametrize("client_type", client_types) async def test_method_get_found_many( httpx_mock: HTTPXMock, From ca53b9a0546b07dc00c03b92c45eaf0484bd52c2 Mon Sep 17 00:00:00 2001 From: Damien Garros Date: Mon, 23 Sep 2024 10:39:39 +0200 Subject: [PATCH 4/6] Remove local validation of query filters --- changelog/10.fixed.md | 1 + changelog/30.fixed.md | 1 + changelog/9.changed.md | 1 + infrahub_sdk/__init__.py | 2 -- infrahub_sdk/client.py | 6 ------ infrahub_sdk/ctl/utils.py | 3 +-- infrahub_sdk/exceptions.py | 9 --------- infrahub_sdk/node.py | 21 --------------------- infrahub_sdk/protocols_base.py | 2 -- tests/unit/sdk/test_client.py | 15 +-------------- 10 files changed, 5 insertions(+), 56 deletions(-) create mode 100644 changelog/10.fixed.md create mode 100644 changelog/30.fixed.md create mode 100644 changelog/9.changed.md diff --git a/changelog/10.fixed.md b/changelog/10.fixed.md new file mode 100644 index 00000000..6fc778c5 --- /dev/null +++ b/changelog/10.fixed.md @@ -0,0 +1 @@ +prefix and address attribute filters are now available in the Python SDK \ No newline at end of file diff --git a/changelog/30.fixed.md b/changelog/30.fixed.md new file mode 100644 index 00000000..5cbce266 --- /dev/null +++ b/changelog/30.fixed.md @@ -0,0 +1 @@ +Queries using isnull as a filter are now supported by the Python SDK \ No newline at end of file diff --git a/changelog/9.changed.md b/changelog/9.changed.md new file mode 100644 index 00000000..92417330 --- /dev/null +++ b/changelog/9.changed.md @@ -0,0 +1 @@ +Query filters are not validated locally anymore, the validation will be done on the server side instead. \ No newline at end of file diff --git a/infrahub_sdk/__init__.py b/infrahub_sdk/__init__.py index 4d44fdd2..624f7ee0 100644 --- a/infrahub_sdk/__init__.py +++ b/infrahub_sdk/__init__.py @@ -10,7 +10,6 @@ from infrahub_sdk.exceptions import ( AuthenticationError, Error, - FilterNotFoundError, GraphQLError, NodeNotFoundError, ServerNotReachableError, @@ -50,7 +49,6 @@ "InfrahubNodeSync", "InfrahubRepositoryConfig", "InfrahubSchema", - "FilterNotFoundError", "generate_uuid", "GenericSchema", "GraphQLQueryAnalyzer", diff --git a/infrahub_sdk/client.py b/infrahub_sdk/client.py index 972c0d73..ba7f883b 100644 --- a/infrahub_sdk/client.py +++ b/infrahub_sdk/client.py @@ -623,9 +623,6 @@ async def filters( node = InfrahubNode(client=self, schema=schema, branch=branch) filters = kwargs - if filters: - node.validate_filters(filters=filters) - nodes: list[InfrahubNode] = [] related_nodes: list[InfrahubNode] = [] @@ -1680,9 +1677,6 @@ def filters( node = InfrahubNodeSync(client=self, schema=schema, branch=branch) filters = kwargs - if filters: - node.validate_filters(filters=filters) - nodes: list[InfrahubNodeSync] = [] related_nodes: list[InfrahubNodeSync] = [] diff --git a/infrahub_sdk/ctl/utils.py b/infrahub_sdk/ctl/utils.py index 032a4d2b..73eb3e6d 100644 --- a/infrahub_sdk/ctl/utils.py +++ b/infrahub_sdk/ctl/utils.py @@ -18,7 +18,6 @@ from infrahub_sdk.exceptions import ( AuthenticationError, Error, - FilterNotFoundError, GraphQLError, NodeNotFoundError, SchemaNotFoundError, @@ -57,7 +56,7 @@ def handle_exception(exc: Exception, console: Console, exit_code: int): if isinstance(exc, GraphQLError): print_graphql_errors(console=console, errors=exc.errors) raise typer.Exit(code=exit_code) - if isinstance(exc, (SchemaNotFoundError, NodeNotFoundError, FilterNotFoundError)): + if isinstance(exc, (SchemaNotFoundError, NodeNotFoundError)): console.print(f"[red]Error: {str(exc)}") raise typer.Exit(code=exit_code) diff --git a/infrahub_sdk/exceptions.py b/infrahub_sdk/exceptions.py index b9628beb..1aa92e58 100644 --- a/infrahub_sdk/exceptions.py +++ b/infrahub_sdk/exceptions.py @@ -87,15 +87,6 @@ def __str__(self) -> str: """ -class FilterNotFoundError(Error): - def __init__(self, identifier: str, kind: str, message: Optional[str] = None, filters: Optional[list[str]] = None): - self.identifier = identifier - self.kind = kind - self.filters = filters or [] - self.message = message or f"{identifier!r} is not a valid filter for {self.kind!r} ({', '.join(self.filters)})." - super().__init__(self.message) - - class InfrahubCheckNotFoundError(Error): def __init__(self, name: str, message: Optional[str] = None): self.message = message or f"The requested InfrahubCheck '{name}' was not found." diff --git a/infrahub_sdk/node.py b/infrahub_sdk/node.py index 66b9bb0f..639333ea 100644 --- a/infrahub_sdk/node.py +++ b/infrahub_sdk/node.py @@ -9,7 +9,6 @@ from infrahub_sdk.exceptions import ( Error, FeatureNotSupportedError, - FilterNotFoundError, NodeNotFoundError, UninitializedError, ) @@ -987,26 +986,6 @@ def generate_query_data_init( return data - def validate_filters(self, filters: Optional[dict[str, Any]] = None) -> bool: - if not filters: - return True - - for filter_name in filters.keys(): - found = False - for filter_schema in self._schema.filters: - if filter_name == filter_schema.name: - found = True - break - if not found: - valid_filters = [entry.name for entry in self._schema.filters] - raise FilterNotFoundError( - identifier=filter_name, - kind=self._schema.kind, - filters=valid_filters, - ) - - return True - def extract(self, params: dict[str, str]) -> dict[str, Any]: """Extract some datapoints defined in a flat notation.""" result: dict[str, Any] = {} diff --git a/infrahub_sdk/protocols_base.py b/infrahub_sdk/protocols_base.py index b23b15c2..e2520d01 100644 --- a/infrahub_sdk/protocols_base.py +++ b/infrahub_sdk/protocols_base.py @@ -149,8 +149,6 @@ def is_resource_pool(self) -> bool: ... def get_raw_graphql_data(self) -> Optional[dict]: ... - def validate_filters(self, filters: Optional[dict[str, Any]] = None) -> bool: ... - def extract(self, params: dict[str, str]) -> dict[str, Any]: ... diff --git a/tests/unit/sdk/test_client.py b/tests/unit/sdk/test_client.py index 50dc0b34..edd96f3a 100644 --- a/tests/unit/sdk/test_client.py +++ b/tests/unit/sdk/test_client.py @@ -4,7 +4,7 @@ from pytest_httpx import HTTPXMock from infrahub_sdk import InfrahubClient, InfrahubClientSync -from infrahub_sdk.exceptions import FilterNotFoundError, NodeNotFoundError +from infrahub_sdk.exceptions import NodeNotFoundError from infrahub_sdk.node import InfrahubNode, InfrahubNodeSync async_client_methods = [method for method in dir(InfrahubClient) if not method.startswith("_")] @@ -329,19 +329,6 @@ async def test_method_get_found_many( clients.sync.get(kind="CoreRepository", id="bfae43e8-5ebb-456c-a946-bf64e930710a") -@pytest.mark.parametrize("client_type", client_types) -async def test_method_get_invalid_filter(httpx_mock: HTTPXMock, clients, mock_schema_query_01, client_type): # pylint: disable=unused-argument - with pytest.raises(FilterNotFoundError) as excinfo: - if client_type == "standard": - await clients.standard.get(kind="CoreRepository", name__name="infrahub-demo-core") - else: - clients.sync.get(kind="CoreRepository", name__name="infrahub-demo-core") - assert isinstance(excinfo.value.message, str) - assert "'name__name' is not a valid filter for 'CoreRepository'" in excinfo.value.message - assert "default_branch__value" in excinfo.value.message - assert "default_branch__value" in excinfo.value.filters - - @pytest.mark.parametrize("client_type", client_types) async def test_method_filters_many(httpx_mock: HTTPXMock, clients, mock_query_repository_page1_1, client_type): # pylint: disable=unused-argument if client_type == "standard": From e2f0ad6e8237964526b5ea9a4c29f183cafa2bbf Mon Sep 17 00:00:00 2001 From: Brett Lykins Date: Fri, 4 Oct 2024 11:46:07 -0400 Subject: [PATCH 5/6] v0.14.0 prep --- CHANGELOG.md | 23 +++ changelog/10.fixed.md | 1 - changelog/11.changed.md | 7 - changelog/30.fixed.md | 1 - changelog/33.removed.md | 1 - changelog/46.fixed.md | 1 - changelog/9.changed.md | 1 - poetry.lock | 313 ++++++++++++++++++++-------------------- pyproject.toml | 2 +- 9 files changed, 181 insertions(+), 169 deletions(-) delete mode 100644 changelog/10.fixed.md delete mode 100644 changelog/11.changed.md delete mode 100644 changelog/30.fixed.md delete mode 100644 changelog/33.removed.md delete mode 100644 changelog/46.fixed.md delete mode 100644 changelog/9.changed.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 6162718b..c242a18c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,29 @@ This project uses [*towncrier*](https://towncrier.readthedocs.io/) and the chang +## [0.14.0](https://github.com/opsmill/infrahub-sdk-python/tree/v0.14.0) - 2024-10-04 + +### Removed + +- Removed depreceted methods InfrahubClient.init and InfrahubClientSync.init ([#33](https://github.com/opsmill/infrahub-sdk-python/issues/33)) + +### Changed + +- Query filters are not validated locally anymore, the validation will be done on the server side instead. ([#9](https://github.com/opsmill/infrahub-sdk-python/issues/9)) +- Method client.get() can now return `None` instead of raising an exception when `raise_when_missing` is set to False + + ```python + response = await clients.get( + kind="CoreRepository", name__value="infrahub-demo", raise_when_missing=False + ) + ``` ([#11](https://github.com/opsmill/infrahub-sdk-python/issues/11)) + +### Fixed + +- prefix and address attribute filters are now available in the Python SDK ([#10](https://github.com/opsmill/infrahub-sdk-python/issues/10)) +- Queries using isnull as a filter are now supported by the Python SDK ([#30](https://github.com/opsmill/infrahub-sdk-python/issues/30)) +- `execute_graphql` method for InfrahubClient(Sync) now properly considers the `default_branch` setting ([#46](https://github.com/opsmill/infrahub-sdk-python/issues/46)) + ## [0.13.1.dev0](https://github.com/opsmill/infrahub-sdk-python/tree/v0.13.1.dev0) - 2024-09-24 ### Added diff --git a/changelog/10.fixed.md b/changelog/10.fixed.md deleted file mode 100644 index 6fc778c5..00000000 --- a/changelog/10.fixed.md +++ /dev/null @@ -1 +0,0 @@ -prefix and address attribute filters are now available in the Python SDK \ No newline at end of file diff --git a/changelog/11.changed.md b/changelog/11.changed.md deleted file mode 100644 index a72fa814..00000000 --- a/changelog/11.changed.md +++ /dev/null @@ -1,7 +0,0 @@ -Method client.get() can now return `None` instead of raising an exception when `raise_when_missing` is set to False - -```python -response = await clients.get( - kind="CoreRepository", name__value="infrahub-demo", raise_when_missing=False -) -``` \ No newline at end of file diff --git a/changelog/30.fixed.md b/changelog/30.fixed.md deleted file mode 100644 index 5cbce266..00000000 --- a/changelog/30.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Queries using isnull as a filter are now supported by the Python SDK \ No newline at end of file diff --git a/changelog/33.removed.md b/changelog/33.removed.md deleted file mode 100644 index c1b747a3..00000000 --- a/changelog/33.removed.md +++ /dev/null @@ -1 +0,0 @@ -Removed depreceted methods InfrahubClient.init and InfrahubClientSync.init diff --git a/changelog/46.fixed.md b/changelog/46.fixed.md deleted file mode 100644 index b21af46d..00000000 --- a/changelog/46.fixed.md +++ /dev/null @@ -1 +0,0 @@ -`execute_graphql` method for InfrahubClient(Sync) now properly considers the `default_branch` setting diff --git a/changelog/9.changed.md b/changelog/9.changed.md deleted file mode 100644 index 92417330..00000000 --- a/changelog/9.changed.md +++ /dev/null @@ -1 +0,0 @@ -Query filters are not validated locally anymore, the validation will be done on the server side instead. \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index fdc0609a..7ca2d765 100644 --- a/poetry.lock +++ b/poetry.lock @@ -13,13 +13,13 @@ files = [ [[package]] name = "anyio" -version = "4.4.0" +version = "4.6.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, - {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, + {file = "anyio-4.6.0-py3-none-any.whl", hash = "sha256:c7d2e9d63e31599eeb636c8c5c03a7e108d73b345f064f1c19fdc87b79036a9a"}, + {file = "anyio-4.6.0.tar.gz", hash = "sha256:137b4559cbb034c477165047febb6ff83f390fc3b20bf181c1fc0a728cb8beeb"}, ] [package.dependencies] @@ -29,19 +29,19 @@ sniffio = ">=1.1" typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} [package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (>=0.23)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.21.0b1)"] +trio = ["trio (>=0.26.1)"] [[package]] name = "astroid" -version = "3.2.4" +version = "3.3.4" description = "An abstract syntax tree for Python with inference support." optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.9.0" files = [ - {file = "astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25"}, - {file = "astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a"}, + {file = "astroid-3.3.4-py3-none-any.whl", hash = "sha256:5eba185467253501b62a9f113c263524b4f5d55e1b30456370eed4cdbd6438fd"}, + {file = "astroid-3.3.4.tar.gz", hash = "sha256:e73d0b62dd680a7c07cb2cd0ce3c22570b044dd01bd994bc3a2dd16c6cbba162"}, ] [package.dependencies] @@ -311,13 +311,13 @@ files = [ [[package]] name = "dill" -version = "0.3.8" +version = "0.3.9" description = "serialize all of Python" optional = false python-versions = ">=3.8" files = [ - {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, - {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, + {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"}, + {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"}, ] [package.extras] @@ -379,18 +379,18 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "filelock" -version = "3.16.0" +version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"}, - {file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"}, + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, ] [package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.1.1)", "pytest (>=8.3.2)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.3)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] typing = ["typing-extensions (>=4.12.2)"] [[package]] @@ -449,13 +449,13 @@ files = [ [[package]] name = "httpcore" -version = "1.0.5" +version = "1.0.6" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, - {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, + {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, + {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, ] [package.dependencies] @@ -466,7 +466,7 @@ h11 = ">=0.13,<0.15" asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<0.26.0)"] +trio = ["trio (>=0.22.0,<1.0)"] [[package]] name = "httpx" @@ -495,13 +495,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "identify" -version = "2.6.0" +version = "2.6.1" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, - {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, + {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, + {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, ] [package.extras] @@ -509,13 +509,13 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.9" +version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" files = [ - {file = "idna-3.9-py3-none-any.whl", hash = "sha256:69297d5da0cc9281c77efffb4e730254dd45943f45bbfb461de5991713989b1e"}, - {file = "idna-3.9.tar.gz", hash = "sha256:e5c5dafde284f26e9e0f28f6ea2d6400abd5ca099864a67f576f3981c6476124"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] [package.extras] @@ -1070,13 +1070,13 @@ ptyprocess = ">=0.5" [[package]] name = "platformdirs" -version = "4.3.3" +version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.3.3-py3-none-any.whl", hash = "sha256:50a5450e2e84f44539718293cbb1da0a0885c9d14adf21b77bae4e66fc99d9b5"}, - {file = "platformdirs-4.3.3.tar.gz", hash = "sha256:d4e0b7d8ec176b341fb03cb11ca12d0276faa8c485f9cd218f613840463fc2c0"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] @@ -1130,13 +1130,13 @@ virtualenv = ">=20.10.0" [[package]] name = "prompt-toolkit" -version = "3.0.47" +version = "3.0.48" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.7.0" files = [ - {file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"}, - {file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"}, + {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"}, + {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"}, ] [package.dependencies] @@ -1217,18 +1217,18 @@ numpy = ">=1.16.6" [[package]] name = "pydantic" -version = "2.9.1" +version = "2.9.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.9.1-py3-none-any.whl", hash = "sha256:7aff4db5fdf3cf573d4b3c30926a510a10e19a0774d38fc4967f78beb6deb612"}, - {file = "pydantic-2.9.1.tar.gz", hash = "sha256:1363c7d975c7036df0db2b4a61f2e062fbc0aa5ab5f2772e0ffc7191a4f4bce2"}, + {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, + {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.23.3" +pydantic-core = "2.23.4" typing-extensions = [ {version = ">=4.6.1", markers = "python_version < \"3.13\""}, {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, @@ -1240,100 +1240,100 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.23.3" +version = "2.23.4" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.23.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7f10a5d1b9281392f1bf507d16ac720e78285dfd635b05737c3911637601bae6"}, - {file = "pydantic_core-2.23.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c09a7885dd33ee8c65266e5aa7fb7e2f23d49d8043f089989726391dd7350c5"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6470b5a1ec4d1c2e9afe928c6cb37eb33381cab99292a708b8cb9aa89e62429b"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9172d2088e27d9a185ea0a6c8cebe227a9139fd90295221d7d495944d2367700"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86fc6c762ca7ac8fbbdff80d61b2c59fb6b7d144aa46e2d54d9e1b7b0e780e01"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0cb80fd5c2df4898693aa841425ea1727b1b6d2167448253077d2a49003e0ed"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03667cec5daf43ac4995cefa8aaf58f99de036204a37b889c24a80927b629cec"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:047531242f8e9c2db733599f1c612925de095e93c9cc0e599e96cf536aaf56ba"}, - {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5499798317fff7f25dbef9347f4451b91ac2a4330c6669821c8202fd354c7bee"}, - {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bbb5e45eab7624440516ee3722a3044b83fff4c0372efe183fd6ba678ff681fe"}, - {file = "pydantic_core-2.23.3-cp310-none-win32.whl", hash = "sha256:8b5b3ed73abb147704a6e9f556d8c5cb078f8c095be4588e669d315e0d11893b"}, - {file = "pydantic_core-2.23.3-cp310-none-win_amd64.whl", hash = "sha256:2b603cde285322758a0279995b5796d64b63060bfbe214b50a3ca23b5cee3e83"}, - {file = "pydantic_core-2.23.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c889fd87e1f1bbeb877c2ee56b63bb297de4636661cc9bbfcf4b34e5e925bc27"}, - {file = "pydantic_core-2.23.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea85bda3189fb27503af4c45273735bcde3dd31c1ab17d11f37b04877859ef45"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7f7f72f721223f33d3dc98a791666ebc6a91fa023ce63733709f4894a7dc611"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b2b55b0448e9da68f56b696f313949cda1039e8ec7b5d294285335b53104b61"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c24574c7e92e2c56379706b9a3f07c1e0c7f2f87a41b6ee86653100c4ce343e5"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2b05e6ccbee333a8f4b8f4d7c244fdb7a979e90977ad9c51ea31261e2085ce0"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2c409ce1c219c091e47cb03feb3c4ed8c2b8e004efc940da0166aaee8f9d6c8"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d965e8b325f443ed3196db890d85dfebbb09f7384486a77461347f4adb1fa7f8"}, - {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f56af3a420fb1ffaf43ece3ea09c2d27c444e7c40dcb7c6e7cf57aae764f2b48"}, - {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b01a078dd4f9a52494370af21aa52964e0a96d4862ac64ff7cea06e0f12d2c5"}, - {file = "pydantic_core-2.23.3-cp311-none-win32.whl", hash = "sha256:560e32f0df04ac69b3dd818f71339983f6d1f70eb99d4d1f8e9705fb6c34a5c1"}, - {file = "pydantic_core-2.23.3-cp311-none-win_amd64.whl", hash = "sha256:c744fa100fdea0d000d8bcddee95213d2de2e95b9c12be083370b2072333a0fa"}, - {file = "pydantic_core-2.23.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e0ec50663feedf64d21bad0809f5857bac1ce91deded203efc4a84b31b2e4305"}, - {file = "pydantic_core-2.23.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db6e6afcb95edbe6b357786684b71008499836e91f2a4a1e55b840955b341dbb"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ccd69edcf49f0875d86942f4418a4e83eb3047f20eb897bffa62a5d419c8fa"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a678c1ac5c5ec5685af0133262103defb427114e62eafeda12f1357a12140162"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01491d8b4d8db9f3391d93b0df60701e644ff0894352947f31fff3e52bd5c801"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fcf31facf2796a2d3b7fe338fe8640aa0166e4e55b4cb108dbfd1058049bf4cb"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7200fd561fb3be06827340da066df4311d0b6b8eb0c2116a110be5245dceb326"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc1636770a809dee2bd44dd74b89cc80eb41172bcad8af75dd0bc182c2666d4c"}, - {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:67a5def279309f2e23014b608c4150b0c2d323bd7bccd27ff07b001c12c2415c"}, - {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:748bdf985014c6dd3e1e4cc3db90f1c3ecc7246ff5a3cd4ddab20c768b2f1dab"}, - {file = "pydantic_core-2.23.3-cp312-none-win32.whl", hash = "sha256:255ec6dcb899c115f1e2a64bc9ebc24cc0e3ab097775755244f77360d1f3c06c"}, - {file = "pydantic_core-2.23.3-cp312-none-win_amd64.whl", hash = "sha256:40b8441be16c1e940abebed83cd006ddb9e3737a279e339dbd6d31578b802f7b"}, - {file = "pydantic_core-2.23.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6daaf5b1ba1369a22c8b050b643250e3e5efc6a78366d323294aee54953a4d5f"}, - {file = "pydantic_core-2.23.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d015e63b985a78a3d4ccffd3bdf22b7c20b3bbd4b8227809b3e8e75bc37f9cb2"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3fc572d9b5b5cfe13f8e8a6e26271d5d13f80173724b738557a8c7f3a8a3791"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f6bd91345b5163ee7448bee201ed7dd601ca24f43f439109b0212e296eb5b423"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc379c73fd66606628b866f661e8785088afe2adaba78e6bbe80796baf708a63"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbdce4b47592f9e296e19ac31667daed8753c8367ebb34b9a9bd89dacaa299c9"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3cf31edf405a161a0adad83246568647c54404739b614b1ff43dad2b02e6d5"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e22b477bf90db71c156f89a55bfe4d25177b81fce4aa09294d9e805eec13855"}, - {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0a0137ddf462575d9bce863c4c95bac3493ba8e22f8c28ca94634b4a1d3e2bb4"}, - {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:203171e48946c3164fe7691fc349c79241ff8f28306abd4cad5f4f75ed80bc8d"}, - {file = "pydantic_core-2.23.3-cp313-none-win32.whl", hash = "sha256:76bdab0de4acb3f119c2a4bff740e0c7dc2e6de7692774620f7452ce11ca76c8"}, - {file = "pydantic_core-2.23.3-cp313-none-win_amd64.whl", hash = "sha256:37ba321ac2a46100c578a92e9a6aa33afe9ec99ffa084424291d84e456f490c1"}, - {file = "pydantic_core-2.23.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d063c6b9fed7d992bcbebfc9133f4c24b7a7f215d6b102f3e082b1117cddb72c"}, - {file = "pydantic_core-2.23.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6cb968da9a0746a0cf521b2b5ef25fc5a0bee9b9a1a8214e0a1cfaea5be7e8a4"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edbefe079a520c5984e30e1f1f29325054b59534729c25b874a16a5048028d16"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cbaaf2ef20d282659093913da9d402108203f7cb5955020bd8d1ae5a2325d1c4"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb539d7e5dc4aac345846f290cf504d2fd3c1be26ac4e8b5e4c2b688069ff4cf"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e6f33503c5495059148cc486867e1d24ca35df5fc064686e631e314d959ad5b"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04b07490bc2f6f2717b10c3969e1b830f5720b632f8ae2f3b8b1542394c47a8e"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03795b9e8a5d7fda05f3873efc3f59105e2dcff14231680296b87b80bb327295"}, - {file = "pydantic_core-2.23.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c483dab0f14b8d3f0df0c6c18d70b21b086f74c87ab03c59250dbf6d3c89baba"}, - {file = "pydantic_core-2.23.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b2682038e255e94baf2c473dca914a7460069171ff5cdd4080be18ab8a7fd6e"}, - {file = "pydantic_core-2.23.3-cp38-none-win32.whl", hash = "sha256:f4a57db8966b3a1d1a350012839c6a0099f0898c56512dfade8a1fe5fb278710"}, - {file = "pydantic_core-2.23.3-cp38-none-win_amd64.whl", hash = "sha256:13dd45ba2561603681a2676ca56006d6dee94493f03d5cadc055d2055615c3ea"}, - {file = "pydantic_core-2.23.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:82da2f4703894134a9f000e24965df73cc103e31e8c31906cc1ee89fde72cbd8"}, - {file = "pydantic_core-2.23.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dd9be0a42de08f4b58a3cc73a123f124f65c24698b95a54c1543065baca8cf0e"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89b731f25c80830c76fdb13705c68fef6a2b6dc494402987c7ea9584fe189f5d"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6de1ec30c4bb94f3a69c9f5f2182baeda5b809f806676675e9ef6b8dc936f28"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb68b41c3fa64587412b104294b9cbb027509dc2f6958446c502638d481525ef"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c3980f2843de5184656aab58698011b42763ccba11c4a8c35936c8dd6c7068c"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94f85614f2cba13f62c3c6481716e4adeae48e1eaa7e8bac379b9d177d93947a"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:510b7fb0a86dc8f10a8bb43bd2f97beb63cffad1203071dc434dac26453955cd"}, - {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1eba2f7ce3e30ee2170410e2171867ea73dbd692433b81a93758ab2de6c64835"}, - {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b259fd8409ab84b4041b7b3f24dcc41e4696f180b775961ca8142b5b21d0e70"}, - {file = "pydantic_core-2.23.3-cp39-none-win32.whl", hash = "sha256:40d9bd259538dba2f40963286009bf7caf18b5112b19d2b55b09c14dde6db6a7"}, - {file = "pydantic_core-2.23.3-cp39-none-win_amd64.whl", hash = "sha256:5a8cd3074a98ee70173a8633ad3c10e00dcb991ecec57263aacb4095c5efb958"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f399e8657c67313476a121a6944311fab377085ca7f490648c9af97fc732732d"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6b5547d098c76e1694ba85f05b595720d7c60d342f24d5aad32c3049131fa5c4"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0dda0290a6f608504882d9f7650975b4651ff91c85673341789a476b1159f211"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b6e5da855e9c55a0c67f4db8a492bf13d8d3316a59999cfbaf98cc6e401961"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:09e926397f392059ce0afdcac920df29d9c833256354d0c55f1584b0b70cf07e"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:87cfa0ed6b8c5bd6ae8b66de941cece179281239d482f363814d2b986b79cedc"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e61328920154b6a44d98cabcb709f10e8b74276bc709c9a513a8c37a18786cc4"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce3317d155628301d649fe5e16a99528d5680af4ec7aa70b90b8dacd2d725c9b"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e89513f014c6be0d17b00a9a7c81b1c426f4eb9224b15433f3d98c1a071f8433"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4f62c1c953d7ee375df5eb2e44ad50ce2f5aff931723b398b8bc6f0ac159791a"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2718443bc671c7ac331de4eef9b673063b10af32a0bb385019ad61dcf2cc8f6c"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d90e08b2727c5d01af1b5ef4121d2f0c99fbee692c762f4d9d0409c9da6541"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b676583fc459c64146debea14ba3af54e540b61762dfc0613dc4e98c3f66eeb"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:50e4661f3337977740fdbfbae084ae5693e505ca2b3130a6d4eb0f2281dc43b8"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:68f4cf373f0de6abfe599a38307f4417c1c867ca381c03df27c873a9069cda25"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:59d52cf01854cb26c46958552a21acb10dd78a52aa34c86f284e66b209db8cab"}, - {file = "pydantic_core-2.23.3.tar.gz", hash = "sha256:3cb0f65d8b4121c1b015c60104a685feb929a29d7cf204387c7f2688c7974690"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, + {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, + {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, + {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, + {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, + {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, + {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, + {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, + {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, + {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, + {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, + {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, + {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, + {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, ] [package.dependencies] @@ -1375,17 +1375,17 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pylint" -version = "3.2.7" +version = "3.3.1" description = "python code static checker" optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.9.0" files = [ - {file = "pylint-3.2.7-py3-none-any.whl", hash = "sha256:02f4aedeac91be69fb3b4bea997ce580a4ac68ce58b89eaefeaf06749df73f4b"}, - {file = "pylint-3.2.7.tar.gz", hash = "sha256:1b7a721b575eaeaa7d39db076b6e7743c993ea44f57979127c517c6c572c803e"}, + {file = "pylint-3.3.1-py3-none-any.whl", hash = "sha256:2f846a466dd023513240bc140ad2dd73bfc080a5d85a710afdb728c420a5a2b9"}, + {file = "pylint-3.3.1.tar.gz", hash = "sha256:9f3dcc87b1203e612b78d91a896407787e708b3f189b5fa0b307712d49ff0c6e"}, ] [package.dependencies] -astroid = ">=3.2.4,<=3.3.0-dev0" +astroid = ">=3.3.4,<=3.4.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, @@ -1478,21 +1478,21 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pytest-httpx" -version = "0.30.0" +version = "0.32.0" description = "Send responses to httpx." optional = false python-versions = ">=3.9" files = [ - {file = "pytest-httpx-0.30.0.tar.gz", hash = "sha256:755b8edca87c974dd4f3605c374fda11db84631de3d163b99c0df5807023a19a"}, - {file = "pytest_httpx-0.30.0-py3-none-any.whl", hash = "sha256:6d47849691faf11d2532565d0c8e0e02b9f4ee730da31687feae315581d7520c"}, + {file = "pytest_httpx-0.32.0-py3-none-any.whl", hash = "sha256:685d93ce5e5edb5e52310b72342cdc190bebf83aab058328943dd8bd8f6ac790"}, + {file = "pytest_httpx-0.32.0.tar.gz", hash = "sha256:7807647e8254e5cff79bf2041ae272449ce915d3cf1bbecaa581c384163adb87"}, ] [package.dependencies] httpx = "==0.27.*" -pytest = ">=7,<9" +pytest = "==8.*" [package.extras] -testing = ["pytest-asyncio (==0.23.*)", "pytest-cov (==4.*)"] +testing = ["pytest-asyncio (==0.24.*)", "pytest-cov (==5.*)"] [[package]] name = "pytest-xdist" @@ -1627,18 +1627,19 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "13.8.1" +version = "13.9.2" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.8.0" files = [ - {file = "rich-13.8.1-py3-none-any.whl", hash = "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06"}, - {file = "rich-13.8.1.tar.gz", hash = "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a"}, + {file = "rich-13.9.2-py3-none-any.whl", hash = "sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1"}, + {file = "rich-13.9.2.tar.gz", hash = "sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c"}, ] [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] @@ -1746,13 +1747,13 @@ files = [ [[package]] name = "tomli" -version = "2.0.1" +version = "2.0.2" description = "A lil' TOML parser" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, + {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, + {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, ] [[package]] @@ -1832,13 +1833,13 @@ files = [ [[package]] name = "types-pyyaml" -version = "6.0.12.20240808" +version = "6.0.12.20240917" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.8" files = [ - {file = "types-PyYAML-6.0.12.20240808.tar.gz", hash = "sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af"}, - {file = "types_PyYAML-6.0.12.20240808-py3-none-any.whl", hash = "sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35"}, + {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"}, + {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"}, ] [[package]] @@ -1876,13 +1877,13 @@ files = [ [[package]] name = "tzdata" -version = "2024.1" +version = "2024.2" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ - {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, - {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, + {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, + {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, ] [[package]] @@ -1991,13 +1992,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.26.4" +version = "20.26.6" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.4-py3-none-any.whl", hash = "sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55"}, - {file = "virtualenv-20.26.4.tar.gz", hash = "sha256:c17f4e0f3e6036e9f26700446f85c76ab11df65ff6d8a9cbfad9f71aabfcf23c"}, + {file = "virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2"}, + {file = "virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index b114f5db..2dc2a983 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "infrahub-sdk" -version = "0.14.0-dev0" +version = "0.14.0" description = "Python Client to interact with Infrahub" authors = ["OpsMill "] readme = "README.md" From d1f06e83a28527ab05e7de8d6e45310375555ea4 Mon Sep 17 00:00:00 2001 From: Brett Lykins Date: Fri, 4 Oct 2024 12:14:46 -0400 Subject: [PATCH 6/6] undo poetry locking/upgrading --- poetry.lock | 313 ++++++++++++++++++++++++++-------------------------- 1 file changed, 156 insertions(+), 157 deletions(-) diff --git a/poetry.lock b/poetry.lock index 7ca2d765..fdc0609a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -13,13 +13,13 @@ files = [ [[package]] name = "anyio" -version = "4.6.0" +version = "4.4.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false -python-versions = ">=3.9" +python-versions = ">=3.8" files = [ - {file = "anyio-4.6.0-py3-none-any.whl", hash = "sha256:c7d2e9d63e31599eeb636c8c5c03a7e108d73b345f064f1c19fdc87b79036a9a"}, - {file = "anyio-4.6.0.tar.gz", hash = "sha256:137b4559cbb034c477165047febb6ff83f390fc3b20bf181c1fc0a728cb8beeb"}, + {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, + {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, ] [package.dependencies] @@ -29,19 +29,19 @@ sniffio = ">=1.1" typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} [package.extras] -doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.21.0b1)"] -trio = ["trio (>=0.26.1)"] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] [[package]] name = "astroid" -version = "3.3.4" +version = "3.2.4" description = "An abstract syntax tree for Python with inference support." optional = false -python-versions = ">=3.9.0" +python-versions = ">=3.8.0" files = [ - {file = "astroid-3.3.4-py3-none-any.whl", hash = "sha256:5eba185467253501b62a9f113c263524b4f5d55e1b30456370eed4cdbd6438fd"}, - {file = "astroid-3.3.4.tar.gz", hash = "sha256:e73d0b62dd680a7c07cb2cd0ce3c22570b044dd01bd994bc3a2dd16c6cbba162"}, + {file = "astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25"}, + {file = "astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a"}, ] [package.dependencies] @@ -311,13 +311,13 @@ files = [ [[package]] name = "dill" -version = "0.3.9" +version = "0.3.8" description = "serialize all of Python" optional = false python-versions = ">=3.8" files = [ - {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"}, - {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"}, + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, ] [package.extras] @@ -379,18 +379,18 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "filelock" -version = "3.16.1" +version = "3.16.0" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, - {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, + {file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"}, + {file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"}, ] [package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.1.1)", "pytest (>=8.3.2)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.3)"] typing = ["typing-extensions (>=4.12.2)"] [[package]] @@ -449,13 +449,13 @@ files = [ [[package]] name = "httpcore" -version = "1.0.6" +version = "1.0.5" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, - {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, + {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, + {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, ] [package.dependencies] @@ -466,7 +466,7 @@ h11 = ">=0.13,<0.15" asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<1.0)"] +trio = ["trio (>=0.22.0,<0.26.0)"] [[package]] name = "httpx" @@ -495,13 +495,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "identify" -version = "2.6.1" +version = "2.6.0" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, - {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, + {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, + {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, ] [package.extras] @@ -509,13 +509,13 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.10" +version = "3.9" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, + {file = "idna-3.9-py3-none-any.whl", hash = "sha256:69297d5da0cc9281c77efffb4e730254dd45943f45bbfb461de5991713989b1e"}, + {file = "idna-3.9.tar.gz", hash = "sha256:e5c5dafde284f26e9e0f28f6ea2d6400abd5ca099864a67f576f3981c6476124"}, ] [package.extras] @@ -1070,13 +1070,13 @@ ptyprocess = ">=0.5" [[package]] name = "platformdirs" -version = "4.3.6" +version = "4.3.3" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, - {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, + {file = "platformdirs-4.3.3-py3-none-any.whl", hash = "sha256:50a5450e2e84f44539718293cbb1da0a0885c9d14adf21b77bae4e66fc99d9b5"}, + {file = "platformdirs-4.3.3.tar.gz", hash = "sha256:d4e0b7d8ec176b341fb03cb11ca12d0276faa8c485f9cd218f613840463fc2c0"}, ] [package.extras] @@ -1130,13 +1130,13 @@ virtualenv = ">=20.10.0" [[package]] name = "prompt-toolkit" -version = "3.0.48" +version = "3.0.47" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.7.0" files = [ - {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"}, - {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"}, + {file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"}, + {file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"}, ] [package.dependencies] @@ -1217,18 +1217,18 @@ numpy = ">=1.16.6" [[package]] name = "pydantic" -version = "2.9.2" +version = "2.9.1" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, - {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, + {file = "pydantic-2.9.1-py3-none-any.whl", hash = "sha256:7aff4db5fdf3cf573d4b3c30926a510a10e19a0774d38fc4967f78beb6deb612"}, + {file = "pydantic-2.9.1.tar.gz", hash = "sha256:1363c7d975c7036df0db2b4a61f2e062fbc0aa5ab5f2772e0ffc7191a4f4bce2"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.23.4" +pydantic-core = "2.23.3" typing-extensions = [ {version = ">=4.6.1", markers = "python_version < \"3.13\""}, {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, @@ -1240,100 +1240,100 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.23.4" +version = "2.23.3" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, - {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, - {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, - {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, - {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, - {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, - {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, - {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, - {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, - {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, - {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, - {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, - {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, - {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, + {file = "pydantic_core-2.23.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7f10a5d1b9281392f1bf507d16ac720e78285dfd635b05737c3911637601bae6"}, + {file = "pydantic_core-2.23.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c09a7885dd33ee8c65266e5aa7fb7e2f23d49d8043f089989726391dd7350c5"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6470b5a1ec4d1c2e9afe928c6cb37eb33381cab99292a708b8cb9aa89e62429b"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9172d2088e27d9a185ea0a6c8cebe227a9139fd90295221d7d495944d2367700"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86fc6c762ca7ac8fbbdff80d61b2c59fb6b7d144aa46e2d54d9e1b7b0e780e01"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0cb80fd5c2df4898693aa841425ea1727b1b6d2167448253077d2a49003e0ed"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03667cec5daf43ac4995cefa8aaf58f99de036204a37b889c24a80927b629cec"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:047531242f8e9c2db733599f1c612925de095e93c9cc0e599e96cf536aaf56ba"}, + {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5499798317fff7f25dbef9347f4451b91ac2a4330c6669821c8202fd354c7bee"}, + {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bbb5e45eab7624440516ee3722a3044b83fff4c0372efe183fd6ba678ff681fe"}, + {file = "pydantic_core-2.23.3-cp310-none-win32.whl", hash = "sha256:8b5b3ed73abb147704a6e9f556d8c5cb078f8c095be4588e669d315e0d11893b"}, + {file = "pydantic_core-2.23.3-cp310-none-win_amd64.whl", hash = "sha256:2b603cde285322758a0279995b5796d64b63060bfbe214b50a3ca23b5cee3e83"}, + {file = "pydantic_core-2.23.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c889fd87e1f1bbeb877c2ee56b63bb297de4636661cc9bbfcf4b34e5e925bc27"}, + {file = "pydantic_core-2.23.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea85bda3189fb27503af4c45273735bcde3dd31c1ab17d11f37b04877859ef45"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7f7f72f721223f33d3dc98a791666ebc6a91fa023ce63733709f4894a7dc611"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b2b55b0448e9da68f56b696f313949cda1039e8ec7b5d294285335b53104b61"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c24574c7e92e2c56379706b9a3f07c1e0c7f2f87a41b6ee86653100c4ce343e5"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2b05e6ccbee333a8f4b8f4d7c244fdb7a979e90977ad9c51ea31261e2085ce0"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2c409ce1c219c091e47cb03feb3c4ed8c2b8e004efc940da0166aaee8f9d6c8"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d965e8b325f443ed3196db890d85dfebbb09f7384486a77461347f4adb1fa7f8"}, + {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f56af3a420fb1ffaf43ece3ea09c2d27c444e7c40dcb7c6e7cf57aae764f2b48"}, + {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b01a078dd4f9a52494370af21aa52964e0a96d4862ac64ff7cea06e0f12d2c5"}, + {file = "pydantic_core-2.23.3-cp311-none-win32.whl", hash = "sha256:560e32f0df04ac69b3dd818f71339983f6d1f70eb99d4d1f8e9705fb6c34a5c1"}, + {file = "pydantic_core-2.23.3-cp311-none-win_amd64.whl", hash = "sha256:c744fa100fdea0d000d8bcddee95213d2de2e95b9c12be083370b2072333a0fa"}, + {file = "pydantic_core-2.23.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e0ec50663feedf64d21bad0809f5857bac1ce91deded203efc4a84b31b2e4305"}, + {file = "pydantic_core-2.23.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db6e6afcb95edbe6b357786684b71008499836e91f2a4a1e55b840955b341dbb"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ccd69edcf49f0875d86942f4418a4e83eb3047f20eb897bffa62a5d419c8fa"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a678c1ac5c5ec5685af0133262103defb427114e62eafeda12f1357a12140162"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01491d8b4d8db9f3391d93b0df60701e644ff0894352947f31fff3e52bd5c801"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fcf31facf2796a2d3b7fe338fe8640aa0166e4e55b4cb108dbfd1058049bf4cb"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7200fd561fb3be06827340da066df4311d0b6b8eb0c2116a110be5245dceb326"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc1636770a809dee2bd44dd74b89cc80eb41172bcad8af75dd0bc182c2666d4c"}, + {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:67a5def279309f2e23014b608c4150b0c2d323bd7bccd27ff07b001c12c2415c"}, + {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:748bdf985014c6dd3e1e4cc3db90f1c3ecc7246ff5a3cd4ddab20c768b2f1dab"}, + {file = "pydantic_core-2.23.3-cp312-none-win32.whl", hash = "sha256:255ec6dcb899c115f1e2a64bc9ebc24cc0e3ab097775755244f77360d1f3c06c"}, + {file = "pydantic_core-2.23.3-cp312-none-win_amd64.whl", hash = "sha256:40b8441be16c1e940abebed83cd006ddb9e3737a279e339dbd6d31578b802f7b"}, + {file = "pydantic_core-2.23.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6daaf5b1ba1369a22c8b050b643250e3e5efc6a78366d323294aee54953a4d5f"}, + {file = "pydantic_core-2.23.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d015e63b985a78a3d4ccffd3bdf22b7c20b3bbd4b8227809b3e8e75bc37f9cb2"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3fc572d9b5b5cfe13f8e8a6e26271d5d13f80173724b738557a8c7f3a8a3791"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f6bd91345b5163ee7448bee201ed7dd601ca24f43f439109b0212e296eb5b423"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc379c73fd66606628b866f661e8785088afe2adaba78e6bbe80796baf708a63"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbdce4b47592f9e296e19ac31667daed8753c8367ebb34b9a9bd89dacaa299c9"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3cf31edf405a161a0adad83246568647c54404739b614b1ff43dad2b02e6d5"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e22b477bf90db71c156f89a55bfe4d25177b81fce4aa09294d9e805eec13855"}, + {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0a0137ddf462575d9bce863c4c95bac3493ba8e22f8c28ca94634b4a1d3e2bb4"}, + {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:203171e48946c3164fe7691fc349c79241ff8f28306abd4cad5f4f75ed80bc8d"}, + {file = "pydantic_core-2.23.3-cp313-none-win32.whl", hash = "sha256:76bdab0de4acb3f119c2a4bff740e0c7dc2e6de7692774620f7452ce11ca76c8"}, + {file = "pydantic_core-2.23.3-cp313-none-win_amd64.whl", hash = "sha256:37ba321ac2a46100c578a92e9a6aa33afe9ec99ffa084424291d84e456f490c1"}, + {file = "pydantic_core-2.23.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d063c6b9fed7d992bcbebfc9133f4c24b7a7f215d6b102f3e082b1117cddb72c"}, + {file = "pydantic_core-2.23.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6cb968da9a0746a0cf521b2b5ef25fc5a0bee9b9a1a8214e0a1cfaea5be7e8a4"}, + {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edbefe079a520c5984e30e1f1f29325054b59534729c25b874a16a5048028d16"}, + {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cbaaf2ef20d282659093913da9d402108203f7cb5955020bd8d1ae5a2325d1c4"}, + {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb539d7e5dc4aac345846f290cf504d2fd3c1be26ac4e8b5e4c2b688069ff4cf"}, + {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e6f33503c5495059148cc486867e1d24ca35df5fc064686e631e314d959ad5b"}, + {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04b07490bc2f6f2717b10c3969e1b830f5720b632f8ae2f3b8b1542394c47a8e"}, + {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03795b9e8a5d7fda05f3873efc3f59105e2dcff14231680296b87b80bb327295"}, + {file = "pydantic_core-2.23.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c483dab0f14b8d3f0df0c6c18d70b21b086f74c87ab03c59250dbf6d3c89baba"}, + {file = "pydantic_core-2.23.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b2682038e255e94baf2c473dca914a7460069171ff5cdd4080be18ab8a7fd6e"}, + {file = "pydantic_core-2.23.3-cp38-none-win32.whl", hash = "sha256:f4a57db8966b3a1d1a350012839c6a0099f0898c56512dfade8a1fe5fb278710"}, + {file = "pydantic_core-2.23.3-cp38-none-win_amd64.whl", hash = "sha256:13dd45ba2561603681a2676ca56006d6dee94493f03d5cadc055d2055615c3ea"}, + {file = "pydantic_core-2.23.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:82da2f4703894134a9f000e24965df73cc103e31e8c31906cc1ee89fde72cbd8"}, + {file = "pydantic_core-2.23.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dd9be0a42de08f4b58a3cc73a123f124f65c24698b95a54c1543065baca8cf0e"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89b731f25c80830c76fdb13705c68fef6a2b6dc494402987c7ea9584fe189f5d"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6de1ec30c4bb94f3a69c9f5f2182baeda5b809f806676675e9ef6b8dc936f28"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb68b41c3fa64587412b104294b9cbb027509dc2f6958446c502638d481525ef"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c3980f2843de5184656aab58698011b42763ccba11c4a8c35936c8dd6c7068c"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94f85614f2cba13f62c3c6481716e4adeae48e1eaa7e8bac379b9d177d93947a"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:510b7fb0a86dc8f10a8bb43bd2f97beb63cffad1203071dc434dac26453955cd"}, + {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1eba2f7ce3e30ee2170410e2171867ea73dbd692433b81a93758ab2de6c64835"}, + {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b259fd8409ab84b4041b7b3f24dcc41e4696f180b775961ca8142b5b21d0e70"}, + {file = "pydantic_core-2.23.3-cp39-none-win32.whl", hash = "sha256:40d9bd259538dba2f40963286009bf7caf18b5112b19d2b55b09c14dde6db6a7"}, + {file = "pydantic_core-2.23.3-cp39-none-win_amd64.whl", hash = "sha256:5a8cd3074a98ee70173a8633ad3c10e00dcb991ecec57263aacb4095c5efb958"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f399e8657c67313476a121a6944311fab377085ca7f490648c9af97fc732732d"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6b5547d098c76e1694ba85f05b595720d7c60d342f24d5aad32c3049131fa5c4"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0dda0290a6f608504882d9f7650975b4651ff91c85673341789a476b1159f211"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b6e5da855e9c55a0c67f4db8a492bf13d8d3316a59999cfbaf98cc6e401961"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:09e926397f392059ce0afdcac920df29d9c833256354d0c55f1584b0b70cf07e"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:87cfa0ed6b8c5bd6ae8b66de941cece179281239d482f363814d2b986b79cedc"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e61328920154b6a44d98cabcb709f10e8b74276bc709c9a513a8c37a18786cc4"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce3317d155628301d649fe5e16a99528d5680af4ec7aa70b90b8dacd2d725c9b"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e89513f014c6be0d17b00a9a7c81b1c426f4eb9224b15433f3d98c1a071f8433"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4f62c1c953d7ee375df5eb2e44ad50ce2f5aff931723b398b8bc6f0ac159791a"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2718443bc671c7ac331de4eef9b673063b10af32a0bb385019ad61dcf2cc8f6c"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d90e08b2727c5d01af1b5ef4121d2f0c99fbee692c762f4d9d0409c9da6541"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b676583fc459c64146debea14ba3af54e540b61762dfc0613dc4e98c3f66eeb"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:50e4661f3337977740fdbfbae084ae5693e505ca2b3130a6d4eb0f2281dc43b8"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:68f4cf373f0de6abfe599a38307f4417c1c867ca381c03df27c873a9069cda25"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:59d52cf01854cb26c46958552a21acb10dd78a52aa34c86f284e66b209db8cab"}, + {file = "pydantic_core-2.23.3.tar.gz", hash = "sha256:3cb0f65d8b4121c1b015c60104a685feb929a29d7cf204387c7f2688c7974690"}, ] [package.dependencies] @@ -1375,17 +1375,17 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pylint" -version = "3.3.1" +version = "3.2.7" description = "python code static checker" optional = false -python-versions = ">=3.9.0" +python-versions = ">=3.8.0" files = [ - {file = "pylint-3.3.1-py3-none-any.whl", hash = "sha256:2f846a466dd023513240bc140ad2dd73bfc080a5d85a710afdb728c420a5a2b9"}, - {file = "pylint-3.3.1.tar.gz", hash = "sha256:9f3dcc87b1203e612b78d91a896407787e708b3f189b5fa0b307712d49ff0c6e"}, + {file = "pylint-3.2.7-py3-none-any.whl", hash = "sha256:02f4aedeac91be69fb3b4bea997ce580a4ac68ce58b89eaefeaf06749df73f4b"}, + {file = "pylint-3.2.7.tar.gz", hash = "sha256:1b7a721b575eaeaa7d39db076b6e7743c993ea44f57979127c517c6c572c803e"}, ] [package.dependencies] -astroid = ">=3.3.4,<=3.4.0-dev0" +astroid = ">=3.2.4,<=3.3.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, @@ -1478,21 +1478,21 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pytest-httpx" -version = "0.32.0" +version = "0.30.0" description = "Send responses to httpx." optional = false python-versions = ">=3.9" files = [ - {file = "pytest_httpx-0.32.0-py3-none-any.whl", hash = "sha256:685d93ce5e5edb5e52310b72342cdc190bebf83aab058328943dd8bd8f6ac790"}, - {file = "pytest_httpx-0.32.0.tar.gz", hash = "sha256:7807647e8254e5cff79bf2041ae272449ce915d3cf1bbecaa581c384163adb87"}, + {file = "pytest-httpx-0.30.0.tar.gz", hash = "sha256:755b8edca87c974dd4f3605c374fda11db84631de3d163b99c0df5807023a19a"}, + {file = "pytest_httpx-0.30.0-py3-none-any.whl", hash = "sha256:6d47849691faf11d2532565d0c8e0e02b9f4ee730da31687feae315581d7520c"}, ] [package.dependencies] httpx = "==0.27.*" -pytest = "==8.*" +pytest = ">=7,<9" [package.extras] -testing = ["pytest-asyncio (==0.24.*)", "pytest-cov (==5.*)"] +testing = ["pytest-asyncio (==0.23.*)", "pytest-cov (==4.*)"] [[package]] name = "pytest-xdist" @@ -1627,19 +1627,18 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "13.9.2" +version = "13.8.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.7.0" files = [ - {file = "rich-13.9.2-py3-none-any.whl", hash = "sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1"}, - {file = "rich-13.9.2.tar.gz", hash = "sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c"}, + {file = "rich-13.8.1-py3-none-any.whl", hash = "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06"}, + {file = "rich-13.8.1.tar.gz", hash = "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a"}, ] [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] @@ -1747,13 +1746,13 @@ files = [ [[package]] name = "tomli" -version = "2.0.2" +version = "2.0.1" description = "A lil' TOML parser" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] @@ -1833,13 +1832,13 @@ files = [ [[package]] name = "types-pyyaml" -version = "6.0.12.20240917" +version = "6.0.12.20240808" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.8" files = [ - {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"}, - {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"}, + {file = "types-PyYAML-6.0.12.20240808.tar.gz", hash = "sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af"}, + {file = "types_PyYAML-6.0.12.20240808-py3-none-any.whl", hash = "sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35"}, ] [[package]] @@ -1877,13 +1876,13 @@ files = [ [[package]] name = "tzdata" -version = "2024.2" +version = "2024.1" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ - {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, - {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] [[package]] @@ -1992,13 +1991,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.26.6" +version = "20.26.4" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2"}, - {file = "virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48"}, + {file = "virtualenv-20.26.4-py3-none-any.whl", hash = "sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55"}, + {file = "virtualenv-20.26.4.tar.gz", hash = "sha256:c17f4e0f3e6036e9f26700446f85c76ab11df65ff6d8a9cbfad9f71aabfcf23c"}, ] [package.dependencies]