From 75742fdbe78ecd0a4f7e060396c31e52b6b15a94 Mon Sep 17 00:00:00 2001 From: sevaho Date: Wed, 26 Feb 2025 21:38:24 +0100 Subject: [PATCH 01/24] Add a compat file as in fastapi to support both pydantic v1 and v2 --- natsapi/_compat.py | 248 +++++++++++++++++++++++++++++++++ natsapi/asyncapi/constants.py | 1 + natsapi/asyncapi/models.py | 136 +++++++++--------- natsapi/asyncapi/utils.py | 60 ++++++-- natsapi/client/config.py | 4 +- natsapi/encoders.py | 10 +- natsapi/utils.py | 60 ++++++-- tests/test_jsonable_encoder.py | 5 +- 8 files changed, 422 insertions(+), 102 deletions(-) create mode 100644 natsapi/_compat.py diff --git a/natsapi/_compat.py b/natsapi/_compat.py new file mode 100644 index 0000000..00d165f --- /dev/null +++ b/natsapi/_compat.py @@ -0,0 +1,248 @@ +from collections import deque +from typing_extensions import Annotated, Literal, get_args, get_origin +from copy import copy +from dataclasses import dataclass, is_dataclass +from natsapi.asyncapi.constants import REF_PREFIX, REF_TEMPLATE +from enum import Enum +from functools import lru_cache +from typing import ( + Any, + Optional, + Callable, + Literal, + Deque, + Dict, + FrozenSet, + List, + Mapping, + Sequence, + Set, + Tuple, + Type, + Union, +) +from pydantic import BaseModel, create_model +from pydantic.version import VERSION as PYDANTIC_VERSION + +PYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(".")[:2]) +PYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2 + +ModelNameMap = Dict[Union[Type[BaseModel], Type[Enum]], str] + +if PYDANTIC_V2: + from pydantic.fields import FieldInfo + from pydantic import ValidationError as ValidationError + from pydantic.deprecated.json import ENCODERS_BY_TYPE + from pydantic_settings import BaseSettings + from pydantic import RootModel # noqa + from pydantic import TypeAdapter + + from pydantic import BaseConfig + + from pydantic_core import PydanticUndefined, PydanticUndefinedType + from pydantic._internal._utils import lenient_issubclass as lenient_issubclass + + from pydantic.json_schema import GenerateJsonSchema as GenerateJsonSchema + from pydantic.json_schema import JsonSchemaValue as JsonSchemaValue + + Undefined = PydanticUndefined + UndefinedType = PydanticUndefinedType + + @dataclass + class ModelField: + field_info: FieldInfo + name: str + mode: Literal["validation", "serialization"] = "validation" + # required: bool + sub_fields: Optional[str] = None + # type_: Any + # class_validators: Optional[Dict[str, Any]] = None + # model_config: Type[BaseConfig] = BaseConfig + + @property + def alias(self) -> str: + a = self.field_info.alias + return a if a is not None else self.name + + @property + def required(self) -> bool: + return self.field_info.is_required() + + @property + def default(self) -> Any: + return self.get_default() + + @property + def type_(self) -> Any: + return self.field_info.annotation + + def __post_init__(self) -> None: + self._type_adapter: TypeAdapter[Any] = TypeAdapter(Annotated[self.field_info.annotation, self.field_info]) + + def get_default(self) -> Any: + if self.field_info.is_required(): + return Undefined + return self.field_info.get_default(call_default_factory=True) + + def validate( + self, + value: Any, + values: Dict[str, Any] = {}, # noqa: B006 + *, + loc: Tuple[Union[int, str], ...] = (), + ) -> Tuple[Any, Union[List[Dict[str, Any]], None]]: + try: + return ( + self._type_adapter.validate_python(value, from_attributes=True), + None, + ) + except ValidationError as exc: + return None, _regenerate_error_with_loc(errors=exc.errors(include_url=False), loc_prefix=loc) + + def serialize( + self, + value: Any, + *, + mode: Literal["json", "python"] = "json", + by_alias: bool = True, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + ) -> Any: + # What calls this code passes a value that already called + # self._type_adapter.validate_python(value) + return self._type_adapter.dump_python( + value, + mode=mode, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + def __hash__(self) -> int: + # Each ModelField is unique for our purposes, to allow making a dict from + # ModelField to its JSON Schema. + return id(self) + + def _normalize_errors(errors: Sequence[Any]) -> List[Dict[str, Any]]: + return errors # type: ignore[return-value] + + def get_model_fields(model: Type[BaseModel]) -> List[ModelField]: + return [ModelField(field_info=field_info, name=name) for name, field_info in model.model_fields.items()] + + def get_compat_model_name_map(fields: List[ModelField]): + return {} + + def get_definitions( + *, + fields: List[ModelField], + schema_generator: GenerateJsonSchema, + model_name_map: ModelNameMap, + separate_input_output_schemas: bool = True, + ) -> Tuple[ + Dict[Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue], + Dict[str, Dict[str, Any]], + ]: + override_mode: Union[Literal["validation"], None] = None if separate_input_output_schemas else "validation" + inputs = [(field, override_mode or field.mode, field._type_adapter.core_schema) for field in fields] + field_mapping, definitions = schema_generator.generate_definitions(inputs=inputs) + return field_mapping, definitions # type: ignore[return-value] + +else: + from pydantic.json import ENCODERS_BY_TYPE # noqa: F401 + from pydantic import BaseSettings # noqa + from pydantic import BaseModel + + from pydantic.fields import ( # type: ignore[no-redef,attr-defined] + ModelField as ModelField, # noqa: F401 + ) + from pydantic.error_wrappers import ( # type: ignore[no-redef] + ErrorWrapper as ErrorWrapper, # noqa: F401 + ) + + from pydantic.utils import ( # type: ignore[no-redef] + lenient_issubclass as lenient_issubclass, # noqa: F401 + ) + + from pydantic.schema import ( + field_schema, + get_flat_models_from_fields, + get_model_name_map, + model_process_schema, + ) + + class RootModel(BaseModel): + __root__: str + + GetJsonSchemaHandler = Any # type: ignore[assignment,misc] + JsonSchemaValue = Dict[str, Any] # type: ignore[misc] + CoreSchema = Any # type: ignore[assignment,misc] + + @dataclass + class GenerateJsonSchema: # type: ignore[no-redef] + ref_template: str + + def _normalize_errors(errors: Sequence[Any]) -> List[Dict[str, Any]]: + use_errors: List[Any] = [] + for error in errors: + if isinstance(error, ErrorWrapper): + new_errors = ValidationError(errors=[error]).errors() # type: ignore[call-arg] + use_errors.extend(new_errors) + elif isinstance(error, list): + use_errors.extend(_normalize_errors(error)) + else: + use_errors.append(error) + return use_errors + + def get_model_fields(model: Type[BaseModel]) -> List[ModelField]: + return list(model.__fields__.values()) # type: ignore[attr-defined] + + def get_compat_model_name_map(fields: List[ModelField]): + models = get_flat_models_from_fields(fields, known_models=set()) + return get_model_name_map(models) # type: ignore[no-any-return] + + def get_model_definitions( + *, + flat_models: Set[Union[Type[BaseModel], Type[Enum]]], + model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str], + ) -> Dict[str, Any]: + definitions: Dict[str, Dict[str, Any]] = {} + for model in flat_models: + m_schema, m_definitions, m_nested_models = model_process_schema( + model, model_name_map=model_name_map, ref_prefix=REF_PREFIX + ) + definitions.update(m_definitions) + model_name = model_name_map[model] + if "description" in m_schema: + m_schema["description"] = m_schema["description"].split("\f")[0] + definitions[model_name] = m_schema + return definitions + + def get_definitions( + *, + fields: List[ModelField], + schema_generator: GenerateJsonSchema, + model_name_map: ModelNameMap, + separate_input_output_schemas: bool = True, + ) -> Tuple[ + Dict[Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue], + Dict[str, Dict[str, Any]], + ]: + models = get_flat_models_from_fields(fields, known_models=set()) + return {}, get_model_definitions(flat_models=models, model_name_map=model_name_map) + + +def _regenerate_error_with_loc( + *, errors: Sequence[Any], loc_prefix: Tuple[Union[str, int], ...] +) -> List[Dict[str, Any]]: + updated_loc_errors: List[Any] = [ + {**err, "loc": loc_prefix + err.get("loc", ())} for err in _normalize_errors(errors) + ] + + return updated_loc_errors + + +@lru_cache +def get_cached_model_fields(model: Type[BaseModel]) -> List[ModelField]: + return get_model_fields(model) diff --git a/natsapi/asyncapi/constants.py b/natsapi/asyncapi/constants.py index e4b7b47..c8504f2 100644 --- a/natsapi/asyncapi/constants.py +++ b/natsapi/asyncapi/constants.py @@ -1 +1,2 @@ REF_PREFIX = "#/components/schemas/" +REF_TEMPLATE = "#/components/schemas/{model}" diff --git a/natsapi/asyncapi/models.py b/natsapi/asyncapi/models.py index e14c95b..1f21529 100644 --- a/natsapi/asyncapi/models.py +++ b/natsapi/asyncapi/models.py @@ -77,10 +77,10 @@ class License(BaseModel): class Info(BaseModel): title: str version: str - description: Optional[str] - termsOfService: Optional[str] - contact: Optional[Contact] - licence: Optional[License] + description: Optional[str] = None + termsOfService: Optional[str] = None + contact: Optional[Contact] = None + licence: Optional[License] = None class Reference(BaseModel): @@ -89,8 +89,8 @@ class Reference(BaseModel): class Tag(BaseModel): name: str - description: Optional[str] - externalDocs: Optional[ExternalDocumentation] + description: Optional[str] = None + externalDocs: Optional[ExternalDocumentation] = None # TODO: Add nats bindings @@ -124,7 +124,7 @@ class CorrelationId(BaseModel): class MessageTrait(BaseModel): - headers: Optional[Union[Schema, Reference]] + headers: Optional[Union[Schema, Reference]] correlationId: Optional[Union[CorrelationId, Reference]] schemaFormat: Optional[str] contentType: Optional[str] @@ -139,40 +139,40 @@ class MessageTrait(BaseModel): class Message(BaseModel): - headers: Optional[Union[Schema, Reference]] - payload: Optional[Any] - correlationId: Optional[Union[CorrelationId, Reference]] - schemaFormat: Optional[str] - contentType: Optional[str] - name: Optional[str] - title: Optional[str] - summary: Optional[str] - description: Optional[str] - tags: Optional[List[Tag]] - externalDocs: Optional[ExternalDocumentation] - bindings: Optional[Union[MessageBindings, Reference]] - examples: Optional[Dict[str, Any]] - traits: Optional[Union[MessageTrait, Reference]] + headers: Optional[Union[Schema, Reference]] = None + payload: Optional[Any] = None + correlationId: Optional[Union[CorrelationId, Reference]] = None + schemaFormat: Optional[str] = None + contentType: Optional[str] = None + name: Optional[str] = None + title: Optional[str] = None + summary: Optional[str] = None + description: Optional[str] = None + tags: Optional[List[Tag]] = None + externalDocs: Optional[ExternalDocumentation] = None + bindings: Optional[Union[MessageBindings, Reference]] = None + examples: Optional[Dict[str, Any]] = None + traits: Optional[Union[MessageTrait, Reference]] = None class OperationTrait(BaseModel): - operationId: Optional[str] - summary: Optional[str] - description: Optional[str] - tags: Optional[List[Tag]] - externalDocs: Optional[ExternalDocumentation] - bindings: Optional[Union[OperationBindings, Reference]] + operationId: Optional[str] = None + summary: Optional[str] = None + description: Optional[str] = None + tags: Optional[List[Tag]] = None + externalDocs: Optional[ExternalDocumentation] = None + bindings: Optional[Union[OperationBindings, Reference]] = None class Operation(BaseModel): - operationId: Optional[str] - summary: Optional[str] - description: Optional[str] - tags: Optional[List[Tag]] - externalDocs: Optional[ExternalDocumentation] - bindings: Optional[Union[OperationBindings, Reference]] - traits: Optional[List[Union[OperationTrait, Reference]]] - message: Optional[Union[Message, Reference]] + operationId: Optional[str] = None + summary: Optional[str] = None + description: Optional[str] = None + tags: Optional[List[Tag]] = None + externalDocs: Optional[ExternalDocumentation] = None + bindings: Optional[Union[OperationBindings, Reference]] = None + traits: Optional[List[Union[OperationTrait, Reference]]] = None + message: Optional[Union[Message, Reference]] = None class SubscribeOperation(Operation): @@ -180,53 +180,53 @@ class SubscribeOperation(Operation): class RequestOperation(Operation): - replies: Optional[List[Union[Message, Reference]]] + replies: Optional[List[Union[Message, Reference]]] = None suggestedTimeout: Optional[float] = Field(None, alias="x-suggested-timeout") class Parameter(BaseModel): - description: Optional[str] + description: Optional[str] = None schema_: Union[Dict[str, Any], Reference] = Field(..., alias="schema") - location: Optional[str] + location: Optional[str] = None class ChannelItem(BaseModel): - description: Optional[str] - subscribe: Optional[SubscribeOperation] - publish: Optional[Operation] - request: Optional[RequestOperation] - parameters: Optional[Dict[str, Union[Parameter, Reference]]] - bindings: Optional[Union[ChannelBindings, Reference]] - deprecated: Optional[bool] = None + description: Optional[str] = None + subscribe: Optional[SubscribeOperation] = None + publish: Optional[Operation] = None + request: Optional[RequestOperation] = None + parameters: Optional[Dict[str, Union[Parameter, Reference]]] = None + bindings: Optional[Union[ChannelBindings, Reference]] = None + deprecated: bool | None = None class ServerVariable(BaseModel): - enum: Optional[List[str]] - default: Optional[str] - description: Optional[str] - examples: Optional[List[str]] + enum: Optional[List[str]] = None + default: Optional[str] = None + description: Optional[str] = None + examples: Optional[List[str]] = None class Server(BaseModel): url: str protocol: str - protocolVersion: Optional[str] + protocolVersion: str | None description: Optional[str] variables: Optional[Dict[str, ServerVariable]] - bindings: Optional[Union[ServerBindings, Reference]] + bindings: Optional[Union[ServerBindings, Reference]] = None class Components(BaseModel): - schemas: Optional[Dict[str, Union[Schema, Reference]]] - messages: Optional[Dict[str, Union[Message, Reference]]] - parameters: Optional[Dict[str, Union[Dict[str, Parameter], Reference]]] - correlationIds: Optional[Dict[str, Union[CorrelationId, Reference]]] - operationTraits: Optional[Dict[str, Union[OperationTrait, Reference]]] - messageTraits: Optional[Dict[str, Union[MessageTrait, Reference]]] - serverBindings: Optional[Dict[str, Union[ServerBindings, Reference]]] - channelBindings: Optional[Dict[str, Union[ChannelBindings, Reference]]] - operationBindings: Optional[Dict[str, Union[OperationBindings, Reference]]] - messageBindings: Optional[Dict[str, Union[MessageBindings, Reference]]] + schemas: Optional[Dict[str, Union[Schema, Reference]]] = None + messages: Optional[Dict[str, Union[Message, Reference]]] = None + parameters: Optional[Dict[str, Union[Dict[str, Parameter], Reference]]] = None + correlationIds: Optional[Dict[str, Union[CorrelationId, Reference]]] = None + operationTraits: Optional[Dict[str, Union[OperationTrait, Reference]]] = None + messageTraits: Optional[Dict[str, Union[MessageTrait, Reference]]] = None + serverBindings: Optional[Dict[str, Union[ServerBindings, Reference]]] = None + channelBindings: Optional[Dict[str, Union[ChannelBindings, Reference]]] = None + operationBindings: Optional[Dict[str, Union[OperationBindings, Reference]]] = None + messageBindings: Optional[Dict[str, Union[MessageBindings, Reference]]] = None class Range(BaseModel): @@ -246,12 +246,12 @@ class Errors(BaseModel): class AsyncAPI(BaseModel): asyncapi: str - id: Optional[str] + id: Optional[str] = None info: Info - servers: Optional[Dict[str, Server]] + servers: Optional[Dict[str, Server]] = None defaultContentType: Optional[str] = "application/json" - channels: Optional[Dict[str, Union[ChannelItem, Reference]]] - components: Optional[Components] - tags: Optional[List[Tag]] - externalDocs: Optional[ExternalDocumentation] - errors: Optional[Errors] + channels: Optional[Dict[str, Union[ChannelItem, Reference]]] = None + components: Optional[Components] = None + tags: Optional[List[Tag]] = None + externalDocs: Optional[ExternalDocumentation] = None + errors: Optional[Errors] = None diff --git a/natsapi/asyncapi/utils.py b/natsapi/asyncapi/utils.py index ea14f01..6d66d14 100644 --- a/natsapi/asyncapi/utils.py +++ b/natsapi/asyncapi/utils.py @@ -5,10 +5,24 @@ from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union from pydantic import BaseModel -from pydantic.fields import ModelField -from pydantic.schema import get_flat_models_from_fields, get_model_name_map - -from natsapi.asyncapi.constants import REF_PREFIX +from natsapi._compat import ( + ModelField, + lenient_issubclass, + get_cached_model_fields, + get_compat_model_name_map, + get_definitions, + GenerateJsonSchema, +) + +# from pydantic.schema import get_flat_models_from_fields, get_model_name_map +from pydantic.v1.schema import ( + get_flat_models_from_fields, + get_model_name_map, + get_flat_models_from_field, + get_flat_models_from_model, +) + +from natsapi.asyncapi.constants import REF_PREFIX, REF_TEMPLATE from natsapi.encoders import jsonable_encoder from natsapi.models import JsonRPCError from natsapi.routing import Pub, Publish, Request, Sub @@ -18,6 +32,16 @@ from .models import AsyncAPI +def _get_flat_fields_from_params(fields: List[ModelField]) -> List[ModelField]: + if not fields: + return fields + first_field = fields[0] + if len(fields) == 1 and lenient_issubclass(first_field.type_, BaseModel): + fields_to_extract = get_cached_model_fields(first_field.type_) + return fields_to_extract + return fields + + def get_flat_models_from_routes( routes: Sequence[Request], pubs: Sequence[Pub] ) -> Set[Union[Type[BaseModel], Type[Enum]]]: @@ -36,10 +60,9 @@ def get_flat_models_from_routes( for pub in pubs: messages_from_pubs.add(pub.params_field) - flat_models = get_flat_models_from_fields( - replies_from_routes | requests_from_routes | messages_from_pubs, - known_models=set(), - ) + inputs = replies_from_routes | requests_from_routes | messages_from_pubs + + flat_models = _get_flat_fields_from_params([x for x in inputs]) return flat_models @@ -76,7 +99,7 @@ def generate_asyncapi_request_channel(operation: Request, model_name_map: Dict[s operation_results = get_flat_response_models(operation.result) request_field_ref: str = REF_PREFIX + operation.params.__name__ - reply_field_refs: str = [REF_PREFIX + model_name_map[o] for o in operation_results] + reply_field_refs: str = [REF_PREFIX + o.__name__ for o in operation_results] failed_reply_ref: str = REF_PREFIX + JsonRPCError.__name__ operation_schema = get_asyncapi_request_operation_metadata(operation) @@ -148,7 +171,9 @@ def get_sub_operation_schema(sub: Sub) -> Tuple[str, Dict[str, Any]]: def get_pub_operation_schema(pub: Pub, model_name_map: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]: - pub_payload: str = REF_PREFIX + model_name_map[pub.params] + # pub_payload: str = REF_PREFIX + model_name_map[pub.params] + pub_payload: str = REF_PREFIX + pub.params.__name__ + _pub = { "summary": pub.summary, "description": pub.description, @@ -157,7 +182,6 @@ def get_pub_operation_schema(pub: Pub, model_name_map: Dict[str, Any]) -> Tuple[ "tags": [{"name": tag} for tag in pub.tags] if len(pub.tags) > 0 else None, } op = {"publish": _pub} - return pub.subject, op @@ -181,11 +205,18 @@ def get_asyncapi( output: Dict[str, Any] = {"asyncapi": asyncapi_version, "info": info} flat_models = get_flat_models_from_routes(routes.values(), pubs) - model_name_map = get_model_name_map(flat_models) - definitions = get_model_definitions(flat_models=flat_models, model_name_map=model_name_map) + model_name_map = get_compat_model_name_map(flat_models) + schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE) + + # TODO: <26-02-25, Sebastiaan Van Hoecke> # Where to use the first paramter (see https://github.com/fastapi/fastapi/blob/master/fastapi/openapi/utils.py#L493) + _, definitions = get_definitions( + fields=flat_models, schema_generator=schema_generator, model_name_map=model_name_map + ) definitions[JsonRPCError.__name__] = JsonRPCError.schema() components["schemas"] = definitions - if bool(flat_models): + + # if bool(flat_models): + if 1: subjects: Dict[str, Dict[str, Any]] = {} for subject, endpoint in routes.items(): if getattr(endpoint, "include_schema", None) and isinstance(endpoint, Request): @@ -197,6 +228,7 @@ def get_asyncapi( for sub in subs: channel, operation = get_sub_operation_schema(sub) + subjects[channel] = operation for pub in pubs: diff --git a/natsapi/client/config.py b/natsapi/client/config.py index 115005f..689f0b0 100644 --- a/natsapi/client/config.py +++ b/natsapi/client/config.py @@ -12,7 +12,7 @@ DEFAULT_SUB_PENDING_BYTES_LIMIT, DEFAULT_SUB_PENDING_MSGS_LIMIT, ) -from pydantic import BaseSettings +from natsapi._compat import BaseSettings class ConnectConfig(BaseSettings): @@ -43,7 +43,7 @@ class ConnectConfig(BaseSettings): signature_cb: Any = None user_jwt_cb: Any = None user_credentials: Any = None - nkeys_seed: str = None + nkeys_seed: str | None = None flush_timeout: Optional[float] = None pending_size: int = DEFAULT_PENDING_SIZE diff --git a/natsapi/encoders.py b/natsapi/encoders.py index 84bf426..1c90272 100644 --- a/natsapi/encoders.py +++ b/natsapi/encoders.py @@ -7,7 +7,8 @@ from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union from pydantic import BaseModel -from pydantic.json import ENCODERS_BY_TYPE + +from natsapi._compat import ENCODERS_BY_TYPE, PYDANTIC_V2 SetIntStr = Set[Union[int, str]] DictIntStrAny = Dict[Union[int, str], Any] @@ -41,7 +42,12 @@ def jsonable_encoder( if exclude is not None and not isinstance(exclude, set): exclude = set(exclude) if isinstance(obj, BaseModel): - encoder = getattr(obj.__config__, "json_encoders", {}) + + if PYDANTIC_V2: + encoder = obj.model_config.get("json_encoders", {}) + else: + encoder = getattr(obj.__config__, "json_encoders", {}) + if custom_encoder: encoder.update(custom_encoder) obj_dict = obj.dict( diff --git a/natsapi/utils.py b/natsapi/utils.py index ee84d3f..7492320 100644 --- a/natsapi/utils.py +++ b/natsapi/utils.py @@ -8,9 +8,16 @@ from typing import Any, Callable, Dict, Optional, Set, Type, Union from pydantic import BaseConfig, BaseModel, create_model -from pydantic.class_validators import Validator -from pydantic.fields import FieldInfo, ModelField -from pydantic.schema import model_process_schema + +# from pydantic.class_validators import Validator + +# from pydantic.deprecated.class_validators import V1Validator as Validator +from pydantic.fields import FieldInfo +from natsapi._compat import ModelField, PYDANTIC_V2 + +from pydantic.v1.schema import model_process_schema + +# from pydantic.schema import model_process_schema from natsapi.asyncapi.constants import REF_PREFIX from natsapi.exceptions import NatsAPIError @@ -32,28 +39,53 @@ def generate_operation_id_for_subject(*, summary: str, subject: str) -> str: def create_field( name: str, type_: Type[Any], - class_validators: Optional[Dict[str, Validator]] = None, + class_validators: Optional[Dict[str, Any]] = None, model_config: Type[BaseConfig] = BaseConfig, field_info: Optional[FieldInfo] = None, + + # TODO: <26-02-25, Sebastiaan Van Hoecke> # Might fix the defaults? + # default: Optional[Any] = Undefined, + # alias: Optional[str] = None, + # required: Union[bool, UndefinedType] = Undefined, ) -> ModelField: """ Yanked from fastapi.utils Create a new reply field. Raises if type_ is invalid. """ class_validators = class_validators or {} - field_info = field_info or FieldInfo(None) - field = functools.partial( - ModelField, - name=name, - type_=type_, - class_validators=class_validators, - required=True, - model_config=model_config, - ) + if PYDANTIC_V2: + field_info = field_info or FieldInfo( + annotation=type_ + ) + else: + field_info = field_info or FieldInfo() + + # field = functools.partial( + # ModelField, + # name=name, + # type_=type_, + # class_validators=class_validators, + # required=True, + # model_config=model_config, + # ) + + kwargs = {"name": name, "field_info": field_info} + + if not PYDANTIC_V2: + kwargs.update( + { + "type_": type_, + "class_validators": class_validators, + "model_config": model_config, + "required": True, + # "default": default, + # "alias": alias, + } + ) try: - return field(field_info=field_info) + return ModelField(**kwargs) except RuntimeError: raise NatsAPIError(f"Invalid args for reply field! Hint: check that {type_} is a valid pydantic field type") diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py index 49862b5..ba1c9c1 100644 --- a/tests/test_jsonable_encoder.py +++ b/tests/test_jsonable_encoder.py @@ -1,4 +1,5 @@ """yanked from fastapi""" +from natsapi._compat import RootModel from datetime import datetime, timezone from enum import Enum from pathlib import PurePath, PurePosixPath, PureWindowsPath @@ -76,8 +77,8 @@ class ModelWithDefault(BaseModel): bla: str = "bla" -class ModelWithRoot(BaseModel): - __root__: str +class ModelWithRoot(RootModel): + pass @pytest.fixture(name="model_with_path", params=[PurePath, PurePosixPath, PureWindowsPath]) From a9114524cacf71c8f2267c7a210147e2b2ee0868 Mon Sep 17 00:00:00 2001 From: sevaho Date: Fri, 28 Feb 2025 10:12:07 +0100 Subject: [PATCH 02/24] Fix issue with retrieving flat models --- natsapi/asyncapi/utils.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/natsapi/asyncapi/utils.py b/natsapi/asyncapi/utils.py index 6d66d14..2f071aa 100644 --- a/natsapi/asyncapi/utils.py +++ b/natsapi/asyncapi/utils.py @@ -26,7 +26,6 @@ from natsapi.encoders import jsonable_encoder from natsapi.models import JsonRPCError from natsapi.routing import Pub, Publish, Request, Sub -from natsapi.utils import get_model_definitions from . import Errors, ExternalDocumentation, Server from .models import AsyncAPI @@ -36,15 +35,14 @@ def _get_flat_fields_from_params(fields: List[ModelField]) -> List[ModelField]: if not fields: return fields first_field = fields[0] + if len(fields) == 1 and lenient_issubclass(first_field.type_, BaseModel): fields_to_extract = get_cached_model_fields(first_field.type_) return fields_to_extract return fields -def get_flat_models_from_routes( - routes: Sequence[Request], pubs: Sequence[Pub] -) -> Set[Union[Type[BaseModel], Type[Enum]]]: +def get_fields_from_routes(routes: Sequence[Request], pubs: Sequence[Pub]) -> Set[Union[Type[BaseModel], Type[Enum]]]: replies_from_routes: Set[ModelField] = set() requests_from_routes: Set[ModelField] = set() messages_from_pubs: Set[ModelField] = set() @@ -60,10 +58,9 @@ def get_flat_models_from_routes( for pub in pubs: messages_from_pubs.add(pub.params_field) - inputs = replies_from_routes | requests_from_routes | messages_from_pubs + fields = replies_from_routes | requests_from_routes | messages_from_pubs - flat_models = _get_flat_fields_from_params([x for x in inputs]) - return flat_models + return fields def get_flat_response_models(r) -> List[Type[BaseModel]]: @@ -204,13 +201,13 @@ def get_asyncapi( output: Dict[str, Any] = {"asyncapi": asyncapi_version, "info": info} - flat_models = get_flat_models_from_routes(routes.values(), pubs) - model_name_map = get_compat_model_name_map(flat_models) + all_fields = get_fields_from_routes(routes.values(), pubs) + model_name_map = get_compat_model_name_map(all_fields) schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE) # TODO: <26-02-25, Sebastiaan Van Hoecke> # Where to use the first paramter (see https://github.com/fastapi/fastapi/blob/master/fastapi/openapi/utils.py#L493) _, definitions = get_definitions( - fields=flat_models, schema_generator=schema_generator, model_name_map=model_name_map + fields=all_fields, schema_generator=schema_generator, model_name_map=model_name_map ) definitions[JsonRPCError.__name__] = JsonRPCError.schema() components["schemas"] = definitions From 48ced5574a88e15f561f9adfe691a328b4a5f3c0 Mon Sep 17 00:00:00 2001 From: sevaho Date: Fri, 28 Feb 2025 14:19:12 +0100 Subject: [PATCH 03/24] Make code work with pydantic v2 --- natsapi/_compat.py | 39 +- natsapi/asyncapi/models.py | 2 +- natsapi/asyncapi/utils.py | 26 +- natsapi/utils.py | 6 +- poetry.lock | 730 +++++++++++++++------------ pyproject.toml | 2 - tests/plugins/test_mock.py | 2 +- tests/test_jsonable_encoder.py | 3 +- tests/test_method_type_conversion.py | 2 +- 9 files changed, 434 insertions(+), 378 deletions(-) diff --git a/natsapi/_compat.py b/natsapi/_compat.py index 00d165f..cc2b0f4 100644 --- a/natsapi/_compat.py +++ b/natsapi/_compat.py @@ -1,27 +1,21 @@ -from collections import deque -from typing_extensions import Annotated, Literal, get_args, get_origin -from copy import copy -from dataclasses import dataclass, is_dataclass -from natsapi.asyncapi.constants import REF_PREFIX, REF_TEMPLATE +from typing_extensions import Annotated +from dataclasses import dataclass +from natsapi.asyncapi.constants import REF_PREFIX from enum import Enum from functools import lru_cache from typing import ( Any, Optional, - Callable, Literal, - Deque, Dict, - FrozenSet, List, - Mapping, Sequence, Set, Tuple, Type, Union, ) -from pydantic import BaseModel, create_model +from pydantic import BaseModel from pydantic.version import VERSION as PYDANTIC_VERSION PYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(".")[:2]) @@ -32,15 +26,16 @@ if PYDANTIC_V2: from pydantic.fields import FieldInfo from pydantic import ValidationError as ValidationError - from pydantic.deprecated.json import ENCODERS_BY_TYPE - from pydantic_settings import BaseSettings from pydantic import RootModel # noqa + from pydantic_settings import BaseSettings + + from pydantic.deprecated.json import ENCODERS_BY_TYPE + from pydantic import TypeAdapter - from pydantic import BaseConfig + from pydantic._internal._utils import lenient_issubclass as lenient_issubclass from pydantic_core import PydanticUndefined, PydanticUndefinedType - from pydantic._internal._utils import lenient_issubclass as lenient_issubclass from pydantic.json_schema import GenerateJsonSchema as GenerateJsonSchema from pydantic.json_schema import JsonSchemaValue as JsonSchemaValue @@ -53,11 +48,7 @@ class ModelField: field_info: FieldInfo name: str mode: Literal["validation", "serialization"] = "validation" - # required: bool sub_fields: Optional[str] = None - # type_: Any - # class_validators: Optional[Dict[str, Any]] = None - # model_config: Type[BaseConfig] = BaseConfig @property def alias(self) -> str: @@ -151,7 +142,7 @@ def get_definitions( else: from pydantic.json import ENCODERS_BY_TYPE # noqa: F401 - from pydantic import BaseSettings # noqa + from pydantic import BaseModel from pydantic.fields import ( # type: ignore[no-redef,attr-defined] @@ -160,18 +151,18 @@ def get_definitions( from pydantic.error_wrappers import ( # type: ignore[no-redef] ErrorWrapper as ErrorWrapper, # noqa: F401 ) - - from pydantic.utils import ( # type: ignore[no-redef] - lenient_issubclass as lenient_issubclass, # noqa: F401 - ) + from pydantic import BaseSettings # noqa: F401 from pydantic.schema import ( - field_schema, get_flat_models_from_fields, get_model_name_map, model_process_schema, ) + from pydantic.utils import ( # noqa + lenient_issubclass as lenient_issubclass, # noqa: F401 + ) + class RootModel(BaseModel): __root__: str diff --git a/natsapi/asyncapi/models.py b/natsapi/asyncapi/models.py index 1f21529..10ffec0 100644 --- a/natsapi/asyncapi/models.py +++ b/natsapi/asyncapi/models.py @@ -124,7 +124,7 @@ class CorrelationId(BaseModel): class MessageTrait(BaseModel): - headers: Optional[Union[Schema, Reference]] + headers: Optional[Union[Schema, Reference]] correlationId: Optional[Union[CorrelationId, Reference]] schemaFormat: Optional[str] contentType: Optional[str] diff --git a/natsapi/asyncapi/utils.py b/natsapi/asyncapi/utils.py index 2f071aa..5365f0a 100644 --- a/natsapi/asyncapi/utils.py +++ b/natsapi/asyncapi/utils.py @@ -14,14 +14,6 @@ GenerateJsonSchema, ) -# from pydantic.schema import get_flat_models_from_fields, get_model_name_map -from pydantic.v1.schema import ( - get_flat_models_from_fields, - get_model_name_map, - get_flat_models_from_field, - get_flat_models_from_model, -) - from natsapi.asyncapi.constants import REF_PREFIX, REF_TEMPLATE from natsapi.encoders import jsonable_encoder from natsapi.models import JsonRPCError @@ -212,16 +204,14 @@ def get_asyncapi( definitions[JsonRPCError.__name__] = JsonRPCError.schema() components["schemas"] = definitions - # if bool(flat_models): - if 1: - subjects: Dict[str, Dict[str, Any]] = {} - for subject, endpoint in routes.items(): - if getattr(endpoint, "include_schema", None) and isinstance(endpoint, Request): - result = generate_asyncapi_request_channel(endpoint, model_name_map) - subjects[subject] = result - elif getattr(endpoint, "include_schema", None) and isinstance(endpoint, Publish): - result = generate_asyncapi_publish_channel(endpoint, model_name_map) - subjects[subject] = result + subjects: Dict[str, Dict[str, Any]] = {} + for subject, endpoint in routes.items(): + if getattr(endpoint, "include_schema", None) and isinstance(endpoint, Request): + result = generate_asyncapi_request_channel(endpoint, model_name_map) + subjects[subject] = result + elif getattr(endpoint, "include_schema", None) and isinstance(endpoint, Publish): + result = generate_asyncapi_publish_channel(endpoint, model_name_map) + subjects[subject] = result for sub in subs: channel, operation = get_sub_operation_schema(sub) diff --git a/natsapi/utils.py b/natsapi/utils.py index 7492320..9fe89d9 100644 --- a/natsapi/utils.py +++ b/natsapi/utils.py @@ -1,5 +1,4 @@ import collections -import functools import inspect import re @@ -42,7 +41,6 @@ def create_field( class_validators: Optional[Dict[str, Any]] = None, model_config: Type[BaseConfig] = BaseConfig, field_info: Optional[FieldInfo] = None, - # TODO: <26-02-25, Sebastiaan Van Hoecke> # Might fix the defaults? # default: Optional[Any] = Undefined, # alias: Optional[str] = None, @@ -55,9 +53,7 @@ def create_field( class_validators = class_validators or {} if PYDANTIC_V2: - field_info = field_info or FieldInfo( - annotation=type_ - ) + field_info = field_info or FieldInfo(annotation=type_) else: field_info = field_info or FieldInfo() diff --git a/poetry.lock b/poetry.lock index 38dae20..406dcc7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,13 +2,13 @@ [[package]] name = "bandit" -version = "1.7.9" +version = "1.8.3" description = "Security oriented static analyser for python code." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "bandit-1.7.9-py3-none-any.whl", hash = "sha256:52077cb339000f337fb25f7e045995c4ad01511e716e5daac37014b9752de8ec"}, - {file = "bandit-1.7.9.tar.gz", hash = "sha256:7c395a436743018f7be0a4cbb0a4ea9b902b6d87264ddecf8cfdc73b4f78ff61"}, + {file = "bandit-1.8.3-py3-none-any.whl", hash = "sha256:28f04dc0d258e1dd0f99dee8eefa13d1cb5e3fde1a5ab0c523971f97b289bcd8"}, + {file = "bandit-1.8.3.tar.gz", hash = "sha256:f5847beb654d309422985c36644649924e0ea4425c76dec2e89110b87506193a"}, ] [package.dependencies] @@ -61,74 +61,89 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2024.7.4" +version = "2025.1.31" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, - {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, + {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, + {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, ] [[package]] name = "cffi" -version = "1.16.0" +version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" files = [ - {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, - {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, - {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, - {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, - {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, - {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, - {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, - {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, - {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, - {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, - {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, - {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, - {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, - {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, - {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, ] [package.dependencies] @@ -147,112 +162,114 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.3.2" +version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.7" files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, + {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, + {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, ] [[package]] name = "click" -version = "8.1.7" +version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, ] [package.dependencies] @@ -335,24 +352,24 @@ toml = ["toml"] [[package]] name = "distlib" -version = "0.3.8" +version = "0.3.9" description = "Distribution utilities" optional = false python-versions = "*" files = [ - {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, - {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, + {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, + {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, ] [[package]] name = "dparse" -version = "0.6.3" +version = "0.6.4" description = "A parser for Python dependency files" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "dparse-0.6.3-py3-none-any.whl", hash = "sha256:0d8fe18714056ca632d98b24fbfc4e9791d4e47065285ab486182288813a5318"}, - {file = "dparse-0.6.3.tar.gz", hash = "sha256:27bb8b4bcaefec3997697ba3f6e06b2447200ba273c0b085c3d012a04571b528"}, + {file = "dparse-0.6.4-py3-none-any.whl", hash = "sha256:fbab4d50d54d0e739fbb4dedfc3d92771003a5b9aa8545ca7a7045e3b174af57"}, + {file = "dparse-0.6.4.tar.gz", hash = "sha256:90b29c39e3edc36c6284c82c4132648eaf28a01863eb3c231c2512196132201a"}, ] [package.dependencies] @@ -360,8 +377,10 @@ packaging = "*" tomli = {version = "*", markers = "python_version < \"3.11\""} [package.extras] +all = ["pipenv", "poetry", "pyyaml"] conda = ["pyyaml"] -pipenv = ["pipenv (<=2022.12.19)"] +pipenv = ["pipenv"] +poetry = ["poetry"] [[package]] name = "exceptiongroup" @@ -379,19 +398,19 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.15.4" +version = "3.17.0" description = "A platform independent file lock." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, - {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, + {file = "filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338"}, + {file = "filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] -typing = ["typing-extensions (>=4.8)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] +typing = ["typing-extensions (>=4.12.2)"] [[package]] name = "flake9" @@ -411,13 +430,13 @@ pyflakes = ">=2.2.0,<2.3.0" [[package]] name = "identify" -version = "2.6.0" +version = "2.6.8" description = "File identification library for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" 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.8-py2.py3-none-any.whl", hash = "sha256:83657f0f766a3c8d0eaea16d4ef42494b39b34629a4b3192a9d020d349b3e255"}, + {file = "identify-2.6.8.tar.gz", hash = "sha256:61491417ea2c0c5c670484fd8abbb34de34cdae1e5f39a73ee65e48e4bb663fc"}, ] [package.extras] @@ -425,15 +444,18 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.7" +version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -518,12 +540,12 @@ files = [ [[package]] name = "nats-py" -version = "2.8.0" +version = "2.9.0" description = "NATS client for Python" optional = false python-versions = ">=3.7" files = [ - {file = "nats_py-2.8.0.tar.gz", hash = "sha256:e998dcacd711db3b90b469dcdd96c526cc56c421bad27be0f5f40e88005a7c44"}, + {file = "nats_py-2.9.0.tar.gz", hash = "sha256:01886eb9e0a87f0ec630652cf1fae65d2a8556378a609bc6cc07d2ea60c8d0dd"}, ] [package.dependencies] @@ -536,12 +558,12 @@ nkeys = ["nkeys"] [[package]] name = "nkeys" -version = "0.2.0" +version = "0.2.1" description = "A public-key signature system based on Ed25519 for the NATS ecosystem." optional = false python-versions = ">=3.6" files = [ - {file = "nkeys-0.2.0.tar.gz", hash = "sha256:e7d12a31b3126b4620589ea2013aa2889497cb5c4bd8e806ceb39816f7cc1bb7"}, + {file = "nkeys-0.2.1.tar.gz", hash = "sha256:3a201dcd203d8bb05ba2884d441b2c92918b2a537a10d324e73738887dde9da3"}, ] [package.dependencies] @@ -560,13 +582,13 @@ files = [ [[package]] name = "packaging" -version = "24.1" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -582,15 +604,18 @@ files = [ [[package]] name = "pbr" -version = "6.0.0" +version = "6.1.1" description = "Python Build Reasonableness" optional = false python-versions = ">=2.6" files = [ - {file = "pbr-6.0.0-py2.py3-none-any.whl", hash = "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda"}, - {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"}, + {file = "pbr-6.1.1-py2.py3-none-any.whl", hash = "sha256:38d4daea5d9fa63b3f626131b9d34947fd0c8be9b05a29276870580050a25a76"}, + {file = "pbr-6.1.1.tar.gz", hash = "sha256:93ea72ce6989eb2eed99d0f75721474f69ad88128afdef5ac377eb797c4bf76b"}, ] +[package.dependencies] +setuptools = "*" + [[package]] name = "piprot" version = "0.9.11" @@ -608,19 +633,19 @@ six = "*" [[package]] name = "platformdirs" -version = "4.2.2" +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.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] -type = ["mypy (>=1.8)"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] [[package]] name = "pluggy" @@ -679,54 +704,61 @@ files = [ [[package]] name = "pydantic" -version = "1.10.17" +version = "1.10.21" description = "Data validation and settings management using python type hints" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.17-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fa51175313cc30097660b10eec8ca55ed08bfa07acbfe02f7a42f6c242e9a4b"}, - {file = "pydantic-1.10.17-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7e8988bb16988890c985bd2093df9dd731bfb9d5e0860db054c23034fab8f7a"}, - {file = "pydantic-1.10.17-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:371dcf1831f87c9e217e2b6a0c66842879a14873114ebb9d0861ab22e3b5bb1e"}, - {file = "pydantic-1.10.17-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4866a1579c0c3ca2c40575398a24d805d4db6cb353ee74df75ddeee3c657f9a7"}, - {file = "pydantic-1.10.17-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:543da3c6914795b37785703ffc74ba4d660418620cc273490d42c53949eeeca6"}, - {file = "pydantic-1.10.17-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7623b59876f49e61c2e283551cc3647616d2fbdc0b4d36d3d638aae8547ea681"}, - {file = "pydantic-1.10.17-cp310-cp310-win_amd64.whl", hash = "sha256:409b2b36d7d7d19cd8310b97a4ce6b1755ef8bd45b9a2ec5ec2b124db0a0d8f3"}, - {file = "pydantic-1.10.17-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fa43f362b46741df8f201bf3e7dff3569fa92069bcc7b4a740dea3602e27ab7a"}, - {file = "pydantic-1.10.17-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2a72d2a5ff86a3075ed81ca031eac86923d44bc5d42e719d585a8eb547bf0c9b"}, - {file = "pydantic-1.10.17-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4ad32aed3bf5eea5ca5decc3d1bbc3d0ec5d4fbcd72a03cdad849458decbc63"}, - {file = "pydantic-1.10.17-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb4e741782e236ee7dc1fb11ad94dc56aabaf02d21df0e79e0c21fe07c95741"}, - {file = "pydantic-1.10.17-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d2f89a719411cb234105735a520b7c077158a81e0fe1cb05a79c01fc5eb59d3c"}, - {file = "pydantic-1.10.17-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db3b48d9283d80a314f7a682f7acae8422386de659fffaba454b77a083c3937d"}, - {file = "pydantic-1.10.17-cp311-cp311-win_amd64.whl", hash = "sha256:9c803a5113cfab7bbb912f75faa4fc1e4acff43e452c82560349fff64f852e1b"}, - {file = "pydantic-1.10.17-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:820ae12a390c9cbb26bb44913c87fa2ff431a029a785642c1ff11fed0a095fcb"}, - {file = "pydantic-1.10.17-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c1e51d1af306641b7d1574d6d3307eaa10a4991542ca324f0feb134fee259815"}, - {file = "pydantic-1.10.17-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e53fb834aae96e7b0dadd6e92c66e7dd9cdf08965340ed04c16813102a47fab"}, - {file = "pydantic-1.10.17-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e2495309b1266e81d259a570dd199916ff34f7f51f1b549a0d37a6d9b17b4dc"}, - {file = "pydantic-1.10.17-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:098ad8de840c92ea586bf8efd9e2e90c6339d33ab5c1cfbb85be66e4ecf8213f"}, - {file = "pydantic-1.10.17-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:525bbef620dac93c430d5d6bdbc91bdb5521698d434adf4434a7ef6ffd5c4b7f"}, - {file = "pydantic-1.10.17-cp312-cp312-win_amd64.whl", hash = "sha256:6654028d1144df451e1da69a670083c27117d493f16cf83da81e1e50edce72ad"}, - {file = "pydantic-1.10.17-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c87cedb4680d1614f1d59d13fea353faf3afd41ba5c906a266f3f2e8c245d655"}, - {file = "pydantic-1.10.17-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11289fa895bcbc8f18704efa1d8020bb9a86314da435348f59745473eb042e6b"}, - {file = "pydantic-1.10.17-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94833612d6fd18b57c359a127cbfd932d9150c1b72fea7c86ab58c2a77edd7c7"}, - {file = "pydantic-1.10.17-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d4ecb515fa7cb0e46e163ecd9d52f9147ba57bc3633dca0e586cdb7a232db9e3"}, - {file = "pydantic-1.10.17-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7017971ffa7fd7808146880aa41b266e06c1e6e12261768a28b8b41ba55c8076"}, - {file = "pydantic-1.10.17-cp37-cp37m-win_amd64.whl", hash = "sha256:e840e6b2026920fc3f250ea8ebfdedf6ea7a25b77bf04c6576178e681942ae0f"}, - {file = "pydantic-1.10.17-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bfbb18b616abc4df70591b8c1ff1b3eabd234ddcddb86b7cac82657ab9017e33"}, - {file = "pydantic-1.10.17-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebb249096d873593e014535ab07145498957091aa6ae92759a32d40cb9998e2e"}, - {file = "pydantic-1.10.17-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8c209af63ccd7b22fba94b9024e8b7fd07feffee0001efae50dd99316b27768"}, - {file = "pydantic-1.10.17-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4b40c9e13a0b61583e5599e7950490c700297b4a375b55b2b592774332798b7"}, - {file = "pydantic-1.10.17-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c31d281c7485223caf6474fc2b7cf21456289dbaa31401844069b77160cab9c7"}, - {file = "pydantic-1.10.17-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae5184e99a060a5c80010a2d53c99aee76a3b0ad683d493e5f0620b5d86eeb75"}, - {file = "pydantic-1.10.17-cp38-cp38-win_amd64.whl", hash = "sha256:ad1e33dc6b9787a6f0f3fd132859aa75626528b49cc1f9e429cdacb2608ad5f0"}, - {file = "pydantic-1.10.17-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e17c0ee7192e54a10943f245dc79e36d9fe282418ea05b886e1c666063a7b54"}, - {file = "pydantic-1.10.17-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cafb9c938f61d1b182dfc7d44a7021326547b7b9cf695db5b68ec7b590214773"}, - {file = "pydantic-1.10.17-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95ef534e3c22e5abbdbdd6f66b6ea9dac3ca3e34c5c632894f8625d13d084cbe"}, - {file = "pydantic-1.10.17-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d96b8799ae3d782df7ec9615cb59fc32c32e1ed6afa1b231b0595f6516e8ab"}, - {file = "pydantic-1.10.17-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ab2f976336808fd5d539fdc26eb51f9aafc1f4b638e212ef6b6f05e753c8011d"}, - {file = "pydantic-1.10.17-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8ad363330557beac73159acfbeed220d5f1bfcd6b930302a987a375e02f74fd"}, - {file = "pydantic-1.10.17-cp39-cp39-win_amd64.whl", hash = "sha256:48db882e48575ce4b39659558b2f9f37c25b8d348e37a2b4e32971dd5a7d6227"}, - {file = "pydantic-1.10.17-py3-none-any.whl", hash = "sha256:e41b5b973e5c64f674b3b4720286ded184dcc26a691dd55f34391c62c6934688"}, - {file = "pydantic-1.10.17.tar.gz", hash = "sha256:f434160fb14b353caf634149baaf847206406471ba70e64657c1e8330277a991"}, + {file = "pydantic-1.10.21-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:245e486e0fec53ec2366df9cf1cba36e0bbf066af7cd9c974bbbd9ba10e1e586"}, + {file = "pydantic-1.10.21-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c54f8d4c151c1de784c5b93dfbb872067e3414619e10e21e695f7bb84d1d1fd"}, + {file = "pydantic-1.10.21-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b64708009cfabd9c2211295144ff455ec7ceb4c4fb45a07a804309598f36187"}, + {file = "pydantic-1.10.21-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a148410fa0e971ba333358d11a6dea7b48e063de127c2b09ece9d1c1137dde4"}, + {file = "pydantic-1.10.21-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:36ceadef055af06e7756eb4b871cdc9e5a27bdc06a45c820cd94b443de019bbf"}, + {file = "pydantic-1.10.21-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0501e1d12df6ab1211b8cad52d2f7b2cd81f8e8e776d39aa5e71e2998d0379f"}, + {file = "pydantic-1.10.21-cp310-cp310-win_amd64.whl", hash = "sha256:c261127c275d7bce50b26b26c7d8427dcb5c4803e840e913f8d9df3f99dca55f"}, + {file = "pydantic-1.10.21-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8b6350b68566bb6b164fb06a3772e878887f3c857c46c0c534788081cb48adf4"}, + {file = "pydantic-1.10.21-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:935b19fdcde236f4fbf691959fa5c3e2b6951fff132964e869e57c70f2ad1ba3"}, + {file = "pydantic-1.10.21-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b6a04efdcd25486b27f24c1648d5adc1633ad8b4506d0e96e5367f075ed2e0b"}, + {file = "pydantic-1.10.21-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1ba253eb5af8d89864073e6ce8e6c8dec5f49920cff61f38f5c3383e38b1c9f"}, + {file = "pydantic-1.10.21-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:57f0101e6c97b411f287a0b7cf5ebc4e5d3b18254bf926f45a11615d29475793"}, + {file = "pydantic-1.10.21-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e85834f0370d737c77a386ce505c21b06bfe7086c1c568b70e15a568d9670d"}, + {file = "pydantic-1.10.21-cp311-cp311-win_amd64.whl", hash = "sha256:6a497bc66b3374b7d105763d1d3de76d949287bf28969bff4656206ab8a53aa9"}, + {file = "pydantic-1.10.21-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2ed4a5f13cf160d64aa331ab9017af81f3481cd9fd0e49f1d707b57fe1b9f3ae"}, + {file = "pydantic-1.10.21-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b7693bb6ed3fbe250e222f9415abb73111bb09b73ab90d2d4d53f6390e0ccc1"}, + {file = "pydantic-1.10.21-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185d5f1dff1fead51766da9b2de4f3dc3b8fca39e59383c273f34a6ae254e3e2"}, + {file = "pydantic-1.10.21-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38e6d35cf7cd1727822c79e324fa0677e1a08c88a34f56695101f5ad4d5e20e5"}, + {file = "pydantic-1.10.21-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:1d7c332685eafacb64a1a7645b409a166eb7537f23142d26895746f628a3149b"}, + {file = "pydantic-1.10.21-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c9b782db6f993a36092480eeaab8ba0609f786041b01f39c7c52252bda6d85f"}, + {file = "pydantic-1.10.21-cp312-cp312-win_amd64.whl", hash = "sha256:7ce64d23d4e71d9698492479505674c5c5b92cda02b07c91dfc13633b2eef805"}, + {file = "pydantic-1.10.21-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0067935d35044950be781933ab91b9a708eaff124bf860fa2f70aeb1c4be7212"}, + {file = "pydantic-1.10.21-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5e8148c2ce4894ce7e5a4925d9d3fdce429fb0e821b5a8783573f3611933a251"}, + {file = "pydantic-1.10.21-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4973232c98b9b44c78b1233693e5e1938add5af18042f031737e1214455f9b8"}, + {file = "pydantic-1.10.21-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:662bf5ce3c9b1cef32a32a2f4debe00d2f4839fefbebe1d6956e681122a9c839"}, + {file = "pydantic-1.10.21-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:98737c3ab5a2f8a85f2326eebcd214510f898881a290a7939a45ec294743c875"}, + {file = "pydantic-1.10.21-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0bb58bbe65a43483d49f66b6c8474424d551a3fbe8a7796c42da314bac712738"}, + {file = "pydantic-1.10.21-cp313-cp313-win_amd64.whl", hash = "sha256:e622314542fb48542c09c7bd1ac51d71c5632dd3c92dc82ede6da233f55f4848"}, + {file = "pydantic-1.10.21-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d356aa5b18ef5a24d8081f5c5beb67c0a2a6ff2a953ee38d65a2aa96526b274f"}, + {file = "pydantic-1.10.21-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08caa8c0468172d27c669abfe9e7d96a8b1655ec0833753e117061febaaadef5"}, + {file = "pydantic-1.10.21-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c677aa39ec737fec932feb68e4a2abe142682f2885558402602cd9746a1c92e8"}, + {file = "pydantic-1.10.21-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:79577cc045d3442c4e845df53df9f9202546e2ba54954c057d253fc17cd16cb1"}, + {file = "pydantic-1.10.21-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:b6b73ab347284719f818acb14f7cd80696c6fdf1bd34feee1955d7a72d2e64ce"}, + {file = "pydantic-1.10.21-cp37-cp37m-win_amd64.whl", hash = "sha256:46cffa24891b06269e12f7e1ec50b73f0c9ab4ce71c2caa4ccf1fb36845e1ff7"}, + {file = "pydantic-1.10.21-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:298d6f765e3c9825dfa78f24c1efd29af91c3ab1b763e1fd26ae4d9e1749e5c8"}, + {file = "pydantic-1.10.21-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f2f4a2305f15eff68f874766d982114ac89468f1c2c0b97640e719cf1a078374"}, + {file = "pydantic-1.10.21-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35b263b60c519354afb3a60107d20470dd5250b3ce54c08753f6975c406d949b"}, + {file = "pydantic-1.10.21-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e23a97a6c2f2db88995496db9387cd1727acdacc85835ba8619dce826c0b11a6"}, + {file = "pydantic-1.10.21-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:3c96fed246ccc1acb2df032ff642459e4ae18b315ecbab4d95c95cfa292e8517"}, + {file = "pydantic-1.10.21-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b92893ebefc0151474f682e7debb6ab38552ce56a90e39a8834734c81f37c8a9"}, + {file = "pydantic-1.10.21-cp38-cp38-win_amd64.whl", hash = "sha256:b8460bc256bf0de821839aea6794bb38a4c0fbd48f949ea51093f6edce0be459"}, + {file = "pydantic-1.10.21-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d387940f0f1a0adb3c44481aa379122d06df8486cc8f652a7b3b0caf08435f7"}, + {file = "pydantic-1.10.21-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:266ecfc384861d7b0b9c214788ddff75a2ea123aa756bcca6b2a1175edeca0fe"}, + {file = "pydantic-1.10.21-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61da798c05a06a362a2f8c5e3ff0341743e2818d0f530eaac0d6898f1b187f1f"}, + {file = "pydantic-1.10.21-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a621742da75ce272d64ea57bd7651ee2a115fa67c0f11d66d9dcfc18c2f1b106"}, + {file = "pydantic-1.10.21-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9e3e4000cd54ef455694b8be9111ea20f66a686fc155feda1ecacf2322b115da"}, + {file = "pydantic-1.10.21-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f198c8206640f4c0ef5a76b779241efb1380a300d88b1bce9bfe95a6362e674d"}, + {file = "pydantic-1.10.21-cp39-cp39-win_amd64.whl", hash = "sha256:e7f0cda108b36a30c8fc882e4fc5b7eec8ef584aa43aa43694c6a7b274fb2b56"}, + {file = "pydantic-1.10.21-py3-none-any.whl", hash = "sha256:db70c920cba9d05c69ad4a9e7f8e9e83011abb2c6490e561de9ae24aee44925c"}, + {file = "pydantic-1.10.21.tar.gz", hash = "sha256:64b48e2b609a6c22178a56c408ee1215a7206077ecb8a193e2fda31858b2362a"}, ] [package.dependencies] @@ -749,13 +781,13 @@ files = [ [[package]] name = "pygments" -version = "2.18.0" +version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" files = [ - {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, - {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, + {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, + {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, ] [package.extras] @@ -789,13 +821,13 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] [[package]] name = "pytest" -version = "8.3.2" +version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, - {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, ] [package.dependencies] @@ -847,51 +879,64 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pyyaml" -version = "6.0.1" +version = "6.0.2" description = "YAML parser and emitter for Python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] [[package]] @@ -917,35 +962,36 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "requests-futures" -version = "1.0.1" +version = "1.0.2" description = "Asynchronous Python HTTP for Humans." optional = false python-versions = "*" files = [ - {file = "requests-futures-1.0.1.tar.gz", hash = "sha256:f55a4ef80070e2858e7d1e73123d2bfaeaf25b93fd34384d8ddf148e2b676373"}, - {file = "requests_futures-1.0.1-py2.py3-none-any.whl", hash = "sha256:4a2f5472e9911a79532137d156aa937cd9cd90fec55677f71b2976d1f7a66d38"}, + {file = "requests_futures-1.0.2-py2.py3-none-any.whl", hash = "sha256:a3534af7c2bf670cd7aa730716e9e7d4386497554f87792be7514063b8912897"}, + {file = "requests_futures-1.0.2.tar.gz", hash = "sha256:6b7eb57940336e800faebc3dab506360edec9478f7b22dc570858ad3aa7458da"}, ] [package.dependencies] requests = ">=1.2.0" [package.extras] -dev = ["black (>=22.3.0)", "build (>=0.7.0)", "isort (>=5.11.4)", "pyflakes (>=2.2.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "pytest-network (>=0.0.1)", "readme-renderer[rst] (>=26.0)", "twine (>=3.4.2)"] +dev = ["Werkzeug (>=3.0.6)", "black (>=24.3.0,<25.0.0)", "build (>=0.7.0)", "docutils (<=0.20.1)", "greenlet (<=2.0.2)", "greenlet (>=3.0.0)", "isort (>=5.11.4)", "pyflakes (>=2.2.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "pytest-httpbin (>=2.0.0)", "readme-renderer[rst] (>=26.0)", "twine (>=3.4.2)"] [[package]] name = "rich" -version = "13.7.1" +version = "13.9.4" 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.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, - {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, + {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, + {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, ] [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)"] @@ -970,44 +1016,48 @@ setuptools = "*" [[package]] name = "setuptools" -version = "72.1.0" +version = "75.8.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "setuptools-72.1.0-py3-none-any.whl", hash = "sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1"}, - {file = "setuptools-72.1.0.tar.gz", hash = "sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec"}, + {file = "setuptools-75.8.2-py3-none-any.whl", hash = "sha256:558e47c15f1811c1fa7adbd0096669bf76c1d3f433f58324df69f3f5ecac4e8f"}, + {file = "setuptools-75.8.2.tar.gz", hash = "sha256:4880473a969e5f23f2a2be3646b2dfd84af9028716d398e46192f84bc36900d2"}, ] [package.extras] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] +core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "six" -version = "1.16.0" +version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] [[package]] name = "stevedore" -version = "5.2.0" +version = "5.4.1" description = "Manage dynamic plugins for Python applications" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "stevedore-5.2.0-py3-none-any.whl", hash = "sha256:1c15d95766ca0569cad14cb6272d4d31dae66b011a929d7c18219c176ea1b5c9"}, - {file = "stevedore-5.2.0.tar.gz", hash = "sha256:46b93ca40e1114cea93d738a6c1e365396981bb6bb78c27045b7587c9473544d"}, + {file = "stevedore-5.4.1-py3-none-any.whl", hash = "sha256:d10a31c7b86cba16c1f6e8d15416955fc797052351a56af15e608ad20811fcfe"}, + {file = "stevedore-5.4.1.tar.gz", hash = "sha256:3135b5ae50fe12816ef291baff420acb727fcd356106e3e9cbfa9e5985cd6f4b"}, ] [package.dependencies] -pbr = ">=2.0.0,<2.1.0 || >2.1.0" +pbr = ">=2.0.0" [[package]] name = "toml" @@ -1022,13 +1072,43 @@ files = [ [[package]] name = "tomli" -version = "2.0.1" +version = "2.2.1" 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.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] @@ -1044,13 +1124,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.2" +version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, - {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, + {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, + {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, ] [package.extras] @@ -1061,13 +1141,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.26.3" +version = "20.29.2" description = "Virtual Python Environment builder" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, - {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, + {file = "virtualenv-20.29.2-py3-none-any.whl", hash = "sha256:febddfc3d1ea571bdb1dc0f98d7b45d24def7428214d4fb73cc486c9568cce6a"}, + {file = "virtualenv-20.29.2.tar.gz", hash = "sha256:fdaabebf6d03b5ba83ae0a02cfe96f48a716f4fae556461d180825866f75b728"}, ] [package.dependencies] @@ -1081,13 +1161,13 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [[package]] name = "vulture" -version = "2.11" +version = "2.14" description = "Find dead code" optional = false python-versions = ">=3.8" files = [ - {file = "vulture-2.11-py2.py3-none-any.whl", hash = "sha256:12d745f7710ffbf6aeb8279ba9068a24d4e52e8ed333b8b044035c9d6b823aba"}, - {file = "vulture-2.11.tar.gz", hash = "sha256:f0fbb60bce6511aad87ee0736c502456737490a82d919a44e6d92262cb35f1c2"}, + {file = "vulture-2.14-py2.py3-none-any.whl", hash = "sha256:d9a90dba89607489548a49d557f8bac8112bd25d3cbc8aeef23e860811bd5ed9"}, + {file = "vulture-2.14.tar.gz", hash = "sha256:cb8277902a1138deeab796ec5bef7076a6e0248ca3607a3f3dee0b6d9e9b8415"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index 86a104b..693741c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,10 +14,8 @@ Pypi = "https://pypi.org/project/natsapi/" [tool.poetry.build] generate-setup-file = false -# Requirements [tool.poetry.dependencies] python = "^3.9" - pydantic = "^1.10.13" nats-py = {extras = ["nkeys"], version = "^2.2.0"} diff --git a/tests/plugins/test_mock.py b/tests/plugins/test_mock.py index dd5f011..4e3077a 100644 --- a/tests/plugins/test_mock.py +++ b/tests/plugins/test_mock.py @@ -59,7 +59,7 @@ async def test_nats_mock_should_raise_error_when_invalid_error_response(app, nat reply = await app.nc.request(f"{ch}.items.retrieve", timeout=1) # then: - assert "value is not a valid dict" in reply.error.message + assert "valid dict" in reply.error.message async def test_be_able_to_intercept_nats_publish_event_payload(app, natsapi_mock): diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py index ba1c9c1..dfc1659 100644 --- a/tests/test_jsonable_encoder.py +++ b/tests/test_jsonable_encoder.py @@ -8,7 +8,7 @@ import pytest -from pydantic import BaseModel, Field, ValidationError, create_model +from pydantic import BaseModel, Field, ValidationError, create_model, ConfigDict from natsapi.encoders import jsonable_encoder @@ -151,6 +151,7 @@ class safe_datetime(datetime): pass class MyModel(BaseModel): + model_config = ConfigDict(arbitrary_types_allowed=True) dt_field: safe_datetime instance = MyModel(dt_field=safe_datetime.now()) diff --git a/tests/test_method_type_conversion.py b/tests/test_method_type_conversion.py index 3e66507..87fd0de 100644 --- a/tests/test_method_type_conversion.py +++ b/tests/test_method_type_conversion.py @@ -19,7 +19,7 @@ class FastAPI(BaseModel): async def test_method_parameters_should_get_parsed_to_correct_typing(app): class ThemesCreateCmd(BaseModel): primary: str - color: typing.Optional[str] + color: typing.Optional[str] | None = None @app.request("themes.CREATE", result=StatusResult) async def create_theme(app, data: ThemesCreateCmd): From 7dff408f62dabbdfa5c89485c5584692867e80ae Mon Sep 17 00:00:00 2001 From: sevaho Date: Fri, 28 Feb 2025 14:22:14 +0100 Subject: [PATCH 04/24] Keep previous test coverage flags --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 988da1f..9942f5a 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ PREFIX = /usr/local # Default installation directory PYTEST_GENERAL_FLAGS := -vvvx --asyncio-mode=auto PYTEST_COV_FLAGS := --cov=natsapi --cov-append --cov-report=term-missing --cov-fail-under=92 -PYTEST_COV_ENV := COV_CORE_SOURCE=natsapi COV_CORE_CONFIG=.coveragerc +PYTEST_COV_ENV := COV_CORE_SOURCE=natsapi COV_CORE_CONFIG=.coveragerc COV_CORE_DATAFILE=.coverage.eager ##---------- Build targets ---------------------------------------------------- From 266ac1a2964c39a5e0a29e11700c04300e492ce0 Mon Sep 17 00:00:00 2001 From: sevaho Date: Fri, 28 Feb 2025 14:28:51 +0100 Subject: [PATCH 05/24] Update deps --- Makefile | 2 +- poetry.lock | 191 ++++++++++++++++++++++++++++++++++--------------- pyproject.toml | 5 +- 3 files changed, 138 insertions(+), 60 deletions(-) diff --git a/Makefile b/Makefile index 9942f5a..f0494be 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ ##---------- Variables -------------------------------------------------------- PREFIX = /usr/local # Default installation directory PYTEST_GENERAL_FLAGS := -vvvx --asyncio-mode=auto -PYTEST_COV_FLAGS := --cov=natsapi --cov-append --cov-report=term-missing --cov-fail-under=92 +PYTEST_COV_FLAGS := --cov=natsapi --cov-append --cov-report=term-missing --cov-fail-under=85 PYTEST_COV_ENV := COV_CORE_SOURCE=natsapi COV_CORE_CONFIG=.coveragerc COV_CORE_DATAFILE=.coverage.eager ##---------- Build targets ---------------------------------------------------- diff --git a/poetry.lock b/poetry.lock index 406dcc7..1d6ad96 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,16 @@ # This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + [[package]] name = "bandit" version = "1.8.3" @@ -704,69 +715,135 @@ files = [ [[package]] name = "pydantic" -version = "1.10.21" -description = "Data validation and settings management using python type hints" +version = "2.10.6" +description = "Data validation using Python type hints" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic-1.10.21-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:245e486e0fec53ec2366df9cf1cba36e0bbf066af7cd9c974bbbd9ba10e1e586"}, - {file = "pydantic-1.10.21-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c54f8d4c151c1de784c5b93dfbb872067e3414619e10e21e695f7bb84d1d1fd"}, - {file = "pydantic-1.10.21-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b64708009cfabd9c2211295144ff455ec7ceb4c4fb45a07a804309598f36187"}, - {file = "pydantic-1.10.21-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a148410fa0e971ba333358d11a6dea7b48e063de127c2b09ece9d1c1137dde4"}, - {file = "pydantic-1.10.21-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:36ceadef055af06e7756eb4b871cdc9e5a27bdc06a45c820cd94b443de019bbf"}, - {file = "pydantic-1.10.21-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0501e1d12df6ab1211b8cad52d2f7b2cd81f8e8e776d39aa5e71e2998d0379f"}, - {file = "pydantic-1.10.21-cp310-cp310-win_amd64.whl", hash = "sha256:c261127c275d7bce50b26b26c7d8427dcb5c4803e840e913f8d9df3f99dca55f"}, - {file = "pydantic-1.10.21-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8b6350b68566bb6b164fb06a3772e878887f3c857c46c0c534788081cb48adf4"}, - {file = "pydantic-1.10.21-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:935b19fdcde236f4fbf691959fa5c3e2b6951fff132964e869e57c70f2ad1ba3"}, - {file = "pydantic-1.10.21-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b6a04efdcd25486b27f24c1648d5adc1633ad8b4506d0e96e5367f075ed2e0b"}, - {file = "pydantic-1.10.21-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1ba253eb5af8d89864073e6ce8e6c8dec5f49920cff61f38f5c3383e38b1c9f"}, - {file = "pydantic-1.10.21-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:57f0101e6c97b411f287a0b7cf5ebc4e5d3b18254bf926f45a11615d29475793"}, - {file = "pydantic-1.10.21-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e85834f0370d737c77a386ce505c21b06bfe7086c1c568b70e15a568d9670d"}, - {file = "pydantic-1.10.21-cp311-cp311-win_amd64.whl", hash = "sha256:6a497bc66b3374b7d105763d1d3de76d949287bf28969bff4656206ab8a53aa9"}, - {file = "pydantic-1.10.21-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2ed4a5f13cf160d64aa331ab9017af81f3481cd9fd0e49f1d707b57fe1b9f3ae"}, - {file = "pydantic-1.10.21-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b7693bb6ed3fbe250e222f9415abb73111bb09b73ab90d2d4d53f6390e0ccc1"}, - {file = "pydantic-1.10.21-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185d5f1dff1fead51766da9b2de4f3dc3b8fca39e59383c273f34a6ae254e3e2"}, - {file = "pydantic-1.10.21-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38e6d35cf7cd1727822c79e324fa0677e1a08c88a34f56695101f5ad4d5e20e5"}, - {file = "pydantic-1.10.21-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:1d7c332685eafacb64a1a7645b409a166eb7537f23142d26895746f628a3149b"}, - {file = "pydantic-1.10.21-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c9b782db6f993a36092480eeaab8ba0609f786041b01f39c7c52252bda6d85f"}, - {file = "pydantic-1.10.21-cp312-cp312-win_amd64.whl", hash = "sha256:7ce64d23d4e71d9698492479505674c5c5b92cda02b07c91dfc13633b2eef805"}, - {file = "pydantic-1.10.21-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0067935d35044950be781933ab91b9a708eaff124bf860fa2f70aeb1c4be7212"}, - {file = "pydantic-1.10.21-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5e8148c2ce4894ce7e5a4925d9d3fdce429fb0e821b5a8783573f3611933a251"}, - {file = "pydantic-1.10.21-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4973232c98b9b44c78b1233693e5e1938add5af18042f031737e1214455f9b8"}, - {file = "pydantic-1.10.21-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:662bf5ce3c9b1cef32a32a2f4debe00d2f4839fefbebe1d6956e681122a9c839"}, - {file = "pydantic-1.10.21-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:98737c3ab5a2f8a85f2326eebcd214510f898881a290a7939a45ec294743c875"}, - {file = "pydantic-1.10.21-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0bb58bbe65a43483d49f66b6c8474424d551a3fbe8a7796c42da314bac712738"}, - {file = "pydantic-1.10.21-cp313-cp313-win_amd64.whl", hash = "sha256:e622314542fb48542c09c7bd1ac51d71c5632dd3c92dc82ede6da233f55f4848"}, - {file = "pydantic-1.10.21-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d356aa5b18ef5a24d8081f5c5beb67c0a2a6ff2a953ee38d65a2aa96526b274f"}, - {file = "pydantic-1.10.21-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08caa8c0468172d27c669abfe9e7d96a8b1655ec0833753e117061febaaadef5"}, - {file = "pydantic-1.10.21-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c677aa39ec737fec932feb68e4a2abe142682f2885558402602cd9746a1c92e8"}, - {file = "pydantic-1.10.21-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:79577cc045d3442c4e845df53df9f9202546e2ba54954c057d253fc17cd16cb1"}, - {file = "pydantic-1.10.21-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:b6b73ab347284719f818acb14f7cd80696c6fdf1bd34feee1955d7a72d2e64ce"}, - {file = "pydantic-1.10.21-cp37-cp37m-win_amd64.whl", hash = "sha256:46cffa24891b06269e12f7e1ec50b73f0c9ab4ce71c2caa4ccf1fb36845e1ff7"}, - {file = "pydantic-1.10.21-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:298d6f765e3c9825dfa78f24c1efd29af91c3ab1b763e1fd26ae4d9e1749e5c8"}, - {file = "pydantic-1.10.21-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f2f4a2305f15eff68f874766d982114ac89468f1c2c0b97640e719cf1a078374"}, - {file = "pydantic-1.10.21-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35b263b60c519354afb3a60107d20470dd5250b3ce54c08753f6975c406d949b"}, - {file = "pydantic-1.10.21-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e23a97a6c2f2db88995496db9387cd1727acdacc85835ba8619dce826c0b11a6"}, - {file = "pydantic-1.10.21-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:3c96fed246ccc1acb2df032ff642459e4ae18b315ecbab4d95c95cfa292e8517"}, - {file = "pydantic-1.10.21-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b92893ebefc0151474f682e7debb6ab38552ce56a90e39a8834734c81f37c8a9"}, - {file = "pydantic-1.10.21-cp38-cp38-win_amd64.whl", hash = "sha256:b8460bc256bf0de821839aea6794bb38a4c0fbd48f949ea51093f6edce0be459"}, - {file = "pydantic-1.10.21-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d387940f0f1a0adb3c44481aa379122d06df8486cc8f652a7b3b0caf08435f7"}, - {file = "pydantic-1.10.21-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:266ecfc384861d7b0b9c214788ddff75a2ea123aa756bcca6b2a1175edeca0fe"}, - {file = "pydantic-1.10.21-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61da798c05a06a362a2f8c5e3ff0341743e2818d0f530eaac0d6898f1b187f1f"}, - {file = "pydantic-1.10.21-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a621742da75ce272d64ea57bd7651ee2a115fa67c0f11d66d9dcfc18c2f1b106"}, - {file = "pydantic-1.10.21-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9e3e4000cd54ef455694b8be9111ea20f66a686fc155feda1ecacf2322b115da"}, - {file = "pydantic-1.10.21-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f198c8206640f4c0ef5a76b779241efb1380a300d88b1bce9bfe95a6362e674d"}, - {file = "pydantic-1.10.21-cp39-cp39-win_amd64.whl", hash = "sha256:e7f0cda108b36a30c8fc882e4fc5b7eec8ef584aa43aa43694c6a7b274fb2b56"}, - {file = "pydantic-1.10.21-py3-none-any.whl", hash = "sha256:db70c920cba9d05c69ad4a9e7f8e9e83011abb2c6490e561de9ae24aee44925c"}, - {file = "pydantic-1.10.21.tar.gz", hash = "sha256:64b48e2b609a6c22178a56c408ee1215a7206077ecb8a193e2fda31858b2362a"}, + {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, + {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, ] [package.dependencies] -typing-extensions = ">=4.2.0" +annotated-types = ">=0.6.0" +pydantic-core = "2.27.2" +typing-extensions = ">=4.12.2" [package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.27.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, + {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pyflakes" @@ -1176,4 +1253,4 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "0427c54a46a70c8c640b7a118e9134b09c23663ffa2e9470b8d575cedc9dbaad" +content-hash = "fca41522304ae72a9dde8c62b9a59dc42858c6d1367757ef62553cf2e7c6809f" diff --git a/pyproject.toml b/pyproject.toml index 693741c..6de7b72 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "natsapi" -version = "0.0.5" +version = "0.1.0-alpha.1" description = "A Python microservice framework that speaks nats.io with asyncapi spec generation capability" authors = ["WeGroup NV "] readme = "README.md" @@ -16,7 +16,8 @@ generate-setup-file = false [tool.poetry.dependencies] python = "^3.9" -pydantic = "^1.10.13" +pydantic = ">=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0" + nats-py = {extras = ["nkeys"], version = "^2.2.0"} [tool.poetry.group.dev.dependencies] From 4abbcfe7a324df4835029994994c7a8ba2ffaba9 Mon Sep 17 00:00:00 2001 From: sevaho Date: Fri, 28 Feb 2025 17:31:24 +0100 Subject: [PATCH 06/24] Moved to ruff --- .pre-commit-config.yaml | 82 +++----- Makefile | 5 +- benchmarks/frontend-gateway.py | 2 - examples/client.py | 3 +- examples/minimal.py | 3 +- natsapi/_compat.py | 113 +++++------ natsapi/applications.py | 152 +++++++-------- natsapi/asyncapi/__init__.py | 4 +- natsapi/asyncapi/models.py | 272 +++++++++++++-------------- natsapi/asyncapi/utils.py | 62 +++--- natsapi/client/client.py | 34 ++-- natsapi/client/config.py | 7 +- natsapi/encoders.py | 25 +-- natsapi/models.py | 24 +-- natsapi/plugin.py | 17 +- natsapi/routing.py | 159 ++++++++-------- natsapi/state.py | 8 +- natsapi/types.py | 3 +- natsapi/utils.py | 62 ++---- poetry.lock | 137 ++++++-------- pyproject.toml | 29 +-- shell.nix | 34 ++-- tests/asyncapi/test_generation.py | 12 +- tests/test_applications.py | 2 - tests/test_fastapi.py | 4 +- tests/test_jsonable_encoder.py | 13 +- tests/test_method_type_conversion.py | 6 +- tests/test_models.py | 7 +- tests/test_reload.py | 27 --- tests/test_requests.py | 6 +- tests/test_routing.py | 2 - 31 files changed, 586 insertions(+), 730 deletions(-) delete mode 100644 tests/test_reload.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c6a928f..f392b5b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,63 +1,33 @@ exclude: ^(blib2to3/|profiling/|tests/data/|\.mypy/|\.tox/) repos: - - repo: https://github.com/pre-commit/pre-commit-hooks.git - rev: v2.1.0 +- repo: git@github.com:pre-commit/pre-commit-hooks + rev: v4.4.0 hooks: - - id: detect-private-key - - id: requirements-txt-fixer - - id: check-case-conflict - - id: check-json - - id: debug-statements - - id: check-merge-conflict - - id: check-symlinks - - id: end-of-file-fixer - - id: pretty-format-json + - id: requirements-txt-fixer + - id: check-case-conflict + - id: check-json + - id: debug-statements + - id: check-merge-conflict + - id: check-symlinks + - id: end-of-file-fixer + - id: check-added-large-files + - id: check-toml + - id: trailing-whitespace + - id: pretty-format-json args: - - --autofix - - id: trailing-whitespace - - repo: local + - --autofix + - id: trailing-whitespace +- repo: local hooks: - - id: black - args: ["-S", "-l", "120"] - name: black - language: system - entry: black - types: [python] - - id: flake8 - args: ["--ignore", "F722,W503,E501,E741,W605,E203,C901,F541", "."] - name: flake8 - language: system - entry: flake8 - types: [python] - - repo: https://github.com/humitos/mirrors-autoflake.git - rev: v1.1 + - id: black + name: black + language: system + entry: black + types: [python] + +- repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.0.254 hooks: - - id: autoflake + - id: ruff args: - [ - "--in-place", - "--remove-all-unused-imports", - "--remove-unused-variable", - ] - - repo: https://github.com/timothycrosley/isort.git - rev: 5.1.4 - hooks: - - id: isort - args: - [ - "--multi-line=3", - "--trailing-comma", - "--force-grid-wrap=0", - "--combine-as", - "--line-width", - "120", - "--apply", - "--thirdparty", - "pydantic", - "natsapi", - ] - name: isort - entry: isort - require_serial: true - language: python - types: [python] + - --fix diff --git a/Makefile b/Makefile index f0494be..cb1a03b 100644 --- a/Makefile +++ b/Makefile @@ -20,10 +20,7 @@ testr: ## Run tests with entr find natsapi tests | entr -r poetry run pytest --disable-warnings -vvvx lint: ## Lint checks - poetry run flake8 . - -isort: ## Sort checks - poetry run isort + poetry run ruff check . format: ## Format checks poetry run black . diff --git a/benchmarks/frontend-gateway.py b/benchmarks/frontend-gateway.py index 81b3694..b0dec6a 100644 --- a/benchmarks/frontend-gateway.py +++ b/benchmarks/frontend-gateway.py @@ -4,8 +4,6 @@ import httpx import nats - - from fastapi import FastAPI, Request from pydantic import BaseModel diff --git a/examples/client.py b/examples/client.py index b0039a5..fb1061f 100644 --- a/examples/client.py +++ b/examples/client.py @@ -1,6 +1,7 @@ -from natsapi import NatsAPI import asyncio +from natsapi import NatsAPI + async def main(): app = await NatsAPI("client").startup() diff --git a/examples/minimal.py b/examples/minimal.py index d181bed..5a06b14 100644 --- a/examples/minimal.py +++ b/examples/minimal.py @@ -1,6 +1,7 @@ -from natsapi import NatsAPI, SubjectRouter from pydantic import BaseModel +from natsapi import NatsAPI, SubjectRouter + class Message(BaseModel): message: str diff --git a/natsapi/_compat.py b/natsapi/_compat.py index cc2b0f4..0b18bda 100644 --- a/natsapi/_compat.py +++ b/natsapi/_compat.py @@ -1,44 +1,36 @@ -from typing_extensions import Annotated +from collections.abc import Sequence from dataclasses import dataclass -from natsapi.asyncapi.constants import REF_PREFIX from enum import Enum from functools import lru_cache from typing import ( + Annotated, Any, - Optional, Literal, - Dict, - List, - Sequence, - Set, - Tuple, - Type, - Union, ) + from pydantic import BaseModel from pydantic.version import VERSION as PYDANTIC_VERSION +from natsapi.asyncapi.constants import REF_PREFIX + PYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(".")[:2]) PYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2 -ModelNameMap = Dict[Union[Type[BaseModel], Type[Enum]], str] +ModelNameMap = dict[type[BaseModel] | type[Enum], str] if PYDANTIC_V2: - from pydantic.fields import FieldInfo + from pydantic import ( + RootModel, # noqa + TypeAdapter, + ) from pydantic import ValidationError as ValidationError - from pydantic import RootModel # noqa - from pydantic_settings import BaseSettings - - from pydantic.deprecated.json import ENCODERS_BY_TYPE - - from pydantic import TypeAdapter - from pydantic._internal._utils import lenient_issubclass as lenient_issubclass - - from pydantic_core import PydanticUndefined, PydanticUndefinedType - + from pydantic.deprecated.json import ENCODERS_BY_TYPE + from pydantic.fields import FieldInfo from pydantic.json_schema import GenerateJsonSchema as GenerateJsonSchema from pydantic.json_schema import JsonSchemaValue as JsonSchemaValue + from pydantic_core import PydanticUndefined, PydanticUndefinedType + from pydantic_settings import BaseSettings Undefined = PydanticUndefined UndefinedType = PydanticUndefinedType @@ -48,7 +40,7 @@ class ModelField: field_info: FieldInfo name: str mode: Literal["validation", "serialization"] = "validation" - sub_fields: Optional[str] = None + sub_fields: str | None = None @property def alias(self) -> str: @@ -78,10 +70,10 @@ def get_default(self) -> Any: def validate( self, value: Any, - values: Dict[str, Any] = {}, # noqa: B006 + values: dict[str, Any] = {}, # noqa: B006 *, - loc: Tuple[Union[int, str], ...] = (), - ) -> Tuple[Any, Union[List[Dict[str, Any]], None]]: + loc: tuple[int | str, ...] = (), + ) -> tuple[Any, list[dict[str, Any]] | None]: try: return ( self._type_adapter.validate_python(value, from_attributes=True), @@ -101,7 +93,6 @@ def serialize( exclude_none: bool = False, ) -> Any: # What calls this code passes a value that already called - # self._type_adapter.validate_python(value) return self._type_adapter.dump_python( value, mode=mode, @@ -116,49 +107,47 @@ def __hash__(self) -> int: # ModelField to its JSON Schema. return id(self) - def _normalize_errors(errors: Sequence[Any]) -> List[Dict[str, Any]]: + def _normalize_errors(errors: Sequence[Any]) -> list[dict[str, Any]]: return errors # type: ignore[return-value] - def get_model_fields(model: Type[BaseModel]) -> List[ModelField]: + def get_model_fields(model: type[BaseModel]) -> list[ModelField]: return [ModelField(field_info=field_info, name=name) for name, field_info in model.model_fields.items()] - def get_compat_model_name_map(fields: List[ModelField]): + def get_compat_model_name_map(fields: list[ModelField]): return {} def get_definitions( *, - fields: List[ModelField], + fields: list[ModelField], schema_generator: GenerateJsonSchema, model_name_map: ModelNameMap, separate_input_output_schemas: bool = True, - ) -> Tuple[ - Dict[Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue], - Dict[str, Dict[str, Any]], + ) -> tuple[ + dict[tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue], + dict[str, dict[str, Any]], ]: - override_mode: Union[Literal["validation"], None] = None if separate_input_output_schemas else "validation" + override_mode: Literal["validation"] | None = None if separate_input_output_schemas else "validation" inputs = [(field, override_mode or field.mode, field._type_adapter.core_schema) for field in fields] field_mapping, definitions = schema_generator.generate_definitions(inputs=inputs) return field_mapping, definitions # type: ignore[return-value] else: - from pydantic.json import ENCODERS_BY_TYPE # noqa: F401 - - from pydantic import BaseModel - - from pydantic.fields import ( # type: ignore[no-redef,attr-defined] - ModelField as ModelField, # noqa: F401 + from pydantic import ( + BaseModel, + BaseSettings, # noqa: F401 ) from pydantic.error_wrappers import ( # type: ignore[no-redef] ErrorWrapper as ErrorWrapper, # noqa: F401 ) - from pydantic import BaseSettings # noqa: F401 - + from pydantic.fields import ( # type: ignore[no-redef,attr-defined] + ModelField as ModelField, # noqa: F401 + ) + from pydantic.json import ENCODERS_BY_TYPE # noqa: F401 from pydantic.schema import ( get_flat_models_from_fields, get_model_name_map, model_process_schema, ) - from pydantic.utils import ( # noqa lenient_issubclass as lenient_issubclass, # noqa: F401 ) @@ -167,15 +156,15 @@ class RootModel(BaseModel): __root__: str GetJsonSchemaHandler = Any # type: ignore[assignment,misc] - JsonSchemaValue = Dict[str, Any] # type: ignore[misc] + JsonSchemaValue = dict[str, Any] # type: ignore[misc] CoreSchema = Any # type: ignore[assignment,misc] @dataclass class GenerateJsonSchema: # type: ignore[no-redef] ref_template: str - def _normalize_errors(errors: Sequence[Any]) -> List[Dict[str, Any]]: - use_errors: List[Any] = [] + def _normalize_errors(errors: Sequence[Any]) -> list[dict[str, Any]]: + use_errors: list[Any] = [] for error in errors: if isinstance(error, ErrorWrapper): new_errors = ValidationError(errors=[error]).errors() # type: ignore[call-arg] @@ -186,22 +175,22 @@ def _normalize_errors(errors: Sequence[Any]) -> List[Dict[str, Any]]: use_errors.append(error) return use_errors - def get_model_fields(model: Type[BaseModel]) -> List[ModelField]: + def get_model_fields(model: type[BaseModel]) -> list[ModelField]: return list(model.__fields__.values()) # type: ignore[attr-defined] - def get_compat_model_name_map(fields: List[ModelField]): + def get_compat_model_name_map(fields: list[ModelField]): models = get_flat_models_from_fields(fields, known_models=set()) return get_model_name_map(models) # type: ignore[no-any-return] def get_model_definitions( *, - flat_models: Set[Union[Type[BaseModel], Type[Enum]]], - model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str], - ) -> Dict[str, Any]: - definitions: Dict[str, Dict[str, Any]] = {} + flat_models: set[type[BaseModel] | type[Enum]], + model_name_map: dict[type[BaseModel] | type[Enum], str], + ) -> dict[str, Any]: + definitions: dict[str, dict[str, Any]] = {} for model in flat_models: m_schema, m_definitions, m_nested_models = model_process_schema( - model, model_name_map=model_name_map, ref_prefix=REF_PREFIX + model, model_name_map=model_name_map, ref_prefix=REF_PREFIX, ) definitions.update(m_definitions) model_name = model_name_map[model] @@ -212,22 +201,22 @@ def get_model_definitions( def get_definitions( *, - fields: List[ModelField], + fields: list[ModelField], schema_generator: GenerateJsonSchema, model_name_map: ModelNameMap, separate_input_output_schemas: bool = True, - ) -> Tuple[ - Dict[Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue], - Dict[str, Dict[str, Any]], + ) -> tuple[ + dict[tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue], + dict[str, dict[str, Any]], ]: models = get_flat_models_from_fields(fields, known_models=set()) return {}, get_model_definitions(flat_models=models, model_name_map=model_name_map) def _regenerate_error_with_loc( - *, errors: Sequence[Any], loc_prefix: Tuple[Union[str, int], ...] -) -> List[Dict[str, Any]]: - updated_loc_errors: List[Any] = [ + *, errors: Sequence[Any], loc_prefix: tuple[str | int, ...], +) -> list[dict[str, Any]]: + updated_loc_errors: list[Any] = [ {**err, "loc": loc_prefix + err.get("loc", ())} for err in _normalize_errors(errors) ] @@ -235,5 +224,5 @@ def _regenerate_error_with_loc( @lru_cache -def get_cached_model_fields(model: Type[BaseModel]) -> List[ModelField]: +def get_cached_model_fields(model: type[BaseModel]) -> list[ModelField]: return get_model_fields(model) diff --git a/natsapi/applications.py b/natsapi/applications.py index 90dfbd7..a842ec4 100644 --- a/natsapi/applications.py +++ b/natsapi/applications.py @@ -1,9 +1,8 @@ import asyncio import inspect import signal - - -from typing import Any, Callable, Dict, List, Optional, Set, Type, Union +from collections.abc import Callable +from typing import Any from pydantic import BaseModel, ValidationError @@ -20,27 +19,22 @@ from natsapi.types import DecoratedCallable -class NatsAPI(object): +class NatsAPI: def __init__( self, root_path: str, *, app: Any = None, - client_config: Optional[Config] = None, - rpc_methods: Optional[List[str]] = None, - exception_handlers: Optional[ - Dict[ - Type[Exception], - Callable[[Type[Exception]], JsonRPCException], - ] - ] = None, + client_config: Config | None = None, + rpc_methods: list[str] | None = None, + exception_handlers: dict[type[Exception], Callable[[type[Exception]], JsonRPCException]] | None = None, title: str = "NatsAPI", version: str = "0.1.0", description: str = None, - tags: Optional[List[Dict[str, Any]]] = None, - servers: Optional[Dict[str, Union[str, Any]]] = None, - domain_errors: Optional[Dict[str, Any]] = None, - external_docs: Optional[Dict[str, Any]] = None, + tags: list[dict[str, Any]] | None = None, + servers: dict[str, str | Any] | None = None, + domain_errors: dict[str, Any] | None = None, + external_docs: dict[str, Any] | None = None, ): """ Parameters @@ -48,7 +42,7 @@ def __init__( root_path: str The path that every application-specific subject app: FastAPI Must be a FastAPI instance or None. If none the app is the NatsAPI instance itself """ - self.routes: Dict[str, Request] = {} + self.routes: dict[str, Request] = {} self.root_path = root_path self._root_paths = [root_path] self.rpc_methods = rpc_methods if rpc_methods else None @@ -60,15 +54,15 @@ def __init__( self.asyncapi_tags = tags or [] self.asyncapi_version = "2.0.0" self.domain_errors: Errors = domain_errors - self.asyncapi_schema: Optional[Dict[str, Any]] = None + self.asyncapi_schema: dict[str, Any] | None = None self.nc: NatsClient = None - self.subs: Set[Sub] = set() - self.pubs: Set[Pub] = set() + self.subs: set[Sub] = set() + self.pubs: set[Pub] = set() self.state = State() self._on_startup_method = None self._on_shutdown_method = None self.client_config = client_config or default_config - self._exception_handlers: Dict[Type[Exception], Callable[[Type[Exception]], JsonRPCException]] = ( + self._exception_handlers: dict[type[Exception], Callable[[type[Exception]], JsonRPCException]] = ( {} if exception_handlers is None else dict(exception_handlers) ) self._exception_handlers.setdefault(JsonRPCException, handle_jsonrpc_exception) @@ -97,7 +91,7 @@ async def __aenter__(self): async def __aexit__(self, *args): await self.shutdown() - def include_router(self, router: Type[SubjectRouter], root_path: str = None) -> None: + def include_router(self, router: type[SubjectRouter], root_path: str = None) -> None: current_root_path = root_path or self.root_path if current_root_path not in self._root_paths: self._root_paths.append(current_root_path) @@ -117,7 +111,7 @@ def include_router(self, router: Type[SubjectRouter], root_path: str = None) -> self.subs = self.subs | router.subs self.pubs = self.pubs | router.pubs - def generate_asyncapi(self) -> Dict[str, Any]: + def generate_asyncapi(self) -> dict[str, Any]: if not self.asyncapi_schema: self.asyncapi_schema = get_asyncapi( title=self.title, @@ -133,7 +127,7 @@ def generate_asyncapi(self) -> Dict[str, Any]: ) return self.asyncapi_schema - def _add_asyncapi_route(self) -> Dict[str, Any]: + def _add_asyncapi_route(self) -> dict[str, Any]: """ Adds default route to retrieve the asyncapi schema. """ @@ -156,7 +150,7 @@ async def startup(self, loop=None): self._listen_to_signals() self.nc = NatsClient( - self.routes, app=self.app, config=self.client_config, exception_handlers=self._exception_handlers + self.routes, app=self.app, config=self.client_config, exception_handlers=self._exception_handlers, ) await self.nc.connect() logger.info("Connected to NATS server") @@ -170,7 +164,7 @@ async def startup(self, loop=None): for path in self._root_paths: sub_path = ".".join([path, ">"]) await self.nc.root_path_subscribe( - sub_path, cb=self.nc.handle_request, queue=self.client_config.subscribe.queue + sub_path, cb=self.nc.handle_request, queue=self.client_config.subscribe.queue, ) self.include_subs( [ @@ -179,8 +173,8 @@ async def startup(self, loop=None): queue=self.client_config.subscribe.queue, summary=f"Sub to root path {sub_path}", tags=["automatic subs"], - ) - ] + ), + ], ) logger.info(f"Subscribed to {sub_path}") self._add_asyncapi_route() @@ -229,14 +223,14 @@ def add_request( subject: str, endpoint: Callable[..., Any], *, - result=Type[Any], - skip_validation: Optional[bool] = False, - description: Optional[str] = None, - deprecated: Optional[bool] = None, - tags: Optional[List[str]] = None, - summary: Optional[str] = None, - suggested_timeout: Optional[float] = None, - include_schema: Optional[bool] = True, + result=type[Any], + skip_validation: bool | None = False, + description: str | None = None, + deprecated: bool | None = None, + tags: list[str] | None = None, + summary: str | None = None, + suggested_timeout: float | None = None, + include_schema: bool | None = True, ) -> None: request = Request( subject=subject, @@ -266,12 +260,12 @@ def add_publish( subject: str, endpoint: Callable[..., Any], *, - skip_validation: Optional[bool] = False, - description: Optional[str] = None, - deprecated: Optional[bool] = None, - tags: Optional[List[str]] = None, - summary: Optional[str] = None, - include_schema: Optional[bool] = True, + skip_validation: bool | None = False, + description: str | None = None, + deprecated: bool | None = None, + tags: list[str] | None = None, + summary: str | None = None, + include_schema: bool | None = True, ) -> None: publish = Publish( subject=subject, @@ -298,14 +292,14 @@ def request( self, subject: str, *, - result=Type[Any], - skip_validation: Optional[bool] = False, - description: Optional[str] = None, - deprecated: Optional[bool] = None, - tags: Optional[List[str]] = None, - summary: Optional[str] = None, - suggested_timeout: Optional[float] = None, - include_schema: Optional[bool] = True, + result=type[Any], + skip_validation: bool | None = False, + description: str | None = None, + deprecated: bool | None = None, + tags: list[str] | None = None, + summary: str | None = None, + suggested_timeout: float | None = None, + include_schema: bool | None = True, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_request( @@ -328,12 +322,12 @@ def publish( self, subject: str, *, - skip_validation: Optional[bool] = False, - description: Optional[str] = None, - deprecated: Optional[bool] = None, - tags: Optional[List[str]] = None, - summary: Optional[str] = None, - include_schema: Optional[bool] = True, + skip_validation: bool | None = False, + description: str | None = None, + deprecated: bool | None = None, + tags: list[str] | None = None, + summary: str | None = None, + include_schema: bool | None = True, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_publish( @@ -355,10 +349,10 @@ def add_pub( subject: str, params: BaseModel, *, - summary: Optional[str] = None, - description: Optional[str] = None, - tags: Optional[List[str]] = None, - externalDocs: Optional[ExternalDocumentation] = None, + summary: str | None = None, + description: str | None = None, + tags: list[str] | None = None, + externalDocs: ExternalDocumentation | None = None, ) -> None: """ Include pub in asyncapi schema @@ -377,11 +371,11 @@ def pub( self, subject: str, *, - params=Type[Any], - description: Optional[str] = None, - tags: Optional[List[str]] = None, - summary: Optional[str] = None, - externalDocs: Optional[ExternalDocumentation] = None, + params=type[Any], + description: str | None = None, + tags: list[str] | None = None, + summary: str | None = None, + externalDocs: ExternalDocumentation | None = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_pub( @@ -396,7 +390,7 @@ def decorator(func: DecoratedCallable) -> DecoratedCallable: return decorator - def include_subs(self, subs: List[Sub]): + def include_subs(self, subs: list[Sub]): for sub in subs: self.subs.add(sub) @@ -404,11 +398,11 @@ def add_sub( self, subject: str, *, - queue: Optional[str] = None, - summary: Optional[str] = None, - description: Optional[str] = None, - tags: Optional[List[str]] = None, - externalDocs: Optional[ExternalDocumentation] = None, + queue: str | None = None, + summary: str | None = None, + description: str | None = None, + tags: list[str] | None = None, + externalDocs: ExternalDocumentation | None = None, ) -> None: """ Include sub in asyncapi schema @@ -427,11 +421,11 @@ def sub( self, subject: str, *, - queue: Optional[str] = None, - description: Optional[str] = None, - tags: Optional[List[str]] = None, - summary: Optional[str] = None, - externalDocs: Optional[ExternalDocumentation] = None, + queue: str | None = None, + description: str | None = None, + tags: list[str] | None = None, + summary: str | None = None, + externalDocs: ExternalDocumentation | None = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_sub( @@ -446,18 +440,18 @@ def decorator(func: DecoratedCallable) -> DecoratedCallable: return decorator - def include_pubs(self, pubs: List[Pub]): + def include_pubs(self, pubs: list[Pub]): for pub in pubs: self.pubs.add(pub) def add_exception_handler( self, - exc_class: Type[Exception], + exc_class: type[Exception], handler: Callable[[Exception], JsonRPCException], ) -> None: self._exception_handlers[exc_class] = handler - def exception_handler(self, exc_class: Type[Exception]) -> Callable: + def exception_handler(self, exc_class: type[Exception]) -> Callable: def decorator(func: Callable[[Exception], JsonRPCException]) -> Callable: self.add_exception_handler(exc_class, func) return func diff --git a/natsapi/asyncapi/__init__.py b/natsapi/asyncapi/__init__.py index 30146e0..ffc347f 100644 --- a/natsapi/asyncapi/__init__.py +++ b/natsapi/asyncapi/__init__.py @@ -1,4 +1,4 @@ -from typing import Any, List +from typing import Any from pydantic import BaseModel, validator @@ -11,7 +11,7 @@ class Config: upper_bound: int lower_bound: int - errors: List[Any] + errors: list[Any] @validator("lower_bound") def upper_bigger_than_lower(v, values): diff --git a/natsapi/asyncapi/models.py b/natsapi/asyncapi/models.py index 10ffec0..e0956c8 100644 --- a/natsapi/asyncapi/models.py +++ b/natsapi/asyncapi/models.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional, Union +from typing import Any from pydantic import BaseModel, Field, validator @@ -6,81 +6,81 @@ class ExternalDocumentation(BaseModel): - description: Optional[str] + description: str | None url: str class Discriminator(BaseModel): - propertyName: Dict[str, Any] - mapping: Optional[Dict[str, str]] = None + propertyName: dict[str, Any] + mapping: dict[str, str] | None = None class SchemaBase(BaseModel): - ref: Optional[str] = Field(None, alias="$ref") - title: Optional[str] = None - multipleOf: Optional[float] = None - maximum: Optional[float] = None - exclusiveMaximum: Optional[float] = None - minimum: Optional[float] = None - exclusiveMinimum: Optional[float] = None - maxLength: Optional[int] = Field(None, gte=0) - minLength: Optional[int] = Field(None, gte=0) - pattern: Optional[str] = None - maxItems: Optional[int] = Field(None, gte=0) - minItems: Optional[int] = Field(None, gte=0) - uniqueItems: Optional[bool] = None - maxProperties: Optional[int] = Field(None, gte=0) - minProperties: Optional[int] = Field(None, gte=0) - required: Optional[List[str]] = None - enum: Optional[List[Any]] = None - type: Optional[str] = None - allOf: Optional[List[Any]] = None - oneOf: Optional[List[Any]] = None - anyOf: Optional[List[Any]] = None - not_: Optional[Any] = Field(None, alias="not") - items: Optional[Any] = None - properties: Optional[Dict[str, Any]] = None - additionalProperties: Optional[Union[Dict[str, Any], bool]] = None - description: Optional[str] = None - format: Optional[str] = None - default: Optional[Any] = None - nullable: Optional[bool] = None - discriminator: Optional[Discriminator] = None - readOnly: Optional[bool] = None - writeOnly: Optional[bool] = None - externalDocs: Optional[ExternalDocumentation] = None - example: Optional[Any] = None - deprecated: Optional[bool] = None + ref: str | None = Field(None, alias="$ref") + title: str | None = None + multipleOf: float | None = None + maximum: float | None = None + exclusiveMaximum: float | None = None + minimum: float | None = None + exclusiveMinimum: float | None = None + maxLength: int | None = Field(None, gte=0) + minLength: int | None = Field(None, gte=0) + pattern: str | None = None + maxItems: int | None = Field(None, gte=0) + minItems: int | None = Field(None, gte=0) + uniqueItems: bool | None = None + maxProperties: int | None = Field(None, gte=0) + minProperties: int | None = Field(None, gte=0) + required: list[str] | None = None + enum: list[Any] | None = None + type: str | None = None + allOf: list[Any] | None = None + oneOf: list[Any] | None = None + anyOf: list[Any] | None = None + not_: Any | None = Field(None, alias="not") + items: Any | None = None + properties: dict[str, Any] | None = None + additionalProperties: dict[str, Any] | bool | None = None + description: str | None = None + format: str | None = None + default: Any | None = None + nullable: bool | None = None + discriminator: Discriminator | None = None + readOnly: bool | None = None + writeOnly: bool | None = None + externalDocs: ExternalDocumentation | None = None + example: Any | None = None + deprecated: bool | None = None class Schema(SchemaBase): - allOf: Optional[List[SchemaBase]] = None - oneOf: Optional[List[SchemaBase]] = None - anyOf: Optional[List[SchemaBase]] = None - not_: Optional[SchemaBase] = Field(None, alias="not") - items: Optional[SchemaBase] = None - properties: Optional[Dict[str, SchemaBase]] = None - additionalProperties: Optional[Union[Dict[str, Any], bool]] = None + allOf: list[SchemaBase] | None = None + oneOf: list[SchemaBase] | None = None + anyOf: list[SchemaBase] | None = None + not_: SchemaBase | None = Field(None, alias="not") + items: SchemaBase | None = None + properties: dict[str, SchemaBase] | None = None + additionalProperties: dict[str, Any] | bool | None = None class Contact(BaseModel): - name: Optional[str] - url: Optional[str] - email: Optional[str] + name: str | None + url: str | None + email: str | None class License(BaseModel): name: str - url: Optional[str] + url: str | None class Info(BaseModel): title: str version: str - description: Optional[str] = None - termsOfService: Optional[str] = None - contact: Optional[Contact] = None - licence: Optional[License] = None + description: str | None = None + termsOfService: str | None = None + contact: Contact | None = None + licence: License | None = None class Reference(BaseModel): @@ -89,8 +89,8 @@ class Reference(BaseModel): class Tag(BaseModel): name: str - description: Optional[str] = None - externalDocs: Optional[ExternalDocumentation] = None + description: str | None = None + externalDocs: ExternalDocumentation | None = None # TODO: Add nats bindings @@ -99,7 +99,7 @@ class NatsBindings(BaseModel): class BindingsBase(BaseModel): - nats: Optional[Union[NatsBindings, Reference]] + nats: NatsBindings | Reference | None class ServerBindings(BindingsBase): @@ -120,113 +120,113 @@ class MessageBindings(BindingsBase): class CorrelationId(BaseModel): location: str - description: Optional[str] + description: str | None class MessageTrait(BaseModel): - headers: Optional[Union[Schema, Reference]] - correlationId: Optional[Union[CorrelationId, Reference]] - schemaFormat: Optional[str] - contentType: Optional[str] - name: Optional[str] - title: Optional[str] - summary: Optional[str] - description: Optional[str] - tags: Optional[List[Tag]] - externalDocs: Optional[ExternalDocumentation] - bindings: Optional[Union[MessageBindings, Reference]] - examples: Optional[Dict[str, Any]] + headers: Schema | Reference | None + correlationId: CorrelationId | Reference | None + schemaFormat: str | None + contentType: str | None + name: str | None + title: str | None + summary: str | None + description: str | None + tags: list[Tag] | None + externalDocs: ExternalDocumentation | None + bindings: MessageBindings | Reference | None + examples: dict[str, Any] | None class Message(BaseModel): - headers: Optional[Union[Schema, Reference]] = None - payload: Optional[Any] = None - correlationId: Optional[Union[CorrelationId, Reference]] = None - schemaFormat: Optional[str] = None - contentType: Optional[str] = None - name: Optional[str] = None - title: Optional[str] = None - summary: Optional[str] = None - description: Optional[str] = None - tags: Optional[List[Tag]] = None - externalDocs: Optional[ExternalDocumentation] = None - bindings: Optional[Union[MessageBindings, Reference]] = None - examples: Optional[Dict[str, Any]] = None - traits: Optional[Union[MessageTrait, Reference]] = None + headers: Schema | Reference | None = None + payload: Any | None = None + correlationId: CorrelationId | Reference | None = None + schemaFormat: str | None = None + contentType: str | None = None + name: str | None = None + title: str | None = None + summary: str | None = None + description: str | None = None + tags: list[Tag] | None = None + externalDocs: ExternalDocumentation | None = None + bindings: MessageBindings | Reference | None = None + examples: dict[str, Any] | None = None + traits: MessageTrait | Reference | None = None class OperationTrait(BaseModel): - operationId: Optional[str] = None - summary: Optional[str] = None - description: Optional[str] = None - tags: Optional[List[Tag]] = None - externalDocs: Optional[ExternalDocumentation] = None - bindings: Optional[Union[OperationBindings, Reference]] = None + operationId: str | None = None + summary: str | None = None + description: str | None = None + tags: list[Tag] | None = None + externalDocs: ExternalDocumentation | None = None + bindings: OperationBindings | Reference | None = None class Operation(BaseModel): - operationId: Optional[str] = None - summary: Optional[str] = None - description: Optional[str] = None - tags: Optional[List[Tag]] = None - externalDocs: Optional[ExternalDocumentation] = None - bindings: Optional[Union[OperationBindings, Reference]] = None - traits: Optional[List[Union[OperationTrait, Reference]]] = None - message: Optional[Union[Message, Reference]] = None + operationId: str | None = None + summary: str | None = None + description: str | None = None + tags: list[Tag] | None = None + externalDocs: ExternalDocumentation | None = None + bindings: OperationBindings | Reference | None = None + traits: list[OperationTrait | Reference] | None = None + message: Message | Reference | None = None class SubscribeOperation(Operation): - queue: Optional[str] = Field(None, alias="x-queue") + queue: str | None = Field(None, alias="x-queue") class RequestOperation(Operation): - replies: Optional[List[Union[Message, Reference]]] = None - suggestedTimeout: Optional[float] = Field(None, alias="x-suggested-timeout") + replies: list[Message | Reference] | None = None + suggestedTimeout: float | None = Field(None, alias="x-suggested-timeout") class Parameter(BaseModel): - description: Optional[str] = None - schema_: Union[Dict[str, Any], Reference] = Field(..., alias="schema") - location: Optional[str] = None + description: str | None = None + schema_: dict[str, Any] | Reference = Field(..., alias="schema") + location: str | None = None class ChannelItem(BaseModel): - description: Optional[str] = None - subscribe: Optional[SubscribeOperation] = None - publish: Optional[Operation] = None - request: Optional[RequestOperation] = None - parameters: Optional[Dict[str, Union[Parameter, Reference]]] = None - bindings: Optional[Union[ChannelBindings, Reference]] = None + description: str | None = None + subscribe: SubscribeOperation | None = None + publish: Operation | None = None + request: RequestOperation | None = None + parameters: dict[str, Parameter | Reference] | None = None + bindings: ChannelBindings | Reference | None = None deprecated: bool | None = None class ServerVariable(BaseModel): - enum: Optional[List[str]] = None - default: Optional[str] = None - description: Optional[str] = None - examples: Optional[List[str]] = None + enum: list[str] | None = None + default: str | None = None + description: str | None = None + examples: list[str] | None = None class Server(BaseModel): url: str protocol: str protocolVersion: str | None - description: Optional[str] - variables: Optional[Dict[str, ServerVariable]] - bindings: Optional[Union[ServerBindings, Reference]] = None + description: str | None + variables: dict[str, ServerVariable] | None + bindings: ServerBindings | Reference | None = None class Components(BaseModel): - schemas: Optional[Dict[str, Union[Schema, Reference]]] = None - messages: Optional[Dict[str, Union[Message, Reference]]] = None - parameters: Optional[Dict[str, Union[Dict[str, Parameter], Reference]]] = None - correlationIds: Optional[Dict[str, Union[CorrelationId, Reference]]] = None - operationTraits: Optional[Dict[str, Union[OperationTrait, Reference]]] = None - messageTraits: Optional[Dict[str, Union[MessageTrait, Reference]]] = None - serverBindings: Optional[Dict[str, Union[ServerBindings, Reference]]] = None - channelBindings: Optional[Dict[str, Union[ChannelBindings, Reference]]] = None - operationBindings: Optional[Dict[str, Union[OperationBindings, Reference]]] = None - messageBindings: Optional[Dict[str, Union[MessageBindings, Reference]]] = None + schemas: dict[str, Schema | Reference] | None = None + messages: dict[str, Message | Reference] | None = None + parameters: dict[str, dict[str, Parameter] | Reference] | None = None + correlationIds: dict[str, CorrelationId | Reference] | None = None + operationTraits: dict[str, OperationTrait | Reference] | None = None + messageTraits: dict[str, MessageTrait | Reference] | None = None + serverBindings: dict[str, ServerBindings | Reference] | None = None + channelBindings: dict[str, ChannelBindings | Reference] | None = None + operationBindings: dict[str, OperationBindings | Reference] | None = None + messageBindings: dict[str, MessageBindings | Reference] | None = None class Range(BaseModel): @@ -241,17 +241,17 @@ def upper_bigger_than_lower(v, values): class Errors(BaseModel): range: Range = Field(..., alias="range") - items: List[Any] + items: list[Any] class AsyncAPI(BaseModel): asyncapi: str - id: Optional[str] = None + id: str | None = None info: Info - servers: Optional[Dict[str, Server]] = None - defaultContentType: Optional[str] = "application/json" - channels: Optional[Dict[str, Union[ChannelItem, Reference]]] = None - components: Optional[Components] = None - tags: Optional[List[Tag]] = None - externalDocs: Optional[ExternalDocumentation] = None - errors: Optional[Errors] = None + servers: dict[str, Server] | None = None + defaultContentType: str | None = "application/json" + channels: dict[str, ChannelItem | Reference] | None = None + components: Components | None = None + tags: list[Tag] | None = None + externalDocs: ExternalDocumentation | None = None + errors: Errors | None = None diff --git a/natsapi/asyncapi/utils.py b/natsapi/asyncapi/utils.py index 5365f0a..7df6359 100644 --- a/natsapi/asyncapi/utils.py +++ b/natsapi/asyncapi/utils.py @@ -1,19 +1,18 @@ import typing - - +from collections.abc import Sequence from enum import Enum -from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union +from typing import Any from pydantic import BaseModel + from natsapi._compat import ( + GenerateJsonSchema, ModelField, - lenient_issubclass, get_cached_model_fields, get_compat_model_name_map, get_definitions, - GenerateJsonSchema, + lenient_issubclass, ) - from natsapi.asyncapi.constants import REF_PREFIX, REF_TEMPLATE from natsapi.encoders import jsonable_encoder from natsapi.models import JsonRPCError @@ -23,7 +22,7 @@ from .models import AsyncAPI -def _get_flat_fields_from_params(fields: List[ModelField]) -> List[ModelField]: +def _get_flat_fields_from_params(fields: list[ModelField]) -> list[ModelField]: if not fields: return fields first_field = fields[0] @@ -34,10 +33,10 @@ def _get_flat_fields_from_params(fields: List[ModelField]) -> List[ModelField]: return fields -def get_fields_from_routes(routes: Sequence[Request], pubs: Sequence[Pub]) -> Set[Union[Type[BaseModel], Type[Enum]]]: - replies_from_routes: Set[ModelField] = set() - requests_from_routes: Set[ModelField] = set() - messages_from_pubs: Set[ModelField] = set() +def get_fields_from_routes(routes: Sequence[Request], pubs: Sequence[Pub]) -> set[type[BaseModel] | type[Enum]]: + replies_from_routes: set[ModelField] = set() + requests_from_routes: set[ModelField] = set() + messages_from_pubs: set[ModelField] = set() for route in routes: if getattr(route, "include_schema", True) and isinstance(route, Request): if route.result: @@ -55,7 +54,7 @@ def get_fields_from_routes(routes: Sequence[Request], pubs: Sequence[Pub]) -> Se return fields -def get_flat_response_models(r) -> List[Type[BaseModel]]: +def get_flat_response_models(r) -> list[type[BaseModel]]: """ Returns flattened collection of response models of a route. If the response models are of typing.Union, a list of possible response models is returned. @@ -68,8 +67,8 @@ def get_flat_response_models(r) -> List[Type[BaseModel]]: return [r] -def get_asyncapi_request_operation_metadata(operation: Request) -> Dict[str, Any]: - metadata: Dict[str, Any] = {} +def get_asyncapi_request_operation_metadata(operation: Request) -> dict[str, Any]: + metadata: dict[str, Any] = {} metadata["summary"] = operation.summary.replace("_", " ").title() metadata["description"] = operation.description @@ -83,7 +82,7 @@ def get_asyncapi_request_operation_metadata(operation: Request) -> Dict[str, Any return metadata -def generate_asyncapi_request_channel(operation: Request, model_name_map: Dict[str, Any]) -> Any: +def generate_asyncapi_request_channel(operation: Request, model_name_map: dict[str, Any]) -> Any: operation_results = get_flat_response_models(operation.result) @@ -107,7 +106,7 @@ def generate_asyncapi_request_channel(operation: Request, model_name_map: Dict[s return {"request": operation_schema, "deprecated": operation.deprecated} -def generate_asyncapi_publish_channel(operation: Publish, model_name_map: Dict[str, Any]) -> Any: +def generate_asyncapi_publish_channel(operation: Publish, model_name_map: dict[str, Any]) -> Any: request_field_ref: str = REF_PREFIX + operation.params.__name__ operation_schema = get_asyncapi_request_operation_metadata(operation) @@ -117,7 +116,7 @@ def generate_asyncapi_publish_channel(operation: Publish, model_name_map: Dict[s return {"publish": operation_schema, "deprecated": operation.deprecated} -def domain_errors_schema(lower_bound: int, upper_bound: int, exceptions: List[Exception]): +def domain_errors_schema(lower_bound: int, upper_bound: int, exceptions: list[Exception]): schema = {} schema["range"] = {"upper": upper_bound, "lower": lower_bound} errors = [] @@ -147,7 +146,7 @@ def domain_errors_schema(lower_bound: int, upper_bound: int, exceptions: List[Ex return schema -def get_sub_operation_schema(sub: Sub) -> Tuple[str, Dict[str, Any]]: +def get_sub_operation_schema(sub: Sub) -> tuple[str, dict[str, Any]]: _sub = { "summary": sub.summary, "description": sub.description, @@ -159,8 +158,7 @@ def get_sub_operation_schema(sub: Sub) -> Tuple[str, Dict[str, Any]]: return sub.subject, op -def get_pub_operation_schema(pub: Pub, model_name_map: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]: - # pub_payload: str = REF_PREFIX + model_name_map[pub.params] +def get_pub_operation_schema(pub: Pub, model_name_map: dict[str, Any]) -> tuple[str, dict[str, Any]]: pub_payload: str = REF_PREFIX + pub.params.__name__ _pub = { @@ -180,18 +178,18 @@ def get_asyncapi( asyncapi_version: str, external_docs: ExternalDocumentation, errors: Errors, - routes: Dict[str, Request], - subs: List[Sub], - pubs: List[Pub], - description: Optional[str] = None, - servers: Optional[Dict[str, Server]] = None, -) -> Dict[str, Any]: - subjects: Dict[str, Dict[str, Any]] = {} + routes: dict[str, Request], + subs: list[Sub], + pubs: list[Pub], + description: str | None = None, + servers: dict[str, Server] | None = None, +) -> dict[str, Any]: + subjects: dict[str, dict[str, Any]] = {} info = {"title": title, "version": version} info["description"] = description if description else None - components: Dict[str, Dict[str, Any]] = {} + components: dict[str, dict[str, Any]] = {} - output: Dict[str, Any] = {"asyncapi": asyncapi_version, "info": info} + output: dict[str, Any] = {"asyncapi": asyncapi_version, "info": info} all_fields = get_fields_from_routes(routes.values(), pubs) model_name_map = get_compat_model_name_map(all_fields) @@ -199,12 +197,12 @@ def get_asyncapi( # TODO: <26-02-25, Sebastiaan Van Hoecke> # Where to use the first paramter (see https://github.com/fastapi/fastapi/blob/master/fastapi/openapi/utils.py#L493) _, definitions = get_definitions( - fields=all_fields, schema_generator=schema_generator, model_name_map=model_name_map + fields=all_fields, schema_generator=schema_generator, model_name_map=model_name_map, ) definitions[JsonRPCError.__name__] = JsonRPCError.schema() components["schemas"] = definitions - subjects: Dict[str, Dict[str, Any]] = {} + subjects: dict[str, dict[str, Any]] = {} for subject, endpoint in routes.items(): if getattr(endpoint, "include_schema", None) and isinstance(endpoint, Request): result = generate_asyncapi_request_channel(endpoint, model_name_map) @@ -223,7 +221,7 @@ def get_asyncapi( subjects[channel] = operation info["description"] = description if description else None - output: Dict[str, Any] = {"asyncapi": asyncapi_version, "info": info} + output: dict[str, Any] = {"asyncapi": asyncapi_version, "info": info} output["servers"] = servers if {n: s.dict() for n, s in servers.items()} else None output["externalDocs"] = external_docs.dict() if external_docs else None diff --git a/natsapi/client/client.py b/natsapi/client/client.py index c72be6f..74c6109 100644 --- a/natsapi/client/client.py +++ b/natsapi/client/client.py @@ -2,15 +2,14 @@ import inspect import logging import secrets -from natsapi.context import CTX_JSONRPC_ID - - +from collections.abc import Callable from ssl import create_default_context -from typing import Any, Callable, Dict, Optional, Type +from typing import Any from uuid import uuid4 from nats.aio.client import Client as NATS +from natsapi.context import CTX_JSONRPC_ID from natsapi.exceptions import JsonRPCException, JsonRPCUnknownMethodException from natsapi.models import JsonRPCError, JsonRPCReply, JsonRPCRequest from natsapi.routing import Request @@ -18,18 +17,13 @@ from .config import Config, default_config -class NatsClient(object): +class NatsClient: def __init__( self, - routes: Dict[str, Request], + routes: dict[str, Request], app: Any = None, - config: Optional[Config] = None, - exception_handlers: Optional[ - Dict[ - Type[Exception], - Callable[[Type[Exception]], JsonRPCException], - ] - ] = None, + config: Config | None = None, + exception_handlers: dict[type[Exception], Callable[[type[Exception]], JsonRPCException]] | None = None, ) -> None: self.routes = routes self.app = app @@ -49,7 +43,7 @@ async def connect(self) -> None: async def root_path_subscribe(self, subject: str, cb: Callable, queue: str = ""): await self.nats.subscribe(subject, cb=cb, **(self.config.subscribe.dict())) - async def publish(self, subject: str, params: Dict[str, Any], method: str = None, reply=None, headers: dict = None): + async def publish(self, subject: str, params: dict[str, Any], method: str = None, reply=None, headers: dict = None): """ method: legacy attribute, used for backwards compatibility """ @@ -60,7 +54,7 @@ async def publish_on_reply(self, subject, payload): await self.nats.publish(subject, payload) async def request( - self, subject: str, params: Dict[str, Any] = {}, timeout=60, method: str = None, headers: dict = None + self, subject: str, params: dict[str, Any] = dict(), timeout=60, method: str = None, headers: dict = None, ) -> JsonRPCReply: """ method: legacy attribute, used for backwards compatibility @@ -88,8 +82,8 @@ async def _handle_publish(self, msg): try: logging.debug(f"Handling: {subject}") route: Request = self.routes[subject] - except KeyError: - raise JsonRPCUnknownMethodException(data=f"No such endpoint available for {subject}") + except KeyError as e: + raise JsonRPCUnknownMethodException(data=f"No such endpoint available for {subject}") from e handler = route.endpoint params_model = route.params @@ -115,8 +109,8 @@ async def _handle_request(self, msg): try: logging.debug(f"Handling: {subject}") route: Request = self.routes[subject] - except KeyError: - raise JsonRPCUnknownMethodException(data=f"No such endpoint available. Checked for {subject}") + except KeyError as e: + raise JsonRPCUnknownMethodException(data=f"No such endpoint available. Checked for {subject}") from e handler = route.endpoint params_model = route.params @@ -139,7 +133,7 @@ async def _handle_request(self, msg): finally: await self.publish_on_reply(msg.reply, reply.json().encode()) - def _lookup_exception_handler(self, exc: Exception) -> Optional[Callable]: + def _lookup_exception_handler(self, exc: Exception) -> Callable | None: """ Gets list of all the types the exception instance inherits from and checks if exception type is in the 'exception_handlers' dict. diff --git a/natsapi/client/config.py b/natsapi/client/config.py index 689f0b0..2099a8a 100644 --- a/natsapi/client/config.py +++ b/natsapi/client/config.py @@ -1,4 +1,4 @@ -from typing import Any, List, Optional, Union +from typing import Any from nats.aio.client import ( DEFAULT_CONNECT_TIMEOUT, @@ -12,11 +12,12 @@ DEFAULT_SUB_PENDING_BYTES_LIMIT, DEFAULT_SUB_PENDING_MSGS_LIMIT, ) + from natsapi._compat import BaseSettings class ConnectConfig(BaseSettings): - servers: Union[str, List[str]] = ["nats://127.0.0.1:4222"] + servers: str | list[str] = ["nats://127.0.0.1:4222"] error_cb: Any = None closed_cb: Any = None reconnected_cb: Any = None @@ -44,7 +45,7 @@ class ConnectConfig(BaseSettings): user_jwt_cb: Any = None user_credentials: Any = None nkeys_seed: str | None = None - flush_timeout: Optional[float] = None + flush_timeout: float | None = None pending_size: int = DEFAULT_PENDING_SIZE diff --git a/natsapi/encoders.py b/natsapi/encoders.py index 1c90272..77131b9 100644 --- a/natsapi/encoders.py +++ b/natsapi/encoders.py @@ -1,23 +1,24 @@ """Yanked from fastapi.encoders""" from collections import defaultdict +from collections.abc import Callable from enum import Enum from pathlib import PurePath from types import GeneratorType -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union +from typing import Any from pydantic import BaseModel from natsapi._compat import ENCODERS_BY_TYPE, PYDANTIC_V2 -SetIntStr = Set[Union[int, str]] -DictIntStrAny = Dict[Union[int, str], Any] +SetIntStr = set[int | str] +DictIntStrAny = dict[int | str, Any] def generate_encoders_by_class_tuples( - type_encoder_map: Dict[Any, Callable[[Any], Any]] -) -> Dict[Callable[[Any], Any], Tuple[Any, ...]]: - encoders_by_class_tuples: Dict[Callable[[Any], Any], Tuple[Any, ...]] = defaultdict(tuple) + type_encoder_map: dict[Any, Callable[[Any], Any]], +) -> dict[Callable[[Any], Any], tuple[Any, ...]]: + encoders_by_class_tuples: dict[Callable[[Any], Any], tuple[Any, ...]] = defaultdict(tuple) for type_, encoder in type_encoder_map.items(): encoders_by_class_tuples[encoder] += (type_,) return encoders_by_class_tuples @@ -28,13 +29,13 @@ def generate_encoders_by_class_tuples( def jsonable_encoder( obj: Any, - include: Optional[Union[SetIntStr, DictIntStrAny]] = None, - exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None, + include: SetIntStr | DictIntStrAny | None = None, + exclude: SetIntStr | DictIntStrAny | None = None, by_alias: bool = True, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, - custom_encoder: Dict[Any, Callable[[Any], Any]] = {}, + custom_encoder: dict[Any, Callable[[Any], Any]] = dict(), sqlalchemy_safe: bool = True, ) -> Any: if include is not None and not isinstance(include, set): @@ -113,7 +114,7 @@ def jsonable_encoder( exclude_none=exclude_none, custom_encoder=custom_encoder, sqlalchemy_safe=sqlalchemy_safe, - ) + ), ) return encoded_list @@ -131,7 +132,7 @@ def jsonable_encoder( if isinstance(obj, classes_tuple): return encoder(obj) - errors: List[Exception] = [] + errors: list[Exception] = [] try: data = dict(obj) except Exception as e: @@ -140,7 +141,7 @@ def jsonable_encoder( data = vars(obj) except Exception as e: errors.append(e) - raise ValueError(errors) + raise ValueError(errors) from e return jsonable_encoder( data, by_alias=by_alias, diff --git a/natsapi/models.py b/natsapi/models.py index 01b2c63..bdb549c 100644 --- a/natsapi/models.py +++ b/natsapi/models.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Any, Dict, List, Optional +from typing import Any from uuid import UUID, uuid4 from pydantic import BaseModel, Field, create_model, root_validator, validator @@ -9,13 +9,13 @@ class ErrorDetail(BaseModel): type: str - target: Optional[str] = None + target: str | None = None message: str class ErrorData(BaseModel): - type: Optional[str] = None - errors: List[ErrorDetail] = [] + type: str | None = None + errors: list[ErrorDetail] = [] class JsonRPCError(BaseModel): @@ -31,8 +31,8 @@ class JsonRPCError(BaseModel): class JsonRPCReply(BaseModel): jsonrpc: JSON_RPC_VERSION = Field("2.0") id: UUID = Field(...) - result: Optional[Dict[str, Any]] = Field(None) - error: Optional[JsonRPCError] = Field(None) + result: dict[str, Any] | None = Field(None) + error: JsonRPCError | None = Field(None) @root_validator(pre=True) def check_result_and_error(cls, values): @@ -40,20 +40,20 @@ def check_result_and_error(cls, values): assert result or error, "A result or error should be required" if result and error: raise AttributeError( - "An RPC reply MUST NOT have an error and a result. Based on the result, you should provide only one." + "An RPC reply MUST NOT have an error and a result. Based on the result, you should provide only one.", ) return values class JsonRPCRequest(BaseModel): - jsonrpc: Optional[JSON_RPC_VERSION] = Field("2.0") - timeout: Optional[float] = Field( + jsonrpc: JSON_RPC_VERSION | None = Field("2.0") + timeout: float | None = Field( None, description="Timeout set by client, should be equal to the timeout set when doing nc.request, if publish use '-1'", ) - method: Optional[str] = Field(None, description="Request method used") - params: Dict[str, Any] = Field(...) - id: Optional[UUID] = Field(None, alias="id", description="UUID created at the creation of the request") + method: str | None = Field(None, description="Request method used") + params: dict[str, Any] = Field(...) + id: UUID | None = Field(None, alias="id", description="UUID created at the creation of the request") @validator("id", pre=True, always=True) def set_id(cls, id): diff --git a/natsapi/plugin.py b/natsapi/plugin.py index b5e665a..018dc88 100644 --- a/natsapi/plugin.py +++ b/natsapi/plugin.py @@ -1,16 +1,13 @@ import asyncio import json import os - - from collections import defaultdict -from typing import Any, Dict, Tuple +from typing import Any from uuid import uuid4 import pytest - - -from nats.aio.client import Client as NATS, Msg +from nats.aio.client import Client as NATS +from nats.aio.client import Msg from natsapi.exceptions import JsonRPCUnknownMethodException from natsapi.models import JsonRPCReply @@ -42,9 +39,9 @@ async def test(natsapi_mock): def __init__(self, host: str) -> None: self.host = host self.nats: NATS = NATS() - self.subjects: Dict[str, Tuple[Any, Any]] = {} - self.responses: Dict[str, Tuple[Any, Any]] = {} - self.payloads: Dict[str, Any] = defaultdict(list) + self.subjects: dict[str, tuple[Any, Any]] = {} + self.responses: dict[str, tuple[Any, Any]] = {} + self.payloads: dict[str, Any] = defaultdict(list) async def lifespan(self) -> None: await self.nats.connect(self.host, verbose=True, ping_interval=5) @@ -76,7 +73,7 @@ async def handle(self, message: Msg) -> None: if message.reply: await self.nats.publish(message.reply, response.json().encode()) - async def request(self, subject: str, *, response: Any = None, error: Dict[str, Any] = None) -> None: + async def request(self, subject: str, *, response: Any = None, error: dict[str, Any] = None) -> None: assert response or error, "Need a response of an error" self.responses[subject] = response, error await self.nats.subscribe(subject, cb=self.handle) diff --git a/natsapi/routing.py b/natsapi/routing.py index 9ada06f..da24b68 100644 --- a/natsapi/routing.py +++ b/natsapi/routing.py @@ -1,7 +1,6 @@ import inspect - - -from typing import Any, Callable, List, Optional, Set, Type +from collections.abc import Callable +from typing import Any from pydantic import BaseModel @@ -16,14 +15,14 @@ def __init__( subject: str, endpoint: Callable[..., Any], *, - result=Type[Any], - skip_validation: Optional[bool] = False, - description: Optional[str] = None, - deprecated: Optional[bool] = None, - tags: Optional[List[str]] = None, - summary: Optional[str] = None, - include_schema: Optional[bool] = True, - suggested_timeout: Optional[float] = None, + result=type[Any], + skip_validation: bool | None = False, + description: str | None = None, + deprecated: bool | None = None, + tags: list[str] | None = None, + summary: str | None = None, + include_schema: bool | None = True, + suggested_timeout: float | None = None, ): self.subject = subject self.endpoint = endpoint @@ -53,12 +52,12 @@ def __init__( subject: str, endpoint: Callable[..., Any], *, - skip_validation: Optional[bool] = False, - description: Optional[str] = None, - deprecated: Optional[bool] = None, - tags: Optional[List[str]] = None, - summary: Optional[str] = None, - include_schema: Optional[bool] = True, + skip_validation: bool | None = False, + description: str | None = None, + deprecated: bool | None = None, + tags: list[str] | None = None, + summary: str | None = None, + include_schema: bool | None = True, ): self.subject = subject self.endpoint = endpoint @@ -83,11 +82,11 @@ def __init__( self, subject: str, *, - queue: Optional[str] = None, - summary: Optional[str] = None, - description: Optional[str] = None, - tags: Optional[List[str]] = None, - externalDocs: Optional[ExternalDocumentation] = None, + queue: str | None = None, + summary: str | None = None, + description: str | None = None, + tags: list[str] | None = None, + externalDocs: ExternalDocumentation | None = None, ): self.subject = subject @@ -104,10 +103,10 @@ def __init__( subject: str, params: BaseModel, *, - summary: Optional[str] = None, - description: Optional[str] = None, - tags: Optional[List[str]] = None, - externalDocs: Optional[ExternalDocumentation] = None, + summary: str | None = None, + description: str | None = None, + tags: list[str] | None = None, + externalDocs: ExternalDocumentation | None = None, ): self.subject = subject self.summary = summary @@ -123,18 +122,18 @@ def __init__( self, *, prefix: str = None, - tags: Optional[List[str]] = None, - routes: Optional[List[Request]] = None, - subs: Optional[Set[Sub]] = None, - pubs: Optional[Set[Pub]] = None, - deprecated: Optional[bool] = None, + tags: list[str] | None = None, + routes: list[Request] | None = None, + subs: set[Sub] | None = None, + pubs: set[Pub] | None = None, + deprecated: bool | None = None, include_in_schema: bool = True, ) -> None: self.prefix = prefix self.routes = routes or [] self.pubs = pubs or set() self.subs = subs or set() - self.tags: List[str] = tags or [] + self.tags: list[str] = tags or [] self.deprecated = deprecated self.include_in_schema = include_in_schema @@ -143,14 +142,14 @@ def add_request( subject: str, endpoint: Callable[..., Any], *, - result=Type[Any], - skip_validation: Optional[bool] = False, - description: Optional[str] = None, - deprecated: Optional[bool] = None, - tags: Optional[List[str]] = None, - summary: Optional[str] = None, - suggested_timeout: Optional[float] = None, - include_schema: Optional[bool] = True, + result=type[Any], + skip_validation: bool | None = False, + description: str | None = None, + deprecated: bool | None = None, + tags: list[str] | None = None, + summary: str | None = None, + suggested_timeout: float | None = None, + include_schema: bool | None = True, ) -> None: current_tags = self.tags.copy() if tags: @@ -175,12 +174,12 @@ def add_publish( subject: str, endpoint: Callable[..., Any], *, - skip_validation: Optional[bool] = False, - description: Optional[str] = None, - deprecated: Optional[bool] = None, - tags: Optional[List[str]] = None, - summary: Optional[str] = None, - include_schema: Optional[bool] = True, + skip_validation: bool | None = False, + description: str | None = None, + deprecated: bool | None = None, + tags: list[str] | None = None, + summary: str | None = None, + include_schema: bool | None = True, ) -> None: current_tags = self.tags.copy() if tags: @@ -202,14 +201,14 @@ def request( self, subject: str, *, - result=Type[Any], - skip_validation: Optional[bool] = False, - description: Optional[str] = None, - deprecated: Optional[bool] = None, - tags: Optional[List[str]] = None, - summary: Optional[str] = None, - suggested_timeout: Optional[float] = None, - include_schema: Optional[bool] = True, + result=type[Any], + skip_validation: bool | None = False, + description: str | None = None, + deprecated: bool | None = None, + tags: list[str] | None = None, + summary: str | None = None, + suggested_timeout: float | None = None, + include_schema: bool | None = True, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_request( @@ -232,13 +231,13 @@ def publish( self, subject: str, *, - result=Type[Any], - skip_validation: Optional[bool] = False, - description: Optional[str] = None, - deprecated: Optional[bool] = None, - tags: Optional[List[str]] = None, - summary: Optional[str] = None, - include_schema: Optional[bool] = True, + result=type[Any], + skip_validation: bool | None = False, + description: str | None = None, + deprecated: bool | None = None, + tags: list[str] | None = None, + summary: str | None = None, + include_schema: bool | None = True, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_publish( @@ -260,10 +259,10 @@ def add_pub( subject: str, params: BaseModel, *, - summary: Optional[str] = None, - description: Optional[str] = None, - tags: Optional[List[str]] = None, - externalDocs: Optional[ExternalDocumentation] = None, + summary: str | None = None, + description: str | None = None, + tags: list[str] | None = None, + externalDocs: ExternalDocumentation | None = None, ) -> None: """ Include pub in asyncapi schema @@ -282,11 +281,11 @@ def pub( self, subject: str, *, - params=Type[Any], - description: Optional[str] = None, - tags: Optional[List[str]] = None, - summary: Optional[str] = None, - externalDocs: Optional[ExternalDocumentation] = None, + params=type[Any], + description: str | None = None, + tags: list[str] | None = None, + summary: str | None = None, + externalDocs: ExternalDocumentation | None = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_pub( @@ -305,11 +304,11 @@ def add_sub( self, subject: str, *, - queue: Optional[str] = None, - summary: Optional[str] = None, - description: Optional[str] = None, - tags: Optional[List[str]] = None, - externalDocs: Optional[ExternalDocumentation] = None, + queue: str | None = None, + summary: str | None = None, + description: str | None = None, + tags: list[str] | None = None, + externalDocs: ExternalDocumentation | None = None, ) -> None: """ Include sub in asyncapi schema @@ -328,11 +327,11 @@ def sub( self, subject: str, *, - queue: Optional[str] = None, - description: Optional[str] = None, - tags: Optional[List[str]] = None, - summary: Optional[str] = None, - externalDocs: Optional[ExternalDocumentation] = None, + queue: str | None = None, + description: str | None = None, + tags: list[str] | None = None, + summary: str | None = None, + externalDocs: ExternalDocumentation | None = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_sub( diff --git a/natsapi/state.py b/natsapi/state.py index f784f7a..cf06456 100644 --- a/natsapi/state.py +++ b/natsapi/state.py @@ -1,7 +1,7 @@ import typing -class State(object): +class State: """ Yanked from starlette.datascructures @@ -10,7 +10,7 @@ class State(object): Used for `app.state`. """ - def __init__(self, state: typing.Dict = None): + def __init__(self, state: dict = None): if state is None: state = {} super(State, self).__setattr__("_state", state) @@ -21,9 +21,9 @@ def __setattr__(self, key: typing.Any, value: typing.Any) -> None: def __getattr__(self, key: typing.Any) -> typing.Any: try: return self._state[key] - except KeyError: + except KeyError as e: message = "'{}' object has no attribute '{}'" - raise AttributeError(message.format(self.__class__.__name__, key)) + raise AttributeError(message.format(self.__class__.__name__, key)) from e def __delattr__(self, key: typing.Any) -> None: del self._state[key] diff --git a/natsapi/types.py b/natsapi/types.py index 6a959df..17f2dfa 100644 --- a/natsapi/types.py +++ b/natsapi/types.py @@ -1,4 +1,5 @@ """Yanked from FastApi.typing""" -from typing import Any, Callable, TypeVar +from collections.abc import Callable +from typing import Any, TypeVar DecoratedCallable = TypeVar("DecoratedCallable", bound=Callable[..., Any]) diff --git a/natsapi/utils.py b/natsapi/utils.py index 9fe89d9..805cf2b 100644 --- a/natsapi/utils.py +++ b/natsapi/utils.py @@ -1,22 +1,16 @@ import collections import inspect import re - - +from collections.abc import Callable from enum import Enum -from typing import Any, Callable, Dict, Optional, Set, Type, Union +from typing import Any from pydantic import BaseConfig, BaseModel, create_model -# from pydantic.class_validators import Validator - -# from pydantic.deprecated.class_validators import V1Validator as Validator from pydantic.fields import FieldInfo -from natsapi._compat import ModelField, PYDANTIC_V2 - from pydantic.v1.schema import model_process_schema -# from pydantic.schema import model_process_schema +from natsapi._compat import PYDANTIC_V2, ModelField from natsapi.asyncapi.constants import REF_PREFIX from natsapi.exceptions import NatsAPIError @@ -37,14 +31,10 @@ def generate_operation_id_for_subject(*, summary: str, subject: str) -> str: def create_field( name: str, - type_: Type[Any], - class_validators: Optional[Dict[str, Any]] = None, - model_config: Type[BaseConfig] = BaseConfig, - field_info: Optional[FieldInfo] = None, - # TODO: <26-02-25, Sebastiaan Van Hoecke> # Might fix the defaults? - # default: Optional[Any] = Undefined, - # alias: Optional[str] = None, - # required: Union[bool, UndefinedType] = Undefined, + type_: type[Any], + class_validators: dict[str, Any] | None = None, + model_config: type[BaseConfig] = BaseConfig, + field_info: FieldInfo | None = None, ) -> ModelField: """ Yanked from fastapi.utils @@ -52,19 +42,7 @@ def create_field( """ class_validators = class_validators or {} - if PYDANTIC_V2: - field_info = field_info or FieldInfo(annotation=type_) - else: - field_info = field_info or FieldInfo() - - # field = functools.partial( - # ModelField, - # name=name, - # type_=type_, - # class_validators=class_validators, - # required=True, - # model_config=model_config, - # ) + field_info = (field_info or FieldInfo(annotation=type_)) if PYDANTIC_V2 else (field_info or FieldInfo()) kwargs = {"name": name, "field_info": field_info} @@ -75,26 +53,24 @@ def create_field( "class_validators": class_validators, "model_config": model_config, "required": True, - # "default": default, - # "alias": alias, - } + }, ) try: return ModelField(**kwargs) - except RuntimeError: - raise NatsAPIError(f"Invalid args for reply field! Hint: check that {type_} is a valid pydantic field type") + except RuntimeError as e: + raise NatsAPIError(f"Invalid args for reply field! Hint: check that {type_} is a valid pydantic field type") from e def get_model_definitions( *, - flat_models: Set[Union[Type[BaseModel], Type[Enum]]], - model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str], -) -> Dict[str, Any]: - definitions: Dict[str, Dict[str, Any]] = {} + flat_models: set[type[BaseModel] | type[Enum]], + model_name_map: dict[type[BaseModel] | type[Enum], str], +) -> dict[str, Any]: + definitions: dict[str, dict[str, Any]] = {} for model in flat_models: m_schema, m_definitions, m_nested_models = model_process_schema( - model, model_name_map=model_name_map, ref_prefix=REF_PREFIX + model, model_name_map=model_name_map, ref_prefix=REF_PREFIX, ) definitions.update(m_definitions) try: @@ -102,15 +78,15 @@ def get_model_definitions( except KeyError as exc: method_name = str(exc.args[0]).replace("", "") raise NatsAPIError( - f"Could not generate schema. Two or more functions share the name '{method_name}'. Make sure methods don't share the same name" - ) + f"Could not generate schema. Two or more functions share the name '{method_name}'. Make sure methods don't share the same name", + ) from exc definitions[model_name] = m_schema return definitions def get_request_model(func: Callable, subject: str, skip_validation: bool): parameters = collections.OrderedDict(inspect.signature(func).parameters) - name_prefix = func.__name__ if not func.__name__ == "_" else subject + name_prefix = func.__name__ if func.__name__ != "_" else subject if skip_validation: assert ( diff --git a/poetry.lock b/poetry.lock index 1d6ad96..e81ab15 100644 --- a/poetry.lock +++ b/poetry.lock @@ -37,36 +37,47 @@ yaml = ["PyYAML"] [[package]] name = "black" -version = "22.12.0" +version = "24.10.0" description = "The uncompromising code formatter." optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, - {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, - {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, - {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, - {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, - {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, - {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, - {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, - {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, - {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, - {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, - {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, ] [package.dependencies] click = ">=8.0.0" mypy-extensions = ">=0.4.3" +packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] +d = ["aiohttp (>=3.10)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] @@ -423,22 +434,6 @@ docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3) testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] typing = ["typing-extensions (>=4.12.2)"] -[[package]] -name = "flake9" -version = "3.8.3.post2" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.4" -files = [ - {file = "flake9-3.8.3.post2-py3-none-any.whl", hash = "sha256:47dced969a802a8892740bcaa35ae07232709b2ade803c45f48dd03ccb7f825f"}, - {file = "flake9-3.8.3.post2.tar.gz", hash = "sha256:daefdbfb3d320eb215a4a52c62a4b4a027cbe11d39f5dab30df908b40fce5ba7"}, -] - -[package.dependencies] -mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.6.0a1,<2.7.0" -pyflakes = ">=2.2.0,<2.3.0" - [[package]] name = "identify" version = "2.6.8" @@ -478,20 +473,6 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - [[package]] name = "markdown-it-py" version = "3.0.0" @@ -516,17 +497,6 @@ profiling = ["gprof2dot"] rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] -[[package]] -name = "mccabe" -version = "0.6.1" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = "*" -files = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, -] - [[package]] name = "mdurl" version = "0.1.2" @@ -691,17 +661,6 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" -[[package]] -name = "pycodestyle" -version = "2.6.0" -description = "Python style guide checker" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, - {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, -] - [[package]] name = "pycparser" version = "2.22" @@ -845,17 +804,6 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" -[[package]] -name = "pyflakes" -version = "2.2.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"}, - {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, -] - [[package]] name = "pygments" version = "2.19.1" @@ -1073,6 +1021,33 @@ typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.1 [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] +[[package]] +name = "ruff" +version = "0.7.4" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.7.4-py3-none-linux_armv6l.whl", hash = "sha256:a4919925e7684a3f18e18243cd6bea7cfb8e968a6eaa8437971f681b7ec51478"}, + {file = "ruff-0.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfb365c135b830778dda8c04fb7d4280ed0b984e1aec27f574445231e20d6c63"}, + {file = "ruff-0.7.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:63a569b36bc66fbadec5beaa539dd81e0527cb258b94e29e0531ce41bacc1f20"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d06218747d361d06fd2fdac734e7fa92df36df93035db3dc2ad7aa9852cb109"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0cea28d0944f74ebc33e9f934238f15c758841f9f5edd180b5315c203293452"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80094ecd4793c68b2571b128f91754d60f692d64bc0d7272ec9197fdd09bf9ea"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:997512325c6620d1c4c2b15db49ef59543ef9cd0f4aa8065ec2ae5103cedc7e7"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00b4cf3a6b5fad6d1a66e7574d78956bbd09abfd6c8a997798f01f5da3d46a05"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7dbdc7d8274e1422722933d1edddfdc65b4336abf0b16dfcb9dedd6e6a517d06"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e92dfb5f00eaedb1501b2f906ccabfd67b2355bdf117fea9719fc99ac2145bc"}, + {file = "ruff-0.7.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3bd726099f277d735dc38900b6a8d6cf070f80828877941983a57bca1cd92172"}, + {file = "ruff-0.7.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2e32829c429dd081ee5ba39aef436603e5b22335c3d3fff013cd585806a6486a"}, + {file = "ruff-0.7.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:662a63b4971807623f6f90c1fb664613f67cc182dc4d991471c23c541fee62dd"}, + {file = "ruff-0.7.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:876f5e09eaae3eb76814c1d3b68879891d6fde4824c015d48e7a7da4cf066a3a"}, + {file = "ruff-0.7.4-py3-none-win32.whl", hash = "sha256:75c53f54904be42dd52a548728a5b572344b50d9b2873d13a3f8c5e3b91f5cac"}, + {file = "ruff-0.7.4-py3-none-win_amd64.whl", hash = "sha256:745775c7b39f914238ed1f1b0bebed0b9155a17cd8bc0b08d3c87e4703b990d6"}, + {file = "ruff-0.7.4-py3-none-win_arm64.whl", hash = "sha256:11bff065102c3ae9d3ea4dc9ecdfe5a5171349cdd0787c1fc64761212fc9cf1f"}, + {file = "ruff-0.7.4.tar.gz", hash = "sha256:cd12e35031f5af6b9b93715d8c4f40360070b2041f81273d0527683d5708fce2"}, +] + [[package]] name = "safety" version = "1.10.3" @@ -1253,4 +1228,4 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "fca41522304ae72a9dde8c62b9a59dc42858c6d1367757ef62553cf2e7c6809f" +content-hash = "2155652ed37d58f4bcc57a15d55e6581dd92b39e7210a177df2f1de590914de3" diff --git a/pyproject.toml b/pyproject.toml index 6de7b72..07914f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,15 +25,14 @@ pre-commit = "^2.12.1" [tool.poetry.group.test.dependencies] pytest-cov = "^2.11.1" -coverage = "5.5" -black = "^22.3.0" -flake9 = "^3.8.3" -isort = "^5.8.0" +black = "^24.10.0" bandit = "^1.7.0" piprot = "^0.9.11" safety = "^1.10.3" pytest-asyncio = "^0.17.2" -vulture = "^2.3" +vulture = "^2.13" +ruff = "^0.7.4" + [tool.poetry.plugins."pytest11"] natsapi = "natsapi.plugin" @@ -48,16 +47,18 @@ testpaths = [ requires = ["poetry-core>=1.1.0"] build-backend = "poetry.core.masonry.api" -[tool.isort] -profile = "black" -lines_between_types = 2 -combine_as_imports = true -line_length = 120 -multi_line_output = 3 +[tool.ruff] +line-length = 120 +exclude = ["scripts"] +target-version = "py312" + +[tool.ruff.lint] +extend-select = ["C90", "I", "B", "Q", "UP", "S", "COM", "C4", "T10", "SIM", "TID", "PTH", "ERA", "ASYNC"] +# ignore = ["F722", "E501","E741","W605","C901","F541","F811", "B026", "S101", "S104", "S105", "S106", "S113", "B008", "UP007", "UP008", "UP038"] +# ignore = ["F722", "E501","E741","W605","C901","F541","F811", "B026", "S101", "S104", "S105", "S106", "S113", "B008", "UP007", "UP008", "UP038"] +unfixable = ["B", "F811"] -[tool.flake8] -ignore = "F722,W503,E501,E741,W605,E203,C901,F541,E133" -max-line-length = 120 +[tool.ruff.lint.mccabe] max-complexity = 25 [tool.black] diff --git a/shell.nix b/shell.nix index 8d0e23a..0703d57 100644 --- a/shell.nix +++ b/shell.nix @@ -1,26 +1,32 @@ -{pkgs ? import {} }: +let + pkgs = import {}; +in pkgs.mkShell { + shellHook = '' + export PIP_NO_BINARY="ruff" + set -a; source .env; set +a + echo "SHELLHOOK LOG: .env loaded to ENV variables" + ''; + + packages = with pkgs; [ + python311 -pkgs.mkShell { - packages = [ - pkgs.python311 + ruff + rustc + cargo - (pkgs.poetry.override { python3 = pkgs.python311; }) + (poetry.override { python3 = python311; }) - (pkgs.python311.withPackages (p: with p; [ + (python311.withPackages (p: with p; [ pip python-lsp-server pynvim pyls-isort python-lsp-black ])) - ]; - LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath [ - pkgs.stdenv.cc.cc - ]}"; + ]; - shellHook = '' - set -a; source .env; set +a - echo "SHELLHOOK LOG: .env loaded to ENV variables" - ''; + env.LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ + pkgs.stdenv.cc.cc.lib + ]; } diff --git a/tests/asyncapi/test_generation.py b/tests/asyncapi/test_generation.py index cc91fa8..bf67918 100644 --- a/tests/asyncapi/test_generation.py +++ b/tests/asyncapi/test_generation.py @@ -2,8 +2,6 @@ from uuid import uuid4 import pytest - - from pydantic import BaseModel, Field from natsapi import NatsAPI, Pub, SubjectRouter @@ -20,7 +18,7 @@ "protocol": "nats", "protocolVersion": "2.0", "variables": {"port": {"default": "422"}}, - } + }, ) staging_server = Server( @@ -30,7 +28,7 @@ "protocol": "nats", "protocolVersion": "2.0", "variables": {"port": {"default": "422"}}, - } + }, ) servers_schema = {"production": production_server, "staging": staging_server} @@ -99,7 +97,7 @@ def patched(): def test_asyncapi_schema_w_personal_title_should_generate(): client = NatsAPI( - "natsapi.development", title="My Nats Client", description="This is my nats client", version="2.4.3" + "natsapi.development", title="My Nats Client", description="This is my nats client", version="2.4.3", ) client.generate_asyncapi() schema = client.asyncapi_schema @@ -266,8 +264,8 @@ def get_subject_b(app): "$ref" ].split("/")[-1] - assert subject_a_reply_ref in schema["components"]["schemas"].keys() - assert subject_b_reply_ref in schema["components"]["schemas"].keys() + assert subject_a_reply_ref in schema["components"]["schemas"] + assert subject_b_reply_ref in schema["components"]["schemas"] async def test_generate_shema_w_docstring_should_generate_proper_description(app: NatsAPI): diff --git a/tests/test_applications.py b/tests/test_applications.py index 60fd7a1..f106c99 100644 --- a/tests/test_applications.py +++ b/tests/test_applications.py @@ -1,6 +1,4 @@ import pytest - - from pydantic import BaseModel from natsapi import NatsAPI, Pub, Sub, SubjectRouter diff --git a/tests/test_fastapi.py b/tests/test_fastapi.py index 28f6abd..6de56d9 100644 --- a/tests/test_fastapi.py +++ b/tests/test_fastapi.py @@ -1,8 +1,6 @@ import asyncio import pytest - - from pydantic import BaseModel from natsapi import NatsAPI @@ -38,6 +36,6 @@ def test_pass_fastapi_instance_as_app_should_work(): natsapi = NatsAPI("natsapi.development", app=fastapi) - assert type(natsapi.app) == type(fastapi) + assert type(natsapi.app) is type(fastapi) assert natsapi.app.controllers == "dirty_object" assert natsapi.app.state.db == "postgresql" diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py index dfc1659..245aa19 100644 --- a/tests/test_jsonable_encoder.py +++ b/tests/test_jsonable_encoder.py @@ -1,15 +1,12 @@ """yanked from fastapi""" -from natsapi._compat import RootModel -from datetime import datetime, timezone +from datetime import UTC, datetime from enum import Enum from pathlib import PurePath, PurePosixPath, PureWindowsPath -from typing import Optional import pytest +from pydantic import BaseModel, ConfigDict, Field, ValidationError, create_model - -from pydantic import BaseModel, Field, ValidationError, create_model, ConfigDict - +from natsapi._compat import RootModel from natsapi.encoders import jsonable_encoder @@ -47,7 +44,7 @@ class ModelWithCustomEncoder(BaseModel): dt_field: datetime class Config: - json_encoders = {datetime: lambda dt: dt.replace(microsecond=0, tzinfo=timezone.utc).isoformat()} + json_encoders = {datetime: lambda dt: dt.replace(microsecond=0, tzinfo=UTC).isoformat()} class ModelWithCustomEncoderSubclass(ModelWithCustomEncoder): @@ -61,7 +58,7 @@ class RoleEnum(Enum): class ModelWithConfig(BaseModel): - role: Optional[RoleEnum] = None + role: RoleEnum | None = None class Config: use_enum_values = True diff --git a/tests/test_method_type_conversion.py b/tests/test_method_type_conversion.py index 87fd0de..df03b30 100644 --- a/tests/test_method_type_conversion.py +++ b/tests/test_method_type_conversion.py @@ -1,8 +1,6 @@ import typing import pytest - - from pydantic import BaseModel from natsapi import NatsAPI, SubjectRouter @@ -19,7 +17,7 @@ class FastAPI(BaseModel): async def test_method_parameters_should_get_parsed_to_correct_typing(app): class ThemesCreateCmd(BaseModel): primary: str - color: typing.Optional[str] | None = None + color: str | None | None = None @app.request("themes.CREATE", result=StatusResult) async def create_theme(app, data: ThemesCreateCmd): @@ -64,7 +62,7 @@ class TypeResult(BaseModel): async def test_exotic_typing_should_convert_to_correct_type(app): @app.request("themes.CONVERT", result=TypeResult) - async def convert_theme(app, param: typing.Union[typing.List[str], int]): + async def convert_theme(app, param: list[str] | int): return {"typing": type(param).__name__} reply = await app.nc.request("natsapi.development.themes.CONVERT", {"param": ["foo", "bar", "baz"]}) diff --git a/tests/test_models.py b/tests/test_models.py index 997cde4..ed79fd6 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,9 +1,6 @@ -from typing import Optional from uuid import uuid4 import pytest - - from pydantic import BaseModel, ValidationError from pydantic.fields import Field @@ -13,7 +10,7 @@ def test_change_param_type_of_model_should_change(): class Params(BaseModel): foo: int = Field(...) - bar: Optional[str] = Field(None) + bar: str | None = Field(None) new_model = JsonRPCRequest.with_params(Params) @@ -23,7 +20,7 @@ class Params(BaseModel): actual = new_model.parse_obj(d).params expected = Params - assert type(actual) == expected + assert type(actual) is expected def test_result_or_error_should_be_provided_in_jsonrpcreply(): diff --git a/tests/test_reload.py b/tests/test_reload.py deleted file mode 100644 index 9f588cd..0000000 --- a/tests/test_reload.py +++ /dev/null @@ -1,27 +0,0 @@ -# import asyncio -# -# -# from pathlib import Path -# -# import pytest -# -# from natsapi import NatsAPI -# from natsapi.reload import Reloader -# -# -# @pytest.mark.skip(reason="no way of currently testing this, need another way.") -# async def test_reload_when_file_touch_should_succeed(caplog): -# app = NatsAPI("natsapi.development") -# p = Path("natsapi/logger.py") -# -# r = Reloader(app.startup) -# asyncio.create_task(r.run()) -# p.touch() -# await asyncio.sleep(2) -# -# logs = [str(x) for x in caplog.records] -# print(len(logs)) -# assert r.restarts == 1 -# for x in logs: -# print(logs) -# assert "WatchGodReload detected file change in ['./natsapi/logger.py']. Reloading..." in logs diff --git a/tests/test_requests.py b/tests/test_requests.py index d35e1c8..567852a 100644 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -1,9 +1,9 @@ from pydantic import BaseModel -from natsapi.models import JsonRPCReply, JsonRPCRequest -from natsapi.context import CTX_JSONRPC_ID from natsapi import NatsAPI, SubjectRouter +from natsapi.context import CTX_JSONRPC_ID from natsapi.exceptions import JsonRPCException +from natsapi.models import JsonRPCReply, JsonRPCRequest class StatusResult(BaseModel): @@ -156,7 +156,7 @@ def _(app, data: SomeParams, **kwargs): assert "dict" in reply.result["status"] schema = (await app.nc.request("natsapi.development.schema.RETRIEVE", {})).result - assert "SomeParams" in schema["components"]["schemas"].keys() + assert "SomeParams" in schema["components"]["schemas"] async def test_each_nats_request_should_have_different_id(app, natsapi_mock): diff --git a/tests/test_routing.py b/tests/test_routing.py index fcce431..c6b7a64 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -1,6 +1,4 @@ import pytest - - from pydantic import BaseModel from natsapi import NatsAPI, SubjectRouter From 951c10cf2d42f738e61e61612a5e6dd6bb54fef4 Mon Sep 17 00:00:00 2001 From: sevaho Date: Fri, 28 Feb 2025 17:47:52 +0100 Subject: [PATCH 07/24] Fix issue where return value model is not allowed and should be a dict --- natsapi/client/client.py | 15 ++++++++++++++- natsapi/utils.py | 2 -- tests/test_requests.py | 8 ++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/natsapi/client/client.py b/natsapi/client/client.py index 74c6109..76cf4e8 100644 --- a/natsapi/client/client.py +++ b/natsapi/client/client.py @@ -2,6 +2,7 @@ import inspect import logging import secrets +import json from collections.abc import Callable from ssl import create_default_context from typing import Any @@ -54,7 +55,12 @@ async def publish_on_reply(self, subject, payload): await self.nats.publish(subject, payload) async def request( - self, subject: str, params: dict[str, Any] = dict(), timeout=60, method: str = None, headers: dict = None, + self, + subject: str, + params: dict[str, Any] = dict(), + timeout=60, + method: str = None, + headers: dict = None, ) -> JsonRPCReply: """ method: legacy attribute, used for backwards compatibility @@ -120,6 +126,13 @@ async def _handle_request(self, msg): result = await handler(app=self.app, **params) else: result = handler(self.app, **params) + + if not isinstance(result, dict): + if hasattr(result, "dict"): + result = result.dict() + elif hasattr(result, "json"): + result = json.loads(result.json()) + reply = JsonRPCReply(id=request.id, result=result) except Exception as exc: if not request: diff --git a/natsapi/utils.py b/natsapi/utils.py index 805cf2b..08470fc 100644 --- a/natsapi/utils.py +++ b/natsapi/utils.py @@ -6,12 +6,10 @@ from typing import Any from pydantic import BaseConfig, BaseModel, create_model - from pydantic.fields import FieldInfo from pydantic.v1.schema import model_process_schema from natsapi._compat import PYDANTIC_V2, ModelField - from natsapi.asyncapi.constants import REF_PREFIX from natsapi.exceptions import NatsAPIError diff --git a/tests/test_requests.py b/tests/test_requests.py index 567852a..3d9cc47 100644 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -83,6 +83,14 @@ async def _(app, foo: int): reply = await app.nc.request("natsapi.development.foo", {"foo": 1}) assert reply.result["status"] == "OK" +async def test_payload_with_empty_request_method_and_method__in_subject_get_successful_reply_with_return_model(app): + @app.request(subject="foo") + async def _(app, foo: int): + return StatusResult(status="OK") + + reply = await app.nc.request("natsapi.development.foo", {"foo": 1}) + assert reply.result["status"] == "OK" + async def test_unhandled_application_error_should_get_failed_reply(app): expected = EOFError("Unhandled exception, e.g. UniqueViolationError") From 7cbf918800318919e18a1f5abd7fba5fbfb9dff7 Mon Sep 17 00:00:00 2001 From: sevaho Date: Fri, 28 Feb 2025 17:47:59 +0100 Subject: [PATCH 08/24] Change to ruff --- poetry.lock | 121 ++++++++++++++++++--------------- pyproject.toml | 6 +- tests/test_jsonable_encoder.py | 5 +- 3 files changed, 69 insertions(+), 63 deletions(-) diff --git a/poetry.lock b/poetry.lock index e81ab15..b40d8d3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -310,67 +310,78 @@ files = [ [[package]] name = "coverage" -version = "5.5" +version = "7.6.12" description = "Code coverage measurement for Python" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +python-versions = ">=3.9" files = [ - {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, - {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, - {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, - {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, - {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, - {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, - {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, - {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, - {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, - {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, - {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, - {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, - {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, - {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, - {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, - {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, - {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, - {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, - {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, - {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, - {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, - {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, - {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, - {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, - {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, - {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, - {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, - {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, - {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, - {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, - {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, - {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, - {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, - {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, - {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, - {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, - {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, - {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, - {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, - {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, - {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, - {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, - {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, - {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, - {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, - {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, - {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, - {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, + {file = "coverage-7.6.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8"}, + {file = "coverage-7.6.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879"}, + {file = "coverage-7.6.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06097c7abfa611c91edb9e6920264e5be1d6ceb374efb4986f38b09eed4cb2fe"}, + {file = "coverage-7.6.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:220fa6c0ad7d9caef57f2c8771918324563ef0d8272c94974717c3909664e674"}, + {file = "coverage-7.6.12-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3688b99604a24492bcfe1c106278c45586eb819bf66a654d8a9a1433022fb2eb"}, + {file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d1a987778b9c71da2fc8948e6f2656da6ef68f59298b7e9786849634c35d2c3c"}, + {file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cec6b9ce3bd2b7853d4a4563801292bfee40b030c05a3d29555fd2a8ee9bd68c"}, + {file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ace9048de91293e467b44bce0f0381345078389814ff6e18dbac8fdbf896360e"}, + {file = "coverage-7.6.12-cp310-cp310-win32.whl", hash = "sha256:ea31689f05043d520113e0552f039603c4dd71fa4c287b64cb3606140c66f425"}, + {file = "coverage-7.6.12-cp310-cp310-win_amd64.whl", hash = "sha256:676f92141e3c5492d2a1596d52287d0d963df21bf5e55c8b03075a60e1ddf8aa"}, + {file = "coverage-7.6.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e18aafdfb3e9ec0d261c942d35bd7c28d031c5855dadb491d2723ba54f4c3015"}, + {file = "coverage-7.6.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66fe626fd7aa5982cdebad23e49e78ef7dbb3e3c2a5960a2b53632f1f703ea45"}, + {file = "coverage-7.6.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ef01d70198431719af0b1f5dcbefc557d44a190e749004042927b2a3fed0702"}, + {file = "coverage-7.6.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e92ae5a289a4bc4c0aae710c0948d3c7892e20fd3588224ebe242039573bf0"}, + {file = "coverage-7.6.12-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e695df2c58ce526eeab11a2e915448d3eb76f75dffe338ea613c1201b33bab2f"}, + {file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d74c08e9aaef995f8c4ef6d202dbd219c318450fe2a76da624f2ebb9c8ec5d9f"}, + {file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e995b3b76ccedc27fe4f477b349b7d64597e53a43fc2961db9d3fbace085d69d"}, + {file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b1f097878d74fe51e1ddd1be62d8e3682748875b461232cf4b52ddc6e6db0bba"}, + {file = "coverage-7.6.12-cp311-cp311-win32.whl", hash = "sha256:1f7ffa05da41754e20512202c866d0ebfc440bba3b0ed15133070e20bf5aeb5f"}, + {file = "coverage-7.6.12-cp311-cp311-win_amd64.whl", hash = "sha256:e216c5c45f89ef8971373fd1c5d8d1164b81f7f5f06bbf23c37e7908d19e8558"}, + {file = "coverage-7.6.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad"}, + {file = "coverage-7.6.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3"}, + {file = "coverage-7.6.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574"}, + {file = "coverage-7.6.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985"}, + {file = "coverage-7.6.12-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750"}, + {file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea"}, + {file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3"}, + {file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a"}, + {file = "coverage-7.6.12-cp312-cp312-win32.whl", hash = "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95"}, + {file = "coverage-7.6.12-cp312-cp312-win_amd64.whl", hash = "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288"}, + {file = "coverage-7.6.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:488c27b3db0ebee97a830e6b5a3ea930c4a6e2c07f27a5e67e1b3532e76b9ef1"}, + {file = "coverage-7.6.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d1095bbee1851269f79fd8e0c9b5544e4c00c0c24965e66d8cba2eb5bb535fd"}, + {file = "coverage-7.6.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0533adc29adf6a69c1baa88c3d7dbcaadcffa21afbed3ca7a225a440e4744bf9"}, + {file = "coverage-7.6.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53c56358d470fa507a2b6e67a68fd002364d23c83741dbc4c2e0680d80ca227e"}, + {file = "coverage-7.6.12-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64cbb1a3027c79ca6310bf101014614f6e6e18c226474606cf725238cf5bc2d4"}, + {file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:79cac3390bfa9836bb795be377395f28410811c9066bc4eefd8015258a7578c6"}, + {file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b148068e881faa26d878ff63e79650e208e95cf1c22bd3f77c3ca7b1d9821a3"}, + {file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8bec2ac5da793c2685ce5319ca9bcf4eee683b8a1679051f8e6ec04c4f2fd7dc"}, + {file = "coverage-7.6.12-cp313-cp313-win32.whl", hash = "sha256:200e10beb6ddd7c3ded322a4186313d5ca9e63e33d8fab4faa67ef46d3460af3"}, + {file = "coverage-7.6.12-cp313-cp313-win_amd64.whl", hash = "sha256:2b996819ced9f7dbb812c701485d58f261bef08f9b85304d41219b1496b591ef"}, + {file = "coverage-7.6.12-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:299cf973a7abff87a30609879c10df0b3bfc33d021e1adabc29138a48888841e"}, + {file = "coverage-7.6.12-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4b467a8c56974bf06e543e69ad803c6865249d7a5ccf6980457ed2bc50312703"}, + {file = "coverage-7.6.12-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2458f275944db8129f95d91aee32c828a408481ecde3b30af31d552c2ce284a0"}, + {file = "coverage-7.6.12-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a9d8be07fb0832636a0f72b80d2a652fe665e80e720301fb22b191c3434d924"}, + {file = "coverage-7.6.12-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14d47376a4f445e9743f6c83291e60adb1b127607a3618e3185bbc8091f0467b"}, + {file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b95574d06aa9d2bd6e5cc35a5bbe35696342c96760b69dc4287dbd5abd4ad51d"}, + {file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:ecea0c38c9079570163d663c0433a9af4094a60aafdca491c6a3d248c7432827"}, + {file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2251fabcfee0a55a8578a9d29cecfee5f2de02f11530e7d5c5a05859aa85aee9"}, + {file = "coverage-7.6.12-cp313-cp313t-win32.whl", hash = "sha256:eb5507795caabd9b2ae3f1adc95f67b1104971c22c624bb354232d65c4fc90b3"}, + {file = "coverage-7.6.12-cp313-cp313t-win_amd64.whl", hash = "sha256:f60a297c3987c6c02ffb29effc70eadcbb412fe76947d394a1091a3615948e2f"}, + {file = "coverage-7.6.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e7575ab65ca8399c8c4f9a7d61bbd2d204c8b8e447aab9d355682205c9dd948d"}, + {file = "coverage-7.6.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8161d9fbc7e9fe2326de89cd0abb9f3599bccc1287db0aba285cb68d204ce929"}, + {file = "coverage-7.6.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a1e465f398c713f1b212400b4e79a09829cd42aebd360362cd89c5bdc44eb87"}, + {file = "coverage-7.6.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f25d8b92a4e31ff1bd873654ec367ae811b3a943583e05432ea29264782dc32c"}, + {file = "coverage-7.6.12-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a936309a65cc5ca80fa9f20a442ff9e2d06927ec9a4f54bcba9c14c066323f2"}, + {file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aa6f302a3a0b5f240ee201297fff0bbfe2fa0d415a94aeb257d8b461032389bd"}, + {file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f973643ef532d4f9be71dd88cf7588936685fdb576d93a79fe9f65bc337d9d73"}, + {file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:78f5243bb6b1060aed6213d5107744c19f9571ec76d54c99cc15938eb69e0e86"}, + {file = "coverage-7.6.12-cp39-cp39-win32.whl", hash = "sha256:69e62c5034291c845fc4df7f8155e8544178b6c774f97a99e2734b05eb5bed31"}, + {file = "coverage-7.6.12-cp39-cp39-win_amd64.whl", hash = "sha256:b01a840ecc25dce235ae4c1b6a0daefb2a203dba0e6e980637ee9c2f6ee0df57"}, + {file = "coverage-7.6.12-pp39.pp310-none-any.whl", hash = "sha256:7e39e845c4d764208e7b8f6a21c541ade741e2c41afabdfa1caa28687a3c98cf"}, + {file = "coverage-7.6.12-py3-none-any.whl", hash = "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953"}, + {file = "coverage-7.6.12.tar.gz", hash = "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2"}, ] [package.extras] -toml = ["toml"] +toml = ["tomli"] [[package]] name = "distlib" diff --git a/pyproject.toml b/pyproject.toml index 07914f1..d502cf4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,10 +53,8 @@ exclude = ["scripts"] target-version = "py312" [tool.ruff.lint] -extend-select = ["C90", "I", "B", "Q", "UP", "S", "COM", "C4", "T10", "SIM", "TID", "PTH", "ERA", "ASYNC"] -# ignore = ["F722", "E501","E741","W605","C901","F541","F811", "B026", "S101", "S104", "S105", "S106", "S113", "B008", "UP007", "UP008", "UP038"] -# ignore = ["F722", "E501","E741","W605","C901","F541","F811", "B026", "S101", "S104", "S105", "S106", "S113", "B008", "UP007", "UP008", "UP038"] -unfixable = ["B", "F811"] +extend-select = ["C90", "I", "B", "Q", "UP", "S", "COM", "C4", "T10", "SIM", "TID", "PTH", "ERA"] +ignore = ["S101", "UP038", "B017", "UP007", "UP008", "B006", "C408"] [tool.ruff.lint.mccabe] max-complexity = 25 diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py index 245aa19..97c0321 100644 --- a/tests/test_jsonable_encoder.py +++ b/tests/test_jsonable_encoder.py @@ -158,10 +158,7 @@ class MyModel(BaseModel): def test_encode_model_with_path(model_with_path): - if isinstance(model_with_path.path, PureWindowsPath): - expected = "\\foo\\bar" - else: - expected = "/foo/bar" + expected = "\\foo\\bar" if isinstance(model_with_path.path, PureWindowsPath) else "/foo/bar" assert jsonable_encoder(model_with_path) == {"path": expected} From b8c7b395eff3ba1a1600b753d925217c43e982fa Mon Sep 17 00:00:00 2001 From: sevaho Date: Fri, 28 Feb 2025 17:49:30 +0100 Subject: [PATCH 09/24] Resort --- natsapi/client/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/natsapi/client/client.py b/natsapi/client/client.py index 76cf4e8..200bca8 100644 --- a/natsapi/client/client.py +++ b/natsapi/client/client.py @@ -1,8 +1,8 @@ import asyncio import inspect +import json import logging import secrets -import json from collections.abc import Callable from ssl import create_default_context from typing import Any From eafb53d9cecb24c4ffe8e56829e4dd93d55bd212 Mon Sep 17 00:00:00 2001 From: sevaho Date: Mon, 3 Mar 2025 10:19:25 +0100 Subject: [PATCH 10/24] Reformated --- natsapi/_compat.py | 8 ++++++-- natsapi/applications.py | 9 +++++++-- natsapi/asyncapi/utils.py | 4 +++- natsapi/exceptions.py | 3 +-- natsapi/types.py | 1 + natsapi/utils.py | 8 ++++++-- pyproject.toml | 2 +- tests/asyncapi/test_generation.py | 5 ++++- tests/test_jsonable_encoder.py | 1 + tests/test_requests.py | 1 + 10 files changed, 31 insertions(+), 11 deletions(-) diff --git a/natsapi/_compat.py b/natsapi/_compat.py index 0b18bda..96ac3d0 100644 --- a/natsapi/_compat.py +++ b/natsapi/_compat.py @@ -190,7 +190,9 @@ def get_model_definitions( definitions: dict[str, dict[str, Any]] = {} for model in flat_models: m_schema, m_definitions, m_nested_models = model_process_schema( - model, model_name_map=model_name_map, ref_prefix=REF_PREFIX, + model, + model_name_map=model_name_map, + ref_prefix=REF_PREFIX, ) definitions.update(m_definitions) model_name = model_name_map[model] @@ -214,7 +216,9 @@ def get_definitions( def _regenerate_error_with_loc( - *, errors: Sequence[Any], loc_prefix: tuple[str | int, ...], + *, + errors: Sequence[Any], + loc_prefix: tuple[str | int, ...], ) -> list[dict[str, Any]]: updated_loc_errors: list[Any] = [ {**err, "loc": loc_prefix + err.get("loc", ())} for err in _normalize_errors(errors) diff --git a/natsapi/applications.py b/natsapi/applications.py index a842ec4..7604eb4 100644 --- a/natsapi/applications.py +++ b/natsapi/applications.py @@ -150,7 +150,10 @@ async def startup(self, loop=None): self._listen_to_signals() self.nc = NatsClient( - self.routes, app=self.app, config=self.client_config, exception_handlers=self._exception_handlers, + self.routes, + app=self.app, + config=self.client_config, + exception_handlers=self._exception_handlers, ) await self.nc.connect() logger.info("Connected to NATS server") @@ -164,7 +167,9 @@ async def startup(self, loop=None): for path in self._root_paths: sub_path = ".".join([path, ">"]) await self.nc.root_path_subscribe( - sub_path, cb=self.nc.handle_request, queue=self.client_config.subscribe.queue, + sub_path, + cb=self.nc.handle_request, + queue=self.client_config.subscribe.queue, ) self.include_subs( [ diff --git a/natsapi/asyncapi/utils.py b/natsapi/asyncapi/utils.py index 7df6359..07199fc 100644 --- a/natsapi/asyncapi/utils.py +++ b/natsapi/asyncapi/utils.py @@ -197,7 +197,9 @@ def get_asyncapi( # TODO: <26-02-25, Sebastiaan Van Hoecke> # Where to use the first paramter (see https://github.com/fastapi/fastapi/blob/master/fastapi/openapi/utils.py#L493) _, definitions = get_definitions( - fields=all_fields, schema_generator=schema_generator, model_name_map=model_name_map, + fields=all_fields, + schema_generator=schema_generator, + model_name_map=model_name_map, ) definitions[JsonRPCError.__name__] = JsonRPCError.schema() components["schemas"] = definitions diff --git a/natsapi/exceptions.py b/natsapi/exceptions.py index 7711ad5..7dad434 100644 --- a/natsapi/exceptions.py +++ b/natsapi/exceptions.py @@ -1,8 +1,7 @@ from typing import Any -class NatsAPIError(RuntimeError): - ... +class NatsAPIError(RuntimeError): ... class DuplicateRouteException(NatsAPIError): diff --git a/natsapi/types.py b/natsapi/types.py index 17f2dfa..6c45a07 100644 --- a/natsapi/types.py +++ b/natsapi/types.py @@ -1,4 +1,5 @@ """Yanked from FastApi.typing""" + from collections.abc import Callable from typing import Any, TypeVar diff --git a/natsapi/utils.py b/natsapi/utils.py index 08470fc..89015e0 100644 --- a/natsapi/utils.py +++ b/natsapi/utils.py @@ -57,7 +57,9 @@ def create_field( try: return ModelField(**kwargs) except RuntimeError as e: - raise NatsAPIError(f"Invalid args for reply field! Hint: check that {type_} is a valid pydantic field type") from e + raise NatsAPIError( + f"Invalid args for reply field! Hint: check that {type_} is a valid pydantic field type" + ) from e def get_model_definitions( @@ -68,7 +70,9 @@ def get_model_definitions( definitions: dict[str, dict[str, Any]] = {} for model in flat_models: m_schema, m_definitions, m_nested_models = model_process_schema( - model, model_name_map=model_name_map, ref_prefix=REF_PREFIX, + model, + model_name_map=model_name_map, + ref_prefix=REF_PREFIX, ) definitions.update(m_definitions) try: diff --git a/pyproject.toml b/pyproject.toml index d502cf4..d6a70c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "natsapi" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" description = "A Python microservice framework that speaks nats.io with asyncapi spec generation capability" authors = ["WeGroup NV "] readme = "README.md" diff --git a/tests/asyncapi/test_generation.py b/tests/asyncapi/test_generation.py index bf67918..9d68cba 100644 --- a/tests/asyncapi/test_generation.py +++ b/tests/asyncapi/test_generation.py @@ -97,7 +97,10 @@ def patched(): def test_asyncapi_schema_w_personal_title_should_generate(): client = NatsAPI( - "natsapi.development", title="My Nats Client", description="This is my nats client", version="2.4.3", + "natsapi.development", + title="My Nats Client", + description="This is my nats client", + version="2.4.3", ) client.generate_asyncapi() schema = client.asyncapi_schema diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py index 97c0321..90246c1 100644 --- a/tests/test_jsonable_encoder.py +++ b/tests/test_jsonable_encoder.py @@ -1,4 +1,5 @@ """yanked from fastapi""" + from datetime import UTC, datetime from enum import Enum from pathlib import PurePath, PurePosixPath, PureWindowsPath diff --git a/tests/test_requests.py b/tests/test_requests.py index 3d9cc47..5d2e4e5 100644 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -83,6 +83,7 @@ async def _(app, foo: int): reply = await app.nc.request("natsapi.development.foo", {"foo": 1}) assert reply.result["status"] == "OK" + async def test_payload_with_empty_request_method_and_method__in_subject_get_successful_reply_with_return_model(app): @app.request(subject="foo") async def _(app, foo: int): From 4b8d32eb5f7b159cc452763cc72688be52ac2901 Mon Sep 17 00:00:00 2001 From: sevaho Date: Mon, 3 Mar 2025 10:21:17 +0100 Subject: [PATCH 11/24] Add missing trailing comma --- natsapi/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/natsapi/utils.py b/natsapi/utils.py index 89015e0..49751a3 100644 --- a/natsapi/utils.py +++ b/natsapi/utils.py @@ -58,7 +58,7 @@ def create_field( return ModelField(**kwargs) except RuntimeError as e: raise NatsAPIError( - f"Invalid args for reply field! Hint: check that {type_} is a valid pydantic field type" + f"Invalid args for reply field! Hint: check that {type_} is a valid pydantic field type", ) from e From 4b75a535c0a2ef79cf625c54b3e736e9db81dfc3 Mon Sep 17 00:00:00 2001 From: sevaho Date: Mon, 3 Mar 2025 10:25:24 +0100 Subject: [PATCH 12/24] Add pydantic settings --- poetry.lock | 74 ++++++++++++++++++++++++++++++++++++-------------- pyproject.toml | 3 +- 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/poetry.lock b/poetry.lock index b40d8d3..f6a5b55 100644 --- a/poetry.lock +++ b/poetry.lock @@ -815,6 +815,26 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pydantic-settings" +version = "2.8.1" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c"}, + {file = "pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585"}, +] + +[package.dependencies] +pydantic = ">=2.7.0" +python-dotenv = ">=0.21.0" + +[package.extras] +azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0)"] +toml = ["tomli (>=2.0.1)"] +yaml = ["pyyaml (>=6.0.1)"] + [[package]] name = "pygments" version = "2.19.1" @@ -913,6 +933,20 @@ toml = "*" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + [[package]] name = "pyyaml" version = "6.0.2" @@ -1034,29 +1068,29 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.7.4" +version = "0.8.6" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.7.4-py3-none-linux_armv6l.whl", hash = "sha256:a4919925e7684a3f18e18243cd6bea7cfb8e968a6eaa8437971f681b7ec51478"}, - {file = "ruff-0.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfb365c135b830778dda8c04fb7d4280ed0b984e1aec27f574445231e20d6c63"}, - {file = "ruff-0.7.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:63a569b36bc66fbadec5beaa539dd81e0527cb258b94e29e0531ce41bacc1f20"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d06218747d361d06fd2fdac734e7fa92df36df93035db3dc2ad7aa9852cb109"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0cea28d0944f74ebc33e9f934238f15c758841f9f5edd180b5315c203293452"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80094ecd4793c68b2571b128f91754d60f692d64bc0d7272ec9197fdd09bf9ea"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:997512325c6620d1c4c2b15db49ef59543ef9cd0f4aa8065ec2ae5103cedc7e7"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00b4cf3a6b5fad6d1a66e7574d78956bbd09abfd6c8a997798f01f5da3d46a05"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7dbdc7d8274e1422722933d1edddfdc65b4336abf0b16dfcb9dedd6e6a517d06"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e92dfb5f00eaedb1501b2f906ccabfd67b2355bdf117fea9719fc99ac2145bc"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3bd726099f277d735dc38900b6a8d6cf070f80828877941983a57bca1cd92172"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2e32829c429dd081ee5ba39aef436603e5b22335c3d3fff013cd585806a6486a"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:662a63b4971807623f6f90c1fb664613f67cc182dc4d991471c23c541fee62dd"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:876f5e09eaae3eb76814c1d3b68879891d6fde4824c015d48e7a7da4cf066a3a"}, - {file = "ruff-0.7.4-py3-none-win32.whl", hash = "sha256:75c53f54904be42dd52a548728a5b572344b50d9b2873d13a3f8c5e3b91f5cac"}, - {file = "ruff-0.7.4-py3-none-win_amd64.whl", hash = "sha256:745775c7b39f914238ed1f1b0bebed0b9155a17cd8bc0b08d3c87e4703b990d6"}, - {file = "ruff-0.7.4-py3-none-win_arm64.whl", hash = "sha256:11bff065102c3ae9d3ea4dc9ecdfe5a5171349cdd0787c1fc64761212fc9cf1f"}, - {file = "ruff-0.7.4.tar.gz", hash = "sha256:cd12e35031f5af6b9b93715d8c4f40360070b2041f81273d0527683d5708fce2"}, + {file = "ruff-0.8.6-py3-none-linux_armv6l.whl", hash = "sha256:defed167955d42c68b407e8f2e6f56ba52520e790aba4ca707a9c88619e580e3"}, + {file = "ruff-0.8.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:54799ca3d67ae5e0b7a7ac234baa657a9c1784b48ec954a094da7c206e0365b1"}, + {file = "ruff-0.8.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e88b8f6d901477c41559ba540beeb5a671e14cd29ebd5683903572f4b40a9807"}, + {file = "ruff-0.8.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0509e8da430228236a18a677fcdb0c1f102dd26d5520f71f79b094963322ed25"}, + {file = "ruff-0.8.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91a7ddb221779871cf226100e677b5ea38c2d54e9e2c8ed847450ebbdf99b32d"}, + {file = "ruff-0.8.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:248b1fb3f739d01d528cc50b35ee9c4812aa58cc5935998e776bf8ed5b251e75"}, + {file = "ruff-0.8.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:bc3c083c50390cf69e7e1b5a5a7303898966be973664ec0c4a4acea82c1d4315"}, + {file = "ruff-0.8.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52d587092ab8df308635762386f45f4638badb0866355b2b86760f6d3c076188"}, + {file = "ruff-0.8.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:61323159cf21bc3897674e5adb27cd9e7700bab6b84de40d7be28c3d46dc67cf"}, + {file = "ruff-0.8.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ae4478b1471fc0c44ed52a6fb787e641a2ac58b1c1f91763bafbc2faddc5117"}, + {file = "ruff-0.8.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0c000a471d519b3e6cfc9c6680025d923b4ca140ce3e4612d1a2ef58e11f11fe"}, + {file = "ruff-0.8.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9257aa841e9e8d9b727423086f0fa9a86b6b420fbf4bf9e1465d1250ce8e4d8d"}, + {file = "ruff-0.8.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:45a56f61b24682f6f6709636949ae8cc82ae229d8d773b4c76c09ec83964a95a"}, + {file = "ruff-0.8.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:496dd38a53aa173481a7d8866bcd6451bd934d06976a2505028a50583e001b76"}, + {file = "ruff-0.8.6-py3-none-win32.whl", hash = "sha256:e169ea1b9eae61c99b257dc83b9ee6c76f89042752cb2d83486a7d6e48e8f764"}, + {file = "ruff-0.8.6-py3-none-win_amd64.whl", hash = "sha256:f1d70bef3d16fdc897ee290d7d20da3cbe4e26349f62e8a0274e7a3f4ce7a905"}, + {file = "ruff-0.8.6-py3-none-win_arm64.whl", hash = "sha256:7d7fc2377a04b6e04ffe588caad613d0c460eb2ecba4c0ccbbfe2bc973cbc162"}, + {file = "ruff-0.8.6.tar.gz", hash = "sha256:dcad24b81b62650b0eb8814f576fc65cfee8674772a6e24c9b747911801eeaa5"}, ] [[package]] @@ -1239,4 +1273,4 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "2155652ed37d58f4bcc57a15d55e6581dd92b39e7210a177df2f1de590914de3" +content-hash = "417b8d51d0fe9204241b9c5518fc445c51ba4007da56f3efd73331f965a7327c" diff --git a/pyproject.toml b/pyproject.toml index d6a70c3..596ed1d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ python = "^3.9" pydantic = ">=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0" nats-py = {extras = ["nkeys"], version = "^2.2.0"} +pydantic-settings = "^2.8.1" [tool.poetry.group.dev.dependencies] pre-commit = "^2.12.1" @@ -31,7 +32,7 @@ piprot = "^0.9.11" safety = "^1.10.3" pytest-asyncio = "^0.17.2" vulture = "^2.13" -ruff = "^0.7.4" +ruff = "^0.8.6" [tool.poetry.plugins."pytest11"] From 6ae153e4c2714e3aabd6614fd607c16ea7dcc9c2 Mon Sep 17 00:00:00 2001 From: sevaho Date: Mon, 3 Mar 2025 10:31:45 +0100 Subject: [PATCH 13/24] Adjust github workflow --- .github/workflows/test.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b56f0e7..70783fb 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -12,9 +12,10 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - # python-version: ["3.9", "3.10", "3.11"] - python-version: ["3.11"] + python-version: ["3.11", "3.12", "3.13"] poetry-version: ["1.8.3"] + pydantic-version: ["pydantic<2", "pydantic>=2"] + services: nats: @@ -33,6 +34,8 @@ jobs: with: poetry-version: ${{ matrix.poetry-version }} - name: Install Dependencies - run: poetry install + run: | + poetry install + poetry add "${{ matrix.pydantic-version }}" - name: Run CI scripts run: make ci From b8ef9ea56a7db01839be7bfb118a3d9305d32622 Mon Sep 17 00:00:00 2001 From: sevaho Date: Mon, 3 Mar 2025 10:46:40 +0100 Subject: [PATCH 14/24] Fix install python deps in ci workflow --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 70783fb..b700540 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -36,6 +36,6 @@ jobs: - name: Install Dependencies run: | poetry install - poetry add "${{ matrix.pydantic-version }}" + pip install --upgrade "${{ matrix.pydantic-version }}" - name: Run CI scripts run: make ci From f91a85c0a5d48416e2b0448f46d95980449878e6 Mon Sep 17 00:00:00 2001 From: sevaho Date: Mon, 3 Mar 2025 11:19:59 +0100 Subject: [PATCH 15/24] clean pyproject --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 596ed1d..896ed3c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,6 @@ generate-setup-file = false [tool.poetry.dependencies] python = "^3.9" pydantic = ">=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0" - nats-py = {extras = ["nkeys"], version = "^2.2.0"} pydantic-settings = "^2.8.1" From 369adf538930590f720cf2b3029eb8afa6d2048a Mon Sep 17 00:00:00 2001 From: Lander Moerkerke Date: Mon, 3 Mar 2025 11:20:03 +0100 Subject: [PATCH 16/24] Add failing test --- tests/plugins/test_mock.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/plugins/test_mock.py b/tests/plugins/test_mock.py index 4e3077a..4a60bf9 100644 --- a/tests/plugins/test_mock.py +++ b/tests/plugins/test_mock.py @@ -26,6 +26,23 @@ async def test_nats_mock_should_respond_with_mocked_response(app, natsapi_mock): assert reply.result == {"items": [{"id": 1}]}, reply.result +async def test_nats_mock_should_respond_with_mocked_response_given_a_model(app, natsapi_mock): + from pydantic import BaseModel + + class Foo(BaseModel): + items: list[str] + + # given: + await natsapi_mock.request(f"{ch}.items.retrieve", response=Foo(items=["a", "b"])) + + # when: + reply = await app.nc.request(f"{ch}.items.retrieve", timeout=1) + + # then: + assert not reply.error + assert reply.result == {"items": [{"id": 1}]}, reply.result + + async def test_be_able_to_intercept_nats_request_payload(app, natsapi_mock): # given: await natsapi_mock.request(f"{ch}.items.id.retrieve", response={"id": 1}) From cf39153ac0bc15a655fda9d2d6637ec7821391d1 Mon Sep 17 00:00:00 2001 From: Lander Moerkerke Date: Mon, 3 Mar 2025 11:23:38 +0100 Subject: [PATCH 17/24] Fix issue where mock with Pydantic model raises an error --- natsapi/plugin.py | 7 +++++++ tests/plugins/test_mock.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/natsapi/plugin.py b/natsapi/plugin.py index 018dc88..1c61d34 100644 --- a/natsapi/plugin.py +++ b/natsapi/plugin.py @@ -59,6 +59,13 @@ async def handle(self, message: Msg) -> None: try: result, error = self.responses[message.subject] + + if not isinstance(result, dict): + if hasattr(result, "dict"): + result = result.dict() + elif hasattr(result, "json"): + result = json.loads(result.json()) + response = JsonRPCReply(jsonrpc="2.0", id=uuid4(), error=error, result=result) except KeyError: exc = JsonRPCUnknownMethodException() diff --git a/tests/plugins/test_mock.py b/tests/plugins/test_mock.py index 4a60bf9..21308a8 100644 --- a/tests/plugins/test_mock.py +++ b/tests/plugins/test_mock.py @@ -40,7 +40,7 @@ class Foo(BaseModel): # then: assert not reply.error - assert reply.result == {"items": [{"id": 1}]}, reply.result + assert reply.result == {"items": ["a", "b"]}, reply.result async def test_be_able_to_intercept_nats_request_payload(app, natsapi_mock): From 9b03d85495963e77d8d984ee5ab78dc281358552 Mon Sep 17 00:00:00 2001 From: sevaho Date: Mon, 3 Mar 2025 12:01:03 +0100 Subject: [PATCH 18/24] Bump to 0.1.0-alpha.3 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 896ed3c..5a8f74e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "natsapi" -version = "0.1.0-alpha.2" +version = "0.1.0-alpha.3" description = "A Python microservice framework that speaks nats.io with asyncapi spec generation capability" authors = ["WeGroup NV "] readme = "README.md" From 181b6ec86695686200cc0c9074a32216681a8888 Mon Sep 17 00:00:00 2001 From: sevaho Date: Mon, 3 Mar 2025 12:19:55 +0100 Subject: [PATCH 19/24] Also test python3.9 and python3.10 --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b700540..362daf6 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] poetry-version: ["1.8.3"] pydantic-version: ["pydantic<2", "pydantic>=2"] From d04df47ad14156906cb5610d8ef2982f1741f072 Mon Sep 17 00:00:00 2001 From: sevaho Date: Mon, 3 Mar 2025 14:05:02 +0100 Subject: [PATCH 20/24] Move back to typing to pre pydantic v10 --- natsapi/_compat.py | 14 +- natsapi/applications.py | 106 +++++------ natsapi/asyncapi/models.py | 272 +++++++++++++-------------- natsapi/asyncapi/utils.py | 8 +- natsapi/client/client.py | 8 +- natsapi/client/config.py | 6 +- natsapi/encoders.py | 6 +- natsapi/models.py | 18 +- natsapi/routing.py | 144 +++++++------- natsapi/utils.py | 10 +- pyproject.toml | 2 +- tests/test_jsonable_encoder.py | 3 +- tests/test_method_type_conversion.py | 2 +- tests/test_models.py | 3 +- 14 files changed, 303 insertions(+), 299 deletions(-) diff --git a/natsapi/_compat.py b/natsapi/_compat.py index 96ac3d0..ba7d571 100644 --- a/natsapi/_compat.py +++ b/natsapi/_compat.py @@ -6,6 +6,8 @@ Annotated, Any, Literal, + Union, + Optional ) from pydantic import BaseModel @@ -16,7 +18,7 @@ PYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(".")[:2]) PYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2 -ModelNameMap = dict[type[BaseModel] | type[Enum], str] +ModelNameMap = Union[dict[type[BaseModel] , type[Enum], str]] if PYDANTIC_V2: from pydantic import ( @@ -40,7 +42,7 @@ class ModelField: field_info: FieldInfo name: str mode: Literal["validation", "serialization"] = "validation" - sub_fields: str | None = None + sub_fields: Optional[str] = None @property def alias(self) -> str: @@ -72,8 +74,8 @@ def validate( value: Any, values: dict[str, Any] = {}, # noqa: B006 *, - loc: tuple[int | str, ...] = (), - ) -> tuple[Any, list[dict[str, Any]] | None]: + loc: tuple[Union[int , str], ...] = (), + ) -> tuple[Any, list[dict[str, Any]]]: try: return ( self._type_adapter.validate_python(value, from_attributes=True), @@ -126,7 +128,7 @@ def get_definitions( dict[tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue], dict[str, dict[str, Any]], ]: - override_mode: Literal["validation"] | None = None if separate_input_output_schemas else "validation" + override_mode: Optiona[Literal["validation"]] = None if separate_input_output_schemas else "validation" inputs = [(field, override_mode or field.mode, field._type_adapter.core_schema) for field in fields] field_mapping, definitions = schema_generator.generate_definitions(inputs=inputs) return field_mapping, definitions # type: ignore[return-value] @@ -218,7 +220,7 @@ def get_definitions( def _regenerate_error_with_loc( *, errors: Sequence[Any], - loc_prefix: tuple[str | int, ...], + loc_prefix: tuple[Union[str , int], ...], ) -> list[dict[str, Any]]: updated_loc_errors: list[Any] = [ {**err, "loc": loc_prefix + err.get("loc", ())} for err in _normalize_errors(errors) diff --git a/natsapi/applications.py b/natsapi/applications.py index 7604eb4..9ac3bd5 100644 --- a/natsapi/applications.py +++ b/natsapi/applications.py @@ -2,7 +2,7 @@ import inspect import signal from collections.abc import Callable -from typing import Any +from typing import Any, Optional, Union from pydantic import BaseModel, ValidationError @@ -25,16 +25,16 @@ def __init__( root_path: str, *, app: Any = None, - client_config: Config | None = None, - rpc_methods: list[str] | None = None, - exception_handlers: dict[type[Exception], Callable[[type[Exception]], JsonRPCException]] | None = None, + client_config: Optional[Config] = None, + rpc_methods: Optional[list[str]] = None, + exception_handlers: Optional[dict[type[Exception], Callable[[type[Exception]], JsonRPCException]]] = None, title: str = "NatsAPI", version: str = "0.1.0", description: str = None, - tags: list[dict[str, Any]] | None = None, - servers: dict[str, str | Any] | None = None, - domain_errors: dict[str, Any] | None = None, - external_docs: dict[str, Any] | None = None, + tags: Optional[list[dict[str, Any]]] = None, + servers: Optional[dict[str, Union[str | Any]]] = None, + domain_errors: Optional[dict[str, Any]] = None, + external_docs: Optional[dict[str, Any]] = None, ): """ Parameters @@ -54,7 +54,7 @@ def __init__( self.asyncapi_tags = tags or [] self.asyncapi_version = "2.0.0" self.domain_errors: Errors = domain_errors - self.asyncapi_schema: dict[str, Any] | None = None + self.asyncapi_schema: Optional[dict[str, Any]] = None self.nc: NatsClient = None self.subs: set[Sub] = set() self.pubs: set[Pub] = set() @@ -229,13 +229,13 @@ def add_request( endpoint: Callable[..., Any], *, result=type[Any], - skip_validation: bool | None = False, - description: str | None = None, - deprecated: bool | None = None, - tags: list[str] | None = None, - summary: str | None = None, - suggested_timeout: float | None = None, - include_schema: bool | None = True, + skip_validation: Optional[bool] = False, + description: Optional[str] = None, + deprecated: Optional[bool] = None, + tags: Optional[list[str]] = None, + summary: Optional[str] = None, + suggested_timeout: Optional[float] = None, + include_schema: Optional[bool] = True, ) -> None: request = Request( subject=subject, @@ -265,12 +265,12 @@ def add_publish( subject: str, endpoint: Callable[..., Any], *, - skip_validation: bool | None = False, - description: str | None = None, - deprecated: bool | None = None, - tags: list[str] | None = None, - summary: str | None = None, - include_schema: bool | None = True, + skip_validation: Optional[bool] = False, + description: Optional[str] = None, + deprecated: Optional[bool] = None, + tags: Optional[list[str]] = None, + summary: Optional[str] = None, + include_schema: Optional[bool] = True, ) -> None: publish = Publish( subject=subject, @@ -298,13 +298,13 @@ def request( subject: str, *, result=type[Any], - skip_validation: bool | None = False, - description: str | None = None, - deprecated: bool | None = None, - tags: list[str] | None = None, - summary: str | None = None, - suggested_timeout: float | None = None, - include_schema: bool | None = True, + skip_validation: Optional[bool] = False, + description: Optional[str] = None, + deprecated: Optional[bool] = None, + tags: Optional[list[str]] = None, + summary: Optional[str] = None, + suggested_timeout: Optional[float] = None, + include_schema: Optional[bool] = True, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_request( @@ -327,12 +327,12 @@ def publish( self, subject: str, *, - skip_validation: bool | None = False, - description: str | None = None, - deprecated: bool | None = None, - tags: list[str] | None = None, - summary: str | None = None, - include_schema: bool | None = True, + skip_validation: Optional[bool] = False, + description: Optional[str] = None, + deprecated: Optional[bool] = None, + tags: Optional[list[str]] = None, + summary: Optional[str] = None, + include_schema: Optional[bool] = True, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_publish( @@ -354,10 +354,10 @@ def add_pub( subject: str, params: BaseModel, *, - summary: str | None = None, - description: str | None = None, - tags: list[str] | None = None, - externalDocs: ExternalDocumentation | None = None, + summary: Optional[str] = None, + description: Optional[str] = None, + tags: Optional[list[str]] = None, + externalDocs: Optional[ExternalDocumentation] = None, ) -> None: """ Include pub in asyncapi schema @@ -377,10 +377,10 @@ def pub( subject: str, *, params=type[Any], - description: str | None = None, - tags: list[str] | None = None, - summary: str | None = None, - externalDocs: ExternalDocumentation | None = None, + description: Optional[str] = None, + tags: Optional[list[str]] = None, + summary: Optional[str] = None, + externalDocs: Optional[ExternalDocumentation] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_pub( @@ -403,11 +403,11 @@ def add_sub( self, subject: str, *, - queue: str | None = None, - summary: str | None = None, - description: str | None = None, - tags: list[str] | None = None, - externalDocs: ExternalDocumentation | None = None, + queue: Optional[str] = None, + summary: Optional[str] = None, + description: Optional[str] = None, + tags: Optional[list[str]] = None, + externalDocs: Optional[ExternalDocumentation] = None, ) -> None: """ Include sub in asyncapi schema @@ -426,11 +426,11 @@ def sub( self, subject: str, *, - queue: str | None = None, - description: str | None = None, - tags: list[str] | None = None, - summary: str | None = None, - externalDocs: ExternalDocumentation | None = None, + queue: Optional[str] = None, + description: Optional[str] = None, + tags: Optional[list[str]] = None, + summary: Optional[str] = None, + externalDocs: Optional[ExternalDocumentation] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_sub( diff --git a/natsapi/asyncapi/models.py b/natsapi/asyncapi/models.py index e0956c8..ac50732 100644 --- a/natsapi/asyncapi/models.py +++ b/natsapi/asyncapi/models.py @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, Optional, Union from pydantic import BaseModel, Field, validator @@ -6,81 +6,81 @@ class ExternalDocumentation(BaseModel): - description: str | None + description: Optional[str] url: str class Discriminator(BaseModel): propertyName: dict[str, Any] - mapping: dict[str, str] | None = None + mapping: Optional[dict[str, str]] = None class SchemaBase(BaseModel): - ref: str | None = Field(None, alias="$ref") - title: str | None = None - multipleOf: float | None = None - maximum: float | None = None - exclusiveMaximum: float | None = None - minimum: float | None = None - exclusiveMinimum: float | None = None - maxLength: int | None = Field(None, gte=0) - minLength: int | None = Field(None, gte=0) - pattern: str | None = None - maxItems: int | None = Field(None, gte=0) - minItems: int | None = Field(None, gte=0) - uniqueItems: bool | None = None - maxProperties: int | None = Field(None, gte=0) - minProperties: int | None = Field(None, gte=0) - required: list[str] | None = None - enum: list[Any] | None = None - type: str | None = None - allOf: list[Any] | None = None - oneOf: list[Any] | None = None - anyOf: list[Any] | None = None - not_: Any | None = Field(None, alias="not") - items: Any | None = None - properties: dict[str, Any] | None = None - additionalProperties: dict[str, Any] | bool | None = None - description: str | None = None - format: str | None = None - default: Any | None = None - nullable: bool | None = None - discriminator: Discriminator | None = None - readOnly: bool | None = None - writeOnly: bool | None = None - externalDocs: ExternalDocumentation | None = None - example: Any | None = None - deprecated: bool | None = None + ref: Optional[str] = Field(None, alias="$ref") + title: Optional[str] = None + multipleOf: Optional[float] = None + maximum: Optional[float] = None + exclusiveMaximum: Optional[float] = None + minimum: Optional[float] = None + exclusiveMinimum: Optional[float] = None + maxLength: Optional[int] = Field(None, gte=0) + minLength: Optional[int] = Field(None, gte=0) + pattern: Optional[str] = None + maxItems: Optional[int] = Field(None, gte=0) + minItems: Optional[int] = Field(None, gte=0) + uniqueItems: Optional[bool] = None + maxProperties: Optional[int] = Field(None, gte=0) + minProperties: Optional[int] = Field(None, gte=0) + required: Optional[list[str]] = None + enum: Optional[list[Any]] = None + type: Optional[str] = None + allOf: Optional[list[Any]] = None + oneOf: Optional[list[Any]] = None + anyOf: Optional[list[Any]] = None + not_: Any = Field(None, alias="not") + items: Any = None + properties: Optional[dict[str, Any]] = None + additionalProperties: Optional[dict[str, Any] | bool] = None + description: Optional[str] = None + format: Optional[str] = None + default: Any = None + nullable: Optional[bool] = None + discriminator: Optional[Discriminator] = None + readOnly: Optional[bool] = None + writeOnly: Optional[bool] = None + externalDocs: Optional[ExternalDocumentation] = None + example: Any = None + deprecated: Optional[bool] = None class Schema(SchemaBase): - allOf: list[SchemaBase] | None = None - oneOf: list[SchemaBase] | None = None - anyOf: list[SchemaBase] | None = None - not_: SchemaBase | None = Field(None, alias="not") - items: SchemaBase | None = None - properties: dict[str, SchemaBase] | None = None - additionalProperties: dict[str, Any] | bool | None = None + allOf: Optional[list[SchemaBase]] = None + oneOf: Optional[list[SchemaBase]] = None + anyOf: Optional[list[SchemaBase]] = None + not_: Optional[SchemaBase] = Field(None, alias="not") + items: Optional[SchemaBase] = None + properties: Optional[dict[str, SchemaBase]] = None + additionalProperties: Optional[dict[str, Any] | bool] = None class Contact(BaseModel): - name: str | None - url: str | None - email: str | None + name: Optional[str] + url: Optional[str] + email: Optional[str] class License(BaseModel): name: str - url: str | None + url: Optional[str] class Info(BaseModel): title: str version: str - description: str | None = None - termsOfService: str | None = None - contact: Contact | None = None - licence: License | None = None + description: Optional[str] = None + termsOfService: Optional[str] = None + contact: Optional[Contact] = None + licence: Optional[License] = None class Reference(BaseModel): @@ -89,8 +89,8 @@ class Reference(BaseModel): class Tag(BaseModel): name: str - description: str | None = None - externalDocs: ExternalDocumentation | None = None + description: Optional[str] = None + externalDocs: Optional[ExternalDocumentation] = None # TODO: Add nats bindings @@ -99,7 +99,7 @@ class NatsBindings(BaseModel): class BindingsBase(BaseModel): - nats: NatsBindings | Reference | None + nats: Union[NatsBindings, Reference, None] class ServerBindings(BindingsBase): @@ -120,113 +120,113 @@ class MessageBindings(BindingsBase): class CorrelationId(BaseModel): location: str - description: str | None + description: Optional[str] class MessageTrait(BaseModel): - headers: Schema | Reference | None - correlationId: CorrelationId | Reference | None - schemaFormat: str | None - contentType: str | None - name: str | None - title: str | None - summary: str | None - description: str | None - tags: list[Tag] | None - externalDocs: ExternalDocumentation | None - bindings: MessageBindings | Reference | None - examples: dict[str, Any] | None + headers: Union[Schema, Reference, None] + correlationId: Union[CorrelationId, Reference, None] + schemaFormat: Optional[str] + contentType: Optional[str] + name: Optional[str] + title: Optional[str] + summary: Optional[str] + description: Optional[str] + tags: Optional[list[Tag]] + externalDocs: Optional[ExternalDocumentation] + bindings: Union[MessageBindings, Reference, None] + examples: Optional[dict[str, Any]] class Message(BaseModel): - headers: Schema | Reference | None = None - payload: Any | None = None - correlationId: CorrelationId | Reference | None = None - schemaFormat: str | None = None - contentType: str | None = None - name: str | None = None - title: str | None = None - summary: str | None = None - description: str | None = None - tags: list[Tag] | None = None - externalDocs: ExternalDocumentation | None = None - bindings: MessageBindings | Reference | None = None - examples: dict[str, Any] | None = None - traits: MessageTrait | Reference | None = None + headers: Union[Schema, Reference, None] = None + payload: Any = None + correlationId: Union[CorrelationId, Reference, None] = None + schemaFormat: Optional[str] = None + contentType: Optional[str] = None + name: Optional[str] = None + title: Optional[str] = None + summary: Optional[str] = None + description: Optional[str] = None + tags: Optional[list[Tag]] = None + externalDocs: Optional[ExternalDocumentation] = None + bindings: Union[MessageBindings, Reference, None] = None + examples: Optional[dict[str, Any]] = None + traits: Union[MessageTrait, Reference, None] = None class OperationTrait(BaseModel): - operationId: str | None = None - summary: str | None = None - description: str | None = None - tags: list[Tag] | None = None - externalDocs: ExternalDocumentation | None = None - bindings: OperationBindings | Reference | None = None + operationId: Optional[str] = None + summary: Optional[str] = None + description: Optional[str] = None + tags: Optional[list[Tag]] = None + externalDocs: Optional[ExternalDocumentation] = None + bindings: Union[OperationBindings, Reference, None] = None class Operation(BaseModel): - operationId: str | None = None - summary: str | None = None - description: str | None = None - tags: list[Tag] | None = None - externalDocs: ExternalDocumentation | None = None - bindings: OperationBindings | Reference | None = None - traits: list[OperationTrait | Reference] | None = None - message: Message | Reference | None = None + operationId: Optional[str] = None + summary: Optional[str] = None + description: Optional[str] = None + tags: Optional[list[Tag]] = None + externalDocs: Optional[ExternalDocumentation] = None + bindings: Union[OperationBindings, Reference, None] = None + traits: Optional[list[Union[OperationTrait, Reference]]] = None + message: Union[Message, Reference, None] = None class SubscribeOperation(Operation): - queue: str | None = Field(None, alias="x-queue") + queue: Optional[str] = Field(None, alias="x-queue") class RequestOperation(Operation): - replies: list[Message | Reference] | None = None - suggestedTimeout: float | None = Field(None, alias="x-suggested-timeout") + replies: Optional[list[Union[Message, Reference]]] = None + suggestedTimeout: Optional[float] = Field(None, alias="x-suggested-timeout") class Parameter(BaseModel): - description: str | None = None - schema_: dict[str, Any] | Reference = Field(..., alias="schema") - location: str | None = None + description: Optional[str] = None + schema_: Union[dict[str, Any], Reference] = Field(..., alias="schema") + location: Optional[str] = None class ChannelItem(BaseModel): - description: str | None = None - subscribe: SubscribeOperation | None = None - publish: Operation | None = None - request: RequestOperation | None = None - parameters: dict[str, Parameter | Reference] | None = None - bindings: ChannelBindings | Reference | None = None - deprecated: bool | None = None + description: Optional[str] = None + subscribe: Optional[SubscribeOperation] = None + publish: Optional[Operation] = None + request: Optional[RequestOperation] = None + parameters: Optional[dict[str, Union[Parameter, Reference]]] = None + bindings: Union[ChannelBindings, Reference, None] = None + deprecated: Optional[bool] = None class ServerVariable(BaseModel): - enum: list[str] | None = None - default: str | None = None - description: str | None = None - examples: list[str] | None = None + enum: Optional[list[str]] = None + default: Optional[str] = None + description: Optional[str] = None + examples: Optional[list[str]] = None class Server(BaseModel): url: str protocol: str - protocolVersion: str | None - description: str | None - variables: dict[str, ServerVariable] | None - bindings: ServerBindings | Reference | None = None + protocolVersion: Optional[str] + description: Optional[str] + variables: Optional[dict[str, ServerVariable]] + bindings: Union[ServerBindings, Reference, None] = None class Components(BaseModel): - schemas: dict[str, Schema | Reference] | None = None - messages: dict[str, Message | Reference] | None = None - parameters: dict[str, dict[str, Parameter] | Reference] | None = None - correlationIds: dict[str, CorrelationId | Reference] | None = None - operationTraits: dict[str, OperationTrait | Reference] | None = None - messageTraits: dict[str, MessageTrait | Reference] | None = None - serverBindings: dict[str, ServerBindings | Reference] | None = None - channelBindings: dict[str, ChannelBindings | Reference] | None = None - operationBindings: dict[str, OperationBindings | Reference] | None = None - messageBindings: dict[str, MessageBindings | Reference] | None = None + schemas: Optional[dict[str, Union[Schema, Reference]]] = None + messages: Optional[dict[str, Union[Message, Reference]]] = None + parameters: Optional[dict[str, Union[dict[str, Parameter], Reference]]] = None + correlationIds: Optional[dict[str, Union[CorrelationId, Reference]]] = None + operationTraits: Optional[dict[str, Union[OperationTrait, Reference]]] = None + messageTraits: Optional[dict[str, Union[MessageTrait, Reference]]] = None + serverBindings: Optional[dict[str, Union[ServerBindings, Reference]]] = None + channelBindings: Optional[dict[str, Union[ChannelBindings, Reference]]] = None + operationBindings: Optional[dict[str, Union[OperationBindings, Reference]]] = None + messageBindings: Optional[dict[str, Union[MessageBindings, Reference]]] = None class Range(BaseModel): @@ -246,12 +246,12 @@ class Errors(BaseModel): class AsyncAPI(BaseModel): asyncapi: str - id: str | None = None + id: Optional[str] = None info: Info - servers: dict[str, Server] | None = None - defaultContentType: str | None = "application/json" - channels: dict[str, ChannelItem | Reference] | None = None - components: Components | None = None - tags: list[Tag] | None = None - externalDocs: ExternalDocumentation | None = None - errors: Errors | None = None + servers: Optional[dict[str, Server]] = None + defaultContentType: Optional[str] = "application/json" + channels: Optional[dict[str, Union[ChannelItem, Reference]]] = None + components: Optional[Components] = None + tags: Optional[list[Tag]] = None + externalDocs: Optional[ExternalDocumentation] = None + errors: Optional[Errors] = None diff --git a/natsapi/asyncapi/utils.py b/natsapi/asyncapi/utils.py index 07199fc..319c01d 100644 --- a/natsapi/asyncapi/utils.py +++ b/natsapi/asyncapi/utils.py @@ -1,7 +1,7 @@ import typing from collections.abc import Sequence from enum import Enum -from typing import Any +from typing import Any, Optional, Union from pydantic import BaseModel @@ -33,7 +33,7 @@ def _get_flat_fields_from_params(fields: list[ModelField]) -> list[ModelField]: return fields -def get_fields_from_routes(routes: Sequence[Request], pubs: Sequence[Pub]) -> set[type[BaseModel] | type[Enum]]: +def get_fields_from_routes(routes: Sequence[Request], pubs: Sequence[Pub]) -> Union[set[type[BaseModel] , type[Enum]]]: replies_from_routes: set[ModelField] = set() requests_from_routes: set[ModelField] = set() messages_from_pubs: set[ModelField] = set() @@ -181,8 +181,8 @@ def get_asyncapi( routes: dict[str, Request], subs: list[Sub], pubs: list[Pub], - description: str | None = None, - servers: dict[str, Server] | None = None, + description: Optional[str] = None, + servers: Optional[dict[str, Server]] = None, ) -> dict[str, Any]: subjects: dict[str, dict[str, Any]] = {} info = {"title": title, "version": version} diff --git a/natsapi/client/client.py b/natsapi/client/client.py index 200bca8..b242a94 100644 --- a/natsapi/client/client.py +++ b/natsapi/client/client.py @@ -5,7 +5,7 @@ import secrets from collections.abc import Callable from ssl import create_default_context -from typing import Any +from typing import Any, Optional from uuid import uuid4 from nats.aio.client import Client as NATS @@ -23,8 +23,8 @@ def __init__( self, routes: dict[str, Request], app: Any = None, - config: Config | None = None, - exception_handlers: dict[type[Exception], Callable[[type[Exception]], JsonRPCException]] | None = None, + config: Optional[Config] = None, + exception_handlers: Optional[dict[type[Exception], Callable[[type[Exception]], JsonRPCException]]] = None, ) -> None: self.routes = routes self.app = app @@ -146,7 +146,7 @@ async def _handle_request(self, msg): finally: await self.publish_on_reply(msg.reply, reply.json().encode()) - def _lookup_exception_handler(self, exc: Exception) -> Callable | None: + def _lookup_exception_handler(self, exc: Exception) -> Optional[Callable]: """ Gets list of all the types the exception instance inherits from and checks if exception type is in the 'exception_handlers' dict. diff --git a/natsapi/client/config.py b/natsapi/client/config.py index 2099a8a..fb175de 100644 --- a/natsapi/client/config.py +++ b/natsapi/client/config.py @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, Optional from nats.aio.client import ( DEFAULT_CONNECT_TIMEOUT, @@ -44,8 +44,8 @@ class ConnectConfig(BaseSettings): signature_cb: Any = None user_jwt_cb: Any = None user_credentials: Any = None - nkeys_seed: str | None = None - flush_timeout: float | None = None + nkeys_seed: Optional[str] = None + flush_timeout: Optional[float] = None pending_size: int = DEFAULT_PENDING_SIZE diff --git a/natsapi/encoders.py b/natsapi/encoders.py index 77131b9..5f81e8f 100644 --- a/natsapi/encoders.py +++ b/natsapi/encoders.py @@ -5,7 +5,7 @@ from enum import Enum from pathlib import PurePath from types import GeneratorType -from typing import Any +from typing import Any, Union from pydantic import BaseModel @@ -29,8 +29,8 @@ def generate_encoders_by_class_tuples( def jsonable_encoder( obj: Any, - include: SetIntStr | DictIntStrAny | None = None, - exclude: SetIntStr | DictIntStrAny | None = None, + include: Union[SetIntStr, DictIntStrAny, None] = None, + exclude: Union[SetIntStr, DictIntStrAny, None] = None, by_alias: bool = True, exclude_unset: bool = False, exclude_defaults: bool = False, diff --git a/natsapi/models.py b/natsapi/models.py index bdb549c..62983fb 100644 --- a/natsapi/models.py +++ b/natsapi/models.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Any +from typing import Any, Optional from uuid import UUID, uuid4 from pydantic import BaseModel, Field, create_model, root_validator, validator @@ -9,12 +9,12 @@ class ErrorDetail(BaseModel): type: str - target: str | None = None + target: Optional[str] = None message: str class ErrorData(BaseModel): - type: str | None = None + type: Optional[str] = None errors: list[ErrorDetail] = [] @@ -31,8 +31,8 @@ class JsonRPCError(BaseModel): class JsonRPCReply(BaseModel): jsonrpc: JSON_RPC_VERSION = Field("2.0") id: UUID = Field(...) - result: dict[str, Any] | None = Field(None) - error: JsonRPCError | None = Field(None) + result: Optional[dict[str, Any]] = Field(None) + error: Optional[JsonRPCError] = Field(None) @root_validator(pre=True) def check_result_and_error(cls, values): @@ -46,14 +46,14 @@ def check_result_and_error(cls, values): class JsonRPCRequest(BaseModel): - jsonrpc: JSON_RPC_VERSION | None = Field("2.0") - timeout: float | None = Field( + jsonrpc: Optional[JSON_RPC_VERSION] = Field("2.0") + timeout: Optional[float] = Field( None, description="Timeout set by client, should be equal to the timeout set when doing nc.request, if publish use '-1'", ) - method: str | None = Field(None, description="Request method used") + method: Optional[str] = Field(None, description="Request method used") params: dict[str, Any] = Field(...) - id: UUID | None = Field(None, alias="id", description="UUID created at the creation of the request") + id: Optional[UUID] = Field(None, alias="id", description="UUID created at the creation of the request") @validator("id", pre=True, always=True) def set_id(cls, id): diff --git a/natsapi/routing.py b/natsapi/routing.py index da24b68..8eee88d 100644 --- a/natsapi/routing.py +++ b/natsapi/routing.py @@ -1,6 +1,6 @@ import inspect from collections.abc import Callable -from typing import Any +from typing import Any, Optional from pydantic import BaseModel @@ -16,13 +16,13 @@ def __init__( endpoint: Callable[..., Any], *, result=type[Any], - skip_validation: bool | None = False, - description: str | None = None, - deprecated: bool | None = None, - tags: list[str] | None = None, - summary: str | None = None, - include_schema: bool | None = True, - suggested_timeout: float | None = None, + skip_validation: Optional[bool] = False, + description: Optional[str] = None, + deprecated: Optional[bool] = None, + tags: Optional[list[str]] = None, + summary: Optional[str] = None, + include_schema: Optional[bool] = True, + suggested_timeout: Optional[float] = None, ): self.subject = subject self.endpoint = endpoint @@ -52,12 +52,12 @@ def __init__( subject: str, endpoint: Callable[..., Any], *, - skip_validation: bool | None = False, - description: str | None = None, - deprecated: bool | None = None, - tags: list[str] | None = None, - summary: str | None = None, - include_schema: bool | None = True, + skip_validation: Optional[bool] = False, + description: Optional[str] = None, + deprecated: Optional[bool] = None, + tags: Optional[list[str]] = None, + summary: Optional[str] = None, + include_schema: Optional[bool] = True, ): self.subject = subject self.endpoint = endpoint @@ -82,11 +82,11 @@ def __init__( self, subject: str, *, - queue: str | None = None, - summary: str | None = None, - description: str | None = None, - tags: list[str] | None = None, - externalDocs: ExternalDocumentation | None = None, + queue: Optional[str] = None, + summary: Optional[str] = None, + description: Optional[str] = None, + tags: Optional[list[str]] = None, + externalDocs: Optional[ExternalDocumentation] = None, ): self.subject = subject @@ -103,10 +103,10 @@ def __init__( subject: str, params: BaseModel, *, - summary: str | None = None, - description: str | None = None, - tags: list[str] | None = None, - externalDocs: ExternalDocumentation | None = None, + summary: Optional[str] = None, + description: Optional[str] = None, + tags: Optional[list[str]] = None, + externalDocs: Optional[ExternalDocumentation] = None, ): self.subject = subject self.summary = summary @@ -122,11 +122,11 @@ def __init__( self, *, prefix: str = None, - tags: list[str] | None = None, - routes: list[Request] | None = None, - subs: set[Sub] | None = None, - pubs: set[Pub] | None = None, - deprecated: bool | None = None, + tags: Optional[list[str]] = None, + routes: Optional[list[Request]] = None, + subs: Optional[set[Sub]] = None, + pubs: Optional[set[Pub]] = None, + deprecated: Optional[bool] = None, include_in_schema: bool = True, ) -> None: self.prefix = prefix @@ -143,13 +143,13 @@ def add_request( endpoint: Callable[..., Any], *, result=type[Any], - skip_validation: bool | None = False, - description: str | None = None, - deprecated: bool | None = None, - tags: list[str] | None = None, - summary: str | None = None, - suggested_timeout: float | None = None, - include_schema: bool | None = True, + skip_validation: Optional[bool] = False, + description: Optional[str] = None, + deprecated: Optional[bool] = None, + tags: Optional[list[str]] = None, + summary: Optional[str] = None, + suggested_timeout: Optional[float] = None, + include_schema: Optional[bool] = True, ) -> None: current_tags = self.tags.copy() if tags: @@ -174,12 +174,12 @@ def add_publish( subject: str, endpoint: Callable[..., Any], *, - skip_validation: bool | None = False, - description: str | None = None, - deprecated: bool | None = None, - tags: list[str] | None = None, - summary: str | None = None, - include_schema: bool | None = True, + skip_validation: Optional[bool] = False, + description: Optional[str] = None, + deprecated: Optional[bool] = None, + tags: Optional[list[str]] = None, + summary: Optional[str] = None, + include_schema: Optional[bool] = True, ) -> None: current_tags = self.tags.copy() if tags: @@ -202,13 +202,13 @@ def request( subject: str, *, result=type[Any], - skip_validation: bool | None = False, - description: str | None = None, - deprecated: bool | None = None, - tags: list[str] | None = None, - summary: str | None = None, - suggested_timeout: float | None = None, - include_schema: bool | None = True, + skip_validation: Optional[bool] = False, + description: Optional[str] = None, + deprecated: Optional[bool] = None, + tags: Optional[list[str]] = None, + summary: Optional[str] = None, + suggested_timeout: Optional[float] = None, + include_schema: Optional[bool] = True, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_request( @@ -232,12 +232,12 @@ def publish( subject: str, *, result=type[Any], - skip_validation: bool | None = False, - description: str | None = None, - deprecated: bool | None = None, - tags: list[str] | None = None, - summary: str | None = None, - include_schema: bool | None = True, + skip_validation: Optional[bool] = False, + description: Optional[str] = None, + deprecated: Optional[bool] = None, + tags: Optional[list[str]] = None, + summary: Optional[str] = None, + include_schema: Optional[bool] = True, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_publish( @@ -259,10 +259,10 @@ def add_pub( subject: str, params: BaseModel, *, - summary: str | None = None, - description: str | None = None, - tags: list[str] | None = None, - externalDocs: ExternalDocumentation | None = None, + summary: Optional[str] = None, + description: Optional[str] = None, + tags: Optional[list[str]] = None, + externalDocs: Optional[ExternalDocumentation] = None, ) -> None: """ Include pub in asyncapi schema @@ -282,10 +282,10 @@ def pub( subject: str, *, params=type[Any], - description: str | None = None, - tags: list[str] | None = None, - summary: str | None = None, - externalDocs: ExternalDocumentation | None = None, + description: Optional[str] = None, + tags: Optional[list[str]] = None, + summary: Optional[str] = None, + externalDocs: Optional[ExternalDocumentation] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_pub( @@ -304,11 +304,11 @@ def add_sub( self, subject: str, *, - queue: str | None = None, - summary: str | None = None, - description: str | None = None, - tags: list[str] | None = None, - externalDocs: ExternalDocumentation | None = None, + queue: Optional[str] = None, + summary: Optional[str] = None, + description: Optional[str] = None, + tags: Optional[list[str]] = None, + externalDocs: Optional[ExternalDocumentation] = None, ) -> None: """ Include sub in asyncapi schema @@ -327,11 +327,11 @@ def sub( self, subject: str, *, - queue: str | None = None, - description: str | None = None, - tags: list[str] | None = None, - summary: str | None = None, - externalDocs: ExternalDocumentation | None = None, + queue: Optional[str] = None, + description: Optional[str] = None, + tags: Optional[list[str]] = None, + summary: Optional[str] = None, + externalDocs: Optional[ExternalDocumentation] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_sub( diff --git a/natsapi/utils.py b/natsapi/utils.py index 49751a3..a8e0fee 100644 --- a/natsapi/utils.py +++ b/natsapi/utils.py @@ -3,7 +3,7 @@ import re from collections.abc import Callable from enum import Enum -from typing import Any +from typing import Any, Optional, Union from pydantic import BaseConfig, BaseModel, create_model from pydantic.fields import FieldInfo @@ -30,9 +30,9 @@ def generate_operation_id_for_subject(*, summary: str, subject: str) -> str: def create_field( name: str, type_: type[Any], - class_validators: dict[str, Any] | None = None, + class_validators: Optional[dict[str, Any]] = None, model_config: type[BaseConfig] = BaseConfig, - field_info: FieldInfo | None = None, + field_info: Optional[FieldInfo] = None, ) -> ModelField: """ Yanked from fastapi.utils @@ -64,8 +64,8 @@ def create_field( def get_model_definitions( *, - flat_models: set[type[BaseModel] | type[Enum]], - model_name_map: dict[type[BaseModel] | type[Enum], str], + flat_models: Union[set[type[BaseModel], type[Enum]]], + model_name_map: Union[dict[type[BaseModel], type[Enum], str]], ) -> dict[str, Any]: definitions: dict[str, dict[str, Any]] = {} for model in flat_models: diff --git a/pyproject.toml b/pyproject.toml index 5a8f74e..737d0d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,7 @@ build-backend = "poetry.core.masonry.api" [tool.ruff] line-length = 120 exclude = ["scripts"] -target-version = "py312" +target-version = "py39" [tool.ruff.lint] extend-select = ["C90", "I", "B", "Q", "UP", "S", "COM", "C4", "T10", "SIM", "TID", "PTH", "ERA"] diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py index 90246c1..605430f 100644 --- a/tests/test_jsonable_encoder.py +++ b/tests/test_jsonable_encoder.py @@ -9,6 +9,7 @@ from natsapi._compat import RootModel from natsapi.encoders import jsonable_encoder +from typing import Optional class Person: @@ -59,7 +60,7 @@ class RoleEnum(Enum): class ModelWithConfig(BaseModel): - role: RoleEnum | None = None + role: Optional[RoleEnum] = None class Config: use_enum_values = True diff --git a/tests/test_method_type_conversion.py b/tests/test_method_type_conversion.py index df03b30..71ebb86 100644 --- a/tests/test_method_type_conversion.py +++ b/tests/test_method_type_conversion.py @@ -17,7 +17,7 @@ class FastAPI(BaseModel): async def test_method_parameters_should_get_parsed_to_correct_typing(app): class ThemesCreateCmd(BaseModel): primary: str - color: str | None | None = None + color: typing.Union[str , None] = None @app.request("themes.CREATE", result=StatusResult) async def create_theme(app, data: ThemesCreateCmd): diff --git a/tests/test_models.py b/tests/test_models.py index ed79fd6..6e009db 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,5 +1,6 @@ from uuid import uuid4 +from typing import Optional import pytest from pydantic import BaseModel, ValidationError from pydantic.fields import Field @@ -10,7 +11,7 @@ def test_change_param_type_of_model_should_change(): class Params(BaseModel): foo: int = Field(...) - bar: str | None = Field(None) + bar: Optional[str] = Field(None) new_model = JsonRPCRequest.with_params(Params) From dc3f3816bd7dd39f41004c1f7ac999900f56beed Mon Sep 17 00:00:00 2001 From: sevaho Date: Mon, 3 Mar 2025 14:13:52 +0100 Subject: [PATCH 21/24] Be python39 compatible --- natsapi/_compat.py | 20 +++++++------------- natsapi/applications.py | 2 +- natsapi/asyncapi/models.py | 4 ++-- natsapi/client/config.py | 4 ++-- natsapi/encoders.py | 4 ++-- tests/test_jsonable_encoder.py | 7 ++++--- tests/test_method_type_conversion.py | 2 +- tests/test_models.py | 2 +- 8 files changed, 20 insertions(+), 25 deletions(-) diff --git a/natsapi/_compat.py b/natsapi/_compat.py index ba7d571..7ed5658 100644 --- a/natsapi/_compat.py +++ b/natsapi/_compat.py @@ -2,13 +2,7 @@ from dataclasses import dataclass from enum import Enum from functools import lru_cache -from typing import ( - Annotated, - Any, - Literal, - Union, - Optional -) +from typing import Annotated, Any, Literal, Optional, Union from pydantic import BaseModel from pydantic.version import VERSION as PYDANTIC_VERSION @@ -18,7 +12,7 @@ PYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(".")[:2]) PYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2 -ModelNameMap = Union[dict[type[BaseModel] , type[Enum], str]] +ModelNameMap = Union[dict[type[BaseModel], type[Enum], str]] if PYDANTIC_V2: from pydantic import ( @@ -74,7 +68,7 @@ def validate( value: Any, values: dict[str, Any] = {}, # noqa: B006 *, - loc: tuple[Union[int , str], ...] = (), + loc: tuple[Union[int, str], ...] = (), ) -> tuple[Any, list[dict[str, Any]]]: try: return ( @@ -128,7 +122,7 @@ def get_definitions( dict[tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue], dict[str, dict[str, Any]], ]: - override_mode: Optiona[Literal["validation"]] = None if separate_input_output_schemas else "validation" + override_mode: Optional[Literal["validation"]] = None if separate_input_output_schemas else "validation" inputs = [(field, override_mode or field.mode, field._type_adapter.core_schema) for field in fields] field_mapping, definitions = schema_generator.generate_definitions(inputs=inputs) return field_mapping, definitions # type: ignore[return-value] @@ -186,8 +180,8 @@ def get_compat_model_name_map(fields: list[ModelField]): def get_model_definitions( *, - flat_models: set[type[BaseModel] | type[Enum]], - model_name_map: dict[type[BaseModel] | type[Enum], str], + flat_models: Union[set[type[BaseModel], type[Enum]]], + model_name_map: Union[dict[type[BaseModel], type[Enum], str]], ) -> dict[str, Any]: definitions: dict[str, dict[str, Any]] = {} for model in flat_models: @@ -220,7 +214,7 @@ def get_definitions( def _regenerate_error_with_loc( *, errors: Sequence[Any], - loc_prefix: tuple[Union[str , int], ...], + loc_prefix: tuple[Union[str, int], ...], ) -> list[dict[str, Any]]: updated_loc_errors: list[Any] = [ {**err, "loc": loc_prefix + err.get("loc", ())} for err in _normalize_errors(errors) diff --git a/natsapi/applications.py b/natsapi/applications.py index 9ac3bd5..005fc10 100644 --- a/natsapi/applications.py +++ b/natsapi/applications.py @@ -32,7 +32,7 @@ def __init__( version: str = "0.1.0", description: str = None, tags: Optional[list[dict[str, Any]]] = None, - servers: Optional[dict[str, Union[str | Any]]] = None, + servers: Optional[dict[str, Union[str , Any]]] = None, domain_errors: Optional[dict[str, Any]] = None, external_docs: Optional[dict[str, Any]] = None, ): diff --git a/natsapi/asyncapi/models.py b/natsapi/asyncapi/models.py index ac50732..a14f6d0 100644 --- a/natsapi/asyncapi/models.py +++ b/natsapi/asyncapi/models.py @@ -40,7 +40,7 @@ class SchemaBase(BaseModel): not_: Any = Field(None, alias="not") items: Any = None properties: Optional[dict[str, Any]] = None - additionalProperties: Optional[dict[str, Any] | bool] = None + additionalProperties: Optional[Union[dict[str, Any] , bool]] = None description: Optional[str] = None format: Optional[str] = None default: Any = None @@ -60,7 +60,7 @@ class Schema(SchemaBase): not_: Optional[SchemaBase] = Field(None, alias="not") items: Optional[SchemaBase] = None properties: Optional[dict[str, SchemaBase]] = None - additionalProperties: Optional[dict[str, Any] | bool] = None + additionalProperties: Optional[Union[dict[str, Any] , bool]] = None class Contact(BaseModel): diff --git a/natsapi/client/config.py b/natsapi/client/config.py index fb175de..1845462 100644 --- a/natsapi/client/config.py +++ b/natsapi/client/config.py @@ -1,4 +1,4 @@ -from typing import Any, Optional +from typing import Any, Optional, Union from nats.aio.client import ( DEFAULT_CONNECT_TIMEOUT, @@ -17,7 +17,7 @@ class ConnectConfig(BaseSettings): - servers: str | list[str] = ["nats://127.0.0.1:4222"] + servers: Union[str , list[str]] = ["nats://127.0.0.1:4222"] error_cb: Any = None closed_cb: Any = None reconnected_cb: Any = None diff --git a/natsapi/encoders.py b/natsapi/encoders.py index 5f81e8f..77ffdfd 100644 --- a/natsapi/encoders.py +++ b/natsapi/encoders.py @@ -11,8 +11,8 @@ from natsapi._compat import ENCODERS_BY_TYPE, PYDANTIC_V2 -SetIntStr = set[int | str] -DictIntStrAny = dict[int | str, Any] +SetIntStr = set[Union[int , str]] +DictIntStrAny = dict[Union[int , str], Any] def generate_encoders_by_class_tuples( diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py index 605430f..da85341 100644 --- a/tests/test_jsonable_encoder.py +++ b/tests/test_jsonable_encoder.py @@ -1,15 +1,15 @@ """yanked from fastapi""" -from datetime import UTC, datetime +from datetime import datetime, timezone from enum import Enum from pathlib import PurePath, PurePosixPath, PureWindowsPath +from typing import Optional import pytest from pydantic import BaseModel, ConfigDict, Field, ValidationError, create_model from natsapi._compat import RootModel from natsapi.encoders import jsonable_encoder -from typing import Optional class Person: @@ -45,8 +45,9 @@ def __dict__(self): class ModelWithCustomEncoder(BaseModel): dt_field: datetime + class Config: - json_encoders = {datetime: lambda dt: dt.replace(microsecond=0, tzinfo=UTC).isoformat()} + json_encoders = {datetime: lambda dt: dt.replace(microsecond=0, tzinfo=timezone.utc).isoformat()} class ModelWithCustomEncoderSubclass(ModelWithCustomEncoder): diff --git a/tests/test_method_type_conversion.py b/tests/test_method_type_conversion.py index 71ebb86..8a6ad23 100644 --- a/tests/test_method_type_conversion.py +++ b/tests/test_method_type_conversion.py @@ -62,7 +62,7 @@ class TypeResult(BaseModel): async def test_exotic_typing_should_convert_to_correct_type(app): @app.request("themes.CONVERT", result=TypeResult) - async def convert_theme(app, param: list[str] | int): + async def convert_theme(app, param: typing.Union[list[str] , int]): return {"typing": type(param).__name__} reply = await app.nc.request("natsapi.development.themes.CONVERT", {"param": ["foo", "bar", "baz"]}) diff --git a/tests/test_models.py b/tests/test_models.py index 6e009db..1292cdc 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,6 +1,6 @@ +from typing import Optional from uuid import uuid4 -from typing import Optional import pytest from pydantic import BaseModel, ValidationError from pydantic.fields import Field From 7e3715aec4c74e1ba505e112933b3b1355768029 Mon Sep 17 00:00:00 2001 From: sevaho Date: Tue, 4 Mar 2025 09:33:58 +0100 Subject: [PATCH 22/24] Fix issue with json schema generation sorting logic --- natsapi/_compat.py | 9 +++++++++ natsapi/asyncapi/utils.py | 4 ++-- poetry.lock | 6 +++--- pyproject.toml | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/natsapi/_compat.py b/natsapi/_compat.py index 7ed5658..6a0c9c4 100644 --- a/natsapi/_compat.py +++ b/natsapi/_compat.py @@ -226,3 +226,12 @@ def _regenerate_error_with_loc( @lru_cache def get_cached_model_fields(model: type[BaseModel]) -> list[ModelField]: return get_model_fields(model) + + +class MyGenerateJsonSchema(GenerateJsonSchema): + def sort(self, value: JsonSchemaValue, parent_key=None) -> JsonSchemaValue: + """ + No-op, we don't want to sort schema values at all. + https://docs.pydantic.dev/latest/concepts/json_schema/#json-schema-sorting + """ + return value diff --git a/natsapi/asyncapi/utils.py b/natsapi/asyncapi/utils.py index 319c01d..04abfb7 100644 --- a/natsapi/asyncapi/utils.py +++ b/natsapi/asyncapi/utils.py @@ -6,7 +6,7 @@ from pydantic import BaseModel from natsapi._compat import ( - GenerateJsonSchema, + MyGenerateJsonSchema, ModelField, get_cached_model_fields, get_compat_model_name_map, @@ -193,7 +193,7 @@ def get_asyncapi( all_fields = get_fields_from_routes(routes.values(), pubs) model_name_map = get_compat_model_name_map(all_fields) - schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE) + schema_generator = MyGenerateJsonSchema(ref_template=REF_TEMPLATE) # TODO: <26-02-25, Sebastiaan Van Hoecke> # Where to use the first paramter (see https://github.com/fastapi/fastapi/blob/master/fastapi/openapi/utils.py#L493) _, definitions = get_definitions( diff --git a/poetry.lock b/poetry.lock index f6a5b55..ec0ae7b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -877,13 +877,13 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] [[package]] name = "pytest" -version = "8.3.4" +version = "8.3.5" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, - {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, + {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, + {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index 737d0d0..3f74091 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "natsapi" -version = "0.1.0-alpha.3" +version = "0.1.0-alpha.4" description = "A Python microservice framework that speaks nats.io with asyncapi spec generation capability" authors = ["WeGroup NV "] readme = "README.md" From cfd182e14977b99b3f7907dec20abeaf0d748979 Mon Sep 17 00:00:00 2001 From: sevaho Date: Tue, 4 Mar 2025 09:37:13 +0100 Subject: [PATCH 23/24] Fix isort --- natsapi/asyncapi/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/natsapi/asyncapi/utils.py b/natsapi/asyncapi/utils.py index 04abfb7..a987959 100644 --- a/natsapi/asyncapi/utils.py +++ b/natsapi/asyncapi/utils.py @@ -6,8 +6,8 @@ from pydantic import BaseModel from natsapi._compat import ( - MyGenerateJsonSchema, ModelField, + MyGenerateJsonSchema, get_cached_model_fields, get_compat_model_name_map, get_definitions, From 012eaf3d6b39ef26d2a3812fd084f8fdf8b03690 Mon Sep 17 00:00:00 2001 From: sevaho Date: Tue, 4 Mar 2025 09:39:35 +0100 Subject: [PATCH 24/24] Fix unused var with args --- natsapi/_compat.py | 2 +- natsapi/applications.py | 2 +- natsapi/asyncapi/models.py | 4 ++-- natsapi/asyncapi/utils.py | 2 +- natsapi/client/config.py | 2 +- natsapi/encoders.py | 4 ++-- tests/test_jsonable_encoder.py | 1 - tests/test_method_type_conversion.py | 4 ++-- 8 files changed, 10 insertions(+), 11 deletions(-) diff --git a/natsapi/_compat.py b/natsapi/_compat.py index 6a0c9c4..e8f1d82 100644 --- a/natsapi/_compat.py +++ b/natsapi/_compat.py @@ -229,7 +229,7 @@ def get_cached_model_fields(model: type[BaseModel]) -> list[ModelField]: class MyGenerateJsonSchema(GenerateJsonSchema): - def sort(self, value: JsonSchemaValue, parent_key=None) -> JsonSchemaValue: + def sort(self, value: JsonSchemaValue, *args) -> JsonSchemaValue: """ No-op, we don't want to sort schema values at all. https://docs.pydantic.dev/latest/concepts/json_schema/#json-schema-sorting diff --git a/natsapi/applications.py b/natsapi/applications.py index 005fc10..c17f3de 100644 --- a/natsapi/applications.py +++ b/natsapi/applications.py @@ -32,7 +32,7 @@ def __init__( version: str = "0.1.0", description: str = None, tags: Optional[list[dict[str, Any]]] = None, - servers: Optional[dict[str, Union[str , Any]]] = None, + servers: Optional[dict[str, Union[str, Any]]] = None, domain_errors: Optional[dict[str, Any]] = None, external_docs: Optional[dict[str, Any]] = None, ): diff --git a/natsapi/asyncapi/models.py b/natsapi/asyncapi/models.py index a14f6d0..87aa1df 100644 --- a/natsapi/asyncapi/models.py +++ b/natsapi/asyncapi/models.py @@ -40,7 +40,7 @@ class SchemaBase(BaseModel): not_: Any = Field(None, alias="not") items: Any = None properties: Optional[dict[str, Any]] = None - additionalProperties: Optional[Union[dict[str, Any] , bool]] = None + additionalProperties: Optional[Union[dict[str, Any], bool]] = None description: Optional[str] = None format: Optional[str] = None default: Any = None @@ -60,7 +60,7 @@ class Schema(SchemaBase): not_: Optional[SchemaBase] = Field(None, alias="not") items: Optional[SchemaBase] = None properties: Optional[dict[str, SchemaBase]] = None - additionalProperties: Optional[Union[dict[str, Any] , bool]] = None + additionalProperties: Optional[Union[dict[str, Any], bool]] = None class Contact(BaseModel): diff --git a/natsapi/asyncapi/utils.py b/natsapi/asyncapi/utils.py index a987959..2ce2c33 100644 --- a/natsapi/asyncapi/utils.py +++ b/natsapi/asyncapi/utils.py @@ -33,7 +33,7 @@ def _get_flat_fields_from_params(fields: list[ModelField]) -> list[ModelField]: return fields -def get_fields_from_routes(routes: Sequence[Request], pubs: Sequence[Pub]) -> Union[set[type[BaseModel] , type[Enum]]]: +def get_fields_from_routes(routes: Sequence[Request], pubs: Sequence[Pub]) -> Union[set[type[BaseModel], type[Enum]]]: replies_from_routes: set[ModelField] = set() requests_from_routes: set[ModelField] = set() messages_from_pubs: set[ModelField] = set() diff --git a/natsapi/client/config.py b/natsapi/client/config.py index 1845462..57a6a03 100644 --- a/natsapi/client/config.py +++ b/natsapi/client/config.py @@ -17,7 +17,7 @@ class ConnectConfig(BaseSettings): - servers: Union[str , list[str]] = ["nats://127.0.0.1:4222"] + servers: Union[str, list[str]] = ["nats://127.0.0.1:4222"] error_cb: Any = None closed_cb: Any = None reconnected_cb: Any = None diff --git a/natsapi/encoders.py b/natsapi/encoders.py index 77ffdfd..e952127 100644 --- a/natsapi/encoders.py +++ b/natsapi/encoders.py @@ -11,8 +11,8 @@ from natsapi._compat import ENCODERS_BY_TYPE, PYDANTIC_V2 -SetIntStr = set[Union[int , str]] -DictIntStrAny = dict[Union[int , str], Any] +SetIntStr = set[Union[int, str]] +DictIntStrAny = dict[Union[int, str], Any] def generate_encoders_by_class_tuples( diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py index da85341..d6cc7a0 100644 --- a/tests/test_jsonable_encoder.py +++ b/tests/test_jsonable_encoder.py @@ -45,7 +45,6 @@ def __dict__(self): class ModelWithCustomEncoder(BaseModel): dt_field: datetime - class Config: json_encoders = {datetime: lambda dt: dt.replace(microsecond=0, tzinfo=timezone.utc).isoformat()} diff --git a/tests/test_method_type_conversion.py b/tests/test_method_type_conversion.py index 8a6ad23..a6ef709 100644 --- a/tests/test_method_type_conversion.py +++ b/tests/test_method_type_conversion.py @@ -17,7 +17,7 @@ class FastAPI(BaseModel): async def test_method_parameters_should_get_parsed_to_correct_typing(app): class ThemesCreateCmd(BaseModel): primary: str - color: typing.Union[str , None] = None + color: typing.Union[str, None] = None @app.request("themes.CREATE", result=StatusResult) async def create_theme(app, data: ThemesCreateCmd): @@ -62,7 +62,7 @@ class TypeResult(BaseModel): async def test_exotic_typing_should_convert_to_correct_type(app): @app.request("themes.CONVERT", result=TypeResult) - async def convert_theme(app, param: typing.Union[list[str] , int]): + async def convert_theme(app, param: typing.Union[list[str], int]): return {"typing": type(param).__name__} reply = await app.nc.request("natsapi.development.themes.CONVERT", {"param": ["foo", "bar", "baz"]})