From f8e063b4097555eced0208f3fc1c30ddfae47cc8 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Wed, 1 Oct 2025 07:49:25 +0000
Subject: [PATCH 01/55] New endpoint and related tests for requests with
non-object body.
---
.../libcore/suites/test_get_request_data.robot | 18 +++++++++++++++++-
tests/server/testserver.py | 9 +++++++++
2 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/tests/libcore/suites/test_get_request_data.robot b/tests/libcore/suites/test_get_request_data.robot
index 3c317a7..f124aac 100644
--- a/tests/libcore/suites/test_get_request_data.robot
+++ b/tests/libcore/suites/test_get_request_data.robot
@@ -26,7 +26,7 @@ Test Get Request Data For Invalid Method On Endpoint
Should Be Equal ${request_data.headers} ${dict}
Should Not Be True ${request_data.has_body}
-Test Get Request Data For Endpoint With RequestBody
+Test Get Request Data For Endpoint With Object RequestBody
${request_data}= Get Request Data path=/employees method=post
VAR &{dict}= &{EMPTY}
VAR @{list}= @{EMPTY}
@@ -53,6 +53,22 @@ Test Get Request Data For Endpoint With RequestBody
Should Be Equal ${request_data.headers} ${expected_headers}
Should Be True ${request_data.has_body}
+Test Get Request Data For Endpoint With Array Request Body
+ VAR ${empty_array_seen}= ${FALSE}
+ VAR ${non_empty_array_seen}= ${FALSE}
+ WHILE not ($empty_array_seen and $non_empty_array_seen)
+ ${request_data}= Get Request Data path=/events/ method=put
+ TRY
+ Dictionary Should Contain Key ${request_data.dto.message} message
+ List Should Not Contain Duplicates ${request_data.dto.details}
+ VAR ${non_empty_array_seen}= ${TRUE}
+ EXCEPT * AttributeError: 'DefaultDto' object has no attribute 'message' type=glob
+ Should Be Equal ${request_data.body_schema.type} array
+ Should Be Equal As Strings ${request_data.dto} DefaultDto()
+ VAR ${empty_array_seen}= ${TRUE}
+ END
+ END
+
Test Get Request Data For Endpoint Without RequestBody But With DtoClass
${request_data}= Get Request Data path=/wagegroups/{wagegroup_id} method=delete
VAR &{dict}= &{EMPTY}
diff --git a/tests/server/testserver.py b/tests/server/testserver.py
index 30690f6..1341bef 100644
--- a/tests/server/testserver.py
+++ b/tests/server/testserver.py
@@ -177,6 +177,15 @@ def post_event(event: Event, draft: bool = Header(False)) -> Event:
return event
+@app.put("/events/", status_code=201, response_model=list[Event])
+def put_events(events: list[Event]) -> list[Event]:
+ for event in events:
+ event.details.append(Detail(detail=f"Published {datetime.datetime.now()}"))
+ event.details.append(Detail(detail="Event details subject to change."))
+ EVENTS.append(event)
+ return events
+
+
@app.get(
"/energy_label/{zipcode}/{home_number}",
status_code=200,
From 4ef9dc395f4ddec8fd502fabfab6914a27657dfb Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Thu, 2 Oct 2025 11:40:10 +0000
Subject: [PATCH 02/55] Reorganize into submodules, moving logic into
oas_models
---
src/OpenApiDriver/__init__.py | 6 +-
src/OpenApiDriver/openapi_executors.py | 15 +-
src/OpenApiDriver/openapi_reader.py | 2 +-
src/OpenApiLibCore/__init__.py | 14 +-
.../data_constraints/__init__.py | 0
.../{ => data_constraints}/dto_base.py | 134 +++++++++++++++++-
.../data_generation/__init__.py | 6 -
.../data_generation/body_data_generation.py | 12 +-
.../data_generation/data_generation_core.py | 52 ++++---
.../data_invalidation.py | 24 +++-
.../{ => data_generation}/localized_faker.py | 0
.../{ => data_generation}/value_utils.py | 74 +---------
src/OpenApiLibCore/dto_utils.py | 125 ----------------
src/OpenApiLibCore/keyword_logic/__init__.py | 0
.../{ => keyword_logic}/path_functions.py | 8 +-
.../{ => keyword_logic}/path_invalidation.py | 0
.../{ => keyword_logic}/resource_relations.py | 8 +-
.../{ => keyword_logic}/validation.py | 4 +-
src/OpenApiLibCore/models/__init__.py | 17 +++
src/OpenApiLibCore/{ => models}/oas_cache.py | 0
.../{models.py => models/oas_models.py} | 57 +++++++-
.../{ => models}/request_data.py | 9 +-
src/OpenApiLibCore/openapi_libcore.py | 29 ++--
src/OpenApiLibCore/protocols.py | 10 +-
src/OpenApiLibCore/utils/__init__.py | 0
.../{ => utils}/parameter_utils.py | 2 +-
src/openapi_libgen/generator.py | 2 +-
src/openapi_libgen/spec_parser.py | 4 +-
src/openapi_libgen/templates/library.jinja | 2 +-
29 files changed, 320 insertions(+), 296 deletions(-)
create mode 100644 src/OpenApiLibCore/data_constraints/__init__.py
rename src/OpenApiLibCore/{ => data_constraints}/dto_base.py (66%)
rename src/OpenApiLibCore/{ => data_generation}/data_invalidation.py (93%)
rename src/OpenApiLibCore/{ => data_generation}/localized_faker.py (100%)
rename src/OpenApiLibCore/{ => data_generation}/value_utils.py (70%)
delete mode 100644 src/OpenApiLibCore/dto_utils.py
create mode 100644 src/OpenApiLibCore/keyword_logic/__init__.py
rename src/OpenApiLibCore/{ => keyword_logic}/path_functions.py (97%)
rename src/OpenApiLibCore/{ => keyword_logic}/path_invalidation.py (100%)
rename src/OpenApiLibCore/{ => keyword_logic}/resource_relations.py (86%)
rename src/OpenApiLibCore/{ => keyword_logic}/validation.py (99%)
create mode 100644 src/OpenApiLibCore/models/__init__.py
rename src/OpenApiLibCore/{ => models}/oas_cache.py (100%)
rename src/OpenApiLibCore/{models.py => models/oas_models.py} (91%)
rename src/OpenApiLibCore/{ => models}/request_data.py (97%)
create mode 100644 src/OpenApiLibCore/utils/__init__.py
rename src/OpenApiLibCore/{ => utils}/parameter_utils.py (97%)
diff --git a/src/OpenApiDriver/__init__.py b/src/OpenApiDriver/__init__.py
index 7557256..a177631 100644
--- a/src/OpenApiDriver/__init__.py
+++ b/src/OpenApiDriver/__init__.py
@@ -13,7 +13,7 @@
from importlib.metadata import version
from OpenApiDriver.openapidriver import OpenApiDriver
-from OpenApiLibCore.dto_base import (
+from OpenApiLibCore.data_constraints.dto_base import (
Dto,
IdDependency,
IdReference,
@@ -22,8 +22,8 @@
ResourceRelation,
UniquePropertyValueConstraint,
)
-from OpenApiLibCore.validation import ValidationLevel
-from OpenApiLibCore.value_utils import IGNORE
+from OpenApiLibCore.data_generation.value_utils import IGNORE
+from OpenApiLibCore.keyword_logic.validation import ValidationLevel
try:
__version__ = version("robotframework-openapidriver")
diff --git a/src/OpenApiDriver/openapi_executors.py b/src/OpenApiDriver/openapi_executors.py
index 3daa00d..8eb3385 100644
--- a/src/OpenApiDriver/openapi_executors.py
+++ b/src/OpenApiDriver/openapi_executors.py
@@ -25,6 +25,7 @@
ValidationLevel,
)
from OpenApiLibCore.annotations import JSON
+from OpenApiLibCore.models.oas_models import ArraySchema, ObjectSchema
run_keyword = BuiltIn().run_keyword
default_str_mapping: Mapping[str, str] = MappingProxyType({})
@@ -199,7 +200,10 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
params = request_data.params
headers = request_data.headers
if request_data.has_body:
- json_data = request_data.dto.as_dict()
+ if isinstance(request_data.body_schema, ArraySchema):
+ json_data = request_data.dto.as_list()
+ else:
+ json_data = request_data.dto.as_dict()
# when patching, get the original data to check only patched data has changed
if method == "PATCH":
original_data = self.get_original_data(url=url)
@@ -285,9 +289,12 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
request_data = run_keyword("get_request_data", path, method)
params = request_data.get_required_params()
headers = request_data.get_required_headers()
- json_data = (
- request_data.get_minimal_body_dict() if request_data.has_body else {}
- )
+ if isinstance(request_data.body_schema, ObjectSchema):
+ json_data = (
+ request_data.get_minimal_body_dict()
+ if request_data.has_body
+ else {}
+ )
original_data = {}
if method == "PATCH":
original_data = self.get_original_data(url=url)
diff --git a/src/OpenApiDriver/openapi_reader.py b/src/OpenApiDriver/openapi_reader.py
index 90be78d..22366d0 100644
--- a/src/OpenApiDriver/openapi_reader.py
+++ b/src/OpenApiDriver/openapi_reader.py
@@ -5,7 +5,7 @@
from DataDriver.AbstractReaderClass import AbstractReaderClass
from DataDriver.ReaderConfig import TestCaseData
-from OpenApiLibCore.models import PathItemObject
+from OpenApiLibCore.models.oas_models import PathItemObject
class Test:
diff --git a/src/OpenApiLibCore/__init__.py b/src/OpenApiLibCore/__init__.py
index b4d373e..b1844db 100644
--- a/src/OpenApiLibCore/__init__.py
+++ b/src/OpenApiLibCore/__init__.py
@@ -13,7 +13,8 @@
from importlib.metadata import version
-from OpenApiLibCore.dto_base import (
+from OpenApiLibCore.data_constraints.dto_base import (
+ DefaultDto,
Dto,
IdDependency,
IdReference,
@@ -22,13 +23,10 @@
ResourceRelation,
UniquePropertyValueConstraint,
)
-from OpenApiLibCore.dto_utils import DefaultDto
-from OpenApiLibCore.openapi_libcore import (
- OpenApiLibCore,
-)
-from OpenApiLibCore.request_data import RequestData, RequestValues
-from OpenApiLibCore.validation import ValidationLevel
-from OpenApiLibCore.value_utils import IGNORE, UNSET
+from OpenApiLibCore.keyword_logic.validation import ValidationLevel
+from OpenApiLibCore.models import IGNORE, UNSET
+from OpenApiLibCore.models.request_data import RequestData, RequestValues
+from OpenApiLibCore.openapi_libcore import OpenApiLibCore
try:
__version__ = version("robotframework-openapi-libcore")
diff --git a/src/OpenApiLibCore/data_constraints/__init__.py b/src/OpenApiLibCore/data_constraints/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/OpenApiLibCore/dto_base.py b/src/OpenApiLibCore/data_constraints/dto_base.py
similarity index 66%
rename from src/OpenApiLibCore/dto_base.py
rename to src/OpenApiLibCore/data_constraints/dto_base.py
index b646103..9f87fe0 100644
--- a/src/OpenApiLibCore/dto_base.py
+++ b/src/OpenApiLibCore/data_constraints/dto_base.py
@@ -6,15 +6,20 @@
from abc import ABC
from dataclasses import dataclass, fields
+from importlib import import_module
from random import choice, shuffle
-from typing import Any
+from typing import Any, Callable, Type, overload
from uuid import uuid4
from robot.api import logger
-from OpenApiLibCore import value_utils
-from OpenApiLibCore.models import NullSchema, ObjectSchema, UnionTypeSchema
-from OpenApiLibCore.parameter_utils import get_oas_name_from_safe_name
+from OpenApiLibCore.models.oas_models import NullSchema, ObjectSchema, UnionTypeSchema
+from OpenApiLibCore.protocols import (
+ GetDtoClassType,
+ GetIdPropertyNameType,
+ GetPathDtoClassType,
+)
+from OpenApiLibCore.utils import parameter_utils
NOT_SET = object()
SENTINEL = object()
@@ -232,8 +237,7 @@ def get_invalidated_data(
and r.property_name == property_name
]
- invalid_value = value_utils.get_invalid_value(
- value_schema=value_schema,
+ invalid_value = value_schema.get_invalid_value(
current_value=current_value,
values_from_constraint=values_from_constraint,
)
@@ -254,7 +258,123 @@ def as_dict(self) -> dict[Any, Any]:
field_name = field.name
if field_name not in self.__dict__:
continue
- original_name = get_oas_name_from_safe_name(field_name)
+ original_name = parameter_utils.get_oas_name_from_safe_name(field_name)
result[original_name] = getattr(self, field_name)
return result
+
+ def as_list(self) -> list[Any]:
+ """Return the list representation of the Dto."""
+ items = self.as_dict()
+ return [items] if items else []
+
+
+@dataclass
+class _DefaultIdPropertyName:
+ id_property_name: str = "id"
+
+
+DEFAULT_ID_PROPERTY_NAME = _DefaultIdPropertyName()
+
+
+@dataclass
+class DefaultDto(Dto):
+ """A default Dto that can be instantiated."""
+
+
+def get_dto_class(mappings_module_name: str) -> GetDtoClassType:
+ return GetDtoClass(mappings_module_name=mappings_module_name)
+
+
+class GetDtoClass:
+ """Callable class to return Dtos from user-implemented mappings file."""
+
+ def __init__(self, mappings_module_name: str) -> None:
+ try:
+ mappings_module = import_module(mappings_module_name)
+ self.dto_mapping: dict[tuple[str, str], Type[Dto]] = (
+ mappings_module.DTO_MAPPING
+ )
+ except (ImportError, AttributeError, ValueError) as exception:
+ if mappings_module_name != "no mapping":
+ logger.error(f"DTO_MAPPING was not imported: {exception}")
+ self.dto_mapping = {}
+
+ def __call__(self, path: str, method: str) -> Type[Dto]:
+ try:
+ return self.dto_mapping[(path, method.lower())]
+ except KeyError:
+ logger.debug(f"No Dto mapping for {path} {method}.")
+ return DefaultDto
+
+
+def get_path_dto_class(mappings_module_name: str) -> GetPathDtoClassType:
+ return GetPathDtoClass(mappings_module_name=mappings_module_name)
+
+
+class GetPathDtoClass:
+ """Callable class to return Dtos from user-implemented mappings file."""
+
+ def __init__(self, mappings_module_name: str) -> None:
+ try:
+ mappings_module = import_module(mappings_module_name)
+ self.dto_mapping: dict[str, Type[Dto]] = mappings_module.PATH_MAPPING
+ except (ImportError, AttributeError, ValueError) as exception:
+ if mappings_module_name != "no mapping":
+ logger.error(f"PATH_MAPPING was not imported: {exception}")
+ self.dto_mapping = {}
+
+ def __call__(self, path: str) -> Type[Dto]:
+ try:
+ return self.dto_mapping[path]
+ except KeyError:
+ logger.debug(f"No Dto mapping for {path}.")
+ return DefaultDto
+
+
+def get_id_property_name(mappings_module_name: str) -> GetIdPropertyNameType:
+ return GetIdPropertyName(mappings_module_name=mappings_module_name)
+
+
+class GetIdPropertyName:
+ """
+ Callable class to return the name of the property that uniquely identifies
+ the resource from user-implemented mappings file.
+ """
+
+ def __init__(self, mappings_module_name: str) -> None:
+ try:
+ mappings_module = import_module(mappings_module_name)
+ self.id_mapping: dict[
+ str,
+ str | tuple[str, Callable[[str], str] | Callable[[int], int]],
+ ] = mappings_module.ID_MAPPING
+ except (ImportError, AttributeError, ValueError) as exception:
+ if mappings_module_name != "no mapping":
+ logger.error(f"ID_MAPPING was not imported: {exception}")
+ self.id_mapping = {}
+
+ def __call__(
+ self, path: str
+ ) -> tuple[str, Callable[[str], str] | Callable[[int], int]]:
+ try:
+ value_or_mapping = self.id_mapping[path]
+ if isinstance(value_or_mapping, str):
+ return (value_or_mapping, dummy_transformer)
+ return value_or_mapping
+ except KeyError:
+ default_id_name = DEFAULT_ID_PROPERTY_NAME.id_property_name
+ logger.debug(f"No id mapping for {path} ('{default_id_name}' will be used)")
+ return (default_id_name, dummy_transformer)
+
+
+@overload
+def dummy_transformer(valid_id: str) -> str: ... # pragma: no cover
+
+
+@overload
+def dummy_transformer(valid_id: int) -> int: ... # pragma: no cover
+
+
+def dummy_transformer(valid_id: Any) -> Any:
+ return valid_id
diff --git a/src/OpenApiLibCore/data_generation/__init__.py b/src/OpenApiLibCore/data_generation/__init__.py
index be5f703..c41511c 100644
--- a/src/OpenApiLibCore/data_generation/__init__.py
+++ b/src/OpenApiLibCore/data_generation/__init__.py
@@ -2,9 +2,3 @@
Module holding the functions related to data generation
for the requests made as part of keyword exection.
"""
-
-from .data_generation_core import get_request_data
-
-__all__ = [
- "get_request_data",
-]
diff --git a/src/OpenApiLibCore/data_generation/body_data_generation.py b/src/OpenApiLibCore/data_generation/body_data_generation.py
index 0ae0270..772cbd5 100644
--- a/src/OpenApiLibCore/data_generation/body_data_generation.py
+++ b/src/OpenApiLibCore/data_generation/body_data_generation.py
@@ -8,23 +8,23 @@
from robot.api import logger
-import OpenApiLibCore.path_functions as _path_functions
+import OpenApiLibCore.keyword_logic.path_functions as _path_functions
from OpenApiLibCore.annotations import JSON
-from OpenApiLibCore.dto_base import (
+from OpenApiLibCore.data_constraints.dto_base import (
+ DefaultDto,
Dto,
IdDependency,
PropertyValueConstraint,
)
-from OpenApiLibCore.dto_utils import DefaultDto
-from OpenApiLibCore.models import (
+from OpenApiLibCore.data_generation.value_utils import IGNORE
+from OpenApiLibCore.models.oas_models import (
ArraySchema,
ObjectSchema,
SchemaObjectTypes,
UnionTypeSchema,
)
-from OpenApiLibCore.parameter_utils import get_safe_name_for_oas_name
from OpenApiLibCore.protocols import GetIdPropertyNameType
-from OpenApiLibCore.value_utils import IGNORE
+from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
def get_json_data_for_dto_class(
diff --git a/src/OpenApiLibCore/data_generation/data_generation_core.py b/src/OpenApiLibCore/data_generation/data_generation_core.py
index 71f3ac6..90a2e01 100644
--- a/src/OpenApiLibCore/data_generation/data_generation_core.py
+++ b/src/OpenApiLibCore/data_generation/data_generation_core.py
@@ -10,25 +10,27 @@
from robot.api import logger
-import OpenApiLibCore.path_functions as _path_functions
+import OpenApiLibCore.keyword_logic.path_functions as _path_functions
from OpenApiLibCore.annotations import JSON
-from OpenApiLibCore.dto_base import (
+from OpenApiLibCore.data_constraints.dto_base import (
+ DefaultDto,
Dto,
PropertyValueConstraint,
ResourceRelation,
)
-from OpenApiLibCore.dto_utils import DefaultDto
-from OpenApiLibCore.models import (
+from OpenApiLibCore.data_generation.value_utils import IGNORE
+from OpenApiLibCore.models.oas_models import (
+ ArraySchema,
ObjectSchema,
OpenApiObject,
OperationObject,
ParameterObject,
+ ResolvedSchemaObjectTypes,
UnionTypeSchema,
)
-from OpenApiLibCore.parameter_utils import get_safe_name_for_oas_name
+from OpenApiLibCore.models.request_data import RequestData
from OpenApiLibCore.protocols import GetDtoClassType, GetIdPropertyNameType
-from OpenApiLibCore.request_data import RequestData
-from OpenApiLibCore.value_utils import IGNORE
+from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
from .body_data_generation import (
get_json_data_for_dto_class as _get_json_data_for_dto_class,
@@ -91,9 +93,6 @@ def get_request_data(
resolved_schemas = body_schema.resolved_schemas
body_schema = choice(resolved_schemas)
- if not isinstance(body_schema, ObjectSchema):
- raise ValueError(f"Selected schema is not an object schema: {body_schema}")
-
dto_data = _get_json_data_for_dto_class(
schema=body_schema,
dto_class=dto_class,
@@ -101,7 +100,7 @@ def get_request_data(
operation_id=operation_spec.operationId,
)
dto_instance = _get_dto_instance_from_dto_data(
- object_schema=body_schema,
+ schema=body_schema,
dto_class=dto_class,
dto_data=dto_data,
method_spec=operation_spec,
@@ -135,26 +134,43 @@ def _get_dto_instance_for_empty_body(
def _get_dto_instance_from_dto_data(
- object_schema: ObjectSchema,
+ schema: ResolvedSchemaObjectTypes,
dto_class: type[Dto],
dto_data: JSON,
method_spec: OperationObject,
dto_cls_name: str,
) -> Dto:
- if not isinstance(dto_data, (dict, list)):
+ if not isinstance(schema, (ObjectSchema, ArraySchema)):
+ # if not isinstance(dto_data, (dict, list)):
return DefaultDto()
- if isinstance(dto_data, list):
- raise NotImplementedError
+ if isinstance(schema, ArraySchema):
+ if not dto_data or not isinstance(dto_data, list):
+ return DefaultDto()
+ first_item_data = dto_data[0]
+ item_object_schema = schema.items
+ if isinstance(item_object_schema, UnionTypeSchema):
+ resolved_schemas = item_object_schema.resolved_schemas
+ item_object_schema = choice(resolved_schemas)
+ item_dto = _get_dto_instance_from_dto_data(
+ schema=item_object_schema,
+ dto_class=dto_class,
+ dto_data=first_item_data,
+ method_spec=method_spec,
+ dto_cls_name=dto_cls_name,
+ )
+ return item_dto
- fields = get_fields_from_dto_data(object_schema, dto_data)
+ assert isinstance(dto_data, dict), (
+ "Data consistency error: schema is of type ObjectSchema but dto_data is not a dict."
+ )
+ fields = get_fields_from_dto_data(schema, dto_data)
cls_name = method_spec.operationId if method_spec.operationId else dto_cls_name
dto_class_ = make_dataclass(
cls_name=cls_name,
fields=fields,
bases=(dto_class,),
)
- # dto_data = {get_safe_key(key): value for key, value in dto_data.items()}
dto_data = {
get_safe_name_for_oas_name(key): value for key, value in dto_data.items()
}
@@ -168,9 +184,7 @@ def get_fields_from_dto_data(
fields: list[tuple[str, type[object], Field[object]]] = []
for key, value in dto_data.items():
- # safe_key = get_safe_key(key)
safe_key = get_safe_name_for_oas_name(key)
- # metadata = {"original_property_name": key}
if key in object_schema.required:
# The fields list is used to create a dataclass, so non-default fields
# must go before fields with a default
diff --git a/src/OpenApiLibCore/data_invalidation.py b/src/OpenApiLibCore/data_generation/data_invalidation.py
similarity index 93%
rename from src/OpenApiLibCore/data_invalidation.py
rename to src/OpenApiLibCore/data_generation/data_invalidation.py
index 63fa745..426c227 100644
--- a/src/OpenApiLibCore/data_invalidation.py
+++ b/src/OpenApiLibCore/data_generation/data_invalidation.py
@@ -12,16 +12,20 @@
from robot.libraries.BuiltIn import BuiltIn
from OpenApiLibCore.annotations import JSON
-from OpenApiLibCore.dto_base import (
+from OpenApiLibCore.data_constraints.dto_base import (
NOT_SET,
Dto,
IdReference,
PropertyValueConstraint,
UniquePropertyValueConstraint,
)
-from OpenApiLibCore.models import ParameterObject, UnionTypeSchema
-from OpenApiLibCore.request_data import RequestData
-from OpenApiLibCore.value_utils import IGNORE, get_invalid_value
+from OpenApiLibCore.data_generation.value_utils import IGNORE
+from OpenApiLibCore.models.oas_models import (
+ ArraySchema,
+ ParameterObject,
+ UnionTypeSchema,
+)
+from OpenApiLibCore.models.request_data import RequestData
run_keyword = BuiltIn().run_keyword
@@ -32,7 +36,7 @@ def get_invalid_body_data(
status_code: int,
request_data: RequestData,
invalid_property_default_response: int,
-) -> dict[str, Any]:
+) -> dict[str, Any] | list[Any]:
method = method.lower()
data_relations = request_data.dto.get_body_relations_for_error_code(status_code)
if not data_relations:
@@ -40,6 +44,13 @@ def get_invalid_body_data(
raise ValueError(
"Failed to invalidate: request_data does not contain a body_schema."
)
+ if isinstance(request_data.body_schema, ArraySchema):
+ invalid_item_data = request_data.dto.get_invalidated_data(
+ schema=request_data.body_schema.items,
+ status_code=status_code,
+ invalid_property_default_code=invalid_property_default_response,
+ )
+ return [invalid_item_data]
json_data = request_data.dto.get_invalidated_data(
schema=request_data.body_schema,
status_code=status_code,
@@ -200,8 +211,7 @@ def get_invalidated_parameters(
# FIXME: extra handling may be needed in case of values_from_constraint
value_schema = choice(value_schema.resolved_schemas)
- invalid_value = get_invalid_value(
- value_schema=value_schema,
+ invalid_value = value_schema.get_invalid_value(
current_value=valid_value,
values_from_constraint=values_from_constraint,
)
diff --git a/src/OpenApiLibCore/localized_faker.py b/src/OpenApiLibCore/data_generation/localized_faker.py
similarity index 100%
rename from src/OpenApiLibCore/localized_faker.py
rename to src/OpenApiLibCore/data_generation/localized_faker.py
diff --git a/src/OpenApiLibCore/value_utils.py b/src/OpenApiLibCore/data_generation/value_utils.py
similarity index 70%
rename from src/OpenApiLibCore/value_utils.py
rename to src/OpenApiLibCore/data_generation/value_utils.py
index ec133f8..7949892 100644
--- a/src/OpenApiLibCore/value_utils.py
+++ b/src/OpenApiLibCore/data_generation/value_utils.py
@@ -2,31 +2,10 @@
"""Utility module with functions to handle OpenAPI value types and restrictions."""
from copy import deepcopy
-from random import choice
-from typing import Any, Iterable, cast, overload
+from typing import Any, cast, overload
from OpenApiLibCore.annotations import JSON
-from OpenApiLibCore.localized_faker import FAKE
-from OpenApiLibCore.models import ResolvedSchemaObjectTypes
-
-
-class Ignore:
- """Helper class to flag properties to be ignored in data generation."""
-
- def __str__(self) -> str:
- return "IGNORE"
-
-
-class UnSet:
- """Helper class to flag arguments that have not been set in a keyword call."""
-
- def __str__(self) -> str:
- return "UNSET"
-
-
-IGNORE = Ignore()
-
-UNSET = UnSet()
+from OpenApiLibCore.models import IGNORE, Ignore
def json_type_name_of_python_type(python_type: Any) -> str:
@@ -67,55 +46,6 @@ def python_type_by_json_type_name(type_name: str) -> type:
raise ValueError(f"No Python type mapping for JSON type '{type_name}' available.")
-def get_invalid_value(
- value_schema: ResolvedSchemaObjectTypes,
- current_value: JSON,
- values_from_constraint: Iterable[JSON] = tuple(),
-) -> JSON | Ignore:
- """Return a random value that violates the provided value_schema."""
- invalid_values: list[JSON | Ignore] = []
- value_type = value_schema.type
-
- if not isinstance(current_value, python_type_by_json_type_name(value_type)):
- current_value = value_schema.get_valid_value()
-
- if values_from_constraint:
- try:
- return get_invalid_value_from_constraint(
- values_from_constraint=list(values_from_constraint),
- value_type=value_type,
- )
- except ValueError:
- pass
-
- # For schemas with a const or enum, add invalidated values from those
- try:
- invalid_value = value_schema.get_invalid_value_from_const_or_enum()
- invalid_values.append(invalid_value)
- except ValueError:
- pass
-
- # Violate min / max values or length if possible
- try:
- values_out_of_bounds = value_schema.get_values_out_of_bounds(
- current_value=current_value # type: ignore[arg-type]
- )
- invalid_values += values_out_of_bounds
- except ValueError:
- pass
-
- # No value constraints or min / max ranges to violate, so change the data type
- if value_type == "string":
- # Since int / float / bool can always be cast to sting, change
- # the string to a nested object.
- # An array gets exploded in query strings, "null" is then often invalid
- invalid_values.append([{"invalid": [None, False]}, "null", None, True])
- else:
- invalid_values.append(FAKE.uuid())
-
- return choice(invalid_values)
-
-
def get_invalid_value_from_constraint(
values_from_constraint: list[JSON | Ignore], value_type: str
) -> JSON | Ignore:
diff --git a/src/OpenApiLibCore/dto_utils.py b/src/OpenApiLibCore/dto_utils.py
deleted file mode 100644
index 4f8c8c9..0000000
--- a/src/OpenApiLibCore/dto_utils.py
+++ /dev/null
@@ -1,125 +0,0 @@
-"""Module for helper methods and classes used by the openapi_executors module."""
-
-from dataclasses import dataclass
-from importlib import import_module
-from typing import Any, Callable, Type, overload
-
-from robot.api import logger
-
-from OpenApiLibCore.dto_base import Dto
-from OpenApiLibCore.protocols import (
- GetDtoClassType,
- GetIdPropertyNameType,
- GetPathDtoClassType,
-)
-
-
-@dataclass
-class _DefaultIdPropertyName:
- id_property_name: str = "id"
-
-
-DEFAULT_ID_PROPERTY_NAME = _DefaultIdPropertyName()
-
-
-@dataclass
-class DefaultDto(Dto):
- """A default Dto that can be instantiated."""
-
-
-def get_dto_class(mappings_module_name: str) -> GetDtoClassType:
- return GetDtoClass(mappings_module_name=mappings_module_name)
-
-
-class GetDtoClass:
- """Callable class to return Dtos from user-implemented mappings file."""
-
- def __init__(self, mappings_module_name: str) -> None:
- try:
- mappings_module = import_module(mappings_module_name)
- self.dto_mapping: dict[tuple[str, str], Type[Dto]] = (
- mappings_module.DTO_MAPPING
- )
- except (ImportError, AttributeError, ValueError) as exception:
- if mappings_module_name != "no mapping":
- logger.error(f"DTO_MAPPING was not imported: {exception}")
- self.dto_mapping = {}
-
- def __call__(self, path: str, method: str) -> Type[Dto]:
- try:
- return self.dto_mapping[(path, method.lower())]
- except KeyError:
- logger.debug(f"No Dto mapping for {path} {method}.")
- return DefaultDto
-
-
-def get_path_dto_class(mappings_module_name: str) -> GetPathDtoClassType:
- return GetPathDtoClass(mappings_module_name=mappings_module_name)
-
-
-class GetPathDtoClass:
- """Callable class to return Dtos from user-implemented mappings file."""
-
- def __init__(self, mappings_module_name: str) -> None:
- try:
- mappings_module = import_module(mappings_module_name)
- self.dto_mapping: dict[str, Type[Dto]] = mappings_module.PATH_MAPPING
- except (ImportError, AttributeError, ValueError) as exception:
- if mappings_module_name != "no mapping":
- logger.error(f"PATH_MAPPING was not imported: {exception}")
- self.dto_mapping = {}
-
- def __call__(self, path: str) -> Type[Dto]:
- try:
- return self.dto_mapping[path]
- except KeyError:
- logger.debug(f"No Dto mapping for {path}.")
- return DefaultDto
-
-
-def get_id_property_name(mappings_module_name: str) -> GetIdPropertyNameType:
- return GetIdPropertyName(mappings_module_name=mappings_module_name)
-
-
-class GetIdPropertyName:
- """
- Callable class to return the name of the property that uniquely identifies
- the resource from user-implemented mappings file.
- """
-
- def __init__(self, mappings_module_name: str) -> None:
- try:
- mappings_module = import_module(mappings_module_name)
- self.id_mapping: dict[
- str,
- str | tuple[str, Callable[[str], str] | Callable[[int], int]],
- ] = mappings_module.ID_MAPPING
- except (ImportError, AttributeError, ValueError) as exception:
- if mappings_module_name != "no mapping":
- logger.error(f"ID_MAPPING was not imported: {exception}")
- self.id_mapping = {}
-
- def __call__(
- self, path: str
- ) -> tuple[str, Callable[[str], str] | Callable[[int], int]]:
- try:
- value_or_mapping = self.id_mapping[path]
- if isinstance(value_or_mapping, str):
- return (value_or_mapping, dummy_transformer)
- return value_or_mapping
- except KeyError:
- default_id_name = DEFAULT_ID_PROPERTY_NAME.id_property_name
- logger.debug(f"No id mapping for {path} ('{default_id_name}' will be used)")
- return (default_id_name, dummy_transformer)
-
-
-@overload
-def dummy_transformer(valid_id: str) -> str: ... # pragma: no cover
-
-
-@overload
-def dummy_transformer(valid_id: int) -> int: ... # pragma: no cover
-
-
-def dummy_transformer(valid_id: Any) -> Any:
- return valid_id
diff --git a/src/OpenApiLibCore/keyword_logic/__init__.py b/src/OpenApiLibCore/keyword_logic/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/OpenApiLibCore/path_functions.py b/src/OpenApiLibCore/keyword_logic/path_functions.py
similarity index 97%
rename from src/OpenApiLibCore/path_functions.py
rename to src/OpenApiLibCore/keyword_logic/path_functions.py
index d104d7b..4452721 100644
--- a/src/OpenApiLibCore/path_functions.py
+++ b/src/OpenApiLibCore/keyword_logic/path_functions.py
@@ -8,9 +8,9 @@
from requests import Response
from robot.libraries.BuiltIn import BuiltIn
-from OpenApiLibCore.models import OpenApiObject
+from OpenApiLibCore.models import oas_models
+from OpenApiLibCore.models.request_data import RequestData
from OpenApiLibCore.protocols import GetIdPropertyNameType, GetPathDtoClassType
-from OpenApiLibCore.request_data import RequestData
run_keyword = BuiltIn().run_keyword
@@ -24,7 +24,7 @@ def match_parts(parts: list[str], spec_parts: list[str]) -> bool:
return True
-def get_parametrized_path(path: str, openapi_spec: OpenApiObject) -> str:
+def get_parametrized_path(path: str, openapi_spec: oas_models.OpenApiObject) -> str:
path_parts = path.split("/")
# if the last part is empty, the path has a trailing `/` that
# should be ignored during matching
@@ -64,7 +64,7 @@ def get_valid_url(
path: str,
base_url: str,
get_path_dto_class: GetPathDtoClassType,
- openapi_spec: OpenApiObject,
+ openapi_spec: oas_models.OpenApiObject,
) -> str:
try:
# path can be partially resolved or provided by a PathPropertiesConstraint
diff --git a/src/OpenApiLibCore/path_invalidation.py b/src/OpenApiLibCore/keyword_logic/path_invalidation.py
similarity index 100%
rename from src/OpenApiLibCore/path_invalidation.py
rename to src/OpenApiLibCore/keyword_logic/path_invalidation.py
diff --git a/src/OpenApiLibCore/resource_relations.py b/src/OpenApiLibCore/keyword_logic/resource_relations.py
similarity index 86%
rename from src/OpenApiLibCore/resource_relations.py
rename to src/OpenApiLibCore/keyword_logic/resource_relations.py
index 600ff03..a36f99e 100644
--- a/src/OpenApiLibCore/resource_relations.py
+++ b/src/OpenApiLibCore/keyword_logic/resource_relations.py
@@ -4,10 +4,10 @@
from robot.api import logger
from robot.libraries.BuiltIn import BuiltIn
-import OpenApiLibCore.path_functions as _path_functions
-from OpenApiLibCore.dto_base import IdReference
-from OpenApiLibCore.models import OpenApiObject
-from OpenApiLibCore.request_data import RequestData
+import OpenApiLibCore.keyword_logic.path_functions as _path_functions
+from OpenApiLibCore.data_constraints.dto_base import IdReference
+from OpenApiLibCore.models.oas_models import OpenApiObject
+from OpenApiLibCore.models.request_data import RequestData
run_keyword = BuiltIn().run_keyword
diff --git a/src/OpenApiLibCore/validation.py b/src/OpenApiLibCore/keyword_logic/validation.py
similarity index 99%
rename from src/OpenApiLibCore/validation.py
rename to src/OpenApiLibCore/keyword_logic/validation.py
index 4df33b5..e900c17 100644
--- a/src/OpenApiLibCore/validation.py
+++ b/src/OpenApiLibCore/keyword_logic/validation.py
@@ -18,13 +18,13 @@
from robot.api.exceptions import Failure
from robot.libraries.BuiltIn import BuiltIn
-from OpenApiLibCore.models import (
+from OpenApiLibCore.models.oas_models import (
OpenApiObject,
ResponseObject,
UnionTypeSchema,
)
+from OpenApiLibCore.models.request_data import RequestData, RequestValues
from OpenApiLibCore.protocols import ResponseValidatorType
-from OpenApiLibCore.request_data import RequestData, RequestValues
run_keyword = BuiltIn().run_keyword
diff --git a/src/OpenApiLibCore/models/__init__.py b/src/OpenApiLibCore/models/__init__.py
new file mode 100644
index 0000000..9b05208
--- /dev/null
+++ b/src/OpenApiLibCore/models/__init__.py
@@ -0,0 +1,17 @@
+class Ignore:
+ """Helper class to flag properties to be ignored in data generation."""
+
+ def __str__(self) -> str:
+ return "IGNORE"
+
+
+class UnSet:
+ """Helper class to flag arguments that have not been set in a keyword call."""
+
+ def __str__(self) -> str:
+ return "UNSET"
+
+
+IGNORE = Ignore()
+
+UNSET = UnSet()
diff --git a/src/OpenApiLibCore/oas_cache.py b/src/OpenApiLibCore/models/oas_cache.py
similarity index 100%
rename from src/OpenApiLibCore/oas_cache.py
rename to src/OpenApiLibCore/models/oas_cache.py
diff --git a/src/OpenApiLibCore/models.py b/src/OpenApiLibCore/models/oas_models.py
similarity index 91%
rename from src/OpenApiLibCore/models.py
rename to src/OpenApiLibCore/models/oas_models.py
index 397eddc..61711e1 100644
--- a/src/OpenApiLibCore/models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -7,6 +7,7 @@
from typing import (
Generator,
Generic,
+ Iterable,
Literal,
Mapping,
TypeAlias,
@@ -18,7 +19,12 @@
from robot.api import logger
from OpenApiLibCore.annotations import JSON
-from OpenApiLibCore.localized_faker import FAKE, fake_string
+from OpenApiLibCore.data_generation.localized_faker import FAKE, fake_string
+from OpenApiLibCore.data_generation.value_utils import (
+ get_invalid_value_from_constraint,
+ python_type_by_json_type_name,
+)
+from OpenApiLibCore.models import Ignore
EPSILON = float_info.epsilon
@@ -38,6 +44,54 @@ def get_values_out_of_bounds(self, current_value: O) -> list[O]: ...
@abstractmethod
def get_invalid_value_from_const_or_enum(self) -> O: ...
+ def get_invalid_value(
+ self,
+ current_value: JSON,
+ values_from_constraint: Iterable[JSON] = tuple(),
+ ) -> JSON | Ignore:
+ """Return a random value that violates the provided value_schema."""
+ invalid_values: list[JSON | Ignore] = []
+ value_type = getattr(self, "type")
+
+ if not isinstance(current_value, python_type_by_json_type_name(value_type)):
+ current_value = self.get_valid_value()
+
+ if values_from_constraint:
+ try:
+ return get_invalid_value_from_constraint(
+ values_from_constraint=list(values_from_constraint),
+ value_type=value_type,
+ )
+ except ValueError:
+ pass
+
+ # For schemas with a const or enum, add invalidated values from those
+ try:
+ invalid_value = self.get_invalid_value_from_const_or_enum()
+ invalid_values.append(invalid_value)
+ except ValueError:
+ pass
+
+ # Violate min / max values or length if possible
+ try:
+ values_out_of_bounds = self.get_values_out_of_bounds(
+ current_value=current_value # type: ignore[arg-type]
+ )
+ invalid_values += values_out_of_bounds
+ except ValueError:
+ pass
+
+ # No value constraints or min / max ranges to violate, so change the data type
+ if value_type == "string":
+ # Since int / float / bool can always be cast to sting, change
+ # the string to a nested object.
+ # An array gets exploded in query strings, "null" is then often invalid
+ invalid_values.append([{"invalid": [None, False]}, "null", None, True])
+ else:
+ invalid_values.append(FAKE.uuid())
+
+ return choice(invalid_values)
+
class NullSchema(SchemaBase[None], frozen=True):
type: Literal["null"] = "null"
@@ -693,6 +747,7 @@ class OperationObject(BaseModel):
parameters: list[ParameterObject] | None = None
requestBody: RequestBodyObject | None = None
responses: dict[str, ResponseObject] = {}
+ # dto: Dto = DefaultDto()
class PathItemObject(BaseModel):
diff --git a/src/OpenApiLibCore/request_data.py b/src/OpenApiLibCore/models/request_data.py
similarity index 97%
rename from src/OpenApiLibCore/request_data.py
rename to src/OpenApiLibCore/models/request_data.py
index 707539d..0df86d2 100644
--- a/src/OpenApiLibCore/request_data.py
+++ b/src/OpenApiLibCore/models/request_data.py
@@ -6,10 +6,9 @@
from random import sample
from typing import Any
+from OpenApiLibCore import DefaultDto, Dto
from OpenApiLibCore.annotations import JSON
-from OpenApiLibCore.dto_base import Dto
-from OpenApiLibCore.dto_utils import DefaultDto
-from OpenApiLibCore.models import (
+from OpenApiLibCore.models.oas_models import (
ObjectSchema,
ParameterObject,
ResolvedSchemaObjectTypes,
@@ -56,7 +55,7 @@ class RequestData:
"""Helper class to manage parameters used when making requests."""
dto: Dto | DefaultDto = field(default_factory=DefaultDto)
- body_schema: ObjectSchema | None = None
+ body_schema: ResolvedSchemaObjectTypes | None = None
parameters: list[ParameterObject] = field(default_factory=list)
params: dict[str, JSON] = field(default_factory=dict)
headers: dict[str, JSON] = field(default_factory=dict)
@@ -79,7 +78,7 @@ def is_required_property(property_name: str) -> bool:
@property
def required_property_names(self) -> list[str]:
- if self.body_schema:
+ if isinstance(self.body_schema, ObjectSchema):
return self.body_schema.required
return []
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index 36ce83c..b12b62f 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -19,32 +19,33 @@
from robot.api.exceptions import FatalError
from robot.libraries.BuiltIn import BuiltIn
-import OpenApiLibCore.data_generation as _data_generation
-import OpenApiLibCore.data_invalidation as _data_invalidation
-import OpenApiLibCore.path_functions as _path_functions
-import OpenApiLibCore.path_invalidation as _path_invalidation
-import OpenApiLibCore.resource_relations as _resource_relations
-import OpenApiLibCore.validation as _validation
+import OpenApiLibCore.data_generation.data_generation_core as _data_generation
+import OpenApiLibCore.data_generation.data_invalidation as _data_invalidation
+import OpenApiLibCore.keyword_logic.path_functions as _path_functions
+import OpenApiLibCore.keyword_logic.path_invalidation as _path_invalidation
+import OpenApiLibCore.keyword_logic.resource_relations as _resource_relations
+import OpenApiLibCore.keyword_logic.validation as _validation
from OpenApiLibCore.annotations import JSON
-from OpenApiLibCore.dto_base import Dto, IdReference
-from OpenApiLibCore.dto_utils import (
+from OpenApiLibCore.data_constraints.dto_base import (
DEFAULT_ID_PROPERTY_NAME,
+ Dto,
+ IdReference,
get_dto_class,
get_id_property_name,
get_path_dto_class,
)
-from OpenApiLibCore.localized_faker import FAKE
-from OpenApiLibCore.models import (
+from OpenApiLibCore.data_generation.localized_faker import FAKE
+from OpenApiLibCore.models.oas_cache import PARSER_CACHE, CachedParser
+from OpenApiLibCore.models.oas_models import (
OpenApiObject,
PathItemObject,
)
-from OpenApiLibCore.oas_cache import PARSER_CACHE, CachedParser
-from OpenApiLibCore.parameter_utils import (
+from OpenApiLibCore.models.request_data import RequestData, RequestValues
+from OpenApiLibCore.protocols import ResponseValidatorType
+from OpenApiLibCore.utils.parameter_utils import (
get_oas_name_from_safe_name,
register_path_parameters,
)
-from OpenApiLibCore.protocols import ResponseValidatorType
-from OpenApiLibCore.request_data import RequestData, RequestValues
from openapitools_docs.docstrings import (
OPENAPILIBCORE_INIT_DOCSTRING,
OPENAPILIBCORE_LIBRARY_DOCSTRING,
diff --git a/src/OpenApiLibCore/protocols.py b/src/OpenApiLibCore/protocols.py
index 40958f7..d3d6463 100644
--- a/src/OpenApiLibCore/protocols.py
+++ b/src/OpenApiLibCore/protocols.py
@@ -1,5 +1,7 @@
"""A module holding Protcols."""
+from __future__ import annotations
+
from typing import Callable, Protocol, Type
from openapi_core.contrib.requests import (
@@ -7,7 +9,7 @@
RequestsOpenAPIResponse,
)
-from OpenApiLibCore.dto_base import Dto
+from OpenApiLibCore.data_constraints import dto_base
class ResponseValidatorType(Protocol):
@@ -19,7 +21,9 @@ def __call__(
class GetDtoClassType(Protocol):
def __init__(self, mappings_module_name: str) -> None: ... # pragma: no cover
- def __call__(self, path: str, method: str) -> Type[Dto]: ... # pragma: no cover
+ def __call__(
+ self, path: str, method: str
+ ) -> Type[dto_base.Dto]: ... # pragma: no cover
class GetIdPropertyNameType(Protocol):
@@ -35,4 +39,4 @@ def __call__(
class GetPathDtoClassType(Protocol):
def __init__(self, mappings_module_name: str) -> None: ... # pragma: no cover
- def __call__(self, path: str) -> Type[Dto]: ... # pragma: no cover
+ def __call__(self, path: str) -> Type[dto_base.Dto]: ... # pragma: no cover
diff --git a/src/OpenApiLibCore/utils/__init__.py b/src/OpenApiLibCore/utils/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/OpenApiLibCore/parameter_utils.py b/src/OpenApiLibCore/utils/parameter_utils.py
similarity index 97%
rename from src/OpenApiLibCore/parameter_utils.py
rename to src/OpenApiLibCore/utils/parameter_utils.py
index 9afa22f..ea702af 100644
--- a/src/OpenApiLibCore/parameter_utils.py
+++ b/src/OpenApiLibCore/utils/parameter_utils.py
@@ -5,7 +5,7 @@
from typing import Generator
-from OpenApiLibCore.models import ParameterObject, PathItemObject
+from OpenApiLibCore.models.oas_models import ParameterObject, PathItemObject
PARAMETER_REGISTRY: dict[str, str] = {
"body": "body",
diff --git a/src/openapi_libgen/generator.py b/src/openapi_libgen/generator.py
index c0fd40e..82af8d4 100644
--- a/src/openapi_libgen/generator.py
+++ b/src/openapi_libgen/generator.py
@@ -5,7 +5,7 @@
from prance import ResolvingParser
from openapi_libgen.spec_parser import get_keyword_data
-from OpenApiLibCore.models import OpenApiObject
+from OpenApiLibCore.models.oas_models import OpenApiObject
HERE = Path(__file__).parent.resolve()
diff --git a/src/openapi_libgen/spec_parser.py b/src/openapi_libgen/spec_parser.py
index 39e3002..0e26b07 100644
--- a/src/openapi_libgen/spec_parser.py
+++ b/src/openapi_libgen/spec_parser.py
@@ -5,14 +5,14 @@
from robot.utils import is_truthy
from openapi_libgen.parsing_utils import remove_unsafe_characters_from_string
-from OpenApiLibCore.models import (
+from OpenApiLibCore.models.oas_models import (
ObjectSchema,
OpenApiObject,
OperationObject,
PathItemObject,
SchemaObjectTypes,
)
-from OpenApiLibCore.parameter_utils import get_safe_name_for_oas_name
+from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
KEYWORD_TEMPLATE = r"""@keyword
{signature}
diff --git a/src/openapi_libgen/templates/library.jinja b/src/openapi_libgen/templates/library.jinja
index 5dd486c..1390068 100644
--- a/src/openapi_libgen/templates/library.jinja
+++ b/src/openapi_libgen/templates/library.jinja
@@ -6,7 +6,7 @@ from robot.api.deco import keyword, library
from robot.libraries.BuiltIn import BuiltIn
from OpenApiLibCore import UNSET, OpenApiLibCore, RequestValues
-from OpenApiLibCore.path_functions import substitute_path_parameters
+from OpenApiLibCore.keyword_logic.path_functions import substitute_path_parameters
run_keyword = BuiltIn().run_keyword
From c5a4df5a9a56429fbd2437d367ee3dbdc4c6d896 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Thu, 2 Oct 2025 11:42:06 +0000
Subject: [PATCH 03/55] Fixed imports in unit tests
---
.../unittests/test_can_be_invalidated.py | 2 +-
tests/libcore/unittests/test_dto_utils.py | 17 ++++++++---------
tests/libcore/unittests/test_get_valid_value.py | 2 +-
.../unittests/test_get_values_out_of_bounds.py | 2 +-
.../test_invalid_value_from_const_or_enum.py | 2 +-
tests/libcore/unittests/test_localized_faker.py | 2 +-
tests/libcore/unittests/test_parameter_utils.py | 2 +-
.../unittests/test_resolve_union_schema.py | 2 +-
.../test_invalid_value_from_constraint.py | 3 ++-
.../value_utils/test_type_name_mappers.py | 2 +-
10 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/tests/libcore/unittests/test_can_be_invalidated.py b/tests/libcore/unittests/test_can_be_invalidated.py
index d17eafe..cde05b3 100644
--- a/tests/libcore/unittests/test_can_be_invalidated.py
+++ b/tests/libcore/unittests/test_can_be_invalidated.py
@@ -1,7 +1,7 @@
# pylint: disable="missing-class-docstring", "missing-function-docstring"
import unittest
-from OpenApiLibCore.models import (
+from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
IntegerSchema,
diff --git a/tests/libcore/unittests/test_dto_utils.py b/tests/libcore/unittests/test_dto_utils.py
index db1df7a..cb4314d 100644
--- a/tests/libcore/unittests/test_dto_utils.py
+++ b/tests/libcore/unittests/test_dto_utils.py
@@ -4,14 +4,15 @@
import unittest
from OpenApiLibCore import (
+ DefaultDto,
Dto,
IdDependency,
IdReference,
PathPropertiesConstraint,
PropertyValueConstraint,
UniquePropertyValueConstraint,
- dto_utils,
)
+from OpenApiLibCore.data_constraints.dto_base import get_dto_class
unittest_folder = pathlib.Path(__file__).parent.resolve()
mappings_path = (
@@ -21,7 +22,7 @@
class TestDefaultDto(unittest.TestCase):
def test_can_init(self) -> None:
- default_dto = dto_utils.DefaultDto()
+ default_dto = DefaultDto()
self.assertIsInstance(default_dto, Dto)
@@ -44,16 +45,16 @@ def tearDownClass(cls) -> None:
print(f"removed {sys.path.pop()} from path")
def test_no_mapping(self) -> None:
- get_dto_class_instance = dto_utils.get_dto_class("dummy")
+ get_dto_class_instance = get_dto_class("dummy")
self.assertDictEqual(get_dto_class_instance.dto_mapping, {})
def test_valid_mapping(self) -> None:
- get_dto_class_instance = dto_utils.get_dto_class(self.mappings_module_name)
+ get_dto_class_instance = get_dto_class(self.mappings_module_name)
self.assertIsInstance(get_dto_class_instance.dto_mapping, dict)
self.assertGreater(len(get_dto_class_instance.dto_mapping.keys()), 0)
def mapped_returns_dto_instance(self) -> None:
- get_dto_class_instance = dto_utils.get_dto_class(self.mappings_module_name)
+ get_dto_class_instance = get_dto_class(self.mappings_module_name)
keys = get_dto_class_instance.dto_mapping.keys()
for key in keys:
self.assertIsInstance(key, tuple)
@@ -69,10 +70,8 @@ def mapped_returns_dto_instance(self) -> None:
)
def unmapped_returns_defaultdto(self) -> None:
- get_dto_class_instance = dto_utils.get_dto_class(self.mappings_module_name)
- self.assertIsInstance(
- get_dto_class_instance(("dummy", "post")), dto_utils.DefaultDto
- )
+ get_dto_class_instance = get_dto_class(self.mappings_module_name)
+ self.assertIsInstance(get_dto_class_instance(("dummy", "post")), DefaultDto)
if __name__ == "__main__":
diff --git a/tests/libcore/unittests/test_get_valid_value.py b/tests/libcore/unittests/test_get_valid_value.py
index b3ca1f1..ac272e8 100644
--- a/tests/libcore/unittests/test_get_valid_value.py
+++ b/tests/libcore/unittests/test_get_valid_value.py
@@ -1,7 +1,7 @@
# pylint: disable="missing-class-docstring", "missing-function-docstring"
import unittest
-from OpenApiLibCore.models import (
+from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
IntegerSchema,
diff --git a/tests/libcore/unittests/test_get_values_out_of_bounds.py b/tests/libcore/unittests/test_get_values_out_of_bounds.py
index 6b16503..79a994f 100644
--- a/tests/libcore/unittests/test_get_values_out_of_bounds.py
+++ b/tests/libcore/unittests/test_get_values_out_of_bounds.py
@@ -2,7 +2,7 @@
import unittest
from sys import float_info
-from OpenApiLibCore.models import (
+from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
IntegerSchema,
diff --git a/tests/libcore/unittests/test_invalid_value_from_const_or_enum.py b/tests/libcore/unittests/test_invalid_value_from_const_or_enum.py
index 79cd1de..7fdc814 100644
--- a/tests/libcore/unittests/test_invalid_value_from_const_or_enum.py
+++ b/tests/libcore/unittests/test_invalid_value_from_const_or_enum.py
@@ -1,7 +1,7 @@
# pylint: disable="missing-class-docstring", "missing-function-docstring"
import unittest
-from OpenApiLibCore.models import (
+from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
IntegerSchema,
diff --git a/tests/libcore/unittests/test_localized_faker.py b/tests/libcore/unittests/test_localized_faker.py
index 54af991..146b1ba 100644
--- a/tests/libcore/unittests/test_localized_faker.py
+++ b/tests/libcore/unittests/test_localized_faker.py
@@ -2,7 +2,7 @@
import datetime
import unittest
-from OpenApiLibCore.localized_faker import LocalizedFaker
+from OpenApiLibCore.data_generation.localized_faker import LocalizedFaker
class TestLocalizedFaker(unittest.TestCase):
diff --git a/tests/libcore/unittests/test_parameter_utils.py b/tests/libcore/unittests/test_parameter_utils.py
index 2d5be9a..b857d57 100644
--- a/tests/libcore/unittests/test_parameter_utils.py
+++ b/tests/libcore/unittests/test_parameter_utils.py
@@ -1,7 +1,7 @@
# pylint: disable="missing-class-docstring", "missing-function-docstring"
import unittest
-from OpenApiLibCore.parameter_utils import (
+from OpenApiLibCore.utils.parameter_utils import (
get_oas_name_from_safe_name,
get_safe_name_for_oas_name,
)
diff --git a/tests/libcore/unittests/test_resolve_union_schema.py b/tests/libcore/unittests/test_resolve_union_schema.py
index 515c39c..f9b994f 100644
--- a/tests/libcore/unittests/test_resolve_union_schema.py
+++ b/tests/libcore/unittests/test_resolve_union_schema.py
@@ -1,7 +1,7 @@
# pylint: disable="missing-class-docstring", "missing-function-docstring"
import unittest
-from OpenApiLibCore.models import (
+from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
IntegerSchema,
diff --git a/tests/libcore/unittests/value_utils/test_invalid_value_from_constraint.py b/tests/libcore/unittests/value_utils/test_invalid_value_from_constraint.py
index 3505ed5..b0da351 100644
--- a/tests/libcore/unittests/value_utils/test_invalid_value_from_constraint.py
+++ b/tests/libcore/unittests/value_utils/test_invalid_value_from_constraint.py
@@ -2,7 +2,8 @@
import unittest
from typing import Any
-from OpenApiLibCore import IGNORE, value_utils
+from OpenApiLibCore import IGNORE
+from OpenApiLibCore.data_generation import value_utils
class TestInvalidValueFromConstraint(unittest.TestCase):
diff --git a/tests/libcore/unittests/value_utils/test_type_name_mappers.py b/tests/libcore/unittests/value_utils/test_type_name_mappers.py
index d95244b..5c2a1a0 100644
--- a/tests/libcore/unittests/value_utils/test_type_name_mappers.py
+++ b/tests/libcore/unittests/value_utils/test_type_name_mappers.py
@@ -1,7 +1,7 @@
# pylint: disable="missing-class-docstring", "missing-function-docstring"
import unittest
-from OpenApiLibCore import value_utils
+from OpenApiLibCore.data_generation import value_utils
class TestTypeNameMappers(unittest.TestCase):
From bff924de04ce129a7078c0ef881c5436a0fa2e2a Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Thu, 2 Oct 2025 15:20:23 +0000
Subject: [PATCH 04/55] Refactor towards removing get_path_dto_class
---
src/OpenApiDriver/openapi_executors.py | 4 +-
.../keyword_logic/path_functions.py | 10 ++---
.../keyword_logic/path_invalidation.py | 17 ++++---
src/OpenApiLibCore/models/oas_models.py | 6 ++-
src/OpenApiLibCore/openapi_libcore.py | 31 ++++++++++---
src/OpenApiLibCore/protocols.py | 44 ++++++++++++++++++-
.../suites/test_get_invalidated_url.robot | 1 -
7 files changed, 92 insertions(+), 21 deletions(-)
diff --git a/src/OpenApiDriver/openapi_executors.py b/src/OpenApiDriver/openapi_executors.py
index 8eb3385..95823b2 100644
--- a/src/OpenApiDriver/openapi_executors.py
+++ b/src/OpenApiDriver/openapi_executors.py
@@ -152,9 +152,7 @@ def test_invalid_url(
valid_url: str = run_keyword("get_valid_url", path)
try:
- url = run_keyword(
- "get_invalidated_url", valid_url, path, expected_status_code
- )
+ url = run_keyword("get_invalidated_url", valid_url, expected_status_code)
except Exception as exception:
message = getattr(exception, "message", "")
if not message.startswith("ValueError"):
diff --git a/src/OpenApiLibCore/keyword_logic/path_functions.py b/src/OpenApiLibCore/keyword_logic/path_functions.py
index 4452721..6855baa 100644
--- a/src/OpenApiLibCore/keyword_logic/path_functions.py
+++ b/src/OpenApiLibCore/keyword_logic/path_functions.py
@@ -10,7 +10,7 @@
from OpenApiLibCore.models import oas_models
from OpenApiLibCore.models.request_data import RequestData
-from OpenApiLibCore.protocols import GetIdPropertyNameType, GetPathDtoClassType
+from OpenApiLibCore.protocols import GetIdPropertyNameType
run_keyword = BuiltIn().run_keyword
@@ -63,19 +63,19 @@ def get_parametrized_path(path: str, openapi_spec: oas_models.OpenApiObject) ->
def get_valid_url(
path: str,
base_url: str,
- get_path_dto_class: GetPathDtoClassType,
openapi_spec: oas_models.OpenApiObject,
) -> str:
try:
# path can be partially resolved or provided by a PathPropertiesConstraint
parametrized_path = get_parametrized_path(path=path, openapi_spec=openapi_spec)
- _ = openapi_spec.paths[parametrized_path]
+ path_item = openapi_spec.paths[parametrized_path]
except KeyError:
raise ValueError(
f"{path} not found in paths section of the OpenAPI document."
) from None
- dto_class = get_path_dto_class(path=path)
- relations = dto_class.get_path_relations()
+
+ dto_class = path_item.dto
+ relations = dto_class.get_path_relations() if dto_class else []
paths = [p.path for p in relations]
if paths:
url = f"{base_url}{choice(paths)}"
diff --git a/src/OpenApiLibCore/keyword_logic/path_invalidation.py b/src/OpenApiLibCore/keyword_logic/path_invalidation.py
index 31cd042..532f546 100644
--- a/src/OpenApiLibCore/keyword_logic/path_invalidation.py
+++ b/src/OpenApiLibCore/keyword_logic/path_invalidation.py
@@ -5,20 +5,27 @@
from robot.libraries.BuiltIn import BuiltIn
-from OpenApiLibCore.protocols import GetPathDtoClassType
+from OpenApiLibCore.models import oas_models
run_keyword = BuiltIn().run_keyword
def get_invalidated_url(
valid_url: str,
- path: str,
base_url: str,
- get_path_dto_class: GetPathDtoClassType,
+ openapi_spec: oas_models.OpenApiObject,
expected_status_code: int,
) -> str:
- dto_class = get_path_dto_class(path=path)
- relations = dto_class.get_path_relations()
+ path: str = run_keyword("get_parameterized_path_from_url", valid_url)
+ try:
+ path_item = openapi_spec.paths[path]
+ except KeyError:
+ raise ValueError(
+ f"{path} not found in paths section of the OpenAPI document."
+ ) from None
+
+ dto_class = path_item.dto
+ relations = dto_class.get_path_relations() if dto_class else []
paths = [
p.invalid_value
for p in relations
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index 61711e1..393cd8f 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
import base64
from abc import abstractmethod
from collections import ChainMap
@@ -25,6 +27,7 @@
python_type_by_json_type_name,
)
from OpenApiLibCore.models import Ignore
+from OpenApiLibCore.protocols import DtoType
EPSILON = float_info.epsilon
@@ -747,7 +750,7 @@ class OperationObject(BaseModel):
parameters: list[ParameterObject] | None = None
requestBody: RequestBodyObject | None = None
responses: dict[str, ResponseObject] = {}
- # dto: Dto = DefaultDto()
+ dto: DtoType | None = None
class PathItemObject(BaseModel):
@@ -759,6 +762,7 @@ class PathItemObject(BaseModel):
summary: str = ""
description: str = ""
parameters: list[ParameterObject] | None = None
+ dto: DtoType | None = None
def get_operations(self) -> dict[str, OperationObject]:
return {
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index b12b62f..0fac68f 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -323,7 +323,6 @@ def get_valid_url(self, path: str) -> str:
return _path_functions.get_valid_url(
path=path,
base_url=self.base_url,
- get_path_dto_class=self.get_path_dto_class,
openapi_spec=self.openapi_spec,
)
@@ -367,22 +366,20 @@ def get_ids_from_url(self, url: str) -> list[str]:
def get_invalidated_url(
self,
valid_url: str,
- path: str = "",
expected_status_code: int = 404,
) -> str:
"""
Return an url with all the path parameters in the `valid_url` replaced by a
random UUID if no PathPropertiesConstraint is mapped for the `"get"` operation
- on the mapped `path` and `expected_status_code`.
+ on the related `path` and `expected_status_code`.
If a PathPropertiesConstraint is mapped, the `invalid_value` is returned.
Raises: ValueError if the valid_url cannot be invalidated.
"""
return _path_invalidation.get_invalidated_url(
valid_url=valid_url,
- path=path,
base_url=self.base_url,
- get_path_dto_class=self.get_path_dto_class,
+ openapi_spec=self.openapi_spec,
expected_status_code=expected_status_code,
)
@@ -568,9 +565,33 @@ def openapi_spec(self) -> OpenApiObject:
def _openapi_spec(self) -> OpenApiObject:
parser, _, _ = self._load_specs_and_validator()
spec_model = OpenApiObject.model_validate(parser.specification)
+ spec_model = self._attach_user_mappings(spec_model=spec_model)
register_path_parameters(spec_model.paths)
return spec_model
+ def _attach_user_mappings(self, spec_model: OpenApiObject) -> OpenApiObject:
+ data_constraints_mapping = self.get_dto_class.dto_mapping
+ for (path, operation), data_constraint in data_constraints_mapping.items():
+ try:
+ operation_item = getattr(spec_model.paths[path], operation.lower())
+ operation_item.dto = data_constraint
+ except KeyError:
+ logger.warn(
+ f"The DTO_MAPPING contains a path that is not found in the OpenAPI spec: {path}"
+ )
+
+ path_constraints_mapping = self.get_path_dto_class.dto_mapping
+ for path, path_constraint in path_constraints_mapping.items():
+ try:
+ path_item = spec_model.paths[path]
+ path_item.dto = path_constraint
+ except KeyError:
+ logger.warn(
+ f"The PATH_MAPPING contains a path that is not found in the OpenAPI spec: {path}"
+ )
+
+ return spec_model
+
@cached_property
def response_validator(
self,
diff --git a/src/OpenApiLibCore/protocols.py b/src/OpenApiLibCore/protocols.py
index d3d6463..167dea6 100644
--- a/src/OpenApiLibCore/protocols.py
+++ b/src/OpenApiLibCore/protocols.py
@@ -2,12 +2,14 @@
from __future__ import annotations
-from typing import Callable, Protocol, Type
+from typing import Any, Callable, Protocol, Type, runtime_checkable
from openapi_core.contrib.requests import (
RequestsOpenAPIRequest,
RequestsOpenAPIResponse,
)
+from pydantic import GetCoreSchemaHandler, TypeAdapter
+from pydantic_core import CoreSchema, core_schema
from OpenApiLibCore.data_constraints import dto_base
@@ -40,3 +42,43 @@ class GetPathDtoClassType(Protocol):
def __init__(self, mappings_module_name: str) -> None: ... # pragma: no cover
def __call__(self, path: str) -> Type[dto_base.Dto]: ... # pragma: no cover
+
+
+class DtoType(Protocol):
+ @classmethod
+ def __get_pydantic_core_schema__(
+ cls, source_type: Any, handler: GetCoreSchemaHandler
+ ) -> CoreSchema:
+ return core_schema.no_info_after_validator_function(cls, handler(str))
+
+ @staticmethod
+ def get_path_relations() -> list[dto_base.PathPropertiesConstraint]: ...
+
+ def get_path_relations_for_error_code(
+ self, error_code: int
+ ) -> list[dto_base.PathPropertiesConstraint]: ...
+
+ @staticmethod
+ def get_parameter_relations() -> list[dto_base.ResourceRelation]: ...
+
+ def get_parameter_relations_for_error_code(
+ self, error_code: int
+ ) -> list[dto_base.ResourceRelation]: ...
+
+ @staticmethod
+ def get_relations() -> list[dto_base.ResourceRelation]: ...
+
+ def get_body_relations_for_error_code(
+ self, error_code: int
+ ) -> list[dto_base.ResourceRelation]: ...
+
+ def get_invalidated_data(
+ self,
+ schema: Any,
+ status_code: int,
+ invalid_property_default_code: int,
+ ) -> dict[str, Any]: ...
+
+ def as_dict(self) -> dict[Any, Any]: ...
+
+ def as_list(self) -> list[Any]: ...
diff --git a/tests/libcore/suites/test_get_invalidated_url.robot b/tests/libcore/suites/test_get_invalidated_url.robot
index 69d7649..6da306d 100644
--- a/tests/libcore/suites/test_get_invalidated_url.robot
+++ b/tests/libcore/suites/test_get_invalidated_url.robot
@@ -44,7 +44,6 @@ Test Get Invalidated Url For PathPropertiesConstraint Invalid Value Status Code
${url}= Get Valid Url path=/energy_label/{zipcode}/{home_number}
${invalidated}= Get Invalidated Url
... valid_url=${url}
- ... path=/energy_label/{zipcode}/{home_number}
... expected_status_code=422
Should Not Be Equal ${url} ${invalidated}
Should Start With ${invalidated} http://localhost:8000/energy_label/0123AA
From 24aadd2dc1f73a3a5e60afffbf869636801c572a Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Fri, 3 Oct 2025 09:53:46 +0000
Subject: [PATCH 05/55] id_mapping attached to PathItemObject
---
.../data_constraints/dto_base.py | 42 +++++++------------
.../data_generation/body_data_generation.py | 24 ++---------
.../data_generation/data_generation_core.py | 4 +-
.../keyword_logic/path_functions.py | 15 +++----
src/OpenApiLibCore/models/oas_models.py | 6 +++
src/OpenApiLibCore/openapi_libcore.py | 31 +++++++-------
src/OpenApiLibCore/protocols.py | 8 ++--
src/OpenApiLibCore/utils/id_mapping.py | 13 ++++++
.../{models => utils}/oas_cache.py | 0
9 files changed, 66 insertions(+), 77 deletions(-)
create mode 100644 src/OpenApiLibCore/utils/id_mapping.py
rename src/OpenApiLibCore/{models => utils}/oas_cache.py (100%)
diff --git a/src/OpenApiLibCore/data_constraints/dto_base.py b/src/OpenApiLibCore/data_constraints/dto_base.py
index 9f87fe0..ba237fe 100644
--- a/src/OpenApiLibCore/data_constraints/dto_base.py
+++ b/src/OpenApiLibCore/data_constraints/dto_base.py
@@ -8,7 +8,7 @@
from dataclasses import dataclass, fields
from importlib import import_module
from random import choice, shuffle
-from typing import Any, Callable, Type, overload
+from typing import Any, Callable, Type
from uuid import uuid4
from robot.api import logger
@@ -20,6 +20,7 @@
GetPathDtoClassType,
)
from OpenApiLibCore.utils import parameter_utils
+from OpenApiLibCore.utils.id_mapping import dummy_transformer
NOT_SET = object()
SENTINEL = object()
@@ -269,14 +270,6 @@ def as_list(self) -> list[Any]:
return [items] if items else []
-@dataclass
-class _DefaultIdPropertyName:
- id_property_name: str = "id"
-
-
-DEFAULT_ID_PROPERTY_NAME = _DefaultIdPropertyName()
-
-
@dataclass
class DefaultDto(Dto):
"""A default Dto that can be instantiated."""
@@ -325,6 +318,7 @@ def __init__(self, mappings_module_name: str) -> None:
self.dto_mapping = {}
def __call__(self, path: str) -> Type[Dto]:
+ raise DeprecationWarning
try:
return self.dto_mapping[path]
except KeyError:
@@ -332,8 +326,13 @@ def __call__(self, path: str) -> Type[Dto]:
return DefaultDto
-def get_id_property_name(mappings_module_name: str) -> GetIdPropertyNameType:
- return GetIdPropertyName(mappings_module_name=mappings_module_name)
+def get_id_property_name(
+ mappings_module_name: str, default_id_property_name: str
+) -> GetIdPropertyNameType:
+ return GetIdPropertyName(
+ mappings_module_name=mappings_module_name,
+ default_id_property_name=default_id_property_name,
+ )
class GetIdPropertyName:
@@ -342,7 +341,10 @@ class GetIdPropertyName:
the resource from user-implemented mappings file.
"""
- def __init__(self, mappings_module_name: str) -> None:
+ def __init__(
+ self, mappings_module_name: str, default_id_property_name: str
+ ) -> None:
+ self.default_id_property_name = default_id_property_name
try:
mappings_module = import_module(mappings_module_name)
self.id_mapping: dict[
@@ -363,18 +365,4 @@ def __call__(
return (value_or_mapping, dummy_transformer)
return value_or_mapping
except KeyError:
- default_id_name = DEFAULT_ID_PROPERTY_NAME.id_property_name
- logger.debug(f"No id mapping for {path} ('{default_id_name}' will be used)")
- return (default_id_name, dummy_transformer)
-
-
-@overload
-def dummy_transformer(valid_id: str) -> str: ... # pragma: no cover
-
-
-@overload
-def dummy_transformer(valid_id: int) -> int: ... # pragma: no cover
-
-
-def dummy_transformer(valid_id: Any) -> Any:
- return valid_id
+ return (self.default_id_property_name, dummy_transformer)
diff --git a/src/OpenApiLibCore/data_generation/body_data_generation.py b/src/OpenApiLibCore/data_generation/body_data_generation.py
index 772cbd5..199b788 100644
--- a/src/OpenApiLibCore/data_generation/body_data_generation.py
+++ b/src/OpenApiLibCore/data_generation/body_data_generation.py
@@ -7,8 +7,8 @@
from typing import Any
from robot.api import logger
+from robot.libraries.BuiltIn import BuiltIn
-import OpenApiLibCore.keyword_logic.path_functions as _path_functions
from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.data_constraints.dto_base import (
DefaultDto,
@@ -23,14 +23,14 @@
SchemaObjectTypes,
UnionTypeSchema,
)
-from OpenApiLibCore.protocols import GetIdPropertyNameType
from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
+run_keyword = BuiltIn().run_keyword
+
def get_json_data_for_dto_class(
schema: SchemaObjectTypes,
dto_class: type[Dto],
- get_id_property_name: GetIdPropertyNameType,
operation_id: str | None = None,
) -> JSON:
if isinstance(schema, UnionTypeSchema):
@@ -38,7 +38,6 @@ def get_json_data_for_dto_class(
return get_json_data_for_dto_class(
schema=chosen_schema,
dto_class=dto_class,
- get_id_property_name=get_id_property_name,
operation_id=operation_id,
)
@@ -47,14 +46,12 @@ def get_json_data_for_dto_class(
return get_dict_data_for_dto_class(
schema=schema,
dto_class=dto_class,
- get_id_property_name=get_id_property_name,
operation_id=operation_id,
)
case ArraySchema():
return get_list_data_for_dto_class(
schema=schema,
dto_class=dto_class,
- get_id_property_name=get_id_property_name,
operation_id=operation_id,
)
case _:
@@ -64,7 +61,6 @@ def get_json_data_for_dto_class(
def get_dict_data_for_dto_class(
schema: ObjectSchema,
dto_class: type[Dto],
- get_id_property_name: GetIdPropertyNameType,
operation_id: str | None = None,
) -> dict[str, Any]:
json_data: dict[str, Any] = {}
@@ -79,7 +75,6 @@ def get_dict_data_for_dto_class(
json_data[property_name] = get_data_for_property(
property_name=property_name,
property_schema=property_schema,
- get_id_property_name=get_id_property_name,
dto_class=dto_class,
operation_id=operation_id,
)
@@ -90,7 +85,6 @@ def get_dict_data_for_dto_class(
def get_list_data_for_dto_class(
schema: ArraySchema,
dto_class: type[Dto],
- get_id_property_name: GetIdPropertyNameType,
operation_id: str | None = None,
) -> list[JSON]:
json_data: list[JSON] = []
@@ -102,7 +96,6 @@ def get_list_data_for_dto_class(
list_item_data = get_json_data_for_dto_class(
schema=list_item_schema,
dto_class=dto_class,
- get_id_property_name=get_id_property_name,
operation_id=operation_id,
)
json_data.append(list_item_data)
@@ -112,7 +105,6 @@ def get_list_data_for_dto_class(
def get_data_for_property(
property_name: str,
property_schema: SchemaObjectTypes,
- get_id_property_name: GetIdPropertyNameType,
dto_class: type[Dto],
operation_id: str | None,
) -> JSON:
@@ -126,7 +118,6 @@ def get_data_for_property(
return get_value_constrained_by_nested_dto(
property_schema=property_schema,
nested_dto_class=constrained_value,
- get_id_property_name=get_id_property_name,
operation_id=operation_id,
)
return constrained_value
@@ -136,7 +127,6 @@ def get_data_for_property(
dto_class=dto_class,
property_name=property_name,
operation_id=operation_id,
- get_id_property_name=get_id_property_name,
)
) is not None:
return dependent_id
@@ -144,21 +134,18 @@ def get_data_for_property(
return get_json_data_for_dto_class(
schema=property_schema,
dto_class=DefaultDto,
- get_id_property_name=get_id_property_name,
)
def get_value_constrained_by_nested_dto(
property_schema: SchemaObjectTypes,
nested_dto_class: type[Dto],
- get_id_property_name: GetIdPropertyNameType,
operation_id: str | None,
) -> JSON:
nested_schema = get_schema_for_nested_dto(property_schema=property_schema)
nested_value = get_json_data_for_dto_class(
schema=nested_schema,
dto_class=nested_dto_class,
- get_id_property_name=get_id_property_name,
operation_id=operation_id,
)
return nested_value
@@ -221,7 +208,6 @@ def get_dependent_id(
dto_class: type[Dto],
property_name: str,
operation_id: str | None,
- get_id_property_name: GetIdPropertyNameType,
) -> str | int | float | None:
relations = dto_class.get_relations()
# multiple get paths are possible based on the operation being performed
@@ -243,8 +229,6 @@ def get_dependent_id(
except ValueError:
return None
- valid_id = _path_functions.get_valid_id_for_path(
- path=id_get_path, get_id_property_name=get_id_property_name
- )
+ valid_id = run_keyword("get_valid_id_for_path", id_get_path)
logger.debug(f"get_dependent_id for {id_get_path} returned {valid_id}")
return valid_id
diff --git a/src/OpenApiLibCore/data_generation/data_generation_core.py b/src/OpenApiLibCore/data_generation/data_generation_core.py
index 90a2e01..714ff73 100644
--- a/src/OpenApiLibCore/data_generation/data_generation_core.py
+++ b/src/OpenApiLibCore/data_generation/data_generation_core.py
@@ -29,7 +29,7 @@
UnionTypeSchema,
)
from OpenApiLibCore.models.request_data import RequestData
-from OpenApiLibCore.protocols import GetDtoClassType, GetIdPropertyNameType
+from OpenApiLibCore.protocols import GetDtoClassType
from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
from .body_data_generation import (
@@ -41,7 +41,6 @@ def get_request_data(
path: str,
method: str,
get_dto_class: GetDtoClassType,
- get_id_property_name: GetIdPropertyNameType,
openapi_spec: OpenApiObject,
) -> RequestData:
method = method.lower()
@@ -96,7 +95,6 @@ def get_request_data(
dto_data = _get_json_data_for_dto_class(
schema=body_schema,
dto_class=dto_class,
- get_id_property_name=get_id_property_name,
operation_id=operation_spec.operationId,
)
dto_instance = _get_dto_instance_from_dto_data(
diff --git a/src/OpenApiLibCore/keyword_logic/path_functions.py b/src/OpenApiLibCore/keyword_logic/path_functions.py
index 6855baa..fb16996 100644
--- a/src/OpenApiLibCore/keyword_logic/path_functions.py
+++ b/src/OpenApiLibCore/keyword_logic/path_functions.py
@@ -10,7 +10,6 @@
from OpenApiLibCore.models import oas_models
from OpenApiLibCore.models.request_data import RequestData
-from OpenApiLibCore.protocols import GetIdPropertyNameType
run_keyword = BuiltIn().run_keyword
@@ -96,7 +95,7 @@ def get_valid_url(
def get_valid_id_for_path(
path: str,
- get_id_property_name: GetIdPropertyNameType,
+ openapi_spec: oas_models.OpenApiObject,
) -> str | int:
url: str = run_keyword("get_valid_url", path)
# Try to create a new resource to prevent conflicts caused by
@@ -112,7 +111,8 @@ def get_valid_id_for_path(
request_data.get_required_properties_dict(),
)
- id_property, id_transformer = get_id_property_name(path=path)
+ path_item = openapi_spec.paths[path]
+ id_property, id_transformer = path_item.id_mapper
if not response.ok:
# If a new resource cannot be created using POST, try to retrieve a
@@ -172,7 +172,7 @@ def get_valid_id_for_path(
def get_ids_from_url(
url: str,
- get_id_property_name: GetIdPropertyNameType,
+ openapi_spec: oas_models.OpenApiObject,
) -> list[str]:
path: str = run_keyword("get_parameterized_path_from_url", url)
request_data: RequestData = run_keyword("get_request_data", path, "get")
@@ -187,11 +187,8 @@ def get_ids_from_url(
response_data: dict[str, Any] | list[dict[str, Any]] = response.json()
# determine the property name to use
- mapping = get_id_property_name(path=path)
- if isinstance(mapping, str):
- id_property = mapping
- else:
- id_property, _ = mapping
+ path_item = openapi_spec.paths[path]
+ id_property, _ = path_item.id_mapper
if isinstance(response_data, list):
valid_ids: list[str] = [item[id_property] for item in response_data]
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index 393cd8f..6fbf308 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -7,6 +7,7 @@
from random import choice, randint, uniform
from sys import float_info
from typing import (
+ Callable,
Generator,
Generic,
Iterable,
@@ -28,6 +29,7 @@
)
from OpenApiLibCore.models import Ignore
from OpenApiLibCore.protocols import DtoType
+from OpenApiLibCore.utils.id_mapping import dummy_transformer
EPSILON = float_info.epsilon
@@ -763,6 +765,10 @@ class PathItemObject(BaseModel):
description: str = ""
parameters: list[ParameterObject] | None = None
dto: DtoType | None = None
+ id_mapper: tuple[str, Callable[[str], str] | Callable[[int], int]] = (
+ "id",
+ dummy_transformer,
+ )
def get_operations(self) -> dict[str, OperationObject]:
return {
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index 0fac68f..48303f1 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -27,7 +27,6 @@
import OpenApiLibCore.keyword_logic.validation as _validation
from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.data_constraints.dto_base import (
- DEFAULT_ID_PROPERTY_NAME,
Dto,
IdReference,
get_dto_class,
@@ -35,13 +34,13 @@
get_path_dto_class,
)
from OpenApiLibCore.data_generation.localized_faker import FAKE
-from OpenApiLibCore.models.oas_cache import PARSER_CACHE, CachedParser
from OpenApiLibCore.models.oas_models import (
OpenApiObject,
PathItemObject,
)
from OpenApiLibCore.models.request_data import RequestData, RequestValues
from OpenApiLibCore.protocols import ResponseValidatorType
+from OpenApiLibCore.utils.oas_cache import PARSER_CACHE, CachedParser
from OpenApiLibCore.utils.parameter_utils import (
get_oas_name_from_safe_name,
register_path_parameters,
@@ -106,6 +105,11 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
self.cookies = cookies
self.proxies = proxies
self.invalid_property_default_response = invalid_property_default_response
+ if faker_locale:
+ FAKE.set_locale(locale=faker_locale)
+ self.require_body_for_invalid_url = require_body_for_invalid_url
+ self._server_validation_warning_logged = False
+
if mappings_path and str(mappings_path) != ".":
mappings_path = Path(mappings_path)
if not mappings_path.is_file():
@@ -122,7 +126,8 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
mappings_module_name=mappings_module_name
)
self.get_id_property_name = get_id_property_name(
- mappings_module_name=mappings_module_name
+ mappings_module_name=mappings_module_name,
+ default_id_property_name=default_id_property_name,
)
sys.path.pop()
else:
@@ -131,14 +136,9 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
mappings_module_name="no mapping"
)
self.get_id_property_name = get_id_property_name(
- mappings_module_name="no mapping"
+ mappings_module_name="no mapping",
+ default_id_property_name=default_id_property_name,
)
- if faker_locale:
- FAKE.set_locale(locale=faker_locale)
- self.require_body_for_invalid_url = require_body_for_invalid_url
- # update the globally available DEFAULT_ID_PROPERTY_NAME to the provided value
- DEFAULT_ID_PROPERTY_NAME.id_property_name = default_id_property_name
- self._server_validation_warning_logged = False
# region: library configuration keywords
@keyword
@@ -246,7 +246,6 @@ def get_request_data(self, path: str, method: str) -> RequestData:
path=path,
method=method,
get_dto_class=self.get_dto_class,
- get_id_property_name=self.get_id_property_name,
openapi_spec=self.openapi_spec,
)
@@ -335,7 +334,7 @@ def get_valid_id_for_path(self, path: str) -> str | int | float:
(by a POST operation) if possible.
"""
return _path_functions.get_valid_id_for_path(
- path=path, get_id_property_name=self.get_id_property_name
+ path=path, openapi_spec=self.openapi_spec
)
@keyword
@@ -358,9 +357,7 @@ def get_ids_from_url(self, url: str) -> list[str]:
Perform a GET request on the `url` and return the list of resource
`ids` from the response.
"""
- return _path_functions.get_ids_from_url(
- url=url, get_id_property_name=self.get_id_property_name
- )
+ return _path_functions.get_ids_from_url(url=url, openapi_spec=self.openapi_spec)
@keyword
def get_invalidated_url(
@@ -590,6 +587,10 @@ def _attach_user_mappings(self, spec_model: OpenApiObject) -> OpenApiObject:
f"The PATH_MAPPING contains a path that is not found in the OpenAPI spec: {path}"
)
+ for path, path_item in spec_model.paths.items():
+ mapper = self.get_id_property_name(path)
+ path_item.id_mapper = mapper
+
return spec_model
@cached_property
diff --git a/src/OpenApiLibCore/protocols.py b/src/OpenApiLibCore/protocols.py
index 167dea6..e49291b 100644
--- a/src/OpenApiLibCore/protocols.py
+++ b/src/OpenApiLibCore/protocols.py
@@ -2,13 +2,13 @@
from __future__ import annotations
-from typing import Any, Callable, Protocol, Type, runtime_checkable
+from typing import Any, Callable, Protocol, Type
from openapi_core.contrib.requests import (
RequestsOpenAPIRequest,
RequestsOpenAPIResponse,
)
-from pydantic import GetCoreSchemaHandler, TypeAdapter
+from pydantic import GetCoreSchemaHandler
from pydantic_core import CoreSchema, core_schema
from OpenApiLibCore.data_constraints import dto_base
@@ -29,7 +29,9 @@ def __call__(
class GetIdPropertyNameType(Protocol):
- def __init__(self, mappings_module_name: str) -> None: ... # pragma: no cover
+ def __init__(
+ self, mappings_module_name: str, default_id_property_name: str
+ ) -> None: ... # pragma: no cover
def __call__(
self, path: str
diff --git a/src/OpenApiLibCore/utils/id_mapping.py b/src/OpenApiLibCore/utils/id_mapping.py
new file mode 100644
index 0000000..2da5b32
--- /dev/null
+++ b/src/OpenApiLibCore/utils/id_mapping.py
@@ -0,0 +1,13 @@
+from typing import Any, overload
+
+
+@overload
+def dummy_transformer(valid_id: str) -> str: ... # pragma: no cover
+
+
+@overload
+def dummy_transformer(valid_id: int) -> int: ... # pragma: no cover
+
+
+def dummy_transformer(valid_id: Any) -> Any:
+ return valid_id
diff --git a/src/OpenApiLibCore/models/oas_cache.py b/src/OpenApiLibCore/utils/oas_cache.py
similarity index 100%
rename from src/OpenApiLibCore/models/oas_cache.py
rename to src/OpenApiLibCore/utils/oas_cache.py
From 92c64ea10a855b677ef2a8ad370ef8f0999929b2 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Fri, 3 Oct 2025 10:51:15 +0000
Subject: [PATCH 06/55] path_mapping cleanup
---
.../data_constraints/dto_base.py | 33 +++++--------------
src/OpenApiLibCore/openapi_libcore.py | 9 +++--
src/OpenApiLibCore/protocols.py | 6 ----
3 files changed, 13 insertions(+), 35 deletions(-)
diff --git a/src/OpenApiLibCore/data_constraints/dto_base.py b/src/OpenApiLibCore/data_constraints/dto_base.py
index ba237fe..058ec91 100644
--- a/src/OpenApiLibCore/data_constraints/dto_base.py
+++ b/src/OpenApiLibCore/data_constraints/dto_base.py
@@ -15,9 +15,9 @@
from OpenApiLibCore.models.oas_models import NullSchema, ObjectSchema, UnionTypeSchema
from OpenApiLibCore.protocols import (
+ DtoType,
GetDtoClassType,
GetIdPropertyNameType,
- GetPathDtoClassType,
)
from OpenApiLibCore.utils import parameter_utils
from OpenApiLibCore.utils.id_mapping import dummy_transformer
@@ -301,29 +301,14 @@ def __call__(self, path: str, method: str) -> Type[Dto]:
return DefaultDto
-def get_path_dto_class(mappings_module_name: str) -> GetPathDtoClassType:
- return GetPathDtoClass(mappings_module_name=mappings_module_name)
-
-
-class GetPathDtoClass:
- """Callable class to return Dtos from user-implemented mappings file."""
-
- def __init__(self, mappings_module_name: str) -> None:
- try:
- mappings_module = import_module(mappings_module_name)
- self.dto_mapping: dict[str, Type[Dto]] = mappings_module.PATH_MAPPING
- except (ImportError, AttributeError, ValueError) as exception:
- if mappings_module_name != "no mapping":
- logger.error(f"PATH_MAPPING was not imported: {exception}")
- self.dto_mapping = {}
-
- def __call__(self, path: str) -> Type[Dto]:
- raise DeprecationWarning
- try:
- return self.dto_mapping[path]
- except KeyError:
- logger.debug(f"No Dto mapping for {path}.")
- return DefaultDto
+def get_path_mapping_dict(mappings_module_name: str) -> dict[str, DtoType]:
+ try:
+ mappings_module = import_module(mappings_module_name)
+ return mappings_module.PATH_MAPPING
+ except (ImportError, AttributeError, ValueError) as exception:
+ if mappings_module_name != "no mapping":
+ logger.error(f"PATH_MAPPING was not imported: {exception}")
+ return {}
def get_id_property_name(
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index 48303f1..f563b42 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -31,7 +31,7 @@
IdReference,
get_dto_class,
get_id_property_name,
- get_path_dto_class,
+ get_path_mapping_dict,
)
from OpenApiLibCore.data_generation.localized_faker import FAKE
from OpenApiLibCore.models.oas_models import (
@@ -122,7 +122,7 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
self.get_dto_class = get_dto_class(
mappings_module_name=mappings_module_name
)
- self.get_path_dto_class = get_path_dto_class(
+ self.path_mapping_dict = get_path_mapping_dict(
mappings_module_name=mappings_module_name
)
self.get_id_property_name = get_id_property_name(
@@ -132,7 +132,7 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
sys.path.pop()
else:
self.get_dto_class = get_dto_class(mappings_module_name="no mapping")
- self.get_path_dto_class = get_path_dto_class(
+ self.path_mapping_dict = get_path_mapping_dict(
mappings_module_name="no mapping"
)
self.get_id_property_name = get_id_property_name(
@@ -577,8 +577,7 @@ def _attach_user_mappings(self, spec_model: OpenApiObject) -> OpenApiObject:
f"The DTO_MAPPING contains a path that is not found in the OpenAPI spec: {path}"
)
- path_constraints_mapping = self.get_path_dto_class.dto_mapping
- for path, path_constraint in path_constraints_mapping.items():
+ for path, path_constraint in self.path_mapping_dict.items():
try:
path_item = spec_model.paths[path]
path_item.dto = path_constraint
diff --git a/src/OpenApiLibCore/protocols.py b/src/OpenApiLibCore/protocols.py
index e49291b..33e293a 100644
--- a/src/OpenApiLibCore/protocols.py
+++ b/src/OpenApiLibCore/protocols.py
@@ -40,12 +40,6 @@ def __call__(
]: ... # pragma: no cover
-class GetPathDtoClassType(Protocol):
- def __init__(self, mappings_module_name: str) -> None: ... # pragma: no cover
-
- def __call__(self, path: str) -> Type[dto_base.Dto]: ... # pragma: no cover
-
-
class DtoType(Protocol):
@classmethod
def __get_pydantic_core_schema__(
From aeb05e8358837121d3e4ecd1ac7d5019869b09ca Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Fri, 3 Oct 2025 14:37:30 +0000
Subject: [PATCH 07/55] dto_mapping refactored, DefaultDto no longer needed
---
src/OpenApiLibCore/__init__.py | 2 -
.../data_constraints/dto_base.py | 42 ++++-----------
.../data_generation/body_data_generation.py | 27 +++++-----
.../data_generation/data_generation_core.py | 45 ++++++++--------
src/OpenApiLibCore/models/oas_models.py | 2 +-
src/OpenApiLibCore/models/request_data.py | 4 +-
src/OpenApiLibCore/openapi_libcore.py | 15 +++---
src/OpenApiLibCore/protocols.py | 10 +---
.../test_get_json_data_with_conflict.robot | 4 +-
.../suites/test_get_request_data.robot | 6 +--
tests/libcore/unittests/test_dto_utils.py | 54 ++++++-------------
.../user_implemented/custom_user_mappings.py | 6 +--
tests/variables.py | 3 --
13 files changed, 84 insertions(+), 136 deletions(-)
diff --git a/src/OpenApiLibCore/__init__.py b/src/OpenApiLibCore/__init__.py
index b1844db..7940050 100644
--- a/src/OpenApiLibCore/__init__.py
+++ b/src/OpenApiLibCore/__init__.py
@@ -14,7 +14,6 @@
from importlib.metadata import version
from OpenApiLibCore.data_constraints.dto_base import (
- DefaultDto,
Dto,
IdDependency,
IdReference,
@@ -63,7 +62,6 @@
__all__ = [
"IGNORE",
"UNSET",
- "DefaultDto",
"Dto",
"IdDependency",
"IdReference",
diff --git a/src/OpenApiLibCore/data_constraints/dto_base.py b/src/OpenApiLibCore/data_constraints/dto_base.py
index 058ec91..9ac81af 100644
--- a/src/OpenApiLibCore/data_constraints/dto_base.py
+++ b/src/OpenApiLibCore/data_constraints/dto_base.py
@@ -8,7 +8,7 @@
from dataclasses import dataclass, fields
from importlib import import_module
from random import choice, shuffle
-from typing import Any, Callable, Type
+from typing import Any, Callable
from uuid import uuid4
from robot.api import logger
@@ -16,7 +16,6 @@
from OpenApiLibCore.models.oas_models import NullSchema, ObjectSchema, UnionTypeSchema
from OpenApiLibCore.protocols import (
DtoType,
- GetDtoClassType,
GetIdPropertyNameType,
)
from OpenApiLibCore.utils import parameter_utils
@@ -270,35 +269,16 @@ def as_list(self) -> list[Any]:
return [items] if items else []
-@dataclass
-class DefaultDto(Dto):
- """A default Dto that can be instantiated."""
-
-
-def get_dto_class(mappings_module_name: str) -> GetDtoClassType:
- return GetDtoClass(mappings_module_name=mappings_module_name)
-
-
-class GetDtoClass:
- """Callable class to return Dtos from user-implemented mappings file."""
-
- def __init__(self, mappings_module_name: str) -> None:
- try:
- mappings_module = import_module(mappings_module_name)
- self.dto_mapping: dict[tuple[str, str], Type[Dto]] = (
- mappings_module.DTO_MAPPING
- )
- except (ImportError, AttributeError, ValueError) as exception:
- if mappings_module_name != "no mapping":
- logger.error(f"DTO_MAPPING was not imported: {exception}")
- self.dto_mapping = {}
-
- def __call__(self, path: str, method: str) -> Type[Dto]:
- try:
- return self.dto_mapping[(path, method.lower())]
- except KeyError:
- logger.debug(f"No Dto mapping for {path} {method}.")
- return DefaultDto
+def get_value_constraints_mapping_dict(
+ mappings_module_name: str,
+) -> dict[tuple[str, str], DtoType]:
+ try:
+ mappings_module = import_module(mappings_module_name)
+ return mappings_module.DTO_MAPPING
+ except (ImportError, AttributeError, ValueError) as exception:
+ if mappings_module_name != "no mapping":
+ logger.error(f"DTO_MAPPING was not imported: {exception}")
+ return {}
def get_path_mapping_dict(mappings_module_name: str) -> dict[str, DtoType]:
diff --git a/src/OpenApiLibCore/data_generation/body_data_generation.py b/src/OpenApiLibCore/data_generation/body_data_generation.py
index 199b788..831c0be 100644
--- a/src/OpenApiLibCore/data_generation/body_data_generation.py
+++ b/src/OpenApiLibCore/data_generation/body_data_generation.py
@@ -11,8 +11,6 @@
from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.data_constraints.dto_base import (
- DefaultDto,
- Dto,
IdDependency,
PropertyValueConstraint,
)
@@ -23,6 +21,7 @@
SchemaObjectTypes,
UnionTypeSchema,
)
+from OpenApiLibCore.protocols import DtoType
from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
run_keyword = BuiltIn().run_keyword
@@ -30,7 +29,7 @@
def get_json_data_for_dto_class(
schema: SchemaObjectTypes,
- dto_class: type[Dto],
+ dto_class: type[DtoType] | None,
operation_id: str | None = None,
) -> JSON:
if isinstance(schema, UnionTypeSchema):
@@ -60,7 +59,7 @@ def get_json_data_for_dto_class(
def get_dict_data_for_dto_class(
schema: ObjectSchema,
- dto_class: type[Dto],
+ dto_class: type[DtoType] | None,
operation_id: str | None = None,
) -> dict[str, Any]:
json_data: dict[str, Any] = {}
@@ -84,7 +83,7 @@ def get_dict_data_for_dto_class(
def get_list_data_for_dto_class(
schema: ArraySchema,
- dto_class: type[Dto],
+ dto_class: type[DtoType] | None,
operation_id: str | None = None,
) -> list[JSON]:
json_data: list[JSON] = []
@@ -105,7 +104,7 @@ def get_list_data_for_dto_class(
def get_data_for_property(
property_name: str,
property_schema: SchemaObjectTypes,
- dto_class: type[Dto],
+ dto_class: type[DtoType] | None,
operation_id: str | None,
) -> JSON:
if constrained_values := get_constrained_values(
@@ -133,13 +132,13 @@ def get_data_for_property(
return get_json_data_for_dto_class(
schema=property_schema,
- dto_class=DefaultDto,
+ dto_class=None,
)
def get_value_constrained_by_nested_dto(
property_schema: SchemaObjectTypes,
- nested_dto_class: type[Dto],
+ nested_dto_class: type[DtoType],
operation_id: str | None,
) -> JSON:
nested_schema = get_schema_for_nested_dto(property_schema=property_schema)
@@ -161,7 +160,7 @@ def get_schema_for_nested_dto(property_schema: SchemaObjectTypes) -> SchemaObjec
def get_property_names_to_process(
schema: ObjectSchema,
- dto_class: type[Dto],
+ dto_class: type[DtoType] | None,
) -> list[str]:
property_names = []
@@ -192,9 +191,9 @@ def get_property_names_to_process(
def get_constrained_values(
- dto_class: type[Dto], property_name: str
-) -> list[JSON | type[Dto]]:
- relations = dto_class.get_relations()
+ dto_class: type[DtoType] | None, property_name: str
+) -> list[JSON | type[DtoType]]:
+ relations = dto_class.get_relations() if dto_class else []
values_list = [
c.values
for c in relations
@@ -205,11 +204,11 @@ def get_constrained_values(
def get_dependent_id(
- dto_class: type[Dto],
+ dto_class: type[DtoType] | None,
property_name: str,
operation_id: str | None,
) -> str | int | float | None:
- relations = dto_class.get_relations()
+ relations = dto_class.get_relations() if dto_class else []
# multiple get paths are possible based on the operation being performed
id_get_paths = [
(d.get_path, d.operation_id)
diff --git a/src/OpenApiLibCore/data_generation/data_generation_core.py b/src/OpenApiLibCore/data_generation/data_generation_core.py
index 714ff73..28b24ed 100644
--- a/src/OpenApiLibCore/data_generation/data_generation_core.py
+++ b/src/OpenApiLibCore/data_generation/data_generation_core.py
@@ -13,7 +13,6 @@
import OpenApiLibCore.keyword_logic.path_functions as _path_functions
from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.data_constraints.dto_base import (
- DefaultDto,
Dto,
PropertyValueConstraint,
ResourceRelation,
@@ -29,7 +28,7 @@
UnionTypeSchema,
)
from OpenApiLibCore.models.request_data import RequestData
-from OpenApiLibCore.protocols import GetDtoClassType
+from OpenApiLibCore.protocols import DtoType
from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
from .body_data_generation import (
@@ -40,7 +39,6 @@
def get_request_data(
path: str,
method: str,
- get_dto_class: GetDtoClassType,
openapi_spec: OpenApiObject,
) -> RequestData:
method = method.lower()
@@ -50,17 +48,18 @@ def get_request_data(
spec_path = _path_functions.get_parametrized_path(
path=path, openapi_spec=openapi_spec
)
- dto_class = get_dto_class(path=spec_path, method=method)
try:
path_item = openapi_spec.paths[spec_path]
operation_spec: OperationObject | None = getattr(path_item, method)
if operation_spec is None:
raise AttributeError
+ dto_class = operation_spec.dto
except AttributeError:
logger.info(
f"method '{method}' not supported on '{spec_path}, using empty spec."
)
operation_spec = OperationObject(operationId="")
+ dto_class = None
parameters, params, headers = get_request_parameters(
dto_class=dto_class, method_spec=operation_spec
@@ -114,37 +113,38 @@ def get_request_data(
def _get_dto_instance_for_empty_body(
- dto_class: type[Dto],
+ dto_class: type[DtoType] | None,
dto_cls_name: str,
method_spec: OperationObject,
) -> Dto:
- if dto_class == DefaultDto:
- dto_instance: Dto = DefaultDto()
- else:
- cls_name = method_spec.operationId if method_spec.operationId else dto_cls_name
- dto_class = make_dataclass(
- cls_name=cls_name,
- fields=[],
- bases=(dto_class,),
- )
- dto_instance = dto_class()
+ cls_name = method_spec.operationId if method_spec.operationId else dto_cls_name
+ base = dto_class if dto_class else Dto
+ dto_class_ = make_dataclass(
+ cls_name=cls_name,
+ fields=[],
+ bases=(base,),
+ )
+ dto_instance = dto_class_()
return dto_instance
def _get_dto_instance_from_dto_data(
schema: ResolvedSchemaObjectTypes,
- dto_class: type[Dto],
+ dto_class: type[DtoType] | None,
dto_data: JSON,
method_spec: OperationObject,
dto_cls_name: str,
) -> Dto:
if not isinstance(schema, (ObjectSchema, ArraySchema)):
- # if not isinstance(dto_data, (dict, list)):
- return DefaultDto()
+ return _get_dto_instance_for_empty_body(
+ dto_class=dto_class, dto_cls_name=dto_cls_name, method_spec=method_spec
+ )
if isinstance(schema, ArraySchema):
if not dto_data or not isinstance(dto_data, list):
- return DefaultDto()
+ return _get_dto_instance_for_empty_body(
+ dto_class=dto_class, dto_cls_name=dto_cls_name, method_spec=method_spec
+ )
first_item_data = dto_data[0]
item_object_schema = schema.items
if isinstance(item_object_schema, UnionTypeSchema):
@@ -164,10 +164,11 @@ def _get_dto_instance_from_dto_data(
)
fields = get_fields_from_dto_data(schema, dto_data)
cls_name = method_spec.operationId if method_spec.operationId else dto_cls_name
+ base = dto_class if dto_class else Dto
dto_class_ = make_dataclass(
cls_name=cls_name,
fields=fields,
- bases=(dto_class,),
+ bases=(base,),
)
dto_data = {
get_safe_name_for_oas_name(key): value for key, value in dto_data.items()
@@ -204,11 +205,11 @@ def get_dto_cls_name(path: str, method: str) -> str:
def get_request_parameters(
- dto_class: Dto | type[Dto], method_spec: OperationObject
+ dto_class: type[DtoType] | None, method_spec: OperationObject
) -> tuple[list[ParameterObject], dict[str, Any], dict[str, str]]:
"""Get the methods parameter spec and params and headers with valid data."""
parameters = method_spec.parameters if method_spec.parameters else []
- parameter_relations = dto_class.get_parameter_relations()
+ parameter_relations = dto_class.get_parameter_relations() if dto_class else []
query_params = [p for p in parameters if p.in_ == "query"]
header_params = [p for p in parameters if p.in_ == "header"]
params = get_parameter_data(query_params, parameter_relations)
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index 6fbf308..da2a2ab 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -752,7 +752,7 @@ class OperationObject(BaseModel):
parameters: list[ParameterObject] | None = None
requestBody: RequestBodyObject | None = None
responses: dict[str, ResponseObject] = {}
- dto: DtoType | None = None
+ dto: type[DtoType] | None = None
class PathItemObject(BaseModel):
diff --git a/src/OpenApiLibCore/models/request_data.py b/src/OpenApiLibCore/models/request_data.py
index 0df86d2..cc70a20 100644
--- a/src/OpenApiLibCore/models/request_data.py
+++ b/src/OpenApiLibCore/models/request_data.py
@@ -6,7 +6,6 @@
from random import sample
from typing import Any
-from OpenApiLibCore import DefaultDto, Dto
from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.models.oas_models import (
ObjectSchema,
@@ -14,6 +13,7 @@
ResolvedSchemaObjectTypes,
UnionTypeSchema,
)
+from OpenApiLibCore.protocols import DtoType
@dataclass
@@ -54,7 +54,7 @@ def remove_parameters(self, parameters: list[str]) -> None:
class RequestData:
"""Helper class to manage parameters used when making requests."""
- dto: Dto | DefaultDto = field(default_factory=DefaultDto)
+ dto: DtoType
body_schema: ResolvedSchemaObjectTypes | None = None
parameters: list[ParameterObject] = field(default_factory=list)
params: dict[str, JSON] = field(default_factory=dict)
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index f563b42..4686a95 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -29,9 +29,9 @@
from OpenApiLibCore.data_constraints.dto_base import (
Dto,
IdReference,
- get_dto_class,
get_id_property_name,
get_path_mapping_dict,
+ get_value_constraints_mapping_dict,
)
from OpenApiLibCore.data_generation.localized_faker import FAKE
from OpenApiLibCore.models.oas_models import (
@@ -119,7 +119,7 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
mappings_folder = str(mappings_path.parent)
sys.path.append(mappings_folder)
mappings_module_name = mappings_path.stem
- self.get_dto_class = get_dto_class(
+ self.value_constraints_mapping_dict = get_value_constraints_mapping_dict(
mappings_module_name=mappings_module_name
)
self.path_mapping_dict = get_path_mapping_dict(
@@ -131,7 +131,9 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
)
sys.path.pop()
else:
- self.get_dto_class = get_dto_class(mappings_module_name="no mapping")
+ self.value_constraints_mapping_dict = get_value_constraints_mapping_dict(
+ mappings_module_name="no mapping"
+ )
self.path_mapping_dict = get_path_mapping_dict(
mappings_module_name="no mapping"
)
@@ -245,7 +247,6 @@ def get_request_data(self, path: str, method: str) -> RequestData:
return _data_generation.get_request_data(
path=path,
method=method,
- get_dto_class=self.get_dto_class,
openapi_spec=self.openapi_spec,
)
@@ -567,8 +568,10 @@ def _openapi_spec(self) -> OpenApiObject:
return spec_model
def _attach_user_mappings(self, spec_model: OpenApiObject) -> OpenApiObject:
- data_constraints_mapping = self.get_dto_class.dto_mapping
- for (path, operation), data_constraint in data_constraints_mapping.items():
+ for (
+ path,
+ operation,
+ ), data_constraint in self.value_constraints_mapping_dict.items():
try:
operation_item = getattr(spec_model.paths[path], operation.lower())
operation_item.dto = data_constraint
diff --git a/src/OpenApiLibCore/protocols.py b/src/OpenApiLibCore/protocols.py
index 33e293a..57b4775 100644
--- a/src/OpenApiLibCore/protocols.py
+++ b/src/OpenApiLibCore/protocols.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Any, Callable, Protocol, Type
+from typing import Any, Callable, Protocol
from openapi_core.contrib.requests import (
RequestsOpenAPIRequest,
@@ -20,14 +20,6 @@ def __call__(
) -> None: ... # pragma: no cover
-class GetDtoClassType(Protocol):
- def __init__(self, mappings_module_name: str) -> None: ... # pragma: no cover
-
- def __call__(
- self, path: str, method: str
- ) -> Type[dto_base.Dto]: ... # pragma: no cover
-
-
class GetIdPropertyNameType(Protocol):
def __init__(
self, mappings_module_name: str, default_id_property_name: str
diff --git a/tests/libcore/suites/test_get_json_data_with_conflict.robot b/tests/libcore/suites/test_get_json_data_with_conflict.robot
index 6e400f1..2784fad 100644
--- a/tests/libcore/suites/test_get_json_data_with_conflict.robot
+++ b/tests/libcore/suites/test_get_json_data_with_conflict.robot
@@ -13,12 +13,14 @@ ${ORIGIN} http://localhost:8000
*** Test Cases ***
Test Get Json Data With Conflict Raises For No UniquePropertyValueConstraint
+ # No mapping for /wagegroups GET will yield a default dto on the request_data
+ ${request_data}= Get Request Data path=/wagegroups method=get
${url}= Get Valid Url path=/wagegroups
Run Keyword And Expect Error ValueError: No UniquePropertyValueConstraint*
... Get Json Data With Conflict
... url=${url}
... method=post
- ... dto=${DEFAULT_DTO()}
+ ... dto=${request_data.dto}
... conflict_status_code=418
Test Get Json Data With Conflict For Post Request
diff --git a/tests/libcore/suites/test_get_request_data.robot b/tests/libcore/suites/test_get_request_data.robot
index f124aac..befc5c4 100644
--- a/tests/libcore/suites/test_get_request_data.robot
+++ b/tests/libcore/suites/test_get_request_data.robot
@@ -19,7 +19,7 @@ Test Get Request Data For Invalid Method On Endpoint
${request_data}= Get Request Data path=/events/ method=delete
VAR &{dict}= &{EMPTY}
VAR @{list}= @{EMPTY}
- Should Be Equal ${request_data.dto} ${DEFAULT_DTO()}
+ Should Be Equal ${request_data.dto.__doc__} DeleteEvents()
Should Be Equal ${request_data.body_schema} ${NONE}
Should Be Equal ${request_data.parameters} ${list}
Should Be Equal ${request_data.params} ${dict}
@@ -62,9 +62,9 @@ Test Get Request Data For Endpoint With Array Request Body
Dictionary Should Contain Key ${request_data.dto.message} message
List Should Not Contain Duplicates ${request_data.dto.details}
VAR ${non_empty_array_seen}= ${TRUE}
- EXCEPT * AttributeError: 'DefaultDto' object has no attribute 'message' type=glob
+ EXCEPT * AttributeError: 'put_events_events__put' object has no attribute 'message' type=glob
Should Be Equal ${request_data.body_schema.type} array
- Should Be Equal As Strings ${request_data.dto} DefaultDto()
+ Should Be Equal As Strings ${request_data.dto} put_events_events__put()
VAR ${empty_array_seen}= ${TRUE}
END
END
diff --git a/tests/libcore/unittests/test_dto_utils.py b/tests/libcore/unittests/test_dto_utils.py
index cb4314d..83bf6de 100644
--- a/tests/libcore/unittests/test_dto_utils.py
+++ b/tests/libcore/unittests/test_dto_utils.py
@@ -3,16 +3,8 @@
import sys
import unittest
-from OpenApiLibCore import (
- DefaultDto,
- Dto,
- IdDependency,
- IdReference,
- PathPropertiesConstraint,
- PropertyValueConstraint,
- UniquePropertyValueConstraint,
-)
-from OpenApiLibCore.data_constraints.dto_base import get_dto_class
+from OpenApiLibCore import Dto
+from OpenApiLibCore.data_constraints.dto_base import get_value_constraints_mapping_dict
unittest_folder = pathlib.Path(__file__).parent.resolve()
mappings_path = (
@@ -20,12 +12,6 @@
)
-class TestDefaultDto(unittest.TestCase):
- def test_can_init(self) -> None:
- default_dto = DefaultDto()
- self.assertIsInstance(default_dto, Dto)
-
-
class TestGetDtoClass(unittest.TestCase):
mappings_module_name = ""
@@ -45,33 +31,25 @@ def tearDownClass(cls) -> None:
print(f"removed {sys.path.pop()} from path")
def test_no_mapping(self) -> None:
- get_dto_class_instance = get_dto_class("dummy")
- self.assertDictEqual(get_dto_class_instance.dto_mapping, {})
+ value_constraints_mapping_dict = get_value_constraints_mapping_dict("dummy")
+ self.assertDictEqual(value_constraints_mapping_dict, {})
def test_valid_mapping(self) -> None:
- get_dto_class_instance = get_dto_class(self.mappings_module_name)
- self.assertIsInstance(get_dto_class_instance.dto_mapping, dict)
- self.assertGreater(len(get_dto_class_instance.dto_mapping.keys()), 0)
-
- def mapped_returns_dto_instance(self) -> None:
- get_dto_class_instance = get_dto_class(self.mappings_module_name)
- keys = get_dto_class_instance.dto_mapping.keys()
+ value_constraints_mapping_dict = get_value_constraints_mapping_dict(
+ self.mappings_module_name
+ )
+ self.assertIsInstance(value_constraints_mapping_dict, dict)
+ self.assertGreater(len(value_constraints_mapping_dict.keys()), 0)
+
+ def test_mapped_returns_dto_instance(self) -> None:
+ value_constraints_mapping_dict = get_value_constraints_mapping_dict(
+ self.mappings_module_name
+ )
+ keys = value_constraints_mapping_dict.keys()
for key in keys:
self.assertIsInstance(key, tuple)
self.assertEqual(len(key), 2)
- self.assertIsInstance(
- get_dto_class_instance(key),
- (
- IdDependency,
- IdReference,
- PropertyValueConstraint,
- UniquePropertyValueConstraint,
- ),
- )
-
- def unmapped_returns_defaultdto(self) -> None:
- get_dto_class_instance = get_dto_class(self.mappings_module_name)
- self.assertIsInstance(get_dto_class_instance(("dummy", "post")), DefaultDto)
+ self.assertIsInstance(value_constraints_mapping_dict[key](), Dto)
if __name__ == "__main__":
diff --git a/tests/user_implemented/custom_user_mappings.py b/tests/user_implemented/custom_user_mappings.py
index 30bc78a..836ed27 100644
--- a/tests/user_implemented/custom_user_mappings.py
+++ b/tests/user_implemented/custom_user_mappings.py
@@ -1,6 +1,4 @@
# pylint: disable=invalid-name
-from typing import Type
-
from OpenApiLibCore import (
IGNORE,
Dto,
@@ -176,7 +174,7 @@ def get_parameter_relations() -> list[ResourceRelation]:
return relations
-DTO_MAPPING: dict[tuple[str, str], Type[Dto]] = {
+DTO_MAPPING: dict[tuple[str, str], type[Dto]] = {
("/wagegroups", "post"): WagegroupDto,
("/wagegroups/{wagegroup_id}", "delete"): WagegroupDeleteDto,
("/wagegroups/{wagegroup_id}", "put"): WagegroupDto,
@@ -196,6 +194,6 @@ def get_parameter_relations() -> list[ResourceRelation]:
}
-PATH_MAPPING: dict[str, Type[Dto]] = {
+PATH_MAPPING: dict[str, type[Dto]] = {
"/energy_label/{zipcode}/{home_number}": EnergyLabelDto,
}
diff --git a/tests/variables.py b/tests/variables.py
index cd2e373..5ac9347 100644
--- a/tests/variables.py
+++ b/tests/variables.py
@@ -4,7 +4,6 @@
from OpenApiLibCore import (
IGNORE,
- DefaultDto,
Dto,
IdDependency,
IdReference,
@@ -76,12 +75,10 @@ def get_variables() -> dict[str, Any]:
)
wagegroup_dto = WagegroupDto
employee_dto = EmployeeDto
- default_dto = DefaultDto
extra_headers: dict[str, str] = {"foo": "bar", "eggs": "bacon"}
return {
"ID_REFERENCE": id_reference,
"INVALID_ID_REFERENCE": invalid_id_reference,
- "DEFAULT_DTO": default_dto,
"WAGEGROUP_DTO": wagegroup_dto,
"EMPLOYEE_DTO": employee_dto,
"EXTRA_HEADERS": extra_headers,
From 17fb906dde25b970f9f978d2b2cc832acb16fb3c Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Fri, 3 Oct 2025 15:00:54 +0000
Subject: [PATCH 08/55] Some naming improvements
---
.../data_constraints/dto_base.py | 10 +++--
.../data_generation/body_data_generation.py | 20 +++++-----
.../data_generation/data_generation_core.py | 8 ++--
src/OpenApiLibCore/models/oas_models.py | 38 +++++++++++++------
src/OpenApiLibCore/models/request_data.py | 4 +-
src/OpenApiLibCore/openapi_libcore.py | 8 ++--
src/OpenApiLibCore/protocols.py | 2 +-
tests/libcore/unittests/test_dto_utils.py | 8 ++--
8 files changed, 58 insertions(+), 40 deletions(-)
diff --git a/src/OpenApiLibCore/data_constraints/dto_base.py b/src/OpenApiLibCore/data_constraints/dto_base.py
index 9ac81af..3e89fa2 100644
--- a/src/OpenApiLibCore/data_constraints/dto_base.py
+++ b/src/OpenApiLibCore/data_constraints/dto_base.py
@@ -15,7 +15,7 @@
from OpenApiLibCore.models.oas_models import NullSchema, ObjectSchema, UnionTypeSchema
from OpenApiLibCore.protocols import (
- DtoType,
+ ConstraintMappingType,
GetIdPropertyNameType,
)
from OpenApiLibCore.utils import parameter_utils
@@ -269,9 +269,9 @@ def as_list(self) -> list[Any]:
return [items] if items else []
-def get_value_constraints_mapping_dict(
+def get_constraint_mapping_dict(
mappings_module_name: str,
-) -> dict[tuple[str, str], DtoType]:
+) -> dict[tuple[str, str], ConstraintMappingType]:
try:
mappings_module = import_module(mappings_module_name)
return mappings_module.DTO_MAPPING
@@ -281,7 +281,9 @@ def get_value_constraints_mapping_dict(
return {}
-def get_path_mapping_dict(mappings_module_name: str) -> dict[str, DtoType]:
+def get_path_mapping_dict(
+ mappings_module_name: str,
+) -> dict[str, ConstraintMappingType]:
try:
mappings_module = import_module(mappings_module_name)
return mappings_module.PATH_MAPPING
diff --git a/src/OpenApiLibCore/data_generation/body_data_generation.py b/src/OpenApiLibCore/data_generation/body_data_generation.py
index 831c0be..956de8c 100644
--- a/src/OpenApiLibCore/data_generation/body_data_generation.py
+++ b/src/OpenApiLibCore/data_generation/body_data_generation.py
@@ -21,7 +21,7 @@
SchemaObjectTypes,
UnionTypeSchema,
)
-from OpenApiLibCore.protocols import DtoType
+from OpenApiLibCore.protocols import ConstraintMappingType
from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
run_keyword = BuiltIn().run_keyword
@@ -29,7 +29,7 @@
def get_json_data_for_dto_class(
schema: SchemaObjectTypes,
- dto_class: type[DtoType] | None,
+ dto_class: type[ConstraintMappingType] | None,
operation_id: str | None = None,
) -> JSON:
if isinstance(schema, UnionTypeSchema):
@@ -59,7 +59,7 @@ def get_json_data_for_dto_class(
def get_dict_data_for_dto_class(
schema: ObjectSchema,
- dto_class: type[DtoType] | None,
+ dto_class: type[ConstraintMappingType] | None,
operation_id: str | None = None,
) -> dict[str, Any]:
json_data: dict[str, Any] = {}
@@ -83,7 +83,7 @@ def get_dict_data_for_dto_class(
def get_list_data_for_dto_class(
schema: ArraySchema,
- dto_class: type[DtoType] | None,
+ dto_class: type[ConstraintMappingType] | None,
operation_id: str | None = None,
) -> list[JSON]:
json_data: list[JSON] = []
@@ -104,7 +104,7 @@ def get_list_data_for_dto_class(
def get_data_for_property(
property_name: str,
property_schema: SchemaObjectTypes,
- dto_class: type[DtoType] | None,
+ dto_class: type[ConstraintMappingType] | None,
operation_id: str | None,
) -> JSON:
if constrained_values := get_constrained_values(
@@ -138,7 +138,7 @@ def get_data_for_property(
def get_value_constrained_by_nested_dto(
property_schema: SchemaObjectTypes,
- nested_dto_class: type[DtoType],
+ nested_dto_class: type[ConstraintMappingType],
operation_id: str | None,
) -> JSON:
nested_schema = get_schema_for_nested_dto(property_schema=property_schema)
@@ -160,7 +160,7 @@ def get_schema_for_nested_dto(property_schema: SchemaObjectTypes) -> SchemaObjec
def get_property_names_to_process(
schema: ObjectSchema,
- dto_class: type[DtoType] | None,
+ dto_class: type[ConstraintMappingType] | None,
) -> list[str]:
property_names = []
@@ -191,8 +191,8 @@ def get_property_names_to_process(
def get_constrained_values(
- dto_class: type[DtoType] | None, property_name: str
-) -> list[JSON | type[DtoType]]:
+ dto_class: type[ConstraintMappingType] | None, property_name: str
+) -> list[JSON | type[ConstraintMappingType]]:
relations = dto_class.get_relations() if dto_class else []
values_list = [
c.values
@@ -204,7 +204,7 @@ def get_constrained_values(
def get_dependent_id(
- dto_class: type[DtoType] | None,
+ dto_class: type[ConstraintMappingType] | None,
property_name: str,
operation_id: str | None,
) -> str | int | float | None:
diff --git a/src/OpenApiLibCore/data_generation/data_generation_core.py b/src/OpenApiLibCore/data_generation/data_generation_core.py
index 28b24ed..dc86471 100644
--- a/src/OpenApiLibCore/data_generation/data_generation_core.py
+++ b/src/OpenApiLibCore/data_generation/data_generation_core.py
@@ -28,7 +28,7 @@
UnionTypeSchema,
)
from OpenApiLibCore.models.request_data import RequestData
-from OpenApiLibCore.protocols import DtoType
+from OpenApiLibCore.protocols import ConstraintMappingType
from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
from .body_data_generation import (
@@ -113,7 +113,7 @@ def get_request_data(
def _get_dto_instance_for_empty_body(
- dto_class: type[DtoType] | None,
+ dto_class: type[ConstraintMappingType] | None,
dto_cls_name: str,
method_spec: OperationObject,
) -> Dto:
@@ -130,7 +130,7 @@ def _get_dto_instance_for_empty_body(
def _get_dto_instance_from_dto_data(
schema: ResolvedSchemaObjectTypes,
- dto_class: type[DtoType] | None,
+ dto_class: type[ConstraintMappingType] | None,
dto_data: JSON,
method_spec: OperationObject,
dto_cls_name: str,
@@ -205,7 +205,7 @@ def get_dto_cls_name(path: str, method: str) -> str:
def get_request_parameters(
- dto_class: type[DtoType] | None, method_spec: OperationObject
+ dto_class: type[ConstraintMappingType] | None, method_spec: OperationObject
) -> tuple[list[ParameterObject], dict[str, Any], dict[str, str]]:
"""Get the methods parameter spec and params and headers with valid data."""
parameters = method_spec.parameters if method_spec.parameters else []
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index da2a2ab..f66fceb 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -28,7 +28,7 @@
python_type_by_json_type_name,
)
from OpenApiLibCore.models import Ignore
-from OpenApiLibCore.protocols import DtoType
+from OpenApiLibCore.protocols import ConstraintMappingType
from OpenApiLibCore.utils.id_mapping import dummy_transformer
EPSILON = float_info.epsilon
@@ -41,7 +41,9 @@ class SchemaBase(BaseModel, Generic[O], frozen=True):
writeOnly: bool = False
@abstractmethod
- def get_valid_value(self) -> JSON: ...
+ def get_valid_value(
+ self, constraint_mapping: ConstraintMappingType | None = None
+ ) -> JSON: ...
@abstractmethod
def get_values_out_of_bounds(self, current_value: O) -> list[O]: ...
@@ -101,7 +103,9 @@ def get_invalid_value(
class NullSchema(SchemaBase[None], frozen=True):
type: Literal["null"] = "null"
- def get_valid_value(self) -> None:
+ def get_valid_value(
+ self, constraint_mapping: ConstraintMappingType | None = None
+ ) -> None:
return None
def get_values_out_of_bounds(self, current_value: None) -> list[None]:
@@ -124,7 +128,9 @@ class BooleanSchema(SchemaBase[bool], frozen=True):
const: bool | None = None
nullable: bool = False
- def get_valid_value(self) -> bool:
+ def get_valid_value(
+ self, constraint_mapping: ConstraintMappingType | None = None
+ ) -> bool:
if self.const is not None:
return self.const
return choice([True, False])
@@ -156,7 +162,9 @@ class StringSchema(SchemaBase[str], frozen=True):
enum: list[str] | None = None
nullable: bool = False
- def get_valid_value(self) -> bytes | str:
+ def get_valid_value(
+ self, constraint_mapping: ConstraintMappingType | None = None
+ ) -> bytes | str:
"""Generate a random string within the min/max length in the schema, if specified."""
if self.const is not None:
return self.const
@@ -284,7 +292,9 @@ def _min_value(self) -> int:
return self._min_int
- def get_valid_value(self) -> int:
+ def get_valid_value(
+ self, constraint_mapping: ConstraintMappingType | None = None
+ ) -> int:
"""Generate a random int within the min/max range of the schema, if specified."""
if self.const is not None:
return self.const
@@ -383,7 +393,9 @@ def _min_value(self) -> float:
return self._min_float
- def get_valid_value(self) -> float:
+ def get_valid_value(
+ self, constraint_mapping: ConstraintMappingType | None = None
+ ) -> float:
"""Generate a random float within the min/max range of the schema, if specified."""
if self.const is not None:
return self.const
@@ -441,7 +453,9 @@ class ArraySchema(SchemaBase[list[JSON]], frozen=True):
enum: list[list[JSON]] | None = None
nullable: bool = False
- def get_valid_value(self) -> list[JSON]:
+ def get_valid_value(
+ self, constraint_mapping: ConstraintMappingType | None = None
+ ) -> list[JSON]:
if self.const is not None:
return self.const
@@ -532,7 +546,9 @@ class ObjectSchema(SchemaBase[dict[str, JSON]], frozen=True):
enum: list[dict[str, JSON]] | None = None
nullable: bool = False
- def get_valid_value(self) -> dict[str, JSON]:
+ def get_valid_value(
+ self, constraint_mapping: ConstraintMappingType | None = None
+ ) -> dict[str, JSON]:
raise NotImplementedError
def get_values_out_of_bounds(
@@ -752,7 +768,7 @@ class OperationObject(BaseModel):
parameters: list[ParameterObject] | None = None
requestBody: RequestBodyObject | None = None
responses: dict[str, ResponseObject] = {}
- dto: type[DtoType] | None = None
+ dto: type[ConstraintMappingType] | None = None
class PathItemObject(BaseModel):
@@ -764,7 +780,7 @@ class PathItemObject(BaseModel):
summary: str = ""
description: str = ""
parameters: list[ParameterObject] | None = None
- dto: DtoType | None = None
+ dto: ConstraintMappingType | None = None
id_mapper: tuple[str, Callable[[str], str] | Callable[[int], int]] = (
"id",
dummy_transformer,
diff --git a/src/OpenApiLibCore/models/request_data.py b/src/OpenApiLibCore/models/request_data.py
index cc70a20..0482689 100644
--- a/src/OpenApiLibCore/models/request_data.py
+++ b/src/OpenApiLibCore/models/request_data.py
@@ -13,7 +13,7 @@
ResolvedSchemaObjectTypes,
UnionTypeSchema,
)
-from OpenApiLibCore.protocols import DtoType
+from OpenApiLibCore.protocols import ConstraintMappingType
@dataclass
@@ -54,7 +54,7 @@ def remove_parameters(self, parameters: list[str]) -> None:
class RequestData:
"""Helper class to manage parameters used when making requests."""
- dto: DtoType
+ dto: ConstraintMappingType
body_schema: ResolvedSchemaObjectTypes | None = None
parameters: list[ParameterObject] = field(default_factory=list)
params: dict[str, JSON] = field(default_factory=dict)
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index 4686a95..830434a 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -29,9 +29,9 @@
from OpenApiLibCore.data_constraints.dto_base import (
Dto,
IdReference,
+ get_constraint_mapping_dict,
get_id_property_name,
get_path_mapping_dict,
- get_value_constraints_mapping_dict,
)
from OpenApiLibCore.data_generation.localized_faker import FAKE
from OpenApiLibCore.models.oas_models import (
@@ -119,7 +119,7 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
mappings_folder = str(mappings_path.parent)
sys.path.append(mappings_folder)
mappings_module_name = mappings_path.stem
- self.value_constraints_mapping_dict = get_value_constraints_mapping_dict(
+ self.constraint_mapping_dict = get_constraint_mapping_dict(
mappings_module_name=mappings_module_name
)
self.path_mapping_dict = get_path_mapping_dict(
@@ -131,7 +131,7 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
)
sys.path.pop()
else:
- self.value_constraints_mapping_dict = get_value_constraints_mapping_dict(
+ self.constraint_mapping_dict = get_constraint_mapping_dict(
mappings_module_name="no mapping"
)
self.path_mapping_dict = get_path_mapping_dict(
@@ -571,7 +571,7 @@ def _attach_user_mappings(self, spec_model: OpenApiObject) -> OpenApiObject:
for (
path,
operation,
- ), data_constraint in self.value_constraints_mapping_dict.items():
+ ), data_constraint in self.constraint_mapping_dict.items():
try:
operation_item = getattr(spec_model.paths[path], operation.lower())
operation_item.dto = data_constraint
diff --git a/src/OpenApiLibCore/protocols.py b/src/OpenApiLibCore/protocols.py
index 57b4775..abdb96f 100644
--- a/src/OpenApiLibCore/protocols.py
+++ b/src/OpenApiLibCore/protocols.py
@@ -32,7 +32,7 @@ def __call__(
]: ... # pragma: no cover
-class DtoType(Protocol):
+class ConstraintMappingType(Protocol):
@classmethod
def __get_pydantic_core_schema__(
cls, source_type: Any, handler: GetCoreSchemaHandler
diff --git a/tests/libcore/unittests/test_dto_utils.py b/tests/libcore/unittests/test_dto_utils.py
index 83bf6de..0e61a39 100644
--- a/tests/libcore/unittests/test_dto_utils.py
+++ b/tests/libcore/unittests/test_dto_utils.py
@@ -4,7 +4,7 @@
import unittest
from OpenApiLibCore import Dto
-from OpenApiLibCore.data_constraints.dto_base import get_value_constraints_mapping_dict
+from OpenApiLibCore.data_constraints.dto_base import get_constraint_mapping_dict
unittest_folder = pathlib.Path(__file__).parent.resolve()
mappings_path = (
@@ -31,18 +31,18 @@ def tearDownClass(cls) -> None:
print(f"removed {sys.path.pop()} from path")
def test_no_mapping(self) -> None:
- value_constraints_mapping_dict = get_value_constraints_mapping_dict("dummy")
+ value_constraints_mapping_dict = get_constraint_mapping_dict("dummy")
self.assertDictEqual(value_constraints_mapping_dict, {})
def test_valid_mapping(self) -> None:
- value_constraints_mapping_dict = get_value_constraints_mapping_dict(
+ value_constraints_mapping_dict = get_constraint_mapping_dict(
self.mappings_module_name
)
self.assertIsInstance(value_constraints_mapping_dict, dict)
self.assertGreater(len(value_constraints_mapping_dict.keys()), 0)
def test_mapped_returns_dto_instance(self) -> None:
- value_constraints_mapping_dict = get_value_constraints_mapping_dict(
+ value_constraints_mapping_dict = get_constraint_mapping_dict(
self.mappings_module_name
)
keys = value_constraints_mapping_dict.keys()
From a653cbfae0db61fb7d662cf81164df5f007e6346 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Tue, 7 Oct 2025 07:35:32 +0000
Subject: [PATCH 09/55] Separate generated json_data from constraint logic,
move valid value generation to schema models
---
src/OpenApiDriver/__init__.py | 6 +-
src/OpenApiDriver/openapi_executors.py | 10 +-
src/OpenApiLibCore/__init__.py | 10 +-
.../data_constraints/dto_base.py | 184 +--------
.../data_generation/body_data_generation.py | 233 -----------
.../data_generation/data_generation_core.py | 28 +-
.../data_generation/data_invalidation.py | 51 ++-
.../keyword_logic/resource_relations.py | 4 +-
src/OpenApiLibCore/models/oas_models.py | 389 +++++++++++++++++-
src/OpenApiLibCore/models/request_data.py | 18 +-
.../models/resource_relations.py | 63 +++
src/OpenApiLibCore/openapi_libcore.py | 33 +-
src/OpenApiLibCore/protocols.py | 37 +-
src/OpenApiLibCore/utils/parameter_utils.py | 19 -
.../suites/test_get_invalid_json_data.robot | 2 +-
.../test_get_json_data_with_conflict.robot | 3 +
.../suites/test_get_request_data.robot | 16 +-
tests/libcore/suites/test_readonly.robot | 2 +-
.../suites/test_request_data_class.robot | 2 +-
.../suites/test_validate_response.robot | 2 +-
.../libcore/unittests/test_get_valid_value.py | 20 +-
21 files changed, 599 insertions(+), 533 deletions(-)
delete mode 100644 src/OpenApiLibCore/data_generation/body_data_generation.py
create mode 100644 src/OpenApiLibCore/models/resource_relations.py
diff --git a/src/OpenApiDriver/__init__.py b/src/OpenApiDriver/__init__.py
index a177631..f459108 100644
--- a/src/OpenApiDriver/__init__.py
+++ b/src/OpenApiDriver/__init__.py
@@ -15,6 +15,10 @@
from OpenApiDriver.openapidriver import OpenApiDriver
from OpenApiLibCore.data_constraints.dto_base import (
Dto,
+)
+from OpenApiLibCore.data_generation.value_utils import IGNORE
+from OpenApiLibCore.keyword_logic.validation import ValidationLevel
+from OpenApiLibCore.models.resource_relations import (
IdDependency,
IdReference,
PathPropertiesConstraint,
@@ -22,8 +26,6 @@
ResourceRelation,
UniquePropertyValueConstraint,
)
-from OpenApiLibCore.data_generation.value_utils import IGNORE
-from OpenApiLibCore.keyword_logic.validation import ValidationLevel
try:
__version__ = version("robotframework-openapidriver")
diff --git a/src/OpenApiDriver/openapi_executors.py b/src/OpenApiDriver/openapi_executors.py
index 95823b2..f40ac6e 100644
--- a/src/OpenApiDriver/openapi_executors.py
+++ b/src/OpenApiDriver/openapi_executors.py
@@ -168,8 +168,7 @@ def test_invalid_url(
request_data: RequestData = run_keyword("get_request_data", path, method)
params = request_data.params
headers = request_data.headers
- dto = request_data.dto
- json_data = dto.as_dict()
+ json_data = request_data.valid_data
response: Response = run_keyword(
"authorized_request", url, method, params, headers, json_data
)
@@ -190,18 +189,13 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
The keyword calls other keywords to generate the neccesary data to perform
the desired operation and validate the response against the openapi document.
"""
- json_data: dict[str, JSON] = {}
original_data = {}
url: str = run_keyword("get_valid_url", path)
request_data: RequestData = run_keyword("get_request_data", path, method)
params = request_data.params
headers = request_data.headers
- if request_data.has_body:
- if isinstance(request_data.body_schema, ArraySchema):
- json_data = request_data.dto.as_list()
- else:
- json_data = request_data.dto.as_dict()
+ json_data = request_data.valid_data
# when patching, get the original data to check only patched data has changed
if method == "PATCH":
original_data = self.get_original_data(url=url)
diff --git a/src/OpenApiLibCore/__init__.py b/src/OpenApiLibCore/__init__.py
index 7940050..928debb 100644
--- a/src/OpenApiLibCore/__init__.py
+++ b/src/OpenApiLibCore/__init__.py
@@ -13,8 +13,11 @@
from importlib.metadata import version
-from OpenApiLibCore.data_constraints.dto_base import (
- Dto,
+from OpenApiLibCore.data_constraints.dto_base import Dto
+from OpenApiLibCore.keyword_logic.validation import ValidationLevel
+from OpenApiLibCore.models import IGNORE, UNSET
+from OpenApiLibCore.models.request_data import RequestData, RequestValues
+from OpenApiLibCore.models.resource_relations import (
IdDependency,
IdReference,
PathPropertiesConstraint,
@@ -22,9 +25,6 @@
ResourceRelation,
UniquePropertyValueConstraint,
)
-from OpenApiLibCore.keyword_logic.validation import ValidationLevel
-from OpenApiLibCore.models import IGNORE, UNSET
-from OpenApiLibCore.models.request_data import RequestData, RequestValues
from OpenApiLibCore.openapi_libcore import OpenApiLibCore
try:
diff --git a/src/OpenApiLibCore/data_constraints/dto_base.py b/src/OpenApiLibCore/data_constraints/dto_base.py
index 3e89fa2..5edb70e 100644
--- a/src/OpenApiLibCore/data_constraints/dto_base.py
+++ b/src/OpenApiLibCore/data_constraints/dto_base.py
@@ -7,13 +7,15 @@
from abc import ABC
from dataclasses import dataclass, fields
from importlib import import_module
-from random import choice, shuffle
from typing import Any, Callable
-from uuid import uuid4
from robot.api import logger
-from OpenApiLibCore.models.oas_models import NullSchema, ObjectSchema, UnionTypeSchema
+from OpenApiLibCore.models.resource_relations import (
+ NOT_SET,
+ PathPropertiesConstraint,
+ ResourceRelation,
+)
from OpenApiLibCore.protocols import (
ConstraintMappingType,
GetIdPropertyNameType,
@@ -21,67 +23,6 @@
from OpenApiLibCore.utils import parameter_utils
from OpenApiLibCore.utils.id_mapping import dummy_transformer
-NOT_SET = object()
-SENTINEL = object()
-
-
-class ResourceRelation(ABC):
- """ABC for all resource relations or restrictions within the API."""
-
- property_name: str
- error_code: int
-
-
-@dataclass
-class PathPropertiesConstraint(ResourceRelation):
- """The value to be used as the ``path`` for related requests."""
-
- path: str
- property_name: str = "id"
- invalid_value: Any = NOT_SET
- invalid_value_error_code: int = 422
- error_code: int = 404
-
-
-@dataclass
-class PropertyValueConstraint(ResourceRelation):
- """The allowed values for property_name."""
-
- property_name: str
- values: list[Any]
- invalid_value: Any = NOT_SET
- invalid_value_error_code: int = 422
- error_code: int = 422
- treat_as_mandatory: bool = False
-
-
-@dataclass
-class IdDependency(ResourceRelation):
- """The path where a valid id for the property_name can be gotten (using GET)."""
-
- property_name: str
- get_path: str
- operation_id: str = ""
- error_code: int = 422
-
-
-@dataclass
-class IdReference(ResourceRelation):
- """The path where a resource that needs this resource's id can be created (using POST)."""
-
- property_name: str
- post_path: str
- error_code: int = 422
-
-
-@dataclass
-class UniquePropertyValueConstraint(ResourceRelation):
- """The value of the property must be unique within the resource scope."""
-
- property_name: str
- value: Any
- error_code: int = 422
-
@dataclass
class Dto(ABC):
@@ -92,13 +33,14 @@ def get_path_relations() -> list[PathPropertiesConstraint]:
"""Return the list of Relations for the header and query parameters."""
return []
+ @classmethod
def get_path_relations_for_error_code(
- self, error_code: int
+ cls, error_code: int
) -> list[PathPropertiesConstraint]:
"""Return the list of Relations associated with the given error_code."""
relations: list[PathPropertiesConstraint] = [
r
- for r in self.get_path_relations()
+ for r in cls.get_path_relations()
if r.error_code == error_code
or (
getattr(r, "invalid_value_error_code", None) == error_code
@@ -112,13 +54,14 @@ def get_parameter_relations() -> list[ResourceRelation]:
"""Return the list of Relations for the header and query parameters."""
return []
+ @classmethod
def get_parameter_relations_for_error_code(
- self, error_code: int
+ cls, error_code: int
) -> list[ResourceRelation]:
"""Return the list of Relations associated with the given error_code."""
relations: list[ResourceRelation] = [
r
- for r in self.get_parameter_relations()
+ for r in cls.get_parameter_relations()
if r.error_code == error_code
or (
getattr(r, "invalid_value_error_code", None) == error_code
@@ -132,8 +75,9 @@ def get_relations() -> list[ResourceRelation]:
"""Return the list of Relations for the (json) body."""
return []
+ @classmethod
def get_body_relations_for_error_code(
- self, error_code: int
+ cls, error_code: int
) -> list[ResourceRelation]:
"""
Return the list of Relations associated with the given error_code that are
@@ -141,7 +85,7 @@ def get_body_relations_for_error_code(
"""
relations: list[ResourceRelation] = [
r
- for r in self.get_relations()
+ for r in cls.get_relations()
if r.error_code == error_code
or (
getattr(r, "invalid_value_error_code", None) == error_code
@@ -150,106 +94,6 @@ def get_body_relations_for_error_code(
]
return relations
- def get_invalidated_data(
- self,
- schema: ObjectSchema,
- status_code: int,
- invalid_property_default_code: int,
- ) -> dict[str, Any]:
- """Return a data set with one of the properties set to an invalid value or type."""
- properties: dict[str, Any] = self.as_dict()
-
- relations = self.get_body_relations_for_error_code(error_code=status_code)
- property_names = [r.property_name for r in relations]
- if status_code == invalid_property_default_code:
- # add all properties defined in the schema, including optional properties
- property_names.extend((schema.properties.root.keys())) # type: ignore[union-attr]
- if not property_names:
- raise ValueError(
- f"No property can be invalidated to cause status_code {status_code}"
- )
- # Remove duplicates, then shuffle the property_names so different properties on
- # the Dto are invalidated when rerunning the test.
- shuffle(list(set(property_names)))
- for property_name in property_names:
- # if possible, invalidate a constraint but send otherwise valid data
- id_dependencies = [
- r
- for r in relations
- if isinstance(r, IdDependency) and r.property_name == property_name
- ]
- if id_dependencies:
- invalid_id = uuid4().hex
- logger.debug(
- f"Breaking IdDependency for status_code {status_code}: setting "
- f"{property_name} to {invalid_id}"
- )
- properties[property_name] = invalid_id
- return properties
-
- invalid_value_from_constraint = [
- r.invalid_value
- for r in relations
- if isinstance(r, PropertyValueConstraint)
- and r.property_name == property_name
- and r.invalid_value_error_code == status_code
- ]
- if (
- invalid_value_from_constraint
- and invalid_value_from_constraint[0] is not NOT_SET
- ):
- properties[property_name] = invalid_value_from_constraint[0]
- logger.debug(
- f"Using invalid_value {invalid_value_from_constraint[0]} to "
- f"invalidate property {property_name}"
- )
- return properties
-
- value_schema = schema.properties.root[property_name] # type: ignore[union-attr]
- if isinstance(value_schema, UnionTypeSchema):
- # Filter "type": "null" from the possible types since this indicates an
- # optional / nullable property that can only be invalidated by sending
- # invalid data of a non-null type
- non_null_schemas = [
- s
- for s in value_schema.resolved_schemas
- if not isinstance(s, NullSchema)
- ]
- value_schema = choice(non_null_schemas)
-
- # there may not be a current_value when invalidating an optional property
- current_value = properties.get(property_name, SENTINEL)
- if current_value is SENTINEL:
- # the current_value isn't very relevant as long as the type is correct
- # so no logic to handle Relations / objects / arrays here
- property_type = value_schema.type
- if property_type == "object":
- current_value = {}
- elif property_type == "array":
- current_value = []
- else:
- current_value = value_schema.get_valid_value()
-
- values_from_constraint = [
- r.values[0]
- for r in relations
- if isinstance(r, PropertyValueConstraint)
- and r.property_name == property_name
- ]
-
- invalid_value = value_schema.get_invalid_value(
- current_value=current_value,
- values_from_constraint=values_from_constraint,
- )
- properties[property_name] = invalid_value
- logger.debug(
- f"Property {property_name} changed to {invalid_value!r} (received from "
- f"get_invalid_value)"
- )
- return properties
- logger.warn("get_invalidated_data returned unchanged properties")
- return properties # pragma: no cover
-
def as_dict(self) -> dict[Any, Any]:
"""Return the dict representation of the Dto."""
result = {}
diff --git a/src/OpenApiLibCore/data_generation/body_data_generation.py b/src/OpenApiLibCore/data_generation/body_data_generation.py
deleted file mode 100644
index 956de8c..0000000
--- a/src/OpenApiLibCore/data_generation/body_data_generation.py
+++ /dev/null
@@ -1,233 +0,0 @@
-"""
-Module holding the functions related to (json) data generation
-for the body of requests made as part of keyword exection.
-"""
-
-from random import choice, randint, sample
-from typing import Any
-
-from robot.api import logger
-from robot.libraries.BuiltIn import BuiltIn
-
-from OpenApiLibCore.annotations import JSON
-from OpenApiLibCore.data_constraints.dto_base import (
- IdDependency,
- PropertyValueConstraint,
-)
-from OpenApiLibCore.data_generation.value_utils import IGNORE
-from OpenApiLibCore.models.oas_models import (
- ArraySchema,
- ObjectSchema,
- SchemaObjectTypes,
- UnionTypeSchema,
-)
-from OpenApiLibCore.protocols import ConstraintMappingType
-from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
-
-run_keyword = BuiltIn().run_keyword
-
-
-def get_json_data_for_dto_class(
- schema: SchemaObjectTypes,
- dto_class: type[ConstraintMappingType] | None,
- operation_id: str | None = None,
-) -> JSON:
- if isinstance(schema, UnionTypeSchema):
- chosen_schema = choice(schema.resolved_schemas)
- return get_json_data_for_dto_class(
- schema=chosen_schema,
- dto_class=dto_class,
- operation_id=operation_id,
- )
-
- match schema:
- case ObjectSchema():
- return get_dict_data_for_dto_class(
- schema=schema,
- dto_class=dto_class,
- operation_id=operation_id,
- )
- case ArraySchema():
- return get_list_data_for_dto_class(
- schema=schema,
- dto_class=dto_class,
- operation_id=operation_id,
- )
- case _:
- return schema.get_valid_value()
-
-
-def get_dict_data_for_dto_class(
- schema: ObjectSchema,
- dto_class: type[ConstraintMappingType] | None,
- operation_id: str | None = None,
-) -> dict[str, Any]:
- json_data: dict[str, Any] = {}
-
- property_names = get_property_names_to_process(schema=schema, dto_class=dto_class)
-
- for property_name in property_names:
- property_schema = schema.properties.root[property_name] # type: ignore[union-attr]
- if property_schema.readOnly:
- continue
-
- json_data[property_name] = get_data_for_property(
- property_name=property_name,
- property_schema=property_schema,
- dto_class=dto_class,
- operation_id=operation_id,
- )
-
- return json_data
-
-
-def get_list_data_for_dto_class(
- schema: ArraySchema,
- dto_class: type[ConstraintMappingType] | None,
- operation_id: str | None = None,
-) -> list[JSON]:
- json_data: list[JSON] = []
- list_item_schema = schema.items
- min_items = schema.minItems if schema.minItems is not None else 0
- max_items = schema.maxItems if schema.maxItems is not None else 1
- number_of_items_to_generate = randint(min_items, max_items)
- for _ in range(number_of_items_to_generate):
- list_item_data = get_json_data_for_dto_class(
- schema=list_item_schema,
- dto_class=dto_class,
- operation_id=operation_id,
- )
- json_data.append(list_item_data)
- return json_data
-
-
-def get_data_for_property(
- property_name: str,
- property_schema: SchemaObjectTypes,
- dto_class: type[ConstraintMappingType] | None,
- operation_id: str | None,
-) -> JSON:
- if constrained_values := get_constrained_values(
- dto_class=dto_class, property_name=property_name
- ):
- constrained_value = choice(constrained_values)
- # Check if the chosen value is a nested Dto; since a Dto is never
- # instantiated, we can use isinstance(..., type) for this.
- if isinstance(constrained_value, type):
- return get_value_constrained_by_nested_dto(
- property_schema=property_schema,
- nested_dto_class=constrained_value,
- operation_id=operation_id,
- )
- return constrained_value
-
- if (
- dependent_id := get_dependent_id(
- dto_class=dto_class,
- property_name=property_name,
- operation_id=operation_id,
- )
- ) is not None:
- return dependent_id
-
- return get_json_data_for_dto_class(
- schema=property_schema,
- dto_class=None,
- )
-
-
-def get_value_constrained_by_nested_dto(
- property_schema: SchemaObjectTypes,
- nested_dto_class: type[ConstraintMappingType],
- operation_id: str | None,
-) -> JSON:
- nested_schema = get_schema_for_nested_dto(property_schema=property_schema)
- nested_value = get_json_data_for_dto_class(
- schema=nested_schema,
- dto_class=nested_dto_class,
- operation_id=operation_id,
- )
- return nested_value
-
-
-def get_schema_for_nested_dto(property_schema: SchemaObjectTypes) -> SchemaObjectTypes:
- if isinstance(property_schema, UnionTypeSchema):
- chosen_schema = choice(property_schema.resolved_schemas)
- return get_schema_for_nested_dto(chosen_schema)
-
- return property_schema
-
-
-def get_property_names_to_process(
- schema: ObjectSchema,
- dto_class: type[ConstraintMappingType] | None,
-) -> list[str]:
- property_names = []
-
- for property_name in schema.properties.root: # type: ignore[union-attr]
- # register the oas_name
- _ = get_safe_name_for_oas_name(property_name)
- if constrained_values := get_constrained_values(
- dto_class=dto_class, property_name=property_name
- ):
- # do not add properties that are configured to be ignored
- if IGNORE in constrained_values: # type: ignore[comparison-overlap]
- continue
- property_names.append(property_name)
-
- max_properties = schema.maxProperties
- if max_properties and len(property_names) > max_properties:
- required_properties = schema.required
- number_of_optional_properties = max_properties - len(required_properties)
- optional_properties = [
- name for name in property_names if name not in required_properties
- ]
- selected_optional_properties = sample(
- optional_properties, number_of_optional_properties
- )
- property_names = required_properties + selected_optional_properties
-
- return property_names
-
-
-def get_constrained_values(
- dto_class: type[ConstraintMappingType] | None, property_name: str
-) -> list[JSON | type[ConstraintMappingType]]:
- relations = dto_class.get_relations() if dto_class else []
- values_list = [
- c.values
- for c in relations
- if (isinstance(c, PropertyValueConstraint) and c.property_name == property_name)
- ]
- # values should be empty or contain 1 list of allowed values
- return values_list.pop() if values_list else []
-
-
-def get_dependent_id(
- dto_class: type[ConstraintMappingType] | None,
- property_name: str,
- operation_id: str | None,
-) -> str | int | float | None:
- relations = dto_class.get_relations() if dto_class else []
- # multiple get paths are possible based on the operation being performed
- id_get_paths = [
- (d.get_path, d.operation_id)
- for d in relations
- if (isinstance(d, IdDependency) and d.property_name == property_name)
- ]
- if not id_get_paths:
- return None
- if len(id_get_paths) == 1:
- id_get_path, _ = id_get_paths.pop()
- else:
- try:
- [id_get_path] = [
- path for path, operation in id_get_paths if operation == operation_id
- ]
- # There could be multiple get_paths, but not one for the current operation
- except ValueError:
- return None
-
- valid_id = run_keyword("get_valid_id_for_path", id_get_path)
- logger.debug(f"get_dependent_id for {id_get_path} returned {valid_id}")
- return valid_id
diff --git a/src/OpenApiLibCore/data_generation/data_generation_core.py b/src/OpenApiLibCore/data_generation/data_generation_core.py
index dc86471..2b02121 100644
--- a/src/OpenApiLibCore/data_generation/data_generation_core.py
+++ b/src/OpenApiLibCore/data_generation/data_generation_core.py
@@ -12,11 +12,7 @@
import OpenApiLibCore.keyword_logic.path_functions as _path_functions
from OpenApiLibCore.annotations import JSON
-from OpenApiLibCore.data_constraints.dto_base import (
- Dto,
- PropertyValueConstraint,
- ResourceRelation,
-)
+from OpenApiLibCore.data_constraints.dto_base import Dto
from OpenApiLibCore.data_generation.value_utils import IGNORE
from OpenApiLibCore.models.oas_models import (
ArraySchema,
@@ -26,15 +22,16 @@
ParameterObject,
ResolvedSchemaObjectTypes,
UnionTypeSchema,
+ get_valid_json_data,
)
from OpenApiLibCore.models.request_data import RequestData
+from OpenApiLibCore.models.resource_relations import (
+ PropertyValueConstraint,
+ ResourceRelation,
+)
from OpenApiLibCore.protocols import ConstraintMappingType
from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
-from .body_data_generation import (
- get_json_data_for_dto_class as _get_json_data_for_dto_class,
-)
-
def get_request_data(
path: str,
@@ -71,6 +68,7 @@ def get_request_data(
method_spec=operation_spec,
)
return RequestData(
+ valid_data=None,
dto=dto_instance,
parameters=parameters,
params=params,
@@ -91,7 +89,7 @@ def get_request_data(
resolved_schemas = body_schema.resolved_schemas
body_schema = choice(resolved_schemas)
- dto_data = _get_json_data_for_dto_class(
+ valid_data = get_valid_json_data(
schema=body_schema,
dto_class=dto_class,
operation_id=operation_spec.operationId,
@@ -99,11 +97,12 @@ def get_request_data(
dto_instance = _get_dto_instance_from_dto_data(
schema=body_schema,
dto_class=dto_class,
- dto_data=dto_data,
+ dto_data=valid_data,
method_spec=operation_spec,
dto_cls_name=dto_cls_name,
)
return RequestData(
+ valid_data=valid_data,
dto=dto_instance,
body_schema=body_schema,
parameters=parameters,
@@ -116,7 +115,7 @@ def _get_dto_instance_for_empty_body(
dto_class: type[ConstraintMappingType] | None,
dto_cls_name: str,
method_spec: OperationObject,
-) -> Dto:
+) -> type[Dto]:
cls_name = method_spec.operationId if method_spec.operationId else dto_cls_name
base = dto_class if dto_class else Dto
dto_class_ = make_dataclass(
@@ -124,8 +123,7 @@ def _get_dto_instance_for_empty_body(
fields=[],
bases=(base,),
)
- dto_instance = dto_class_()
- return dto_instance
+ return dto_class_
def _get_dto_instance_from_dto_data(
@@ -134,7 +132,7 @@ def _get_dto_instance_from_dto_data(
dto_data: JSON,
method_spec: OperationObject,
dto_cls_name: str,
-) -> Dto:
+) -> type[Dto]:
if not isinstance(schema, (ObjectSchema, ArraySchema)):
return _get_dto_instance_for_empty_body(
dto_class=dto_class, dto_cls_name=dto_cls_name, method_spec=method_spec
diff --git a/src/OpenApiLibCore/data_generation/data_invalidation.py b/src/OpenApiLibCore/data_generation/data_invalidation.py
index 426c227..ef65034 100644
--- a/src/OpenApiLibCore/data_generation/data_invalidation.py
+++ b/src/OpenApiLibCore/data_generation/data_invalidation.py
@@ -13,19 +13,22 @@
from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.data_constraints.dto_base import (
- NOT_SET,
Dto,
- IdReference,
- PropertyValueConstraint,
- UniquePropertyValueConstraint,
)
from OpenApiLibCore.data_generation.value_utils import IGNORE
from OpenApiLibCore.models.oas_models import (
ArraySchema,
+ ObjectSchema,
ParameterObject,
UnionTypeSchema,
)
from OpenApiLibCore.models.request_data import RequestData
+from OpenApiLibCore.models.resource_relations import (
+ NOT_SET,
+ IdReference,
+ PropertyValueConstraint,
+ UniquePropertyValueConstraint,
+)
run_keyword = BuiltIn().run_keyword
@@ -36,7 +39,7 @@ def get_invalid_body_data(
status_code: int,
request_data: RequestData,
invalid_property_default_response: int,
-) -> dict[str, Any] | list[Any]:
+) -> dict[str, JSON] | list[JSON]:
method = method.lower()
data_relations = request_data.dto.get_body_relations_for_error_code(status_code)
if not data_relations:
@@ -44,38 +47,52 @@ def get_invalid_body_data(
raise ValueError(
"Failed to invalidate: request_data does not contain a body_schema."
)
+
+ if not isinstance(request_data.body_schema, (ArraySchema, ObjectSchema)):
+ raise NotImplementedError("primitive types not supported for body data.")
+
if isinstance(request_data.body_schema, ArraySchema):
- invalid_item_data = request_data.dto.get_invalidated_data(
- schema=request_data.body_schema.items,
+ if not isinstance(request_data.valid_data, list):
+ raise ValueError("Type of valid_data does not match body_schema type.")
+ invalid_item_data = request_data.body_schema.get_invalidated_data(
+ valid_data=request_data.valid_data,
+ constraint_mapping=request_data.dto,
status_code=status_code,
invalid_property_default_code=invalid_property_default_response,
)
return [invalid_item_data]
- json_data = request_data.dto.get_invalidated_data(
- schema=request_data.body_schema,
+
+ if not isinstance(request_data.valid_data, dict):
+ raise ValueError("Type of valid_data does not match body_schema type.")
+ json_data = request_data.body_schema.get_invalidated_data(
+ valid_data=request_data.valid_data,
+ constraint_mapping=request_data.dto,
status_code=status_code,
invalid_property_default_code=invalid_property_default_response,
)
return json_data
+
resource_relation = choice(data_relations)
if isinstance(resource_relation, UniquePropertyValueConstraint):
json_data = run_keyword(
"get_json_data_with_conflict",
url,
method,
+ request_data.valid_data,
request_data.dto,
status_code,
)
elif isinstance(resource_relation, IdReference):
run_keyword("ensure_in_use", url, resource_relation)
- json_data = request_data.dto.as_dict()
+ json_data = request_data.valid_data
else:
if request_data.body_schema is None:
raise ValueError(
"Failed to invalidate: request_data does not contain a body_schema."
)
- json_data = request_data.dto.get_invalidated_data(
- schema=request_data.body_schema,
+ json_data = request_data.body_schema.get_invalidated_data(
+ valid_data=request_data.valid_data,
+ constraint_mapping=request_data.dto,
status_code=status_code,
invalid_property_default_code=invalid_property_default_response,
)
@@ -264,10 +281,14 @@ def ensure_parameter_in_parameters(
def get_json_data_with_conflict(
- url: str, base_url: str, method: str, dto: Dto, conflict_status_code: int
+ url: str,
+ base_url: str,
+ method: str,
+ json_data: dict[str, JSON],
+ dto: Dto,
+ conflict_status_code: int,
) -> dict[str, Any]:
method = method.lower()
- json_data = dto.as_dict()
unique_property_value_constraints = [
r for r in dto.get_relations() if isinstance(r, UniquePropertyValueConstraint)
]
@@ -281,7 +302,7 @@ def get_json_data_with_conflict(
# so a valid POST dto must be constructed
path = post_url.replace(base_url, "")
request_data: RequestData = run_keyword("get_request_data", path, "post")
- post_json = request_data.dto.as_dict()
+ post_json = request_data.valid_data
for key in post_json.keys():
if key in json_data:
post_json[key] = json_data.get(key)
diff --git a/src/OpenApiLibCore/keyword_logic/resource_relations.py b/src/OpenApiLibCore/keyword_logic/resource_relations.py
index a36f99e..8454fde 100644
--- a/src/OpenApiLibCore/keyword_logic/resource_relations.py
+++ b/src/OpenApiLibCore/keyword_logic/resource_relations.py
@@ -5,9 +5,9 @@
from robot.libraries.BuiltIn import BuiltIn
import OpenApiLibCore.keyword_logic.path_functions as _path_functions
-from OpenApiLibCore.data_constraints.dto_base import IdReference
from OpenApiLibCore.models.oas_models import OpenApiObject
from OpenApiLibCore.models.request_data import RequestData
+from OpenApiLibCore.models.resource_relations import IdReference
run_keyword = BuiltIn().run_keyword
@@ -37,7 +37,7 @@ def ensure_in_use(
request_data: RequestData = run_keyword(
"get_request_data", resource_relation.post_path, "post"
)
- json_data = request_data.dto.as_dict()
+ json_data = request_data.valid_data if request_data.valid_data else {}
json_data[resource_relation.property_name] = resource_id
post_url: str = run_keyword("get_valid_url", resource_relation.post_path)
response: Response = run_keyword(
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index f66fceb..17bfa5d 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -3,10 +3,12 @@
import base64
from abc import abstractmethod
from collections import ChainMap
+from copy import deepcopy
from functools import cached_property
-from random import choice, randint, uniform
+from random import choice, randint, sample, shuffle, uniform
from sys import float_info
from typing import (
+ Any,
Callable,
Generator,
Generic,
@@ -16,23 +18,36 @@
TypeAlias,
TypeVar,
)
+from uuid import uuid4
import rstr
from pydantic import BaseModel, Field, RootModel
from robot.api import logger
+from robot.libraries.BuiltIn import BuiltIn
from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.data_generation.localized_faker import FAKE, fake_string
from OpenApiLibCore.data_generation.value_utils import (
+ IGNORE,
get_invalid_value_from_constraint,
python_type_by_json_type_name,
)
from OpenApiLibCore.models import Ignore
+from OpenApiLibCore.models.resource_relations import (
+ NOT_SET,
+ IdDependency,
+ PropertyValueConstraint,
+)
from OpenApiLibCore.protocols import ConstraintMappingType
from OpenApiLibCore.utils.id_mapping import dummy_transformer
+from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
+
+run_keyword = BuiltIn().run_keyword
EPSILON = float_info.epsilon
+SENTINEL = object()
+
O = TypeVar("O")
@@ -42,7 +57,9 @@ class SchemaBase(BaseModel, Generic[O], frozen=True):
@abstractmethod
def get_valid_value(
- self, constraint_mapping: ConstraintMappingType | None = None
+ self,
+ constraint_mapping: type[ConstraintMappingType] | None = None,
+ operation_id: str | None = None,
) -> JSON: ...
@abstractmethod
@@ -104,7 +121,9 @@ class NullSchema(SchemaBase[None], frozen=True):
type: Literal["null"] = "null"
def get_valid_value(
- self, constraint_mapping: ConstraintMappingType | None = None
+ self,
+ constraint_mapping: type[ConstraintMappingType] | None = None,
+ operation_id: str | None = None,
) -> None:
return None
@@ -129,7 +148,9 @@ class BooleanSchema(SchemaBase[bool], frozen=True):
nullable: bool = False
def get_valid_value(
- self, constraint_mapping: ConstraintMappingType | None = None
+ self,
+ constraint_mapping: type[ConstraintMappingType] | None = None,
+ operation_id: str | None = None,
) -> bool:
if self.const is not None:
return self.const
@@ -163,7 +184,9 @@ class StringSchema(SchemaBase[str], frozen=True):
nullable: bool = False
def get_valid_value(
- self, constraint_mapping: ConstraintMappingType | None = None
+ self,
+ constraint_mapping: type[ConstraintMappingType] | None = None,
+ operation_id: str | None = None,
) -> bytes | str:
"""Generate a random string within the min/max length in the schema, if specified."""
if self.const is not None:
@@ -293,7 +316,9 @@ def _min_value(self) -> int:
return self._min_int
def get_valid_value(
- self, constraint_mapping: ConstraintMappingType | None = None
+ self,
+ constraint_mapping: type[ConstraintMappingType] | None = None,
+ operation_id: str | None = None,
) -> int:
"""Generate a random int within the min/max range of the schema, if specified."""
if self.const is not None:
@@ -394,7 +419,9 @@ def _min_value(self) -> float:
return self._min_float
def get_valid_value(
- self, constraint_mapping: ConstraintMappingType | None = None
+ self,
+ constraint_mapping: type[ConstraintMappingType] | None = None,
+ operation_id: str | None = None,
) -> float:
"""Generate a random float within the min/max range of the schema, if specified."""
if self.const is not None:
@@ -445,7 +472,7 @@ def annotation_string(self) -> str:
class ArraySchema(SchemaBase[list[JSON]], frozen=True):
type: Literal["array"] = "array"
- items: "SchemaObjectTypes"
+ items: SchemaObjectTypes
maxItems: int | None = None
minItems: int | None = None
uniqueItems: bool = False
@@ -454,7 +481,9 @@ class ArraySchema(SchemaBase[list[JSON]], frozen=True):
nullable: bool = False
def get_valid_value(
- self, constraint_mapping: ConstraintMappingType | None = None
+ self,
+ constraint_mapping: type[ConstraintMappingType] | None = None,
+ operation_id: str | None = None,
) -> list[JSON]:
if self.const is not None:
return self.const
@@ -467,7 +496,8 @@ def get_valid_value(
maximum = max(minimum, maximum)
value: list[JSON] = []
- for _ in range(maximum):
+ number_of_items_to_generate = randint(minimum, maximum)
+ for _ in range(number_of_items_to_generate):
item_value = self.items.get_valid_value()
value.append(item_value)
return value
@@ -513,6 +543,52 @@ def get_invalid_value_from_const_or_enum(self) -> list[JSON]:
return invalid_value
+ def get_invalidated_data(
+ self,
+ valid_data: list[JSON],
+ constraint_mapping: ConstraintMappingType,
+ status_code: int,
+ invalid_property_default_code: int,
+ ) -> list[JSON]:
+ """Return a data set with one of the properties set to an invalid value or type."""
+ invalid_values: list[list[JSON]] = []
+
+ relations = constraint_mapping.get_body_relations_for_error_code(
+ error_code=status_code
+ )
+ # TODO: handle relations applicable to arrays / lists
+
+ if status_code == invalid_property_default_code:
+ try:
+ values_out_of_bounds = self.get_values_out_of_bounds(
+ current_value=valid_data
+ )
+ invalid_values.extend(values_out_of_bounds)
+ except ValueError:
+ pass
+ try:
+ invalid_const_or_enum = self.get_invalid_value_from_const_or_enum()
+ invalid_values.append(invalid_const_or_enum)
+ except ValueError:
+ pass
+ if valid_data and isinstance(self.items, ObjectSchema):
+ data_to_invalidate = deepcopy(valid_data)
+ valid_item = data_to_invalidate.pop()
+ invalid_item = self.items.get_invalidated_data(
+ valid_data=valid_item,
+ constraint_mapping=constraint_mapping,
+ status_code=status_code,
+ invalid_property_default_code=invalid_property_default_code,
+ )
+ invalid_data = [*data_to_invalidate, invalid_item]
+ invalid_values.append(invalid_data)
+
+ if not invalid_values:
+ raise ValueError(
+ f"No constraint can be broken to cause status_code {status_code}"
+ )
+ return choice(invalid_values)
+
@property
def can_be_invalidated(self) -> bool:
if (
@@ -547,9 +623,67 @@ class ObjectSchema(SchemaBase[dict[str, JSON]], frozen=True):
nullable: bool = False
def get_valid_value(
- self, constraint_mapping: ConstraintMappingType | None = None
+ self,
+ constraint_mapping: type[ConstraintMappingType] | None = None,
+ operation_id: str | None = None,
) -> dict[str, JSON]:
- raise NotImplementedError
+ if self.const is not None:
+ return self.const
+
+ if self.enum is not None:
+ return choice(self.enum)
+
+ json_data: dict[str, Any] = {}
+
+ property_names = self._get_property_names_to_process(
+ dto_class=constraint_mapping
+ )
+
+ for property_name in property_names:
+ property_schema = self.properties.root[property_name] # type: ignore[union-attr]
+ if property_schema.readOnly:
+ continue
+
+ json_data[property_name] = get_data_for_property(
+ property_name=property_name,
+ property_schema=property_schema,
+ dto_class=constraint_mapping,
+ operation_id=operation_id,
+ )
+
+ return json_data
+
+ def _get_property_names_to_process(
+ self,
+ dto_class: type[ConstraintMappingType] | None,
+ ) -> list[str]:
+ property_names = []
+
+ properties = {} if self.properties is None else self.properties.root
+ for property_name in properties: # type: ignore[union-attr]
+ # register the oas_name
+ _ = get_safe_name_for_oas_name(property_name)
+ if constrained_values := get_constrained_values(
+ dto_class=dto_class, property_name=property_name
+ ):
+ # do not add properties that are configured to be ignored
+ if IGNORE in constrained_values: # type: ignore[comparison-overlap]
+ continue
+ property_names.append(property_name)
+
+ max_properties = self.maxProperties
+ if max_properties and len(property_names) > max_properties:
+ required_properties = self.required
+ number_of_optional_properties = max_properties - len(required_properties)
+ optional_properties = [
+ name for name in property_names if name not in required_properties
+ ]
+ selected_optional_properties = sample(
+ optional_properties, number_of_optional_properties
+ )
+ property_names = required_properties + selected_optional_properties
+
+ return property_names
def get_values_out_of_bounds(
self, current_value: Mapping[str, JSON]
@@ -577,6 +711,110 @@ def get_invalid_value_from_const_or_enum(self) -> dict[str, JSON]:
raise ValueError
+ def get_invalidated_data(
+ self,
+ valid_data: dict[str, JSON],
+ constraint_mapping: ConstraintMappingType,
+ status_code: int,
+ invalid_property_default_code: int,
+ ) -> dict[str, JSON]:
+ """Return a data set with one of the properties set to an invalid value or type."""
+ # FIXME: data should be sperated from constraints_mapping
+ properties: dict[str, JSON] = deepcopy(valid_data)
+
+ relations = constraint_mapping.get_body_relations_for_error_code(
+ error_code=status_code
+ )
+ property_names = [r.property_name for r in relations]
+ if status_code == invalid_property_default_code:
+ # add all properties defined in the schema, including optional properties
+ property_names.extend((self.properties.root.keys())) # type: ignore[union-attr]
+ if not property_names:
+ raise ValueError(
+ f"No property can be invalidated to cause status_code {status_code}"
+ )
+ # Remove duplicates, then shuffle the property_names so different properties on
+ # the Dto are invalidated when rerunning the test.
+ shuffle(list(set(property_names)))
+ for property_name in property_names:
+ # if possible, invalidate a constraint but send otherwise valid data
+ id_dependencies = [
+ r
+ for r in relations
+ if isinstance(r, IdDependency) and r.property_name == property_name
+ ]
+ if id_dependencies:
+ invalid_id = uuid4().hex
+ logger.debug(
+ f"Breaking IdDependency for status_code {status_code}: setting "
+ f"{property_name} to {invalid_id}"
+ )
+ properties[property_name] = invalid_id
+ return properties
+
+ invalid_value_from_constraint = [
+ r.invalid_value
+ for r in relations
+ if isinstance(r, PropertyValueConstraint)
+ and r.property_name == property_name
+ and r.invalid_value_error_code == status_code
+ ]
+ if (
+ invalid_value_from_constraint
+ and invalid_value_from_constraint[0] is not NOT_SET
+ ):
+ properties[property_name] = invalid_value_from_constraint[0]
+ logger.debug(
+ f"Using invalid_value {invalid_value_from_constraint[0]} to "
+ f"invalidate property {property_name}"
+ )
+ return properties
+
+ value_schema = self.properties.root[property_name] # type: ignore[union-attr]
+ if isinstance(value_schema, UnionTypeSchema):
+ # Filter "type": "null" from the possible types since this indicates an
+ # optional / nullable property that can only be invalidated by sending
+ # invalid data of a non-null type
+ non_null_schemas = [
+ s
+ for s in value_schema.resolved_schemas
+ if not isinstance(s, NullSchema)
+ ]
+ value_schema = choice(non_null_schemas)
+
+ # there may not be a current_value when invalidating an optional property
+ current_value = properties.get(property_name, SENTINEL)
+ if current_value is SENTINEL:
+ # the current_value isn't very relevant as long as the type is correct
+ # so no logic to handle Relations / objects / arrays here
+ property_type = value_schema.type
+ if property_type == "object":
+ current_value = {}
+ elif property_type == "array":
+ current_value = []
+ else:
+ current_value = value_schema.get_valid_value()
+
+ values_from_constraint = [
+ r.values[0]
+ for r in relations
+ if isinstance(r, PropertyValueConstraint)
+ and r.property_name == property_name
+ ]
+
+ invalid_value = value_schema.get_invalid_value(
+ current_value=current_value,
+ values_from_constraint=values_from_constraint,
+ )
+ properties[property_name] = invalid_value
+ logger.debug(
+ f"Property {property_name} changed to {invalid_value!r} (received from "
+ f"get_invalid_value)"
+ )
+ return properties
+ logger.warn("get_invalidated_data returned unchanged properties")
+ return properties # pragma: no cover
+
@property
def can_be_invalidated(self) -> bool:
if (
@@ -610,7 +848,11 @@ class UnionTypeSchema(SchemaBase[JSON], frozen=True):
anyOf: list["SchemaObjectTypes"] = []
oneOf: list["SchemaObjectTypes"] = []
- def get_valid_value(self) -> JSON:
+ def get_valid_value(
+ self,
+ constraint_mapping: type[ConstraintMappingType] | None = None,
+ operation_id: str | None = None,
+ ) -> JSON:
chosen_schema = choice(self.resolved_schemas)
return chosen_schema.get_valid_value()
@@ -802,3 +1044,124 @@ class InfoObject(BaseModel):
class OpenApiObject(BaseModel):
info: InfoObject
paths: dict[str, PathItemObject]
+
+
+# TODO: can this be refactored away?
+def get_valid_json_data(
+ schema: SchemaObjectTypes,
+ dto_class: type[ConstraintMappingType] | None,
+ operation_id: str | None = None,
+) -> JSON:
+ if isinstance(schema, UnionTypeSchema):
+ chosen_schema = choice(schema.resolved_schemas)
+ return get_valid_json_data(
+ schema=chosen_schema,
+ dto_class=dto_class,
+ operation_id=operation_id,
+ )
+
+ return schema.get_valid_value(
+ constraint_mapping=dto_class, operation_id=operation_id
+ )
+
+
+# TODO: move to ObjectSchema?
+def get_data_for_property(
+ property_name: str,
+ property_schema: SchemaObjectTypes,
+ dto_class: type[ConstraintMappingType] | None,
+ operation_id: str | None,
+) -> JSON:
+ if constrained_values := get_constrained_values(
+ dto_class=dto_class, property_name=property_name
+ ):
+ constrained_value = choice(constrained_values)
+ # Check if the chosen value is a nested Dto; since a Dto is never
+ # instantiated, we can use isinstance(..., type) for this.
+ if isinstance(constrained_value, type):
+ return get_value_constrained_by_nested_dto(
+ property_schema=property_schema,
+ nested_dto_class=constrained_value,
+ operation_id=operation_id,
+ )
+ return constrained_value
+
+ if (
+ dependent_id := get_dependent_id(
+ dto_class=dto_class,
+ property_name=property_name,
+ operation_id=operation_id,
+ )
+ ) is not None:
+ return dependent_id
+
+ return get_valid_json_data(
+ schema=property_schema,
+ dto_class=None,
+ )
+
+
+# TODO: move to keyword_logic?
+def get_dependent_id(
+ dto_class: type[ConstraintMappingType] | None,
+ property_name: str,
+ operation_id: str | None,
+) -> str | int | float | None:
+ relations = dto_class.get_relations() if dto_class else []
+ # multiple get paths are possible based on the operation being performed
+ id_get_paths = [
+ (d.get_path, d.operation_id)
+ for d in relations
+ if (isinstance(d, IdDependency) and d.property_name == property_name)
+ ]
+ if not id_get_paths:
+ return None
+ if len(id_get_paths) == 1:
+ id_get_path, _ = id_get_paths.pop()
+ else:
+ try:
+ [id_get_path] = [
+ path for path, operation in id_get_paths if operation == operation_id
+ ]
+ # There could be multiple get_paths, but not one for the current operation
+ except ValueError:
+ return None
+
+ valid_id = run_keyword("get_valid_id_for_path", id_get_path)
+ logger.debug(f"get_dependent_id for {id_get_path} returned {valid_id}")
+ return valid_id
+
+
+def get_value_constrained_by_nested_dto(
+ property_schema: SchemaObjectTypes,
+ nested_dto_class: type[ConstraintMappingType],
+ operation_id: str | None,
+) -> JSON:
+ nested_schema = get_schema_for_nested_dto(property_schema=property_schema)
+ nested_value = get_valid_json_data(
+ schema=nested_schema,
+ dto_class=nested_dto_class,
+ operation_id=operation_id,
+ )
+ return nested_value
+
+
+def get_schema_for_nested_dto(property_schema: SchemaObjectTypes) -> SchemaObjectTypes:
+ if isinstance(property_schema, UnionTypeSchema):
+ chosen_schema = choice(property_schema.resolved_schemas)
+ return get_schema_for_nested_dto(chosen_schema)
+
+ return property_schema
+
+
+def get_constrained_values(
+ dto_class: type[ConstraintMappingType] | None, property_name: str
+) -> list[JSON | type[ConstraintMappingType]]:
+ relations = dto_class.get_relations() if dto_class else []
+ values_list = [
+ c.values
+ for c in relations
+ if (isinstance(c, PropertyValueConstraint) and c.property_name == property_name)
+ ]
+ # values should be empty or contain 1 list of allowed values
+ return values_list.pop() if values_list else []
diff --git a/src/OpenApiLibCore/models/request_data.py b/src/OpenApiLibCore/models/request_data.py
index 0482689..74f1bf2 100644
--- a/src/OpenApiLibCore/models/request_data.py
+++ b/src/OpenApiLibCore/models/request_data.py
@@ -24,9 +24,10 @@ class RequestValues:
method: str
params: dict[str, JSON] = field(default_factory=dict)
headers: dict[str, JSON] = field(default_factory=dict)
- json_data: dict[str, JSON] = field(default_factory=dict)
+ json_data: JSON = None
def override_body_value(self, name: str, value: JSON) -> None:
+ # FIXME: update for non-dict bodies
if name in self.json_data:
self.json_data[name] = value
@@ -54,7 +55,8 @@ def remove_parameters(self, parameters: list[str]) -> None:
class RequestData:
"""Helper class to manage parameters used when making requests."""
- dto: ConstraintMappingType
+ valid_data: JSON
+ dto: type[ConstraintMappingType]
body_schema: ResolvedSchemaObjectTypes | None = None
parameters: list[ParameterObject] = field(default_factory=list)
params: dict[str, JSON] = field(default_factory=dict)
@@ -73,7 +75,10 @@ def has_optional_properties(self) -> bool:
def is_required_property(property_name: str) -> bool:
return property_name in self.required_property_names
- properties = (self.dto.as_dict()).keys()
+ if not isinstance(self.valid_data, dict):
+ return False
+
+ properties = (self.valid_data).keys()
return not all(map(is_required_property, properties))
@property
@@ -176,7 +181,10 @@ def get_required_properties_dict(self) -> dict[str, Any]:
required_properties.extend(mandatory_properties)
required_properties_dict: dict[str, Any] = {}
- for key, value in (self.dto.as_dict()).items():
+ if self.valid_data is None:
+ return required_properties_dict
+
+ for key, value in self.valid_data.items():
if key in required_properties:
required_properties_dict[key] = value
return required_properties_dict
@@ -197,7 +205,7 @@ def get_minimal_body_dict(self) -> dict[str, Any]:
optional_properties_dict = {
k: v
- for k, v in self.dto.as_dict().items()
+ for k, v in self.valid_data.items()
if k not in required_properties_dict
}
optional_properties_to_keep = sample(
diff --git a/src/OpenApiLibCore/models/resource_relations.py b/src/OpenApiLibCore/models/resource_relations.py
new file mode 100644
index 0000000..9f38d36
--- /dev/null
+++ b/src/OpenApiLibCore/models/resource_relations.py
@@ -0,0 +1,63 @@
+from abc import ABC
+from dataclasses import dataclass
+from typing import Any
+
+NOT_SET = object()
+
+
+class ResourceRelation(ABC):
+ """ABC for all resource relations or restrictions within the API."""
+
+ property_name: str
+ error_code: int
+
+
+@dataclass
+class PathPropertiesConstraint(ResourceRelation):
+ """The value to be used as the ``path`` for related requests."""
+
+ path: str
+ property_name: str = "id"
+ invalid_value: Any = NOT_SET
+ invalid_value_error_code: int = 422
+ error_code: int = 404
+
+
+@dataclass
+class PropertyValueConstraint(ResourceRelation):
+ """The allowed values for property_name."""
+
+ property_name: str
+ values: list[Any]
+ invalid_value: Any = NOT_SET
+ invalid_value_error_code: int = 422
+ error_code: int = 422
+ treat_as_mandatory: bool = False
+
+
+@dataclass
+class IdDependency(ResourceRelation):
+ """The path where a valid id for the property_name can be gotten (using GET)."""
+
+ property_name: str
+ get_path: str
+ operation_id: str = ""
+ error_code: int = 422
+
+
+@dataclass
+class IdReference(ResourceRelation):
+ """The path where a resource that needs this resource's id can be created (using POST)."""
+
+ property_name: str
+ post_path: str
+ error_code: int = 422
+
+
+@dataclass
+class UniquePropertyValueConstraint(ResourceRelation):
+ """The value of the property must be unique within the resource scope."""
+
+ property_name: str
+ value: Any
+ error_code: int = 422
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index 830434a..9d56de5 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -28,7 +28,6 @@
from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.data_constraints.dto_base import (
Dto,
- IdReference,
get_constraint_mapping_dict,
get_id_property_name,
get_path_mapping_dict,
@@ -36,14 +35,16 @@
from OpenApiLibCore.data_generation.localized_faker import FAKE
from OpenApiLibCore.models.oas_models import (
OpenApiObject,
+ ParameterObject,
PathItemObject,
)
from OpenApiLibCore.models.request_data import RequestData, RequestValues
+from OpenApiLibCore.models.resource_relations import IdReference
from OpenApiLibCore.protocols import ResponseValidatorType
from OpenApiLibCore.utils.oas_cache import PARSER_CACHE, CachedParser
from OpenApiLibCore.utils.parameter_utils import (
get_oas_name_from_safe_name,
- register_path_parameters,
+ get_safe_name_for_oas_name,
)
from openapitools_docs.docstrings import (
OPENAPILIBCORE_INIT_DOCSTRING,
@@ -215,7 +216,7 @@ def get_request_values(
params = request_data.params
headers = request_data.headers
if request_data.has_body:
- json_data = request_data.dto.as_dict()
+ json_data = request_data.valid_data
request_values = RequestValues(
url=url,
@@ -291,7 +292,12 @@ def get_invalidated_parameters(
@keyword
def get_json_data_with_conflict(
- self, url: str, method: str, dto: Dto, conflict_status_code: int
+ self,
+ url: str,
+ method: str,
+ json_data: dict[str, JSON],
+ dto: Dto,
+ conflict_status_code: int,
) -> dict[str, JSON]:
"""
Return `json_data` based on the `UniquePropertyValueConstraint` that must be
@@ -302,6 +308,7 @@ def get_json_data_with_conflict(
url=url,
base_url=self.base_url,
method=method,
+ json_data=json_data,
dto=dto,
conflict_status_code=conflict_status_code,
)
@@ -564,9 +571,25 @@ def _openapi_spec(self) -> OpenApiObject:
parser, _, _ = self._load_specs_and_validator()
spec_model = OpenApiObject.model_validate(parser.specification)
spec_model = self._attach_user_mappings(spec_model=spec_model)
- register_path_parameters(spec_model.paths)
+ self._register_path_parameters(spec_model.paths)
return spec_model
+ def _register_path_parameters(self, paths_data: dict[str, PathItemObject]) -> None:
+ def _register_path_parameter(parameter_object: ParameterObject) -> None:
+ if parameter_object.in_ == "path":
+ _ = get_safe_name_for_oas_name(parameter_object.name)
+
+ for path_item in paths_data.values():
+ if parameters := path_item.parameters:
+ for parameter in path_item.parameters:
+ _register_path_parameter(parameter_object=parameter)
+
+ operations = path_item.get_operations()
+ for operation in operations.values():
+ if parameters := operation.parameters:
+ for parameter in parameters:
+ _register_path_parameter(parameter_object=parameter)
+
def _attach_user_mappings(self, spec_model: OpenApiObject) -> OpenApiObject:
for (
path,
diff --git a/src/OpenApiLibCore/protocols.py b/src/OpenApiLibCore/protocols.py
index abdb96f..cdbc806 100644
--- a/src/OpenApiLibCore/protocols.py
+++ b/src/OpenApiLibCore/protocols.py
@@ -11,7 +11,10 @@
from pydantic import GetCoreSchemaHandler
from pydantic_core import CoreSchema, core_schema
-from OpenApiLibCore.data_constraints import dto_base
+from OpenApiLibCore.models.resource_relations import (
+ PathPropertiesConstraint,
+ ResourceRelation,
+)
class ResponseValidatorType(Protocol):
@@ -40,33 +43,25 @@ def __get_pydantic_core_schema__(
return core_schema.no_info_after_validator_function(cls, handler(str))
@staticmethod
- def get_path_relations() -> list[dto_base.PathPropertiesConstraint]: ...
+ def get_path_relations() -> list[PathPropertiesConstraint]: ...
+ @classmethod
def get_path_relations_for_error_code(
- self, error_code: int
- ) -> list[dto_base.PathPropertiesConstraint]: ...
+ cls, error_code: int
+ ) -> list[PathPropertiesConstraint]: ...
@staticmethod
- def get_parameter_relations() -> list[dto_base.ResourceRelation]: ...
+ def get_parameter_relations() -> list[ResourceRelation]: ...
+ @classmethod
def get_parameter_relations_for_error_code(
- self, error_code: int
- ) -> list[dto_base.ResourceRelation]: ...
+ cls, error_code: int
+ ) -> list[ResourceRelation]: ...
@staticmethod
- def get_relations() -> list[dto_base.ResourceRelation]: ...
+ def get_relations() -> list[ResourceRelation]: ...
+ @classmethod
def get_body_relations_for_error_code(
- self, error_code: int
- ) -> list[dto_base.ResourceRelation]: ...
-
- def get_invalidated_data(
- self,
- schema: Any,
- status_code: int,
- invalid_property_default_code: int,
- ) -> dict[str, Any]: ...
-
- def as_dict(self) -> dict[Any, Any]: ...
-
- def as_list(self) -> list[Any]: ...
+ cls, error_code: int
+ ) -> list[ResourceRelation]: ...
diff --git a/src/OpenApiLibCore/utils/parameter_utils.py b/src/OpenApiLibCore/utils/parameter_utils.py
index ea702af..a8d3450 100644
--- a/src/OpenApiLibCore/utils/parameter_utils.py
+++ b/src/OpenApiLibCore/utils/parameter_utils.py
@@ -5,8 +5,6 @@
from typing import Generator
-from OpenApiLibCore.models.oas_models import ParameterObject, PathItemObject
-
PARAMETER_REGISTRY: dict[str, str] = {
"body": "body",
}
@@ -78,20 +76,3 @@ def _convert_string_to_python_identifier() -> Generator[str, None, None]:
if not _is_python_safe(converted_string):
raise ValueError(f"Failed to convert '{string}' to Python identifier.")
return converted_string
-
-
-def register_path_parameters(paths_data: dict[str, PathItemObject]) -> None:
- def _register_path_parameter(parameter_object: ParameterObject) -> None:
- if parameter_object.in_ == "path":
- _ = get_safe_name_for_oas_name(parameter_object.name)
-
- for path_item in paths_data.values():
- if parameters := path_item.parameters:
- for parameter in path_item.parameters:
- _register_path_parameter(parameter_object=parameter)
-
- operations = path_item.get_operations()
- for operation in operations.values():
- if parameters := operation.parameters:
- for parameter in parameters:
- _register_path_parameter(parameter_object=parameter)
diff --git a/tests/libcore/suites/test_get_invalid_json_data.robot b/tests/libcore/suites/test_get_invalid_json_data.robot
index a93950e..be0ac0b 100644
--- a/tests/libcore/suites/test_get_invalid_json_data.robot
+++ b/tests/libcore/suites/test_get_invalid_json_data.robot
@@ -104,7 +104,7 @@ Test Get Invalid Body Data Can Invalidate Missing Optional Parameters
... method=patch
... status_code=422
... request_data=${request_data}
- Should Not Be Equal ${invalid_json} ${request_data.dto.as_dict()}
+ Should Not Be Equal ${invalid_json} ${request_data.valid_data}
${response}= Authorized Request
... url=${url} method=patch json_data=${invalid_json}
VAR @{expected_status_codes}= ${403} ${422} ${451}
diff --git a/tests/libcore/suites/test_get_json_data_with_conflict.robot b/tests/libcore/suites/test_get_json_data_with_conflict.robot
index 2784fad..54e20a0 100644
--- a/tests/libcore/suites/test_get_json_data_with_conflict.robot
+++ b/tests/libcore/suites/test_get_json_data_with_conflict.robot
@@ -20,6 +20,7 @@ Test Get Json Data With Conflict Raises For No UniquePropertyValueConstraint
... Get Json Data With Conflict
... url=${url}
... method=post
+ ... json_data=&{EMPTY}
... dto=${request_data.dto}
... conflict_status_code=418
@@ -29,6 +30,7 @@ Test Get Json Data With Conflict For Post Request
${invalid_data}= Get Json Data With Conflict
... url=${url}
... method=post
+ ... json_data=${request_data.valid_data}
... dto=${request_data.dto}
... conflict_status_code=418
Should Not Be Empty ${invalid_data}
@@ -39,6 +41,7 @@ Test Get Json Data With Conflict For Put Request
${invalid_json}= Get Json Data With Conflict
... url=${url}
... method=put
+ ... json_data=${request_data.valid_data}
... dto=${request_data.dto}
... conflict_status_code=418
${response}= Authorized Request
diff --git a/tests/libcore/suites/test_get_request_data.robot b/tests/libcore/suites/test_get_request_data.robot
index befc5c4..966797b 100644
--- a/tests/libcore/suites/test_get_request_data.robot
+++ b/tests/libcore/suites/test_get_request_data.robot
@@ -56,15 +56,17 @@ Test Get Request Data For Endpoint With Object RequestBody
Test Get Request Data For Endpoint With Array Request Body
VAR ${empty_array_seen}= ${FALSE}
VAR ${non_empty_array_seen}= ${FALSE}
- WHILE not ($empty_array_seen and $non_empty_array_seen)
+ WHILE not ($empty_array_seen and $non_empty_array_seen) limit=10
${request_data}= Get Request Data path=/events/ method=put
TRY
- Dictionary Should Contain Key ${request_data.dto.message} message
- List Should Not Contain Duplicates ${request_data.dto.details}
+ VAR ${first_valid_item}= ${request_data.valid_data[0]}
+ Dictionary Should Contain Key ${first_valid_item} message
+ Dictionary Should Contain Key ${first_valid_item} details
+ List Should Not Contain Duplicates ${first_valid_item}[details]
VAR ${non_empty_array_seen}= ${TRUE}
- EXCEPT * AttributeError: 'put_events_events__put' object has no attribute 'message' type=glob
+ EXCEPT * IndexError: list index out of range type=glob
Should Be Equal ${request_data.body_schema.type} array
- Should Be Equal As Strings ${request_data.dto} put_events_events__put()
+ Should Be Equal As Strings ${request_data.dto}
VAR ${empty_array_seen}= ${TRUE}
END
END
@@ -72,7 +74,9 @@ Test Get Request Data For Endpoint With Array Request Body
Test Get Request Data For Endpoint Without RequestBody But With DtoClass
${request_data}= Get Request Data path=/wagegroups/{wagegroup_id} method=delete
VAR &{dict}= &{EMPTY}
- Should Be Equal As Strings ${request_data.dto} delete_wagegroup_wagegroups__wagegroup_id__delete()
+ Should Be Equal As Strings
+ ... ${request_data.dto}
+ ...
Should Be Equal ${request_data.body_schema} ${NONE}
Should Not Be Empty ${request_data.parameters}
Should Be Equal ${request_data.params} ${dict}
diff --git a/tests/libcore/suites/test_readonly.robot b/tests/libcore/suites/test_readonly.robot
index 5f27bad..0a5cbc5 100644
--- a/tests/libcore/suites/test_readonly.robot
+++ b/tests/libcore/suites/test_readonly.robot
@@ -16,7 +16,7 @@ ${ORIGIN} http://localhost:8000
*** Test Cases ***
Test ReadOnly Is Filtered From Request Data
${request_data}= Get Request Data path=/api/location method=post
- VAR ${json_data}= ${request_data.dto.as_dict()}
+ VAR ${json_data}= ${request_data.valid_data}
Should Not Contain ${json_data} id
Should Contain ${json_data} locationId
Should Contain ${json_data} timezone
diff --git a/tests/libcore/suites/test_request_data_class.robot b/tests/libcore/suites/test_request_data_class.robot
index 91532ea..a19a0a8 100644
--- a/tests/libcore/suites/test_request_data_class.robot
+++ b/tests/libcore/suites/test_request_data_class.robot
@@ -59,7 +59,7 @@ Test Headers That Can Be Invalidated
Test Get Required Properties Dict
${request_data}= Get Request Data path=/employees method=post
- Should Contain ${request_data.dto.as_dict()} parttime_schedule
+ Should Contain ${request_data.valid_data} parttime_schedule
Should Not Be Empty ${request_data.dto.name}
VAR ${required_properties}= ${request_data.get_required_properties_dict()}
Should Contain ${required_properties} name
diff --git a/tests/libcore/suites/test_validate_response.robot b/tests/libcore/suites/test_validate_response.robot
index c74b787..9cad393 100644
--- a/tests/libcore/suites/test_validate_response.robot
+++ b/tests/libcore/suites/test_validate_response.robot
@@ -20,6 +20,6 @@ Test Bool Response
... method=patch
... params=${request_data.params}
... headers=${request_data.headers}
- ... json_data=${request_data.dto.as_dict()}
+ ... json_data=${request_data.valid_data}
Validate Response path=/employees/{employee_id} response=${response}
diff --git a/tests/libcore/unittests/test_get_valid_value.py b/tests/libcore/unittests/test_get_valid_value.py
index ac272e8..5c5446e 100644
--- a/tests/libcore/unittests/test_get_valid_value.py
+++ b/tests/libcore/unittests/test_get_valid_value.py
@@ -38,12 +38,13 @@ def test_array_schema(self) -> None:
schema = ArraySchema(items=IntegerSchema())
value = schema.get_valid_value()
self.assertIsInstance(value, list)
- self.assertIsInstance(value[0], int)
+ if value:
+ self.assertIsInstance(value[0], int)
def test_object_schema(self) -> None:
schema = ObjectSchema()
- with self.assertRaises(NotImplementedError):
- schema.get_valid_value()
+ value = schema.get_valid_value()
+ self.assertIsInstance(value, dict)
def test_union_schema(self) -> None:
schema = UnionTypeSchema(oneOf=[BooleanSchema(), IntegerSchema()])
@@ -79,8 +80,7 @@ def test_array_schema(self) -> None:
def test_object_schema(self) -> None:
const = {"foo": 42, "bar": 3.14}
schema = ObjectSchema(const=const)
- with self.assertRaises(NotImplementedError):
- schema.get_valid_value()
+ self.assertEqual(schema.get_valid_value(), const)
class TestGetValidValueFromEnum(unittest.TestCase):
@@ -107,8 +107,8 @@ def test_array_schema(self) -> None:
def test_object_schema(self) -> None:
enum: list[dict[str, int | float]] = [{"foo": 42, "bar": 3.14}]
schema = ObjectSchema(enum=enum)
- with self.assertRaises(NotImplementedError):
- schema.get_valid_value()
+ value = schema.get_valid_value()
+ self.assertIn(value, enum)
class TestStringSchemaVariations(unittest.TestCase):
@@ -158,7 +158,7 @@ class TestArraySchemaVariations(unittest.TestCase):
def test_default_min_max(self) -> None:
schema = ArraySchema(items=StringSchema())
value = schema.get_valid_value()
- self.assertEqual(len(value), 1)
+ self.assertIn(len(value), (0, 1))
schema = {"maxItems": 0, "items": {"type": "string"}}
schema = ArraySchema(items=StringSchema(), maxItems=0)
@@ -166,9 +166,9 @@ def test_default_min_max(self) -> None:
self.assertEqual(value, [])
def test_min_max(self) -> None:
- schema = ArraySchema(items=StringSchema(), maxItems=3)
+ schema = ArraySchema(items=StringSchema(), maxItems=3, minItems=2)
value = schema.get_valid_value()
- self.assertEqual(len(value), 3)
+ self.assertIn(len(value), (2, 3))
schema = ArraySchema(items=StringSchema(), minItems=5)
value = schema.get_valid_value()
From 4624527fe9df081151a4e2982680f012e786b605 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Tue, 7 Oct 2025 08:04:18 +0000
Subject: [PATCH 10/55] Improved invalidation logic for array bodies
---
src/OpenApiLibCore/models/oas_models.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index 17bfa5d..3ab4fa9 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -571,9 +571,13 @@ def get_invalidated_data(
invalid_values.append(invalid_const_or_enum)
except ValueError:
pass
- if valid_data and isinstance(self.items, ObjectSchema):
+ if isinstance(self.items, ObjectSchema):
data_to_invalidate = deepcopy(valid_data)
- valid_item = data_to_invalidate.pop()
+ valid_item = (
+ data_to_invalidate.pop()
+ if valid_data
+ else self.items.get_valid_value()
+ )
invalid_item = self.items.get_invalidated_data(
valid_data=valid_item,
constraint_mapping=constraint_mapping,
From 3eff093fa5392884991ad0d93e39ddfba35a93a6 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Mon, 20 Oct 2025 15:10:04 +0000
Subject: [PATCH 11/55] Add Python 3.14 to ci
---
.github/workflows/on-push.yml | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/on-push.yml b/.github/workflows/on-push.yml
index de78dff..69299ae 100644
--- a/.github/workflows/on-push.yml
+++ b/.github/workflows/on-push.yml
@@ -53,7 +53,7 @@ jobs:
strategy:
matrix:
os: [ 'ubuntu-latest', 'windows-latest']
- python-version: ['3.10', '3.11', '3.12', '3.13']
+ python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
robot-version: ['6.1.1', '7.3.2']
exclude:
- os: 'windows-latest'
@@ -62,6 +62,8 @@ jobs:
python-version: '3.11'
- os: 'windows-latest'
python-version: '3.12'
+ - os: 'windows-latest'
+ python-version: '3.13'
- os: 'ubuntu-latest'
python-version: '3.10'
robot-version: '6.1.1'
@@ -71,6 +73,9 @@ jobs:
- os: 'ubuntu-latest'
python-version: '3.12'
robot-version: '6.1.1'
+ - os: 'ubuntu-latest'
+ python-version: '3.13'
+ robot-version: '6.1.1'
fail-fast: false
steps:
- uses: actions/checkout@v5
From 2a078470d5205585cf3efe1c283bfd4213dac323 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Mon, 20 Oct 2025 15:12:19 +0000
Subject: [PATCH 12/55] minimize devcontainer extensions
---
.devcontainer/devcontainer.json | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 2b807ad..44a4d72 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -20,9 +20,8 @@
"charliermarsh.ruff",
"d-biehl.robotcode",
"tamasfe.even-better-toml",
- "ms-azuretools.vscode-docker",
- "Gruntfuggly.todo-tree",
- "shardulm94.trailing-spaces"
+ "ms-azuretools.vscode-docker"
+
]
}
}
From 9874ba98755645bc9c791ef08f5802f86af3e234 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Mon, 20 Oct 2025 15:14:22 +0000
Subject: [PATCH 13/55] version bump and dependency update
---
poetry.lock | 1276 ++++++++++++++++++++++++++----------------------
pyproject.toml | 5 +-
2 files changed, 689 insertions(+), 592 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index d730550..48308e2 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
+# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
[[package]]
name = "annotated-types"
@@ -14,14 +14,14 @@ files = [
[[package]]
name = "anyio"
-version = "4.10.0"
+version = "4.11.0"
description = "High-level concurrency and networking framework on top of asyncio or Trio"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
- {file = "anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1"},
- {file = "anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6"},
+ {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"},
+ {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"},
]
[package.dependencies]
@@ -31,18 +31,18 @@ sniffio = ">=1.1"
typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""}
[package.extras]
-trio = ["trio (>=0.26.1)"]
+trio = ["trio (>=0.31.0)"]
[[package]]
name = "astroid"
-version = "3.3.11"
+version = "4.0.1"
description = "An abstract syntax tree for Python with inference support."
optional = false
-python-versions = ">=3.9.0"
+python-versions = ">=3.10.0"
groups = ["lint-and-format"]
files = [
- {file = "astroid-3.3.11-py3-none-any.whl", hash = "sha256:54c760ae8322ece1abd213057c4b5bba7c49818853fc901ef09719a60dbf9dec"},
- {file = "astroid-3.3.11.tar.gz", hash = "sha256:1e5a5011af2920c7c67a53f65d536d65bfa7116feeaf2354d8b94f29573bb0ce"},
+ {file = "astroid-4.0.1-py3-none-any.whl", hash = "sha256:37ab2f107d14dc173412327febf6c78d39590fdafcb44868f03b6c03452e3db0"},
+ {file = "astroid-4.0.1.tar.gz", hash = "sha256:0d778ec0def05b935e198412e62f9bcca8b3b5c39fdbe50b0ba074005e477aab"},
]
[package.dependencies]
@@ -50,54 +50,46 @@ typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""}
[[package]]
name = "attrs"
-version = "25.3.0"
+version = "25.4.0"
description = "Classes Without Boilerplate"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"},
- {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"},
+ {file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"},
+ {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"},
]
-[package.extras]
-benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
-cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
-dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
-docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"]
-tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
-tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""]
-
[[package]]
name = "black"
-version = "25.1.0"
+version = "25.9.0"
description = "The uncompromising code formatter."
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"},
- {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"},
- {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"},
- {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"},
- {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"},
- {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"},
- {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"},
- {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"},
- {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"},
- {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"},
- {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"},
- {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"},
- {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"},
- {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"},
- {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"},
- {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"},
- {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"},
- {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"},
- {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"},
- {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"},
- {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"},
- {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"},
+ {file = "black-25.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce41ed2614b706fd55fd0b4a6909d06b5bab344ffbfadc6ef34ae50adba3d4f7"},
+ {file = "black-25.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ab0ce111ef026790e9b13bd216fa7bc48edd934ffc4cbf78808b235793cbc92"},
+ {file = "black-25.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f96b6726d690c96c60ba682955199f8c39abc1ae0c3a494a9c62c0184049a713"},
+ {file = "black-25.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:d119957b37cc641596063cd7db2656c5be3752ac17877017b2ffcdb9dfc4d2b1"},
+ {file = "black-25.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:456386fe87bad41b806d53c062e2974615825c7a52159cde7ccaeb0695fa28fa"},
+ {file = "black-25.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a16b14a44c1af60a210d8da28e108e13e75a284bf21a9afa6b4571f96ab8bb9d"},
+ {file = "black-25.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aaf319612536d502fdd0e88ce52d8f1352b2c0a955cc2798f79eeca9d3af0608"},
+ {file = "black-25.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:c0372a93e16b3954208417bfe448e09b0de5cc721d521866cd9e0acac3c04a1f"},
+ {file = "black-25.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1b9dc70c21ef8b43248f1d86aedd2aaf75ae110b958a7909ad8463c4aa0880b0"},
+ {file = "black-25.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8e46eecf65a095fa62e53245ae2795c90bdecabd53b50c448d0a8bcd0d2e74c4"},
+ {file = "black-25.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9101ee58ddc2442199a25cb648d46ba22cd580b00ca4b44234a324e3ec7a0f7e"},
+ {file = "black-25.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:77e7060a00c5ec4b3367c55f39cf9b06e68965a4f2e61cecacd6d0d9b7ec945a"},
+ {file = "black-25.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0172a012f725b792c358d57fe7b6b6e8e67375dd157f64fa7a3097b3ed3e2175"},
+ {file = "black-25.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3bec74ee60f8dfef564b573a96b8930f7b6a538e846123d5ad77ba14a8d7a64f"},
+ {file = "black-25.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b756fc75871cb1bcac5499552d771822fd9db5a2bb8db2a7247936ca48f39831"},
+ {file = "black-25.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:846d58e3ce7879ec1ffe816bb9df6d006cd9590515ed5d17db14e17666b2b357"},
+ {file = "black-25.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ef69351df3c84485a8beb6f7b8f9721e2009e20ef80a8d619e2d1788b7816d47"},
+ {file = "black-25.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e3c1f4cd5e93842774d9ee4ef6cd8d17790e65f44f7cdbaab5f2cf8ccf22a823"},
+ {file = "black-25.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:154b06d618233fe468236ba1f0e40823d4eb08b26f5e9261526fde34916b9140"},
+ {file = "black-25.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:e593466de7b998374ea2585a471ba90553283fb9beefcfa430d84a2651ed5933"},
+ {file = "black-25.9.0-py3-none-any.whl", hash = "sha256:474b34c1342cdc157d307b56c4c65bce916480c4a8f6551fdc6bf9b486a7c4ae"},
+ {file = "black-25.9.0.tar.gz", hash = "sha256:0474bca9a0dd1b51791fcc507a4e02078a1c63f6d4e4ae5544b9848c7adfb619"},
]
[package.dependencies]
@@ -106,6 +98,7 @@ mypy-extensions = ">=0.4.3"
packaging = ">=22.0"
pathspec = ">=0.9.0"
platformdirs = ">=2"
+pytokens = ">=0.1.10"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
@@ -117,14 +110,14 @@ uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "certifi"
-version = "2025.8.3"
+version = "2025.10.5"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.7"
groups = ["main", "dev"]
files = [
- {file = "certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"},
- {file = "certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407"},
+ {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"},
+ {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"},
]
[[package]]
@@ -230,14 +223,14 @@ files = [
[[package]]
name = "click"
-version = "8.2.1"
+version = "8.3.0"
description = "Composable command line interface toolkit"
optional = false
python-versions = ">=3.10"
groups = ["main", "dev", "lint-and-format", "type-checking"]
files = [
- {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"},
- {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"},
+ {file = "click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc"},
+ {file = "click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4"},
]
[package.dependencies]
@@ -258,100 +251,116 @@ markers = {main = "platform_system == \"Windows\"", lint-and-format = "platform_
[[package]]
name = "coverage"
-version = "7.10.6"
+version = "7.10.7"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
- {file = "coverage-7.10.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70e7bfbd57126b5554aa482691145f798d7df77489a177a6bef80de78860a356"},
- {file = "coverage-7.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e41be6f0f19da64af13403e52f2dec38bbc2937af54df8ecef10850ff8d35301"},
- {file = "coverage-7.10.6-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c61fc91ab80b23f5fddbee342d19662f3d3328173229caded831aa0bd7595460"},
- {file = "coverage-7.10.6-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10356fdd33a7cc06e8051413140bbdc6f972137508a3572e3f59f805cd2832fd"},
- {file = "coverage-7.10.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80b1695cf7c5ebe7b44bf2521221b9bb8cdf69b1f24231149a7e3eb1ae5fa2fb"},
- {file = "coverage-7.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2e4c33e6378b9d52d3454bd08847a8651f4ed23ddbb4a0520227bd346382bbc6"},
- {file = "coverage-7.10.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c8a3ec16e34ef980a46f60dc6ad86ec60f763c3f2fa0db6d261e6e754f72e945"},
- {file = "coverage-7.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7d79dabc0a56f5af990cc6da9ad1e40766e82773c075f09cc571e2076fef882e"},
- {file = "coverage-7.10.6-cp310-cp310-win32.whl", hash = "sha256:86b9b59f2b16e981906e9d6383eb6446d5b46c278460ae2c36487667717eccf1"},
- {file = "coverage-7.10.6-cp310-cp310-win_amd64.whl", hash = "sha256:e132b9152749bd33534e5bd8565c7576f135f157b4029b975e15ee184325f528"},
- {file = "coverage-7.10.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c706db3cabb7ceef779de68270150665e710b46d56372455cd741184f3868d8f"},
- {file = "coverage-7.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e0c38dc289e0508ef68ec95834cb5d2e96fdbe792eaccaa1bccac3966bbadcc"},
- {file = "coverage-7.10.6-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:752a3005a1ded28f2f3a6e8787e24f28d6abe176ca64677bcd8d53d6fe2ec08a"},
- {file = "coverage-7.10.6-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:689920ecfd60f992cafca4f5477d55720466ad2c7fa29bb56ac8d44a1ac2b47a"},
- {file = "coverage-7.10.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec98435796d2624d6905820a42f82149ee9fc4f2d45c2c5bc5a44481cc50db62"},
- {file = "coverage-7.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b37201ce4a458c7a758ecc4efa92fa8ed783c66e0fa3c42ae19fc454a0792153"},
- {file = "coverage-7.10.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2904271c80898663c810a6b067920a61dd8d38341244a3605bd31ab55250dad5"},
- {file = "coverage-7.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5aea98383463d6e1fa4e95416d8de66f2d0cb588774ee20ae1b28df826bcb619"},
- {file = "coverage-7.10.6-cp311-cp311-win32.whl", hash = "sha256:e3fb1fa01d3598002777dd259c0c2e6d9d5e10e7222976fc8e03992f972a2cba"},
- {file = "coverage-7.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:f35ed9d945bece26553d5b4c8630453169672bea0050a564456eb88bdffd927e"},
- {file = "coverage-7.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:99e1a305c7765631d74b98bf7dbf54eeea931f975e80f115437d23848ee8c27c"},
- {file = "coverage-7.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5b2dd6059938063a2c9fee1af729d4f2af28fd1a545e9b7652861f0d752ebcea"},
- {file = "coverage-7.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:388d80e56191bf846c485c14ae2bc8898aa3124d9d35903fef7d907780477634"},
- {file = "coverage-7.10.6-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:90cb5b1a4670662719591aa92d0095bb41714970c0b065b02a2610172dbf0af6"},
- {file = "coverage-7.10.6-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:961834e2f2b863a0e14260a9a273aff07ff7818ab6e66d2addf5628590c628f9"},
- {file = "coverage-7.10.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf9a19f5012dab774628491659646335b1928cfc931bf8d97b0d5918dd58033c"},
- {file = "coverage-7.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99c4283e2a0e147b9c9cc6bc9c96124de9419d6044837e9799763a0e29a7321a"},
- {file = "coverage-7.10.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:282b1b20f45df57cc508c1e033403f02283adfb67d4c9c35a90281d81e5c52c5"},
- {file = "coverage-7.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cdbe264f11afd69841bd8c0d83ca10b5b32853263ee62e6ac6a0ab63895f972"},
- {file = "coverage-7.10.6-cp312-cp312-win32.whl", hash = "sha256:a517feaf3a0a3eca1ee985d8373135cfdedfbba3882a5eab4362bda7c7cf518d"},
- {file = "coverage-7.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:856986eadf41f52b214176d894a7de05331117f6035a28ac0016c0f63d887629"},
- {file = "coverage-7.10.6-cp312-cp312-win_arm64.whl", hash = "sha256:acf36b8268785aad739443fa2780c16260ee3fa09d12b3a70f772ef100939d80"},
- {file = "coverage-7.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6"},
- {file = "coverage-7.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80"},
- {file = "coverage-7.10.6-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003"},
- {file = "coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27"},
- {file = "coverage-7.10.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4"},
- {file = "coverage-7.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d"},
- {file = "coverage-7.10.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc"},
- {file = "coverage-7.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc"},
- {file = "coverage-7.10.6-cp313-cp313-win32.whl", hash = "sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e"},
- {file = "coverage-7.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32"},
- {file = "coverage-7.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2"},
- {file = "coverage-7.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b"},
- {file = "coverage-7.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393"},
- {file = "coverage-7.10.6-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27"},
- {file = "coverage-7.10.6-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df"},
- {file = "coverage-7.10.6-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb"},
- {file = "coverage-7.10.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282"},
- {file = "coverage-7.10.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4"},
- {file = "coverage-7.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21"},
- {file = "coverage-7.10.6-cp313-cp313t-win32.whl", hash = "sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0"},
- {file = "coverage-7.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5"},
- {file = "coverage-7.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b"},
- {file = "coverage-7.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6008a021907be8c4c02f37cdc3ffb258493bdebfeaf9a839f9e71dfdc47b018e"},
- {file = "coverage-7.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5e75e37f23eb144e78940b40395b42f2321951206a4f50e23cfd6e8a198d3ceb"},
- {file = "coverage-7.10.6-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0f7cb359a448e043c576f0da00aa8bfd796a01b06aa610ca453d4dde09cc1034"},
- {file = "coverage-7.10.6-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c68018e4fc4e14b5668f1353b41ccf4bc83ba355f0e1b3836861c6f042d89ac1"},
- {file = "coverage-7.10.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd4b2b0707fc55afa160cd5fc33b27ccbf75ca11d81f4ec9863d5793fc6df56a"},
- {file = "coverage-7.10.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cec13817a651f8804a86e4f79d815b3b28472c910e099e4d5a0e8a3b6a1d4cb"},
- {file = "coverage-7.10.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f2a6a8e06bbda06f78739f40bfb56c45d14eb8249d0f0ea6d4b3d48e1f7c695d"},
- {file = "coverage-7.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:081b98395ced0d9bcf60ada7661a0b75f36b78b9d7e39ea0790bb4ed8da14747"},
- {file = "coverage-7.10.6-cp314-cp314-win32.whl", hash = "sha256:6937347c5d7d069ee776b2bf4e1212f912a9f1f141a429c475e6089462fcecc5"},
- {file = "coverage-7.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:adec1d980fa07e60b6ef865f9e5410ba760e4e1d26f60f7e5772c73b9a5b0713"},
- {file = "coverage-7.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:a80f7aef9535442bdcf562e5a0d5a5538ce8abe6bb209cfbf170c462ac2c2a32"},
- {file = "coverage-7.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0de434f4fbbe5af4fa7989521c655c8c779afb61c53ab561b64dcee6149e4c65"},
- {file = "coverage-7.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6e31b8155150c57e5ac43ccd289d079eb3f825187d7c66e755a055d2c85794c6"},
- {file = "coverage-7.10.6-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:98cede73eb83c31e2118ae8d379c12e3e42736903a8afcca92a7218e1f2903b0"},
- {file = "coverage-7.10.6-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f863c08f4ff6b64fa8045b1e3da480f5374779ef187f07b82e0538c68cb4ff8e"},
- {file = "coverage-7.10.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b38261034fda87be356f2c3f42221fdb4171c3ce7658066ae449241485390d5"},
- {file = "coverage-7.10.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e93b1476b79eae849dc3872faeb0bf7948fd9ea34869590bc16a2a00b9c82a7"},
- {file = "coverage-7.10.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ff8a991f70f4c0cf53088abf1e3886edcc87d53004c7bb94e78650b4d3dac3b5"},
- {file = "coverage-7.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ac765b026c9f33044419cbba1da913cfb82cca1b60598ac1c7a5ed6aac4621a0"},
- {file = "coverage-7.10.6-cp314-cp314t-win32.whl", hash = "sha256:441c357d55f4936875636ef2cfb3bee36e466dcf50df9afbd398ce79dba1ebb7"},
- {file = "coverage-7.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:073711de3181b2e204e4870ac83a7c4853115b42e9cd4d145f2231e12d670930"},
- {file = "coverage-7.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:137921f2bac5559334ba66122b753db6dc5d1cf01eb7b64eb412bb0d064ef35b"},
- {file = "coverage-7.10.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90558c35af64971d65fbd935c32010f9a2f52776103a259f1dee865fe8259352"},
- {file = "coverage-7.10.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8953746d371e5695405806c46d705a3cd170b9cc2b9f93953ad838f6c1e58612"},
- {file = "coverage-7.10.6-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c83f6afb480eae0313114297d29d7c295670a41c11b274e6bca0c64540c1ce7b"},
- {file = "coverage-7.10.6-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7eb68d356ba0cc158ca535ce1381dbf2037fa8cb5b1ae5ddfc302e7317d04144"},
- {file = "coverage-7.10.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b15a87265e96307482746d86995f4bff282f14b027db75469c446da6127433b"},
- {file = "coverage-7.10.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fc53ba868875bfbb66ee447d64d6413c2db91fddcfca57025a0e7ab5b07d5862"},
- {file = "coverage-7.10.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:efeda443000aa23f276f4df973cb82beca682fd800bb119d19e80504ffe53ec2"},
- {file = "coverage-7.10.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9702b59d582ff1e184945d8b501ffdd08d2cee38d93a2206aa5f1365ce0b8d78"},
- {file = "coverage-7.10.6-cp39-cp39-win32.whl", hash = "sha256:2195f8e16ba1a44651ca684db2ea2b2d4b5345da12f07d9c22a395202a05b23c"},
- {file = "coverage-7.10.6-cp39-cp39-win_amd64.whl", hash = "sha256:f32ff80e7ef6a5b5b606ea69a36e97b219cd9dc799bcf2963018a4d8f788cfbf"},
- {file = "coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3"},
- {file = "coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90"},
+ {file = "coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a"},
+ {file = "coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5"},
+ {file = "coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17"},
+ {file = "coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b"},
+ {file = "coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87"},
+ {file = "coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e"},
+ {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e"},
+ {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df"},
+ {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0"},
+ {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13"},
+ {file = "coverage-7.10.7-cp310-cp310-win32.whl", hash = "sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b"},
+ {file = "coverage-7.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807"},
+ {file = "coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59"},
+ {file = "coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a"},
+ {file = "coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699"},
+ {file = "coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d"},
+ {file = "coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e"},
+ {file = "coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23"},
+ {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab"},
+ {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82"},
+ {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2"},
+ {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61"},
+ {file = "coverage-7.10.7-cp311-cp311-win32.whl", hash = "sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14"},
+ {file = "coverage-7.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2"},
+ {file = "coverage-7.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a"},
+ {file = "coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417"},
+ {file = "coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973"},
+ {file = "coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c"},
+ {file = "coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7"},
+ {file = "coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6"},
+ {file = "coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59"},
+ {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b"},
+ {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a"},
+ {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb"},
+ {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1"},
+ {file = "coverage-7.10.7-cp312-cp312-win32.whl", hash = "sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256"},
+ {file = "coverage-7.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba"},
+ {file = "coverage-7.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf"},
+ {file = "coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d"},
+ {file = "coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b"},
+ {file = "coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e"},
+ {file = "coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b"},
+ {file = "coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49"},
+ {file = "coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911"},
+ {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0"},
+ {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f"},
+ {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c"},
+ {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f"},
+ {file = "coverage-7.10.7-cp313-cp313-win32.whl", hash = "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698"},
+ {file = "coverage-7.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843"},
+ {file = "coverage-7.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546"},
+ {file = "coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c"},
+ {file = "coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15"},
+ {file = "coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4"},
+ {file = "coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0"},
+ {file = "coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0"},
+ {file = "coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65"},
+ {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541"},
+ {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6"},
+ {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999"},
+ {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2"},
+ {file = "coverage-7.10.7-cp313-cp313t-win32.whl", hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a"},
+ {file = "coverage-7.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb"},
+ {file = "coverage-7.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb"},
+ {file = "coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520"},
+ {file = "coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32"},
+ {file = "coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f"},
+ {file = "coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a"},
+ {file = "coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360"},
+ {file = "coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69"},
+ {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14"},
+ {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe"},
+ {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e"},
+ {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd"},
+ {file = "coverage-7.10.7-cp314-cp314-win32.whl", hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2"},
+ {file = "coverage-7.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681"},
+ {file = "coverage-7.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880"},
+ {file = "coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63"},
+ {file = "coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2"},
+ {file = "coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d"},
+ {file = "coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0"},
+ {file = "coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699"},
+ {file = "coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9"},
+ {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f"},
+ {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1"},
+ {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0"},
+ {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399"},
+ {file = "coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235"},
+ {file = "coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d"},
+ {file = "coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a"},
+ {file = "coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3"},
+ {file = "coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c"},
+ {file = "coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396"},
+ {file = "coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40"},
+ {file = "coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594"},
+ {file = "coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a"},
+ {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b"},
+ {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3"},
+ {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0"},
+ {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f"},
+ {file = "coverage-7.10.7-cp39-cp39-win32.whl", hash = "sha256:5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431"},
+ {file = "coverage-7.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07"},
+ {file = "coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260"},
+ {file = "coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239"},
]
[package.dependencies]
@@ -390,14 +399,14 @@ profile = ["gprof2dot (>=2022.7.29)"]
[[package]]
name = "docutils"
-version = "0.22"
+version = "0.22.2"
description = "Docutils -- Python Documentation Utilities"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "docutils-0.22-py3-none-any.whl", hash = "sha256:4ed966a0e96a0477d852f7af31bdcb3adc049fbb35ccba358c2ea8a03287615e"},
- {file = "docutils-0.22.tar.gz", hash = "sha256:ba9d57750e92331ebe7c08a1bbf7a7f8143b86c476acd51528b042216a6aad0f"},
+ {file = "docutils-0.22.2-py3-none-any.whl", hash = "sha256:b0e98d679283fc3bb0ead8a5da7f501baa632654e7056e9c5846842213d674d8"},
+ {file = "docutils-0.22.2.tar.gz", hash = "sha256:9fdb771707c8784c8f2728b67cb2c691305933d68137ef95a75db5f4dfbc213d"},
]
[[package]]
@@ -407,7 +416,7 @@ description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
-markers = "python_version < \"3.11\""
+markers = "python_version == \"3.10\""
files = [
{file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"},
{file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"},
@@ -421,14 +430,14 @@ test = ["pytest (>=6)"]
[[package]]
name = "faker"
-version = "37.6.0"
+version = "37.11.0"
description = "Faker is a Python package that generates fake data for you."
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "faker-37.6.0-py3-none-any.whl", hash = "sha256:3c5209b23d7049d596a51db5d76403a0ccfea6fc294ffa2ecfef6a8843b1e6a7"},
- {file = "faker-37.6.0.tar.gz", hash = "sha256:0f8cc34f30095184adf87c3c24c45b38b33ad81c35ef6eb0a3118f301143012c"},
+ {file = "faker-37.11.0-py3-none-any.whl", hash = "sha256:1508d2da94dfd1e0087b36f386126d84f8583b3de19ac18e392a2831a6676c57"},
+ {file = "faker-37.11.0.tar.gz", hash = "sha256:22969803849ba0618be8eee2dd01d0d9e2cd3b75e6ff1a291fa9abcdb34da5e6"},
]
[package.dependencies]
@@ -436,25 +445,25 @@ tzdata = "*"
[[package]]
name = "fastapi"
-version = "0.116.1"
+version = "0.119.0"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
- {file = "fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565"},
- {file = "fastapi-0.116.1.tar.gz", hash = "sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143"},
+ {file = "fastapi-0.119.0-py3-none-any.whl", hash = "sha256:90a2e49ed19515320abb864df570dd766be0662c5d577688f1600170f7f73cf2"},
+ {file = "fastapi-0.119.0.tar.gz", hash = "sha256:451082403a2c1f0b99c6bd57c09110ed5463856804c8078d38e5a1f1035dbbb7"},
]
[package.dependencies]
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
-starlette = ">=0.40.0,<0.48.0"
+starlette = ">=0.40.0,<0.49.0"
typing-extensions = ">=4.8.0"
[package.extras]
-all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
-standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
-standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[standard-no-fastapi-cloud-cli] (>=0.0.8)", "httpx (>=0.23.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
+all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
+standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
+standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[standard-no-fastapi-cloud-cli] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
[[package]]
name = "genbadge"
@@ -495,14 +504,14 @@ files = [
[[package]]
name = "idna"
-version = "3.10"
+version = "3.11"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.8"
groups = ["main", "dev"]
files = [
- {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
- {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
+ {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"},
+ {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"},
]
[package.extras]
@@ -510,14 +519,14 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2
[[package]]
name = "invoke"
-version = "2.2.0"
+version = "2.2.1"
description = "Pythonic task execution"
optional = false
python-versions = ">=3.6"
groups = ["dev"]
files = [
- {file = "invoke-2.2.0-py3-none-any.whl", hash = "sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820"},
- {file = "invoke-2.2.0.tar.gz", hash = "sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5"},
+ {file = "invoke-2.2.1-py3-none-any.whl", hash = "sha256:2413bc441b376e5cd3f55bb5d364f973ad8bdd7bf87e53c79de3c11bf3feecc8"},
+ {file = "invoke-2.2.1.tar.gz", hash = "sha256:515bf49b4a48932b79b024590348da22f39c4942dff991ad1fb8b8baea1be707"},
]
[[package]]
@@ -534,14 +543,14 @@ files = [
[[package]]
name = "isort"
-version = "6.0.1"
+version = "7.0.0"
description = "A Python utility / library to sort Python imports."
optional = false
-python-versions = ">=3.9.0"
+python-versions = ">=3.10.0"
groups = ["lint-and-format"]
files = [
- {file = "isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615"},
- {file = "isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450"},
+ {file = "isort-7.0.0-py3-none-any.whl", hash = "sha256:1bcabac8bc3c36c7fb7b98a76c8abb18e0f841a3ba81decac7691008592499c1"},
+ {file = "isort-7.0.0.tar.gz", hash = "sha256:5513527951aadb3ac4292a41a16cbc50dd1642432f5e8c20057d414bdafb4187"},
]
[package.extras]
@@ -701,73 +710,101 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"]
[[package]]
name = "markupsafe"
-version = "3.0.2"
+version = "3.0.3"
description = "Safely add untrusted strings to HTML/XML markup."
optional = false
python-versions = ">=3.9"
groups = ["main", "lint-and-format"]
files = [
- {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"},
- {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"},
+ {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"},
+ {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"},
+ {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"},
+ {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"},
+ {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"},
+ {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"},
+ {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"},
+ {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"},
+ {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"},
+ {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"},
+ {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"},
+ {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"},
+ {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"},
+ {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"},
+ {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"},
+ {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"},
+ {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"},
+ {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"},
+ {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"},
+ {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"},
+ {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"},
+ {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"},
+ {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"},
+ {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"},
+ {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"},
+ {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"},
+ {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"},
+ {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"},
+ {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"},
+ {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"},
+ {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"},
+ {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"},
+ {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"},
+ {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"},
+ {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"},
+ {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"},
+ {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"},
+ {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"},
+ {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"},
+ {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"},
+ {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"},
+ {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"},
+ {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"},
+ {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"},
+ {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"},
+ {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"},
+ {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"},
+ {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"},
+ {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"},
+ {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"},
+ {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"},
+ {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"},
+ {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"},
+ {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"},
+ {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"},
+ {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"},
+ {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"},
+ {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"},
+ {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"},
+ {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"},
+ {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"},
+ {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"},
+ {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"},
+ {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"},
+ {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"},
+ {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"},
+ {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"},
]
[[package]]
@@ -808,50 +845,50 @@ files = [
[[package]]
name = "mypy"
-version = "1.17.1"
+version = "1.18.2"
description = "Optional static typing for Python"
optional = false
python-versions = ">=3.9"
groups = ["type-checking"]
files = [
- {file = "mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972"},
- {file = "mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7"},
- {file = "mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df"},
- {file = "mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390"},
- {file = "mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94"},
- {file = "mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b"},
- {file = "mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58"},
- {file = "mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5"},
- {file = "mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd"},
- {file = "mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b"},
- {file = "mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5"},
- {file = "mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b"},
- {file = "mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb"},
- {file = "mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403"},
- {file = "mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056"},
- {file = "mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341"},
- {file = "mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb"},
- {file = "mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19"},
- {file = "mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7"},
- {file = "mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81"},
- {file = "mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6"},
- {file = "mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849"},
- {file = "mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14"},
- {file = "mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a"},
- {file = "mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733"},
- {file = "mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd"},
- {file = "mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0"},
- {file = "mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a"},
- {file = "mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91"},
- {file = "mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed"},
- {file = "mypy-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d1092694f166a7e56c805caaf794e0585cabdbf1df36911c414e4e9abb62ae9"},
- {file = "mypy-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79d44f9bfb004941ebb0abe8eff6504223a9c1ac51ef967d1263c6572bbebc99"},
- {file = "mypy-1.17.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b01586eed696ec905e61bd2568f48740f7ac4a45b3a468e6423a03d3788a51a8"},
- {file = "mypy-1.17.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43808d9476c36b927fbcd0b0255ce75efe1b68a080154a38ae68a7e62de8f0f8"},
- {file = "mypy-1.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:feb8cc32d319edd5859da2cc084493b3e2ce5e49a946377663cc90f6c15fb259"},
- {file = "mypy-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d7598cf74c3e16539d4e2f0b8d8c318e00041553d83d4861f87c7a72e95ac24d"},
- {file = "mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9"},
- {file = "mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01"},
+ {file = "mypy-1.18.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eab0cf6294dafe397c261a75f96dc2c31bffe3b944faa24db5def4e2b0f77c"},
+ {file = "mypy-1.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a780ca61fc239e4865968ebc5240bb3bf610ef59ac398de9a7421b54e4a207e"},
+ {file = "mypy-1.18.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448acd386266989ef11662ce3c8011fd2a7b632e0ec7d61a98edd8e27472225b"},
+ {file = "mypy-1.18.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9e171c465ad3901dc652643ee4bffa8e9fef4d7d0eece23b428908c77a76a66"},
+ {file = "mypy-1.18.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:592ec214750bc00741af1f80cbf96b5013d81486b7bb24cb052382c19e40b428"},
+ {file = "mypy-1.18.2-cp310-cp310-win_amd64.whl", hash = "sha256:7fb95f97199ea11769ebe3638c29b550b5221e997c63b14ef93d2e971606ebed"},
+ {file = "mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f"},
+ {file = "mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341"},
+ {file = "mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d"},
+ {file = "mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86"},
+ {file = "mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37"},
+ {file = "mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8"},
+ {file = "mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34"},
+ {file = "mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764"},
+ {file = "mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893"},
+ {file = "mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914"},
+ {file = "mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8"},
+ {file = "mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074"},
+ {file = "mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc"},
+ {file = "mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e"},
+ {file = "mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986"},
+ {file = "mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d"},
+ {file = "mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba"},
+ {file = "mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544"},
+ {file = "mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce"},
+ {file = "mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d"},
+ {file = "mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c"},
+ {file = "mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb"},
+ {file = "mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075"},
+ {file = "mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf"},
+ {file = "mypy-1.18.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25a9c8fb67b00599f839cf472713f54249a62efd53a54b565eb61956a7e3296b"},
+ {file = "mypy-1.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2b9c7e284ee20e7598d6f42e13ca40b4928e6957ed6813d1ab6348aa3f47133"},
+ {file = "mypy-1.18.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6985ed057513e344e43a26cc1cd815c7a94602fb6a3130a34798625bc2f07b6"},
+ {file = "mypy-1.18.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22f27105f1525ec024b5c630c0b9f36d5c1cc4d447d61fe51ff4bd60633f47ac"},
+ {file = "mypy-1.18.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:030c52d0ea8144e721e49b1f68391e39553d7451f0c3f8a7565b59e19fcb608b"},
+ {file = "mypy-1.18.2-cp39-cp39-win_amd64.whl", hash = "sha256:aa5e07ac1a60a253445797e42b8b2963c9675563a94f11291ab40718b016a7a0"},
+ {file = "mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e"},
+ {file = "mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b"},
]
[package.dependencies]
@@ -1133,20 +1170,20 @@ xmp = ["defusedxml"]
[[package]]
name = "platformdirs"
-version = "4.3.8"
+version = "4.5.0"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
optional = false
-python-versions = ">=3.9"
+python-versions = ">=3.10"
groups = ["main", "dev", "lint-and-format", "type-checking"]
files = [
- {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"},
- {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"},
+ {file = "platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3"},
+ {file = "platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312"},
]
[package.extras]
-docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
-test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"]
-type = ["mypy (>=1.14.1)"]
+docs = ["furo (>=2025.9.25)", "proselint (>=0.14)", "sphinx (>=8.2.3)", "sphinx-autodoc-typehints (>=3.2)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.4.2)", "pytest-cov (>=7)", "pytest-mock (>=3.15.1)"]
+type = ["mypy (>=1.18.2)"]
[[package]]
name = "pluggy"
@@ -1193,21 +1230,21 @@ ssv = ["swagger-spec-validator (>=3.0.4,<3.1.0)"]
[[package]]
name = "pydantic"
-version = "2.11.7"
+version = "2.12.0"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.9"
groups = ["main", "dev"]
files = [
- {file = "pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b"},
- {file = "pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db"},
+ {file = "pydantic-2.12.0-py3-none-any.whl", hash = "sha256:f6a1da352d42790537e95e83a8bdfb91c7efbae63ffd0b86fa823899e807116f"},
+ {file = "pydantic-2.12.0.tar.gz", hash = "sha256:c1a077e6270dbfb37bfd8b498b3981e2bb18f68103720e51fa6c306a5a9af563"},
]
[package.dependencies]
annotated-types = ">=0.6.0"
-pydantic-core = "2.33.2"
-typing-extensions = ">=4.12.2"
-typing-inspection = ">=0.4.0"
+pydantic-core = "2.41.1"
+typing-extensions = ">=4.14.1"
+typing-inspection = ">=0.4.2"
[package.extras]
email = ["email-validator (>=2.0.0)"]
@@ -1215,115 +1252,129 @@ timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows
[[package]]
name = "pydantic-core"
-version = "2.33.2"
+version = "2.41.1"
description = "Core functionality for Pydantic validation and serialization"
optional = false
python-versions = ">=3.9"
groups = ["main", "dev"]
files = [
- {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"},
- {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"},
- {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"},
- {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"},
- {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"},
- {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"},
- {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"},
- {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"},
- {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"},
- {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"},
- {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"},
- {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"},
- {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"},
- {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"},
- {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"},
- {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"},
- {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"},
- {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"},
- {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"},
- {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"},
- {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"},
- {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"},
- {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"},
- {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"},
- {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"},
- {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"},
- {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"},
- {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"},
- {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"},
- {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"},
- {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"},
- {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"},
- {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"},
- {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"},
- {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"},
- {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"},
- {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"},
- {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"},
- {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"},
- {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"},
- {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"},
- {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"},
- {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"},
- {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"},
- {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"},
- {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"},
- {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"},
- {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"},
- {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"},
- {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"},
- {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"},
- {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"},
- {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"},
- {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"},
- {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"},
- {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"},
- {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"},
- {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"},
- {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"},
- {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"},
- {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"},
- {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"},
- {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"},
- {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"},
- {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"},
- {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"},
- {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"},
- {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"},
- {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"},
- {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"},
- {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"},
- {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"},
+ {file = "pydantic_core-2.41.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e63036298322e9aea1c8b7c0a6c1204d615dbf6ec0668ce5b83ff27f07404a61"},
+ {file = "pydantic_core-2.41.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:241299ca91fc77ef64f11ed909d2d9220a01834e8e6f8de61275c4dd16b7c936"},
+ {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab7e594a2a5c24ab8013a7dc8cfe5f2260e80e490685814122081705c2cf2b0"},
+ {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b054ef1a78519cb934b58e9c90c09e93b837c935dcd907b891f2b265b129eb6e"},
+ {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2ab7d10d0ab2ed6da54c757233eb0f48ebfb4f86e9b88ccecb3f92bbd61a538"},
+ {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2757606b7948bb853a27e4040820306eaa0ccb9e8f9f8a0fa40cb674e170f350"},
+ {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cec0e75eb61f606bad0a32f2be87507087514e26e8c73db6cbdb8371ccd27917"},
+ {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0234236514f44a5bf552105cfe2543a12f48203397d9d0f866affa569345a5b5"},
+ {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1b974e41adfbb4ebb0f65fc4ca951347b17463d60893ba7d5f7b9bb087c83897"},
+ {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:248dafb3204136113c383e91a4d815269f51562b6659b756cf3df14eefc7d0bb"},
+ {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:678f9d76a91d6bcedd7568bbf6beb77ae8447f85d1aeebaab7e2f0829cfc3a13"},
+ {file = "pydantic_core-2.41.1-cp310-cp310-win32.whl", hash = "sha256:dff5bee1d21ee58277900692a641925d2dddfde65182c972569b1a276d2ac8fb"},
+ {file = "pydantic_core-2.41.1-cp310-cp310-win_amd64.whl", hash = "sha256:5042da12e5d97d215f91567110fdfa2e2595a25f17c19b9ff024f31c34f9b53e"},
+ {file = "pydantic_core-2.41.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4f276a6134fe1fc1daa692642a3eaa2b7b858599c49a7610816388f5e37566a1"},
+ {file = "pydantic_core-2.41.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07588570a805296ece009c59d9a679dc08fab72fb337365afb4f3a14cfbfc176"},
+ {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28527e4b53400cd60ffbd9812ccb2b5135d042129716d71afd7e45bf42b855c0"},
+ {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46a1c935c9228bad738c8a41de06478770927baedf581d172494ab36a6b96575"},
+ {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:447ddf56e2b7d28d200d3e9eafa936fe40485744b5a824b67039937580b3cb20"},
+ {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63892ead40c1160ac860b5debcc95c95c5a0035e543a8b5a4eac70dd22e995f4"},
+ {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4a9543ca355e6df8fbe9c83e9faab707701e9103ae857ecb40f1c0cf8b0e94d"},
+ {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2611bdb694116c31e551ed82e20e39a90bea9b7ad9e54aaf2d045ad621aa7a1"},
+ {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fecc130893a9b5f7bfe230be1bb8c61fe66a19db8ab704f808cb25a82aad0bc9"},
+ {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:1e2df5f8344c99b6ea5219f00fdc8950b8e6f2c422fbc1cc122ec8641fac85a1"},
+ {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:35291331e9d8ed94c257bab6be1cb3a380b5eee570a2784bffc055e18040a2ea"},
+ {file = "pydantic_core-2.41.1-cp311-cp311-win32.whl", hash = "sha256:2876a095292668d753f1a868c4a57c4ac9f6acbd8edda8debe4218d5848cf42f"},
+ {file = "pydantic_core-2.41.1-cp311-cp311-win_amd64.whl", hash = "sha256:b92d6c628e9a338846a28dfe3fcdc1a3279388624597898b105e078cdfc59298"},
+ {file = "pydantic_core-2.41.1-cp311-cp311-win_arm64.whl", hash = "sha256:7d82ae99409eb69d507a89835488fb657faa03ff9968a9379567b0d2e2e56bc5"},
+ {file = "pydantic_core-2.41.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:db2f82c0ccbce8f021ad304ce35cbe02aa2f95f215cac388eed542b03b4d5eb4"},
+ {file = "pydantic_core-2.41.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47694a31c710ced9205d5f1e7e8af3ca57cbb8a503d98cb9e33e27c97a501601"},
+ {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e9decce94daf47baf9e9d392f5f2557e783085f7c5e522011545d9d6858e00"},
+ {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab0adafdf2b89c8b84f847780a119437a0931eca469f7b44d356f2b426dd9741"},
+ {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5da98cc81873f39fd56882e1569c4677940fbc12bce6213fad1ead784192d7c8"},
+ {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:209910e88afb01fd0fd403947b809ba8dba0e08a095e1f703294fda0a8fdca51"},
+ {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365109d1165d78d98e33c5bfd815a9b5d7d070f578caefaabcc5771825b4ecb5"},
+ {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:706abf21e60a2857acdb09502bc853ee5bce732955e7b723b10311114f033115"},
+ {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bf0bd5417acf7f6a7ec3b53f2109f587be176cb35f9cf016da87e6017437a72d"},
+ {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2e71b1c6ceb9c78424ae9f63a07292fb769fb890a4e7efca5554c47f33a60ea5"},
+ {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80745b9770b4a38c25015b517451c817799bfb9d6499b0d13d8227ec941cb513"},
+ {file = "pydantic_core-2.41.1-cp312-cp312-win32.whl", hash = "sha256:83b64d70520e7890453f1aa21d66fda44e7b35f1cfea95adf7b4289a51e2b479"},
+ {file = "pydantic_core-2.41.1-cp312-cp312-win_amd64.whl", hash = "sha256:377defd66ee2003748ee93c52bcef2d14fde48fe28a0b156f88c3dbf9bc49a50"},
+ {file = "pydantic_core-2.41.1-cp312-cp312-win_arm64.whl", hash = "sha256:c95caff279d49c1d6cdfe2996e6c2ad712571d3b9caaa209a404426c326c4bde"},
+ {file = "pydantic_core-2.41.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70e790fce5f05204ef4403159857bfcd587779da78627b0babb3654f75361ebf"},
+ {file = "pydantic_core-2.41.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9cebf1ca35f10930612d60bd0f78adfacee824c30a880e3534ba02c207cceceb"},
+ {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:170406a37a5bc82c22c3274616bf6f17cc7df9c4a0a0a50449e559cb755db669"},
+ {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12d4257fc9187a0ccd41b8b327d6a4e57281ab75e11dda66a9148ef2e1fb712f"},
+ {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a75a33b4db105dd1c8d57839e17ee12db8d5ad18209e792fa325dbb4baeb00f4"},
+ {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08a589f850803a74e0fcb16a72081cafb0d72a3cdda500106942b07e76b7bf62"},
+ {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a97939d6ea44763c456bd8a617ceada2c9b96bb5b8ab3dfa0d0827df7619014"},
+ {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae423c65c556f09569524b80ffd11babff61f33055ef9773d7c9fabc11ed8d"},
+ {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:4dc703015fbf8764d6a8001c327a87f1823b7328d40b47ce6000c65918ad2b4f"},
+ {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:968e4ffdfd35698a5fe659e5e44c508b53664870a8e61c8f9d24d3d145d30257"},
+ {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:fff2b76c8e172d34771cd4d4f0ade08072385310f214f823b5a6ad4006890d32"},
+ {file = "pydantic_core-2.41.1-cp313-cp313-win32.whl", hash = "sha256:a38a5263185407ceb599f2f035faf4589d57e73c7146d64f10577f6449e8171d"},
+ {file = "pydantic_core-2.41.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42ae7fd6760782c975897e1fdc810f483b021b32245b0105d40f6e7a3803e4b"},
+ {file = "pydantic_core-2.41.1-cp313-cp313-win_arm64.whl", hash = "sha256:ad4111acc63b7384e205c27a2f15e23ac0ee21a9d77ad6f2e9cb516ec90965fb"},
+ {file = "pydantic_core-2.41.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:440d0df7415b50084a4ba9d870480c16c5f67c0d1d4d5119e3f70925533a0edc"},
+ {file = "pydantic_core-2.41.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71eaa38d342099405dae6484216dcf1e8e4b0bebd9b44a4e08c9b43db6a2ab67"},
+ {file = "pydantic_core-2.41.1-cp313-cp313t-win_amd64.whl", hash = "sha256:555ecf7e50f1161d3f693bc49f23c82cf6cdeafc71fa37a06120772a09a38795"},
+ {file = "pydantic_core-2.41.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:05226894a26f6f27e1deb735d7308f74ef5fa3a6de3e0135bb66cdcaee88f64b"},
+ {file = "pydantic_core-2.41.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:85ff7911c6c3e2fd8d3779c50925f6406d770ea58ea6dde9c230d35b52b16b4a"},
+ {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47f1f642a205687d59b52dc1a9a607f45e588f5a2e9eeae05edd80c7a8c47674"},
+ {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df11c24e138876ace5ec6043e5cae925e34cf38af1a1b3d63589e8f7b5f5cdc4"},
+ {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f0bf7f5c8f7bf345c527e8a0d72d6b26eda99c1227b0c34e7e59e181260de31"},
+ {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82b887a711d341c2c47352375d73b029418f55b20bd7815446d175a70effa706"},
+ {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5f1d5d6bbba484bdf220c72d8ecd0be460f4bd4c5e534a541bb2cd57589fb8b"},
+ {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bf1917385ebe0f968dc5c6ab1375886d56992b93ddfe6bf52bff575d03662be"},
+ {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:4f94f3ab188f44b9a73f7295663f3ecb8f2e2dd03a69c8f2ead50d37785ecb04"},
+ {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:3925446673641d37c30bd84a9d597e49f72eacee8b43322c8999fa17d5ae5bc4"},
+ {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:49bd51cc27adb980c7b97357ae036ce9b3c4d0bb406e84fbe16fb2d368b602a8"},
+ {file = "pydantic_core-2.41.1-cp314-cp314-win32.whl", hash = "sha256:a31ca0cd0e4d12ea0df0077df2d487fc3eb9d7f96bbb13c3c5b88dcc21d05159"},
+ {file = "pydantic_core-2.41.1-cp314-cp314-win_amd64.whl", hash = "sha256:1b5c4374a152e10a22175d7790e644fbd8ff58418890e07e2073ff9d4414efae"},
+ {file = "pydantic_core-2.41.1-cp314-cp314-win_arm64.whl", hash = "sha256:4fee76d757639b493eb600fba668f1e17475af34c17dd61db7a47e824d464ca9"},
+ {file = "pydantic_core-2.41.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f9b9c968cfe5cd576fdd7361f47f27adeb120517e637d1b189eea1c3ece573f4"},
+ {file = "pydantic_core-2.41.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ebc7ab67b856384aba09ed74e3e977dded40e693de18a4f197c67d0d4e6d8e"},
+ {file = "pydantic_core-2.41.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8ae0dc57b62a762985bc7fbf636be3412394acc0ddb4ade07fe104230f1b9762"},
+ {file = "pydantic_core-2.41.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:10ce489cf09a4956a1549af839b983edc59b0f60e1b068c21b10154e58f54f80"},
+ {file = "pydantic_core-2.41.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ff548c908caffd9455fd1342366bcf8a1ec8a3fca42f35c7fc60883d6a901074"},
+ {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d43bf082025082bda13be89a5f876cc2386b7727c7b322be2d2b706a45cea8e"},
+ {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:666aee751faf1c6864b2db795775dd67b61fdcf646abefa309ed1da039a97209"},
+ {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b83aaeff0d7bde852c32e856f3ee410842ebc08bc55c510771d87dcd1c01e1ed"},
+ {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:055c7931b0329cb8acde20cdde6d9c2cbc2a02a0a8e54a792cddd91e2ea92c65"},
+ {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530bbb1347e3e5ca13a91ac087c4971d7da09630ef8febd27a20a10800c2d06d"},
+ {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65a0ea16cfea7bfa9e43604c8bd726e63a3788b61c384c37664b55209fcb1d74"},
+ {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8fa93fadff794c6d15c345c560513b160197342275c6d104cc879f932b978afc"},
+ {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:c8a1af9ac51969a494c6a82b563abae6859dc082d3b999e8fa7ba5ee1b05e8e8"},
+ {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:30edab28829703f876897c9471a857e43d847b8799c3c9e2fbce644724b50aa4"},
+ {file = "pydantic_core-2.41.1-cp39-cp39-win32.whl", hash = "sha256:84d0ff869f98be2e93efdf1ae31e5a15f0926d22af8677d51676e373abbfe57a"},
+ {file = "pydantic_core-2.41.1-cp39-cp39-win_amd64.whl", hash = "sha256:b5674314987cdde5a5511b029fa5fb1556b3d147a367e01dd583b19cfa8e35df"},
+ {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:68f2251559b8efa99041bb63571ec7cdd2d715ba74cc82b3bc9eff824ebc8bf0"},
+ {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:c7bc140c596097cb53b30546ca257dbe3f19282283190b1b5142928e5d5d3a20"},
+ {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2896510fce8f4725ec518f8b9d7f015a00db249d2fd40788f442af303480063d"},
+ {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ced20e62cfa0f496ba68fa5d6c7ee71114ea67e2a5da3114d6450d7f4683572a"},
+ {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b04fa9ed049461a7398138c604b00550bc89e3e1151d84b81ad6dc93e39c4c06"},
+ {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b3b7d9cfbfdc43c80a16638c6dc2768e3956e73031fca64e8e1a3ae744d1faeb"},
+ {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eec83fc6abef04c7f9bec616e2d76ee9a6a4ae2a359b10c21d0f680e24a247ca"},
+ {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6771a2d9f83c4038dfad5970a3eef215940682b2175e32bcc817bdc639019b28"},
+ {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fabcbdb12de6eada8d6e9a759097adb3c15440fafc675b3e94ae5c9cb8d678a0"},
+ {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:80e97ccfaf0aaf67d55de5085b0ed0d994f57747d9d03f2de5cc9847ca737b08"},
+ {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34df1fe8fea5d332484a763702e8b6a54048a9d4fe6ccf41e34a128238e01f52"},
+ {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:421b5595f845842fc093f7250e24ee395f54ca62d494fdde96f43ecf9228ae01"},
+ {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dce8b22663c134583aaad24827863306a933f576c79da450be3984924e2031d1"},
+ {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:300a9c162fea9906cc5c103893ca2602afd84f0ec90d3be36f4cc360125d22e1"},
+ {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e019167628f6e6161ae7ab9fb70f6d076a0bf0d55aa9b20833f86a320c70dd65"},
+ {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:13ab9cc2de6f9d4ab645a050ae5aee61a2424ac4d3a16ba23d4c2027705e0301"},
+ {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:af2385d3f98243fb733862f806c5bb9122e5fba05b373e3af40e3c82d711cef1"},
+ {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6550617a0c2115be56f90c31a5370261d8ce9dbf051c3ed53b51172dd34da696"},
+ {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc17b6ecf4983d298686014c92ebc955a9f9baf9f57dad4065e7906e7bee6222"},
+ {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42ae9352cf211f08b04ea110563d6b1e415878eea5b4c70f6bdb17dca3b932d2"},
+ {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e82947de92068b0a21681a13dd2102387197092fbe7defcfb8453e0913866506"},
+ {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e244c37d5471c9acdcd282890c6c4c83747b77238bfa19429b8473586c907656"},
+ {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1e798b4b304a995110d41ec93653e57975620ccb2842ba9420037985e7d7284e"},
+ {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f1fc716c0eb1663c59699b024428ad5ec2bcc6b928527b8fe28de6cb89f47efb"},
+ {file = "pydantic_core-2.41.1.tar.gz", hash = "sha256:1ad375859a6d8c356b7704ec0f547a58e82ee80bb41baa811ad710e124bc8f2f"},
]
[package.dependencies]
-typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
+typing-extensions = ">=4.14.1"
[[package]]
name = "pygments"
@@ -1342,25 +1393,25 @@ windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pylint"
-version = "3.3.8"
+version = "4.0.0"
description = "python code static checker"
optional = false
-python-versions = ">=3.9.0"
+python-versions = ">=3.10.0"
groups = ["lint-and-format"]
files = [
- {file = "pylint-3.3.8-py3-none-any.whl", hash = "sha256:7ef94aa692a600e82fabdd17102b73fc226758218c97473c7ad67bd4cb905d83"},
- {file = "pylint-3.3.8.tar.gz", hash = "sha256:26698de19941363037e2937d3db9ed94fb3303fdadf7d98847875345a8bb6b05"},
+ {file = "pylint-4.0.0-py3-none-any.whl", hash = "sha256:196b92a85204bb0c0a416a6bb324f6185e59ff1d687ee1d614bf0abf34a348e8"},
+ {file = "pylint-4.0.0.tar.gz", hash = "sha256:62da212808c0681e49ffb125f0a994c685d912cf19ae373075649ebb5870ec28"},
]
[package.dependencies]
-astroid = ">=3.3.8,<=3.4.0.dev0"
+astroid = ">=4.0.1,<=4.1.dev0"
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
dill = [
{version = ">=0.2", markers = "python_version < \"3.11\""},
{version = ">=0.3.7", markers = "python_version >= \"3.12\""},
{version = ">=0.3.6", markers = "python_version == \"3.11\""},
]
-isort = ">=4.2.5,<5.13 || >5.13,<7"
+isort = ">=5,<5.13 || >5.13,<8"
mccabe = ">=0.6,<0.8"
platformdirs = ">=2.2"
tomli = {version = ">=1.1", markers = "python_version < \"3.11\""}
@@ -1372,14 +1423,14 @@ testutils = ["gitpython (>3)"]
[[package]]
name = "pyright"
-version = "1.1.405"
+version = "1.1.406"
description = "Command line wrapper for pyright"
optional = false
python-versions = ">=3.7"
groups = ["type-checking"]
files = [
- {file = "pyright-1.1.405-py3-none-any.whl", hash = "sha256:a2cb13700b5508ce8e5d4546034cb7ea4aedb60215c6c33f56cec7f53996035a"},
- {file = "pyright-1.1.405.tar.gz", hash = "sha256:5c2a30e1037af27eb463a1cc0b9f6d65fec48478ccf092c1ac28385a15c55763"},
+ {file = "pyright-1.1.406-py3-none-any.whl", hash = "sha256:1d81fb43c2407bf566e97e57abb01c811973fdb21b2df8df59f870f688bdca71"},
+ {file = "pyright-1.1.406.tar.gz", hash = "sha256:c4872bc58c9643dac09e8a2e74d472c62036910b3bd37a32813989ef7576ea2c"},
]
[package.dependencies]
@@ -1391,6 +1442,21 @@ all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"]
dev = ["twine (>=3.4.1)"]
nodejs = ["nodejs-wheel-binaries"]
+[[package]]
+name = "pytokens"
+version = "0.1.10"
+description = "A Fast, spec compliant Python 3.12+ tokenizer that runs on older Pythons."
+optional = false
+python-versions = ">=3.8"
+groups = ["main"]
+files = [
+ {file = "pytokens-0.1.10-py3-none-any.whl", hash = "sha256:db7b72284e480e69fb085d9f251f66b3d2df8b7166059261258ff35f50fb711b"},
+ {file = "pytokens-0.1.10.tar.gz", hash = "sha256:c9a4bfa0be1d26aebce03e6884ba454e842f186a59ea43a6d3b25af58223c044"},
+]
+
+[package.extras]
+dev = ["black", "build", "mypy", "pytest", "pytest-cov", "setuptools", "tox", "twine", "wheel"]
+
[[package]]
name = "pytz"
version = "2025.2"
@@ -1405,65 +1471,85 @@ files = [
[[package]]
name = "pyyaml"
-version = "6.0.2"
+version = "6.0.3"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
- {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"},
+ {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"},
+ {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"},
+ {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"},
+ {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"},
+ {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"},
+ {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"},
+ {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"},
+ {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"},
+ {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"},
+ {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"},
+ {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"},
+ {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"},
+ {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"},
+ {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"},
+ {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"},
+ {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"},
+ {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"},
+ {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"},
+ {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"},
+ {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"},
+ {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"},
+ {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"},
+ {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"},
+ {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"},
+ {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"},
+ {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"},
+ {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"},
+ {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"},
+ {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"},
+ {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"},
+ {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"},
+ {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"},
+ {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"},
+ {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"},
+ {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"},
+ {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"},
+ {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"},
+ {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"},
+ {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"},
+ {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"},
+ {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"},
+ {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"},
+ {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"},
+ {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"},
+ {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"},
+ {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"},
+ {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"},
+ {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"},
+ {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"},
+ {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"},
+ {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"},
+ {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"},
+ {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"},
+ {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"},
+ {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"},
+ {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"},
+ {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"},
+ {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"},
+ {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"},
+ {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"},
+ {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"},
+ {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"},
+ {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"},
+ {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"},
]
[[package]]
@@ -1522,14 +1608,14 @@ six = "*"
[[package]]
name = "rich"
-version = "14.1.0"
+version = "14.2.0"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
optional = false
python-versions = ">=3.8.0"
groups = ["main", "lint-and-format"]
files = [
- {file = "rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f"},
- {file = "rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8"},
+ {file = "rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd"},
+ {file = "rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4"},
]
[package.dependencies]
@@ -1541,83 +1627,84 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]]
name = "rich-click"
-version = "1.8.9"
+version = "1.9.3"
description = "Format click help output nicely with rich"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
groups = ["main"]
files = [
- {file = "rich_click-1.8.9-py3-none-any.whl", hash = "sha256:c3fa81ed8a671a10de65a9e20abf642cfdac6fdb882db1ef465ee33919fbcfe2"},
- {file = "rich_click-1.8.9.tar.gz", hash = "sha256:fd98c0ab9ddc1cf9c0b7463f68daf28b4d0033a74214ceb02f761b3ff2af3136"},
+ {file = "rich_click-1.9.3-py3-none-any.whl", hash = "sha256:8ef51bc340db4d048a846c15c035d27b88acf720cbbb9b6fecf6c8b1a297b909"},
+ {file = "rich_click-1.9.3.tar.gz", hash = "sha256:60839150a935604df1378b159da340d3fff91f912903e935da7cb615b5738c1b"},
]
[package.dependencies]
-click = ">=7"
-rich = ">=10.7"
-typing_extensions = ">=4"
+click = ">=8"
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+rich = ">=12"
+typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""}
[package.extras]
-dev = ["mypy", "packaging", "pre-commit", "pytest", "pytest-cov", "rich-codex", "ruff", "types-setuptools"]
-docs = ["markdown_include", "mkdocs", "mkdocs-glightbox", "mkdocs-material-extensions", "mkdocs-material[imaging] (>=9.5.18,<9.6.0)", "mkdocs-rss-plugin", "mkdocstrings[python]", "rich-codex"]
+dev = ["inline-snapshot (>=0.24)", "jsonschema (>=4)", "mypy (>=1.14.1)", "nodeenv (>=1.9.1)", "packaging (>=25)", "pre-commit (>=3.5)", "pytest (>=8.3.5)", "pytest-cov (>=5)", "rich-codex (>=1.2.11)", "ruff (>=0.12.4)", "typer (>=0.15)", "types-setuptools (>=75.8.0.20250110)"]
+docs = ["markdown-include (>=0.8.1)", "mike (>=2.1.3)", "mkdocs-github-admonitions-plugin (>=0.1.1)", "mkdocs-glightbox (>=0.4)", "mkdocs-include-markdown-plugin (>=7.1.7) ; python_version >= \"3.9\"", "mkdocs-material-extensions (>=1.3.1)", "mkdocs-material[imaging] (>=9.5.18,<9.6.0)", "mkdocs-redirects (>=1.2.2)", "mkdocs-rss-plugin (>=1.15)", "mkdocs[docs] (>=1.6.1)", "mkdocstrings[python] (>=0.26.1)", "rich-codex (>=1.2.11)", "typer (>=0.15)"]
[[package]]
name = "robotcode"
-version = "1.8.0"
+version = "2.0.1"
description = "Command line interface for RobotCode"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.10"
groups = ["dev", "type-checking"]
files = [
- {file = "robotcode-1.8.0-py3-none-any.whl", hash = "sha256:83797890f8ae33cb3b3982602ca4807b0f3a8009f6bc3876f6be737a7ad534d5"},
- {file = "robotcode-1.8.0.tar.gz", hash = "sha256:bd6914bbe1f2a15fcd315bdde18fa3fdfa19058535b0c798e67d848059205457"},
+ {file = "robotcode-2.0.1-py3-none-any.whl", hash = "sha256:4259fc1a3b261c7d01194c019fd7c9cd800c93a0d18a5d3b45d5bd21293ac913"},
+ {file = "robotcode-2.0.1.tar.gz", hash = "sha256:2fe0509d91e2f7e351c24bc83cb493ef443ca12c32b0b0a1499309015dd544d1"},
]
[package.dependencies]
-robotcode-core = "1.8.0"
-robotcode-plugin = "1.8.0"
-robotcode-robot = "1.8.0"
+robotcode-core = "2.0.1"
+robotcode-plugin = "2.0.1"
+robotcode-robot = "2.0.1"
[package.extras]
-all = ["docutils", "pyyaml (>=5.4)", "rich", "robotcode-analyze (==1.8.0)", "robotcode-debugger (==1.8.0)", "robotcode-language-server (==1.8.0)", "robotcode-repl (==1.8.0)", "robotcode-repl-server (==1.8.0)", "robotcode-runner (==1.8.0)", "robotframework-robocop (>=2.0.0)"]
-analyze = ["robotcode-analyze (==1.8.0)"]
+all = ["docutils", "pyyaml (>=5.4)", "rich", "robotcode-analyze (==2.0.1)", "robotcode-debugger (==2.0.1)", "robotcode-language-server (==2.0.1)", "robotcode-repl (==2.0.1)", "robotcode-repl-server (==2.0.1)", "robotcode-runner (==2.0.1)", "robotframework-robocop (>=2.0.0)"]
+analyze = ["robotcode-analyze (==2.0.1)"]
colored = ["rich"]
-debugger = ["robotcode-debugger (==1.8.0)"]
-languageserver = ["robotcode-language-server (==1.8.0)"]
+debugger = ["robotcode-debugger (==2.0.1)"]
+languageserver = ["robotcode-language-server (==2.0.1)"]
lint = ["robotframework-robocop (>=2.0.0)"]
-repl = ["robotcode-repl (==1.8.0)"]
-replserver = ["robotcode-repl-server (==1.8.0)"]
+repl = ["robotcode-repl (==2.0.1)"]
+replserver = ["robotcode-repl-server (==2.0.1)"]
rest = ["docutils"]
-runner = ["robotcode-runner (==1.8.0)"]
+runner = ["robotcode-runner (==2.0.1)"]
yaml = ["pyyaml (>=5.4)"]
[[package]]
name = "robotcode-analyze"
-version = "1.8.0"
+version = "2.0.1"
description = "RobotCode analyze plugin for Robot Framework"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.10"
groups = ["type-checking"]
files = [
- {file = "robotcode_analyze-1.8.0-py3-none-any.whl", hash = "sha256:0a469e2e4383aff471ff63bbb5233193e9ca31dd2aa98fe095105b6eae4a22d7"},
- {file = "robotcode_analyze-1.8.0.tar.gz", hash = "sha256:976ab27f53248d87f31db4638b097acdfe3022f00a356bdb7c60a4da882b9e8e"},
+ {file = "robotcode_analyze-2.0.1-py3-none-any.whl", hash = "sha256:b6589fb93b90d82b8506301833157bd243bded858cf2d890b78387bc9ca9d5bf"},
+ {file = "robotcode_analyze-2.0.1.tar.gz", hash = "sha256:4e57805e8ee79f8fb5c210c15f39e817ed109200e53d26c9fa81dd9474a87eab"},
]
[package.dependencies]
-robotcode = "1.8.0"
-robotcode-plugin = "1.8.0"
-robotcode-robot = "1.8.0"
+robotcode = "2.0.1"
+robotcode-plugin = "2.0.1"
+robotcode-robot = "2.0.1"
robotframework = ">=4.1.0"
[[package]]
name = "robotcode-core"
-version = "1.8.0"
+version = "2.0.1"
description = "Some core classes for RobotCode"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.10"
groups = ["dev", "type-checking"]
files = [
- {file = "robotcode_core-1.8.0-py3-none-any.whl", hash = "sha256:1592828bbf0333848143e97c252ef7e21941ba8fac1f248567dc23617aaefe89"},
- {file = "robotcode_core-1.8.0.tar.gz", hash = "sha256:ef8980f705b763768dba714fa40a33ee9d666ff4a2e7884c5b338fa7f85d3d7f"},
+ {file = "robotcode_core-2.0.1-py3-none-any.whl", hash = "sha256:0f77be39d42ad4e331e5b7e19809fc631fb046b1424426d3f582ac2a83eb127a"},
+ {file = "robotcode_core-2.0.1.tar.gz", hash = "sha256:0e5240064f057ff9e64641e896ed16c23c134ecdf9b5116a4c574a5477e4e679"},
]
[package.dependencies]
@@ -1625,14 +1712,14 @@ typing-extensions = ">=4.4.0"
[[package]]
name = "robotcode-modifiers"
-version = "1.8.0"
+version = "2.0.1"
description = "Some Robot Framework Modifiers for RobotCode"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.10"
groups = ["dev"]
files = [
- {file = "robotcode_modifiers-1.8.0-py3-none-any.whl", hash = "sha256:76c016396955742e7b70bcd21a227ae5debaa8f7b38f6592c13171c9459cd04b"},
- {file = "robotcode_modifiers-1.8.0.tar.gz", hash = "sha256:7d47c2994c830d1c8e834bafb16e176b0ce32d55f08db076c6625f54943e39eb"},
+ {file = "robotcode_modifiers-2.0.1-py3-none-any.whl", hash = "sha256:827dc9c1ed4f63b6ebce6be67a105d2030cc18266dd4cf8de14f5411ea968678"},
+ {file = "robotcode_modifiers-2.0.1.tar.gz", hash = "sha256:ce6159925d86360a1f205d4a45a57c7facf7fd3b3c95a1a966805a66b5dd7fab"},
]
[package.dependencies]
@@ -1640,14 +1727,14 @@ robotframework = ">=4.1.0"
[[package]]
name = "robotcode-plugin"
-version = "1.8.0"
+version = "2.0.1"
description = "Some classes for RobotCode plugin management"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.10"
groups = ["dev", "type-checking"]
files = [
- {file = "robotcode_plugin-1.8.0-py3-none-any.whl", hash = "sha256:2a1ff8f5edaa1bae13434a416fcfccb55900d1b2f765d8300104e6a482abde70"},
- {file = "robotcode_plugin-1.8.0.tar.gz", hash = "sha256:79a3a4078fb14027b8491d52a4e0508999f553bcba788f7a938c2145e009769e"},
+ {file = "robotcode_plugin-2.0.1-py3-none-any.whl", hash = "sha256:85e239edfd8c4b6e28af2ee9139958eaef9f09e0aef395f2ed2bd2d5593db4f6"},
+ {file = "robotcode_plugin-2.0.1.tar.gz", hash = "sha256:ceb89b663f8e017b1b20bf770113ab51074cf06516187f695e9917e8a84279b4"},
]
[package.dependencies]
@@ -1658,39 +1745,39 @@ tomli-w = ">=1.0.0"
[[package]]
name = "robotcode-robot"
-version = "1.8.0"
+version = "2.0.1"
description = "Support classes for RobotCode for handling Robot Framework projects."
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.10"
groups = ["dev", "type-checking"]
files = [
- {file = "robotcode_robot-1.8.0-py3-none-any.whl", hash = "sha256:88f790140f959a52bbd06921d792595f210fecf4e8ba95078d9b6519f30a727c"},
- {file = "robotcode_robot-1.8.0.tar.gz", hash = "sha256:07057ac03ded810967293d641e032dfe3713ee592e0b0de2333dde9f15e71628"},
+ {file = "robotcode_robot-2.0.1-py3-none-any.whl", hash = "sha256:628cafed3525a28928f95ae7f4a79dedd3983b042b443a8e0d10380cc8617b85"},
+ {file = "robotcode_robot-2.0.1.tar.gz", hash = "sha256:bb7bba064bd4dbda240975fb9093388926daef5cc7faaa14ed366f89fc38f543"},
]
[package.dependencies]
-platformdirs = ">=3.2.0,<4.4.0"
-robotcode-core = "1.8.0"
+platformdirs = ">=4.3"
+robotcode-core = "2.0.1"
robotframework = ">=4.1.0"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
[[package]]
name = "robotcode-runner"
-version = "1.8.0"
+version = "2.0.1"
description = "RobotCode runner for Robot Framework"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.10"
groups = ["dev"]
files = [
- {file = "robotcode_runner-1.8.0-py3-none-any.whl", hash = "sha256:3f60d9f296900a0f894ebff940f87bb16a6fed18edf31caa0b89994def4a35f6"},
- {file = "robotcode_runner-1.8.0.tar.gz", hash = "sha256:b7328a7d6da525d0a7a16776f2b5e6e93b0b3e9f4c34ac114e27f4149849c094"},
+ {file = "robotcode_runner-2.0.1-py3-none-any.whl", hash = "sha256:7df0b67fa647fa75bea651c7caf1be6128d54836e5f7cc12d3117f77bd10d644"},
+ {file = "robotcode_runner-2.0.1.tar.gz", hash = "sha256:aa350a2b0fe19d32c6efde223e4967d206f63ba085b6c444e914c7855dd15379"},
]
[package.dependencies]
-robotcode = "1.8.0"
-robotcode-modifiers = "1.8.0"
-robotcode-plugin = "1.8.0"
-robotcode-robot = "1.8.0"
+robotcode = "2.0.1"
+robotcode-modifiers = "2.0.1"
+robotcode-plugin = "2.0.1"
+robotcode-robot = "2.0.1"
robotframework = ">=4.1.0"
[[package]]
@@ -1727,14 +1814,14 @@ xls = ["openpyxl", "pandas", "xlrd (>=1.2.0)"]
[[package]]
name = "robotframework-robocop"
-version = "6.6.1"
+version = "6.8.0"
description = "Static code analysis tool (linter) and code formatter for Robot Framework"
optional = false
python-versions = ">=3.9"
groups = ["lint-and-format"]
files = [
- {file = "robotframework_robocop-6.6.1-py3-none-any.whl", hash = "sha256:7e50c9647371511211388fae38638a4b318cc464ddc5a82a7630f55e393f3f17"},
- {file = "robotframework_robocop-6.6.1.tar.gz", hash = "sha256:26a58eb715a1c0fa55a94a9f607997017cb5c581ab982bbcdb51517fe1a31f3a"},
+ {file = "robotframework_robocop-6.8.0-py3-none-any.whl", hash = "sha256:6df4bd5bf1c4a88cfc90d11795a350c9738c9c01a792f6d6e5b5115cfe0be0e6"},
+ {file = "robotframework_robocop-6.8.0.tar.gz", hash = "sha256:4f0da4c5d4b3b29b597b4056c424cd490a18f0362f5ec3d3ebcaac2321de6aaf"},
]
[package.dependencies]
@@ -1961,88 +2048,97 @@ jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"]
[[package]]
name = "ruamel-yaml-clib"
-version = "0.2.12"
+version = "0.2.14"
description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml"
optional = false
python-versions = ">=3.9"
groups = ["main"]
-markers = "platform_python_implementation == \"CPython\" and python_version < \"3.14\""
-files = [
- {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5"},
- {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969"},
- {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df"},
- {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76"},
- {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6"},
- {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd"},
- {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a"},
- {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da"},
- {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28"},
- {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6"},
- {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e"},
- {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e"},
- {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52"},
- {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642"},
- {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2"},
- {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3"},
- {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4"},
- {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb"},
- {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632"},
- {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d"},
- {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c"},
- {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd"},
- {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31"},
- {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680"},
- {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d"},
- {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5"},
- {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4"},
- {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a"},
- {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475"},
- {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef"},
- {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6"},
- {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf"},
- {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1"},
- {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01"},
- {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6"},
- {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3"},
- {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987"},
- {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bc5f1e1c28e966d61d2519f2a3d451ba989f9ea0f2307de7bc45baa526de9e45"},
- {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a0e060aace4c24dcaf71023bbd7d42674e3b230f7e7b97317baf1e953e5b519"},
- {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7"},
- {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285"},
- {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed"},
- {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2c59aa6170b990d8d2719323e628aaf36f3bfbc1c26279c0eeeb24d05d2d11c7"},
- {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win32.whl", hash = "sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12"},
- {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win_amd64.whl", hash = "sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b"},
- {file = "ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f"},
+markers = "python_version < \"3.14\" and platform_python_implementation == \"CPython\""
+files = [
+ {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f8b2acb0ffdd2ce8208accbec2dca4a06937d556fdcaefd6473ba1b5daa7e3c4"},
+ {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:aef953f3b8bd0b50bd52a2e52fb54a6a2171a1889d8dea4a5959d46c6624c451"},
+ {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a0ac90efbc7a77b0d796c03c8cc4e62fd710b3f1e4c32947713ef2ef52e09543"},
+ {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9bf6b699223afe6c7fe9f2ef76e0bfa6dd892c21e94ce8c957478987ade76cd8"},
+ {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d73a0187718f6eec5b2f729b0f98e4603f7bd9c48aa65d01227d1a5dcdfbe9e8"},
+ {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81f6d3b19bc703679a5705c6a16dabdc79823c71d791d73c65949be7f3012c02"},
+ {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b28caeaf3e670c08cb7e8de221266df8494c169bd6ed8875493fab45be9607a4"},
+ {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94f3efb718f8f49b031f2071ec7a27dd20cbfe511b4dfd54ecee54c956da2b31"},
+ {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-win32.whl", hash = "sha256:27c070cf3888e90d992be75dd47292ff9aa17dafd36492812a6a304a1aedc182"},
+ {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-win_amd64.whl", hash = "sha256:4f4a150a737fccae13fb51234d41304ff2222e3b7d4c8e9428ed1a6ab48389b8"},
+ {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5bae1a073ca4244620425cd3d3aa9746bde590992b98ee8c7c8be8c597ca0d4e"},
+ {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:0a54e5e40a7a691a426c2703b09b0d61a14294d25cfacc00631aa6f9c964df0d"},
+ {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:10d9595b6a19778f3269399eff6bab642608e5966183abc2adbe558a42d4efc9"},
+ {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dba72975485f2b87b786075e18a6e5d07dc2b4d8973beb2732b9b2816f1bad70"},
+ {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29757bdb7c142f9595cc1b62ec49a3d1c83fab9cef92db52b0ccebaad4eafb98"},
+ {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:557df28dbccf79b152fe2d1b935f6063d9cc431199ea2b0e84892f35c03bb0ee"},
+ {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:26a8de280ab0d22b6e3ec745b4a5a07151a0f74aad92dd76ab9c8d8d7087720d"},
+ {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e501c096aa3889133d674605ebd018471bc404a59cbc17da3c5924421c54d97c"},
+ {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-win32.whl", hash = "sha256:915748cfc25b8cfd81b14d00f4bfdb2ab227a30d6d43459034533f4d1c207a2a"},
+ {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-win_amd64.whl", hash = "sha256:4ccba93c1e5a40af45b2f08e4591969fa4697eae951c708f3f83dcbf9f6c6bb1"},
+ {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6aeadc170090ff1889f0d2c3057557f9cd71f975f17535c26a5d37af98f19c27"},
+ {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5e56ac47260c0eed992789fa0b8efe43404a9adb608608631a948cee4fc2b052"},
+ {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:a911aa73588d9a8b08d662b9484bc0567949529824a55d3885b77e8dd62a127a"},
+ {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a05ba88adf3d7189a974b2de7a9d56731548d35dc0a822ec3dc669caa7019b29"},
+ {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb04c5650de6668b853623eceadcdb1a9f2fee381f5d7b6bc842ee7c239eeec4"},
+ {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:df3ec9959241d07bc261f4983d25a1205ff37703faf42b474f15d54d88b4f8c9"},
+ {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fbc08c02e9b147a11dfcaa1ac8a83168b699863493e183f7c0c8b12850b7d259"},
+ {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c099cafc1834d3c5dac305865d04235f7c21c167c8dd31ebc3d6bbc357e2f023"},
+ {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-win32.whl", hash = "sha256:b5b0f7e294700b615a3bcf6d28b26e6da94e8eba63b079f4ec92e9ba6c0d6b54"},
+ {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-win_amd64.whl", hash = "sha256:a37f40a859b503304dd740686359fcf541d6fb3ff7fc10f539af7f7150917c68"},
+ {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7e4f9da7e7549946e02a6122dcad00b7c1168513acb1f8a726b1aaf504a99d32"},
+ {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:dd7546c851e59c06197a7c651335755e74aa383a835878ca86d2c650c07a2f85"},
+ {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:1c1acc3a0209ea9042cc3cfc0790edd2eddd431a2ec3f8283d081e4d5018571e"},
+ {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2070bf0ad1540d5c77a664de07ebcc45eebd1ddcab71a7a06f26936920692beb"},
+ {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd8fe07f49c170e09d76773fb86ad9135e0beee44f36e1576a201b0676d3d1d"},
+ {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ff86876889ea478b1381089e55cf9e345707b312beda4986f823e1d95e8c0f59"},
+ {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1f118b707eece8cf84ecbc3e3ec94d9db879d85ed608f95870d39b2d2efa5dca"},
+ {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b30110b29484adc597df6bd92a37b90e63a8c152ca8136aad100a02f8ba6d1b6"},
+ {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-win32.whl", hash = "sha256:f4e97a1cf0b7a30af9e1d9dad10a5671157b9acee790d9e26996391f49b965a2"},
+ {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-win_amd64.whl", hash = "sha256:090782b5fb9d98df96509eecdbcaffd037d47389a89492320280d52f91330d78"},
+ {file = "ruamel.yaml.clib-0.2.14-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:7df6f6e9d0e33c7b1d435defb185095386c469109de723d514142632a7b9d07f"},
+ {file = "ruamel.yaml.clib-0.2.14-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:70eda7703b8126f5e52fcf276e6c0f40b0d314674f896fc58c47b0aef2b9ae83"},
+ {file = "ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a0cb71ccc6ef9ce36eecb6272c81afdc2f565950cdcec33ae8e6cd8f7fc86f27"},
+ {file = "ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e7cb9ad1d525d40f7d87b6df7c0ff916a66bc52cb61b66ac1b2a16d0c1b07640"},
+ {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:18c041b28f3456ddef1f1951d4492dbebe0f8114157c1b3c981a4611c2020792"},
+ {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:d8354515ab62f95a07deaf7f845886cc50e2f345ceab240a3d2d09a9f7d77853"},
+ {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:275f938692013a3883edbd848edde6d9f26825d65c9a2eb1db8baa1adc96a05d"},
+ {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16a60d69f4057ad9a92f3444e2367c08490daed6428291aa16cefb445c29b0e9"},
+ {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ac5ff9425d8acb8f59ac5b96bcb7fd3d272dc92d96a7c730025928ffcc88a7a"},
+ {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e1d1735d97fd8a48473af048739379975651fab186f8a25a9f683534e6904179"},
+ {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:83bbd8354f6abb3fdfb922d1ed47ad8d1db3ea72b0523dac8d07cdacfe1c0fcf"},
+ {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:808c7190a0fe7ae7014c42f73897cf8e9ef14ff3aa533450e51b1e72ec5239ad"},
+ {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-win32.whl", hash = "sha256:6d5472f63a31b042aadf5ed28dd3ef0523da49ac17f0463e10fda9c4a2773352"},
+ {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-win_amd64.whl", hash = "sha256:8dd3c2cc49caa7a8d64b67146462aed6723a0495e44bf0aa0a2e94beaa8432f6"},
+ {file = "ruamel.yaml.clib-0.2.14.tar.gz", hash = "sha256:803f5044b13602d58ea378576dd75aa759f52116a0232608e8fdada4da33752e"},
]
[[package]]
name = "ruff"
-version = "0.13.0"
+version = "0.14.0"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
groups = ["lint-and-format"]
files = [
- {file = "ruff-0.13.0-py3-none-linux_armv6l.whl", hash = "sha256:137f3d65d58ee828ae136a12d1dc33d992773d8f7644bc6b82714570f31b2004"},
- {file = "ruff-0.13.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:21ae48151b66e71fd111b7d79f9ad358814ed58c339631450c66a4be33cc28b9"},
- {file = "ruff-0.13.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:64de45f4ca5441209e41742d527944635a05a6e7c05798904f39c85bafa819e3"},
- {file = "ruff-0.13.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b2c653ae9b9d46e0ef62fc6fbf5b979bda20a0b1d2b22f8f7eb0cde9f4963b8"},
- {file = "ruff-0.13.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4cec632534332062bc9eb5884a267b689085a1afea9801bf94e3ba7498a2d207"},
- {file = "ruff-0.13.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dcd628101d9f7d122e120ac7c17e0a0f468b19bc925501dbe03c1cb7f5415b24"},
- {file = "ruff-0.13.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afe37db8e1466acb173bb2a39ca92df00570e0fd7c94c72d87b51b21bb63efea"},
- {file = "ruff-0.13.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f96a8d90bb258d7d3358b372905fe7333aaacf6c39e2408b9f8ba181f4b6ef2"},
- {file = "ruff-0.13.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b5e3d883e4f924c5298e3f2ee0f3085819c14f68d1e5b6715597681433f153"},
- {file = "ruff-0.13.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03447f3d18479df3d24917a92d768a89f873a7181a064858ea90a804a7538991"},
- {file = "ruff-0.13.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:fbc6b1934eb1c0033da427c805e27d164bb713f8e273a024a7e86176d7f462cf"},
- {file = "ruff-0.13.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a8ab6a3e03665d39d4a25ee199d207a488724f022db0e1fe4002968abdb8001b"},
- {file = "ruff-0.13.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2a5c62f8ccc6dd2fe259917482de7275cecc86141ee10432727c4816235bc41"},
- {file = "ruff-0.13.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b7b85ca27aeeb1ab421bc787009831cffe6048faae08ad80867edab9f2760945"},
- {file = "ruff-0.13.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:79ea0c44a3032af768cabfd9616e44c24303af49d633b43e3a5096e009ebe823"},
- {file = "ruff-0.13.0-py3-none-win32.whl", hash = "sha256:4e473e8f0e6a04e4113f2e1de12a5039579892329ecc49958424e5568ef4f768"},
- {file = "ruff-0.13.0-py3-none-win_amd64.whl", hash = "sha256:48e5c25c7a3713eea9ce755995767f4dcd1b0b9599b638b12946e892123d1efb"},
- {file = "ruff-0.13.0-py3-none-win_arm64.whl", hash = "sha256:ab80525317b1e1d38614addec8ac954f1b3e662de9d59114ecbf771d00cf613e"},
- {file = "ruff-0.13.0.tar.gz", hash = "sha256:5b4b1ee7eb35afae128ab94459b13b2baaed282b1fb0f472a73c82c996c8ae60"},
+ {file = "ruff-0.14.0-py3-none-linux_armv6l.whl", hash = "sha256:58e15bffa7054299becf4bab8a1187062c6f8cafbe9f6e39e0d5aface455d6b3"},
+ {file = "ruff-0.14.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:838d1b065f4df676b7c9957992f2304e41ead7a50a568185efd404297d5701e8"},
+ {file = "ruff-0.14.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:703799d059ba50f745605b04638fa7e9682cc3da084b2092feee63500ff3d9b8"},
+ {file = "ruff-0.14.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ba9a8925e90f861502f7d974cc60e18ca29c72bb0ee8bfeabb6ade35a3abde7"},
+ {file = "ruff-0.14.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e41f785498bd200ffc276eb9e1570c019c1d907b07cfb081092c8ad51975bbe7"},
+ {file = "ruff-0.14.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30a58c087aef4584c193aebf2700f0fbcfc1e77b89c7385e3139956fa90434e2"},
+ {file = "ruff-0.14.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f8d07350bc7af0a5ce8812b7d5c1a7293cf02476752f23fdfc500d24b79b783c"},
+ {file = "ruff-0.14.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eec3bbbf3a7d5482b5c1f42d5fc972774d71d107d447919fca620b0be3e3b75e"},
+ {file = "ruff-0.14.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16b68e183a0e28e5c176d51004aaa40559e8f90065a10a559176713fcf435206"},
+ {file = "ruff-0.14.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb732d17db2e945cfcbbc52af0143eda1da36ca8ae25083dd4f66f1542fdf82e"},
+ {file = "ruff-0.14.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:c958f66ab884b7873e72df38dcabee03d556a8f2ee1b8538ee1c2bbd619883dd"},
+ {file = "ruff-0.14.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7eb0499a2e01f6e0c285afc5bac43ab380cbfc17cd43a2e1dd10ec97d6f2c42d"},
+ {file = "ruff-0.14.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c63b2d99fafa05efca0ab198fd48fa6030d57e4423df3f18e03aa62518c565f"},
+ {file = "ruff-0.14.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:668fce701b7a222f3f5327f86909db2bbe99c30877c8001ff934c5413812ac02"},
+ {file = "ruff-0.14.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a86bf575e05cb68dcb34e4c7dfe1064d44d3f0c04bbc0491949092192b515296"},
+ {file = "ruff-0.14.0-py3-none-win32.whl", hash = "sha256:7450a243d7125d1c032cb4b93d9625dea46c8c42b4f06c6b709baac168e10543"},
+ {file = "ruff-0.14.0-py3-none-win_amd64.whl", hash = "sha256:ea95da28cd874c4d9c922b39381cbd69cb7e7b49c21b8152b014bd4f52acddc2"},
+ {file = "ruff-0.14.0-py3-none-win_arm64.whl", hash = "sha256:f42c9495f5c13ff841b1da4cb3c2a42075409592825dada7c5885c2c844ac730"},
+ {file = "ruff-0.14.0.tar.gz", hash = "sha256:62ec8969b7510f77945df916de15da55311fade8d6050995ff7f680afe582c57"},
]
[[package]]
@@ -2092,14 +2188,14 @@ files = [
[[package]]
name = "starlette"
-version = "0.47.3"
+version = "0.48.0"
description = "The little ASGI library that shines."
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
- {file = "starlette-0.47.3-py3-none-any.whl", hash = "sha256:89c0778ca62a76b826101e7c709e70680a1699ca7da6b44d38eb0a7e61fe4b51"},
- {file = "starlette-0.47.3.tar.gz", hash = "sha256:6bc94f839cc176c4858894f1f8908f0ab79dfec1a6b8402f6da9be26ebea52e9"},
+ {file = "starlette-0.48.0-py3-none-any.whl", hash = "sha256:0764ca97b097582558ecb498132ed0c7d942f233f365b86ba37770e026510659"},
+ {file = "starlette-0.48.0.tar.gz", hash = "sha256:7e8cee469a8ab2352911528110ce9088fdc6a37d9876926e73da7ce4aa4c7a46"},
]
[package.dependencies]
@@ -2116,7 +2212,7 @@ description = "A lil' TOML parser"
optional = false
python-versions = ">=3.8"
groups = ["main", "dev", "lint-and-format", "type-checking"]
-markers = "python_version < \"3.11\""
+markers = "python_version == \"3.10\""
files = [
{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"},
@@ -2178,14 +2274,14 @@ files = [
[[package]]
name = "typer-slim"
-version = "0.17.4"
+version = "0.19.2"
description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
groups = ["lint-and-format"]
files = [
- {file = "typer_slim-0.17.4-py3-none-any.whl", hash = "sha256:b0edb16f7978d945b5bc23c057c2af99e58c0601827bddbbe240b4b52eb364bc"},
- {file = "typer_slim-0.17.4.tar.gz", hash = "sha256:c25430a74e45546df3e76b53bdfd0a1d86569d319f9c387e76e1b25a4dd72a0c"},
+ {file = "typer_slim-0.19.2-py3-none-any.whl", hash = "sha256:1c9cdbbcd5b8d30f4118d3cb7c52dc63438b751903fbd980a35df1dfe10c6c91"},
+ {file = "typer_slim-0.19.2.tar.gz", hash = "sha256:6f601e28fb8249a7507f253e35fb22ccc701403ce99bea6a9923909ddbfcd133"},
]
[package.dependencies]
@@ -2209,14 +2305,14 @@ files = [
[[package]]
name = "types-requests"
-version = "2.32.4.20250809"
+version = "2.32.4.20250913"
description = "Typing stubs for requests"
optional = false
python-versions = ">=3.9"
groups = ["type-checking"]
files = [
- {file = "types_requests-2.32.4.20250809-py3-none-any.whl", hash = "sha256:f73d1832fb519ece02c85b1f09d5f0dd3108938e7d47e7f94bbfa18a6782b163"},
- {file = "types_requests-2.32.4.20250809.tar.gz", hash = "sha256:d8060de1c8ee599311f56ff58010fb4902f462a1470802cf9f6ed27bc46c4df3"},
+ {file = "types_requests-2.32.4.20250913-py3-none-any.whl", hash = "sha256:78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1"},
+ {file = "types_requests-2.32.4.20250913.tar.gz", hash = "sha256:abd6d4f9ce3a9383f269775a9835a4c24e5cd6b9f647d64f88aa4613c33def5d"},
]
[package.dependencies]
@@ -2236,14 +2332,14 @@ files = [
[[package]]
name = "typing-inspection"
-version = "0.4.1"
+version = "0.4.2"
description = "Runtime typing introspection tools"
optional = false
python-versions = ">=3.9"
groups = ["main", "dev"]
files = [
- {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"},
- {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"},
+ {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"},
+ {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"},
]
[package.dependencies]
@@ -2281,14 +2377,14 @@ zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "uvicorn"
-version = "0.35.0"
+version = "0.37.0"
description = "The lightning-fast ASGI server."
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
- {file = "uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a"},
- {file = "uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01"},
+ {file = "uvicorn-0.37.0-py3-none-any.whl", hash = "sha256:913b2b88672343739927ce381ff9e2ad62541f9f8289664fa1d1d3803fa2ce6c"},
+ {file = "uvicorn-0.37.0.tar.gz", hash = "sha256:4115c8add6d3fd536c8ee77f0e14a7fd2ebba939fed9b02583a97f80648f9e13"},
]
[package.dependencies]
diff --git a/pyproject.toml b/pyproject.toml
index 6fb5ec4..4d36031 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name="robotframework-openapitools"
-version = "1.0.2"
+version = "2.0.0b1"
description = "A set of Robot Framework libraries to test APIs for which the OAS is available."
authors = [
{name = "Robin Mackaij", email = "r.a.mackaij@gmail.com"},
@@ -107,7 +107,7 @@ build-backend = "poetry.core.masonry.api"
branch = true
parallel = true
source = ["src/OpenApiDriver", "src/OpenApiLibCore", "src/openapi_libgen"]
-omit = ["src/openapi_libgen/command_line.py"]
+omit = ["src/openapi_libgen/command_line.py", "src/OpenApiLibCore/protocols.py"]
[tool.coverage.report]
exclude_lines = [
@@ -125,6 +125,7 @@ disallow_untyped_defs = true
strict = true
show_error_codes = true
exclude = []
+follow_untyped_imports = true
[[tool.mypy.overrides]]
module = [
From 8dd58ab9c3203352f35626a711867016571bd553 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Mon, 20 Oct 2025 15:14:47 +0000
Subject: [PATCH 14/55] add .robot.tom to gitignore
---
.gitignore | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index 65fd3fe..26adcdd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,9 +25,10 @@ coverage.xml
env/
venv/
-# IDE config
+# IDE config and local tool settings
.vscode/launch.json
.vscode/settings.json
+.robot.toml
# default logs location for the repo
tests/logs
From b916ac5b25f13703f26d7f7dd19b94018bda66b4 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Mon, 20 Oct 2025 15:18:25 +0000
Subject: [PATCH 15/55] Intermediate rename / refactor step
---
src/OpenApiDriver/openapi_executors.py | 20 +--
src/OpenApiDriver/openapidriver.py | 4 +-
.../data_constraints/dto_base.py | 33 +----
.../data_generation/data_generation_core.py | 119 +++++++++--------
.../data_generation/data_invalidation.py | 56 ++++----
.../data_generation/value_utils.py | 19 ++-
.../keyword_logic/path_functions.py | 4 +-
.../keyword_logic/path_invalidation.py | 4 +-
.../keyword_logic/validation.py | 16 +--
src/OpenApiLibCore/models/oas_models.py | 122 +++++++++---------
src/OpenApiLibCore/models/request_data.py | 26 ++--
src/OpenApiLibCore/openapi_libcore.py | 34 ++---
src/OpenApiLibCore/protocols.py | 14 +-
src/OpenApiLibCore/utils/oas_cache.py | 4 +-
src/openapitools_docs/docstrings.py | 2 +-
tests/driver/suites/load_json.robot | 1 +
.../suites/test_endpoint_exceptions.robot | 4 +-
...ml => schema_with_allof_and_nullable.yaml} | 1 +
tests/libcore/suites/test_faker_locale.robot | 4 +-
.../suites/test_get_invalid_json_data.robot | 18 +--
.../test_get_json_data_with_conflict.robot | 10 +-
.../suites/test_get_request_data.robot | 16 +--
.../suites/test_request_data_class.robot | 2 +-
.../suites/test_schema_variations.robot | 12 +-
tests/libcore/unittests/test_dto_utils.py | 4 +-
.../user_implemented/custom_user_mappings.py | 3 +-
26 files changed, 271 insertions(+), 281 deletions(-)
rename tests/files/{schema_with_allof.yaml => schema_with_allof_and_nullable.yaml} (99%)
diff --git a/src/OpenApiDriver/openapi_executors.py b/src/OpenApiDriver/openapi_executors.py
index f40ac6e..cf7aa9f 100644
--- a/src/OpenApiDriver/openapi_executors.py
+++ b/src/OpenApiDriver/openapi_executors.py
@@ -25,7 +25,7 @@
ValidationLevel,
)
from OpenApiLibCore.annotations import JSON
-from OpenApiLibCore.models.oas_models import ArraySchema, ObjectSchema
+from OpenApiLibCore.models.oas_models import ObjectSchema
run_keyword = BuiltIn().run_keyword
default_str_mapping: Mapping[str, str] = MappingProxyType({})
@@ -51,7 +51,7 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
response_validation: ValidationLevel = ValidationLevel.WARN,
disable_server_validation: bool = True,
mappings_path: str | Path = "",
- invalid_property_default_response: int = 422,
+ invalid_data_default_response: int = 422,
default_id_property_name: str = "id",
faker_locale: str | list[str] = "",
require_body_for_invalid_url: bool = False,
@@ -75,7 +75,7 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
disable_server_validation=disable_server_validation,
mappings_path=mappings_path,
default_id_property_name=default_id_property_name,
- invalid_property_default_response=invalid_property_default_response,
+ invalid_data_default_response=invalid_data_default_response,
faker_locale=faker_locale,
require_body_for_invalid_url=require_body_for_invalid_url,
recursion_limit=recursion_limit,
@@ -217,9 +217,13 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
}
invalidation_keywords = []
- if request_data.dto.get_body_relations_for_error_code(status_code):
+ if request_data.constraint_mapping.get_body_relations_for_error_code(
+ status_code
+ ):
invalidation_keywords.append("get_invalid_body_data")
- if request_data.dto.get_parameter_relations_for_error_code(status_code):
+ if request_data.constraint_mapping.get_parameter_relations_for_error_code(
+ status_code
+ ):
invalidation_keywords.append("get_invalidated_parameters")
if invalidation_keywords:
if (
@@ -234,7 +238,7 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
)
# if there are no relations to invalide and the status_code is the default
# response_code for invalid properties, invalidate properties instead
- elif status_code == self.invalid_property_default_response:
+ elif status_code == self.invalid_data_default_response:
if (
request_data.params_that_can_be_invalidated
or request_data.headers_that_can_be_invalidated
@@ -256,7 +260,7 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
)
else:
raise AssertionError(
- f"No Dto mapping found to cause status_code {status_code}."
+ f"No constraint mapping found to cause status_code {status_code}."
)
run_keyword(
"perform_validated_request",
@@ -325,6 +329,6 @@ def get_original_data(self, url: str) -> dict[str, JSON]:
@staticmethod
def get_keyword_names() -> list[str]:
"""Curated keywords for libdoc and libspec."""
- if getenv("HIDE_INHERITED_KEYWORDS") == "true":
+ if getenv("HIDE_INHERITED_KEYWORDS") == "true": # pragma: no cover
return KEYWORD_NAMES
return KEYWORD_NAMES + LIBCORE_KEYWORD_NAMES
diff --git a/src/OpenApiDriver/openapidriver.py b/src/OpenApiDriver/openapidriver.py
index 5f3026d..e0c9323 100644
--- a/src/OpenApiDriver/openapidriver.py
+++ b/src/OpenApiDriver/openapidriver.py
@@ -34,7 +34,7 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
response_validation: ValidationLevel = ValidationLevel.WARN,
disable_server_validation: bool = True,
mappings_path: str | Path = "",
- invalid_property_default_response: int = 422,
+ invalid_data_default_response: int = 422,
default_id_property_name: str = "id",
faker_locale: str | list[str] = "",
require_body_for_invalid_url: bool = False,
@@ -64,7 +64,7 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
response_validation=response_validation,
disable_server_validation=disable_server_validation,
mappings_path=mappings_path,
- invalid_property_default_response=invalid_property_default_response,
+ invalid_data_default_response=invalid_data_default_response,
default_id_property_name=default_id_property_name,
faker_locale=faker_locale,
require_body_for_invalid_url=require_body_for_invalid_url,
diff --git a/src/OpenApiLibCore/data_constraints/dto_base.py b/src/OpenApiLibCore/data_constraints/dto_base.py
index 5edb70e..de49a35 100644
--- a/src/OpenApiLibCore/data_constraints/dto_base.py
+++ b/src/OpenApiLibCore/data_constraints/dto_base.py
@@ -5,9 +5,9 @@
"""
from abc import ABC
-from dataclasses import dataclass, fields
+from dataclasses import dataclass
from importlib import import_module
-from typing import Any, Callable
+from typing import Callable
from robot.api import logger
@@ -17,10 +17,9 @@
ResourceRelation,
)
from OpenApiLibCore.protocols import (
- ConstraintMappingType,
- GetIdPropertyNameType,
+ IConstraintMapping,
+ IGetIdPropertyName,
)
-from OpenApiLibCore.utils import parameter_utils
from OpenApiLibCore.utils.id_mapping import dummy_transformer
@@ -94,28 +93,10 @@ def get_body_relations_for_error_code(
]
return relations
- def as_dict(self) -> dict[Any, Any]:
- """Return the dict representation of the Dto."""
- result = {}
-
- for field in fields(self):
- field_name = field.name
- if field_name not in self.__dict__:
- continue
- original_name = parameter_utils.get_oas_name_from_safe_name(field_name)
- result[original_name] = getattr(self, field_name)
-
- return result
-
- def as_list(self) -> list[Any]:
- """Return the list representation of the Dto."""
- items = self.as_dict()
- return [items] if items else []
-
def get_constraint_mapping_dict(
mappings_module_name: str,
-) -> dict[tuple[str, str], ConstraintMappingType]:
+) -> dict[tuple[str, str], IConstraintMapping]:
try:
mappings_module = import_module(mappings_module_name)
return mappings_module.DTO_MAPPING
@@ -127,7 +108,7 @@ def get_constraint_mapping_dict(
def get_path_mapping_dict(
mappings_module_name: str,
-) -> dict[str, ConstraintMappingType]:
+) -> dict[str, IConstraintMapping]:
try:
mappings_module = import_module(mappings_module_name)
return mappings_module.PATH_MAPPING
@@ -139,7 +120,7 @@ def get_path_mapping_dict(
def get_id_property_name(
mappings_module_name: str, default_id_property_name: str
-) -> GetIdPropertyNameType:
+) -> IGetIdPropertyName:
return GetIdPropertyName(
mappings_module_name=mappings_module_name,
default_id_property_name=default_id_property_name,
diff --git a/src/OpenApiLibCore/data_generation/data_generation_core.py b/src/OpenApiLibCore/data_generation/data_generation_core.py
index 2b02121..e4bce53 100644
--- a/src/OpenApiLibCore/data_generation/data_generation_core.py
+++ b/src/OpenApiLibCore/data_generation/data_generation_core.py
@@ -29,7 +29,7 @@
PropertyValueConstraint,
ResourceRelation,
)
-from OpenApiLibCore.protocols import ConstraintMappingType
+from OpenApiLibCore.protocols import IConstraintMapping
from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
@@ -39,7 +39,7 @@ def get_request_data(
openapi_spec: OpenApiObject,
) -> RequestData:
method = method.lower()
- dto_cls_name = get_dto_cls_name(path=path, method=method)
+ mapping_cls_name = get_mapping_cls_name(path=path, method=method)
# The path can contain already resolved Ids that have to be matched
# against the parametrized paths in the paths section.
spec_path = _path_functions.get_parametrized_path(
@@ -50,26 +50,26 @@ def get_request_data(
operation_spec: OperationObject | None = getattr(path_item, method)
if operation_spec is None:
raise AttributeError
- dto_class = operation_spec.dto
+ constraint_mapping = operation_spec.constraint_mapping
except AttributeError:
logger.info(
f"method '{method}' not supported on '{spec_path}, using empty spec."
)
operation_spec = OperationObject(operationId="")
- dto_class = None
+ constraint_mapping = None
parameters, params, headers = get_request_parameters(
- dto_class=dto_class, method_spec=operation_spec
+ constraint_mapping=constraint_mapping, method_spec=operation_spec
)
if operation_spec.requestBody is None:
- dto_instance = _get_dto_instance_for_empty_body(
- dto_class=dto_class,
- dto_cls_name=dto_cls_name,
+ constraint_mapping = _get_mapping_dataclass_for_empty_body(
+ constraint_mapping=constraint_mapping,
+ mapping_cls_name=mapping_cls_name,
method_spec=operation_spec,
)
return RequestData(
valid_data=None,
- dto=dto_instance,
+ constraint_mapping=constraint_mapping,
parameters=parameters,
params=params,
headers=headers,
@@ -85,25 +85,21 @@ def get_request_data(
headers.update({"content-type": operation_spec.requestBody.mime_type})
- if isinstance(body_schema, UnionTypeSchema):
- resolved_schemas = body_schema.resolved_schemas
- body_schema = choice(resolved_schemas)
-
valid_data = get_valid_json_data(
schema=body_schema,
- dto_class=dto_class,
+ constraint_mapping=constraint_mapping,
operation_id=operation_spec.operationId,
)
- dto_instance = _get_dto_instance_from_dto_data(
+ constraint_mapping = _get_mapping_dataclass_from_valid_data(
schema=body_schema,
- dto_class=dto_class,
- dto_data=valid_data,
+ constraint_mapping=constraint_mapping,
+ valid_data=valid_data,
method_spec=operation_spec,
- dto_cls_name=dto_cls_name,
+ mapping_cls_name=mapping_cls_name,
)
return RequestData(
valid_data=valid_data,
- dto=dto_instance,
+ constraint_mapping=constraint_mapping,
body_schema=body_schema,
parameters=parameters,
params=params,
@@ -111,76 +107,77 @@ def get_request_data(
)
-def _get_dto_instance_for_empty_body(
- dto_class: type[ConstraintMappingType] | None,
- dto_cls_name: str,
+def _get_mapping_dataclass_for_empty_body(
+ constraint_mapping: type[IConstraintMapping] | None,
+ mapping_cls_name: str,
method_spec: OperationObject,
-) -> type[Dto]:
- cls_name = method_spec.operationId if method_spec.operationId else dto_cls_name
- base = dto_class if dto_class else Dto
- dto_class_ = make_dataclass(
+) -> type[IConstraintMapping]:
+ cls_name = method_spec.operationId if method_spec.operationId else mapping_cls_name
+ base = constraint_mapping if constraint_mapping else Dto
+ mapping_class = make_dataclass(
cls_name=cls_name,
fields=[],
bases=(base,),
)
- return dto_class_
+ return mapping_class
-def _get_dto_instance_from_dto_data(
+def _get_mapping_dataclass_from_valid_data(
schema: ResolvedSchemaObjectTypes,
- dto_class: type[ConstraintMappingType] | None,
- dto_data: JSON,
+ constraint_mapping: type[IConstraintMapping] | None,
+ valid_data: JSON,
method_spec: OperationObject,
- dto_cls_name: str,
-) -> type[Dto]:
+ mapping_cls_name: str,
+) -> type[IConstraintMapping]:
if not isinstance(schema, (ObjectSchema, ArraySchema)):
- return _get_dto_instance_for_empty_body(
- dto_class=dto_class, dto_cls_name=dto_cls_name, method_spec=method_spec
+ return _get_mapping_dataclass_for_empty_body(
+ constraint_mapping=constraint_mapping,
+ mapping_cls_name=mapping_cls_name,
+ method_spec=method_spec,
)
if isinstance(schema, ArraySchema):
- if not dto_data or not isinstance(dto_data, list):
- return _get_dto_instance_for_empty_body(
- dto_class=dto_class, dto_cls_name=dto_cls_name, method_spec=method_spec
+ if not valid_data or not isinstance(valid_data, list):
+ return _get_mapping_dataclass_for_empty_body(
+ constraint_mapping=constraint_mapping,
+ mapping_cls_name=mapping_cls_name,
+ method_spec=method_spec,
)
- first_item_data = dto_data[0]
+ first_item_data = valid_data[0]
item_object_schema = schema.items
if isinstance(item_object_schema, UnionTypeSchema):
resolved_schemas = item_object_schema.resolved_schemas
item_object_schema = choice(resolved_schemas)
- item_dto = _get_dto_instance_from_dto_data(
+ mapping_dataclass = _get_mapping_dataclass_from_valid_data(
schema=item_object_schema,
- dto_class=dto_class,
- dto_data=first_item_data,
+ constraint_mapping=constraint_mapping,
+ valid_data=first_item_data,
method_spec=method_spec,
- dto_cls_name=dto_cls_name,
+ mapping_cls_name=mapping_cls_name,
)
- return item_dto
+ return mapping_dataclass
- assert isinstance(dto_data, dict), (
- "Data consistency error: schema is of type ObjectSchema but dto_data is not a dict."
+ assert isinstance(valid_data, dict), (
+ "Data consistency error: schema is of type ObjectSchema but valid_data is not a dict."
)
- fields = get_fields_from_dto_data(schema, dto_data)
- cls_name = method_spec.operationId if method_spec.operationId else dto_cls_name
- base = dto_class if dto_class else Dto
- dto_class_ = make_dataclass(
+ fields = get_dataclass_fields(object_schema=schema, valid_data=valid_data)
+ cls_name = method_spec.operationId if method_spec.operationId else mapping_cls_name
+ base = constraint_mapping if constraint_mapping else Dto
+ mapping_dataclass = make_dataclass(
cls_name=cls_name,
fields=fields,
bases=(base,),
)
- dto_data = {
- get_safe_name_for_oas_name(key): value for key, value in dto_data.items()
- }
- return cast(Dto, dto_class_(**dto_data))
+ return mapping_dataclass
-def get_fields_from_dto_data(
- object_schema: ObjectSchema, dto_data: dict[str, JSON]
+def get_dataclass_fields(
+ object_schema: ObjectSchema, valid_data: dict[str, JSON]
) -> list[tuple[str, type[object], Field[object]]]:
- """Get a dataclasses fields list based on the content_schema and dto_data."""
+ """Get a dataclasses fields list based on the object_schema and valid_data."""
fields: list[tuple[str, type[object], Field[object]]] = []
- for key, value in dto_data.items():
+ for key, value in valid_data.items():
safe_key = get_safe_name_for_oas_name(key)
if key in object_schema.required:
# The fields list is used to create a dataclass, so non-default fields
@@ -193,7 +190,7 @@ def get_fields_from_dto_data(
return fields
-def get_dto_cls_name(path: str, method: str) -> str:
+def get_mapping_cls_name(path: str, method: str) -> str:
method = method.capitalize()
path = path.translate({ord(i): None for i in "{}"})
path_parts = path.split("/")
@@ -203,11 +200,13 @@ def get_dto_cls_name(path: str, method: str) -> str:
def get_request_parameters(
- dto_class: type[ConstraintMappingType] | None, method_spec: OperationObject
+ constraint_mapping: type[IConstraintMapping] | None, method_spec: OperationObject
) -> tuple[list[ParameterObject], dict[str, Any], dict[str, str]]:
"""Get the methods parameter spec and params and headers with valid data."""
parameters = method_spec.parameters if method_spec.parameters else []
- parameter_relations = dto_class.get_parameter_relations() if dto_class else []
+ parameter_relations = (
+ constraint_mapping.get_parameter_relations() if constraint_mapping else []
+ )
query_params = [p for p in parameters if p.in_ == "query"]
header_params = [p for p in parameters if p.in_ == "header"]
params = get_parameter_data(query_params, parameter_relations)
diff --git a/src/OpenApiLibCore/data_generation/data_invalidation.py b/src/OpenApiLibCore/data_generation/data_invalidation.py
index ef65034..d2e9ee9 100644
--- a/src/OpenApiLibCore/data_generation/data_invalidation.py
+++ b/src/OpenApiLibCore/data_generation/data_invalidation.py
@@ -38,10 +38,12 @@ def get_invalid_body_data(
method: str,
status_code: int,
request_data: RequestData,
- invalid_property_default_response: int,
+ invalid_data_default_response: int,
) -> dict[str, JSON] | list[JSON]:
method = method.lower()
- data_relations = request_data.dto.get_body_relations_for_error_code(status_code)
+ data_relations = request_data.constraint_mapping.get_body_relations_for_error_code(
+ status_code
+ )
if not data_relations:
if request_data.body_schema is None:
raise ValueError(
@@ -54,21 +56,21 @@ def get_invalid_body_data(
if isinstance(request_data.body_schema, ArraySchema):
if not isinstance(request_data.valid_data, list):
raise ValueError("Type of valid_data does not match body_schema type.")
- invalid_item_data = request_data.body_schema.get_invalidated_data(
+ invalid_item_data = request_data.body_schema.get_invalid_data(
valid_data=request_data.valid_data,
- constraint_mapping=request_data.dto,
+ constraint_mapping=request_data.constraint_mapping,
status_code=status_code,
- invalid_property_default_code=invalid_property_default_response,
+ invalid_property_default_code=invalid_data_default_response,
)
return [invalid_item_data]
if not isinstance(request_data.valid_data, dict):
raise ValueError("Type of valid_data does not match body_schema type.")
- json_data = request_data.body_schema.get_invalidated_data(
+ json_data = request_data.body_schema.get_invalid_data(
valid_data=request_data.valid_data,
- constraint_mapping=request_data.dto,
+ constraint_mapping=request_data.constraint_mapping,
status_code=status_code,
- invalid_property_default_code=invalid_property_default_response,
+ invalid_property_default_code=invalid_data_default_response,
)
return json_data
@@ -79,7 +81,7 @@ def get_invalid_body_data(
url,
method,
request_data.valid_data,
- request_data.dto,
+ request_data.constraint_mapping,
status_code,
)
elif isinstance(resource_relation, IdReference):
@@ -90,23 +92,25 @@ def get_invalid_body_data(
raise ValueError(
"Failed to invalidate: request_data does not contain a body_schema."
)
- json_data = request_data.body_schema.get_invalidated_data(
+ json_data = request_data.body_schema.get_invalid_data(
valid_data=request_data.valid_data,
- constraint_mapping=request_data.dto,
+ constraint_mapping=request_data.constraint_mapping,
status_code=status_code,
- invalid_property_default_code=invalid_property_default_response,
+ invalid_property_default_code=invalid_data_default_response,
)
return json_data
def get_invalidated_parameters(
- status_code: int, request_data: RequestData, invalid_property_default_response: int
+ status_code: int, request_data: RequestData, invalid_data_default_response: int
) -> tuple[dict[str, JSON], dict[str, JSON]]:
if not request_data.parameters:
raise ValueError("No params or headers to invalidate.")
# ensure the status_code can be triggered
- relations = request_data.dto.get_parameter_relations_for_error_code(status_code)
+ relations = request_data.constraint_mapping.get_parameter_relations_for_error_code(
+ status_code
+ )
relations_for_status_code = [
r
for r in relations
@@ -120,14 +124,14 @@ def get_invalidated_parameters(
}
relation_property_names = {r.property_name for r in relations_for_status_code}
if not relation_property_names:
- if status_code != invalid_property_default_response:
+ if status_code != invalid_data_default_response:
raise ValueError(f"No relations to cause status_code {status_code} found.")
# ensure we're not modifying mutable properties
params = deepcopy(request_data.params)
headers = deepcopy(request_data.headers)
- if status_code == invalid_property_default_response:
+ if status_code == invalid_data_default_response:
# take the params and headers that can be invalidated based on data type
# and expand the set with properties that can be invalided by relations
parameter_names = set(request_data.params_that_can_be_invalidated).union(
@@ -142,8 +146,8 @@ def get_invalidated_parameters(
# non-default status_codes can only be the result of a Relation
parameter_names = relation_property_names
- # Dto mappings may contain generic mappings for properties that are not present
- # in this specific schema
+ # Constraint mappings may contain generic mappings for properties that are
+ # not present in this specific schema
request_data_parameter_names = [p.name for p in request_data.parameters]
additional_relation_property_names = {
n for n in relation_property_names if n not in request_data_parameter_names
@@ -225,11 +229,10 @@ def get_invalidated_parameters(
raise ValueError(f"No schema defined for parameter: {parameter_data}.")
if isinstance(value_schema, UnionTypeSchema):
- # FIXME: extra handling may be needed in case of values_from_constraint
value_schema = choice(value_schema.resolved_schemas)
invalid_value = value_schema.get_invalid_value(
- current_value=valid_value,
+ valid_value=valid_value,
values_from_constraint=values_from_constraint,
)
logger.debug(f"{parameter_to_invalidate} changed to {invalid_value}")
@@ -285,12 +288,14 @@ def get_json_data_with_conflict(
base_url: str,
method: str,
json_data: dict[str, JSON],
- dto: Dto,
+ constraint_mapping: type[Dto],
conflict_status_code: int,
) -> dict[str, Any]:
method = method.lower()
unique_property_value_constraints = [
- r for r in dto.get_relations() if isinstance(r, UniquePropertyValueConstraint)
+ r
+ for r in constraint_mapping.get_relations()
+ if isinstance(r, UniquePropertyValueConstraint)
]
for relation in unique_property_value_constraints:
json_data[relation.property_name] = relation.value
@@ -298,8 +303,8 @@ def get_json_data_with_conflict(
if method in ["patch", "put"]:
post_url_parts = url.split("/")[:-1]
post_url = "/".join(post_url_parts)
- # the PATCH or PUT may use a different dto than required for POST
- # so a valid POST dto must be constructed
+ # the PATCH or PUT may use a different constraint_mapping than required for
+ # POST so valid POST data must be constructed
path = post_url.replace(base_url, "")
request_data: RequestData = run_keyword("get_request_data", path, "post")
post_json = request_data.valid_data
@@ -326,5 +331,6 @@ def get_json_data_with_conflict(
)
return json_data
raise ValueError(
- f"No UniquePropertyValueConstraint in the get_relations list on dto {dto}."
+ f"No UniquePropertyValueConstraint in the get_relations list on "
+ f"constraint_mapping {constraint_mapping}."
)
diff --git a/src/OpenApiLibCore/data_generation/value_utils.py b/src/OpenApiLibCore/data_generation/value_utils.py
index 7949892..8f46130 100644
--- a/src/OpenApiLibCore/data_generation/value_utils.py
+++ b/src/OpenApiLibCore/data_generation/value_utils.py
@@ -2,13 +2,15 @@
"""Utility module with functions to handle OpenAPI value types and restrictions."""
from copy import deepcopy
-from typing import Any, cast, overload
+from typing import Any, TypeVar, cast, overload
from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.models import IGNORE, Ignore
+O = TypeVar("O")
-def json_type_name_of_python_type(python_type: Any) -> str:
+
+def json_type_name_of_python_type(python_type: type) -> str:
"""Return the JSON type name for supported Python types."""
if python_type == str:
return "string"
@@ -47,8 +49,8 @@ def python_type_by_json_type_name(type_name: str) -> type:
def get_invalid_value_from_constraint(
- values_from_constraint: list[JSON | Ignore], value_type: str
-) -> JSON | Ignore:
+ values_from_constraint: list[O | Ignore], value_type: str
+) -> O | Ignore:
"""
Return a value of the same type as the values in the values_from_constraints that
is not in the values_from_constraints, if possible. Otherwise returns None.
@@ -78,12 +80,9 @@ def get_invalid_value_from_constraint(
for key, value in valid_object.items():
python_type_of_value = type(value)
json_type_of_value = json_type_name_of_python_type(python_type_of_value)
- invalid_value = cast(
- JSON,
- get_invalid_value_from_constraint(
- values_from_constraint=[value],
- value_type=json_type_of_value,
- ),
+ invalid_value = get_invalid_value_from_constraint(
+ values_from_constraint=[value],
+ value_type=json_type_of_value,
)
invalid_object[key] = invalid_value
return invalid_object
diff --git a/src/OpenApiLibCore/keyword_logic/path_functions.py b/src/OpenApiLibCore/keyword_logic/path_functions.py
index fb16996..4364e1d 100644
--- a/src/OpenApiLibCore/keyword_logic/path_functions.py
+++ b/src/OpenApiLibCore/keyword_logic/path_functions.py
@@ -73,8 +73,8 @@ def get_valid_url(
f"{path} not found in paths section of the OpenAPI document."
) from None
- dto_class = path_item.dto
- relations = dto_class.get_path_relations() if dto_class else []
+ constraint_mapping = path_item.constraint_mapping
+ relations = constraint_mapping.get_path_relations() if constraint_mapping else []
paths = [p.path for p in relations]
if paths:
url = f"{base_url}{choice(paths)}"
diff --git a/src/OpenApiLibCore/keyword_logic/path_invalidation.py b/src/OpenApiLibCore/keyword_logic/path_invalidation.py
index 532f546..14f1e28 100644
--- a/src/OpenApiLibCore/keyword_logic/path_invalidation.py
+++ b/src/OpenApiLibCore/keyword_logic/path_invalidation.py
@@ -24,8 +24,8 @@ def get_invalidated_url(
f"{path} not found in paths section of the OpenAPI document."
) from None
- dto_class = path_item.dto
- relations = dto_class.get_path_relations() if dto_class else []
+ constraint_mapping = path_item.constraint_mapping
+ relations = constraint_mapping.get_path_relations() if constraint_mapping else []
paths = [
p.invalid_value
for p in relations
diff --git a/src/OpenApiLibCore/keyword_logic/validation.py b/src/OpenApiLibCore/keyword_logic/validation.py
index e900c17..a00c185 100644
--- a/src/OpenApiLibCore/keyword_logic/validation.py
+++ b/src/OpenApiLibCore/keyword_logic/validation.py
@@ -24,7 +24,7 @@
UnionTypeSchema,
)
from OpenApiLibCore.models.request_data import RequestData, RequestValues
-from OpenApiLibCore.protocols import ResponseValidatorType
+from OpenApiLibCore.protocols import IResponseValidator
run_keyword = BuiltIn().run_keyword
@@ -109,10 +109,10 @@ def perform_validated_request(
def validate_response(
path: str,
response: Response,
- response_validator: ResponseValidatorType,
+ response_validator: IResponseValidator,
server_validation_warning_logged: bool,
disable_server_validation: bool,
- invalid_property_default_response: int,
+ invalid_data_default_response: int,
response_validation: str,
openapi_spec: OpenApiObject,
original_data: Mapping[str, Any],
@@ -127,7 +127,7 @@ def validate_response(
response_validator=response_validator,
server_validation_warning_logged=server_validation_warning_logged,
disable_server_validation=disable_server_validation,
- invalid_property_default_response=invalid_property_default_response,
+ invalid_data_default_response=invalid_data_default_response,
response_validation=response_validation,
)
except OpenAPIError as exception:
@@ -310,7 +310,7 @@ def validate_dict_response(
def validate_response_using_validator(
response: Response,
- response_validator: ResponseValidatorType,
+ response_validator: IResponseValidator,
) -> None:
openapi_request = RequestsOpenAPIRequest(response.request)
openapi_response = RequestsOpenAPIResponse(response)
@@ -319,10 +319,10 @@ def validate_response_using_validator(
def _validate_response(
response: Response,
- response_validator: ResponseValidatorType,
+ response_validator: IResponseValidator,
server_validation_warning_logged: bool,
disable_server_validation: bool,
- invalid_property_default_response: int,
+ invalid_data_default_response: int,
response_validation: str,
) -> None:
try:
@@ -354,7 +354,7 @@ def _validate_response(
if disable_server_validation:
return
- if response.status_code == invalid_property_default_response:
+ if response.status_code == invalid_data_default_response:
logger.debug(error_message)
return
if response_validation == ValidationLevel.STRICT:
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index 3ab4fa9..104a379 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -1,6 +1,7 @@
from __future__ import annotations
import base64
+import builtins
from abc import abstractmethod
from collections import ChainMap
from copy import deepcopy
@@ -23,22 +24,21 @@
import rstr
from pydantic import BaseModel, Field, RootModel
from robot.api import logger
-from robot.libraries.BuiltIn import BuiltIn
+from robot.libraries.BuiltIn import BuiltIn # type: ignore[import-untyped]
from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.data_generation.localized_faker import FAKE, fake_string
from OpenApiLibCore.data_generation.value_utils import (
- IGNORE,
get_invalid_value_from_constraint,
python_type_by_json_type_name,
)
-from OpenApiLibCore.models import Ignore
+from OpenApiLibCore.models import IGNORE, Ignore
from OpenApiLibCore.models.resource_relations import (
NOT_SET,
IdDependency,
PropertyValueConstraint,
)
-from OpenApiLibCore.protocols import ConstraintMappingType
+from OpenApiLibCore.protocols import IConstraintMapping
from OpenApiLibCore.utils.id_mapping import dummy_transformer
from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
@@ -49,6 +49,7 @@
SENTINEL = object()
O = TypeVar("O")
+ConstraintMappingType = builtins.type[IConstraintMapping]
class SchemaBase(BaseModel, Generic[O], frozen=True):
@@ -58,9 +59,9 @@ class SchemaBase(BaseModel, Generic[O], frozen=True):
@abstractmethod
def get_valid_value(
self,
- constraint_mapping: type[ConstraintMappingType] | None = None,
+ constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
- ) -> JSON: ...
+ ) -> O: ...
@abstractmethod
def get_values_out_of_bounds(self, current_value: O) -> list[O]: ...
@@ -70,15 +71,15 @@ def get_invalid_value_from_const_or_enum(self) -> O: ...
def get_invalid_value(
self,
- current_value: JSON,
- values_from_constraint: Iterable[JSON] = tuple(),
- ) -> JSON | Ignore:
+ valid_value: O,
+ values_from_constraint: Iterable[O] = tuple(),
+ ) -> O | str | list[JSON] | Ignore:
"""Return a random value that violates the provided value_schema."""
- invalid_values: list[JSON | Ignore] = []
+ invalid_values: list[O | str | list[JSON] | Ignore] = []
value_type = getattr(self, "type")
- if not isinstance(current_value, python_type_by_json_type_name(value_type)):
- current_value = self.get_valid_value()
+ if not isinstance(valid_value, python_type_by_json_type_name(value_type)):
+ valid_value = self.get_valid_value()
if values_from_constraint:
try:
@@ -99,7 +100,7 @@ def get_invalid_value(
# Violate min / max values or length if possible
try:
values_out_of_bounds = self.get_values_out_of_bounds(
- current_value=current_value # type: ignore[arg-type]
+ current_value=valid_value
)
invalid_values += values_out_of_bounds
except ValueError:
@@ -122,7 +123,7 @@ class NullSchema(SchemaBase[None], frozen=True):
def get_valid_value(
self,
- constraint_mapping: type[ConstraintMappingType] | None = None,
+ constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
) -> None:
return None
@@ -149,7 +150,7 @@ class BooleanSchema(SchemaBase[bool], frozen=True):
def get_valid_value(
self,
- constraint_mapping: type[ConstraintMappingType] | None = None,
+ constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
) -> bool:
if self.const is not None:
@@ -185,7 +186,7 @@ class StringSchema(SchemaBase[str], frozen=True):
def get_valid_value(
self,
- constraint_mapping: type[ConstraintMappingType] | None = None,
+ constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
) -> bytes | str:
"""Generate a random string within the min/max length in the schema, if specified."""
@@ -317,7 +318,7 @@ def _min_value(self) -> int:
def get_valid_value(
self,
- constraint_mapping: type[ConstraintMappingType] | None = None,
+ constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
) -> int:
"""Generate a random int within the min/max range of the schema, if specified."""
@@ -420,7 +421,7 @@ def _min_value(self) -> float:
def get_valid_value(
self,
- constraint_mapping: type[ConstraintMappingType] | None = None,
+ constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
) -> float:
"""Generate a random float within the min/max range of the schema, if specified."""
@@ -482,7 +483,7 @@ class ArraySchema(SchemaBase[list[JSON]], frozen=True):
def get_valid_value(
self,
- constraint_mapping: type[ConstraintMappingType] | None = None,
+ constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
) -> list[JSON]:
if self.const is not None:
@@ -543,7 +544,7 @@ def get_invalid_value_from_const_or_enum(self) -> list[JSON]:
return invalid_value
- def get_invalidated_data(
+ def get_invalid_data(
self,
valid_data: list[JSON],
constraint_mapping: ConstraintMappingType,
@@ -578,7 +579,7 @@ def get_invalidated_data(
if valid_data
else self.items.get_valid_value()
)
- invalid_item = self.items.get_invalidated_data(
+ invalid_item = self.items.get_invalid_data(
valid_data=valid_item,
constraint_mapping=constraint_mapping,
status_code=status_code,
@@ -628,7 +629,7 @@ class ObjectSchema(SchemaBase[dict[str, JSON]], frozen=True):
def get_valid_value(
self,
- constraint_mapping: type[ConstraintMappingType] | None = None,
+ constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
) -> dict[str, JSON]:
if self.const is not None:
@@ -640,7 +641,7 @@ def get_valid_value(
json_data: dict[str, Any] = {}
property_names = self._get_property_names_to_process(
- dto_class=constraint_mapping
+ constraint_mapping=constraint_mapping
)
for property_name in property_names:
@@ -651,7 +652,7 @@ def get_valid_value(
json_data[property_name] = get_data_for_property(
property_name=property_name,
property_schema=property_schema,
- dto_class=constraint_mapping,
+ constraint_mapping=constraint_mapping,
operation_id=operation_id,
)
@@ -659,16 +660,16 @@ def get_valid_value(
def _get_property_names_to_process(
self,
- dto_class: type[ConstraintMappingType] | None,
+ constraint_mapping: ConstraintMappingType | None,
) -> list[str]:
property_names = []
properties = {} if self.properties is None else self.properties.root
- for property_name in properties: # type: ignore[union-attr]
+ for property_name in properties:
# register the oas_name
_ = get_safe_name_for_oas_name(property_name)
if constrained_values := get_constrained_values(
- dto_class=dto_class, property_name=property_name
+ constraint_mapping=constraint_mapping, property_name=property_name
):
# do not add properties that are configured to be ignored
if IGNORE in constrained_values: # type: ignore[comparison-overlap]
@@ -715,7 +716,7 @@ def get_invalid_value_from_const_or_enum(self) -> dict[str, JSON]:
raise ValueError
- def get_invalidated_data(
+ def get_invalid_data(
self,
valid_data: dict[str, JSON],
constraint_mapping: ConstraintMappingType,
@@ -723,7 +724,6 @@ def get_invalidated_data(
invalid_property_default_code: int,
) -> dict[str, JSON]:
"""Return a data set with one of the properties set to an invalid value or type."""
- # FIXME: data should be sperated from constraints_mapping
properties: dict[str, JSON] = deepcopy(valid_data)
relations = constraint_mapping.get_body_relations_for_error_code(
@@ -737,8 +737,8 @@ def get_invalidated_data(
raise ValueError(
f"No property can be invalidated to cause status_code {status_code}"
)
- # Remove duplicates, then shuffle the property_names so different properties on
- # the Dto are invalidated when rerunning the test.
+ # Remove duplicates, then shuffle the property_names so different properties in
+ # the data dict are invalidated when rerunning the test.
shuffle(list(set(property_names)))
for property_name in property_names:
# if possible, invalidate a constraint but send otherwise valid data
@@ -807,13 +807,13 @@ def get_invalidated_data(
]
invalid_value = value_schema.get_invalid_value(
- current_value=current_value,
+ valid_value=current_value,
values_from_constraint=values_from_constraint,
)
properties[property_name] = invalid_value
logger.debug(
- f"Property {property_name} changed to {invalid_value!r} (received from "
- f"get_invalid_value)"
+ f"Property {property_name} changed to {invalid_value!r} "
+ f"(received from get_invalid_value)"
)
return properties
logger.warn("get_invalidated_data returned unchanged properties")
@@ -854,7 +854,7 @@ class UnionTypeSchema(SchemaBase[JSON], frozen=True):
def get_valid_value(
self,
- constraint_mapping: type[ConstraintMappingType] | None = None,
+ constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
) -> JSON:
chosen_schema = choice(self.resolved_schemas)
@@ -1014,7 +1014,7 @@ class OperationObject(BaseModel):
parameters: list[ParameterObject] | None = None
requestBody: RequestBodyObject | None = None
responses: dict[str, ResponseObject] = {}
- dto: type[ConstraintMappingType] | None = None
+ constraint_mapping: type[IConstraintMapping] | None = None
class PathItemObject(BaseModel):
@@ -1026,7 +1026,7 @@ class PathItemObject(BaseModel):
summary: str = ""
description: str = ""
parameters: list[ParameterObject] | None = None
- dto: ConstraintMappingType | None = None
+ constraint_mapping: IConstraintMapping | None = None
id_mapper: tuple[str, Callable[[str], str] | Callable[[int], int]] = (
"id",
dummy_transformer,
@@ -1053,19 +1053,19 @@ class OpenApiObject(BaseModel):
# TODO: can this be refactored away?
def get_valid_json_data(
schema: SchemaObjectTypes,
- dto_class: type[ConstraintMappingType] | None,
+ constraint_mapping: type[IConstraintMapping] | None,
operation_id: str | None = None,
) -> JSON:
if isinstance(schema, UnionTypeSchema):
chosen_schema = choice(schema.resolved_schemas)
return get_valid_json_data(
schema=chosen_schema,
- dto_class=dto_class,
+ constraint_mapping=constraint_mapping,
operation_id=operation_id,
)
return schema.get_valid_value(
- constraint_mapping=dto_class, operation_id=operation_id
+ constraint_mapping=constraint_mapping, operation_id=operation_id
)
@@ -1073,26 +1073,26 @@ def get_valid_json_data(
def get_data_for_property(
property_name: str,
property_schema: SchemaObjectTypes,
- dto_class: type[ConstraintMappingType] | None,
+ constraint_mapping: ConstraintMappingType | None,
operation_id: str | None,
) -> JSON:
if constrained_values := get_constrained_values(
- dto_class=dto_class, property_name=property_name
+ constraint_mapping=constraint_mapping, property_name=property_name
):
constrained_value = choice(constrained_values)
- # Check if the chosen value is a nested Dto; since a Dto is never
- # instantiated, we can use isinstance(..., type) for this.
+ # Check if the chosen value is a nested constraint_mapping; since a
+ # mapping is never instantiated, we can use isinstance(..., type) for this.
if isinstance(constrained_value, type):
- return get_value_constrained_by_nested_dto(
+ return get_value_constrained_by_nested_constraint_mapping(
property_schema=property_schema,
- nested_dto_class=constrained_value,
+ nested_constraint_mapping=constrained_value,
operation_id=operation_id,
)
return constrained_value
if (
dependent_id := get_dependent_id(
- dto_class=dto_class,
+ constraint_mapping=constraint_mapping,
property_name=property_name,
operation_id=operation_id,
)
@@ -1101,17 +1101,17 @@ def get_data_for_property(
return get_valid_json_data(
schema=property_schema,
- dto_class=None,
+ constraint_mapping=None,
)
# TODO: move to keyword_logic?
def get_dependent_id(
- dto_class: type[ConstraintMappingType] | None,
+ constraint_mapping: ConstraintMappingType | None,
property_name: str,
operation_id: str | None,
) -> str | int | float | None:
- relations = dto_class.get_relations() if dto_class else []
+ relations = constraint_mapping.get_relations() if constraint_mapping else []
# multiple get paths are possible based on the operation being performed
id_get_paths = [
(d.get_path, d.operation_id)
@@ -1136,32 +1136,36 @@ def get_dependent_id(
return valid_id
-def get_value_constrained_by_nested_dto(
+def get_value_constrained_by_nested_constraint_mapping(
property_schema: SchemaObjectTypes,
- nested_dto_class: type[ConstraintMappingType],
+ nested_constraint_mapping: ConstraintMappingType,
operation_id: str | None,
) -> JSON:
- nested_schema = get_schema_for_nested_dto(property_schema=property_schema)
+ nested_schema = get_schema_for_nested_constraint_mapping(
+ property_schema=property_schema
+ )
nested_value = get_valid_json_data(
schema=nested_schema,
- dto_class=nested_dto_class,
+ constraint_mapping=nested_constraint_mapping,
operation_id=operation_id,
)
return nested_value
-def get_schema_for_nested_dto(property_schema: SchemaObjectTypes) -> SchemaObjectTypes:
+def get_schema_for_nested_constraint_mapping(
+ property_schema: SchemaObjectTypes,
+) -> SchemaObjectTypes:
if isinstance(property_schema, UnionTypeSchema):
chosen_schema = choice(property_schema.resolved_schemas)
- return get_schema_for_nested_dto(chosen_schema)
+ return get_schema_for_nested_constraint_mapping(chosen_schema)
return property_schema
def get_constrained_values(
- dto_class: type[ConstraintMappingType] | None, property_name: str
-) -> list[JSON | type[ConstraintMappingType]]:
- relations = dto_class.get_relations() if dto_class else []
+ constraint_mapping: ConstraintMappingType | None, property_name: str
+) -> list[JSON | ConstraintMappingType]:
+ relations = constraint_mapping.get_relations() if constraint_mapping else []
values_list = [
c.values
for c in relations
diff --git a/src/OpenApiLibCore/models/request_data.py b/src/OpenApiLibCore/models/request_data.py
index 74f1bf2..c31835f 100644
--- a/src/OpenApiLibCore/models/request_data.py
+++ b/src/OpenApiLibCore/models/request_data.py
@@ -4,7 +4,6 @@
from dataclasses import dataclass, field
from functools import cached_property
from random import sample
-from typing import Any
from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.models.oas_models import (
@@ -13,7 +12,7 @@
ResolvedSchemaObjectTypes,
UnionTypeSchema,
)
-from OpenApiLibCore.protocols import ConstraintMappingType
+from OpenApiLibCore.protocols import IConstraintMapping
@dataclass
@@ -27,8 +26,8 @@ class RequestValues:
json_data: JSON = None
def override_body_value(self, name: str, value: JSON) -> None:
- # FIXME: update for non-dict bodies
- if name in self.json_data:
+ # TODO: add support for overriding list body items
+ if isinstance(self.json_data, dict) and name in self.json_data:
self.json_data[name] = value
def override_header_value(self, name: str, value: JSON) -> None:
@@ -48,7 +47,8 @@ def remove_parameters(self, parameters: list[str]) -> None:
for parameter in parameters:
_ = self.params.pop(parameter, None)
_ = self.headers.pop(parameter, None)
- _ = self.json_data.pop(parameter, None)
+ if isinstance(self.json_data, dict):
+ _ = self.json_data.pop(parameter, None)
@dataclass
@@ -56,7 +56,7 @@ class RequestData:
"""Helper class to manage parameters used when making requests."""
valid_data: JSON
- dto: type[ConstraintMappingType]
+ constraint_mapping: type[IConstraintMapping]
body_schema: ResolvedSchemaObjectTypes | None = None
parameters: list[ParameterObject] = field(default_factory=list)
params: dict[str, JSON] = field(default_factory=dict)
@@ -70,7 +70,7 @@ def __post_init__(self) -> None:
@property
def has_optional_properties(self) -> bool:
- """Whether or not the dto data (json data) contains optional properties."""
+ """Whether or not the json data contains optional properties."""
def is_required_property(property_name: str) -> bool:
return property_name in self.required_property_names
@@ -169,9 +169,9 @@ def headers_that_can_be_invalidated(self) -> set[str]:
return result
- def get_required_properties_dict(self) -> dict[str, Any]:
- """Get the json-compatible dto data containing only the required properties."""
- relations = self.dto.get_relations()
+ def get_required_properties_dict(self) -> dict[str, JSON]:
+ """Get the json data containing only the required properties."""
+ relations = self.constraint_mapping.get_relations()
mandatory_properties = [
relation.property_name
for relation in relations
@@ -180,7 +180,7 @@ def get_required_properties_dict(self) -> dict[str, Any]:
required_properties = self.body_schema.required if self.body_schema else []
required_properties.extend(mandatory_properties)
- required_properties_dict: dict[str, Any] = {}
+ required_properties_dict: dict[str, JSON] = {}
if self.valid_data is None:
return required_properties_dict
@@ -189,7 +189,7 @@ def get_required_properties_dict(self) -> dict[str, Any]:
required_properties_dict[key] = value
return required_properties_dict
- def get_minimal_body_dict(self) -> dict[str, Any]:
+ def get_minimal_body_dict(self) -> dict[str, JSON]:
required_properties_dict = self.get_required_properties_dict()
min_properties = 0
@@ -237,7 +237,7 @@ def required_parameter_names(self) -> list[str]:
The names of the mandatory parameters, including the parameters configured to be
treated as mandatory using a PropertyValueConstraint.
"""
- relations = self.dto.get_parameter_relations()
+ relations = self.constraint_mapping.get_parameter_relations()
mandatory_property_names = [
relation.property_name
for relation in relations
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index 9d56de5..bf01b50 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -40,7 +40,7 @@
)
from OpenApiLibCore.models.request_data import RequestData, RequestValues
from OpenApiLibCore.models.resource_relations import IdReference
-from OpenApiLibCore.protocols import ResponseValidatorType
+from OpenApiLibCore.protocols import IResponseValidator
from OpenApiLibCore.utils.oas_cache import PARSER_CACHE, CachedParser
from OpenApiLibCore.utils.parameter_utils import (
get_oas_name_from_safe_name,
@@ -66,7 +66,7 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
response_validation: _validation.ValidationLevel = _validation.ValidationLevel.WARN,
disable_server_validation: bool = True,
mappings_path: str | Path = "",
- invalid_property_default_response: int = 422,
+ invalid_data_default_response: int = 422,
default_id_property_name: str = "id",
faker_locale: str | list[str] = "",
require_body_for_invalid_url: bool = False,
@@ -105,7 +105,7 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
self.extra_headers = extra_headers
self.cookies = cookies
self.proxies = proxies
- self.invalid_property_default_response = invalid_property_default_response
+ self.invalid_data_default_response = invalid_data_default_response
if faker_locale:
FAKE.set_locale(locale=faker_locale)
self.require_body_for_invalid_url = require_body_for_invalid_url
@@ -260,8 +260,8 @@ def get_invalid_body_data(
request_data: RequestData,
) -> dict[str, JSON]:
"""
- Return `json_data` based on the `dto` on the `request_data` that will cause
- the provided `status_code` for the `method` operation on the `url`.
+ Return `json_data` based on the `constraint_mapping` on the `request_data` that
+ will cause the provided `status_code` for the `method` operation on the `url`.
> Note: applicable UniquePropertyValueConstraint and IdReference Relations are
considered before changes to `json_data` are made.
@@ -271,7 +271,7 @@ def get_invalid_body_data(
method=method,
status_code=status_code,
request_data=request_data,
- invalid_property_default_response=self.invalid_property_default_response,
+ invalid_data_default_response=self.invalid_data_default_response,
)
@keyword
@@ -287,7 +287,7 @@ def get_invalidated_parameters(
return _data_invalidation.get_invalidated_parameters(
status_code=status_code,
request_data=request_data,
- invalid_property_default_response=self.invalid_property_default_response,
+ invalid_data_default_response=self.invalid_data_default_response,
)
@keyword
@@ -296,20 +296,20 @@ def get_json_data_with_conflict(
url: str,
method: str,
json_data: dict[str, JSON],
- dto: Dto,
+ constraint_mapping: type[Dto],
conflict_status_code: int,
) -> dict[str, JSON]:
"""
Return `json_data` based on the `UniquePropertyValueConstraint` that must be
- returned by the `get_relations` implementation on the `dto` for the given
- `conflict_status_code`.
+ returned by the `get_relations` implementation on the `constraint_mapping` for
+ the given `conflict_status_code`.
"""
return _data_invalidation.get_json_data_with_conflict(
url=url,
base_url=self.base_url,
method=method,
json_data=json_data,
- dto=dto,
+ constraint_mapping=constraint_mapping,
conflict_status_code=conflict_status_code,
)
@@ -523,7 +523,7 @@ def validate_response(
response_validator=self.response_validator,
server_validation_warning_logged=self._server_validation_warning_logged,
disable_server_validation=self.disable_server_validation,
- invalid_property_default_response=self.invalid_property_default_response,
+ invalid_data_default_response=self.invalid_data_default_response,
response_validation=self.response_validation,
openapi_spec=self.openapi_spec,
original_data=original_data,
@@ -597,7 +597,7 @@ def _attach_user_mappings(self, spec_model: OpenApiObject) -> OpenApiObject:
), data_constraint in self.constraint_mapping_dict.items():
try:
operation_item = getattr(spec_model.paths[path], operation.lower())
- operation_item.dto = data_constraint
+ operation_item.constraint_mapping = data_constraint
except KeyError:
logger.warn(
f"The DTO_MAPPING contains a path that is not found in the OpenAPI spec: {path}"
@@ -606,7 +606,7 @@ def _attach_user_mappings(self, spec_model: OpenApiObject) -> OpenApiObject:
for path, path_constraint in self.path_mapping_dict.items():
try:
path_item = spec_model.paths[path]
- path_item.dto = path_constraint
+ path_item.constraint_mapping = path_constraint
except KeyError:
logger.warn(
f"The PATH_MAPPING contains a path that is not found in the OpenAPI spec: {path}"
@@ -621,7 +621,7 @@ def _attach_user_mappings(self, spec_model: OpenApiObject) -> OpenApiObject:
@cached_property
def response_validator(
self,
- ) -> ResponseValidatorType:
+ ) -> IResponseValidator:
_, _, response_validator = self._load_specs_and_validator()
return response_validator
@@ -651,7 +651,7 @@ def _load_specs_and_validator(
) -> tuple[
ResolvingParser,
Spec,
- ResponseValidatorType,
+ IResponseValidator,
]:
def recursion_limit_handler(
limit: int, # pylint: disable=unused-argument
@@ -696,7 +696,7 @@ def recursion_limit_handler(
}
config = Config(extra_media_type_deserializers=extra_deserializers) # type: ignore[arg-type]
openapi = OpenAPI(spec=validation_spec, config=config)
- response_validator: ResponseValidatorType = openapi.validate_response # type: ignore[assignment]
+ response_validator: IResponseValidator = openapi.validate_response # type: ignore[assignment]
PARSER_CACHE[self._source] = CachedParser(
parser=parser,
diff --git a/src/OpenApiLibCore/protocols.py b/src/OpenApiLibCore/protocols.py
index cdbc806..c884018 100644
--- a/src/OpenApiLibCore/protocols.py
+++ b/src/OpenApiLibCore/protocols.py
@@ -17,25 +17,23 @@
)
-class ResponseValidatorType(Protocol):
+class IResponseValidator(Protocol):
def __call__(
self, request: RequestsOpenAPIRequest, response: RequestsOpenAPIResponse
- ) -> None: ... # pragma: no cover
+ ) -> None: ...
-class GetIdPropertyNameType(Protocol):
+class IGetIdPropertyName(Protocol):
def __init__(
self, mappings_module_name: str, default_id_property_name: str
- ) -> None: ... # pragma: no cover
+ ) -> None: ...
def __call__(
self, path: str
- ) -> tuple[
- str, Callable[[str], str] | Callable[[int], int]
- ]: ... # pragma: no cover
+ ) -> tuple[str, Callable[[str], str] | Callable[[int], int]]: ...
-class ConstraintMappingType(Protocol):
+class IConstraintMapping(Protocol):
@classmethod
def __get_pydantic_core_schema__(
cls, source_type: Any, handler: GetCoreSchemaHandler
diff --git a/src/OpenApiLibCore/utils/oas_cache.py b/src/OpenApiLibCore/utils/oas_cache.py
index a9c4d24..a6b8dfa 100644
--- a/src/OpenApiLibCore/utils/oas_cache.py
+++ b/src/OpenApiLibCore/utils/oas_cache.py
@@ -5,14 +5,14 @@
from openapi_core import Spec
from prance import ResolvingParser
-from OpenApiLibCore.protocols import ResponseValidatorType
+from OpenApiLibCore.protocols import IResponseValidator
@dataclass
class CachedParser:
parser: ResolvingParser
validation_spec: Spec
- response_validator: ResponseValidatorType
+ response_validator: IResponseValidator
PARSER_CACHE: dict[str, CachedParser] = {}
diff --git a/src/openapitools_docs/docstrings.py b/src/openapitools_docs/docstrings.py
index 20e9bff..66db529 100644
--- a/src/openapitools_docs/docstrings.py
+++ b/src/openapitools_docs/docstrings.py
@@ -170,7 +170,7 @@
mappings_path
See the Advanced Use tab for an in-depth explanation.
-invalid_property_default_response
+invalid_data_default_response
The default response code for requests with a JSON body that does not comply
with the schema.
Example: a value outside the specified range or a string value
diff --git a/tests/driver/suites/load_json.robot b/tests/driver/suites/load_json.robot
index b667da0..7db2da8 100644
--- a/tests/driver/suites/load_json.robot
+++ b/tests/driver/suites/load_json.robot
@@ -3,6 +3,7 @@ Library OpenApiDriver
... source=${ROOT}/tests/files/petstore_openapi.json
... ignored_responses=${IGNORED_RESPONSES}
... ignored_testcases=${IGNORED_TESTS}
+... mappings_path=${ROOT}/tests/user_implemented # library can load with invalid mappings_path
Test Template Do Nothing
diff --git a/tests/driver/suites/test_endpoint_exceptions.robot b/tests/driver/suites/test_endpoint_exceptions.robot
index 648ba22..94bf041 100644
--- a/tests/driver/suites/test_endpoint_exceptions.robot
+++ b/tests/driver/suites/test_endpoint_exceptions.robot
@@ -4,7 +4,7 @@ Library OpenApiDriver
... source=http://localhost:8000/openapi.json
... origin=http://localhost:8000
... included_paths=${INCLUDED_PATHS}
-... invalid_property_default_response=400
+... invalid_data_default_response=400
Test Template Validate Test Endpoint Keyword
@@ -25,6 +25,6 @@ Validate Test Endpoint Keyword
Run Keyword And Expect Error Response status_code 401 was not 200.
... Test Endpoint path=${path} method=${method} status_code=${status_code}
ELSE
- Run Keyword And Expect Error No Dto mapping found to cause status_code ${status_code}.
+ Run Keyword And Expect Error No constraint mapping found to cause status_code ${status_code}.
... Test Endpoint path=${path} method=${method} status_code=${status_code}
END
diff --git a/tests/files/schema_with_allof.yaml b/tests/files/schema_with_allof_and_nullable.yaml
similarity index 99%
rename from tests/files/schema_with_allof.yaml
rename to tests/files/schema_with_allof_and_nullable.yaml
index d2d9232..cda2158 100644
--- a/tests/files/schema_with_allof.yaml
+++ b/tests/files/schema_with_allof_and_nullable.yaml
@@ -69,6 +69,7 @@ components:
description: The publication date of the work.
type: string
format: date
+ nullable: true
Images:
title: images
type: object
diff --git a/tests/libcore/suites/test_faker_locale.robot b/tests/libcore/suites/test_faker_locale.robot
index 70187ca..a63f545 100644
--- a/tests/libcore/suites/test_faker_locale.robot
+++ b/tests/libcore/suites/test_faker_locale.robot
@@ -1,7 +1,7 @@
*** Settings ***
Variables ${ROOT}/tests/variables.py
Library OpenApiLibCore
-... source=${ROOT}/tests/files/schema_with_allof.yaml
+... source=${ROOT}/tests/files/schema_with_allof_and_nullable.yaml
... origin=${ORIGIN}
... base_path=${EMPTY}
... faker_locale=zh_CN
@@ -15,4 +15,4 @@ ${ORIGIN} http://localhost:8000
Test Get Request Data For Schema With allOf
${request_data}= Get Request Data path=/hypermedia method=post
# this regex should match all characters in the simplified Chinese character set
- Should Match Regexp ${request_data.dto.title} ^[\u4E00-\u9FA5]+$
+ Should Match Regexp ${request_data.valid_data}[title] ^[\u4E00-\u9FA5]+$
diff --git a/tests/libcore/suites/test_get_invalid_json_data.robot b/tests/libcore/suites/test_get_invalid_json_data.robot
index be0ac0b..7e081d6 100644
--- a/tests/libcore/suites/test_get_invalid_json_data.robot
+++ b/tests/libcore/suites/test_get_invalid_json_data.robot
@@ -34,13 +34,13 @@ Test Get Invalid Body Data Raises If Data Cannot Be Invalidated
Test Get Invalid Body Data Based On Schema
${request_data}= Get Request Data path=/events/ method=post
- Should Be Empty ${request_data.dto.get_body_relations_for_error_code(422)}
+ Should Be Empty ${request_data.constraint_mapping.get_body_relations_for_error_code(422)}
${invalid_json}= Get Invalid Body Data
... url=none
... method=none
... status_code=422
... request_data=${request_data}
- Should Not Be Equal ${invalid_json} ${request_data.dto}
+ Should Not Be Equal ${invalid_json} ${request_data.valid_data}
${response}= Authorized Request
... url=${ORIGIN}/events/ method=post json_data=${invalid_json}
Should Be Equal As Integers ${response.status_code} 422
@@ -52,12 +52,12 @@ Test Get Invalid Body Data For UniquePropertyValueConstraint
... method=post
... status_code=418
... request_data=${request_data}
- Should Not Be Equal ${invalid_json} ${request_data.dto}
+ Should Not Be Equal ${invalid_json} ${request_data.valid_data}
${response}= Authorized Request
... url=${ORIGIN}/wagegroups method=post json_data=${invalid_json}
Should Be Equal As Integers ${response.status_code} 418
-Test Get Invalid Body Data For IdReference
+Test Get Invalid Body Data For IdReference Returns The Original Valid Data
${url}= Get Valid Url path=/wagegroups/{wagegroup_id}
${request_data}= Get Request Data path=/wagegroups/{wagegroup_id} method=delete
${invalid_json}= Get Invalid Body Data
@@ -65,7 +65,7 @@ Test Get Invalid Body Data For IdReference
... method=delete
... status_code=406
... request_data=${request_data}
- Should Not Be Equal ${invalid_json} ${request_data.dto}
+ Should Be Equal ${invalid_json} ${request_data.valid_data}
${response}= Authorized Request
... url=${url} method=delete json_data=${invalid_json}
Should Be Equal As Integers ${response.status_code} 406
@@ -78,19 +78,19 @@ Test Get Invalid Body Data For IdDependency
... method=post
... status_code=451
... request_data=${request_data}
- Should Not Be Equal ${invalid_json} ${request_data.dto}
+ Should Not Be Equal ${invalid_json} ${request_data.valid_data}
${response}= Authorized Request
... url=${url} method=post json_data=${invalid_json}
Should Be Equal As Integers ${response.status_code} 451
-Test Get Invalid Body Data For Dto With Other Relations
+Test Get Invalid Body Data For Constraint Mappings With Other Relations
${request_data}= Get Request Data path=/employees method=post
${invalid_json}= Get Invalid Body Data
... url=${ORIGIN}/employees
... method=post
... status_code=403
... request_data=${request_data}
- Should Not Be Equal ${invalid_json} ${request_data.dto}
+ Should Not Be Equal ${invalid_json} ${request_data.valid_data}
${response}= Authorized Request
... url=${ORIGIN}/employees method=post json_data=${invalid_json}
Should Be Equal As Integers ${response.status_code} 403
@@ -98,7 +98,7 @@ Test Get Invalid Body Data For Dto With Other Relations
Test Get Invalid Body Data Can Invalidate Missing Optional Parameters
${url}= Get Valid Url path=/employees/{emplyee_id}
${request_data}= Get Request Data path=/employees/{emplyee_id} method=patch
- Evaluate ${request_data.dto.__dict__.clear()} is None
+ Evaluate ${request_data.valid_data.clear()} is None
${invalid_json}= Get Invalid Body Data
... url=${url}
... method=patch
diff --git a/tests/libcore/suites/test_get_json_data_with_conflict.robot b/tests/libcore/suites/test_get_json_data_with_conflict.robot
index 54e20a0..15b2cac 100644
--- a/tests/libcore/suites/test_get_json_data_with_conflict.robot
+++ b/tests/libcore/suites/test_get_json_data_with_conflict.robot
@@ -13,7 +13,7 @@ ${ORIGIN} http://localhost:8000
*** Test Cases ***
Test Get Json Data With Conflict Raises For No UniquePropertyValueConstraint
- # No mapping for /wagegroups GET will yield a default dto on the request_data
+ # No mapping for /wagegroups GET will yield a default constraint_mapping on the request_data
${request_data}= Get Request Data path=/wagegroups method=get
${url}= Get Valid Url path=/wagegroups
Run Keyword And Expect Error ValueError: No UniquePropertyValueConstraint*
@@ -21,7 +21,7 @@ Test Get Json Data With Conflict Raises For No UniquePropertyValueConstraint
... url=${url}
... method=post
... json_data=&{EMPTY}
- ... dto=${request_data.dto}
+ ... constraint_mapping=${request_data.constraint_mapping}
... conflict_status_code=418
Test Get Json Data With Conflict For Post Request
@@ -31,7 +31,7 @@ Test Get Json Data With Conflict For Post Request
... url=${url}
... method=post
... json_data=${request_data.valid_data}
- ... dto=${request_data.dto}
+ ... constraint_mapping=${request_data.constraint_mapping}
... conflict_status_code=418
Should Not Be Empty ${invalid_data}
@@ -42,7 +42,7 @@ Test Get Json Data With Conflict For Put Request
... url=${url}
... method=put
... json_data=${request_data.valid_data}
- ... dto=${request_data.dto}
+ ... constraint_mapping=${request_data.constraint_mapping}
... conflict_status_code=418
${response}= Authorized Request
... url=${url} method=put json_data=${invalid_json}
@@ -54,7 +54,7 @@ Test Get Json Data With Conflict For Put Request
# ${invalid_json}= Get Json Data With Conflict
# ... url=${url}
# ... method=put
-# ... dto=${request_data.dto}
+# ... constraint_mapping=${request_data.constraint_mapping}
# ... conflict_status_code=418
# ${response}= Authorized Request
# ... url=${url} method=put json_data=${invalid_json}
diff --git a/tests/libcore/suites/test_get_request_data.robot b/tests/libcore/suites/test_get_request_data.robot
index 966797b..77582ca 100644
--- a/tests/libcore/suites/test_get_request_data.robot
+++ b/tests/libcore/suites/test_get_request_data.robot
@@ -19,7 +19,7 @@ Test Get Request Data For Invalid Method On Endpoint
${request_data}= Get Request Data path=/events/ method=delete
VAR &{dict}= &{EMPTY}
VAR @{list}= @{EMPTY}
- Should Be Equal ${request_data.dto.__doc__} DeleteEvents()
+ Should Be Equal ${request_data.constraint_mapping.__doc__} DeleteEvents()
Should Be Equal ${request_data.body_schema} ${NONE}
Should Be Equal ${request_data.parameters} ${list}
Should Be Equal ${request_data.params} ${dict}
@@ -32,10 +32,10 @@ Test Get Request Data For Endpoint With Object RequestBody
VAR @{list}= @{EMPTY}
VAR @{birthdays}= 1970-07-07 1980-08-08 1990-09-09
VAR @{weekdays}= Monday Tuesday Wednesday Thursday Friday
- Length Should Be ${request_data.dto.name} 36
- Length Should Be ${request_data.dto.wagegroup_id} 36
- Should Contain ${birthdays} ${request_data.dto.date_of_birth}
- VAR ${generated_parttime_schedule}= ${request_data.dto.parttime_schedule}
+ Length Should Be ${request_data.valid_data}[name] 36
+ Length Should Be ${request_data.valid_data}[wagegroup_id] 36
+ Should Contain ${birthdays} ${request_data.valid_data}[date_of_birth]
+ VAR ${generated_parttime_schedule}= ${request_data.valid_data}[parttime_schedule]
IF $generated_parttime_schedule is not None
${parttime_days}= Get From Dictionary ${generated_parttime_schedule} parttime_days
Should Be True 1 <= len($parttime_days) <= 5
@@ -66,17 +66,13 @@ Test Get Request Data For Endpoint With Array Request Body
VAR ${non_empty_array_seen}= ${TRUE}
EXCEPT * IndexError: list index out of range type=glob
Should Be Equal ${request_data.body_schema.type} array
- Should Be Equal As Strings ${request_data.dto}
VAR ${empty_array_seen}= ${TRUE}
END
END
-Test Get Request Data For Endpoint Without RequestBody But With DtoClass
+Test Get Request Data For Endpoint Without RequestBody But With Constraint Mapping
${request_data}= Get Request Data path=/wagegroups/{wagegroup_id} method=delete
VAR &{dict}= &{EMPTY}
- Should Be Equal As Strings
- ... ${request_data.dto}
- ...
Should Be Equal ${request_data.body_schema} ${NONE}
Should Not Be Empty ${request_data.parameters}
Should Be Equal ${request_data.params} ${dict}
diff --git a/tests/libcore/suites/test_request_data_class.robot b/tests/libcore/suites/test_request_data_class.robot
index a19a0a8..ff39b00 100644
--- a/tests/libcore/suites/test_request_data_class.robot
+++ b/tests/libcore/suites/test_request_data_class.robot
@@ -60,7 +60,7 @@ Test Headers That Can Be Invalidated
Test Get Required Properties Dict
${request_data}= Get Request Data path=/employees method=post
Should Contain ${request_data.valid_data} parttime_schedule
- Should Not Be Empty ${request_data.dto.name}
+ Should Not Be Empty ${request_data.valid_data}[name]
VAR ${required_properties}= ${request_data.get_required_properties_dict()}
Should Contain ${required_properties} name
# parttime_schedule is configured with treat_as_mandatory=True
diff --git a/tests/libcore/suites/test_schema_variations.robot b/tests/libcore/suites/test_schema_variations.robot
index b500116..9efc6af 100644
--- a/tests/libcore/suites/test_schema_variations.robot
+++ b/tests/libcore/suites/test_schema_variations.robot
@@ -1,7 +1,7 @@
*** Settings ***
Variables ${ROOT}/tests/variables.py
Library OpenApiLibCore
-... source=${ROOT}/tests/files/schema_with_allof.yaml
+... source=${ROOT}/tests/files/schema_with_allof_and_nullable.yaml
... origin=${ORIGIN}
... base_path=${EMPTY}
@@ -18,11 +18,11 @@ Test Get Request Data For Schema With allOf
VAR &{dict}= &{EMPTY}
VAR @{list}= @{EMPTY}
VAR &{expected_headers}= content-type=application/hal+json
- Length Should Be ${request_data.dto.isan} 36
- Length Should Be ${request_data.dto.published} 10
- Length Should Be ${request_data.dto.tags} 1
- Length Should Be ${request_data.dto.tags}[0] 36
- Length Should Be ${request_data.body_schema.properties.root} 4
+ Length Should Be ${request_data.valid_data}[isan] 36
+ Length Should Be ${request_data.valid_data}[tags] 1
+ Length Should Be ${request_data.valid_data}[tags][0] 36
+ VAR ${resolved_schema}= ${request_data.body_schema.resolved_schemas}[0]
+ Length Should Be ${resolved_schema.properties.root} 4
Should Be Equal ${request_data.parameters} ${list}
Should Be Equal ${request_data.params} ${dict}
Should Be Equal ${request_data.headers} ${expected_headers}
diff --git a/tests/libcore/unittests/test_dto_utils.py b/tests/libcore/unittests/test_dto_utils.py
index 0e61a39..7fc5024 100644
--- a/tests/libcore/unittests/test_dto_utils.py
+++ b/tests/libcore/unittests/test_dto_utils.py
@@ -41,7 +41,7 @@ def test_valid_mapping(self) -> None:
self.assertIsInstance(value_constraints_mapping_dict, dict)
self.assertGreater(len(value_constraints_mapping_dict.keys()), 0)
- def test_mapped_returns_dto_instance(self) -> None:
+ def test_mapped_returns_dto_class(self) -> None:
value_constraints_mapping_dict = get_constraint_mapping_dict(
self.mappings_module_name
)
@@ -49,7 +49,7 @@ def test_mapped_returns_dto_instance(self) -> None:
for key in keys:
self.assertIsInstance(key, tuple)
self.assertEqual(len(key), 2)
- self.assertIsInstance(value_constraints_mapping_dict[key](), Dto)
+ self.assertTrue(issubclass(value_constraints_mapping_dict[key], Dto))
if __name__ == "__main__":
diff --git a/tests/user_implemented/custom_user_mappings.py b/tests/user_implemented/custom_user_mappings.py
index 836ed27..9aa6bf3 100644
--- a/tests/user_implemented/custom_user_mappings.py
+++ b/tests/user_implemented/custom_user_mappings.py
@@ -193,7 +193,8 @@ def get_parameter_relations() -> list[ResourceRelation]:
"/wagegroups/{wagegroup_id}/employees": "identification",
}
-
+# NOTE: WagegroupDeleteDto does not have path mappings for testing purposes
PATH_MAPPING: dict[str, type[Dto]] = {
"/energy_label/{zipcode}/{home_number}": EnergyLabelDto,
+ "/wagegroups/{wagegroup_id}": WagegroupDeleteDto,
}
From 5868713f495e9dbf141dfcbad5bbfcd585c9ab39 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Mon, 3 Nov 2025 09:34:01 +0000
Subject: [PATCH 16/55] poetry.lock regenerated
---
poetry.lock | 376 +---------------------------------------------------
1 file changed, 4 insertions(+), 372 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index fc39728..878f057 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,6 +1,4 @@
-# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
-<<<<<<< HEAD
-=======
+# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
[[package]]
name = "annotated-doc"
@@ -13,7 +11,6 @@ files = [
{file = "annotated_doc-0.0.3-py3-none-any.whl", hash = "sha256:348ec6664a76f1fd3be81f43dffbee4c7e8ce931ba71ec67cc7f4ade7fbbb580"},
{file = "annotated_doc-0.0.3.tar.gz", hash = "sha256:e18370014c70187422c33e945053ff4c286f453a984eba84d0dbfa0c935adeda"},
]
->>>>>>> main
[[package]]
name = "annotated-types"
@@ -300,122 +297,12 @@ markers = {main = "platform_system == \"Windows\"", lint-and-format = "platform_
[[package]]
name = "coverage"
-<<<<<<< HEAD
-version = "7.10.7"
-=======
version = "7.11.0"
->>>>>>> main
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
-<<<<<<< HEAD
- {file = "coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a"},
- {file = "coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5"},
- {file = "coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17"},
- {file = "coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b"},
- {file = "coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87"},
- {file = "coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e"},
- {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e"},
- {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df"},
- {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0"},
- {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13"},
- {file = "coverage-7.10.7-cp310-cp310-win32.whl", hash = "sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b"},
- {file = "coverage-7.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807"},
- {file = "coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59"},
- {file = "coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a"},
- {file = "coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699"},
- {file = "coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d"},
- {file = "coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e"},
- {file = "coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23"},
- {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab"},
- {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82"},
- {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2"},
- {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61"},
- {file = "coverage-7.10.7-cp311-cp311-win32.whl", hash = "sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14"},
- {file = "coverage-7.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2"},
- {file = "coverage-7.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a"},
- {file = "coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417"},
- {file = "coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973"},
- {file = "coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c"},
- {file = "coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7"},
- {file = "coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6"},
- {file = "coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59"},
- {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b"},
- {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a"},
- {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb"},
- {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1"},
- {file = "coverage-7.10.7-cp312-cp312-win32.whl", hash = "sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256"},
- {file = "coverage-7.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba"},
- {file = "coverage-7.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf"},
- {file = "coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d"},
- {file = "coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b"},
- {file = "coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e"},
- {file = "coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b"},
- {file = "coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49"},
- {file = "coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911"},
- {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0"},
- {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f"},
- {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c"},
- {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f"},
- {file = "coverage-7.10.7-cp313-cp313-win32.whl", hash = "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698"},
- {file = "coverage-7.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843"},
- {file = "coverage-7.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546"},
- {file = "coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c"},
- {file = "coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15"},
- {file = "coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4"},
- {file = "coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0"},
- {file = "coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0"},
- {file = "coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65"},
- {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541"},
- {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6"},
- {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999"},
- {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2"},
- {file = "coverage-7.10.7-cp313-cp313t-win32.whl", hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a"},
- {file = "coverage-7.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb"},
- {file = "coverage-7.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb"},
- {file = "coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520"},
- {file = "coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32"},
- {file = "coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f"},
- {file = "coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a"},
- {file = "coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360"},
- {file = "coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69"},
- {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14"},
- {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe"},
- {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e"},
- {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd"},
- {file = "coverage-7.10.7-cp314-cp314-win32.whl", hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2"},
- {file = "coverage-7.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681"},
- {file = "coverage-7.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880"},
- {file = "coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63"},
- {file = "coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2"},
- {file = "coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d"},
- {file = "coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0"},
- {file = "coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699"},
- {file = "coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9"},
- {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f"},
- {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1"},
- {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0"},
- {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399"},
- {file = "coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235"},
- {file = "coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d"},
- {file = "coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a"},
- {file = "coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3"},
- {file = "coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c"},
- {file = "coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396"},
- {file = "coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40"},
- {file = "coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594"},
- {file = "coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a"},
- {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b"},
- {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3"},
- {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0"},
- {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f"},
- {file = "coverage-7.10.7-cp39-cp39-win32.whl", hash = "sha256:5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431"},
- {file = "coverage-7.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07"},
- {file = "coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260"},
- {file = "coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239"},
-=======
{file = "coverage-7.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eb53f1e8adeeb2e78962bade0c08bfdc461853c7969706ed901821e009b35e31"},
{file = "coverage-7.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9a03ec6cb9f40a5c360f138b88266fd8f58408d71e89f536b4f91d85721d075"},
{file = "coverage-7.11.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0d7f0616c557cbc3d1c2090334eddcbb70e1ae3a40b07222d62b3aa47f608fab"},
@@ -508,7 +395,6 @@ files = [
{file = "coverage-7.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b5c2705afa83f49bd91962a4094b6b082f94aef7626365ab3f8f4bd159c5acf3"},
{file = "coverage-7.11.0-py3-none-any.whl", hash = "sha256:4b7589765348d78fb4e5fb6ea35d07564e387da2fc5efff62e0222971f155f68"},
{file = "coverage-7.11.0.tar.gz", hash = "sha256:167bd504ac1ca2af7ff3b81d245dfea0292c5032ebef9d66cc08a7d28c1b8050"},
->>>>>>> main
]
[package.dependencies]
@@ -564,7 +450,7 @@ description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
-markers = "python_version == \"3.10\""
+markers = "python_version < \"3.11\""
files = [
{file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"},
{file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"},
@@ -578,23 +464,14 @@ test = ["pytest (>=6)"]
[[package]]
name = "faker"
-<<<<<<< HEAD
-version = "37.11.0"
-=======
version = "37.12.0"
->>>>>>> main
description = "Faker is a Python package that generates fake data for you."
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
-<<<<<<< HEAD
- {file = "faker-37.11.0-py3-none-any.whl", hash = "sha256:1508d2da94dfd1e0087b36f386126d84f8583b3de19ac18e392a2831a6676c57"},
- {file = "faker-37.11.0.tar.gz", hash = "sha256:22969803849ba0618be8eee2dd01d0d9e2cd3b75e6ff1a291fa9abcdb34da5e6"},
-=======
{file = "faker-37.12.0-py3-none-any.whl", hash = "sha256:afe7ccc038da92f2fbae30d8e16d19d91e92e242f8401ce9caf44de892bab4c4"},
{file = "faker-37.12.0.tar.gz", hash = "sha256:7505e59a7e02fa9010f06c3e1e92f8250d4cfbb30632296140c2d6dbef09b0fa"},
->>>>>>> main
]
[package.dependencies]
@@ -602,33 +479,20 @@ tzdata = "*"
[[package]]
name = "fastapi"
-<<<<<<< HEAD
-version = "0.119.0"
-=======
version = "0.120.4"
->>>>>>> main
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
-<<<<<<< HEAD
- {file = "fastapi-0.119.0-py3-none-any.whl", hash = "sha256:90a2e49ed19515320abb864df570dd766be0662c5d577688f1600170f7f73cf2"},
- {file = "fastapi-0.119.0.tar.gz", hash = "sha256:451082403a2c1f0b99c6bd57c09110ed5463856804c8078d38e5a1f1035dbbb7"},
-=======
{file = "fastapi-0.120.4-py3-none-any.whl", hash = "sha256:9bdf192308676480d3593e10fd05094e56d6fdc7d9283db26053d8104d5f82a0"},
{file = "fastapi-0.120.4.tar.gz", hash = "sha256:2d856bc847893ca4d77896d4504ffdec0fb04312b705065fca9104428eca3868"},
->>>>>>> main
]
[package.dependencies]
annotated-doc = ">=0.0.2"
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
-<<<<<<< HEAD
-starlette = ">=0.40.0,<0.49.0"
-=======
starlette = ">=0.40.0,<0.50.0"
->>>>>>> main
typing-extensions = ">=4.8.0"
[package.extras]
@@ -1385,32 +1249,19 @@ ssv = ["swagger-spec-validator (>=3.0.4,<3.1.0)"]
[[package]]
name = "pydantic"
-<<<<<<< HEAD
-version = "2.12.0"
-=======
version = "2.12.3"
->>>>>>> main
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.9"
groups = ["main", "dev"]
files = [
-<<<<<<< HEAD
- {file = "pydantic-2.12.0-py3-none-any.whl", hash = "sha256:f6a1da352d42790537e95e83a8bdfb91c7efbae63ffd0b86fa823899e807116f"},
- {file = "pydantic-2.12.0.tar.gz", hash = "sha256:c1a077e6270dbfb37bfd8b498b3981e2bb18f68103720e51fa6c306a5a9af563"},
-=======
{file = "pydantic-2.12.3-py3-none-any.whl", hash = "sha256:6986454a854bc3bc6e5443e1369e06a3a456af9d339eda45510f517d9ea5c6bf"},
{file = "pydantic-2.12.3.tar.gz", hash = "sha256:1da1c82b0fc140bb0103bc1441ffe062154c8d38491189751ee00fd8ca65ce74"},
->>>>>>> main
]
[package.dependencies]
annotated-types = ">=0.6.0"
-<<<<<<< HEAD
-pydantic-core = "2.41.1"
-=======
pydantic-core = "2.41.4"
->>>>>>> main
typing-extensions = ">=4.14.1"
typing-inspection = ">=0.4.2"
@@ -1420,131 +1271,12 @@ timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows
[[package]]
name = "pydantic-core"
-<<<<<<< HEAD
-version = "2.41.1"
-=======
version = "2.41.4"
->>>>>>> main
description = "Core functionality for Pydantic validation and serialization"
optional = false
python-versions = ">=3.9"
groups = ["main", "dev"]
files = [
-<<<<<<< HEAD
- {file = "pydantic_core-2.41.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e63036298322e9aea1c8b7c0a6c1204d615dbf6ec0668ce5b83ff27f07404a61"},
- {file = "pydantic_core-2.41.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:241299ca91fc77ef64f11ed909d2d9220a01834e8e6f8de61275c4dd16b7c936"},
- {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab7e594a2a5c24ab8013a7dc8cfe5f2260e80e490685814122081705c2cf2b0"},
- {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b054ef1a78519cb934b58e9c90c09e93b837c935dcd907b891f2b265b129eb6e"},
- {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2ab7d10d0ab2ed6da54c757233eb0f48ebfb4f86e9b88ccecb3f92bbd61a538"},
- {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2757606b7948bb853a27e4040820306eaa0ccb9e8f9f8a0fa40cb674e170f350"},
- {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cec0e75eb61f606bad0a32f2be87507087514e26e8c73db6cbdb8371ccd27917"},
- {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0234236514f44a5bf552105cfe2543a12f48203397d9d0f866affa569345a5b5"},
- {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1b974e41adfbb4ebb0f65fc4ca951347b17463d60893ba7d5f7b9bb087c83897"},
- {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:248dafb3204136113c383e91a4d815269f51562b6659b756cf3df14eefc7d0bb"},
- {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:678f9d76a91d6bcedd7568bbf6beb77ae8447f85d1aeebaab7e2f0829cfc3a13"},
- {file = "pydantic_core-2.41.1-cp310-cp310-win32.whl", hash = "sha256:dff5bee1d21ee58277900692a641925d2dddfde65182c972569b1a276d2ac8fb"},
- {file = "pydantic_core-2.41.1-cp310-cp310-win_amd64.whl", hash = "sha256:5042da12e5d97d215f91567110fdfa2e2595a25f17c19b9ff024f31c34f9b53e"},
- {file = "pydantic_core-2.41.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4f276a6134fe1fc1daa692642a3eaa2b7b858599c49a7610816388f5e37566a1"},
- {file = "pydantic_core-2.41.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07588570a805296ece009c59d9a679dc08fab72fb337365afb4f3a14cfbfc176"},
- {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28527e4b53400cd60ffbd9812ccb2b5135d042129716d71afd7e45bf42b855c0"},
- {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46a1c935c9228bad738c8a41de06478770927baedf581d172494ab36a6b96575"},
- {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:447ddf56e2b7d28d200d3e9eafa936fe40485744b5a824b67039937580b3cb20"},
- {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63892ead40c1160ac860b5debcc95c95c5a0035e543a8b5a4eac70dd22e995f4"},
- {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4a9543ca355e6df8fbe9c83e9faab707701e9103ae857ecb40f1c0cf8b0e94d"},
- {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2611bdb694116c31e551ed82e20e39a90bea9b7ad9e54aaf2d045ad621aa7a1"},
- {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fecc130893a9b5f7bfe230be1bb8c61fe66a19db8ab704f808cb25a82aad0bc9"},
- {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:1e2df5f8344c99b6ea5219f00fdc8950b8e6f2c422fbc1cc122ec8641fac85a1"},
- {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:35291331e9d8ed94c257bab6be1cb3a380b5eee570a2784bffc055e18040a2ea"},
- {file = "pydantic_core-2.41.1-cp311-cp311-win32.whl", hash = "sha256:2876a095292668d753f1a868c4a57c4ac9f6acbd8edda8debe4218d5848cf42f"},
- {file = "pydantic_core-2.41.1-cp311-cp311-win_amd64.whl", hash = "sha256:b92d6c628e9a338846a28dfe3fcdc1a3279388624597898b105e078cdfc59298"},
- {file = "pydantic_core-2.41.1-cp311-cp311-win_arm64.whl", hash = "sha256:7d82ae99409eb69d507a89835488fb657faa03ff9968a9379567b0d2e2e56bc5"},
- {file = "pydantic_core-2.41.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:db2f82c0ccbce8f021ad304ce35cbe02aa2f95f215cac388eed542b03b4d5eb4"},
- {file = "pydantic_core-2.41.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47694a31c710ced9205d5f1e7e8af3ca57cbb8a503d98cb9e33e27c97a501601"},
- {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e9decce94daf47baf9e9d392f5f2557e783085f7c5e522011545d9d6858e00"},
- {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab0adafdf2b89c8b84f847780a119437a0931eca469f7b44d356f2b426dd9741"},
- {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5da98cc81873f39fd56882e1569c4677940fbc12bce6213fad1ead784192d7c8"},
- {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:209910e88afb01fd0fd403947b809ba8dba0e08a095e1f703294fda0a8fdca51"},
- {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365109d1165d78d98e33c5bfd815a9b5d7d070f578caefaabcc5771825b4ecb5"},
- {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:706abf21e60a2857acdb09502bc853ee5bce732955e7b723b10311114f033115"},
- {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bf0bd5417acf7f6a7ec3b53f2109f587be176cb35f9cf016da87e6017437a72d"},
- {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2e71b1c6ceb9c78424ae9f63a07292fb769fb890a4e7efca5554c47f33a60ea5"},
- {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80745b9770b4a38c25015b517451c817799bfb9d6499b0d13d8227ec941cb513"},
- {file = "pydantic_core-2.41.1-cp312-cp312-win32.whl", hash = "sha256:83b64d70520e7890453f1aa21d66fda44e7b35f1cfea95adf7b4289a51e2b479"},
- {file = "pydantic_core-2.41.1-cp312-cp312-win_amd64.whl", hash = "sha256:377defd66ee2003748ee93c52bcef2d14fde48fe28a0b156f88c3dbf9bc49a50"},
- {file = "pydantic_core-2.41.1-cp312-cp312-win_arm64.whl", hash = "sha256:c95caff279d49c1d6cdfe2996e6c2ad712571d3b9caaa209a404426c326c4bde"},
- {file = "pydantic_core-2.41.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70e790fce5f05204ef4403159857bfcd587779da78627b0babb3654f75361ebf"},
- {file = "pydantic_core-2.41.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9cebf1ca35f10930612d60bd0f78adfacee824c30a880e3534ba02c207cceceb"},
- {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:170406a37a5bc82c22c3274616bf6f17cc7df9c4a0a0a50449e559cb755db669"},
- {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12d4257fc9187a0ccd41b8b327d6a4e57281ab75e11dda66a9148ef2e1fb712f"},
- {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a75a33b4db105dd1c8d57839e17ee12db8d5ad18209e792fa325dbb4baeb00f4"},
- {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08a589f850803a74e0fcb16a72081cafb0d72a3cdda500106942b07e76b7bf62"},
- {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a97939d6ea44763c456bd8a617ceada2c9b96bb5b8ab3dfa0d0827df7619014"},
- {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae423c65c556f09569524b80ffd11babff61f33055ef9773d7c9fabc11ed8d"},
- {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:4dc703015fbf8764d6a8001c327a87f1823b7328d40b47ce6000c65918ad2b4f"},
- {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:968e4ffdfd35698a5fe659e5e44c508b53664870a8e61c8f9d24d3d145d30257"},
- {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:fff2b76c8e172d34771cd4d4f0ade08072385310f214f823b5a6ad4006890d32"},
- {file = "pydantic_core-2.41.1-cp313-cp313-win32.whl", hash = "sha256:a38a5263185407ceb599f2f035faf4589d57e73c7146d64f10577f6449e8171d"},
- {file = "pydantic_core-2.41.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42ae7fd6760782c975897e1fdc810f483b021b32245b0105d40f6e7a3803e4b"},
- {file = "pydantic_core-2.41.1-cp313-cp313-win_arm64.whl", hash = "sha256:ad4111acc63b7384e205c27a2f15e23ac0ee21a9d77ad6f2e9cb516ec90965fb"},
- {file = "pydantic_core-2.41.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:440d0df7415b50084a4ba9d870480c16c5f67c0d1d4d5119e3f70925533a0edc"},
- {file = "pydantic_core-2.41.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71eaa38d342099405dae6484216dcf1e8e4b0bebd9b44a4e08c9b43db6a2ab67"},
- {file = "pydantic_core-2.41.1-cp313-cp313t-win_amd64.whl", hash = "sha256:555ecf7e50f1161d3f693bc49f23c82cf6cdeafc71fa37a06120772a09a38795"},
- {file = "pydantic_core-2.41.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:05226894a26f6f27e1deb735d7308f74ef5fa3a6de3e0135bb66cdcaee88f64b"},
- {file = "pydantic_core-2.41.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:85ff7911c6c3e2fd8d3779c50925f6406d770ea58ea6dde9c230d35b52b16b4a"},
- {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47f1f642a205687d59b52dc1a9a607f45e588f5a2e9eeae05edd80c7a8c47674"},
- {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df11c24e138876ace5ec6043e5cae925e34cf38af1a1b3d63589e8f7b5f5cdc4"},
- {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f0bf7f5c8f7bf345c527e8a0d72d6b26eda99c1227b0c34e7e59e181260de31"},
- {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82b887a711d341c2c47352375d73b029418f55b20bd7815446d175a70effa706"},
- {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5f1d5d6bbba484bdf220c72d8ecd0be460f4bd4c5e534a541bb2cd57589fb8b"},
- {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bf1917385ebe0f968dc5c6ab1375886d56992b93ddfe6bf52bff575d03662be"},
- {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:4f94f3ab188f44b9a73f7295663f3ecb8f2e2dd03a69c8f2ead50d37785ecb04"},
- {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:3925446673641d37c30bd84a9d597e49f72eacee8b43322c8999fa17d5ae5bc4"},
- {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:49bd51cc27adb980c7b97357ae036ce9b3c4d0bb406e84fbe16fb2d368b602a8"},
- {file = "pydantic_core-2.41.1-cp314-cp314-win32.whl", hash = "sha256:a31ca0cd0e4d12ea0df0077df2d487fc3eb9d7f96bbb13c3c5b88dcc21d05159"},
- {file = "pydantic_core-2.41.1-cp314-cp314-win_amd64.whl", hash = "sha256:1b5c4374a152e10a22175d7790e644fbd8ff58418890e07e2073ff9d4414efae"},
- {file = "pydantic_core-2.41.1-cp314-cp314-win_arm64.whl", hash = "sha256:4fee76d757639b493eb600fba668f1e17475af34c17dd61db7a47e824d464ca9"},
- {file = "pydantic_core-2.41.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f9b9c968cfe5cd576fdd7361f47f27adeb120517e637d1b189eea1c3ece573f4"},
- {file = "pydantic_core-2.41.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ebc7ab67b856384aba09ed74e3e977dded40e693de18a4f197c67d0d4e6d8e"},
- {file = "pydantic_core-2.41.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8ae0dc57b62a762985bc7fbf636be3412394acc0ddb4ade07fe104230f1b9762"},
- {file = "pydantic_core-2.41.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:10ce489cf09a4956a1549af839b983edc59b0f60e1b068c21b10154e58f54f80"},
- {file = "pydantic_core-2.41.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ff548c908caffd9455fd1342366bcf8a1ec8a3fca42f35c7fc60883d6a901074"},
- {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d43bf082025082bda13be89a5f876cc2386b7727c7b322be2d2b706a45cea8e"},
- {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:666aee751faf1c6864b2db795775dd67b61fdcf646abefa309ed1da039a97209"},
- {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b83aaeff0d7bde852c32e856f3ee410842ebc08bc55c510771d87dcd1c01e1ed"},
- {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:055c7931b0329cb8acde20cdde6d9c2cbc2a02a0a8e54a792cddd91e2ea92c65"},
- {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530bbb1347e3e5ca13a91ac087c4971d7da09630ef8febd27a20a10800c2d06d"},
- {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65a0ea16cfea7bfa9e43604c8bd726e63a3788b61c384c37664b55209fcb1d74"},
- {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8fa93fadff794c6d15c345c560513b160197342275c6d104cc879f932b978afc"},
- {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:c8a1af9ac51969a494c6a82b563abae6859dc082d3b999e8fa7ba5ee1b05e8e8"},
- {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:30edab28829703f876897c9471a857e43d847b8799c3c9e2fbce644724b50aa4"},
- {file = "pydantic_core-2.41.1-cp39-cp39-win32.whl", hash = "sha256:84d0ff869f98be2e93efdf1ae31e5a15f0926d22af8677d51676e373abbfe57a"},
- {file = "pydantic_core-2.41.1-cp39-cp39-win_amd64.whl", hash = "sha256:b5674314987cdde5a5511b029fa5fb1556b3d147a367e01dd583b19cfa8e35df"},
- {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:68f2251559b8efa99041bb63571ec7cdd2d715ba74cc82b3bc9eff824ebc8bf0"},
- {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:c7bc140c596097cb53b30546ca257dbe3f19282283190b1b5142928e5d5d3a20"},
- {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2896510fce8f4725ec518f8b9d7f015a00db249d2fd40788f442af303480063d"},
- {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ced20e62cfa0f496ba68fa5d6c7ee71114ea67e2a5da3114d6450d7f4683572a"},
- {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b04fa9ed049461a7398138c604b00550bc89e3e1151d84b81ad6dc93e39c4c06"},
- {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b3b7d9cfbfdc43c80a16638c6dc2768e3956e73031fca64e8e1a3ae744d1faeb"},
- {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eec83fc6abef04c7f9bec616e2d76ee9a6a4ae2a359b10c21d0f680e24a247ca"},
- {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6771a2d9f83c4038dfad5970a3eef215940682b2175e32bcc817bdc639019b28"},
- {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fabcbdb12de6eada8d6e9a759097adb3c15440fafc675b3e94ae5c9cb8d678a0"},
- {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:80e97ccfaf0aaf67d55de5085b0ed0d994f57747d9d03f2de5cc9847ca737b08"},
- {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34df1fe8fea5d332484a763702e8b6a54048a9d4fe6ccf41e34a128238e01f52"},
- {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:421b5595f845842fc093f7250e24ee395f54ca62d494fdde96f43ecf9228ae01"},
- {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dce8b22663c134583aaad24827863306a933f576c79da450be3984924e2031d1"},
- {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:300a9c162fea9906cc5c103893ca2602afd84f0ec90d3be36f4cc360125d22e1"},
- {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e019167628f6e6161ae7ab9fb70f6d076a0bf0d55aa9b20833f86a320c70dd65"},
- {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:13ab9cc2de6f9d4ab645a050ae5aee61a2424ac4d3a16ba23d4c2027705e0301"},
- {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:af2385d3f98243fb733862f806c5bb9122e5fba05b373e3af40e3c82d711cef1"},
- {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6550617a0c2115be56f90c31a5370261d8ce9dbf051c3ed53b51172dd34da696"},
- {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc17b6ecf4983d298686014c92ebc955a9f9baf9f57dad4065e7906e7bee6222"},
- {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42ae9352cf211f08b04ea110563d6b1e415878eea5b4c70f6bdb17dca3b932d2"},
- {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e82947de92068b0a21681a13dd2102387197092fbe7defcfb8453e0913866506"},
- {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e244c37d5471c9acdcd282890c6c4c83747b77238bfa19429b8473586c907656"},
- {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1e798b4b304a995110d41ec93653e57975620ccb2842ba9420037985e7d7284e"},
- {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f1fc716c0eb1663c59699b024428ad5ec2bcc6b928527b8fe28de6cb89f47efb"},
- {file = "pydantic_core-2.41.1.tar.gz", hash = "sha256:1ad375859a6d8c356b7704ec0f547a58e82ee80bb41baa811ad710e124bc8f2f"},
-=======
{file = "pydantic_core-2.41.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2442d9a4d38f3411f22eb9dd0912b7cbf4b7d5b6c92c4173b75d3e1ccd84e36e"},
{file = "pydantic_core-2.41.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:30a9876226dda131a741afeab2702e2d127209bde3c65a2b8133f428bc5d006b"},
{file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d55bbac04711e2980645af68b97d445cdbcce70e5216de444a6c4b6943ebcccd"},
@@ -1662,7 +1394,6 @@ files = [
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5a28fcedd762349519276c36634e71853b4541079cab4acaaac60c4421827308"},
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c173ddcd86afd2535e2b695217e82191580663a1d1928239f877f5a1649ef39f"},
{file = "pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5"},
->>>>>>> main
]
[package.dependencies]
@@ -1685,23 +1416,14 @@ windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pylint"
-<<<<<<< HEAD
-version = "4.0.0"
-=======
version = "4.0.2"
->>>>>>> main
description = "python code static checker"
optional = false
python-versions = ">=3.10.0"
groups = ["lint-and-format"]
files = [
-<<<<<<< HEAD
- {file = "pylint-4.0.0-py3-none-any.whl", hash = "sha256:196b92a85204bb0c0a416a6bb324f6185e59ff1d687ee1d614bf0abf34a348e8"},
- {file = "pylint-4.0.0.tar.gz", hash = "sha256:62da212808c0681e49ffb125f0a994c685d912cf19ae373075649ebb5870ec28"},
-=======
{file = "pylint-4.0.2-py3-none-any.whl", hash = "sha256:9627ccd129893fb8ee8e8010261cb13485daca83e61a6f854a85528ee579502d"},
{file = "pylint-4.0.2.tar.gz", hash = "sha256:9c22dfa52781d3b79ce86ab2463940f874921a3e5707bcfc98dd0c019945014e"},
->>>>>>> main
]
[package.dependencies]
@@ -1724,23 +1446,14 @@ testutils = ["gitpython (>3)"]
[[package]]
name = "pyright"
-<<<<<<< HEAD
-version = "1.1.406"
-=======
version = "1.1.407"
->>>>>>> main
description = "Command line wrapper for pyright"
optional = false
python-versions = ">=3.7"
groups = ["type-checking"]
files = [
-<<<<<<< HEAD
- {file = "pyright-1.1.406-py3-none-any.whl", hash = "sha256:1d81fb43c2407bf566e97e57abb01c811973fdb21b2df8df59f870f688bdca71"},
- {file = "pyright-1.1.406.tar.gz", hash = "sha256:c4872bc58c9643dac09e8a2e74d472c62036910b3bd37a32813989ef7576ea2c"},
-=======
{file = "pyright-1.1.407-py3-none-any.whl", hash = "sha256:6dd419f54fcc13f03b52285796d65e639786373f433e243f8b94cf93a7444d21"},
{file = "pyright-1.1.407.tar.gz", hash = "sha256:099674dba5c10489832d4a4b2d302636152a9a42d317986c38474c76fe562262"},
->>>>>>> main
]
[package.dependencies]
@@ -1754,24 +1467,14 @@ nodejs = ["nodejs-wheel-binaries"]
[[package]]
name = "pytokens"
-<<<<<<< HEAD
-version = "0.1.10"
-description = "A Fast, spec compliant Python 3.12+ tokenizer that runs on older Pythons."
-=======
version = "0.2.0"
description = "A Fast, spec compliant Python 3.13+ tokenizer that runs on older Pythons."
->>>>>>> main
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
-<<<<<<< HEAD
- {file = "pytokens-0.1.10-py3-none-any.whl", hash = "sha256:db7b72284e480e69fb085d9f251f66b3d2df8b7166059261258ff35f50fb711b"},
- {file = "pytokens-0.1.10.tar.gz", hash = "sha256:c9a4bfa0be1d26aebce03e6884ba454e842f186a59ea43a6d3b25af58223c044"},
-=======
{file = "pytokens-0.2.0-py3-none-any.whl", hash = "sha256:74d4b318c67f4295c13782ddd9abcb7e297ec5630ad060eb90abf7ebbefe59f8"},
{file = "pytokens-0.2.0.tar.gz", hash = "sha256:532d6421364e5869ea57a9523bf385f02586d4662acbcc0342afd69511b4dd43"},
->>>>>>> main
]
[package.extras]
@@ -1947,23 +1650,14 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]]
name = "rich-click"
-<<<<<<< HEAD
-version = "1.9.3"
-=======
version = "1.9.4"
->>>>>>> main
description = "Format click help output nicely with rich"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
-<<<<<<< HEAD
- {file = "rich_click-1.9.3-py3-none-any.whl", hash = "sha256:8ef51bc340db4d048a846c15c035d27b88acf720cbbb9b6fecf6c8b1a297b909"},
- {file = "rich_click-1.9.3.tar.gz", hash = "sha256:60839150a935604df1378b159da340d3fff91f912903e935da7cb615b5738c1b"},
-=======
{file = "rich_click-1.9.4-py3-none-any.whl", hash = "sha256:d70f39938bcecaf5543e8750828cbea94ef51853f7d0e174cda1e10543767389"},
{file = "rich_click-1.9.4.tar.gz", hash = "sha256:af73dc68e85f3bebb80ce302a642b9fe3b65f3df0ceb42eb9a27c467c1b678c8"},
->>>>>>> main
]
[package.dependencies]
@@ -2143,23 +1837,14 @@ xls = ["openpyxl", "pandas", "xlrd (>=1.2.0)"]
[[package]]
name = "robotframework-robocop"
-<<<<<<< HEAD
-version = "6.8.0"
-=======
version = "6.9.2"
->>>>>>> main
description = "Static code analysis tool (linter) and code formatter for Robot Framework"
optional = false
python-versions = ">=3.9"
groups = ["lint-and-format"]
files = [
-<<<<<<< HEAD
- {file = "robotframework_robocop-6.8.0-py3-none-any.whl", hash = "sha256:6df4bd5bf1c4a88cfc90d11795a350c9738c9c01a792f6d6e5b5115cfe0be0e6"},
- {file = "robotframework_robocop-6.8.0.tar.gz", hash = "sha256:4f0da4c5d4b3b29b597b4056c424cd490a18f0362f5ec3d3ebcaac2321de6aaf"},
-=======
{file = "robotframework_robocop-6.9.2-py3-none-any.whl", hash = "sha256:1b6111c614cce67af33998aa35cac60ccc8a1e495b0be44b6b8892a7cdcc7cf9"},
{file = "robotframework_robocop-6.9.2.tar.gz", hash = "sha256:461b1ae8ad9a43ae1a29ba343ec9b626c65cd8615938e94b76c3f32c0eee39f6"},
->>>>>>> main
]
[package.dependencies]
@@ -2351,7 +2036,7 @@ description = "C version of reader, parser and emitter for ruamel.yaml derived f
optional = false
python-versions = ">=3.9"
groups = ["main"]
-markers = "python_version < \"3.14\" and platform_python_implementation == \"CPython\""
+markers = "platform_python_implementation == \"CPython\" and python_version < \"3.14\""
files = [
{file = "ruamel.yaml.clib-0.2.14-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f8b2acb0ffdd2ce8208accbec2dca4a06937d556fdcaefd6473ba1b5daa7e3c4"},
{file = "ruamel.yaml.clib-0.2.14-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:aef953f3b8bd0b50bd52a2e52fb54a6a2171a1889d8dea4a5959d46c6624c451"},
@@ -2412,37 +2097,12 @@ files = [
[[package]]
name = "ruff"
-<<<<<<< HEAD
-version = "0.14.0"
-=======
version = "0.14.3"
->>>>>>> main
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
groups = ["lint-and-format"]
files = [
-<<<<<<< HEAD
- {file = "ruff-0.14.0-py3-none-linux_armv6l.whl", hash = "sha256:58e15bffa7054299becf4bab8a1187062c6f8cafbe9f6e39e0d5aface455d6b3"},
- {file = "ruff-0.14.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:838d1b065f4df676b7c9957992f2304e41ead7a50a568185efd404297d5701e8"},
- {file = "ruff-0.14.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:703799d059ba50f745605b04638fa7e9682cc3da084b2092feee63500ff3d9b8"},
- {file = "ruff-0.14.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ba9a8925e90f861502f7d974cc60e18ca29c72bb0ee8bfeabb6ade35a3abde7"},
- {file = "ruff-0.14.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e41f785498bd200ffc276eb9e1570c019c1d907b07cfb081092c8ad51975bbe7"},
- {file = "ruff-0.14.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30a58c087aef4584c193aebf2700f0fbcfc1e77b89c7385e3139956fa90434e2"},
- {file = "ruff-0.14.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f8d07350bc7af0a5ce8812b7d5c1a7293cf02476752f23fdfc500d24b79b783c"},
- {file = "ruff-0.14.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eec3bbbf3a7d5482b5c1f42d5fc972774d71d107d447919fca620b0be3e3b75e"},
- {file = "ruff-0.14.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16b68e183a0e28e5c176d51004aaa40559e8f90065a10a559176713fcf435206"},
- {file = "ruff-0.14.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb732d17db2e945cfcbbc52af0143eda1da36ca8ae25083dd4f66f1542fdf82e"},
- {file = "ruff-0.14.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:c958f66ab884b7873e72df38dcabee03d556a8f2ee1b8538ee1c2bbd619883dd"},
- {file = "ruff-0.14.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7eb0499a2e01f6e0c285afc5bac43ab380cbfc17cd43a2e1dd10ec97d6f2c42d"},
- {file = "ruff-0.14.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c63b2d99fafa05efca0ab198fd48fa6030d57e4423df3f18e03aa62518c565f"},
- {file = "ruff-0.14.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:668fce701b7a222f3f5327f86909db2bbe99c30877c8001ff934c5413812ac02"},
- {file = "ruff-0.14.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a86bf575e05cb68dcb34e4c7dfe1064d44d3f0c04bbc0491949092192b515296"},
- {file = "ruff-0.14.0-py3-none-win32.whl", hash = "sha256:7450a243d7125d1c032cb4b93d9625dea46c8c42b4f06c6b709baac168e10543"},
- {file = "ruff-0.14.0-py3-none-win_amd64.whl", hash = "sha256:ea95da28cd874c4d9c922b39381cbd69cb7e7b49c21b8152b014bd4f52acddc2"},
- {file = "ruff-0.14.0-py3-none-win_arm64.whl", hash = "sha256:f42c9495f5c13ff841b1da4cb3c2a42075409592825dada7c5885c2c844ac730"},
- {file = "ruff-0.14.0.tar.gz", hash = "sha256:62ec8969b7510f77945df916de15da55311fade8d6050995ff7f680afe582c57"},
-=======
{file = "ruff-0.14.3-py3-none-linux_armv6l.whl", hash = "sha256:876b21e6c824f519446715c1342b8e60f97f93264012de9d8d10314f8a79c371"},
{file = "ruff-0.14.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6fd8c79b457bedd2abf2702b9b472147cd860ed7855c73a5247fa55c9117654"},
{file = "ruff-0.14.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:71ff6edca490c308f083156938c0c1a66907151263c4abdcb588602c6e696a14"},
@@ -2462,7 +2122,6 @@ files = [
{file = "ruff-0.14.3-py3-none-win_amd64.whl", hash = "sha256:d7b7006ac0756306db212fd37116cce2bd307e1e109375e1c6c106002df0ae5f"},
{file = "ruff-0.14.3-py3-none-win_arm64.whl", hash = "sha256:26eb477ede6d399d898791d01961e16b86f02bc2486d0d1a7a9bb2379d055dc1"},
{file = "ruff-0.14.3.tar.gz", hash = "sha256:4ff876d2ab2b161b6de0aa1f5bd714e8e9b4033dc122ee006925fbacc4f62153"},
->>>>>>> main
]
[[package]]
@@ -2512,23 +2171,14 @@ files = [
[[package]]
name = "starlette"
-<<<<<<< HEAD
-version = "0.48.0"
-=======
version = "0.49.3"
->>>>>>> main
description = "The little ASGI library that shines."
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
-<<<<<<< HEAD
- {file = "starlette-0.48.0-py3-none-any.whl", hash = "sha256:0764ca97b097582558ecb498132ed0c7d942f233f365b86ba37770e026510659"},
- {file = "starlette-0.48.0.tar.gz", hash = "sha256:7e8cee469a8ab2352911528110ce9088fdc6a37d9876926e73da7ce4aa4c7a46"},
-=======
{file = "starlette-0.49.3-py3-none-any.whl", hash = "sha256:b579b99715fdc2980cf88c8ec96d3bf1ce16f5a8051a7c2b84ef9b1cdecaea2f"},
{file = "starlette-0.49.3.tar.gz", hash = "sha256:1c14546f299b5901a1ea0e34410575bc33bbd741377a10484a54445588d00284"},
->>>>>>> main
]
[package.dependencies]
@@ -2545,7 +2195,7 @@ description = "A lil' TOML parser"
optional = false
python-versions = ">=3.8"
groups = ["main", "dev", "lint-and-format", "type-checking"]
-markers = "python_version == \"3.10\""
+markers = "python_version < \"3.11\""
files = [
{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"},
@@ -2607,23 +2257,14 @@ files = [
[[package]]
name = "typer-slim"
-<<<<<<< HEAD
-version = "0.19.2"
-=======
version = "0.20.0"
->>>>>>> main
description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
optional = false
python-versions = ">=3.8"
groups = ["lint-and-format"]
files = [
-<<<<<<< HEAD
- {file = "typer_slim-0.19.2-py3-none-any.whl", hash = "sha256:1c9cdbbcd5b8d30f4118d3cb7c52dc63438b751903fbd980a35df1dfe10c6c91"},
- {file = "typer_slim-0.19.2.tar.gz", hash = "sha256:6f601e28fb8249a7507f253e35fb22ccc701403ce99bea6a9923909ddbfcd133"},
-=======
{file = "typer_slim-0.20.0-py3-none-any.whl", hash = "sha256:f42a9b7571a12b97dddf364745d29f12221865acef7a2680065f9bb29c7dc89d"},
{file = "typer_slim-0.20.0.tar.gz", hash = "sha256:9fc6607b3c6c20f5c33ea9590cbeb17848667c51feee27d9e314a579ab07d1a3"},
->>>>>>> main
]
[package.dependencies]
@@ -2719,23 +2360,14 @@ zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "uvicorn"
-<<<<<<< HEAD
-version = "0.37.0"
-=======
version = "0.38.0"
->>>>>>> main
description = "The lightning-fast ASGI server."
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
-<<<<<<< HEAD
- {file = "uvicorn-0.37.0-py3-none-any.whl", hash = "sha256:913b2b88672343739927ce381ff9e2ad62541f9f8289664fa1d1d3803fa2ce6c"},
- {file = "uvicorn-0.37.0.tar.gz", hash = "sha256:4115c8add6d3fd536c8ee77f0e14a7fd2ebba939fed9b02583a97f80648f9e13"},
-=======
{file = "uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02"},
{file = "uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d"},
->>>>>>> main
]
[package.dependencies]
From 22482a4920a6acb2f2d125dd58acb1c587311943 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Tue, 18 Nov 2025 08:03:36 +0000
Subject: [PATCH 17/55] Parameters at path level no longer need to be
registered.
---
src/OpenApiLibCore/openapi_libcore.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index bf01b50..495c962 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -580,10 +580,6 @@ def _register_path_parameter(parameter_object: ParameterObject) -> None:
_ = get_safe_name_for_oas_name(parameter_object.name)
for path_item in paths_data.values():
- if parameters := path_item.parameters:
- for parameter in path_item.parameters:
- _register_path_parameter(parameter_object=parameter)
-
operations = path_item.get_operations()
for operation in operations.values():
if parameters := operation.parameters:
From 56a6908287cc69aadf39a621512497e07fed2518 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Wed, 19 Nov 2025 14:10:13 +0000
Subject: [PATCH 18/55] refactor: get_invalid_value_from_constraint moved to
models
---
src/OpenApiDriver/__init__.py | 2 +-
.../data_generation/data_generation_core.py | 2 +-
.../data_generation/data_invalidation.py | 2 +-
.../data_generation/value_utils.py | 106 -----------
src/OpenApiLibCore/models/oas_models.py | 178 ++++++++++++++----
.../test_invalid_value_from_constraint.py | 106 +++++------
6 files changed, 197 insertions(+), 199 deletions(-)
diff --git a/src/OpenApiDriver/__init__.py b/src/OpenApiDriver/__init__.py
index f459108..be0c6b7 100644
--- a/src/OpenApiDriver/__init__.py
+++ b/src/OpenApiDriver/__init__.py
@@ -16,8 +16,8 @@
from OpenApiLibCore.data_constraints.dto_base import (
Dto,
)
-from OpenApiLibCore.data_generation.value_utils import IGNORE
from OpenApiLibCore.keyword_logic.validation import ValidationLevel
+from OpenApiLibCore.models import IGNORE
from OpenApiLibCore.models.resource_relations import (
IdDependency,
IdReference,
diff --git a/src/OpenApiLibCore/data_generation/data_generation_core.py b/src/OpenApiLibCore/data_generation/data_generation_core.py
index e4bce53..ee74c07 100644
--- a/src/OpenApiLibCore/data_generation/data_generation_core.py
+++ b/src/OpenApiLibCore/data_generation/data_generation_core.py
@@ -13,7 +13,7 @@
import OpenApiLibCore.keyword_logic.path_functions as _path_functions
from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.data_constraints.dto_base import Dto
-from OpenApiLibCore.data_generation.value_utils import IGNORE
+from OpenApiLibCore.models import IGNORE
from OpenApiLibCore.models.oas_models import (
ArraySchema,
ObjectSchema,
diff --git a/src/OpenApiLibCore/data_generation/data_invalidation.py b/src/OpenApiLibCore/data_generation/data_invalidation.py
index d2e9ee9..3e3f102 100644
--- a/src/OpenApiLibCore/data_generation/data_invalidation.py
+++ b/src/OpenApiLibCore/data_generation/data_invalidation.py
@@ -15,7 +15,7 @@
from OpenApiLibCore.data_constraints.dto_base import (
Dto,
)
-from OpenApiLibCore.data_generation.value_utils import IGNORE
+from OpenApiLibCore.models import IGNORE
from OpenApiLibCore.models.oas_models import (
ArraySchema,
ObjectSchema,
diff --git a/src/OpenApiLibCore/data_generation/value_utils.py b/src/OpenApiLibCore/data_generation/value_utils.py
index 8f46130..81dc28c 100644
--- a/src/OpenApiLibCore/data_generation/value_utils.py
+++ b/src/OpenApiLibCore/data_generation/value_utils.py
@@ -1,14 +1,5 @@
-# mypy: disable-error-code=no-any-return
"""Utility module with functions to handle OpenAPI value types and restrictions."""
-from copy import deepcopy
-from typing import Any, TypeVar, cast, overload
-
-from OpenApiLibCore.annotations import JSON
-from OpenApiLibCore.models import IGNORE, Ignore
-
-O = TypeVar("O")
-
def json_type_name_of_python_type(python_type: type) -> str:
"""Return the JSON type name for supported Python types."""
@@ -46,100 +37,3 @@ def python_type_by_json_type_name(type_name: str) -> type:
if type_name == "null":
return type(None)
raise ValueError(f"No Python type mapping for JSON type '{type_name}' available.")
-
-
-def get_invalid_value_from_constraint(
- values_from_constraint: list[O | Ignore], value_type: str
-) -> O | Ignore:
- """
- Return a value of the same type as the values in the values_from_constraints that
- is not in the values_from_constraints, if possible. Otherwise returns None.
- """
- # if IGNORE is in the values_from_constraints, the parameter needs to be
- # ignored for an OK response so leaving the value at it's original value
- # should result in the specified error response
- if any(map(lambda x: isinstance(x, Ignore), values_from_constraint)):
- return IGNORE
- # if the value is forced True or False, return the opposite to invalidate
- if len(values_from_constraint) == 1 and value_type == "boolean":
- return not values_from_constraint[0]
- # for unsupported types or empty constraints lists raise a ValueError
- if (
- value_type not in ["string", "integer", "number", "array", "object"]
- or not values_from_constraint
- ):
- raise ValueError(
- f"Cannot get invalid value for {value_type} from {values_from_constraint}"
- )
-
- values_from_constraint = deepcopy(values_from_constraint)
- # for objects, keep the keys intact but update the values
- if value_type == "object":
- valid_object = cast(dict[str, JSON], values_from_constraint.pop())
- invalid_object: dict[str, JSON] = {}
- for key, value in valid_object.items():
- python_type_of_value = type(value)
- json_type_of_value = json_type_name_of_python_type(python_type_of_value)
- invalid_value = get_invalid_value_from_constraint(
- values_from_constraint=[value],
- value_type=json_type_of_value,
- )
- invalid_object[key] = invalid_value
- return invalid_object
-
- # for arrays, update each value in the array to a value of the same type
- if value_type == "array":
- valid_array = cast(list[JSON], values_from_constraint.pop())
- invalid_array: list[JSON] = []
- for value in valid_array:
- python_type_of_value = type(value)
- json_type_of_value = json_type_name_of_python_type(python_type_of_value)
- invalid_value = cast(
- JSON,
- get_invalid_value_from_constraint(
- values_from_constraint=[value],
- value_type=json_type_of_value,
- ),
- )
- invalid_array.append(invalid_value)
- return invalid_array
-
- if value_type in ["integer", "number"]:
- int_or_number_list = cast(list[int | float], values_from_constraint)
- return get_invalid_int_or_number(values_from_constraint=int_or_number_list)
-
- str_or_bytes_list = cast(list[str] | list[bytes], values_from_constraint)
- invalid_value = get_invalid_str_or_bytes(values_from_constraint=str_or_bytes_list)
- if not invalid_value:
- raise ValueError("Value invalidation yielded an empty string.")
- return invalid_value
-
-
-def get_invalid_int_or_number(values_from_constraint: list[int | float]) -> int | float:
- invalid_values = 2 * values_from_constraint
- invalid_value = invalid_values.pop()
- for value in invalid_values:
- invalid_value = abs(invalid_value) + abs(value)
- if not invalid_value:
- invalid_value += 1
- return invalid_value
-
-
-@overload
-def get_invalid_str_or_bytes(
- values_from_constraint: list[str],
-) -> str: ... # pragma: no cover
-
-
-@overload
-def get_invalid_str_or_bytes(
- values_from_constraint: list[bytes],
-) -> bytes: ... # pragma: no cover
-
-
-def get_invalid_str_or_bytes(values_from_constraint: list[Any]) -> Any:
- invalid_values = 2 * values_from_constraint
- invalid_value = invalid_values.pop()
- for value in invalid_values:
- invalid_value = invalid_value + value
- return invalid_value
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index 0028ddc..d8d658c 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -17,19 +17,21 @@
Literal,
Mapping,
TypeAlias,
+ TypeGuard,
TypeVar,
+ cast,
)
from uuid import uuid4
import rstr
from pydantic import BaseModel, Field, RootModel
from robot.api import logger
-from robot.libraries.BuiltIn import BuiltIn # type: ignore[import-untyped]
+from robot.libraries.BuiltIn import BuiltIn
from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.data_generation.localized_faker import FAKE, fake_string
from OpenApiLibCore.data_generation.value_utils import (
- get_invalid_value_from_constraint,
+ json_type_name_of_python_type,
python_type_by_json_type_name,
)
from OpenApiLibCore.models import IGNORE, Ignore
@@ -49,9 +51,19 @@
SENTINEL = object()
O = TypeVar("O")
+AI = TypeVar("AI", bound=JSON)
+S = TypeVar("S", str, bytes)
ConstraintMappingType = builtins.type[IConstraintMapping]
+def is_object_schema(schema: SchemaObjectTypes) -> TypeGuard[ObjectSchema]:
+ return isinstance(schema, ObjectSchema)
+
+
+def data_is_bytes(data: str | bytes, format: str) -> TypeGuard[bytes]:
+ return format == "byte"
+
+
class SchemaBase(BaseModel, Generic[O], frozen=True):
readOnly: bool = False
writeOnly: bool = False
@@ -69,6 +81,13 @@ def get_values_out_of_bounds(self, current_value: O) -> list[O]: ...
@abstractmethod
def get_invalid_value_from_const_or_enum(self) -> O: ...
+ @abstractmethod
+ def get_invalid_value_from_constraint(self, values_from_constraint: list[O]) -> O:
+ """
+ Return a value of the same type as the values in the values_from_constraints that
+ is not in the values_from_constraints, if possible. Otherwise raise ValueError.
+ """
+
def get_invalid_value(
self,
valid_value: O,
@@ -82,10 +101,14 @@ def get_invalid_value(
valid_value = self.get_valid_value()
if values_from_constraint:
+ # if IGNORE is in the values_from_constraints, the parameter needs to be
+ # ignored for an OK response so leaving the value at it's original value
+ # should result in the specified error response
+ if any(map(lambda x: isinstance(x, Ignore), values_from_constraint)):
+ return IGNORE
try:
- return get_invalid_value_from_constraint(
+ return self.get_invalid_value_from_constraint(
values_from_constraint=list(values_from_constraint),
- value_type=value_type,
)
except ValueError:
pass
@@ -134,6 +157,11 @@ def get_values_out_of_bounds(self, current_value: None) -> list[None]:
def get_invalid_value_from_const_or_enum(self) -> None:
raise ValueError
+ def get_invalid_value_from_constraint(
+ self, values_from_constraint: list[None]
+ ) -> None:
+ raise ValueError
+
@property
def can_be_invalidated(self) -> bool:
return False
@@ -165,6 +193,13 @@ def get_invalid_value_from_const_or_enum(self) -> bool:
return not self.const
raise ValueError
+ def get_invalid_value_from_constraint(
+ self, values_from_constraint: list[bool]
+ ) -> bool:
+ if len(values_from_constraint) == 1:
+ return not values_from_constraint[0]
+ raise ValueError
+
@property
def can_be_invalidated(self) -> bool:
return True
@@ -174,21 +209,21 @@ def annotation_string(self) -> str:
return "bool"
-class StringSchema(SchemaBase[str], frozen=True):
+class StringSchema(SchemaBase[S], frozen=True):
type: Literal["string"] = "string"
format: str = ""
pattern: str = ""
maxLength: int | None = None
minLength: int | None = None
- const: str | None = None
- enum: list[str] | None = None
+ const: S | None = None
+ enum: list[S] | None = None
nullable: bool = False
- def get_valid_value(
+ def get_valid_value( # type: ignore
self,
constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
- ) -> bytes | str:
+ ) -> S:
"""Generate a random string within the min/max length in the schema, if specified."""
if self.const is not None:
return self.const
@@ -212,9 +247,10 @@ def get_valid_value(
maximum = max(minimum, maximum)
format_ = self.format if self.format else "uuid"
# byte is a special case due to the required encoding
- if format_ == "byte":
- data = FAKE.uuid()
+ data = FAKE.uuid()
+ if data_is_bytes(data=data, format=format_):
return base64.b64encode(data.encode("utf-8"))
+
value = fake_string(string_format=format_)
while len(value) < minimum:
# use fake.name() to ensure the returned string uses the provided locale
@@ -223,8 +259,8 @@ def get_valid_value(
value = value[:maximum]
return value
- def get_values_out_of_bounds(self, current_value: str) -> list[str]:
- invalid_values: list[str] = []
+ def get_values_out_of_bounds(self, current_value: S) -> list[S]:
+ invalid_values: list[S] = []
if self.minLength:
invalid_values.append(current_value[0 : self.minLength - 1])
# if there is a maximum length, send 1 character more
@@ -238,7 +274,7 @@ def get_values_out_of_bounds(self, current_value: str) -> list[str]:
return invalid_values
raise ValueError
- def get_invalid_value_from_const_or_enum(self) -> str:
+ def get_invalid_value_from_const_or_enum(self) -> S:
valid_values = []
if self.const is not None:
valid_values = [self.const]
@@ -254,6 +290,16 @@ def get_invalid_value_from_const_or_enum(self) -> str:
return invalid_value
+ def get_invalid_value_from_constraint(self, values_from_constraint: list[S]) -> S:
+ invalid_values = 2 * values_from_constraint
+ invalid_value = invalid_values.pop()
+ for value in invalid_values:
+ invalid_value = invalid_value + value
+
+ if not invalid_value:
+ raise ValueError("Value invalidation yielded an empty string.")
+ return invalid_value
+
@property
def can_be_invalidated(self) -> bool:
if (
@@ -369,6 +415,17 @@ def get_invalid_value_from_const_or_enum(self) -> int:
return invalid_value
+ def get_invalid_value_from_constraint(
+ self, values_from_constraint: list[int]
+ ) -> int:
+ invalid_values = 2 * values_from_constraint
+ invalid_value = invalid_values.pop()
+ for value in invalid_values:
+ invalid_value = abs(invalid_value) + abs(value)
+ if not invalid_value:
+ invalid_value += 1
+ return invalid_value
+
@property
def can_be_invalidated(self) -> bool:
return True
@@ -472,6 +529,17 @@ def get_invalid_value_from_const_or_enum(self) -> float:
return invalid_value
+ def get_invalid_value_from_constraint(
+ self, values_from_constraint: list[float]
+ ) -> float:
+ invalid_values = 2 * values_from_constraint
+ invalid_value = invalid_values.pop()
+ for value in invalid_values:
+ invalid_value = abs(invalid_value) + abs(value)
+ if not invalid_value:
+ invalid_value += 1
+ return invalid_value
+
@property
def can_be_invalidated(self) -> bool:
return True
@@ -481,21 +549,21 @@ def annotation_string(self) -> str:
return "float"
-class ArraySchema(SchemaBase[list[JSON]], frozen=True):
+class ArraySchema(SchemaBase[list[AI]], frozen=True):
type: Literal["array"] = "array"
items: SchemaObjectTypes
maxItems: int | None = None
minItems: int | None = None
uniqueItems: bool = False
- const: list[JSON] | None = None
- enum: list[list[JSON]] | None = None
+ const: list[AI] | None = None
+ enum: list[list[AI]] | None = None
nullable: bool = False
def get_valid_value(
self,
constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
- ) -> list[JSON]:
+ ) -> list[AI]:
if self.const is not None:
return self.const
@@ -506,15 +574,15 @@ def get_valid_value(
maximum = self.maxItems if self.maxItems is not None else 1
maximum = max(minimum, maximum)
- value: list[JSON] = []
+ value: list[AI] = []
number_of_items_to_generate = randint(minimum, maximum)
for _ in range(number_of_items_to_generate):
- item_value = self.items.get_valid_value()
+ item_value = cast("AI", self.items.get_valid_value())
value.append(item_value)
return value
- def get_values_out_of_bounds(self, current_value: list[JSON]) -> list[list[JSON]]:
- invalid_values: list[list[JSON]] = []
+ def get_values_out_of_bounds(self, current_value: list[AI]) -> list[list[AI]]:
+ invalid_values: list[list[AI]] = []
if self.minItems:
invalid_value = current_value[0 : self.minItems - 1]
@@ -526,7 +594,7 @@ def get_values_out_of_bounds(self, current_value: list[JSON]) -> list[list[JSON]
current_value = self.get_valid_value()
if not current_value:
- current_value = [self.items.get_valid_value()]
+ current_value = [self.items.get_valid_value()] # type: ignore
while len(invalid_value) <= self.maxItems:
invalid_value.append(choice(current_value))
@@ -537,7 +605,7 @@ def get_values_out_of_bounds(self, current_value: list[JSON]) -> list[list[JSON]
raise ValueError
- def get_invalid_value_from_const_or_enum(self) -> list[JSON]:
+ def get_invalid_value_from_const_or_enum(self) -> list[AI]:
valid_values = []
if self.const is not None:
valid_values = [self.const]
@@ -554,15 +622,29 @@ def get_invalid_value_from_const_or_enum(self) -> list[JSON]:
return invalid_value
+ def get_invalid_value_from_constraint(
+ self, values_from_constraint: list[list[AI]]
+ ) -> list[AI]:
+ values_from_constraint = deepcopy(values_from_constraint)
+
+ valid_array = values_from_constraint.pop()
+ invalid_array: list[AI] = []
+ for value in valid_array:
+ invalid_value = self.items.get_invalid_value_from_constraint(
+ values_from_constraint=[value],
+ )
+ invalid_array.append(invalid_value)
+ return invalid_array
+
def get_invalid_data(
self,
- valid_data: list[JSON],
+ valid_data: list[AI],
constraint_mapping: ConstraintMappingType,
status_code: int,
invalid_property_default_code: int,
- ) -> list[JSON]:
+ ) -> list[AI]:
"""Return a data set with one of the properties set to an invalid value or type."""
- invalid_values: list[list[JSON]] = []
+ invalid_values: list[list[AI]] = []
relations = constraint_mapping.get_body_relations_for_error_code(
error_code=status_code
@@ -582,7 +664,7 @@ def get_invalid_data(
invalid_values.append(invalid_const_or_enum)
except ValueError:
pass
- if isinstance(self.items, ObjectSchema):
+ if is_object_schema(self.items):
data_to_invalidate = deepcopy(valid_data)
valid_item = (
data_to_invalidate.pop()
@@ -590,7 +672,7 @@ def get_invalid_data(
else self.items.get_valid_value()
)
invalid_item = self.items.get_invalid_data(
- valid_data=valid_item,
+ valid_data=valid_item, # type: ignore
constraint_mapping=constraint_mapping,
status_code=status_code,
invalid_property_default_code=invalid_property_default_code,
@@ -659,7 +741,7 @@ def get_valid_value(
)
for property_name in property_names:
- property_schema = self.properties.root[property_name] # type: ignore[union-attr]
+ property_schema = self.properties.root[property_name]
if property_schema.readOnly:
continue
@@ -730,15 +812,32 @@ def get_invalid_value_from_const_or_enum(self) -> dict[str, JSON]:
raise ValueError
+ def get_invalid_value_from_constraint(
+ self, values_from_constraint: list[dict[str, JSON]]
+ ) -> dict[str, JSON]:
+ values_from_constraint = deepcopy(values_from_constraint)
+
+ valid_object = values_from_constraint.pop()
+ invalid_object: dict[str, JSON] = {}
+ for key, value in valid_object.items():
+ python_type_of_value = type(value)
+ json_type_of_value = json_type_name_of_python_type(python_type_of_value)
+ schema = MediaTypeObject(schema={"type": json_type_of_value}).schema_
+ invalid_value = schema.get_invalid_value_from_constraint(
+ values_from_constraint=[value],
+ )
+ invalid_object[key] = invalid_value
+ return invalid_object
+
def get_invalid_data(
self,
valid_data: dict[str, JSON],
constraint_mapping: ConstraintMappingType,
status_code: int,
invalid_property_default_code: int,
- ) -> dict[str, JSON]:
+ ) -> Mapping[str, JSON | Ignore]:
"""Return a data set with one of the properties set to an invalid value or type."""
- properties: dict[str, JSON] = deepcopy(valid_data)
+ properties: Mapping[str, JSON | Ignore] = deepcopy(valid_data)
relations = constraint_mapping.get_body_relations_for_error_code(
error_code=status_code
@@ -746,7 +845,7 @@ def get_invalid_data(
property_names = [r.property_name for r in relations]
if status_code == invalid_property_default_code:
# add all properties defined in the schema, including optional properties
- property_names.extend((self.properties.root.keys())) # type: ignore[union-attr]
+ property_names.extend((self.properties.root.keys()))
if not property_names:
raise ValueError(
f"No property can be invalidated to cause status_code {status_code}"
@@ -788,7 +887,7 @@ def get_invalid_data(
)
return properties
- value_schema = self.properties.root[property_name] # type: ignore[union-attr]
+ value_schema = self.properties.root[property_name]
if isinstance(value_schema, UnionTypeSchema):
# Filter "type": "null" from the possible types since this indicates an
# optional / nullable property that can only be invalidated by sending
@@ -821,10 +920,10 @@ def get_invalid_data(
]
invalid_value = value_schema.get_invalid_value(
- valid_value=current_value,
+ valid_value=current_value, # type: ignore
values_from_constraint=values_from_constraint,
)
- properties[property_name] = invalid_value
+ properties[property_name] = invalid_value # type: ignore
logger.debug(
f"Property {property_name} changed to {invalid_value!r} "
f"(received from get_invalid_value)"
@@ -934,7 +1033,7 @@ def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, No
merged_schema = ObjectSchema(
type="object",
- properties=properties,
+ properties=PropertiesMapping(root=properties),
additionalProperties=additional_properties_value,
required=required_list,
maxProperties=max_propeties_value,
@@ -952,6 +1051,11 @@ def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, No
def get_invalid_value_from_const_or_enum(self) -> JSON:
raise ValueError
+ def get_invalid_value_from_constraint(
+ self, values_from_constraint: list[JSON]
+ ) -> JSON:
+ raise ValueError
+
@property
def annotation_string(self) -> str:
unique_annotations = {s.annotation_string for s in self.resolved_schemas}
diff --git a/tests/libcore/unittests/value_utils/test_invalid_value_from_constraint.py b/tests/libcore/unittests/value_utils/test_invalid_value_from_constraint.py
index b0da351..b4bee34 100644
--- a/tests/libcore/unittests/value_utils/test_invalid_value_from_constraint.py
+++ b/tests/libcore/unittests/value_utils/test_invalid_value_from_constraint.py
@@ -3,146 +3,146 @@
from typing import Any
from OpenApiLibCore import IGNORE
-from OpenApiLibCore.data_generation import value_utils
+from OpenApiLibCore.models.oas_models import (
+ ArraySchema,
+ BooleanSchema,
+ IntegerSchema,
+ NullSchema,
+ NumberSchema,
+ ObjectSchema,
+ StringSchema,
+ UnionTypeSchema,
+)
class TestInvalidValueFromConstraint(unittest.TestCase):
- def test_ignore(self) -> None:
- values = [42, IGNORE]
- value = value_utils.get_invalid_value_from_constraint(
- values_from_constraint=values,
- value_type="irrelevant",
- )
- self.assertEqual(value, IGNORE)
-
- def test_unsupported(self) -> None:
- values = [{"red": 255, "green": 255, "blue": 255}]
- with self.assertRaises(ValueError):
- _ = value_utils.get_invalid_value_from_constraint(
- values_from_constraint=values,
- value_type="dummy",
- )
-
def test_bool(self) -> None:
+ schema = BooleanSchema()
values = [True]
- value = value_utils.get_invalid_value_from_constraint(
+ value = schema.get_invalid_value_from_constraint(
values_from_constraint=values,
- value_type="boolean",
)
self.assertNotIn(value, values)
self.assertIsInstance(value, bool)
values = [False]
- value = value_utils.get_invalid_value_from_constraint(
+ value = schema.get_invalid_value_from_constraint(
values_from_constraint=values,
- value_type="boolean",
)
self.assertNotIn(value, values)
self.assertIsInstance(value, bool)
values = [True, False]
with self.assertRaises(ValueError):
- _ = value_utils.get_invalid_value_from_constraint(
+ _ = schema.get_invalid_value_from_constraint(
values_from_constraint=values,
- value_type="boolean",
)
def test_string(self) -> None:
+ schema = StringSchema()
values = ["foo"]
- value = value_utils.get_invalid_value_from_constraint(
+ value = schema.get_invalid_value_from_constraint(
values_from_constraint=values,
- value_type="string",
)
self.assertNotIn(value, values)
self.assertIsInstance(value, str)
values = ["foo", "bar", "baz"]
- value = value_utils.get_invalid_value_from_constraint(
+ value = schema.get_invalid_value_from_constraint(
values_from_constraint=values,
- value_type="string",
)
self.assertNotIn(value, values)
self.assertIsInstance(value, str)
values = [""]
with self.assertRaises(ValueError):
- _ = value_utils.get_invalid_value_from_constraint(
+ _ = schema.get_invalid_value_from_constraint(
values_from_constraint=values,
- value_type="string",
)
def test_integer(self) -> None:
+ schema = IntegerSchema()
values = [0]
- value = value_utils.get_invalid_value_from_constraint(
+ value = schema.get_invalid_value_from_constraint(
values_from_constraint=values,
- value_type="integer",
)
self.assertNotIn(value, values)
self.assertIsInstance(value, int)
values = [-3, 0, 3]
- value = value_utils.get_invalid_value_from_constraint(
+ value = schema.get_invalid_value_from_constraint(
values_from_constraint=values,
- value_type="integer",
)
self.assertNotIn(value, values)
self.assertIsInstance(value, int)
def test_number(self) -> None:
+ schema = NumberSchema()
values = [0.0]
- value = value_utils.get_invalid_value_from_constraint(
+ value = schema.get_invalid_value_from_constraint(
values_from_constraint=values,
- value_type="number",
)
self.assertNotIn(value, values)
self.assertIsInstance(value, float)
values = [-0.1, 0.0, 0.1]
- value = value_utils.get_invalid_value_from_constraint(
+ value = schema.get_invalid_value_from_constraint(
values_from_constraint=values,
- value_type="number",
)
self.assertNotIn(value, values)
self.assertIsInstance(value, float)
def test_array(self) -> None:
- values: list[Any] = [[42]]
- value = value_utils.get_invalid_value_from_constraint(
+ schema = ArraySchema(items=IntegerSchema())
+ values = [[42]]
+ value = schema.get_invalid_value_from_constraint(
values_from_constraint=values,
- value_type="array",
)
self.assertNotIn(value, values)
+ for item in value:
+ self.assertIsInstance(item, int)
+ schema = ArraySchema(items=StringSchema())
values = [["spam"], ["ham", "eggs"]]
- value = value_utils.get_invalid_value_from_constraint(
+ value = schema.get_invalid_value_from_constraint(
values_from_constraint=values,
- value_type="array",
)
self.assertNotIn(value, values)
+ for item in value:
+ self.assertIsInstance(item, str)
- values = []
- with self.assertRaises(ValueError):
- _ = value_utils.get_invalid_value_from_constraint(
- values_from_constraint=values,
- value_type="array",
- )
-
+ schema = ArraySchema(items=ArraySchema(items=StringSchema()))
values = [[], []]
- value = value_utils.get_invalid_value_from_constraint(
+ value = schema.get_invalid_value_from_constraint(
values_from_constraint=values,
- value_type="array",
)
self.assertEqual(value, [])
def test_object(self) -> None:
+ schema = ObjectSchema()
values = [{"red": 255, "green": 255, "blue": 255}]
- value = value_utils.get_invalid_value_from_constraint(
+ value = schema.get_invalid_value_from_constraint(
values_from_constraint=values,
- value_type="object",
)
self.assertNotEqual(value, values[0])
self.assertIsInstance(value, dict)
+ def test_union(self) -> None:
+ schema = UnionTypeSchema()
+ values = [None]
+ with self.assertRaises(ValueError):
+ _ = schema.get_invalid_value_from_constraint(
+ values_from_constraint=values,
+ )
+
+ def test_null(self) -> None:
+ schema = NullSchema()
+ values = [None]
+ with self.assertRaises(ValueError):
+ _ = schema.get_invalid_value_from_constraint(
+ values_from_constraint=values,
+ )
+
if __name__ == "__main__":
unittest.main()
From 100bb86360291f0ce5b54b05e170151143b3ffb2 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Wed, 19 Nov 2025 14:10:37 +0000
Subject: [PATCH 19/55] remove unused method
---
.../data_constraints/dto_base.py | 18 +-----------------
src/OpenApiLibCore/protocols.py | 5 -----
2 files changed, 1 insertion(+), 22 deletions(-)
diff --git a/src/OpenApiLibCore/data_constraints/dto_base.py b/src/OpenApiLibCore/data_constraints/dto_base.py
index de49a35..31ab012 100644
--- a/src/OpenApiLibCore/data_constraints/dto_base.py
+++ b/src/OpenApiLibCore/data_constraints/dto_base.py
@@ -29,25 +29,9 @@ class Dto(ABC):
@staticmethod
def get_path_relations() -> list[PathPropertiesConstraint]:
- """Return the list of Relations for the header and query parameters."""
+ """Return the list of path-related Relations."""
return []
- @classmethod
- def get_path_relations_for_error_code(
- cls, error_code: int
- ) -> list[PathPropertiesConstraint]:
- """Return the list of Relations associated with the given error_code."""
- relations: list[PathPropertiesConstraint] = [
- r
- for r in cls.get_path_relations()
- if r.error_code == error_code
- or (
- getattr(r, "invalid_value_error_code", None) == error_code
- and getattr(r, "invalid_value", None) != NOT_SET
- )
- ]
- return relations
-
@staticmethod
def get_parameter_relations() -> list[ResourceRelation]:
"""Return the list of Relations for the header and query parameters."""
diff --git a/src/OpenApiLibCore/protocols.py b/src/OpenApiLibCore/protocols.py
index c884018..a1e097a 100644
--- a/src/OpenApiLibCore/protocols.py
+++ b/src/OpenApiLibCore/protocols.py
@@ -43,11 +43,6 @@ def __get_pydantic_core_schema__(
@staticmethod
def get_path_relations() -> list[PathPropertiesConstraint]: ...
- @classmethod
- def get_path_relations_for_error_code(
- cls, error_code: int
- ) -> list[PathPropertiesConstraint]: ...
-
@staticmethod
def get_parameter_relations() -> list[ResourceRelation]: ...
From 1d020eb82b58f1cf33d5d047ba2cf8f7e718d20e Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Wed, 19 Nov 2025 14:12:25 +0000
Subject: [PATCH 20/55] annotation improvements
---
.../keyword_logic/resource_relations.py | 30 +++++++++++++++++--
1 file changed, 27 insertions(+), 3 deletions(-)
diff --git a/src/OpenApiLibCore/keyword_logic/resource_relations.py b/src/OpenApiLibCore/keyword_logic/resource_relations.py
index 8454fde..9b0ed11 100644
--- a/src/OpenApiLibCore/keyword_logic/resource_relations.py
+++ b/src/OpenApiLibCore/keyword_logic/resource_relations.py
@@ -1,10 +1,13 @@
"""Module holding the functions related to relations between resources."""
+from typing import Literal, overload
+
from requests import Response
from robot.api import logger
from robot.libraries.BuiltIn import BuiltIn
import OpenApiLibCore.keyword_logic.path_functions as _path_functions
+from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.models.oas_models import OpenApiObject
from OpenApiLibCore.models.request_data import RequestData
from OpenApiLibCore.models.resource_relations import IdReference
@@ -12,6 +15,26 @@
run_keyword = BuiltIn().run_keyword
+@overload
+def _run_robot_keyword(
+ keyword_name: Literal["get_request_data"], *args: str
+) -> RequestData: ... # pragma: no cover
+
+
+@overload
+def _run_robot_keyword(keyword_name: Literal["get_valid_url"], *args: str) -> str: ... # pragma: no cover
+
+
+@overload
+def _run_robot_keyword(
+ keyword_name: Literal["authorized_request"], *args: JSON
+) -> Response: ... # pragma: no cover
+
+
+def _run_robot_keyword(keyword_name: str, *args: JSON) -> object:
+ return run_keyword(keyword_name, *args)
+
+
def ensure_in_use(
url: str,
base_url: str,
@@ -34,13 +57,14 @@ def ensure_in_use(
break
if not resource_id:
raise ValueError(f"The provided url ({url}) does not contain an id.")
- request_data: RequestData = run_keyword(
+ request_data: RequestData = _run_robot_keyword(
"get_request_data", resource_relation.post_path, "post"
)
json_data = request_data.valid_data if request_data.valid_data else {}
+ # FIXME: currently only works for object / dict data
json_data[resource_relation.property_name] = resource_id
- post_url: str = run_keyword("get_valid_url", resource_relation.post_path)
- response: Response = run_keyword(
+ post_url: str = _run_robot_keyword("get_valid_url", resource_relation.post_path)
+ response: Response = _run_robot_keyword(
"authorized_request",
post_url,
"post",
From 94ec1c3f0c853427d6c9fd3f8de1cdbcc48fd02c Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Thu, 20 Nov 2025 16:39:01 +0000
Subject: [PATCH 21/55] Missing tests for ID mapping added
---
src/OpenApiLibCore/protocols.py | 8 ++
tests/libcore/unittests/test_dto_utils.py | 76 ++++++++++++++++++-
.../user_implemented/custom_user_mappings.py | 11 ++-
3 files changed, 91 insertions(+), 4 deletions(-)
diff --git a/src/OpenApiLibCore/protocols.py b/src/OpenApiLibCore/protocols.py
index a1e097a..62f4dbf 100644
--- a/src/OpenApiLibCore/protocols.py
+++ b/src/OpenApiLibCore/protocols.py
@@ -32,6 +32,14 @@ def __call__(
self, path: str
) -> tuple[str, Callable[[str], str] | Callable[[int], int]]: ...
+ @property
+ def default_id_property_name(self) -> str: ...
+
+ @property
+ def id_mapping(
+ self,
+ ) -> dict[str, str | tuple[str, Callable[[str], str] | Callable[[int], int]]]: ...
+
class IConstraintMapping(Protocol):
@classmethod
diff --git a/tests/libcore/unittests/test_dto_utils.py b/tests/libcore/unittests/test_dto_utils.py
index 7fc5024..078f09c 100644
--- a/tests/libcore/unittests/test_dto_utils.py
+++ b/tests/libcore/unittests/test_dto_utils.py
@@ -4,7 +4,13 @@
import unittest
from OpenApiLibCore import Dto
-from OpenApiLibCore.data_constraints.dto_base import get_constraint_mapping_dict
+from OpenApiLibCore.data_constraints.dto_base import (
+ GetIdPropertyName,
+ get_constraint_mapping_dict,
+ get_id_property_name,
+ get_path_mapping_dict,
+)
+from OpenApiLibCore.utils.id_mapping import dummy_transformer
unittest_folder = pathlib.Path(__file__).parent.resolve()
mappings_path = (
@@ -12,7 +18,7 @@
)
-class TestGetDtoClass(unittest.TestCase):
+class TestConstraintMapping(unittest.TestCase):
mappings_module_name = ""
@classmethod
@@ -52,5 +58,71 @@ def test_mapped_returns_dto_class(self) -> None:
self.assertTrue(issubclass(value_constraints_mapping_dict[key], Dto))
+class TestPathMapping(unittest.TestCase):
+ mappings_module_name = ""
+
+ @classmethod
+ def setUpClass(cls) -> None:
+ if mappings_path.is_file():
+ mappings_folder = str(mappings_path.parent)
+ sys.path.append(mappings_folder)
+ cls.mappings_module_name = mappings_path.stem
+ print(f"added {mappings_folder} to path")
+ else:
+ assert False, "The mappings_path is not a file."
+
+ def test_no_mapping(self) -> None:
+ path_mapping_dict = get_path_mapping_dict("dummy")
+ self.assertDictEqual(path_mapping_dict, {})
+
+ def test_valid_mapping(self) -> None:
+ path_mapping_dict = get_path_mapping_dict(self.mappings_module_name)
+ self.assertIsInstance(path_mapping_dict, dict)
+ self.assertGreater(len(path_mapping_dict.keys()), 0)
+
+
+class TestIdPropertyNameMapping(unittest.TestCase):
+ mappings_module_name = ""
+
+ @classmethod
+ def setUpClass(cls) -> None:
+ if mappings_path.is_file():
+ mappings_folder = str(mappings_path.parent)
+ sys.path.append(mappings_folder)
+ cls.mappings_module_name = mappings_path.stem
+ print(f"added {mappings_folder} to path")
+ else:
+ assert False, "The mappings_path is not a file."
+
+ def test_no_mapping(self) -> None:
+ id_property_name_mapping = get_id_property_name("dummy", "identifier")
+ self.assertIsInstance(id_property_name_mapping, GetIdPropertyName)
+ self.assertEqual(
+ id_property_name_mapping.default_id_property_name, "identifier"
+ )
+ self.assertDictEqual(id_property_name_mapping.id_mapping, {})
+
+ def test_valid_mapping(self) -> None:
+ id_property_name_mapping = get_id_property_name(
+ self.mappings_module_name,
+ "id",
+ )
+ self.assertIsInstance(id_property_name_mapping, GetIdPropertyName)
+ self.assertEqual(id_property_name_mapping.default_id_property_name, "id")
+ self.assertIsInstance(id_property_name_mapping.id_mapping, dict)
+
+ not_mapped = id_property_name_mapping("/secret_message")
+ self.assertEqual(not_mapped[0], "id")
+ self.assertEqual(not_mapped[1], dummy_transformer)
+
+ default_transformer = id_property_name_mapping("/wagegroups")
+ self.assertEqual(default_transformer[0], "wagegroup_id")
+ self.assertEqual(default_transformer[1], dummy_transformer)
+
+ custom_transformer = id_property_name_mapping("/wagegroups/{wagegroup_id}")
+ self.assertEqual(custom_transformer[0], "wagegroup_id")
+ self.assertEqual(custom_transformer[1].__name__, "my_transformer")
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/tests/user_implemented/custom_user_mappings.py b/tests/user_implemented/custom_user_mappings.py
index 9aa6bf3..0d69753 100644
--- a/tests/user_implemented/custom_user_mappings.py
+++ b/tests/user_implemented/custom_user_mappings.py
@@ -1,4 +1,6 @@
# pylint: disable=invalid-name
+from typing import Callable
+
from OpenApiLibCore import (
IGNORE,
Dto,
@@ -184,12 +186,17 @@ def get_parameter_relations() -> list[ResourceRelation]:
("/secret_message", "get"): MessageDto,
}
+
+def my_transformer(identifier_name: str) -> str:
+ return identifier_name.replace("/", "_")
+
+
# NOTE: "/available_employees": "identification" is not mapped for testing purposes
-ID_MAPPING: dict[str, str] = {
+ID_MAPPING: dict[str, str | tuple[str, Callable[[str], str] | Callable[[int], int]]] = {
"/employees": "identification",
"/employees/{employee_id}": "identification",
"/wagegroups": "wagegroup_id",
- "/wagegroups/{wagegroup_id}": "wagegroup_id",
+ "/wagegroups/{wagegroup_id}": ("wagegroup_id", my_transformer),
"/wagegroups/{wagegroup_id}/employees": "identification",
}
From b4d1eb8848df39178e167738cdc578a8a6b4b2ba Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Thu, 20 Nov 2025 16:40:29 +0000
Subject: [PATCH 22/55] annotation improvements
---
.../keyword_logic/path_functions.py | 2 +-
.../keyword_logic/path_invalidation.py | 28 +++++++++++++------
.../keyword_logic/resource_relations.py | 16 ++++++-----
3 files changed, 30 insertions(+), 16 deletions(-)
diff --git a/src/OpenApiLibCore/keyword_logic/path_functions.py b/src/OpenApiLibCore/keyword_logic/path_functions.py
index 4364e1d..cc8ea53 100644
--- a/src/OpenApiLibCore/keyword_logic/path_functions.py
+++ b/src/OpenApiLibCore/keyword_logic/path_functions.py
@@ -30,7 +30,7 @@ def get_parametrized_path(path: str, openapi_spec: oas_models.OpenApiObject) ->
if path_parts[-1] == "":
_ = path_parts.pop(-1)
- spec_paths: list[str] = list(openapi_spec.paths.keys())
+ spec_paths = list(openapi_spec.paths.keys())
candidates: list[str] = []
diff --git a/src/OpenApiLibCore/keyword_logic/path_invalidation.py b/src/OpenApiLibCore/keyword_logic/path_invalidation.py
index 14f1e28..83c7449 100644
--- a/src/OpenApiLibCore/keyword_logic/path_invalidation.py
+++ b/src/OpenApiLibCore/keyword_logic/path_invalidation.py
@@ -1,6 +1,7 @@
"""Module holding functions related to invalidation of paths and urls."""
from random import choice
+from typing import Literal, overload
from uuid import uuid4
from robot.libraries.BuiltIn import BuiltIn
@@ -10,19 +11,30 @@
run_keyword = BuiltIn().run_keyword
+@overload
+def _run_keyword(
+ keyword_name: Literal["get_parameterized_path_from_url"], *args: str
+) -> str: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["overload_default"], *args: object
+) -> object: ... # pragma: no cover
+
+
+def _run_keyword(keyword_name: str, *args: object) -> object:
+ return run_keyword(keyword_name, *args)
+
+
def get_invalidated_url(
valid_url: str,
base_url: str,
openapi_spec: oas_models.OpenApiObject,
expected_status_code: int,
) -> str:
- path: str = run_keyword("get_parameterized_path_from_url", valid_url)
- try:
- path_item = openapi_spec.paths[path]
- except KeyError:
- raise ValueError(
- f"{path} not found in paths section of the OpenAPI document."
- ) from None
+ path = _run_keyword("get_parameterized_path_from_url", valid_url)
+ path_item = openapi_spec.paths[path]
constraint_mapping = path_item.constraint_mapping
relations = constraint_mapping.get_path_relations() if constraint_mapping else []
@@ -34,7 +46,7 @@ def get_invalidated_url(
if paths:
url = f"{base_url}{choice(paths)}"
return url
- parameterized_path: str = run_keyword("get_parameterized_path_from_url", valid_url)
+ parameterized_path = _run_keyword("get_parameterized_path_from_url", valid_url)
parameterized_url = base_url + parameterized_path
valid_url_parts = list(reversed(valid_url.split("/")))
parameterized_parts = reversed(parameterized_url.split("/"))
diff --git a/src/OpenApiLibCore/keyword_logic/resource_relations.py b/src/OpenApiLibCore/keyword_logic/resource_relations.py
index 9b0ed11..57b11e8 100644
--- a/src/OpenApiLibCore/keyword_logic/resource_relations.py
+++ b/src/OpenApiLibCore/keyword_logic/resource_relations.py
@@ -16,22 +16,24 @@
@overload
-def _run_robot_keyword(
+def _run_keyword(
keyword_name: Literal["get_request_data"], *args: str
) -> RequestData: ... # pragma: no cover
@overload
-def _run_robot_keyword(keyword_name: Literal["get_valid_url"], *args: str) -> str: ... # pragma: no cover
+def _run_keyword(
+ keyword_name: Literal["get_valid_url"], *args: str
+) -> str: ... # pragma: no cover
@overload
-def _run_robot_keyword(
+def _run_keyword(
keyword_name: Literal["authorized_request"], *args: JSON
) -> Response: ... # pragma: no cover
-def _run_robot_keyword(keyword_name: str, *args: JSON) -> object:
+def _run_keyword(keyword_name: str, *args: JSON) -> object:
return run_keyword(keyword_name, *args)
@@ -57,14 +59,14 @@ def ensure_in_use(
break
if not resource_id:
raise ValueError(f"The provided url ({url}) does not contain an id.")
- request_data: RequestData = _run_robot_keyword(
+ request_data: RequestData = _run_keyword(
"get_request_data", resource_relation.post_path, "post"
)
json_data = request_data.valid_data if request_data.valid_data else {}
# FIXME: currently only works for object / dict data
json_data[resource_relation.property_name] = resource_id
- post_url: str = _run_robot_keyword("get_valid_url", resource_relation.post_path)
- response: Response = _run_robot_keyword(
+ post_url: str = _run_keyword("get_valid_url", resource_relation.post_path)
+ response: Response = _run_keyword(
"authorized_request",
post_url,
"post",
From e77901464ea1c42ee4e18da3b8a92f3c07e80a5d Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Thu, 20 Nov 2025 16:40:52 +0000
Subject: [PATCH 23/55] no cover markers added
---
src/OpenApiLibCore/models/__init__.py | 4 ++--
src/OpenApiLibCore/utils/parameter_utils.py | 6 ++++--
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/OpenApiLibCore/models/__init__.py b/src/OpenApiLibCore/models/__init__.py
index 9b05208..dd35889 100644
--- a/src/OpenApiLibCore/models/__init__.py
+++ b/src/OpenApiLibCore/models/__init__.py
@@ -2,14 +2,14 @@ class Ignore:
"""Helper class to flag properties to be ignored in data generation."""
def __str__(self) -> str:
- return "IGNORE"
+ return "IGNORE" # pragma: no cover
class UnSet:
"""Helper class to flag arguments that have not been set in a keyword call."""
def __str__(self) -> str:
- return "UNSET"
+ return "UNSET" # pragma: no cover
IGNORE = Ignore()
diff --git a/src/OpenApiLibCore/utils/parameter_utils.py b/src/OpenApiLibCore/utils/parameter_utils.py
index a8d3450..df8fde3 100644
--- a/src/OpenApiLibCore/utils/parameter_utils.py
+++ b/src/OpenApiLibCore/utils/parameter_utils.py
@@ -70,9 +70,11 @@ def _convert_string_to_python_identifier() -> Generator[str, None, None]:
yield f"_{ascii_code}_"
if _is_python_safe(string):
- return string
+ return string # pragma: no cover
converted_string = "".join(_convert_string_to_python_identifier())
if not _is_python_safe(converted_string):
- raise ValueError(f"Failed to convert '{string}' to Python identifier.")
+ raise ValueError(
+ f"Failed to convert '{string}' to Python identifier."
+ ) # pragma: no cover
return converted_string
From 08d878e7ed9851efc920fe96371dfabc759541fd Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Tue, 25 Nov 2025 14:46:52 +0000
Subject: [PATCH 24/55] Addressing typing issues
---
src/OpenApiDriver/openapi_executors.py | 108 ++++++----
src/OpenApiDriver/openapidriver.py | 2 +-
.../data_constraints/dto_base.py | 10 +-
.../data_generation/data_generation_core.py | 8 +-
.../data_generation/data_invalidation.py | 96 ++++++---
.../keyword_logic/path_functions.py | 62 ++++--
.../keyword_logic/resource_relations.py | 18 +-
.../keyword_logic/validation.py | 59 ++++--
src/OpenApiLibCore/models/oas_models.py | 195 ++++++++++++++----
src/OpenApiLibCore/models/request_data.py | 32 ++-
src/OpenApiLibCore/openapi_libcore.py | 42 ++--
src/OpenApiLibCore/protocols.py | 6 +-
src/OpenApiLibCore/utils/id_mapping.py | 13 +-
src/openapi_libgen/command_line.py | 8 +-
src/openapi_libgen/generator.py | 6 +-
.../suites/test_with_altered_schema.robot | 3 +
tests/files/altered_openapi.json | 3 +-
.../suites/test_schema_variations.robot | 2 +-
.../libcore/unittests/test_get_valid_value.py | 33 ++-
19 files changed, 504 insertions(+), 202 deletions(-)
diff --git a/src/OpenApiDriver/openapi_executors.py b/src/OpenApiDriver/openapi_executors.py
index cf7aa9f..ba8a5b1 100644
--- a/src/OpenApiDriver/openapi_executors.py
+++ b/src/OpenApiDriver/openapi_executors.py
@@ -6,6 +6,7 @@
from pathlib import Path
from random import choice
from types import MappingProxyType
+from typing import Literal, overload
from requests import Response
from requests.auth import AuthBase
@@ -39,6 +40,40 @@
]
+@overload
+def _run_keyword(
+ keyword_name: Literal["get_valid_url"], *args: str
+) -> str: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["authorized_request"], *args: object
+) -> Response: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["get_request_data"], *args: str
+) -> RequestData: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["get_invalid_body_data"], *args: object
+) -> dict[str, JSON] | list[JSON]: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["get_invalidated_parameters"], *args: object
+) -> tuple[dict[str, JSON], dict[str, str]]: ... # pragma: no cover
+
+
+def _run_keyword(keyword_name: str, *args: object) -> object:
+ return run_keyword(keyword_name, *args)
+
+
@library(scope="SUITE", doc_format="ROBOT")
class OpenApiExecutors(OpenApiLibCore):
"""Main class providing the keywords and core logic to perform endpoint validations."""
@@ -103,7 +138,7 @@ def test_unauthorized(self, path: str, method: str) -> None:
> Note: No headers or (json) body are send with the request. For security
reasons, the authorization validation should be checked first.
"""
- url: str = run_keyword("get_valid_url", path)
+ url = _run_keyword("get_valid_url", path)
response = self.session.request(
method=method,
url=url,
@@ -124,8 +159,8 @@ def test_forbidden(self, path: str, method: str) -> None:
> Note: No headers or (json) body are send with the request. For security
reasons, the access rights validation should be checked first.
"""
- url: str = run_keyword("get_valid_url", path)
- response: Response = run_keyword("authorized_request", url, method)
+ url = _run_keyword("get_valid_url", path)
+ response = _run_keyword("authorized_request", url, method)
if response.status_code != int(HTTPStatus.FORBIDDEN):
raise AssertionError(f"Response {response.status_code} was not 403.")
@@ -149,7 +184,7 @@ def test_invalid_url(
parameters are send with the request. The `require_body_for_invalid_url`
parameter can be set to `True` if needed.
"""
- valid_url: str = run_keyword("get_valid_url", path)
+ valid_url = _run_keyword("get_valid_url", path)
try:
url = run_keyword("get_invalidated_url", valid_url, expected_status_code)
@@ -165,11 +200,11 @@ def test_invalid_url(
params, headers, json_data = None, None, None
if self.require_body_for_invalid_url:
- request_data: RequestData = run_keyword("get_request_data", path, method)
+ request_data = _run_keyword("get_request_data", path, method)
params = request_data.params
headers = request_data.headers
json_data = request_data.valid_data
- response: Response = run_keyword(
+ response = _run_keyword(
"authorized_request", url, method, params, headers, json_data
)
if response.status_code != expected_status_code:
@@ -191,8 +226,8 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
"""
original_data = {}
- url: str = run_keyword("get_valid_url", path)
- request_data: RequestData = run_keyword("get_request_data", path, method)
+ url = _run_keyword("get_valid_url", path)
+ request_data = _run_keyword("get_request_data", path, method)
params = request_data.params
headers = request_data.headers
json_data = request_data.valid_data
@@ -201,21 +236,7 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
original_data = self.get_original_data(url=url)
# in case of a status code indicating an error, ensure the error occurs
if status_code >= int(HTTPStatus.BAD_REQUEST):
- invalidation_keyword_data = {
- "get_invalid_body_data": [
- "get_invalid_body_data",
- url,
- method,
- status_code,
- request_data,
- ],
- "get_invalidated_parameters": [
- "get_invalidated_parameters",
- status_code,
- request_data,
- ],
- }
- invalidation_keywords = []
+ invalidation_keywords: list[str] = []
if request_data.constraint_mapping.get_body_relations_for_error_code(
status_code
@@ -226,15 +247,18 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
):
invalidation_keywords.append("get_invalidated_parameters")
if invalidation_keywords:
- if (
- invalidation_keyword := choice(invalidation_keywords)
- ) == "get_invalid_body_data":
- json_data = run_keyword(
- *invalidation_keyword_data[invalidation_keyword]
+ invalidation_keyword = choice(invalidation_keywords)
+ if invalidation_keyword == "get_invalid_body_data":
+ json_data = _run_keyword(
+ "get_invalid_body_data",
+ url,
+ method,
+ status_code,
+ request_data,
)
else:
- params, headers = run_keyword(
- *invalidation_keyword_data[invalidation_keyword]
+ params, headers = _run_keyword(
+ "get_invalidated_parameters", status_code, request_data
)
# if there are no relations to invalide and the status_code is the default
# response_code for invalid properties, invalidate properties instead
@@ -243,16 +267,20 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
request_data.params_that_can_be_invalidated
or request_data.headers_that_can_be_invalidated
):
- params, headers = run_keyword(
- *invalidation_keyword_data["get_invalidated_parameters"]
+ params, headers = _run_keyword(
+ "get_invalidated_parameters", status_code, request_data
)
if request_data.body_schema:
- json_data = run_keyword(
- *invalidation_keyword_data["get_invalid_body_data"]
+ json_data = _run_keyword(
+ "get_invalid_body_data",
+ url,
+ method,
+ status_code,
+ request_data,
)
elif request_data.body_schema:
- json_data = run_keyword(
- *invalidation_keyword_data["get_invalid_body_data"]
+ json_data = _run_keyword(
+ "get_invalid_body_data", url, method, status_code, request_data
)
else:
raise SkipExecution(
@@ -281,8 +309,8 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
or request_data.has_optional_headers
):
logger.info("Performing request without optional properties and parameters")
- url = run_keyword("get_valid_url", path)
- request_data = run_keyword("get_request_data", path, method)
+ url = _run_keyword("get_valid_url", path)
+ request_data = _run_keyword("get_request_data", path, method)
params = request_data.get_required_params()
headers = request_data.get_required_headers()
if isinstance(request_data.body_schema, ObjectSchema):
@@ -316,10 +344,10 @@ def get_original_data(self, url: str) -> dict[str, JSON]:
"""
original_data = {}
path = self.get_parameterized_path_from_url(url)
- get_request_data: RequestData = run_keyword("get_request_data", path, "GET")
+ get_request_data = _run_keyword("get_request_data", path, "GET")
get_params = get_request_data.params
get_headers = get_request_data.headers
- response: Response = run_keyword(
+ response = _run_keyword(
"authorized_request", url, "GET", get_params, get_headers
)
if response.ok:
diff --git a/src/OpenApiDriver/openapidriver.py b/src/OpenApiDriver/openapidriver.py
index e0c9323..ded7560 100644
--- a/src/OpenApiDriver/openapidriver.py
+++ b/src/OpenApiDriver/openapidriver.py
@@ -84,7 +84,7 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
read_paths_method = self.read_paths
DataDriver.__init__(
self,
- reader_class=OpenApiReader,
+ reader_class=OpenApiReader, # type: ignore[arg-type]
read_paths_method=read_paths_method,
included_paths=included_paths,
ignored_paths=ignored_paths,
diff --git a/src/OpenApiLibCore/data_constraints/dto_base.py b/src/OpenApiLibCore/data_constraints/dto_base.py
index 31ab012..5599511 100644
--- a/src/OpenApiLibCore/data_constraints/dto_base.py
+++ b/src/OpenApiLibCore/data_constraints/dto_base.py
@@ -83,7 +83,7 @@ def get_constraint_mapping_dict(
) -> dict[tuple[str, str], IConstraintMapping]:
try:
mappings_module = import_module(mappings_module_name)
- return mappings_module.DTO_MAPPING
+ return mappings_module.DTO_MAPPING # type: ignore[no-any-return]
except (ImportError, AttributeError, ValueError) as exception:
if mappings_module_name != "no mapping":
logger.error(f"DTO_MAPPING was not imported: {exception}")
@@ -95,7 +95,7 @@ def get_path_mapping_dict(
) -> dict[str, IConstraintMapping]:
try:
mappings_module = import_module(mappings_module_name)
- return mappings_module.PATH_MAPPING
+ return mappings_module.PATH_MAPPING # type: ignore[no-any-return]
except (ImportError, AttributeError, ValueError) as exception:
if mappings_module_name != "no mapping":
logger.error(f"PATH_MAPPING was not imported: {exception}")
@@ -125,16 +125,14 @@ def __init__(
mappings_module = import_module(mappings_module_name)
self.id_mapping: dict[
str,
- str | tuple[str, Callable[[str], str] | Callable[[int], int]],
+ str | tuple[str, Callable[[str], str]],
] = mappings_module.ID_MAPPING
except (ImportError, AttributeError, ValueError) as exception:
if mappings_module_name != "no mapping":
logger.error(f"ID_MAPPING was not imported: {exception}")
self.id_mapping = {}
- def __call__(
- self, path: str
- ) -> tuple[str, Callable[[str], str] | Callable[[int], int]]:
+ def __call__(self, path: str) -> tuple[str, Callable[[str], str]]:
try:
value_or_mapping = self.id_mapping[path]
if isinstance(value_or_mapping, str):
diff --git a/src/OpenApiLibCore/data_generation/data_generation_core.py b/src/OpenApiLibCore/data_generation/data_generation_core.py
index ee74c07..0a772dc 100644
--- a/src/OpenApiLibCore/data_generation/data_generation_core.py
+++ b/src/OpenApiLibCore/data_generation/data_generation_core.py
@@ -83,7 +83,11 @@ def get_request_data(
f"No supported content schema found: {operation_spec.requestBody.content}"
)
- headers.update({"content-type": operation_spec.requestBody.mime_type})
+ if operation_spec.requestBody.mime_type:
+ headers.update({"content-type": operation_spec.requestBody.mime_type})
+
+ if isinstance(body_schema, UnionTypeSchema):
+ body_schema = choice(body_schema.resolved_schemas)
valid_data = get_valid_json_data(
schema=body_schema,
@@ -123,7 +127,7 @@ def _get_mapping_dataclass_for_empty_body(
def _get_mapping_dataclass_from_valid_data(
- schema: ResolvedSchemaObjectTypes,
+ schema: ResolvedSchemaObjectTypes, # type: ignore[type-arg]
constraint_mapping: type[IConstraintMapping] | None,
valid_data: JSON,
method_spec: OperationObject,
diff --git a/src/OpenApiLibCore/data_generation/data_invalidation.py b/src/OpenApiLibCore/data_generation/data_invalidation.py
index 3e3f102..8664f24 100644
--- a/src/OpenApiLibCore/data_generation/data_invalidation.py
+++ b/src/OpenApiLibCore/data_generation/data_invalidation.py
@@ -5,7 +5,7 @@
from copy import deepcopy
from random import choice
-from typing import Any
+from typing import Any, Literal, overload
from requests import Response
from robot.api import logger
@@ -15,7 +15,7 @@
from OpenApiLibCore.data_constraints.dto_base import (
Dto,
)
-from OpenApiLibCore.models import IGNORE
+from OpenApiLibCore.models import IGNORE, Ignore
from OpenApiLibCore.models.oas_models import (
ArraySchema,
ObjectSchema,
@@ -33,13 +33,41 @@
run_keyword = BuiltIn().run_keyword
+@overload
+def _run_keyword(
+ keyword_name: Literal["get_json_data_with_conflict"], *args: object
+) -> dict[str, JSON]: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["ensure_in_use"], *args: object
+) -> None: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["get_request_data"], *args: str
+) -> RequestData: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["authorized_request"], *args: object
+) -> Response: ... # pragma: no cover
+
+
+def _run_keyword(keyword_name: str, *args: object) -> object:
+ return run_keyword(keyword_name, *args)
+
+
def get_invalid_body_data(
url: str,
method: str,
status_code: int,
request_data: RequestData,
invalid_data_default_response: int,
-) -> dict[str, JSON] | list[JSON]:
+) -> JSON:
method = method.lower()
data_relations = request_data.constraint_mapping.get_body_relations_for_error_code(
status_code
@@ -56,7 +84,7 @@ def get_invalid_body_data(
if isinstance(request_data.body_schema, ArraySchema):
if not isinstance(request_data.valid_data, list):
raise ValueError("Type of valid_data does not match body_schema type.")
- invalid_item_data = request_data.body_schema.get_invalid_data(
+ invalid_item_data: list[JSON] = request_data.body_schema.get_invalid_data(
valid_data=request_data.valid_data,
constraint_mapping=request_data.constraint_mapping,
status_code=status_code,
@@ -76,7 +104,7 @@ def get_invalid_body_data(
resource_relation = choice(data_relations)
if isinstance(resource_relation, UniquePropertyValueConstraint):
- json_data = run_keyword(
+ return _run_keyword(
"get_json_data_with_conflict",
url,
method,
@@ -84,26 +112,41 @@ def get_invalid_body_data(
request_data.constraint_mapping,
status_code,
)
- elif isinstance(resource_relation, IdReference):
- run_keyword("ensure_in_use", url, resource_relation)
- json_data = request_data.valid_data
- else:
- if request_data.body_schema is None:
- raise ValueError(
- "Failed to invalidate: request_data does not contain a body_schema."
- )
- json_data = request_data.body_schema.get_invalid_data(
+ if isinstance(resource_relation, IdReference):
+ _run_keyword("ensure_in_use", url, resource_relation)
+ return request_data.valid_data
+
+ if request_data.body_schema is None:
+ raise ValueError(
+ "Failed to invalidate: request_data does not contain a body_schema."
+ )
+ if not isinstance(request_data.body_schema, (ArraySchema, ObjectSchema)):
+ raise NotImplementedError("primitive types not supported for body data.")
+
+ if isinstance(request_data.body_schema, ArraySchema):
+ if not isinstance(request_data.valid_data, list):
+ raise ValueError("Type of valid_data does not match body_schema type.")
+ invalid_item_data = request_data.body_schema.get_invalid_data(
valid_data=request_data.valid_data,
constraint_mapping=request_data.constraint_mapping,
status_code=status_code,
invalid_property_default_code=invalid_data_default_response,
)
- return json_data
+ return [invalid_item_data]
+
+ if not isinstance(request_data.valid_data, dict):
+ raise ValueError("Type of valid_data does not match body_schema type.")
+ return request_data.body_schema.get_invalid_data(
+ valid_data=request_data.valid_data,
+ constraint_mapping=request_data.constraint_mapping,
+ status_code=status_code,
+ invalid_property_default_code=invalid_data_default_response,
+ )
def get_invalidated_parameters(
status_code: int, request_data: RequestData, invalid_data_default_response: int
-) -> tuple[dict[str, JSON], dict[str, JSON]]:
+) -> tuple[dict[str, JSON], dict[str, str]]:
if not request_data.parameters:
raise ValueError("No params or headers to invalidate.")
@@ -232,14 +275,14 @@ def get_invalidated_parameters(
value_schema = choice(value_schema.resolved_schemas)
invalid_value = value_schema.get_invalid_value(
- valid_value=valid_value,
+ valid_value=valid_value, # type: ignore[arg-type]
values_from_constraint=values_from_constraint,
)
logger.debug(f"{parameter_to_invalidate} changed to {invalid_value}")
# update the params / headers and return
if parameter_to_invalidate in params.keys():
- params[parameter_to_invalidate] = invalid_value
+ params[parameter_to_invalidate] = invalid_value # pyright: ignore[reportArgumentType]
else:
headers[parameter_to_invalidate] = str(invalid_value)
return params, headers
@@ -248,10 +291,10 @@ def get_invalidated_parameters(
def ensure_parameter_in_parameters(
parameter_to_invalidate: str,
params: dict[str, JSON],
- headers: dict[str, JSON],
+ headers: dict[str, str],
parameter_data: ParameterObject,
values_from_constraint: list[JSON],
-) -> tuple[dict[str, JSON], dict[str, JSON]]:
+) -> tuple[dict[str, JSON], dict[str, str]]:
"""
Returns the params, headers tuple with parameter_to_invalidate with a valid
value to params or headers if not originally present.
@@ -306,18 +349,19 @@ def get_json_data_with_conflict(
# the PATCH or PUT may use a different constraint_mapping than required for
# POST so valid POST data must be constructed
path = post_url.replace(base_url, "")
- request_data: RequestData = run_keyword("get_request_data", path, "post")
+ request_data = _run_keyword("get_request_data", path, "post")
post_json = request_data.valid_data
- for key in post_json.keys():
- if key in json_data:
- post_json[key] = json_data.get(key)
+ if isinstance(post_json, dict):
+ for key in post_json.keys():
+ if key in json_data:
+ post_json[key] = json_data.get(key)
else:
post_url = url
post_json = json_data
path = post_url.replace(base_url, "")
- request_data = run_keyword("get_request_data", path, "post")
+ request_data = _run_keyword("get_request_data", path, "post")
- response: Response = run_keyword(
+ response = _run_keyword(
"authorized_request",
post_url,
"post",
diff --git a/src/OpenApiLibCore/keyword_logic/path_functions.py b/src/OpenApiLibCore/keyword_logic/path_functions.py
index cc8ea53..3ac3f97 100644
--- a/src/OpenApiLibCore/keyword_logic/path_functions.py
+++ b/src/OpenApiLibCore/keyword_logic/path_functions.py
@@ -3,7 +3,7 @@
import json as _json
from itertools import zip_longest
from random import choice
-from typing import Any
+from typing import Any, Literal, overload
from requests import Response
from robot.libraries.BuiltIn import BuiltIn
@@ -14,6 +14,46 @@
run_keyword = BuiltIn().run_keyword
+@overload
+def _run_keyword(
+ keyword_name: Literal["get_valid_id_for_path"], *args: str
+) -> str: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["get_ids_from_url"], *args: str
+) -> list[str]: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["get_valid_url"], *args: str
+) -> str: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["get_parameterized_path_from_url"], *args: str
+) -> str: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["get_request_data"], *args: str
+) -> RequestData: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["authorized_request"], *args: object
+) -> Response: ... # pragma: no cover
+
+
+def _run_keyword(keyword_name: str, *args: object) -> object:
+ return run_keyword(keyword_name, *args)
+
+
def match_parts(parts: list[str], spec_parts: list[str]) -> bool:
for part, spec_part in zip_longest(parts, spec_parts, fillvalue="Filler"):
if part == "Filler" or spec_part == "Filler":
@@ -84,9 +124,7 @@ def get_valid_url(
if part.startswith("{") and part.endswith("}"):
type_path_parts = path_parts[slice(index)]
type_path = "/".join(type_path_parts)
- existing_id: str | int | float = run_keyword(
- "get_valid_id_for_path", type_path
- )
+ existing_id = _run_keyword("get_valid_id_for_path", type_path)
path_parts[index] = str(existing_id)
resolved_path = "/".join(path_parts)
url = f"{base_url}{resolved_path}"
@@ -96,13 +134,13 @@ def get_valid_url(
def get_valid_id_for_path(
path: str,
openapi_spec: oas_models.OpenApiObject,
-) -> str | int:
- url: str = run_keyword("get_valid_url", path)
+) -> str:
+ url = _run_keyword("get_valid_url", path)
# Try to create a new resource to prevent conflicts caused by
# operations performed on the same resource by other test cases
- request_data: RequestData = run_keyword("get_request_data", path, "post")
+ request_data = _run_keyword("get_request_data", path, "post")
- response: Response = run_keyword(
+ response = _run_keyword(
"authorized_request",
url,
"post",
@@ -118,7 +156,7 @@ def get_valid_id_for_path(
# If a new resource cannot be created using POST, try to retrieve a
# valid id using a GET request.
try:
- valid_id = choice(run_keyword("get_ids_from_url", url))
+ valid_id = choice(_run_keyword("get_ids_from_url", url))
return id_transformer(valid_id)
except Exception as exception:
raise AssertionError(
@@ -174,9 +212,9 @@ def get_ids_from_url(
url: str,
openapi_spec: oas_models.OpenApiObject,
) -> list[str]:
- path: str = run_keyword("get_parameterized_path_from_url", url)
- request_data: RequestData = run_keyword("get_request_data", path, "get")
- response = run_keyword(
+ path = _run_keyword("get_parameterized_path_from_url", url)
+ request_data = _run_keyword("get_request_data", path, "get")
+ response = _run_keyword(
"authorized_request",
url,
"get",
diff --git a/src/OpenApiLibCore/keyword_logic/resource_relations.py b/src/OpenApiLibCore/keyword_logic/resource_relations.py
index 57b11e8..0cfeb7e 100644
--- a/src/OpenApiLibCore/keyword_logic/resource_relations.py
+++ b/src/OpenApiLibCore/keyword_logic/resource_relations.py
@@ -7,7 +7,6 @@
from robot.libraries.BuiltIn import BuiltIn
import OpenApiLibCore.keyword_logic.path_functions as _path_functions
-from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.models.oas_models import OpenApiObject
from OpenApiLibCore.models.request_data import RequestData
from OpenApiLibCore.models.resource_relations import IdReference
@@ -29,11 +28,11 @@ def _run_keyword(
@overload
def _run_keyword(
- keyword_name: Literal["authorized_request"], *args: JSON
+ keyword_name: Literal["authorized_request"], *args: object
) -> Response: ... # pragma: no cover
-def _run_keyword(keyword_name: str, *args: JSON) -> object:
+def _run_keyword(keyword_name: str, *args: object) -> object:
return run_keyword(keyword_name, *args)
@@ -59,14 +58,13 @@ def ensure_in_use(
break
if not resource_id:
raise ValueError(f"The provided url ({url}) does not contain an id.")
- request_data: RequestData = _run_keyword(
- "get_request_data", resource_relation.post_path, "post"
- )
+ request_data = _run_keyword("get_request_data", resource_relation.post_path, "post")
json_data = request_data.valid_data if request_data.valid_data else {}
# FIXME: currently only works for object / dict data
- json_data[resource_relation.property_name] = resource_id
- post_url: str = _run_keyword("get_valid_url", resource_relation.post_path)
- response: Response = _run_keyword(
+ if isinstance(json_data, dict):
+ json_data[resource_relation.property_name] = resource_id
+ post_url = _run_keyword("get_valid_url", resource_relation.post_path)
+ response = _run_keyword(
"authorized_request",
post_url,
"post",
@@ -76,6 +74,6 @@ def ensure_in_use(
)
if not response.ok:
logger.debug(
- f"POST on {post_url} with json {json_data} failed: {response.json()}"
+ f"POST on {post_url} with json {json_data!r} failed: {response.json()}"
)
response.raise_for_status()
diff --git a/src/OpenApiLibCore/keyword_logic/validation.py b/src/OpenApiLibCore/keyword_logic/validation.py
index a00c185..2de9e86 100644
--- a/src/OpenApiLibCore/keyword_logic/validation.py
+++ b/src/OpenApiLibCore/keyword_logic/validation.py
@@ -3,7 +3,7 @@
import json as _json
from enum import Enum
from http import HTTPStatus
-from typing import Any, Mapping
+from typing import Any, Literal, Mapping, overload
from openapi_core.contrib.requests import (
RequestsOpenAPIRequest,
@@ -18,6 +18,7 @@
from robot.api.exceptions import Failure
from robot.libraries.BuiltIn import BuiltIn
+from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.models.oas_models import (
OpenApiObject,
ResponseObject,
@@ -29,6 +30,40 @@
run_keyword = BuiltIn().run_keyword
+@overload
+def _run_keyword(
+ keyword_name: Literal["validate_response"], *args: object
+) -> None: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["authorized_request"], *args: object
+) -> Response: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["get_request_data"], *args: str
+) -> RequestData: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["validate_send_response"], *args: Response | JSON
+) -> None: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["assert_href_to_resource_is_valid"], *args: str | JSON
+) -> None: ... # pragma: no cover
+
+
+def _run_keyword(keyword_name: str, *args: object) -> object:
+ return run_keyword(keyword_name, *args)
+
+
class ValidationLevel(str, Enum):
"""The available levels for the response_validation parameter."""
@@ -44,7 +79,7 @@ def perform_validated_request(
request_values: RequestValues,
original_data: Mapping[str, Any],
) -> None:
- response = run_keyword(
+ response = _run_keyword(
"authorized_request",
request_values.url,
request_values.method,
@@ -78,13 +113,13 @@ def perform_validated_request(
f"Response status_code {response.status_code} was not {status_code}."
)
- run_keyword("validate_response", path, response, original_data)
+ _run_keyword("validate_response", path, response, original_data)
if request_values.method == "DELETE":
- request_data: RequestData = run_keyword("get_request_data", path, "GET")
+ request_data = _run_keyword("get_request_data", path, "GET")
get_params = request_data.params
get_headers = request_data.headers
- get_response = run_keyword(
+ get_response = _run_keyword(
"authorized_request", request_values.url, "GET", get_params, get_headers
)
if response.ok:
@@ -115,7 +150,7 @@ def validate_response(
invalid_data_default_response: int,
response_validation: str,
openapi_spec: OpenApiObject,
- original_data: Mapping[str, Any],
+ original_data: JSON,
) -> None:
if response.status_code == int(HTTPStatus.NO_CONTENT):
assert not response.content
@@ -188,26 +223,26 @@ def validate_response(
# ensure the href is valid if the response is an object that contains a href
if isinstance(json_response, dict):
if href := json_response.get("href"):
- run_keyword("assert_href_to_resource_is_valid", href, json_response)
+ _run_keyword("assert_href_to_resource_is_valid", href, json_response)
# every property that was sucessfully send and that is in the response
# schema must have the value that was send
if response.ok and response.request.method in ["POST", "PUT", "PATCH"]:
- run_keyword("validate_send_response", response, original_data)
+ _run_keyword("validate_send_response", response, original_data)
return None
def assert_href_to_resource_is_valid(
- href: str, origin: str, base_url: str, referenced_resource: dict[str, Any]
+ href: str, origin: str, base_url: str, referenced_resource: JSON
) -> None:
url = f"{origin}{href}"
path = url.replace(base_url, "")
- request_data: RequestData = run_keyword("get_request_data", path, "GET")
+ request_data = _run_keyword("get_request_data", path, "GET")
params = request_data.params
headers = request_data.headers
- get_response = run_keyword("authorized_request", url, "GET", params, headers)
+ get_response = _run_keyword("authorized_request", url, "GET", params, headers)
assert get_response.json() == referenced_resource, (
- f"{get_response.json()} not equal to original {referenced_resource}"
+ f"{get_response.json()!r} not equal to original {referenced_resource!r}"
)
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index d8d658c..b5ce829 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -52,7 +52,6 @@
O = TypeVar("O")
AI = TypeVar("AI", bound=JSON)
-S = TypeVar("S", str, bytes)
ConstraintMappingType = builtins.type[IConstraintMapping]
@@ -60,10 +59,6 @@ def is_object_schema(schema: SchemaObjectTypes) -> TypeGuard[ObjectSchema]:
return isinstance(schema, ObjectSchema)
-def data_is_bytes(data: str | bytes, format: str) -> TypeGuard[bytes]:
- return format == "byte"
-
-
class SchemaBase(BaseModel, Generic[O], frozen=True):
readOnly: bool = False
writeOnly: bool = False
@@ -209,21 +204,21 @@ def annotation_string(self) -> str:
return "bool"
-class StringSchema(SchemaBase[S], frozen=True):
+class StringSchema(SchemaBase[str], frozen=True):
type: Literal["string"] = "string"
format: str = ""
pattern: str = ""
maxLength: int | None = None
minLength: int | None = None
- const: S | None = None
- enum: list[S] | None = None
+ const: str | None = None
+ enum: list[str] | None = None
nullable: bool = False
- def get_valid_value( # type: ignore
+ def get_valid_value(
self,
constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
- ) -> S:
+ ) -> str:
"""Generate a random string within the min/max length in the schema, if specified."""
if self.const is not None:
return self.const
@@ -245,36 +240,31 @@ def get_valid_value( # type: ignore
minimum = self.minLength if self.minLength is not None else 0
maximum = self.maxLength if self.maxLength is not None else 36
maximum = max(minimum, maximum)
- format_ = self.format if self.format else "uuid"
- # byte is a special case due to the required encoding
- data = FAKE.uuid()
- if data_is_bytes(data=data, format=format_):
- return base64.b64encode(data.encode("utf-8"))
+ format_ = self.format if self.format else "uuid"
value = fake_string(string_format=format_)
while len(value) < minimum:
- # use fake.name() to ensure the returned string uses the provided locale
- value = value + FAKE.name()
+ value = value + fake_string(string_format=format_)
if len(value) > maximum:
value = value[:maximum]
return value
- def get_values_out_of_bounds(self, current_value: S) -> list[S]:
- invalid_values: list[S] = []
+ def get_values_out_of_bounds(self, current_value: str) -> list[str]:
+ invalid_values: list[str] = []
if self.minLength:
invalid_values.append(current_value[0 : self.minLength - 1])
# if there is a maximum length, send 1 character more
if self.maxLength:
- invalid_string_value = current_value if current_value else "x"
+ invalid_value = current_value if current_value else "x"
# add random characters from the current value to prevent adding new characters
- while len(invalid_string_value) <= self.maxLength:
- invalid_string_value += choice(invalid_string_value)
- invalid_values.append(invalid_string_value)
+ while len(invalid_value) <= self.maxLength:
+ invalid_value += choice(invalid_value)
+ invalid_values.append(invalid_value)
if invalid_values:
return invalid_values
raise ValueError
- def get_invalid_value_from_const_or_enum(self) -> S:
+ def get_invalid_value_from_const_or_enum(self) -> str:
valid_values = []
if self.const is not None:
valid_values = [self.const]
@@ -290,7 +280,110 @@ def get_invalid_value_from_const_or_enum(self) -> S:
return invalid_value
- def get_invalid_value_from_constraint(self, values_from_constraint: list[S]) -> S:
+ def get_invalid_value_from_constraint(
+ self, values_from_constraint: list[str]
+ ) -> str:
+ invalid_values = 2 * values_from_constraint
+ invalid_value = invalid_values.pop()
+ for value in invalid_values:
+ invalid_value = invalid_value + value
+
+ if not invalid_value:
+ raise ValueError("Value invalidation yielded an empty string.")
+ return invalid_value
+
+ @property
+ def can_be_invalidated(self) -> bool:
+ if (
+ self.maxLength is not None
+ or self.minLength is not None
+ or self.const is not None
+ or self.enum is not None
+ ):
+ return True
+ return False
+
+ @property
+ def annotation_string(self) -> str:
+ return "str"
+
+
+class BytesSchema(SchemaBase[bytes], frozen=True):
+ type: Literal["string"] = "string"
+ format: Literal["byte"] = "byte"
+ pattern: str = ""
+ maxLength: int | None = None
+ minLength: int | None = None
+ const: bytes | None = None
+ enum: list[bytes] | None = None
+ nullable: bool = False
+
+ def get_valid_value(
+ self,
+ constraint_mapping: ConstraintMappingType | None = None,
+ operation_id: str | None = None,
+ ) -> bytes:
+ """Generate a random string within the min/max length in the schema, if specified."""
+ if self.const is not None:
+ return self.const
+ if self.enum is not None:
+ return choice(self.enum)
+ if self.pattern:
+ logger.warn(
+ "'pattern' is currently not supported for 'byte' format strings."
+ "To ensure a valid value is generated for this property, a "
+ "PropertyValueConstraint can be configured. See the Advanced Use "
+ "section of the OpenApiTools documentation for more details."
+ )
+ minimum = self.minLength if self.minLength is not None else 0
+ maximum = self.maxLength if self.maxLength is not None else 36
+ maximum = max(minimum, maximum)
+
+ # use "uuid" for generated strings to prevent whitespace characters
+ value = fake_string(string_format="uuid")
+ while len(value) < minimum:
+ value = value + fake_string(string_format="uuid")
+ bytes_value = base64.b64encode(value.encode("utf-8"))
+ if len(bytes_value) > maximum:
+ bytes_value = bytes_value[:maximum]
+ return bytes_value
+
+ def get_values_out_of_bounds(self, current_value: bytes) -> list[bytes]:
+ invalid_values: list[bytes] = []
+ if self.minLength:
+ invalid_values.append(current_value[0 : self.minLength - 1])
+ # if there is a maximum length, send 1 character more
+ if self.maxLength:
+ current_str_value = current_value.decode(encoding="utf-8")
+ invalid_string_value = current_str_value if current_str_value else "x"
+ # add random characters from the current value to prevent adding new characters
+ while len(invalid_string_value) <= self.maxLength:
+ invalid_string_value += choice(current_str_value)
+ invalid_value = base64.b64encode(invalid_string_value.encode("utf-8"))
+ invalid_values.append(invalid_value)
+ if invalid_values:
+ return invalid_values
+ raise ValueError
+
+ def get_invalid_value_from_const_or_enum(self) -> bytes:
+ valid_values = []
+ if self.const is not None:
+ valid_values = [self.const]
+ if self.enum is not None:
+ valid_values = self.enum
+
+ if not valid_values:
+ raise ValueError
+
+ invalid_value = b""
+ for value in valid_values:
+ invalid_value += value + value
+
+ return invalid_value
+
+ def get_invalid_value_from_constraint(
+ self, values_from_constraint: list[bytes]
+ ) -> bytes:
invalid_values = 2 * values_from_constraint
invalid_value = invalid_values.pop()
for value in invalid_values:
@@ -594,7 +687,7 @@ def get_values_out_of_bounds(self, current_value: list[AI]) -> list[list[AI]]:
current_value = self.get_valid_value()
if not current_value:
- current_value = [self.items.get_valid_value()] # type: ignore
+ current_value = [self.items.get_valid_value()] # type: ignore[list-item]
while len(invalid_value) <= self.maxItems:
invalid_value.append(choice(current_value))
@@ -631,9 +724,9 @@ def get_invalid_value_from_constraint(
invalid_array: list[AI] = []
for value in valid_array:
invalid_value = self.items.get_invalid_value_from_constraint(
- values_from_constraint=[value],
+ values_from_constraint=[value], # type: ignore[list-item]
)
- invalid_array.append(invalid_value)
+ invalid_array.append(invalid_value) # type: ignore[arg-type]
return invalid_array
def get_invalid_data(
@@ -672,7 +765,7 @@ def get_invalid_data(
else self.items.get_valid_value()
)
invalid_item = self.items.get_invalid_data(
- valid_data=valid_item, # type: ignore
+ valid_data=valid_item, # type: ignore[arg-type]
constraint_mapping=constraint_mapping,
status_code=status_code,
invalid_property_default_code=invalid_property_default_code,
@@ -822,9 +915,9 @@ def get_invalid_value_from_constraint(
for key, value in valid_object.items():
python_type_of_value = type(value)
json_type_of_value = json_type_name_of_python_type(python_type_of_value)
- schema = MediaTypeObject(schema={"type": json_type_of_value}).schema_
- invalid_value = schema.get_invalid_value_from_constraint(
- values_from_constraint=[value],
+ schema = MediaTypeObject(schema={"type": json_type_of_value}).schema_ # pyright: ignore[reportArgumentType]
+ invalid_value = schema.get_invalid_value_from_constraint( # type: ignore[union-attr]
+ values_from_constraint=[value], # type: ignore[list-item]
)
invalid_object[key] = invalid_value
return invalid_object
@@ -835,9 +928,9 @@ def get_invalid_data(
constraint_mapping: ConstraintMappingType,
status_code: int,
invalid_property_default_code: int,
- ) -> Mapping[str, JSON | Ignore]:
+ ) -> dict[str, JSON]:
"""Return a data set with one of the properties set to an invalid value or type."""
- properties: Mapping[str, JSON | Ignore] = deepcopy(valid_data)
+ properties: dict[str, JSON] = deepcopy(valid_data)
relations = constraint_mapping.get_body_relations_for_error_code(
error_code=status_code
@@ -920,10 +1013,11 @@ def get_invalid_data(
]
invalid_value = value_schema.get_invalid_value(
- valid_value=current_value, # type: ignore
+ valid_value=current_value, # type: ignore[arg-type]
values_from_constraint=values_from_constraint,
)
- properties[property_name] = invalid_value # type: ignore
+ if not isinstance(invalid_value, Ignore):
+ properties[property_name] = invalid_value
logger.debug(
f"Property {property_name} changed to {invalid_value!r} "
f"(received from get_invalid_value)"
@@ -953,11 +1047,22 @@ def annotation_string(self) -> str:
NullSchema
| BooleanSchema
| StringSchema
+ | BytesSchema
| IntegerSchema
| NumberSchema
- | ArraySchema
+ | ArraySchema[AI]
| ObjectSchema
)
+RESOLVED_SCHEMA_INSTANCES = (
+ NullSchema,
+ BooleanSchema,
+ StringSchema,
+ BytesSchema,
+ IntegerSchema,
+ NumberSchema,
+ ArraySchema,
+ ObjectSchema,
+)
class UnionTypeSchema(SchemaBase[JSON], frozen=True):
@@ -977,10 +1082,10 @@ def get_values_out_of_bounds(self, current_value: JSON) -> list[JSON]:
raise ValueError
@property
- def resolved_schemas(self) -> list[ResolvedSchemaObjectTypes]:
+ def resolved_schemas(self) -> list[ResolvedSchemaObjectTypes]: # type: ignore[type-arg]
return list(self._get_resolved_schemas())
- def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, None]:
+ def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, None]: # type: ignore[type-arg]
if self.allOf:
properties_list: list[PropertiesMapping] = []
additional_properties_list = []
@@ -1016,7 +1121,7 @@ def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, No
additional_properties_types = []
for additional_properties_item in additional_properties_list:
if isinstance(
- additional_properties_item, ResolvedSchemaObjectTypes
+ additional_properties_item, RESOLVED_SCHEMA_INSTANCES
):
additional_properties_types.append(additional_properties_item)
if not additional_properties_types:
@@ -1043,7 +1148,7 @@ def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, No
yield merged_schema
else:
for schema in self.anyOf + self.oneOf:
- if isinstance(schema, ResolvedSchemaObjectTypes):
+ if isinstance(schema, RESOLVED_SCHEMA_INSTANCES):
yield schema
else:
yield from schema.resolved_schemas
@@ -1062,7 +1167,7 @@ def annotation_string(self) -> str:
return " | ".join(unique_annotations)
-SchemaObjectTypes: TypeAlias = ResolvedSchemaObjectTypes | UnionTypeSchema
+SchemaObjectTypes: TypeAlias = ResolvedSchemaObjectTypes | UnionTypeSchema # type: ignore[type-arg]
class PropertiesMapping(RootModel[dict[str, "SchemaObjectTypes"]], frozen=True): ...
@@ -1155,7 +1260,7 @@ class PathItemObject(BaseModel):
description: str = ""
parameters: list[ParameterObject] = []
constraint_mapping: IConstraintMapping | None = None
- id_mapper: tuple[str, Callable[[str], str] | Callable[[int], int]] = (
+ id_mapper: tuple[str, Callable[[str], str]] = (
"id",
dummy_transformer,
)
@@ -1271,7 +1376,9 @@ def get_dependent_id(
except ValueError:
return None
- valid_id = run_keyword("get_valid_id_for_path", id_get_path)
+ valid_id = cast(
+ str | int | float, run_keyword("get_valid_id_for_path", id_get_path)
+ )
logger.debug(f"get_dependent_id for {id_get_path} returned {valid_id}")
return valid_id
diff --git a/src/OpenApiLibCore/models/request_data.py b/src/OpenApiLibCore/models/request_data.py
index c31835f..164cb62 100644
--- a/src/OpenApiLibCore/models/request_data.py
+++ b/src/OpenApiLibCore/models/request_data.py
@@ -22,7 +22,7 @@ class RequestValues:
url: str
method: str
params: dict[str, JSON] = field(default_factory=dict)
- headers: dict[str, JSON] = field(default_factory=dict)
+ headers: dict[str, str] = field(default_factory=dict)
json_data: JSON = None
def override_body_value(self, name: str, value: JSON) -> None:
@@ -30,7 +30,7 @@ def override_body_value(self, name: str, value: JSON) -> None:
if isinstance(self.json_data, dict) and name in self.json_data:
self.json_data[name] = value
- def override_header_value(self, name: str, value: JSON) -> None:
+ def override_header_value(self, name: str, value: str) -> None:
if name in self.headers:
self.headers[name] = value
@@ -40,7 +40,7 @@ def override_param_value(self, name: str, value: JSON) -> None:
def override_request_value(self, name: str, value: JSON) -> None:
self.override_body_value(name=name, value=value)
- self.override_header_value(name=name, value=value)
+ self.override_header_value(name=name, value=str(value))
self.override_param_value(name=name, value=value)
def remove_parameters(self, parameters: list[str]) -> None:
@@ -57,10 +57,10 @@ class RequestData:
valid_data: JSON
constraint_mapping: type[IConstraintMapping]
- body_schema: ResolvedSchemaObjectTypes | None = None
+ body_schema: ResolvedSchemaObjectTypes | None = None # type: ignore[type-arg]
parameters: list[ParameterObject] = field(default_factory=list)
params: dict[str, JSON] = field(default_factory=dict)
- headers: dict[str, JSON] = field(default_factory=dict)
+ headers: dict[str, str] = field(default_factory=dict)
has_body: bool = True
def __post_init__(self) -> None:
@@ -116,7 +116,7 @@ def params_that_can_be_invalidated(self) -> set[str]:
if param.schema_ is None:
continue
- possible_schemas: list[ResolvedSchemaObjectTypes] = []
+ possible_schemas: list[ResolvedSchemaObjectTypes] = [] # type: ignore[type-arg]
if isinstance(param.schema_, UnionTypeSchema):
possible_schemas = param.schema_.resolved_schemas
else:
@@ -157,7 +157,7 @@ def headers_that_can_be_invalidated(self) -> set[str]:
if header.schema_ is None:
continue
- possible_schemas: list[ResolvedSchemaObjectTypes] = []
+ possible_schemas: list[ResolvedSchemaObjectTypes] = [] # type: ignore[type-arg]
if isinstance(header.schema_, UnionTypeSchema):
possible_schemas = header.schema_.resolved_schemas
else:
@@ -177,11 +177,15 @@ def get_required_properties_dict(self) -> dict[str, JSON]:
for relation in relations
if getattr(relation, "treat_as_mandatory", False)
]
- required_properties = self.body_schema.required if self.body_schema else []
+ required_properties = (
+ self.body_schema.required
+ if isinstance(self.body_schema, ObjectSchema)
+ else []
+ )
required_properties.extend(mandatory_properties)
required_properties_dict: dict[str, JSON] = {}
- if self.valid_data is None:
+ if not isinstance(self.valid_data, dict):
return required_properties_dict
for key, value in self.valid_data.items():
@@ -193,7 +197,10 @@ def get_minimal_body_dict(self) -> dict[str, JSON]:
required_properties_dict = self.get_required_properties_dict()
min_properties = 0
- if self.body_schema and self.body_schema.minProperties is not None:
+ if (
+ isinstance(self.body_schema, ObjectSchema)
+ and self.body_schema.minProperties is not None
+ ):
min_properties = self.body_schema.minProperties
number_of_optional_properties_to_add = min_properties - len(
@@ -203,6 +210,9 @@ def get_minimal_body_dict(self) -> dict[str, JSON]:
if number_of_optional_properties_to_add < 1:
return required_properties_dict
+ if not isinstance(self.valid_data, dict):
+ return required_properties_dict
+
optional_properties_dict = {
k: v
for k, v in self.valid_data.items()
@@ -225,7 +235,7 @@ def get_required_params(self) -> dict[str, JSON]:
k: v for k, v in self.params.items() if k in self.required_parameter_names
}
- def get_required_headers(self) -> dict[str, JSON]:
+ def get_required_headers(self) -> dict[str, str]:
"""Get the headers dict containing only the required headers."""
return {
k: v for k, v in self.headers.items() if k in self.required_parameter_names
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index 495c962..bcca61f 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -5,7 +5,7 @@
from functools import cached_property
from pathlib import Path
from types import MappingProxyType
-from typing import Any, Generator
+from typing import Any, Generator, Literal, overload
from openapi_core import Config, OpenAPI, Spec
from openapi_core.validation.exceptions import ValidationError
@@ -56,6 +56,22 @@
default_json_mapping: Mapping[str, JSON] = MappingProxyType({})
+@overload
+def _run_keyword(
+ keyword_name: Literal["get_valid_url"], *args: str
+) -> str: ... # pragma: no cover
+
+
+@overload
+def _run_keyword(
+ keyword_name: Literal["get_request_data"], *args: str
+) -> RequestData: ... # pragma: no cover
+
+
+def _run_keyword(keyword_name: str, *args: object) -> object:
+ return run_keyword(keyword_name, *args)
+
+
@library(scope="SUITE", doc_format="HTML")
class OpenApiLibCore: # pylint: disable=too-many-public-methods
def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
@@ -209,10 +225,10 @@ def get_request_values(
overrides: Mapping[str, JSON] = default_json_mapping,
) -> RequestValues:
"""Return an object with all (valid) request values needed to make a request."""
- json_data: dict[str, JSON] = {}
+ json_data: JSON = {}
- url: str = run_keyword("get_valid_url", path)
- request_data: RequestData = run_keyword("get_request_data", path, method)
+ url = _run_keyword("get_valid_url", path)
+ request_data = _run_keyword("get_request_data", path, method)
params = request_data.params
headers = request_data.headers
if request_data.has_body:
@@ -233,7 +249,9 @@ def get_request_values(
if location == "body":
request_values.override_body_value(name=oas_name, value=value)
if location == "header":
- request_values.override_header_value(name=oas_name, value=value)
+ request_values.override_header_value(
+ name=oas_name, value=str(value)
+ )
if location == "query":
request_values.override_param_value(name=oas_name, value=str(value))
else:
@@ -258,7 +276,7 @@ def get_invalid_body_data(
method: str,
status_code: int,
request_data: RequestData,
- ) -> dict[str, JSON]:
+ ) -> JSON:
"""
Return `json_data` based on the `constraint_mapping` on the `request_data` that
will cause the provided `status_code` for the `method` operation on the `url`.
@@ -279,7 +297,7 @@ def get_invalidated_parameters(
self,
status_code: int,
request_data: RequestData,
- ) -> tuple[dict[str, JSON], dict[str, JSON]]:
+ ) -> tuple[dict[str, JSON], dict[str, str]]:
"""
Returns a version of `params, headers` as present on `request_data` that has
been modified to cause the provided `status_code`.
@@ -487,11 +505,11 @@ def validate_response_using_validator(self, response: Response) -> None:
@keyword
def assert_href_to_resource_is_valid(
- self, href: str, referenced_resource: dict[str, JSON]
+ self, href: str, referenced_resource: JSON
) -> None:
"""
Attempt to GET the resource referenced by the `href` and validate it's equal
- to the provided `referenced_resource` object / dictionary.
+ to the provided `referenced_resource`.
"""
_validation.assert_href_to_resource_is_valid(
href=href,
@@ -505,7 +523,7 @@ def validate_response(
self,
path: str,
response: Response,
- original_data: Mapping[str, JSON] = default_json_mapping,
+ original_data: JSON = default_json_mapping, # type: ignore[assignment]
) -> None:
"""
Validate the `response` by performing the following validations:
@@ -675,7 +693,7 @@ def recursion_limit_handler(
backend="openapi-spec-validator",
recursion_limit=self._recursion_limit,
recursion_limit_handler=recursion_limit_handler,
- )
+ ) # type: ignore[no-untyped-call]
if parser.specification is None: # pragma: no cover
raise FatalError(
@@ -692,7 +710,7 @@ def recursion_limit_handler(
}
config = Config(extra_media_type_deserializers=extra_deserializers) # type: ignore[arg-type]
openapi = OpenAPI(spec=validation_spec, config=config)
- response_validator: IResponseValidator = openapi.validate_response # type: ignore[assignment]
+ response_validator: IResponseValidator = openapi.validate_response
PARSER_CACHE[self._source] = CachedParser(
parser=parser,
diff --git a/src/OpenApiLibCore/protocols.py b/src/OpenApiLibCore/protocols.py
index 62f4dbf..31d503f 100644
--- a/src/OpenApiLibCore/protocols.py
+++ b/src/OpenApiLibCore/protocols.py
@@ -28,9 +28,7 @@ def __init__(
self, mappings_module_name: str, default_id_property_name: str
) -> None: ...
- def __call__(
- self, path: str
- ) -> tuple[str, Callable[[str], str] | Callable[[int], int]]: ...
+ def __call__(self, path: str) -> tuple[str, Callable[[str], str]]: ...
@property
def default_id_property_name(self) -> str: ...
@@ -38,7 +36,7 @@ def default_id_property_name(self) -> str: ...
@property
def id_mapping(
self,
- ) -> dict[str, str | tuple[str, Callable[[str], str] | Callable[[int], int]]]: ...
+ ) -> dict[str, str | tuple[str, Callable[[str], str]]]: ...
class IConstraintMapping(Protocol):
diff --git a/src/OpenApiLibCore/utils/id_mapping.py b/src/OpenApiLibCore/utils/id_mapping.py
index 2da5b32..e2f12ab 100644
--- a/src/OpenApiLibCore/utils/id_mapping.py
+++ b/src/OpenApiLibCore/utils/id_mapping.py
@@ -1,13 +1,2 @@
-from typing import Any, overload
-
-
-@overload
-def dummy_transformer(valid_id: str) -> str: ... # pragma: no cover
-
-
-@overload
-def dummy_transformer(valid_id: int) -> int: ... # pragma: no cover
-
-
-def dummy_transformer(valid_id: Any) -> Any:
+def dummy_transformer(valid_id: str) -> str:
return valid_id
diff --git a/src/openapi_libgen/command_line.py b/src/openapi_libgen/command_line.py
index 38f8039..c675419 100644
--- a/src/openapi_libgen/command_line.py
+++ b/src/openapi_libgen/command_line.py
@@ -72,7 +72,7 @@ def main() -> None:
default_module_name,
)
- use_summary = getenv("USE_SUMMARY_AS_KEYWORD_NAME")
+ use_summary: str | bool | None = getenv("USE_SUMMARY_AS_KEYWORD_NAME")
if use_summary is None:
if args.use_summary_as_keyword_name is None:
use_summary = input(
@@ -80,7 +80,7 @@ def main() -> None:
)
use_summary = True if use_summary.lower().startswith("y") else False
- expand_body = getenv("EXPAND_BODY_ARGUMENTS")
+ expand_body: str | bool | None = getenv("EXPAND_BODY_ARGUMENTS")
if expand_body is None:
if args.expand_body_arguments is None:
expand_body = input(
@@ -93,6 +93,6 @@ def main() -> None:
output_folder=path,
library_name=safe_library_name,
module_name=safe_module_name,
- use_summary=is_truthy(use_summary),
- expand_body=is_truthy(expand_body),
+ use_summary=is_truthy(use_summary), # type: ignore[no-untyped-call]
+ expand_body=is_truthy(expand_body), # type: ignore[no-untyped-call]
)
diff --git a/src/openapi_libgen/generator.py b/src/openapi_libgen/generator.py
index 8bb05dd..82e8434 100644
--- a/src/openapi_libgen/generator.py
+++ b/src/openapi_libgen/generator.py
@@ -25,7 +25,7 @@ def recursion_limit_handler(
backend="openapi-spec-validator",
recursion_limit=recursion_limit,
recursion_limit_handler=recursion_limit_handler,
- )
+ ) # type: ignore[no-untyped-call]
assert parser.specification is not None, (
"Source was loaded, but no specification was present after parsing."
)
@@ -81,11 +81,11 @@ def generate(
use_summary = getenv("USE_SUMMARY_AS_KEYWORD_NAME")
use_summary = use_summary if use_summary is not None else sys.argv[5]
- use_summary = is_truthy(use_summary)
+ use_summary = is_truthy(use_summary) # type: ignore[no-untyped-call]
expand_body = getenv("EXPAND_BODY_ARGUMENTS")
expand_body = expand_body if expand_body is not None else sys.argv[6]
- expand_body = is_truthy(expand_body)
+ expand_body = is_truthy(expand_body) # type: ignore[no-untyped-call]
spec = load_openapi_spec(source=source, recursion_limit=1, recursion_default={})
diff --git a/tests/driver/suites/test_with_altered_schema.robot b/tests/driver/suites/test_with_altered_schema.robot
index 4fde0ed..5c5341b 100644
--- a/tests/driver/suites/test_with_altered_schema.robot
+++ b/tests/driver/suites/test_with_altered_schema.robot
@@ -24,10 +24,13 @@ Test Tags rf7
*** Variables ***
@{EXPECTED_FAILURES}
... GET / 200 # Unsupported MIME type for response schema
+... GET /events/ 200 # Message in schema changed to format "byte"
... GET /reactions/ 200 # /reactions/ path not implemented on API server
+... GET /secret_message 200 # Message in schema changed to format "byte"
... POST /events/ 201 # added 'event_number' property to Event schema
... POST /employees 201 # added 'team' property to Employee schema
... GET /employees/{employee_id} 200 # added 'team' property to EmployeeDetails schema
+... GET /energy_label/{zipcode}/{home_number} 200 # Message in schema changed to format "byte"
... PUT /wagegroups/{wagegroup_id} 200 # Unsupported MIME type for requestBody
... PUT /wagegroups/{wagegroup_id} 404 # Unsupported MIME type for requestBody
... PUT /wagegroups/{wagegroup_id} 418 # Unsupported MIME type for requestBody
diff --git a/tests/files/altered_openapi.json b/tests/files/altered_openapi.json
index 78b5b78..4295cd7 100644
--- a/tests/files/altered_openapi.json
+++ b/tests/files/altered_openapi.json
@@ -1065,7 +1065,8 @@
"properties": {
"message": {
"type": "string",
- "title": "Message"
+ "format": "byte",
+ "title": "Binary message"
}
},
"type": "object",
diff --git a/tests/libcore/suites/test_schema_variations.robot b/tests/libcore/suites/test_schema_variations.robot
index 9efc6af..835b90c 100644
--- a/tests/libcore/suites/test_schema_variations.robot
+++ b/tests/libcore/suites/test_schema_variations.robot
@@ -21,7 +21,7 @@ Test Get Request Data For Schema With allOf
Length Should Be ${request_data.valid_data}[isan] 36
Length Should Be ${request_data.valid_data}[tags] 1
Length Should Be ${request_data.valid_data}[tags][0] 36
- VAR ${resolved_schema}= ${request_data.body_schema.resolved_schemas}[0]
+ VAR ${resolved_schema}= ${request_data.body_schema}
Length Should Be ${resolved_schema.properties.root} 4
Should Be Equal ${request_data.parameters} ${list}
Should Be Equal ${request_data.params} ${dict}
diff --git a/tests/libcore/unittests/test_get_valid_value.py b/tests/libcore/unittests/test_get_valid_value.py
index 0040b27..b247f32 100644
--- a/tests/libcore/unittests/test_get_valid_value.py
+++ b/tests/libcore/unittests/test_get_valid_value.py
@@ -4,6 +4,7 @@
from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
+ BytesSchema,
IntegerSchema,
NullSchema,
NumberSchema,
@@ -26,6 +27,10 @@ def test_string_schema(self) -> None:
schema = StringSchema()
self.assertIsInstance(schema.get_valid_value(), str)
+ def test_bytes_schema(self) -> None:
+ schema = BytesSchema()
+ self.assertIsInstance(schema.get_valid_value(), bytes)
+
def test_integer_schema(self) -> None:
schema = IntegerSchema()
self.assertIsInstance(schema.get_valid_value(), int)
@@ -62,6 +67,11 @@ def test_string_schema(self) -> None:
schema = StringSchema(const=const)
self.assertEqual(schema.get_valid_value(), const)
+ def test_bytes_schema(self) -> None:
+ const = b"Hello world!"
+ schema = BytesSchema(const=const)
+ self.assertEqual(schema.get_valid_value(), const)
+
def test_integer_schema(self) -> None:
const = 42
schema = IntegerSchema(const=const)
@@ -89,6 +99,11 @@ def test_string_schema(self) -> None:
schema = StringSchema(enum=enum)
self.assertIn(schema.get_valid_value(), enum)
+ def test_bytes_schema(self) -> None:
+ enum = [b"eggs", b"bacon", b"spam"]
+ schema = BytesSchema(enum=enum)
+ self.assertIn(schema.get_valid_value(), enum)
+
def test_integer_schema(self) -> None:
enum = [1, 3, 5, 7]
schema = IntegerSchema(enum=enum)
@@ -121,6 +136,14 @@ def test_default_min_max(self) -> None:
value = schema.get_valid_value()
self.assertEqual(len(value), 36)
+ schema = BytesSchema(maxLength=0)
+ value = schema.get_valid_value()
+ self.assertEqual(value, b"")
+
+ schema = BytesSchema(minLength=36)
+ value = schema.get_valid_value()
+ self.assertEqual(len(value), 36)
+
def test_min_max(self) -> None:
schema = StringSchema(minLength=42, maxLength=42)
value = schema.get_valid_value()
@@ -130,6 +153,14 @@ def test_min_max(self) -> None:
value = schema.get_valid_value()
self.assertEqual(len(value), 42)
+ schema = BytesSchema(minLength=42, maxLength=42)
+ value = schema.get_valid_value()
+ self.assertEqual(len(value), 42)
+
+ schema = BytesSchema(minLength=42)
+ value = schema.get_valid_value()
+ self.assertEqual(len(value), 42)
+
def test_datetime(self) -> None:
schema = StringSchema(format="date-time")
value = schema.get_valid_value()
@@ -167,7 +198,7 @@ def test_pattern(self) -> None:
)
def test_byte(self) -> None:
- schema = StringSchema(format="byte")
+ schema = BytesSchema(format="byte")
value = schema.get_valid_value()
self.assertIsInstance(value, bytes)
From 0a8ebd20b7c7ab40f75690042ed961c0fdd320ce Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Wed, 26 Nov 2025 12:55:09 +0000
Subject: [PATCH 25/55] Fix for Response headers that contain charset for json
mime types
---
src/OpenApiLibCore/keyword_logic/validation.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/OpenApiLibCore/keyword_logic/validation.py b/src/OpenApiLibCore/keyword_logic/validation.py
index 2de9e86..d419647 100644
--- a/src/OpenApiLibCore/keyword_logic/validation.py
+++ b/src/OpenApiLibCore/keyword_logic/validation.py
@@ -361,6 +361,10 @@ def _validate_response(
response_validation: str,
) -> None:
try:
+ content_type = response.headers.get("Content-Type", "")
+ if "json" in content_type:
+ content_type, _, _ = content_type.partition(";")
+ response.headers.update({"Content-Type": content_type})
validate_response_using_validator(
response=response,
response_validator=response_validator,
From cec0df8a255771264687fde8f88da5a790c72a64 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Thu, 27 Nov 2025 12:02:06 +0000
Subject: [PATCH 26/55] address deprecation in openapi_core
---
src/OpenApiLibCore/openapi_libcore.py | 21 +++++++--------------
src/OpenApiLibCore/utils/oas_cache.py | 2 --
2 files changed, 7 insertions(+), 16 deletions(-)
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index bcca61f..898f807 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -7,7 +7,8 @@
from types import MappingProxyType
from typing import Any, Generator, Literal, overload
-from openapi_core import Config, OpenAPI, Spec
+from jsonschema_path import SchemaPath
+from openapi_core import Config, OpenAPI
from openapi_core.validation.exceptions import ValidationError
from prance import ResolvingParser
from prance.util.url import ResolutionError
@@ -495,7 +496,7 @@ def perform_validated_request(
@keyword
def validate_response_using_validator(self, response: Response) -> None:
"""
- Validate the `response` against the OpenAPI Spec that is
+ Validate the `response` against the OpenAPI spec that is
loaded during library initialization.
"""
_validation.validate_response_using_validator(
@@ -573,11 +574,6 @@ def origin(self) -> str:
def base_url(self) -> str:
return f"{self.origin}{self._base_path}"
- @cached_property
- def validation_spec(self) -> Spec:
- _, validation_spec, _ = self._load_specs_and_validator()
- return validation_spec
-
@property
def openapi_spec(self) -> OpenApiObject:
"""Return a deepcopy of the parsed openapi document."""
@@ -586,7 +582,7 @@ def openapi_spec(self) -> OpenApiObject:
@cached_property
def _openapi_spec(self) -> OpenApiObject:
- parser, _, _ = self._load_specs_and_validator()
+ parser, _ = self._load_specs_and_validator()
spec_model = OpenApiObject.model_validate(parser.specification)
spec_model = self._attach_user_mappings(spec_model=spec_model)
self._register_path_parameters(spec_model.paths)
@@ -636,7 +632,7 @@ def _attach_user_mappings(self, spec_model: OpenApiObject) -> OpenApiObject:
def response_validator(
self,
) -> IResponseValidator:
- _, _, response_validator = self._load_specs_and_validator()
+ _, response_validator = self._load_specs_and_validator()
return response_validator
def _get_json_types_from_spec(self, spec: dict[str, JSON]) -> set[str]:
@@ -664,7 +660,6 @@ def _load_specs_and_validator(
self,
) -> tuple[
ResolvingParser,
- Spec,
IResponseValidator,
]:
def recursion_limit_handler(
@@ -684,7 +679,6 @@ def recursion_limit_handler(
if cached_parser:
return (
cached_parser.parser,
- cached_parser.validation_spec,
cached_parser.response_validator,
)
@@ -700,7 +694,7 @@ def recursion_limit_handler(
"Source was loaded, but no specification was present after parsing."
)
- validation_spec = Spec.from_dict(parser.specification) # pyright: ignore[reportArgumentType]
+ validation_spec = SchemaPath.from_dict(parser.specification) # pyright: ignore[reportArgumentType]
json_types_from_spec: set[str] = self._get_json_types_from_spec(
parser.specification
@@ -714,11 +708,10 @@ def recursion_limit_handler(
PARSER_CACHE[self._source] = CachedParser(
parser=parser,
- validation_spec=validation_spec,
response_validator=response_validator,
)
- return parser, validation_spec, response_validator
+ return parser, response_validator
except ResolutionError as exception: # pragma: no cover
raise FatalError(
diff --git a/src/OpenApiLibCore/utils/oas_cache.py b/src/OpenApiLibCore/utils/oas_cache.py
index a6b8dfa..66f94b0 100644
--- a/src/OpenApiLibCore/utils/oas_cache.py
+++ b/src/OpenApiLibCore/utils/oas_cache.py
@@ -2,7 +2,6 @@
from dataclasses import dataclass
-from openapi_core import Spec
from prance import ResolvingParser
from OpenApiLibCore.protocols import IResponseValidator
@@ -11,7 +10,6 @@
@dataclass
class CachedParser:
parser: ResolvingParser
- validation_spec: Spec
response_validator: IResponseValidator
From bd4d82d4a7803c1c87bd44cd4bd46c87ed2333eb Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Thu, 27 Nov 2025 12:02:50 +0000
Subject: [PATCH 27/55] prevent KeyError during response validation
---
src/OpenApiLibCore/keyword_logic/validation.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/OpenApiLibCore/keyword_logic/validation.py b/src/OpenApiLibCore/keyword_logic/validation.py
index d419647..3b4f3d8 100644
--- a/src/OpenApiLibCore/keyword_logic/validation.py
+++ b/src/OpenApiLibCore/keyword_logic/validation.py
@@ -334,6 +334,9 @@ def validate_dict_response(
if original_data:
for send_property_name, send_value in original_data.items():
if send_property_name not in send_json.keys():
+ if send_property_name not in response_data:
+ logger.debug(f"'{send_property_name}' not found in response data.")
+ continue
assert send_value == response_data[send_property_name], (
f"Received value for {send_property_name} '{response_data[send_property_name]}' does not "
f"match '{send_value}' in the pre-patch data"
From 9c12a0d392499d7c1fab93a2f19873d488efb818 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Thu, 27 Nov 2025 16:58:58 +0000
Subject: [PATCH 28/55] Added missing tests
---
src/OpenApiLibCore/models/oas_models.py | 10 ++--
src/openapi_libgen/templates/library.jinja | 1 +
.../unittests/test_can_be_invalidated.py | 17 +++++++
.../libcore/unittests/test_get_valid_value.py | 46 +++++++++++++++----
.../test_get_values_out_of_bounds.py | 33 +++++++++++++
.../test_invalid_value_from_const_or_enum.py | 16 +++++++
.../unittests/test_resolve_union_schema.py | 5 ++
.../test_invalid_value_from_constraint.py | 25 +++++++++-
8 files changed, 138 insertions(+), 15 deletions(-)
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index b5ce829..bbfb79a 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -330,7 +330,7 @@ def get_valid_value(
return choice(self.enum)
if self.pattern:
logger.warn(
- "'pattern' is currently not supported for 'byte' format strings."
+ "'pattern' is currently not supported for 'byte' format strings. "
"To ensure a valid value is generated for this property, a "
"PropertyValueConstraint can be configured. See the Advanced Use "
"section of the OpenApiTools documentation for more details."
@@ -358,9 +358,9 @@ def get_values_out_of_bounds(self, current_value: bytes) -> list[bytes]:
invalid_string_value = current_str_value if current_str_value else "x"
# add random characters from the current value to prevent adding new characters
while len(invalid_string_value) <= self.maxLength:
- invalid_string_value += choice(current_str_value)
+ invalid_string_value += choice(invalid_string_value)
invalid_value = base64.b64encode(invalid_string_value.encode("utf-8"))
- invalid_values.append(invalid_value)
+ invalid_values.append(invalid_value[0 : self.maxLength + 1])
if invalid_values:
return invalid_values
raise ValueError
@@ -406,7 +406,7 @@ def can_be_invalidated(self) -> bool:
@property
def annotation_string(self) -> str:
- return "str"
+ return "bytes"
class IntegerSchema(SchemaBase[int], frozen=True):
@@ -1040,7 +1040,7 @@ def can_be_invalidated(self) -> bool:
@property
def annotation_string(self) -> str:
- return "dict[str, Any]"
+ return "dict[str, JSON]"
ResolvedSchemaObjectTypes: TypeAlias = (
diff --git a/src/openapi_libgen/templates/library.jinja b/src/openapi_libgen/templates/library.jinja
index 1390068..303987e 100644
--- a/src/openapi_libgen/templates/library.jinja
+++ b/src/openapi_libgen/templates/library.jinja
@@ -6,6 +6,7 @@ from robot.api.deco import keyword, library
from robot.libraries.BuiltIn import BuiltIn
from OpenApiLibCore import UNSET, OpenApiLibCore, RequestValues
+from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.keyword_logic.path_functions import substitute_path_parameters
run_keyword = BuiltIn().run_keyword
diff --git a/tests/libcore/unittests/test_can_be_invalidated.py b/tests/libcore/unittests/test_can_be_invalidated.py
index cde05b3..0dea663 100644
--- a/tests/libcore/unittests/test_can_be_invalidated.py
+++ b/tests/libcore/unittests/test_can_be_invalidated.py
@@ -4,6 +4,7 @@
from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
+ BytesSchema,
IntegerSchema,
NullSchema,
NumberSchema,
@@ -37,6 +38,22 @@ def test_string_schema(self) -> None:
schema = StringSchema(enum=["eggs", "spam"])
self.assertEqual(schema.can_be_invalidated, True)
+ def test_bytes_schema(self) -> None:
+ schema = BytesSchema()
+ self.assertEqual(schema.can_be_invalidated, False)
+
+ schema = BytesSchema(maxLength=1)
+ self.assertEqual(schema.can_be_invalidated, True)
+
+ schema = BytesSchema(minLength=1)
+ self.assertEqual(schema.can_be_invalidated, True)
+
+ schema = BytesSchema(const=b"foo")
+ self.assertEqual(schema.can_be_invalidated, True)
+
+ schema = BytesSchema(enum=[b"eggs", b"spam"])
+ self.assertEqual(schema.can_be_invalidated, True)
+
def test_integer_schema(self) -> None:
schema = IntegerSchema()
self.assertEqual(schema.can_be_invalidated, True)
diff --git a/tests/libcore/unittests/test_get_valid_value.py b/tests/libcore/unittests/test_get_valid_value.py
index b247f32..c5d40af 100644
--- a/tests/libcore/unittests/test_get_valid_value.py
+++ b/tests/libcore/unittests/test_get_valid_value.py
@@ -153,14 +153,6 @@ def test_min_max(self) -> None:
value = schema.get_valid_value()
self.assertEqual(len(value), 42)
- schema = BytesSchema(minLength=42, maxLength=42)
- value = schema.get_valid_value()
- self.assertEqual(len(value), 42)
-
- schema = BytesSchema(minLength=42)
- value = schema.get_valid_value()
- self.assertEqual(len(value), 42)
-
def test_datetime(self) -> None:
schema = StringSchema(format="date-time")
value = schema.get_valid_value()
@@ -197,6 +189,44 @@ def test_pattern(self) -> None:
last_log_entry.endswith(f"The pattern was: {pattern}"), last_log_entry
)
+
+class TestBytesSchemaVariations(unittest.TestCase):
+ def test_default_min_max(self) -> None:
+ schema = BytesSchema(maxLength=0)
+ value = schema.get_valid_value()
+ self.assertEqual(value, b"")
+
+ schema = BytesSchema(minLength=36)
+ value = schema.get_valid_value()
+ self.assertEqual(len(value), 36)
+
+ def test_min_max(self) -> None:
+ schema = BytesSchema(minLength=42, maxLength=42)
+ value = schema.get_valid_value()
+ self.assertEqual(len(value), 42)
+
+ schema = BytesSchema(minLength=42)
+ value = schema.get_valid_value()
+ self.assertEqual(len(value), 42)
+
+ def test_pattern(self) -> None:
+ pattern = r"^[1-9][0-9]{3} ?(?!sa|sd|ss|SA|SD|SS)[A-Za-z]{2}$"
+ schema = BytesSchema(pattern=pattern)
+
+ with self.assertLogs(level="WARN") as logs:
+ value = schema.get_valid_value()
+
+ self.assertTrue(len(logs.output) > 0)
+ last_log_entry = logs.output[-1]
+ self.assertTrue(
+ last_log_entry.startswith(
+ "WARNING:RobotFramework:'pattern' is currently not supported for "
+ "'byte' format strings."
+ ),
+ last_log_entry,
+ )
+ self.assertIsInstance(value, bytes)
+
def test_byte(self) -> None:
schema = BytesSchema(format="byte")
value = schema.get_valid_value()
diff --git a/tests/libcore/unittests/test_get_values_out_of_bounds.py b/tests/libcore/unittests/test_get_values_out_of_bounds.py
index 79a994f..4c022fe 100644
--- a/tests/libcore/unittests/test_get_values_out_of_bounds.py
+++ b/tests/libcore/unittests/test_get_values_out_of_bounds.py
@@ -5,6 +5,7 @@
from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
+ BytesSchema,
IntegerSchema,
NullSchema,
NumberSchema,
@@ -59,6 +60,38 @@ def test_string_schema(self) -> None:
schema.get_values_out_of_bounds(current_value="TTTT"), ["TT", "TTTTTT"]
)
+ def test_bytes_schema(self) -> None:
+ schema = BytesSchema()
+ with self.assertRaises(ValueError):
+ schema.get_values_out_of_bounds(
+ current_value=b"minLength and maxLength not set"
+ )
+
+ schema = BytesSchema(minLength=0)
+ with self.assertRaises(ValueError):
+ schema.get_values_out_of_bounds(
+ current_value=b"minLength 0 and maxLength not set"
+ )
+
+ schema = BytesSchema(minLength=2)
+ self.assertEqual(
+ schema.get_values_out_of_bounds(
+ current_value=b"minLength 2 and maxLength not set"
+ ),
+ [b"m"],
+ )
+
+ schema = BytesSchema(maxLength=5)
+ self.assertEqual(
+ schema.get_values_out_of_bounds(current_value=b""),
+ [b"eHh4eH"],
+ )
+
+ schema = BytesSchema(minLength=3, maxLength=5)
+ self.assertEqual(
+ schema.get_values_out_of_bounds(current_value=b"TTTT"), [b"TT", b"VFRUVF"]
+ )
+
def test_integer_schema(self) -> None:
schema = IntegerSchema()
with self.assertRaises(ValueError):
diff --git a/tests/libcore/unittests/test_invalid_value_from_const_or_enum.py b/tests/libcore/unittests/test_invalid_value_from_const_or_enum.py
index 7fdc814..bb1e284 100644
--- a/tests/libcore/unittests/test_invalid_value_from_const_or_enum.py
+++ b/tests/libcore/unittests/test_invalid_value_from_const_or_enum.py
@@ -4,6 +4,7 @@
from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
+ BytesSchema,
IntegerSchema,
NullSchema,
NumberSchema,
@@ -29,6 +30,11 @@ def test_string_schema(self) -> None:
with self.assertRaises(ValueError):
schema.get_invalid_value_from_const_or_enum()
+ def test_bytes_schema(self) -> None:
+ schema = BytesSchema()
+ with self.assertRaises(ValueError):
+ schema.get_invalid_value_from_const_or_enum()
+
def test_integer_schema(self) -> None:
schema = IntegerSchema()
with self.assertRaises(ValueError):
@@ -66,6 +72,11 @@ def test_string_schema(self) -> None:
schema = StringSchema(const=const)
self.assertNotEqual(schema.get_invalid_value_from_const_or_enum(), const)
+ def test_bytes_schema(self) -> None:
+ const = b"Hello world!"
+ schema = BytesSchema(const=const)
+ self.assertNotEqual(schema.get_invalid_value_from_const_or_enum(), const)
+
def test_integer_schema(self) -> None:
const = 42
schema = IntegerSchema(const=const)
@@ -94,6 +105,11 @@ def test_string_schema(self) -> None:
schema = StringSchema(enum=enum)
self.assertNotIn(schema.get_invalid_value_from_const_or_enum(), enum)
+ def test_bytes_schema(self) -> None:
+ enum = [b"eggs", b"bacon", b"spam"]
+ schema = BytesSchema(enum=enum)
+ self.assertNotIn(schema.get_invalid_value_from_const_or_enum(), enum)
+
def test_integer_schema(self) -> None:
enum = [-1, 0, 1]
schema = IntegerSchema(enum=enum)
diff --git a/tests/libcore/unittests/test_resolve_union_schema.py b/tests/libcore/unittests/test_resolve_union_schema.py
index f9b994f..486ba70 100644
--- a/tests/libcore/unittests/test_resolve_union_schema.py
+++ b/tests/libcore/unittests/test_resolve_union_schema.py
@@ -4,6 +4,7 @@
from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
+ BytesSchema,
IntegerSchema,
NullSchema,
NumberSchema,
@@ -27,6 +28,10 @@ def test_allof_only_supports_object_schemas(self) -> None:
with self.assertRaises(NotImplementedError):
schema.resolved_schemas
+ schema = UnionTypeSchema(allOf=[BytesSchema()])
+ with self.assertRaises(NotImplementedError):
+ schema.resolved_schemas
+
schema = UnionTypeSchema(allOf=[IntegerSchema()])
with self.assertRaises(NotImplementedError):
schema.resolved_schemas
diff --git a/tests/libcore/unittests/value_utils/test_invalid_value_from_constraint.py b/tests/libcore/unittests/value_utils/test_invalid_value_from_constraint.py
index b4bee34..d937105 100644
--- a/tests/libcore/unittests/value_utils/test_invalid_value_from_constraint.py
+++ b/tests/libcore/unittests/value_utils/test_invalid_value_from_constraint.py
@@ -1,11 +1,10 @@
# pylint: disable="missing-class-docstring", "missing-function-docstring"
import unittest
-from typing import Any
-from OpenApiLibCore import IGNORE
from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
+ BytesSchema,
IntegerSchema,
NullSchema,
NumberSchema,
@@ -60,6 +59,28 @@ def test_string(self) -> None:
values_from_constraint=values,
)
+ def test_bytes(self) -> None:
+ schema = BytesSchema()
+ values = [b"foo"]
+ value = schema.get_invalid_value_from_constraint(
+ values_from_constraint=values,
+ )
+ self.assertNotIn(value, values)
+ self.assertIsInstance(value, bytes)
+
+ values = [b"foo", b"bar", b"baz"]
+ value = schema.get_invalid_value_from_constraint(
+ values_from_constraint=values,
+ )
+ self.assertNotIn(value, values)
+ self.assertIsInstance(value, bytes)
+
+ values = [b""]
+ with self.assertRaises(ValueError):
+ _ = schema.get_invalid_value_from_constraint(
+ values_from_constraint=values,
+ )
+
def test_integer(self) -> None:
schema = IntegerSchema()
values = [0]
From deb62f9475b2df982492c3f995ccd711afb2b870 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Thu, 27 Nov 2025 16:59:35 +0000
Subject: [PATCH 29/55] Updated dependencies / minimum versions
---
poetry.lock | 1055 +++++++++++++++++++++++-------------------------
pyproject.toml | 21 +-
2 files changed, 511 insertions(+), 565 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 878f057..964681b 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2,14 +2,14 @@
[[package]]
name = "annotated-doc"
-version = "0.0.3"
+version = "0.0.4"
description = "Document parameters, class attributes, return types, and variables inline, with Annotated."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
- {file = "annotated_doc-0.0.3-py3-none-any.whl", hash = "sha256:348ec6664a76f1fd3be81f43dffbee4c7e8ce931ba71ec67cc7f4ade7fbbb580"},
- {file = "annotated_doc-0.0.3.tar.gz", hash = "sha256:e18370014c70187422c33e945053ff4c286f453a984eba84d0dbfa0c935adeda"},
+ {file = "annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320"},
+ {file = "annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4"},
]
[[package]]
@@ -47,14 +47,14 @@ trio = ["trio (>=0.31.0)"]
[[package]]
name = "astroid"
-version = "4.0.1"
+version = "4.0.2"
description = "An abstract syntax tree for Python with inference support."
optional = false
python-versions = ">=3.10.0"
groups = ["lint-and-format"]
files = [
- {file = "astroid-4.0.1-py3-none-any.whl", hash = "sha256:37ab2f107d14dc173412327febf6c78d39590fdafcb44868f03b6c03452e3db0"},
- {file = "astroid-4.0.1.tar.gz", hash = "sha256:0d778ec0def05b935e198412e62f9bcca8b3b5c39fdbe50b0ba074005e477aab"},
+ {file = "astroid-4.0.2-py3-none-any.whl", hash = "sha256:d7546c00a12efc32650b19a2bb66a153883185d3179ab0d4868086f807338b9b"},
+ {file = "astroid-4.0.2.tar.gz", hash = "sha256:ac8fb7ca1c08eb9afec91ccc23edbd8ac73bb22cbdd7da1d488d9fb8d6579070"},
]
[package.dependencies]
@@ -72,64 +72,16 @@ files = [
{file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"},
]
-[[package]]
-name = "black"
-version = "25.9.0"
-description = "The uncompromising code formatter."
-optional = false
-python-versions = ">=3.9"
-groups = ["main"]
-files = [
- {file = "black-25.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce41ed2614b706fd55fd0b4a6909d06b5bab344ffbfadc6ef34ae50adba3d4f7"},
- {file = "black-25.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ab0ce111ef026790e9b13bd216fa7bc48edd934ffc4cbf78808b235793cbc92"},
- {file = "black-25.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f96b6726d690c96c60ba682955199f8c39abc1ae0c3a494a9c62c0184049a713"},
- {file = "black-25.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:d119957b37cc641596063cd7db2656c5be3752ac17877017b2ffcdb9dfc4d2b1"},
- {file = "black-25.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:456386fe87bad41b806d53c062e2974615825c7a52159cde7ccaeb0695fa28fa"},
- {file = "black-25.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a16b14a44c1af60a210d8da28e108e13e75a284bf21a9afa6b4571f96ab8bb9d"},
- {file = "black-25.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aaf319612536d502fdd0e88ce52d8f1352b2c0a955cc2798f79eeca9d3af0608"},
- {file = "black-25.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:c0372a93e16b3954208417bfe448e09b0de5cc721d521866cd9e0acac3c04a1f"},
- {file = "black-25.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1b9dc70c21ef8b43248f1d86aedd2aaf75ae110b958a7909ad8463c4aa0880b0"},
- {file = "black-25.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8e46eecf65a095fa62e53245ae2795c90bdecabd53b50c448d0a8bcd0d2e74c4"},
- {file = "black-25.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9101ee58ddc2442199a25cb648d46ba22cd580b00ca4b44234a324e3ec7a0f7e"},
- {file = "black-25.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:77e7060a00c5ec4b3367c55f39cf9b06e68965a4f2e61cecacd6d0d9b7ec945a"},
- {file = "black-25.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0172a012f725b792c358d57fe7b6b6e8e67375dd157f64fa7a3097b3ed3e2175"},
- {file = "black-25.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3bec74ee60f8dfef564b573a96b8930f7b6a538e846123d5ad77ba14a8d7a64f"},
- {file = "black-25.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b756fc75871cb1bcac5499552d771822fd9db5a2bb8db2a7247936ca48f39831"},
- {file = "black-25.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:846d58e3ce7879ec1ffe816bb9df6d006cd9590515ed5d17db14e17666b2b357"},
- {file = "black-25.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ef69351df3c84485a8beb6f7b8f9721e2009e20ef80a8d619e2d1788b7816d47"},
- {file = "black-25.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e3c1f4cd5e93842774d9ee4ef6cd8d17790e65f44f7cdbaab5f2cf8ccf22a823"},
- {file = "black-25.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:154b06d618233fe468236ba1f0e40823d4eb08b26f5e9261526fde34916b9140"},
- {file = "black-25.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:e593466de7b998374ea2585a471ba90553283fb9beefcfa430d84a2651ed5933"},
- {file = "black-25.9.0-py3-none-any.whl", hash = "sha256:474b34c1342cdc157d307b56c4c65bce916480c4a8f6551fdc6bf9b486a7c4ae"},
- {file = "black-25.9.0.tar.gz", hash = "sha256:0474bca9a0dd1b51791fcc507a4e02078a1c63f6d4e4ae5544b9848c7adfb619"},
-]
-
-[package.dependencies]
-click = ">=8.0.0"
-mypy-extensions = ">=0.4.3"
-packaging = ">=22.0"
-pathspec = ">=0.9.0"
-platformdirs = ">=2"
-pytokens = ">=0.1.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.10)"]
-jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
-uvloop = ["uvloop (>=0.15.2)"]
-
[[package]]
name = "certifi"
-version = "2025.10.5"
+version = "2025.11.12"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.7"
groups = ["main", "dev"]
files = [
- {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"},
- {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"},
+ {file = "certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b"},
+ {file = "certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316"},
]
[[package]]
@@ -269,14 +221,14 @@ files = [
[[package]]
name = "click"
-version = "8.3.0"
+version = "8.3.1"
description = "Composable command line interface toolkit"
optional = false
python-versions = ">=3.10"
groups = ["main", "dev", "lint-and-format", "type-checking"]
files = [
- {file = "click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc"},
- {file = "click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4"},
+ {file = "click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6"},
+ {file = "click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"},
]
[package.dependencies]
@@ -297,104 +249,104 @@ markers = {main = "platform_system == \"Windows\"", lint-and-format = "platform_
[[package]]
name = "coverage"
-version = "7.11.0"
+version = "7.12.0"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
- {file = "coverage-7.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eb53f1e8adeeb2e78962bade0c08bfdc461853c7969706ed901821e009b35e31"},
- {file = "coverage-7.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9a03ec6cb9f40a5c360f138b88266fd8f58408d71e89f536b4f91d85721d075"},
- {file = "coverage-7.11.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0d7f0616c557cbc3d1c2090334eddcbb70e1ae3a40b07222d62b3aa47f608fab"},
- {file = "coverage-7.11.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e44a86a47bbdf83b0a3ea4d7df5410d6b1a0de984fbd805fa5101f3624b9abe0"},
- {file = "coverage-7.11.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:596763d2f9a0ee7eec6e643e29660def2eef297e1de0d334c78c08706f1cb785"},
- {file = "coverage-7.11.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ef55537ff511b5e0a43edb4c50a7bf7ba1c3eea20b4f49b1490f1e8e0e42c591"},
- {file = "coverage-7.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cbabd8f4d0d3dc571d77ae5bdbfa6afe5061e679a9d74b6797c48d143307088"},
- {file = "coverage-7.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e24045453384e0ae2a587d562df2a04d852672eb63051d16096d3f08aa4c7c2f"},
- {file = "coverage-7.11.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:7161edd3426c8d19bdccde7d49e6f27f748f3c31cc350c5de7c633fea445d866"},
- {file = "coverage-7.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d4ed4de17e692ba6415b0587bc7f12bc80915031fc9db46a23ce70fc88c9841"},
- {file = "coverage-7.11.0-cp310-cp310-win32.whl", hash = "sha256:765c0bc8fe46f48e341ef737c91c715bd2a53a12792592296a095f0c237e09cf"},
- {file = "coverage-7.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:24d6f3128f1b2d20d84b24f4074475457faedc3d4613a7e66b5e769939c7d969"},
- {file = "coverage-7.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d58ecaa865c5b9fa56e35efc51d1014d4c0d22838815b9fce57a27dd9576847"},
- {file = "coverage-7.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b679e171f1c104a5668550ada700e3c4937110dbdd153b7ef9055c4f1a1ee3cc"},
- {file = "coverage-7.11.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ca61691ba8c5b6797deb221a0d09d7470364733ea9c69425a640f1f01b7c5bf0"},
- {file = "coverage-7.11.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:aef1747ede4bd8ca9cfc04cc3011516500c6891f1b33a94add3253f6f876b7b7"},
- {file = "coverage-7.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1839d08406e4cba2953dcc0ffb312252f14d7c4c96919f70167611f4dee2623"},
- {file = "coverage-7.11.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e0eb0a2dcc62478eb5b4cbb80b97bdee852d7e280b90e81f11b407d0b81c4287"},
- {file = "coverage-7.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fbea96343b53f65d5351d8fd3b34fd415a2670d7c300b06d3e14a5af4f552"},
- {file = "coverage-7.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:214b622259dd0cf435f10241f1333d32caa64dbc27f8790ab693428a141723de"},
- {file = "coverage-7.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:258d9967520cca899695d4eb7ea38be03f06951d6ca2f21fb48b1235f791e601"},
- {file = "coverage-7.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cf9e6ff4ca908ca15c157c409d608da77a56a09877b97c889b98fb2c32b6465e"},
- {file = "coverage-7.11.0-cp311-cp311-win32.whl", hash = "sha256:fcc15fc462707b0680cff6242c48625da7f9a16a28a41bb8fd7a4280920e676c"},
- {file = "coverage-7.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:865965bf955d92790f1facd64fe7ff73551bd2c1e7e6b26443934e9701ba30b9"},
- {file = "coverage-7.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:5693e57a065760dcbeb292d60cc4d0231a6d4b6b6f6a3191561e1d5e8820b745"},
- {file = "coverage-7.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9c49e77811cf9d024b95faf86c3f059b11c0c9be0b0d61bc598f453703bd6fd1"},
- {file = "coverage-7.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a61e37a403a778e2cda2a6a39abcc895f1d984071942a41074b5c7ee31642007"},
- {file = "coverage-7.11.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c79cae102bb3b1801e2ef1511fb50e91ec83a1ce466b2c7c25010d884336de46"},
- {file = "coverage-7.11.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:16ce17ceb5d211f320b62df002fa7016b7442ea0fd260c11cec8ce7730954893"},
- {file = "coverage-7.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80027673e9d0bd6aef86134b0771845e2da85755cf686e7c7c59566cf5a89115"},
- {file = "coverage-7.11.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d3ffa07a08657306cd2215b0da53761c4d73cb54d9143b9303a6481ec0cd415"},
- {file = "coverage-7.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a3b6a5f8b2524fd6c1066bc85bfd97e78709bb5e37b5b94911a6506b65f47186"},
- {file = "coverage-7.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fcc0a4aa589de34bc56e1a80a740ee0f8c47611bdfb28cd1849de60660f3799d"},
- {file = "coverage-7.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dba82204769d78c3fd31b35c3d5f46e06511936c5019c39f98320e05b08f794d"},
- {file = "coverage-7.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:81b335f03ba67309a95210caf3eb43bd6fe75a4e22ba653ef97b4696c56c7ec2"},
- {file = "coverage-7.11.0-cp312-cp312-win32.whl", hash = "sha256:037b2d064c2f8cc8716fe4d39cb705779af3fbf1ba318dc96a1af858888c7bb5"},
- {file = "coverage-7.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:d66c0104aec3b75e5fd897e7940188ea1892ca1d0235316bf89286d6a22568c0"},
- {file = "coverage-7.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:d91ebeac603812a09cf6a886ba6e464f3bbb367411904ae3790dfe28311b15ad"},
- {file = "coverage-7.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cc3f49e65ea6e0d5d9bd60368684fe52a704d46f9e7fc413918f18d046ec40e1"},
- {file = "coverage-7.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f39ae2f63f37472c17b4990f794035c9890418b1b8cca75c01193f3c8d3e01be"},
- {file = "coverage-7.11.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7db53b5cdd2917b6eaadd0b1251cf4e7d96f4a8d24e174bdbdf2f65b5ea7994d"},
- {file = "coverage-7.11.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10ad04ac3a122048688387828b4537bc9cf60c0bf4869c1e9989c46e45690b82"},
- {file = "coverage-7.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4036cc9c7983a2b1f2556d574d2eb2154ac6ed55114761685657e38782b23f52"},
- {file = "coverage-7.11.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ab934dd13b1c5e94b692b1e01bd87e4488cb746e3a50f798cb9464fd128374b"},
- {file = "coverage-7.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59a6e5a265f7cfc05f76e3bb53eca2e0dfe90f05e07e849930fecd6abb8f40b4"},
- {file = "coverage-7.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:df01d6c4c81e15a7c88337b795bb7595a8596e92310266b5072c7e301168efbd"},
- {file = "coverage-7.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8c934bd088eed6174210942761e38ee81d28c46de0132ebb1801dbe36a390dcc"},
- {file = "coverage-7.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a03eaf7ec24078ad64a07f02e30060aaf22b91dedf31a6b24d0d98d2bba7f48"},
- {file = "coverage-7.11.0-cp313-cp313-win32.whl", hash = "sha256:695340f698a5f56f795b2836abe6fb576e7c53d48cd155ad2f80fd24bc63a040"},
- {file = "coverage-7.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:2727d47fce3ee2bac648528e41455d1b0c46395a087a229deac75e9f88ba5a05"},
- {file = "coverage-7.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:0efa742f431529699712b92ecdf22de8ff198df41e43aeaaadf69973eb93f17a"},
- {file = "coverage-7.11.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:587c38849b853b157706407e9ebdca8fd12f45869edb56defbef2daa5fb0812b"},
- {file = "coverage-7.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b971bdefdd75096163dd4261c74be813c4508477e39ff7b92191dea19f24cd37"},
- {file = "coverage-7.11.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:269bfe913b7d5be12ab13a95f3a76da23cf147be7fa043933320ba5625f0a8de"},
- {file = "coverage-7.11.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:dadbcce51a10c07b7c72b0ce4a25e4b6dcb0c0372846afb8e5b6307a121eb99f"},
- {file = "coverage-7.11.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ed43fa22c6436f7957df036331f8fe4efa7af132054e1844918866cd228af6c"},
- {file = "coverage-7.11.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9516add7256b6713ec08359b7b05aeff8850c98d357784c7205b2e60aa2513fa"},
- {file = "coverage-7.11.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb92e47c92fcbcdc692f428da67db33337fa213756f7adb6a011f7b5a7a20740"},
- {file = "coverage-7.11.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d06f4fc7acf3cabd6d74941d53329e06bab00a8fe10e4df2714f0b134bfc64ef"},
- {file = "coverage-7.11.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:6fbcee1a8f056af07ecd344482f711f563a9eb1c2cad192e87df00338ec3cdb0"},
- {file = "coverage-7.11.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dbbf012be5f32533a490709ad597ad8a8ff80c582a95adc8d62af664e532f9ca"},
- {file = "coverage-7.11.0-cp313-cp313t-win32.whl", hash = "sha256:cee6291bb4fed184f1c2b663606a115c743df98a537c969c3c64b49989da96c2"},
- {file = "coverage-7.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a386c1061bf98e7ea4758e4313c0ab5ecf57af341ef0f43a0bf26c2477b5c268"},
- {file = "coverage-7.11.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f9ea02ef40bb83823b2b04964459d281688fe173e20643870bb5d2edf68bc836"},
- {file = "coverage-7.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c770885b28fb399aaf2a65bbd1c12bf6f307ffd112d6a76c5231a94276f0c497"},
- {file = "coverage-7.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a3d0e2087dba64c86a6b254f43e12d264b636a39e88c5cc0a01a7c71bcfdab7e"},
- {file = "coverage-7.11.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:73feb83bb41c32811973b8565f3705caf01d928d972b72042b44e97c71fd70d1"},
- {file = "coverage-7.11.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c6f31f281012235ad08f9a560976cc2fc9c95c17604ff3ab20120fe480169bca"},
- {file = "coverage-7.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9570ad567f880ef675673992222746a124b9595506826b210fbe0ce3f0499cd"},
- {file = "coverage-7.11.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8badf70446042553a773547a61fecaa734b55dc738cacf20c56ab04b77425e43"},
- {file = "coverage-7.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a09c1211959903a479e389685b7feb8a17f59ec5a4ef9afde7650bd5eabc2777"},
- {file = "coverage-7.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:5ef83b107f50db3f9ae40f69e34b3bd9337456c5a7fe3461c7abf8b75dd666a2"},
- {file = "coverage-7.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f91f927a3215b8907e214af77200250bb6aae36eca3f760f89780d13e495388d"},
- {file = "coverage-7.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbcd376716d6b7fbfeedd687a6c4be019c5a5671b35f804ba76a4c0a778cba4"},
- {file = "coverage-7.11.0-cp314-cp314-win32.whl", hash = "sha256:bab7ec4bb501743edc63609320aaec8cd9188b396354f482f4de4d40a9d10721"},
- {file = "coverage-7.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:3d4ba9a449e9364a936a27322b20d32d8b166553bfe63059bd21527e681e2fad"},
- {file = "coverage-7.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:ce37f215223af94ef0f75ac68ea096f9f8e8c8ec7d6e8c346ee45c0d363f0479"},
- {file = "coverage-7.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:f413ce6e07e0d0dc9c433228727b619871532674b45165abafe201f200cc215f"},
- {file = "coverage-7.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:05791e528a18f7072bf5998ba772fe29db4da1234c45c2087866b5ba4dea710e"},
- {file = "coverage-7.11.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cacb29f420cfeb9283b803263c3b9a068924474ff19ca126ba9103e1278dfa44"},
- {file = "coverage-7.11.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314c24e700d7027ae3ab0d95fbf8d53544fca1f20345fd30cd219b737c6e58d3"},
- {file = "coverage-7.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:630d0bd7a293ad2fc8b4b94e5758c8b2536fdf36c05f1681270203e463cbfa9b"},
- {file = "coverage-7.11.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e89641f5175d65e2dbb44db15fe4ea48fade5d5bbb9868fdc2b4fce22f4a469d"},
- {file = "coverage-7.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c9f08ea03114a637dab06cedb2e914da9dc67fa52c6015c018ff43fdde25b9c2"},
- {file = "coverage-7.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce9f3bde4e9b031eaf1eb61df95c1401427029ea1bfddb8621c1161dcb0fa02e"},
- {file = "coverage-7.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:e4dc07e95495923d6fd4d6c27bf70769425b71c89053083843fd78f378558996"},
- {file = "coverage-7.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:424538266794db2861db4922b05d729ade0940ee69dcf0591ce8f69784db0e11"},
- {file = "coverage-7.11.0-cp314-cp314t-win32.whl", hash = "sha256:4c1eeb3fb8eb9e0190bebafd0462936f75717687117339f708f395fe455acc73"},
- {file = "coverage-7.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b56efee146c98dbf2cf5cffc61b9829d1e94442df4d7398b26892a53992d3547"},
- {file = "coverage-7.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b5c2705afa83f49bd91962a4094b6b082f94aef7626365ab3f8f4bd159c5acf3"},
- {file = "coverage-7.11.0-py3-none-any.whl", hash = "sha256:4b7589765348d78fb4e5fb6ea35d07564e387da2fc5efff62e0222971f155f68"},
- {file = "coverage-7.11.0.tar.gz", hash = "sha256:167bd504ac1ca2af7ff3b81d245dfea0292c5032ebef9d66cc08a7d28c1b8050"},
+ {file = "coverage-7.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:32b75c2ba3f324ee37af3ccee5b30458038c50b349ad9b88cee85096132a575b"},
+ {file = "coverage-7.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cb2a1b6ab9fe833714a483a915de350abc624a37149649297624c8d57add089c"},
+ {file = "coverage-7.12.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5734b5d913c3755e72f70bf6cc37a0518d4f4745cde760c5d8e12005e62f9832"},
+ {file = "coverage-7.12.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b527a08cdf15753279b7afb2339a12073620b761d79b81cbe2cdebdb43d90daa"},
+ {file = "coverage-7.12.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9bb44c889fb68004e94cab71f6a021ec83eac9aeabdbb5a5a88821ec46e1da73"},
+ {file = "coverage-7.12.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4b59b501455535e2e5dde5881739897967b272ba25988c89145c12d772810ccb"},
+ {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8842f17095b9868a05837b7b1b73495293091bed870e099521ada176aa3e00e"},
+ {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5a6f20bf48b8866095c6820641e7ffbe23f2ac84a2efc218d91235e404c7777"},
+ {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:5f3738279524e988d9da2893f307c2093815c623f8d05a8f79e3eff3a7a9e553"},
+ {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0d68c1f7eabbc8abe582d11fa393ea483caf4f44b0af86881174769f185c94d"},
+ {file = "coverage-7.12.0-cp310-cp310-win32.whl", hash = "sha256:7670d860e18b1e3ee5930b17a7d55ae6287ec6e55d9799982aa103a2cc1fa2ef"},
+ {file = "coverage-7.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:f999813dddeb2a56aab5841e687b68169da0d3f6fc78ccf50952fa2463746022"},
+ {file = "coverage-7.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa124a3683d2af98bd9d9c2bfa7a5076ca7e5ab09fdb96b81fa7d89376ae928f"},
+ {file = "coverage-7.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d93fbf446c31c0140208dcd07c5d882029832e8ed7891a39d6d44bd65f2316c3"},
+ {file = "coverage-7.12.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:52ca620260bd8cd6027317bdd8b8ba929be1d741764ee765b42c4d79a408601e"},
+ {file = "coverage-7.12.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f3433ffd541380f3a0e423cff0f4926d55b0cc8c1d160fdc3be24a4c03aa65f7"},
+ {file = "coverage-7.12.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7bbb321d4adc9f65e402c677cd1c8e4c2d0105d3ce285b51b4d87f1d5db5245"},
+ {file = "coverage-7.12.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22a7aade354a72dff3b59c577bfd18d6945c61f97393bc5fb7bd293a4237024b"},
+ {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3ff651dcd36d2fea66877cd4a82de478004c59b849945446acb5baf9379a1b64"},
+ {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:31b8b2e38391a56e3cea39d22a23faaa7c3fc911751756ef6d2621d2a9daf742"},
+ {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:297bc2da28440f5ae51c845a47c8175a4db0553a53827886e4fb25c66633000c"},
+ {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6ff7651cc01a246908eac162a6a86fc0dbab6de1ad165dfb9a1e2ec660b44984"},
+ {file = "coverage-7.12.0-cp311-cp311-win32.whl", hash = "sha256:313672140638b6ddb2c6455ddeda41c6a0b208298034544cfca138978c6baed6"},
+ {file = "coverage-7.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a1783ed5bd0d5938d4435014626568dc7f93e3cb99bc59188cc18857c47aa3c4"},
+ {file = "coverage-7.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:4648158fd8dd9381b5847622df1c90ff314efbfc1df4550092ab6013c238a5fc"},
+ {file = "coverage-7.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:29644c928772c78512b48e14156b81255000dcfd4817574ff69def189bcb3647"},
+ {file = "coverage-7.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8638cbb002eaa5d7c8d04da667813ce1067080b9a91099801a0053086e52b736"},
+ {file = "coverage-7.12.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:083631eeff5eb9992c923e14b810a179798bb598e6a0dd60586819fc23be6e60"},
+ {file = "coverage-7.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:99d5415c73ca12d558e07776bd957c4222c687b9f1d26fa0e1b57e3598bdcde8"},
+ {file = "coverage-7.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e949ebf60c717c3df63adb4a1a366c096c8d7fd8472608cd09359e1bd48ef59f"},
+ {file = "coverage-7.12.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d907ddccbca819afa2cd014bc69983b146cca2735a0b1e6259b2a6c10be1e70"},
+ {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b1518ecbad4e6173f4c6e6c4a46e49555ea5679bf3feda5edb1b935c7c44e8a0"},
+ {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51777647a749abdf6f6fd8c7cffab12de68ab93aab15efc72fbbb83036c2a068"},
+ {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:42435d46d6461a3b305cdfcad7cdd3248787771f53fe18305548cba474e6523b"},
+ {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5bcead88c8423e1855e64b8057d0544e33e4080b95b240c2a355334bb7ced937"},
+ {file = "coverage-7.12.0-cp312-cp312-win32.whl", hash = "sha256:dcbb630ab034e86d2a0f79aefd2be07e583202f41e037602d438c80044957baa"},
+ {file = "coverage-7.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:2fd8354ed5d69775ac42986a691fbf68b4084278710cee9d7c3eaa0c28fa982a"},
+ {file = "coverage-7.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:737c3814903be30695b2de20d22bcc5428fdae305c61ba44cdc8b3252984c49c"},
+ {file = "coverage-7.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47324fffca8d8eae7e185b5bb20c14645f23350f870c1649003618ea91a78941"},
+ {file = "coverage-7.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ccf3b2ede91decd2fb53ec73c1f949c3e034129d1e0b07798ff1d02ea0c8fa4a"},
+ {file = "coverage-7.12.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b365adc70a6936c6b0582dc38746b33b2454148c02349345412c6e743efb646d"},
+ {file = "coverage-7.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bc13baf85cd8a4cfcf4a35c7bc9d795837ad809775f782f697bf630b7e200211"},
+ {file = "coverage-7.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:099d11698385d572ceafb3288a5b80fe1fc58bf665b3f9d362389de488361d3d"},
+ {file = "coverage-7.12.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:473dc45d69694069adb7680c405fb1e81f60b2aff42c81e2f2c3feaf544d878c"},
+ {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:583f9adbefd278e9de33c33d6846aa8f5d164fa49b47144180a0e037f0688bb9"},
+ {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2089cc445f2dc0af6f801f0d1355c025b76c24481935303cf1af28f636688f0"},
+ {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:950411f1eb5d579999c5f66c62a40961f126fc71e5e14419f004471957b51508"},
+ {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b1aab7302a87bafebfe76b12af681b56ff446dc6f32ed178ff9c092ca776e6bc"},
+ {file = "coverage-7.12.0-cp313-cp313-win32.whl", hash = "sha256:d7e0d0303c13b54db495eb636bc2465b2fb8475d4c8bcec8fe4b5ca454dfbae8"},
+ {file = "coverage-7.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:ce61969812d6a98a981d147d9ac583a36ac7db7766f2e64a9d4d059c2fe29d07"},
+ {file = "coverage-7.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bcec6f47e4cb8a4c2dc91ce507f6eefc6a1b10f58df32cdc61dff65455031dfc"},
+ {file = "coverage-7.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:459443346509476170d553035e4a3eed7b860f4fe5242f02de1010501956ce87"},
+ {file = "coverage-7.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:04a79245ab2b7a61688958f7a855275997134bc84f4a03bc240cf64ff132abf6"},
+ {file = "coverage-7.12.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:09a86acaaa8455f13d6a99221d9654df249b33937b4e212b4e5a822065f12aa7"},
+ {file = "coverage-7.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:907e0df1b71ba77463687a74149c6122c3f6aac56c2510a5d906b2f368208560"},
+ {file = "coverage-7.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b57e2d0ddd5f0582bae5437c04ee71c46cd908e7bc5d4d0391f9a41e812dd12"},
+ {file = "coverage-7.12.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:58c1c6aa677f3a1411fe6fb28ec3a942e4f665df036a3608816e0847fad23296"},
+ {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4c589361263ab2953e3c4cd2a94db94c4ad4a8e572776ecfbad2389c626e4507"},
+ {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:91b810a163ccad2e43b1faa11d70d3cf4b6f3d83f9fd5f2df82a32d47b648e0d"},
+ {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:40c867af715f22592e0d0fb533a33a71ec9e0f73a6945f722a0c85c8c1cbe3a2"},
+ {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:68b0d0a2d84f333de875666259dadf28cc67858bc8fd8b3f1eae84d3c2bec455"},
+ {file = "coverage-7.12.0-cp313-cp313t-win32.whl", hash = "sha256:73f9e7fbd51a221818fd11b7090eaa835a353ddd59c236c57b2199486b116c6d"},
+ {file = "coverage-7.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:24cff9d1f5743f67db7ba46ff284018a6e9aeb649b67aa1e70c396aa1b7cb23c"},
+ {file = "coverage-7.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c87395744f5c77c866d0f5a43d97cc39e17c7f1cb0115e54a2fe67ca75c5d14d"},
+ {file = "coverage-7.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a1c59b7dc169809a88b21a936eccf71c3895a78f5592051b1af8f4d59c2b4f92"},
+ {file = "coverage-7.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8787b0f982e020adb732b9f051f3e49dd5054cebbc3f3432061278512a2b1360"},
+ {file = "coverage-7.12.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5ea5a9f7dc8877455b13dd1effd3202e0bca72f6f3ab09f9036b1bcf728f69ac"},
+ {file = "coverage-7.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fdba9f15849534594f60b47c9a30bc70409b54947319a7c4fd0e8e3d8d2f355d"},
+ {file = "coverage-7.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a00594770eb715854fb1c57e0dea08cce6720cfbc531accdb9850d7c7770396c"},
+ {file = "coverage-7.12.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5560c7e0d82b42eb1951e4f68f071f8017c824ebfd5a6ebe42c60ac16c6c2434"},
+ {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2e26b481c9159c2773a37947a9718cfdc58893029cdfb177531793e375cfc"},
+ {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6e1a8c066dabcde56d5d9fed6a66bc19a2883a3fe051f0c397a41fc42aedd4cc"},
+ {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f7ba9da4726e446d8dd8aae5a6cd872511184a5d861de80a86ef970b5dacce3e"},
+ {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e0f483ab4f749039894abaf80c2f9e7ed77bbf3c737517fb88c8e8e305896a17"},
+ {file = "coverage-7.12.0-cp314-cp314-win32.whl", hash = "sha256:76336c19a9ef4a94b2f8dc79f8ac2da3f193f625bb5d6f51a328cd19bfc19933"},
+ {file = "coverage-7.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7c1059b600aec6ef090721f8f633f60ed70afaffe8ecab85b59df748f24b31fe"},
+ {file = "coverage-7.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:172cf3a34bfef42611963e2b661302a8931f44df31629e5b1050567d6b90287d"},
+ {file = "coverage-7.12.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:aa7d48520a32cb21c7a9b31f81799e8eaec7239db36c3b670be0fa2403828d1d"},
+ {file = "coverage-7.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:90d58ac63bc85e0fb919f14d09d6caa63f35a5512a2205284b7816cafd21bb03"},
+ {file = "coverage-7.12.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ca8ecfa283764fdda3eae1bdb6afe58bf78c2c3ec2b2edcb05a671f0bba7b3f9"},
+ {file = "coverage-7.12.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:874fe69a0785d96bd066059cd4368022cebbec1a8958f224f0016979183916e6"},
+ {file = "coverage-7.12.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3c889c0b8b283a24d721a9eabc8ccafcfc3aebf167e4cd0d0e23bf8ec4e339"},
+ {file = "coverage-7.12.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bb5b894b3ec09dcd6d3743229dc7f2c42ef7787dc40596ae04c0edda487371e"},
+ {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:79a44421cd5fba96aa57b5e3b5a4d3274c449d4c622e8f76882d76635501fd13"},
+ {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:33baadc0efd5c7294f436a632566ccc1f72c867f82833eb59820ee37dc811c6f"},
+ {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c406a71f544800ef7e9e0000af706b88465f3573ae8b8de37e5f96c59f689ad1"},
+ {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e71bba6a40883b00c6d571599b4627f50c360b3d0d02bfc658168936be74027b"},
+ {file = "coverage-7.12.0-cp314-cp314t-win32.whl", hash = "sha256:9157a5e233c40ce6613dead4c131a006adfda70e557b6856b97aceed01b0e27a"},
+ {file = "coverage-7.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e84da3a0fd233aeec797b981c51af1cabac74f9bd67be42458365b30d11b5291"},
+ {file = "coverage-7.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:01d24af36fedda51c2b1aca56e4330a3710f83b02a5ff3743a6b015ffa7c9384"},
+ {file = "coverage-7.12.0-py3-none-any.whl", hash = "sha256:159d50c0b12e060b15ed3d39f87ed43d4f7f7ad40b8a534f4dd331adbb51104a"},
+ {file = "coverage-7.12.0.tar.gz", hash = "sha256:fc11e0a4e372cb5f282f16ef90d4a585034050ccda536451901abfb19a57f40c"},
]
[package.dependencies]
@@ -433,27 +385,27 @@ profile = ["gprof2dot (>=2022.7.29)"]
[[package]]
name = "docutils"
-version = "0.22.2"
+version = "0.22.3"
description = "Docutils -- Python Documentation Utilities"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "docutils-0.22.2-py3-none-any.whl", hash = "sha256:b0e98d679283fc3bb0ead8a5da7f501baa632654e7056e9c5846842213d674d8"},
- {file = "docutils-0.22.2.tar.gz", hash = "sha256:9fdb771707c8784c8f2728b67cb2c691305933d68137ef95a75db5f4dfbc213d"},
+ {file = "docutils-0.22.3-py3-none-any.whl", hash = "sha256:bd772e4aca73aff037958d44f2be5229ded4c09927fcf8690c577b66234d6ceb"},
+ {file = "docutils-0.22.3.tar.gz", hash = "sha256:21486ae730e4ca9f622677b1412b879af1791efcfba517e4c6f60be543fc8cdd"},
]
[[package]]
name = "exceptiongroup"
-version = "1.3.0"
+version = "1.3.1"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
markers = "python_version < \"3.11\""
files = [
- {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"},
- {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"},
+ {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"},
+ {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"},
]
[package.dependencies]
@@ -464,14 +416,14 @@ test = ["pytest (>=6)"]
[[package]]
name = "faker"
-version = "37.12.0"
+version = "38.2.0"
description = "Faker is a Python package that generates fake data for you."
optional = false
-python-versions = ">=3.9"
+python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "faker-37.12.0-py3-none-any.whl", hash = "sha256:afe7ccc038da92f2fbae30d8e16d19d91e92e242f8401ce9caf44de892bab4c4"},
- {file = "faker-37.12.0.tar.gz", hash = "sha256:7505e59a7e02fa9010f06c3e1e92f8250d4cfbb30632296140c2d6dbef09b0fa"},
+ {file = "faker-38.2.0-py3-none-any.whl", hash = "sha256:35fe4a0a79dee0dc4103a6083ee9224941e7d3594811a50e3969e547b0d2ee65"},
+ {file = "faker-38.2.0.tar.gz", hash = "sha256:20672803db9c7cb97f9b56c18c54b915b6f1d8991f63d1d673642dc43f5ce7ab"},
]
[package.dependencies]
@@ -479,20 +431,20 @@ tzdata = "*"
[[package]]
name = "fastapi"
-version = "0.120.4"
+version = "0.122.0"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
- {file = "fastapi-0.120.4-py3-none-any.whl", hash = "sha256:9bdf192308676480d3593e10fd05094e56d6fdc7d9283db26053d8104d5f82a0"},
- {file = "fastapi-0.120.4.tar.gz", hash = "sha256:2d856bc847893ca4d77896d4504ffdec0fb04312b705065fca9104428eca3868"},
+ {file = "fastapi-0.122.0-py3-none-any.whl", hash = "sha256:a456e8915dfc6c8914a50d9651133bd47ec96d331c5b44600baa635538a30d67"},
+ {file = "fastapi-0.122.0.tar.gz", hash = "sha256:cd9b5352031f93773228af8b4c443eedc2ac2aa74b27780387b853c3726fb94b"},
]
[package.dependencies]
annotated-doc = ">=0.0.2"
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
-starlette = ">=0.40.0,<0.50.0"
+starlette = ">=0.40.0,<0.51.0"
typing-extensions = ">=4.8.0"
[package.extras]
@@ -502,14 +454,14 @@ standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[stand
[[package]]
name = "genbadge"
-version = "1.1.2"
+version = "1.1.3"
description = "Generate badges for tools that do not provide one."
optional = false
python-versions = "*"
groups = ["dev"]
files = [
- {file = "genbadge-1.1.2-py2.py3-none-any.whl", hash = "sha256:4e3073cb56c2745fbef4b7da97eb85b28a18a22af519b66acb6706b6546279f1"},
- {file = "genbadge-1.1.2.tar.gz", hash = "sha256:987ed2feaf6e9cc2850fc3883320d8706b3849eb6c9f436156254dcac438515c"},
+ {file = "genbadge-1.1.3-py2.py3-none-any.whl", hash = "sha256:6e4316c171c6f0f84becae4eb116258340bdc054458632abc622d36b8040655e"},
+ {file = "genbadge-1.1.3.tar.gz", hash = "sha256:2292ea9cc20af4463dfde952c6b15544fdab9d6e50945f63a42cc400c521fa74"},
]
[package.dependencies]
@@ -945,7 +897,7 @@ version = "1.1.0"
description = "Type system extensions for programs checked with the mypy type checker."
optional = false
python-versions = ">=3.8"
-groups = ["main", "type-checking"]
+groups = ["type-checking"]
files = [
{file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"},
{file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"},
@@ -1072,7 +1024,7 @@ version = "0.12.1"
description = "Utility library for gitignore style pattern matching of file paths."
optional = false
python-versions = ">=3.8"
-groups = ["main", "lint-and-format", "type-checking"]
+groups = ["lint-and-format", "type-checking"]
files = [
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
@@ -1193,7 +1145,7 @@ version = "4.5.0"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
optional = false
python-versions = ">=3.10"
-groups = ["main", "dev", "lint-and-format", "type-checking"]
+groups = ["dev", "lint-and-format", "type-checking"]
files = [
{file = "platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3"},
{file = "platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312"},
@@ -1249,19 +1201,19 @@ ssv = ["swagger-spec-validator (>=3.0.4,<3.1.0)"]
[[package]]
name = "pydantic"
-version = "2.12.3"
+version = "2.12.5"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.9"
groups = ["main", "dev"]
files = [
- {file = "pydantic-2.12.3-py3-none-any.whl", hash = "sha256:6986454a854bc3bc6e5443e1369e06a3a456af9d339eda45510f517d9ea5c6bf"},
- {file = "pydantic-2.12.3.tar.gz", hash = "sha256:1da1c82b0fc140bb0103bc1441ffe062154c8d38491189751ee00fd8ca65ce74"},
+ {file = "pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d"},
+ {file = "pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49"},
]
[package.dependencies]
annotated-types = ">=0.6.0"
-pydantic-core = "2.41.4"
+pydantic-core = "2.41.5"
typing-extensions = ">=4.14.1"
typing-inspection = ">=0.4.2"
@@ -1271,129 +1223,133 @@ timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows
[[package]]
name = "pydantic-core"
-version = "2.41.4"
+version = "2.41.5"
description = "Core functionality for Pydantic validation and serialization"
optional = false
python-versions = ">=3.9"
groups = ["main", "dev"]
files = [
- {file = "pydantic_core-2.41.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2442d9a4d38f3411f22eb9dd0912b7cbf4b7d5b6c92c4173b75d3e1ccd84e36e"},
- {file = "pydantic_core-2.41.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:30a9876226dda131a741afeab2702e2d127209bde3c65a2b8133f428bc5d006b"},
- {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d55bbac04711e2980645af68b97d445cdbcce70e5216de444a6c4b6943ebcccd"},
- {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e1d778fb7849a42d0ee5927ab0f7453bf9f85eef8887a546ec87db5ddb178945"},
- {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b65077a4693a98b90ec5ad8f203ad65802a1b9b6d4a7e48066925a7e1606706"},
- {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62637c769dee16eddb7686bf421be48dfc2fae93832c25e25bc7242e698361ba"},
- {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfe3aa529c8f501babf6e502936b9e8d4698502b2cfab41e17a028d91b1ac7b"},
- {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca2322da745bf2eeb581fc9ea3bbb31147702163ccbcbf12a3bb630e4bf05e1d"},
- {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e8cd3577c796be7231dcf80badcf2e0835a46665eaafd8ace124d886bab4d700"},
- {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:1cae8851e174c83633f0833e90636832857297900133705ee158cf79d40f03e6"},
- {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a26d950449aae348afe1ac8be5525a00ae4235309b729ad4d3399623125b43c9"},
- {file = "pydantic_core-2.41.4-cp310-cp310-win32.whl", hash = "sha256:0cf2a1f599efe57fa0051312774280ee0f650e11152325e41dfd3018ef2c1b57"},
- {file = "pydantic_core-2.41.4-cp310-cp310-win_amd64.whl", hash = "sha256:a8c2e340d7e454dc3340d3d2e8f23558ebe78c98aa8f68851b04dcb7bc37abdc"},
- {file = "pydantic_core-2.41.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:28ff11666443a1a8cf2a044d6a545ebffa8382b5f7973f22c36109205e65dc80"},
- {file = "pydantic_core-2.41.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61760c3925d4633290292bad462e0f737b840508b4f722247d8729684f6539ae"},
- {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eae547b7315d055b0de2ec3965643b0ab82ad0106a7ffd29615ee9f266a02827"},
- {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef9ee5471edd58d1fcce1c80ffc8783a650e3e3a193fe90d52e43bb4d87bff1f"},
- {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15dd504af121caaf2c95cb90c0ebf71603c53de98305621b94da0f967e572def"},
- {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a926768ea49a8af4d36abd6a8968b8790f7f76dd7cbd5a4c180db2b4ac9a3a2"},
- {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916b9b7d134bff5440098a4deb80e4cb623e68974a87883299de9124126c2a8"},
- {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cf90535979089df02e6f17ffd076f07237efa55b7343d98760bde8743c4b265"},
- {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7533c76fa647fade2d7ec75ac5cc079ab3f34879626dae5689b27790a6cf5a5c"},
- {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:37e516bca9264cbf29612539801ca3cd5d1be465f940417b002905e6ed79d38a"},
- {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0c19cb355224037c83642429b8ce261ae108e1c5fbf5c028bac63c77b0f8646e"},
- {file = "pydantic_core-2.41.4-cp311-cp311-win32.whl", hash = "sha256:09c2a60e55b357284b5f31f5ab275ba9f7f70b7525e18a132ec1f9160b4f1f03"},
- {file = "pydantic_core-2.41.4-cp311-cp311-win_amd64.whl", hash = "sha256:711156b6afb5cb1cb7c14a2cc2c4a8b4c717b69046f13c6b332d8a0a8f41ca3e"},
- {file = "pydantic_core-2.41.4-cp311-cp311-win_arm64.whl", hash = "sha256:6cb9cf7e761f4f8a8589a45e49ed3c0d92d1d696a45a6feaee8c904b26efc2db"},
- {file = "pydantic_core-2.41.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ab06d77e053d660a6faaf04894446df7b0a7e7aba70c2797465a0a1af00fc887"},
- {file = "pydantic_core-2.41.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c53ff33e603a9c1179a9364b0a24694f183717b2e0da2b5ad43c316c956901b2"},
- {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:304c54176af2c143bd181d82e77c15c41cbacea8872a2225dd37e6544dce9999"},
- {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025ba34a4cf4fb32f917d5d188ab5e702223d3ba603be4d8aca2f82bede432a4"},
- {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9f5f30c402ed58f90c70e12eff65547d3ab74685ffe8283c719e6bead8ef53f"},
- {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd96e5d15385d301733113bcaa324c8bcf111275b7675a9c6e88bfb19fc05e3b"},
- {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f348cbb44fae6e9653c1055db7e29de67ea6a9ca03a5fa2c2e11a47cff0e47"},
- {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec22626a2d14620a83ca583c6f5a4080fa3155282718b6055c2ea48d3ef35970"},
- {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a95d4590b1f1a43bf33ca6d647b990a88f4a3824a8c4572c708f0b45a5290ed"},
- {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:f9672ab4d398e1b602feadcffcdd3af44d5f5e6ddc15bc7d15d376d47e8e19f8"},
- {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:84d8854db5f55fead3b579f04bda9a36461dab0730c5d570e1526483e7bb8431"},
- {file = "pydantic_core-2.41.4-cp312-cp312-win32.whl", hash = "sha256:9be1c01adb2ecc4e464392c36d17f97e9110fbbc906bcbe1c943b5b87a74aabd"},
- {file = "pydantic_core-2.41.4-cp312-cp312-win_amd64.whl", hash = "sha256:d682cf1d22bab22a5be08539dca3d1593488a99998f9f412137bc323179067ff"},
- {file = "pydantic_core-2.41.4-cp312-cp312-win_arm64.whl", hash = "sha256:833eebfd75a26d17470b58768c1834dfc90141b7afc6eb0429c21fc5a21dcfb8"},
- {file = "pydantic_core-2.41.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:85e050ad9e5f6fe1004eec65c914332e52f429bc0ae12d6fa2092407a462c746"},
- {file = "pydantic_core-2.41.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7393f1d64792763a48924ba31d1e44c2cfbc05e3b1c2c9abb4ceeadd912cced"},
- {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94dab0940b0d1fb28bcab847adf887c66a27a40291eedf0b473be58761c9799a"},
- {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de7c42f897e689ee6f9e93c4bec72b99ae3b32a2ade1c7e4798e690ff5246e02"},
- {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:664b3199193262277b8b3cd1e754fb07f2c6023289c815a1e1e8fb415cb247b1"},
- {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95b253b88f7d308b1c0b417c4624f44553ba4762816f94e6986819b9c273fb2"},
- {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1351f5bbdbbabc689727cb91649a00cb9ee7203e0a6e54e9f5ba9e22e384b84"},
- {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1affa4798520b148d7182da0615d648e752de4ab1a9566b7471bc803d88a062d"},
- {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7b74e18052fea4aa8dea2fb7dbc23d15439695da6cbe6cfc1b694af1115df09d"},
- {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:285b643d75c0e30abda9dc1077395624f314a37e3c09ca402d4015ef5979f1a2"},
- {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f52679ff4218d713b3b33f88c89ccbf3a5c2c12ba665fb80ccc4192b4608dbab"},
- {file = "pydantic_core-2.41.4-cp313-cp313-win32.whl", hash = "sha256:ecde6dedd6fff127c273c76821bb754d793be1024bc33314a120f83a3c69460c"},
- {file = "pydantic_core-2.41.4-cp313-cp313-win_amd64.whl", hash = "sha256:d081a1f3800f05409ed868ebb2d74ac39dd0c1ff6c035b5162356d76030736d4"},
- {file = "pydantic_core-2.41.4-cp313-cp313-win_arm64.whl", hash = "sha256:f8e49c9c364a7edcbe2a310f12733aad95b022495ef2a8d653f645e5d20c1564"},
- {file = "pydantic_core-2.41.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ed97fd56a561f5eb5706cebe94f1ad7c13b84d98312a05546f2ad036bafe87f4"},
- {file = "pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a870c307bf1ee91fc58a9a61338ff780d01bfae45922624816878dce784095d2"},
- {file = "pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25e97bc1f5f8f7985bdc2335ef9e73843bb561eb1fa6831fdfc295c1c2061cf"},
- {file = "pydantic_core-2.41.4-cp313-cp313t-win_amd64.whl", hash = "sha256:d405d14bea042f166512add3091c1af40437c2e7f86988f3915fabd27b1e9cd2"},
- {file = "pydantic_core-2.41.4-cp313-cp313t-win_arm64.whl", hash = "sha256:19f3684868309db5263a11bace3c45d93f6f24afa2ffe75a647583df22a2ff89"},
- {file = "pydantic_core-2.41.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e9205d97ed08a82ebb9a307e92914bb30e18cdf6f6b12ca4bedadb1588a0bfe1"},
- {file = "pydantic_core-2.41.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82df1f432b37d832709fbcc0e24394bba04a01b6ecf1ee87578145c19cde12ac"},
- {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3b4cc4539e055cfa39a3763c939f9d409eb40e85813257dcd761985a108554"},
- {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1eb1754fce47c63d2ff57fdb88c351a6c0150995890088b33767a10218eaa4e"},
- {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6ab5ab30ef325b443f379ddb575a34969c333004fca5a1daa0133a6ffaad616"},
- {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31a41030b1d9ca497634092b46481b937ff9397a86f9f51bd41c4767b6fc04af"},
- {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a44ac1738591472c3d020f61c6df1e4015180d6262ebd39bf2aeb52571b60f12"},
- {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d72f2b5e6e82ab8f94ea7d0d42f83c487dc159c5240d8f83beae684472864e2d"},
- {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c4d1e854aaf044487d31143f541f7aafe7b482ae72a022c664b2de2e466ed0ad"},
- {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b568af94267729d76e6ee5ececda4e283d07bbb28e8148bb17adad93d025d25a"},
- {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6d55fb8b1e8929b341cc313a81a26e0d48aa3b519c1dbaadec3a6a2b4fcad025"},
- {file = "pydantic_core-2.41.4-cp314-cp314-win32.whl", hash = "sha256:5b66584e549e2e32a1398df11da2e0a7eff45d5c2d9db9d5667c5e6ac764d77e"},
- {file = "pydantic_core-2.41.4-cp314-cp314-win_amd64.whl", hash = "sha256:557a0aab88664cc552285316809cab897716a372afaf8efdbef756f8b890e894"},
- {file = "pydantic_core-2.41.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f1ea6f48a045745d0d9f325989d8abd3f1eaf47dd00485912d1a3a63c623a8d"},
- {file = "pydantic_core-2.41.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6c1fe4c5404c448b13188dd8bd2ebc2bdd7e6727fa61ff481bcc2cca894018da"},
- {file = "pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:523e7da4d43b113bf8e7b49fa4ec0c35bf4fe66b2230bfc5c13cc498f12c6c3e"},
- {file = "pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5729225de81fb65b70fdb1907fcf08c75d498f4a6f15af005aabb1fdadc19dfa"},
- {file = "pydantic_core-2.41.4-cp314-cp314t-win_amd64.whl", hash = "sha256:de2cfbb09e88f0f795fd90cf955858fc2c691df65b1f21f0aa00b99f3fbc661d"},
- {file = "pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0"},
- {file = "pydantic_core-2.41.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:646e76293345954acea6966149683047b7b2ace793011922208c8e9da12b0062"},
- {file = "pydantic_core-2.41.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cc8e85a63085a137d286e2791037f5fdfff0aabb8b899483ca9c496dd5797338"},
- {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:692c622c8f859a17c156492783902d8370ac7e121a611bd6fe92cc71acf9ee8d"},
- {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1e2906efb1031a532600679b424ef1d95d9f9fb507f813951f23320903adbd7"},
- {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04e2f7f8916ad3ddd417a7abdd295276a0bf216993d9318a5d61cc058209166"},
- {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df649916b81822543d1c8e0e1d079235f68acdc7d270c911e8425045a8cfc57e"},
- {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c529f862fdba70558061bb936fe00ddbaaa0c647fd26e4a4356ef1d6561891"},
- {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3b4c5a1fd3a311563ed866c2c9b62da06cb6398bee186484ce95c820db71cb"},
- {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6e0fc40d84448f941df9b3334c4b78fe42f36e3bf631ad54c3047a0cdddc2514"},
- {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:44e7625332683b6c1c8b980461475cde9595eff94447500e80716db89b0da005"},
- {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:170ee6835f6c71081d031ef1c3b4dc4a12b9efa6a9540f93f95b82f3c7571ae8"},
- {file = "pydantic_core-2.41.4-cp39-cp39-win32.whl", hash = "sha256:3adf61415efa6ce977041ba9745183c0e1f637ca849773afa93833e04b163feb"},
- {file = "pydantic_core-2.41.4-cp39-cp39-win_amd64.whl", hash = "sha256:a238dd3feee263eeaeb7dc44aea4ba1364682c4f9f9467e6af5596ba322c2332"},
- {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:a1b2cfec3879afb742a7b0bcfa53e4f22ba96571c9e54d6a3afe1052d17d843b"},
- {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:d175600d975b7c244af6eb9c9041f10059f20b8bbffec9e33fdd5ee3f67cdc42"},
- {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f184d657fa4947ae5ec9c47bd7e917730fa1cbb78195037e32dcbab50aca5ee"},
- {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed810568aeffed3edc78910af32af911c835cc39ebbfacd1f0ab5dd53028e5c"},
- {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:4f5d640aeebb438517150fdeec097739614421900e4a08db4a3ef38898798537"},
- {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:4a9ab037b71927babc6d9e7fc01aea9e66dc2a4a34dff06ef0724a4049629f94"},
- {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4dab9484ec605c3016df9ad4fd4f9a390bc5d816a3b10c6550f8424bb80b18c"},
- {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8a5028425820731d8c6c098ab642d7b8b999758e24acae03ed38a66eca8335"},
- {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1e5ab4fc177dd41536b3c32b2ea11380dd3d4619a385860621478ac2d25ceb00"},
- {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3d88d0054d3fa11ce936184896bed3c1c5441d6fa483b498fac6a5d0dd6f64a9"},
- {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2a054a8725f05b4b6503357e0ac1c4e8234ad3b0c2ac130d6ffc66f0e170e2"},
- {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0d9db5a161c99375a0c68c058e227bee1d89303300802601d76a3d01f74e258"},
- {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:6273ea2c8ffdac7b7fda2653c49682db815aebf4a89243a6feccf5e36c18c347"},
- {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:4c973add636efc61de22530b2ef83a65f39b6d6f656df97f678720e20de26caa"},
- {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b69d1973354758007f46cf2d44a4f3d0933f10b6dc9bf15cf1356e037f6f731a"},
- {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3619320641fd212aaf5997b6ca505e97540b7e16418f4a241f44cdf108ffb50d"},
- {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:491535d45cd7ad7e4a2af4a5169b0d07bebf1adfd164b0368da8aa41e19907a5"},
- {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:54d86c0cada6aba4ec4c047d0e348cbad7063b87ae0f005d9f8c9ad04d4a92a2"},
- {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca1124aced216b2500dc2609eade086d718e8249cb9696660ab447d50a758bd"},
- {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c9024169becccf0cb470ada03ee578d7348c119a0d42af3dcf9eda96e3a247c"},
- {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:26895a4268ae5a2849269f4991cdc97236e4b9c010e51137becf25182daac405"},
- {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:ca4df25762cf71308c446e33c9b1fdca2923a3f13de616e2a949f38bf21ff5a8"},
- {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5a28fcedd762349519276c36634e71853b4541079cab4acaaac60c4421827308"},
- {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c173ddcd86afd2535e2b695217e82191580663a1d1928239f877f5a1649ef39f"},
- {file = "pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5"},
+ {file = "pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146"},
+ {file = "pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2"},
+ {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97"},
+ {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9"},
+ {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52"},
+ {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941"},
+ {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a"},
+ {file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c"},
+ {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2"},
+ {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556"},
+ {file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49"},
+ {file = "pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba"},
+ {file = "pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9"},
+ {file = "pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6"},
+ {file = "pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b"},
+ {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a"},
+ {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8"},
+ {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e"},
+ {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1"},
+ {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b"},
+ {file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b"},
+ {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284"},
+ {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594"},
+ {file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e"},
+ {file = "pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b"},
+ {file = "pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe"},
+ {file = "pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f"},
+ {file = "pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7"},
+ {file = "pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0"},
+ {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69"},
+ {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75"},
+ {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05"},
+ {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc"},
+ {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c"},
+ {file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5"},
+ {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c"},
+ {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294"},
+ {file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1"},
+ {file = "pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d"},
+ {file = "pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815"},
+ {file = "pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3"},
+ {file = "pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9"},
+ {file = "pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34"},
+ {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0"},
+ {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33"},
+ {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e"},
+ {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2"},
+ {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586"},
+ {file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d"},
+ {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740"},
+ {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e"},
+ {file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858"},
+ {file = "pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36"},
+ {file = "pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11"},
+ {file = "pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd"},
+ {file = "pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a"},
+ {file = "pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14"},
+ {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1"},
+ {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66"},
+ {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869"},
+ {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2"},
+ {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375"},
+ {file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553"},
+ {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90"},
+ {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07"},
+ {file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb"},
+ {file = "pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23"},
+ {file = "pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf"},
+ {file = "pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0"},
+ {file = "pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a"},
+ {file = "pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3"},
+ {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c"},
+ {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612"},
+ {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d"},
+ {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9"},
+ {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660"},
+ {file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9"},
+ {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3"},
+ {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf"},
+ {file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470"},
+ {file = "pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa"},
+ {file = "pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c"},
+ {file = "pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008"},
+ {file = "pydantic_core-2.41.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf"},
+ {file = "pydantic_core-2.41.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5"},
+ {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d"},
+ {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60"},
+ {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82"},
+ {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5"},
+ {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3"},
+ {file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425"},
+ {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504"},
+ {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5"},
+ {file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3"},
+ {file = "pydantic_core-2.41.5-cp39-cp39-win32.whl", hash = "sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460"},
+ {file = "pydantic_core-2.41.5-cp39-cp39-win_amd64.whl", hash = "sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b"},
+ {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034"},
+ {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c"},
+ {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2"},
+ {file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad"},
+ {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd"},
+ {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc"},
+ {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56"},
+ {file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b"},
+ {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8"},
+ {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a"},
+ {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b"},
+ {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2"},
+ {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093"},
+ {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a"},
+ {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963"},
+ {file = "pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a"},
+ {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26"},
+ {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808"},
+ {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc"},
+ {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1"},
+ {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84"},
+ {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770"},
+ {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f"},
+ {file = "pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51"},
+ {file = "pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e"},
]
[package.dependencies]
@@ -1416,18 +1372,18 @@ windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pylint"
-version = "4.0.2"
+version = "4.0.3"
description = "python code static checker"
optional = false
python-versions = ">=3.10.0"
groups = ["lint-and-format"]
files = [
- {file = "pylint-4.0.2-py3-none-any.whl", hash = "sha256:9627ccd129893fb8ee8e8010261cb13485daca83e61a6f854a85528ee579502d"},
- {file = "pylint-4.0.2.tar.gz", hash = "sha256:9c22dfa52781d3b79ce86ab2463940f874921a3e5707bcfc98dd0c019945014e"},
+ {file = "pylint-4.0.3-py3-none-any.whl", hash = "sha256:896d09afb0e78bbf2e030cd1f3d8dc92771a51f7e46828cbc3948a89cd03433a"},
+ {file = "pylint-4.0.3.tar.gz", hash = "sha256:a427fe76e0e5355e9fb9b604fd106c419cafb395886ba7f3cebebb03f30e081d"},
]
[package.dependencies]
-astroid = ">=4.0.1,<=4.1.dev0"
+astroid = ">=4.0.2,<=4.1.dev0"
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
dill = [
{version = ">=0.2", markers = "python_version < \"3.11\""},
@@ -1465,21 +1421,6 @@ all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"]
dev = ["twine (>=3.4.1)"]
nodejs = ["nodejs-wheel-binaries"]
-[[package]]
-name = "pytokens"
-version = "0.2.0"
-description = "A Fast, spec compliant Python 3.13+ tokenizer that runs on older Pythons."
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-files = [
- {file = "pytokens-0.2.0-py3-none-any.whl", hash = "sha256:74d4b318c67f4295c13782ddd9abcb7e297ec5630ad060eb90abf7ebbefe59f8"},
- {file = "pytokens-0.2.0.tar.gz", hash = "sha256:532d6421364e5869ea57a9523bf385f02586d4662acbcc0342afd69511b4dd43"},
-]
-
-[package.extras]
-dev = ["black", "build", "mypy", "pytest", "pytest-cov", "setuptools", "tox", "twine", "wheel"]
-
[[package]]
name = "pytz"
version = "2025.2"
@@ -1672,62 +1613,62 @@ docs = ["markdown-include (>=0.8.1)", "mike (>=2.1.3)", "mkdocs-github-admonitio
[[package]]
name = "robotcode"
-version = "2.0.1"
+version = "2.0.4"
description = "Command line interface for RobotCode"
optional = false
python-versions = ">=3.10"
groups = ["dev", "type-checking"]
files = [
- {file = "robotcode-2.0.1-py3-none-any.whl", hash = "sha256:4259fc1a3b261c7d01194c019fd7c9cd800c93a0d18a5d3b45d5bd21293ac913"},
- {file = "robotcode-2.0.1.tar.gz", hash = "sha256:2fe0509d91e2f7e351c24bc83cb493ef443ca12c32b0b0a1499309015dd544d1"},
+ {file = "robotcode-2.0.4-py3-none-any.whl", hash = "sha256:bed5fcceb69cf2b65547cfc2cf9a2e4493992b448e3ef5da0b301f09c594b3ff"},
+ {file = "robotcode-2.0.4.tar.gz", hash = "sha256:d58d602c02899a65de6dc4fff393a2b667b1c7c04b7c0fccb526b4fb1ed586ed"},
]
[package.dependencies]
-robotcode-core = "2.0.1"
-robotcode-plugin = "2.0.1"
-robotcode-robot = "2.0.1"
+robotcode-core = "2.0.4"
+robotcode-plugin = "2.0.4"
+robotcode-robot = "2.0.4"
[package.extras]
-all = ["docutils", "pyyaml (>=5.4)", "rich", "robotcode-analyze (==2.0.1)", "robotcode-debugger (==2.0.1)", "robotcode-language-server (==2.0.1)", "robotcode-repl (==2.0.1)", "robotcode-repl-server (==2.0.1)", "robotcode-runner (==2.0.1)", "robotframework-robocop (>=2.0.0)"]
-analyze = ["robotcode-analyze (==2.0.1)"]
+all = ["docutils", "pyyaml (>=5.4)", "rich", "robotcode-analyze (==2.0.4)", "robotcode-debugger (==2.0.4)", "robotcode-language-server (==2.0.4)", "robotcode-repl (==2.0.4)", "robotcode-repl-server (==2.0.4)", "robotcode-runner (==2.0.4)", "robotframework-robocop (>=2.0.0)"]
+analyze = ["robotcode-analyze (==2.0.4)"]
colored = ["rich"]
-debugger = ["robotcode-debugger (==2.0.1)"]
-languageserver = ["robotcode-language-server (==2.0.1)"]
+debugger = ["robotcode-debugger (==2.0.4)"]
+languageserver = ["robotcode-language-server (==2.0.4)"]
lint = ["robotframework-robocop (>=2.0.0)"]
-repl = ["robotcode-repl (==2.0.1)"]
-replserver = ["robotcode-repl-server (==2.0.1)"]
+repl = ["robotcode-repl (==2.0.4)"]
+replserver = ["robotcode-repl-server (==2.0.4)"]
rest = ["docutils"]
-runner = ["robotcode-runner (==2.0.1)"]
+runner = ["robotcode-runner (==2.0.4)"]
yaml = ["pyyaml (>=5.4)"]
[[package]]
name = "robotcode-analyze"
-version = "2.0.1"
+version = "2.0.4"
description = "RobotCode analyze plugin for Robot Framework"
optional = false
python-versions = ">=3.10"
groups = ["type-checking"]
files = [
- {file = "robotcode_analyze-2.0.1-py3-none-any.whl", hash = "sha256:b6589fb93b90d82b8506301833157bd243bded858cf2d890b78387bc9ca9d5bf"},
- {file = "robotcode_analyze-2.0.1.tar.gz", hash = "sha256:4e57805e8ee79f8fb5c210c15f39e817ed109200e53d26c9fa81dd9474a87eab"},
+ {file = "robotcode_analyze-2.0.4-py3-none-any.whl", hash = "sha256:75660bbe9d8c3c83968e92f8492aaf2011eaac65b6f915f173f1e08564afae79"},
+ {file = "robotcode_analyze-2.0.4.tar.gz", hash = "sha256:c7d76f544b4e27cb970f2cec030c375e1765627db91ff1b26c6bf45a360b6534"},
]
[package.dependencies]
-robotcode = "2.0.1"
-robotcode-plugin = "2.0.1"
-robotcode-robot = "2.0.1"
+robotcode = "2.0.4"
+robotcode-plugin = "2.0.4"
+robotcode-robot = "2.0.4"
robotframework = ">=4.1.0"
[[package]]
name = "robotcode-core"
-version = "2.0.1"
+version = "2.0.4"
description = "Some core classes for RobotCode"
optional = false
python-versions = ">=3.10"
groups = ["dev", "type-checking"]
files = [
- {file = "robotcode_core-2.0.1-py3-none-any.whl", hash = "sha256:0f77be39d42ad4e331e5b7e19809fc631fb046b1424426d3f582ac2a83eb127a"},
- {file = "robotcode_core-2.0.1.tar.gz", hash = "sha256:0e5240064f057ff9e64641e896ed16c23c134ecdf9b5116a4c574a5477e4e679"},
+ {file = "robotcode_core-2.0.4-py3-none-any.whl", hash = "sha256:2100975759c6bc385643fc3ee578740f735619a92febd318f07a2910833a6a67"},
+ {file = "robotcode_core-2.0.4.tar.gz", hash = "sha256:99e17b824dd1d37c8e2c013b5ed8920af8aaa83fdd7a8938326db952a669683a"},
]
[package.dependencies]
@@ -1735,14 +1676,14 @@ typing-extensions = ">=4.4.0"
[[package]]
name = "robotcode-modifiers"
-version = "2.0.1"
+version = "2.0.4"
description = "Some Robot Framework Modifiers for RobotCode"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
- {file = "robotcode_modifiers-2.0.1-py3-none-any.whl", hash = "sha256:827dc9c1ed4f63b6ebce6be67a105d2030cc18266dd4cf8de14f5411ea968678"},
- {file = "robotcode_modifiers-2.0.1.tar.gz", hash = "sha256:ce6159925d86360a1f205d4a45a57c7facf7fd3b3c95a1a966805a66b5dd7fab"},
+ {file = "robotcode_modifiers-2.0.4-py3-none-any.whl", hash = "sha256:9abbf02d7da5e01bfd0b9f440b5a005366b9f4079001467b066b1797da6bac7c"},
+ {file = "robotcode_modifiers-2.0.4.tar.gz", hash = "sha256:36dec0d624eb8366f2db9b52de337f5647ae0b0b821dc6d0e8da74999be5ea6c"},
]
[package.dependencies]
@@ -1750,14 +1691,14 @@ robotframework = ">=4.1.0"
[[package]]
name = "robotcode-plugin"
-version = "2.0.1"
+version = "2.0.4"
description = "Some classes for RobotCode plugin management"
optional = false
python-versions = ">=3.10"
groups = ["dev", "type-checking"]
files = [
- {file = "robotcode_plugin-2.0.1-py3-none-any.whl", hash = "sha256:85e239edfd8c4b6e28af2ee9139958eaef9f09e0aef395f2ed2bd2d5593db4f6"},
- {file = "robotcode_plugin-2.0.1.tar.gz", hash = "sha256:ceb89b663f8e017b1b20bf770113ab51074cf06516187f695e9917e8a84279b4"},
+ {file = "robotcode_plugin-2.0.4-py3-none-any.whl", hash = "sha256:a3cf6b73741a03feeb1e570010ba07519b4ecda05e3a65213388e8110c5b875a"},
+ {file = "robotcode_plugin-2.0.4.tar.gz", hash = "sha256:d58661344a0f4b547e93d611945032b01ae8a53f2e8b5db2d02d27aeaa3f09aa"},
]
[package.dependencies]
@@ -1768,39 +1709,39 @@ tomli-w = ">=1.0.0"
[[package]]
name = "robotcode-robot"
-version = "2.0.1"
+version = "2.0.4"
description = "Support classes for RobotCode for handling Robot Framework projects."
optional = false
python-versions = ">=3.10"
groups = ["dev", "type-checking"]
files = [
- {file = "robotcode_robot-2.0.1-py3-none-any.whl", hash = "sha256:628cafed3525a28928f95ae7f4a79dedd3983b042b443a8e0d10380cc8617b85"},
- {file = "robotcode_robot-2.0.1.tar.gz", hash = "sha256:bb7bba064bd4dbda240975fb9093388926daef5cc7faaa14ed366f89fc38f543"},
+ {file = "robotcode_robot-2.0.4-py3-none-any.whl", hash = "sha256:6df93aec89bd412e3c675044d0eec5d969c4c2392058dc41b170831296b229ac"},
+ {file = "robotcode_robot-2.0.4.tar.gz", hash = "sha256:6fd4a5dd41ca6133dadb71bdb0e94952203dca9d7f7055a3c8af99db68ef5dce"},
]
[package.dependencies]
platformdirs = ">=4.3"
-robotcode-core = "2.0.1"
+robotcode-core = "2.0.4"
robotframework = ">=4.1.0"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
[[package]]
name = "robotcode-runner"
-version = "2.0.1"
+version = "2.0.4"
description = "RobotCode runner for Robot Framework"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
- {file = "robotcode_runner-2.0.1-py3-none-any.whl", hash = "sha256:7df0b67fa647fa75bea651c7caf1be6128d54836e5f7cc12d3117f77bd10d644"},
- {file = "robotcode_runner-2.0.1.tar.gz", hash = "sha256:aa350a2b0fe19d32c6efde223e4967d206f63ba085b6c444e914c7855dd15379"},
+ {file = "robotcode_runner-2.0.4-py3-none-any.whl", hash = "sha256:e835aad189c5702b1a2d061b96bb4e4e0acee9056ca453e8ced2572a9da58978"},
+ {file = "robotcode_runner-2.0.4.tar.gz", hash = "sha256:93c846f7524e0e00f72a38fcaa2ed7ad0c6d5d3b92a62ca0338d58866cd8cc15"},
]
[package.dependencies]
-robotcode = "2.0.1"
-robotcode-modifiers = "2.0.1"
-robotcode-plugin = "2.0.1"
-robotcode-robot = "2.0.1"
+robotcode = "2.0.4"
+robotcode-modifiers = "2.0.4"
+robotcode-plugin = "2.0.4"
+robotcode-robot = "2.0.4"
robotframework = ">=4.1.0"
[[package]]
@@ -1837,14 +1778,14 @@ xls = ["openpyxl", "pandas", "xlrd (>=1.2.0)"]
[[package]]
name = "robotframework-robocop"
-version = "6.9.2"
+version = "6.12.0"
description = "Static code analysis tool (linter) and code formatter for Robot Framework"
optional = false
python-versions = ">=3.9"
groups = ["lint-and-format"]
files = [
- {file = "robotframework_robocop-6.9.2-py3-none-any.whl", hash = "sha256:1b6111c614cce67af33998aa35cac60ccc8a1e495b0be44b6b8892a7cdcc7cf9"},
- {file = "robotframework_robocop-6.9.2.tar.gz", hash = "sha256:461b1ae8ad9a43ae1a29ba343ec9b626c65cd8615938e94b76c3f32c0eee39f6"},
+ {file = "robotframework_robocop-6.12.0-py3-none-any.whl", hash = "sha256:ee1146ff4fccf3bd01f98a7965947ec878fa6bb794dec137e49948726eb116e0"},
+ {file = "robotframework_robocop-6.12.0.tar.gz", hash = "sha256:b49a9677f5da514c40bb334a2cc97badab09754e5c09ee31df1e9b8c5d595852"},
]
[package.dependencies]
@@ -1875,127 +1816,127 @@ robotframework = ">=3.2"
[[package]]
name = "rpds-py"
-version = "0.28.0"
+version = "0.29.0"
description = "Python bindings to Rust's persistent data structures (rpds)"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "rpds_py-0.28.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7b6013db815417eeb56b2d9d7324e64fcd4fa289caeee6e7a78b2e11fc9b438a"},
- {file = "rpds_py-0.28.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a4c6b05c685c0c03f80dabaeb73e74218c49deea965ca63f76a752807397207"},
- {file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4794c6c3fbe8f9ac87699b131a1f26e7b4abcf6d828da46a3a52648c7930eba"},
- {file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2e8456b6ee5527112ff2354dd9087b030e3429e43a74f480d4a5ca79d269fd85"},
- {file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:beb880a9ca0a117415f241f66d56025c02037f7c4efc6fe59b5b8454f1eaa50d"},
- {file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6897bebb118c44b38c9cb62a178e09f1593c949391b9a1a6fe777ccab5934ee7"},
- {file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1b553dd06e875249fd43efd727785efb57a53180e0fde321468222eabbeaafa"},
- {file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:f0b2044fdddeea5b05df832e50d2a06fe61023acb44d76978e1b060206a8a476"},
- {file = "rpds_py-0.28.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05cf1e74900e8da73fa08cc76c74a03345e5a3e37691d07cfe2092d7d8e27b04"},
- {file = "rpds_py-0.28.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:efd489fec7c311dae25e94fe7eeda4b3d06be71c68f2cf2e8ef990ffcd2cd7e8"},
- {file = "rpds_py-0.28.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ada7754a10faacd4f26067e62de52d6af93b6d9542f0df73c57b9771eb3ba9c4"},
- {file = "rpds_py-0.28.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c2a34fd26588949e1e7977cfcbb17a9a42c948c100cab890c6d8d823f0586457"},
- {file = "rpds_py-0.28.0-cp310-cp310-win32.whl", hash = "sha256:f9174471d6920cbc5e82a7822de8dfd4dcea86eb828b04fc8c6519a77b0ee51e"},
- {file = "rpds_py-0.28.0-cp310-cp310-win_amd64.whl", hash = "sha256:6e32dd207e2c4f8475257a3540ab8a93eff997abfa0a3fdb287cae0d6cd874b8"},
- {file = "rpds_py-0.28.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:03065002fd2e287725d95fbc69688e0c6daf6c6314ba38bdbaa3895418e09296"},
- {file = "rpds_py-0.28.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28ea02215f262b6d078daec0b45344c89e161eab9526b0d898221d96fdda5f27"},
- {file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25dbade8fbf30bcc551cb352376c0ad64b067e4fc56f90e22ba70c3ce205988c"},
- {file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c03002f54cc855860bfdc3442928ffdca9081e73b5b382ed0b9e8efe6e5e205"},
- {file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9699fa7990368b22032baf2b2dce1f634388e4ffc03dfefaaac79f4695edc95"},
- {file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9b06fe1a75e05e0713f06ea0c89ecb6452210fd60e2f1b6ddc1067b990e08d9"},
- {file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9f83e7b326a3f9ec3ef84cda98fb0a74c7159f33e692032233046e7fd15da2"},
- {file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:0d3259ea9ad8743a75a43eb7819324cdab393263c91be86e2d1901ee65c314e0"},
- {file = "rpds_py-0.28.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a7548b345f66f6695943b4ef6afe33ccd3f1b638bd9afd0f730dd255c249c9e"},
- {file = "rpds_py-0.28.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9a40040aa388b037eb39416710fbcce9443498d2eaab0b9b45ae988b53f5c67"},
- {file = "rpds_py-0.28.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8f60c7ea34e78c199acd0d3cda37a99be2c861dd2b8cf67399784f70c9f8e57d"},
- {file = "rpds_py-0.28.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1571ae4292649100d743b26d5f9c63503bb1fedf538a8f29a98dce2d5ba6b4e6"},
- {file = "rpds_py-0.28.0-cp311-cp311-win32.whl", hash = "sha256:5cfa9af45e7c1140af7321fa0bef25b386ee9faa8928c80dc3a5360971a29e8c"},
- {file = "rpds_py-0.28.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd8d86b5d29d1b74100982424ba53e56033dc47720a6de9ba0259cf81d7cecaa"},
- {file = "rpds_py-0.28.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e27d3a5709cc2b3e013bf93679a849213c79ae0573f9b894b284b55e729e120"},
- {file = "rpds_py-0.28.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6b4f28583a4f247ff60cd7bdda83db8c3f5b05a7a82ff20dd4b078571747708f"},
- {file = "rpds_py-0.28.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d678e91b610c29c4b3d52a2c148b641df2b4676ffe47c59f6388d58b99cdc424"},
- {file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e819e0e37a44a78e1383bf1970076e2ccc4dc8c2bbaa2f9bd1dc987e9afff628"},
- {file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5ee514e0f0523db5d3fb171f397c54875dbbd69760a414dccf9d4d7ad628b5bd"},
- {file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3fa06d27fdcee47f07a39e02862da0100cb4982508f5ead53ec533cd5fe55e"},
- {file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:46959ef2e64f9e4a41fc89aa20dbca2b85531f9a72c21099a3360f35d10b0d5a"},
- {file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8455933b4bcd6e83fde3fefc987a023389c4b13f9a58c8d23e4b3f6d13f78c84"},
- {file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:ad50614a02c8c2962feebe6012b52f9802deec4263946cddea37aaf28dd25a66"},
- {file = "rpds_py-0.28.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e5deca01b271492553fdb6c7fd974659dce736a15bae5dad7ab8b93555bceb28"},
- {file = "rpds_py-0.28.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:735f8495a13159ce6a0d533f01e8674cec0c57038c920495f87dcb20b3ddb48a"},
- {file = "rpds_py-0.28.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:961ca621ff10d198bbe6ba4957decca61aa2a0c56695384c1d6b79bf61436df5"},
- {file = "rpds_py-0.28.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2374e16cc9131022e7d9a8f8d65d261d9ba55048c78f3b6e017971a4f5e6353c"},
- {file = "rpds_py-0.28.0-cp312-cp312-win32.whl", hash = "sha256:d15431e334fba488b081d47f30f091e5d03c18527c325386091f31718952fe08"},
- {file = "rpds_py-0.28.0-cp312-cp312-win_amd64.whl", hash = "sha256:a410542d61fc54710f750d3764380b53bf09e8c4edbf2f9141a82aa774a04f7c"},
- {file = "rpds_py-0.28.0-cp312-cp312-win_arm64.whl", hash = "sha256:1f0cfd1c69e2d14f8c892b893997fa9a60d890a0c8a603e88dca4955f26d1edd"},
- {file = "rpds_py-0.28.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e9e184408a0297086f880556b6168fa927d677716f83d3472ea333b42171ee3b"},
- {file = "rpds_py-0.28.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:edd267266a9b0448f33dc465a97cfc5d467594b600fe28e7fa2f36450e03053a"},
- {file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85beb8b3f45e4e32f6802fb6cd6b17f615ef6c6a52f265371fb916fae02814aa"},
- {file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d2412be8d00a1b895f8ad827cc2116455196e20ed994bb704bf138fe91a42724"},
- {file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cf128350d384b777da0e68796afdcebc2e9f63f0e9f242217754e647f6d32491"},
- {file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a2036d09b363aa36695d1cc1a97b36865597f4478470b0697b5ee9403f4fe399"},
- {file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8e1e9be4fa6305a16be628959188e4fd5cd6f1b0e724d63c6d8b2a8adf74ea6"},
- {file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0a403460c9dd91a7f23fc3188de6d8977f1d9603a351d5db6cf20aaea95b538d"},
- {file = "rpds_py-0.28.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d7366b6553cdc805abcc512b849a519167db8f5e5c3472010cd1228b224265cb"},
- {file = "rpds_py-0.28.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b43c6a3726efd50f18d8120ec0551241c38785b68952d240c45ea553912ac41"},
- {file = "rpds_py-0.28.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0cb7203c7bc69d7c1585ebb33a2e6074492d2fc21ad28a7b9d40457ac2a51ab7"},
- {file = "rpds_py-0.28.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a52a5169c664dfb495882adc75c304ae1d50df552fbd68e100fdc719dee4ff9"},
- {file = "rpds_py-0.28.0-cp313-cp313-win32.whl", hash = "sha256:2e42456917b6687215b3e606ab46aa6bca040c77af7df9a08a6dcfe8a4d10ca5"},
- {file = "rpds_py-0.28.0-cp313-cp313-win_amd64.whl", hash = "sha256:e0a0311caedc8069d68fc2bf4c9019b58a2d5ce3cd7cb656c845f1615b577e1e"},
- {file = "rpds_py-0.28.0-cp313-cp313-win_arm64.whl", hash = "sha256:04c1b207ab8b581108801528d59ad80aa83bb170b35b0ddffb29c20e411acdc1"},
- {file = "rpds_py-0.28.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f296ea3054e11fc58ad42e850e8b75c62d9a93a9f981ad04b2e5ae7d2186ff9c"},
- {file = "rpds_py-0.28.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5a7306c19b19005ad98468fcefeb7100b19c79fc23a5f24a12e06d91181193fa"},
- {file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5d9b86aa501fed9862a443c5c3116f6ead8bc9296185f369277c42542bd646b"},
- {file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e5bbc701eff140ba0e872691d573b3d5d30059ea26e5785acba9132d10c8c31d"},
- {file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a5690671cd672a45aa8616d7374fdf334a1b9c04a0cac3c854b1136e92374fe"},
- {file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9f1d92ecea4fa12f978a367c32a5375a1982834649cdb96539dcdc12e609ab1a"},
- {file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d252db6b1a78d0a3928b6190156042d54c93660ce4d98290d7b16b5296fb7cc"},
- {file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d61b355c3275acb825f8777d6c4505f42b5007e357af500939d4a35b19177259"},
- {file = "rpds_py-0.28.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:acbe5e8b1026c0c580d0321c8aae4b0a1e1676861d48d6e8c6586625055b606a"},
- {file = "rpds_py-0.28.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8aa23b6f0fc59b85b4c7d89ba2965af274346f738e8d9fc2455763602e62fd5f"},
- {file = "rpds_py-0.28.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7b14b0c680286958817c22d76fcbca4800ddacef6f678f3a7c79a1fe7067fe37"},
- {file = "rpds_py-0.28.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bcf1d210dfee61a6c86551d67ee1031899c0fdbae88b2d44a569995d43797712"},
- {file = "rpds_py-0.28.0-cp313-cp313t-win32.whl", hash = "sha256:3aa4dc0fdab4a7029ac63959a3ccf4ed605fee048ba67ce89ca3168da34a1342"},
- {file = "rpds_py-0.28.0-cp313-cp313t-win_amd64.whl", hash = "sha256:7b7d9d83c942855e4fdcfa75d4f96f6b9e272d42fffcb72cd4bb2577db2e2907"},
- {file = "rpds_py-0.28.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:dcdcb890b3ada98a03f9f2bb108489cdc7580176cb73b4f2d789e9a1dac1d472"},
- {file = "rpds_py-0.28.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f274f56a926ba2dc02976ca5b11c32855cbd5925534e57cfe1fda64e04d1add2"},
- {file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fe0438ac4a29a520ea94c8c7f1754cdd8feb1bc490dfda1bfd990072363d527"},
- {file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a358a32dd3ae50e933347889b6af9a1bdf207ba5d1a3f34e1a38cd3540e6733"},
- {file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e80848a71c78aa328fefaba9c244d588a342c8e03bda518447b624ea64d1ff56"},
- {file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f586db2e209d54fe177e58e0bc4946bea5fb0102f150b1b2f13de03e1f0976f8"},
- {file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ae8ee156d6b586e4292491e885d41483136ab994e719a13458055bec14cf370"},
- {file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:a805e9b3973f7e27f7cab63a6b4f61d90f2e5557cff73b6e97cd5b8540276d3d"},
- {file = "rpds_py-0.28.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5d3fd16b6dc89c73a4da0b4ac8b12a7ecc75b2864b95c9e5afed8003cb50a728"},
- {file = "rpds_py-0.28.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6796079e5d24fdaba6d49bda28e2c47347e89834678f2bc2c1b4fc1489c0fb01"},
- {file = "rpds_py-0.28.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:76500820c2af232435cbe215e3324c75b950a027134e044423f59f5b9a1ba515"},
- {file = "rpds_py-0.28.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bbdc5640900a7dbf9dd707fe6388972f5bbd883633eb68b76591044cfe346f7e"},
- {file = "rpds_py-0.28.0-cp314-cp314-win32.whl", hash = "sha256:adc8aa88486857d2b35d75f0640b949759f79dc105f50aa2c27816b2e0dd749f"},
- {file = "rpds_py-0.28.0-cp314-cp314-win_amd64.whl", hash = "sha256:66e6fa8e075b58946e76a78e69e1a124a21d9a48a5b4766d15ba5b06869d1fa1"},
- {file = "rpds_py-0.28.0-cp314-cp314-win_arm64.whl", hash = "sha256:a6fe887c2c5c59413353b7c0caff25d0e566623501ccfff88957fa438a69377d"},
- {file = "rpds_py-0.28.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7a69df082db13c7070f7b8b1f155fa9e687f1d6aefb7b0e3f7231653b79a067b"},
- {file = "rpds_py-0.28.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b1cde22f2c30ebb049a9e74c5374994157b9b70a16147d332f89c99c5960737a"},
- {file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5338742f6ba7a51012ea470bd4dc600a8c713c0c72adaa0977a1b1f4327d6592"},
- {file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e1460ebde1bcf6d496d80b191d854adedcc619f84ff17dc1c6d550f58c9efbba"},
- {file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e3eb248f2feba84c692579257a043a7699e28a77d86c77b032c1d9fbb3f0219c"},
- {file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3bbba5def70b16cd1c1d7255666aad3b290fbf8d0fe7f9f91abafb73611a91"},
- {file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3114f4db69ac5a1f32e7e4d1cbbe7c8f9cf8217f78e6e002cedf2d54c2a548ed"},
- {file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:4b0cb8a906b1a0196b863d460c0222fb8ad0f34041568da5620f9799b83ccf0b"},
- {file = "rpds_py-0.28.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf681ac76a60b667106141e11a92a3330890257e6f559ca995fbb5265160b56e"},
- {file = "rpds_py-0.28.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1e8ee6413cfc677ce8898d9cde18cc3a60fc2ba756b0dec5b71eb6eb21c49fa1"},
- {file = "rpds_py-0.28.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b3072b16904d0b5572a15eb9d31c1954e0d3227a585fc1351aa9878729099d6c"},
- {file = "rpds_py-0.28.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b670c30fd87a6aec281c3c9896d3bae4b205fd75d79d06dc87c2503717e46092"},
- {file = "rpds_py-0.28.0-cp314-cp314t-win32.whl", hash = "sha256:8014045a15b4d2b3476f0a287fcc93d4f823472d7d1308d47884ecac9e612be3"},
- {file = "rpds_py-0.28.0-cp314-cp314t-win_amd64.whl", hash = "sha256:7a4e59c90d9c27c561eb3160323634a9ff50b04e4f7820600a2beb0ac90db578"},
- {file = "rpds_py-0.28.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f5e7101145427087e493b9c9b959da68d357c28c562792300dd21a095118ed16"},
- {file = "rpds_py-0.28.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:31eb671150b9c62409a888850aaa8e6533635704fe2b78335f9aaf7ff81eec4d"},
- {file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48b55c1f64482f7d8bd39942f376bfdf2f6aec637ee8c805b5041e14eeb771db"},
- {file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:24743a7b372e9a76171f6b69c01aedf927e8ac3e16c474d9fe20d552a8cb45c7"},
- {file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:389c29045ee8bbb1627ea190b4976a310a295559eaf9f1464a1a6f2bf84dde78"},
- {file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23690b5827e643150cf7b49569679ec13fe9a610a15949ed48b85eb7f98f34ec"},
- {file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f0c9266c26580e7243ad0d72fc3e01d6b33866cfab5084a6da7576bcf1c4f72"},
- {file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4c6c4db5d73d179746951486df97fd25e92396be07fc29ee8ff9a8f5afbdfb27"},
- {file = "rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3b695a8fa799dd2cfdb4804b37096c5f6dba1ac7f48a7fbf6d0485bcd060316"},
- {file = "rpds_py-0.28.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:6aa1bfce3f83baf00d9c5fcdbba93a3ab79958b4c7d7d1f55e7fe68c20e63912"},
- {file = "rpds_py-0.28.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:7b0f9dceb221792b3ee6acb5438eb1f02b0cb2c247796a72b016dcc92c6de829"},
- {file = "rpds_py-0.28.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5d0145edba8abd3db0ab22b5300c99dc152f5c9021fab861be0f0544dc3cbc5f"},
- {file = "rpds_py-0.28.0.tar.gz", hash = "sha256:abd4df20485a0983e2ca334a216249b6186d6e3c1627e106651943dbdb791aea"},
+ {file = "rpds_py-0.29.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4ae4b88c6617e1b9e5038ab3fccd7bac0842fdda2b703117b2aa99bc85379113"},
+ {file = "rpds_py-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7d9128ec9d8cecda6f044001fde4fb71ea7c24325336612ef8179091eb9596b9"},
+ {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37812c3da8e06f2bb35b3cf10e4a7b68e776a706c13058997238762b4e07f4f"},
+ {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66786c3fb1d8de416a7fa8e1cb1ec6ba0a745b2b0eee42f9b7daa26f1a495545"},
+ {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58f5c77f1af888b5fd1876c9a0d9858f6f88a39c9dd7c073a88e57e577da66d"},
+ {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:799156ef1f3529ed82c36eb012b5d7a4cf4b6ef556dd7cc192148991d07206ae"},
+ {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:453783477aa4f2d9104c4b59b08c871431647cb7af51b549bbf2d9eb9c827756"},
+ {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:24a7231493e3c4a4b30138b50cca089a598e52c34cf60b2f35cebf62f274fdea"},
+ {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7033c1010b1f57bb44d8067e8c25aa6fa2e944dbf46ccc8c92b25043839c3fd2"},
+ {file = "rpds_py-0.29.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0248b19405422573621172ab8e3a1f29141362d13d9f72bafa2e28ea0cdca5a2"},
+ {file = "rpds_py-0.29.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f9f436aee28d13b9ad2c764fc273e0457e37c2e61529a07b928346b219fcde3b"},
+ {file = "rpds_py-0.29.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24a16cb7163933906c62c272de20ea3c228e4542c8c45c1d7dc2b9913e17369a"},
+ {file = "rpds_py-0.29.0-cp310-cp310-win32.whl", hash = "sha256:1a409b0310a566bfd1be82119891fefbdce615ccc8aa558aff7835c27988cbef"},
+ {file = "rpds_py-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5523b0009e7c3c1263471b69d8da1c7d41b3ecb4cb62ef72be206b92040a950"},
+ {file = "rpds_py-0.29.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9b9c764a11fd637e0322a488560533112837f5334ffeb48b1be20f6d98a7b437"},
+ {file = "rpds_py-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fd2164d73812026ce970d44c3ebd51e019d2a26a4425a5dcbdfa93a34abc383"},
+ {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a097b7f7f7274164566ae90a221fd725363c0e9d243e2e9ed43d195ccc5495c"},
+ {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cdc0490374e31cedefefaa1520d5fe38e82fde8748cbc926e7284574c714d6b"},
+ {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89ca2e673ddd5bde9b386da9a0aac0cab0e76f40c8f0aaf0d6311b6bbf2aa311"},
+ {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5d9da3ff5af1ca1249b1adb8ef0573b94c76e6ae880ba1852f033bf429d4588"},
+ {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8238d1d310283e87376c12f658b61e1ee23a14c0e54c7c0ce953efdbdc72deed"},
+ {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2d6fb2ad1c36f91c4646989811e84b1ea5e0c3cf9690b826b6e32b7965853a63"},
+ {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:534dc9df211387547267ccdb42253aa30527482acb38dd9b21c5c115d66a96d2"},
+ {file = "rpds_py-0.29.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d456e64724a075441e4ed648d7f154dc62e9aabff29bcdf723d0c00e9e1d352f"},
+ {file = "rpds_py-0.29.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a738f2da2f565989401bd6fd0b15990a4d1523c6d7fe83f300b7e7d17212feca"},
+ {file = "rpds_py-0.29.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a110e14508fd26fd2e472bb541f37c209409876ba601cf57e739e87d8a53cf95"},
+ {file = "rpds_py-0.29.0-cp311-cp311-win32.whl", hash = "sha256:923248a56dd8d158389a28934f6f69ebf89f218ef96a6b216a9be6861804d3f4"},
+ {file = "rpds_py-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:539eb77eb043afcc45314d1be09ea6d6cafb3addc73e0547c171c6d636957f60"},
+ {file = "rpds_py-0.29.0-cp311-cp311-win_arm64.whl", hash = "sha256:bdb67151ea81fcf02d8f494703fb728d4d34d24556cbff5f417d74f6f5792e7c"},
+ {file = "rpds_py-0.29.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0891cfd8db43e085c0ab93ab7e9b0c8fee84780d436d3b266b113e51e79f954"},
+ {file = "rpds_py-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3897924d3f9a0361472d884051f9a2460358f9a45b1d85a39a158d2f8f1ad71c"},
+ {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21deb8e0d1571508c6491ce5ea5e25669b1dd4adf1c9d64b6314842f708b5d"},
+ {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9efe71687d6427737a0a2de9ca1c0a216510e6cd08925c44162be23ed7bed2d5"},
+ {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:40f65470919dc189c833e86b2c4bd21bd355f98436a2cef9e0a9a92aebc8e57e"},
+ {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:def48ff59f181130f1a2cb7c517d16328efac3ec03951cca40c1dc2049747e83"},
+ {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7bd570be92695d89285a4b373006930715b78d96449f686af422debb4d3949"},
+ {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:5a572911cd053137bbff8e3a52d31c5d2dba51d3a67ad902629c70185f3f2181"},
+ {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d583d4403bcbf10cffc3ab5cee23d7643fcc960dff85973fd3c2d6c86e8dbb0c"},
+ {file = "rpds_py-0.29.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:070befbb868f257d24c3bb350dbd6e2f645e83731f31264b19d7231dd5c396c7"},
+ {file = "rpds_py-0.29.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fc935f6b20b0c9f919a8ff024739174522abd331978f750a74bb68abd117bd19"},
+ {file = "rpds_py-0.29.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c5a8ecaa44ce2d8d9d20a68a2483a74c07f05d72e94a4dff88906c8807e77b0"},
+ {file = "rpds_py-0.29.0-cp312-cp312-win32.whl", hash = "sha256:ba5e1aeaf8dd6d8f6caba1f5539cddda87d511331714b7b5fc908b6cfc3636b7"},
+ {file = "rpds_py-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:b5f6134faf54b3cb83375db0f113506f8b7770785be1f95a631e7e2892101977"},
+ {file = "rpds_py-0.29.0-cp312-cp312-win_arm64.whl", hash = "sha256:b016eddf00dca7944721bf0cd85b6af7f6c4efaf83ee0b37c4133bd39757a8c7"},
+ {file = "rpds_py-0.29.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1585648d0760b88292eecab5181f5651111a69d90eff35d6b78aa32998886a61"},
+ {file = "rpds_py-0.29.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:521807963971a23996ddaf764c682b3e46459b3c58ccd79fefbe16718db43154"},
+ {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8896986efaa243ab713c69e6491a4138410f0fe36f2f4c71e18bd5501e8014"},
+ {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d24564a700ef41480a984c5ebed62b74e6ce5860429b98b1fede76049e953e6"},
+ {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6596b93c010d386ae46c9fba9bfc9fc5965fa8228edeac51576299182c2e31c"},
+ {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5cc58aac218826d054c7da7f95821eba94125d88be673ff44267bb89d12a5866"},
+ {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de73e40ebc04dd5d9556f50180395322193a78ec247e637e741c1b954810f295"},
+ {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:295ce5ac7f0cf69a651ea75c8f76d02a31f98e5698e82a50a5f4d4982fbbae3b"},
+ {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ea59b23ea931d494459c8338056fe7d93458c0bf3ecc061cd03916505369d55"},
+ {file = "rpds_py-0.29.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f49d41559cebd608042fdcf54ba597a4a7555b49ad5c1c0c03e0af82692661cd"},
+ {file = "rpds_py-0.29.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:05a2bd42768ea988294ca328206efbcc66e220d2d9b7836ee5712c07ad6340ea"},
+ {file = "rpds_py-0.29.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33ca7bdfedd83339ca55da3a5e1527ee5870d4b8369456b5777b197756f3ca22"},
+ {file = "rpds_py-0.29.0-cp313-cp313-win32.whl", hash = "sha256:20c51ae86a0bb9accc9ad4e6cdeec58d5ebb7f1b09dd4466331fc65e1766aae7"},
+ {file = "rpds_py-0.29.0-cp313-cp313-win_amd64.whl", hash = "sha256:6410e66f02803600edb0b1889541f4b5cc298a5ccda0ad789cc50ef23b54813e"},
+ {file = "rpds_py-0.29.0-cp313-cp313-win_arm64.whl", hash = "sha256:56838e1cd9174dc23c5691ee29f1d1be9eab357f27efef6bded1328b23e1ced2"},
+ {file = "rpds_py-0.29.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:37d94eadf764d16b9a04307f2ab1d7af6dc28774bbe0535c9323101e14877b4c"},
+ {file = "rpds_py-0.29.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d472cf73efe5726a067dce63eebe8215b14beabea7c12606fd9994267b3cfe2b"},
+ {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72fdfd5ff8992e4636621826371e3ac5f3e3b8323e9d0e48378e9c13c3dac9d0"},
+ {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2549d833abdf8275c901313b9e8ff8fba57e50f6a495035a2a4e30621a2f7cc4"},
+ {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4448dad428f28a6a767c3e3b80cde3446a22a0efbddaa2360f4bb4dc836d0688"},
+ {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:115f48170fd4296a33938d8c11f697f5f26e0472e43d28f35624764173a60e4d"},
+ {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e5bb73ffc029820f4348e9b66b3027493ae00bca6629129cd433fd7a76308ee"},
+ {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:b1581fcde18fcdf42ea2403a16a6b646f8eb1e58d7f90a0ce693da441f76942e"},
+ {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16e9da2bda9eb17ea318b4c335ec9ac1818e88922cbe03a5743ea0da9ecf74fb"},
+ {file = "rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:28fd300326dd21198f311534bdb6d7e989dd09b3418b3a91d54a0f384c700967"},
+ {file = "rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2aba991e041d031c7939e1358f583ae405a7bf04804ca806b97a5c0e0af1ea5e"},
+ {file = "rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f437026dbbc3f08c99cc41a5b2570c6e1a1ddbe48ab19a9b814254128d4ea7a"},
+ {file = "rpds_py-0.29.0-cp313-cp313t-win32.whl", hash = "sha256:6e97846e9800a5d0fe7be4d008f0c93d0feeb2700da7b1f7528dabafb31dfadb"},
+ {file = "rpds_py-0.29.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f49196aec7c4b406495f60e6f947ad71f317a765f956d74bbd83996b9edc0352"},
+ {file = "rpds_py-0.29.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:394d27e4453d3b4d82bb85665dc1fcf4b0badc30fc84282defed71643b50e1a1"},
+ {file = "rpds_py-0.29.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55d827b2ae95425d3be9bc9a5838b6c29d664924f98146557f7715e331d06df8"},
+ {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc31a07ed352e5462d3ee1b22e89285f4ce97d5266f6d1169da1142e78045626"},
+ {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c4695dd224212f6105db7ea62197144230b808d6b2bba52238906a2762f1d1e7"},
+ {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcae1770b401167f8b9e1e3f566562e6966ffa9ce63639916248a9e25fa8a244"},
+ {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:90f30d15f45048448b8da21c41703b31c61119c06c216a1bf8c245812a0f0c17"},
+ {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44a91e0ab77bdc0004b43261a4b8cd6d6b451e8d443754cfda830002b5745b32"},
+ {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:4aa195e5804d32c682e453b34474f411ca108e4291c6a0f824ebdc30a91c973c"},
+ {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7971bdb7bf4ee0f7e6f67fa4c7fbc6019d9850cc977d126904392d363f6f8318"},
+ {file = "rpds_py-0.29.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8ae33ad9ce580c7a47452c3b3f7d8a9095ef6208e0a0c7e4e2384f9fc5bf8212"},
+ {file = "rpds_py-0.29.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c661132ab2fb4eeede2ef69670fd60da5235209874d001a98f1542f31f2a8a94"},
+ {file = "rpds_py-0.29.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bb78b3a0d31ac1bde132c67015a809948db751cb4e92cdb3f0b242e430b6ed0d"},
+ {file = "rpds_py-0.29.0-cp314-cp314-win32.whl", hash = "sha256:f475f103488312e9bd4000bc890a95955a07b2d0b6e8884aef4be56132adbbf1"},
+ {file = "rpds_py-0.29.0-cp314-cp314-win_amd64.whl", hash = "sha256:b9cf2359a4fca87cfb6801fae83a76aedf66ee1254a7a151f1341632acf67f1b"},
+ {file = "rpds_py-0.29.0-cp314-cp314-win_arm64.whl", hash = "sha256:9ba8028597e824854f0f1733d8b964e914ae3003b22a10c2c664cb6927e0feb9"},
+ {file = "rpds_py-0.29.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:e71136fd0612556b35c575dc2726ae04a1669e6a6c378f2240312cf5d1a2ab10"},
+ {file = "rpds_py-0.29.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:76fe96632d53f3bf0ea31ede2f53bbe3540cc2736d4aec3b3801b0458499ef3a"},
+ {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9459a33f077130dbb2c7c3cea72ee9932271fb3126404ba2a2661e4fe9eb7b79"},
+ {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5c9546cfdd5d45e562cc0444b6dddc191e625c62e866bf567a2c69487c7ad28a"},
+ {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12597d11d97b8f7e376c88929a6e17acb980e234547c92992f9f7c058f1a7310"},
+ {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28de03cf48b8a9e6ec10318f2197b83946ed91e2891f651a109611be4106ac4b"},
+ {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd7951c964069039acc9d67a8ff1f0a7f34845ae180ca542b17dc1456b1f1808"},
+ {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:c07d107b7316088f1ac0177a7661ca0c6670d443f6fe72e836069025e6266761"},
+ {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1de2345af363d25696969befc0c1688a6cb5e8b1d32b515ef84fc245c6cddba3"},
+ {file = "rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:00e56b12d2199ca96068057e1ae7f9998ab6e99cda82431afafd32f3ec98cca9"},
+ {file = "rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3919a3bbecee589300ed25000b6944174e07cd20db70552159207b3f4bbb45b8"},
+ {file = "rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7fa2ccc312bbd91e43aa5e0869e46bc03278a3dddb8d58833150a18b0f0283a"},
+ {file = "rpds_py-0.29.0-cp314-cp314t-win32.whl", hash = "sha256:97c817863ffc397f1e6a6e9d2d89fe5408c0a9922dac0329672fb0f35c867ea5"},
+ {file = "rpds_py-0.29.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2023473f444752f0f82a58dfcbee040d0a1b3d1b3c2ec40e884bd25db6d117d2"},
+ {file = "rpds_py-0.29.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:acd82a9e39082dc5f4492d15a6b6c8599aa21db5c35aaf7d6889aea16502c07d"},
+ {file = "rpds_py-0.29.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:715b67eac317bf1c7657508170a3e011a1ea6ccb1c9d5f296e20ba14196be6b3"},
+ {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3b1b87a237cb2dba4db18bcfaaa44ba4cd5936b91121b62292ff21df577fc43"},
+ {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c3c3e8101bb06e337c88eb0c0ede3187131f19d97d43ea0e1c5407ea74c0cbf"},
+ {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8e54d6e61f3ecd3abe032065ce83ea63417a24f437e4a3d73d2f85ce7b7cfe"},
+ {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fbd4e9aebf110473a420dea85a238b254cf8a15acb04b22a5a6b5ce8925b760"},
+ {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80fdf53d36e6c72819993e35d1ebeeb8e8fc688d0c6c2b391b55e335b3afba5a"},
+ {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:ea7173df5d86f625f8dde6d5929629ad811ed8decda3b60ae603903839ac9ac0"},
+ {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:76054d540061eda273274f3d13a21a4abdde90e13eaefdc205db37c05230efce"},
+ {file = "rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:9f84c549746a5be3bc7415830747a3a0312573afc9f95785eb35228bb17742ec"},
+ {file = "rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:0ea962671af5cb9a260489e311fa22b2e97103e3f9f0caaea6f81390af96a9ed"},
+ {file = "rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:f7728653900035fb7b8d06e1e5900545d8088efc9d5d4545782da7df03ec803f"},
+ {file = "rpds_py-0.29.0.tar.gz", hash = "sha256:fe55fe686908f50154d1dc599232016e50c243b438c3b7432f24e2895b0e5359"},
]
[[package]]
@@ -2031,97 +1972,103 @@ jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"]
[[package]]
name = "ruamel-yaml-clib"
-version = "0.2.14"
+version = "0.2.15"
description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml"
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "platform_python_implementation == \"CPython\" and python_version < \"3.14\""
files = [
- {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f8b2acb0ffdd2ce8208accbec2dca4a06937d556fdcaefd6473ba1b5daa7e3c4"},
- {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:aef953f3b8bd0b50bd52a2e52fb54a6a2171a1889d8dea4a5959d46c6624c451"},
- {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a0ac90efbc7a77b0d796c03c8cc4e62fd710b3f1e4c32947713ef2ef52e09543"},
- {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9bf6b699223afe6c7fe9f2ef76e0bfa6dd892c21e94ce8c957478987ade76cd8"},
- {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d73a0187718f6eec5b2f729b0f98e4603f7bd9c48aa65d01227d1a5dcdfbe9e8"},
- {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81f6d3b19bc703679a5705c6a16dabdc79823c71d791d73c65949be7f3012c02"},
- {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b28caeaf3e670c08cb7e8de221266df8494c169bd6ed8875493fab45be9607a4"},
- {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94f3efb718f8f49b031f2071ec7a27dd20cbfe511b4dfd54ecee54c956da2b31"},
- {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-win32.whl", hash = "sha256:27c070cf3888e90d992be75dd47292ff9aa17dafd36492812a6a304a1aedc182"},
- {file = "ruamel.yaml.clib-0.2.14-cp310-cp310-win_amd64.whl", hash = "sha256:4f4a150a737fccae13fb51234d41304ff2222e3b7d4c8e9428ed1a6ab48389b8"},
- {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5bae1a073ca4244620425cd3d3aa9746bde590992b98ee8c7c8be8c597ca0d4e"},
- {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:0a54e5e40a7a691a426c2703b09b0d61a14294d25cfacc00631aa6f9c964df0d"},
- {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:10d9595b6a19778f3269399eff6bab642608e5966183abc2adbe558a42d4efc9"},
- {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dba72975485f2b87b786075e18a6e5d07dc2b4d8973beb2732b9b2816f1bad70"},
- {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29757bdb7c142f9595cc1b62ec49a3d1c83fab9cef92db52b0ccebaad4eafb98"},
- {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:557df28dbccf79b152fe2d1b935f6063d9cc431199ea2b0e84892f35c03bb0ee"},
- {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:26a8de280ab0d22b6e3ec745b4a5a07151a0f74aad92dd76ab9c8d8d7087720d"},
- {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e501c096aa3889133d674605ebd018471bc404a59cbc17da3c5924421c54d97c"},
- {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-win32.whl", hash = "sha256:915748cfc25b8cfd81b14d00f4bfdb2ab227a30d6d43459034533f4d1c207a2a"},
- {file = "ruamel.yaml.clib-0.2.14-cp311-cp311-win_amd64.whl", hash = "sha256:4ccba93c1e5a40af45b2f08e4591969fa4697eae951c708f3f83dcbf9f6c6bb1"},
- {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6aeadc170090ff1889f0d2c3057557f9cd71f975f17535c26a5d37af98f19c27"},
- {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5e56ac47260c0eed992789fa0b8efe43404a9adb608608631a948cee4fc2b052"},
- {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:a911aa73588d9a8b08d662b9484bc0567949529824a55d3885b77e8dd62a127a"},
- {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a05ba88adf3d7189a974b2de7a9d56731548d35dc0a822ec3dc669caa7019b29"},
- {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb04c5650de6668b853623eceadcdb1a9f2fee381f5d7b6bc842ee7c239eeec4"},
- {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:df3ec9959241d07bc261f4983d25a1205ff37703faf42b474f15d54d88b4f8c9"},
- {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fbc08c02e9b147a11dfcaa1ac8a83168b699863493e183f7c0c8b12850b7d259"},
- {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c099cafc1834d3c5dac305865d04235f7c21c167c8dd31ebc3d6bbc357e2f023"},
- {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-win32.whl", hash = "sha256:b5b0f7e294700b615a3bcf6d28b26e6da94e8eba63b079f4ec92e9ba6c0d6b54"},
- {file = "ruamel.yaml.clib-0.2.14-cp312-cp312-win_amd64.whl", hash = "sha256:a37f40a859b503304dd740686359fcf541d6fb3ff7fc10f539af7f7150917c68"},
- {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7e4f9da7e7549946e02a6122dcad00b7c1168513acb1f8a726b1aaf504a99d32"},
- {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:dd7546c851e59c06197a7c651335755e74aa383a835878ca86d2c650c07a2f85"},
- {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:1c1acc3a0209ea9042cc3cfc0790edd2eddd431a2ec3f8283d081e4d5018571e"},
- {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2070bf0ad1540d5c77a664de07ebcc45eebd1ddcab71a7a06f26936920692beb"},
- {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd8fe07f49c170e09d76773fb86ad9135e0beee44f36e1576a201b0676d3d1d"},
- {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ff86876889ea478b1381089e55cf9e345707b312beda4986f823e1d95e8c0f59"},
- {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1f118b707eece8cf84ecbc3e3ec94d9db879d85ed608f95870d39b2d2efa5dca"},
- {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b30110b29484adc597df6bd92a37b90e63a8c152ca8136aad100a02f8ba6d1b6"},
- {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-win32.whl", hash = "sha256:f4e97a1cf0b7a30af9e1d9dad10a5671157b9acee790d9e26996391f49b965a2"},
- {file = "ruamel.yaml.clib-0.2.14-cp313-cp313-win_amd64.whl", hash = "sha256:090782b5fb9d98df96509eecdbcaffd037d47389a89492320280d52f91330d78"},
- {file = "ruamel.yaml.clib-0.2.14-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:7df6f6e9d0e33c7b1d435defb185095386c469109de723d514142632a7b9d07f"},
- {file = "ruamel.yaml.clib-0.2.14-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:70eda7703b8126f5e52fcf276e6c0f40b0d314674f896fc58c47b0aef2b9ae83"},
- {file = "ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a0cb71ccc6ef9ce36eecb6272c81afdc2f565950cdcec33ae8e6cd8f7fc86f27"},
- {file = "ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e7cb9ad1d525d40f7d87b6df7c0ff916a66bc52cb61b66ac1b2a16d0c1b07640"},
- {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:18c041b28f3456ddef1f1951d4492dbebe0f8114157c1b3c981a4611c2020792"},
- {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:d8354515ab62f95a07deaf7f845886cc50e2f345ceab240a3d2d09a9f7d77853"},
- {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:275f938692013a3883edbd848edde6d9f26825d65c9a2eb1db8baa1adc96a05d"},
- {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16a60d69f4057ad9a92f3444e2367c08490daed6428291aa16cefb445c29b0e9"},
- {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ac5ff9425d8acb8f59ac5b96bcb7fd3d272dc92d96a7c730025928ffcc88a7a"},
- {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e1d1735d97fd8a48473af048739379975651fab186f8a25a9f683534e6904179"},
- {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:83bbd8354f6abb3fdfb922d1ed47ad8d1db3ea72b0523dac8d07cdacfe1c0fcf"},
- {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:808c7190a0fe7ae7014c42f73897cf8e9ef14ff3aa533450e51b1e72ec5239ad"},
- {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-win32.whl", hash = "sha256:6d5472f63a31b042aadf5ed28dd3ef0523da49ac17f0463e10fda9c4a2773352"},
- {file = "ruamel.yaml.clib-0.2.14-cp39-cp39-win_amd64.whl", hash = "sha256:8dd3c2cc49caa7a8d64b67146462aed6723a0495e44bf0aa0a2e94beaa8432f6"},
- {file = "ruamel.yaml.clib-0.2.14.tar.gz", hash = "sha256:803f5044b13602d58ea378576dd75aa759f52116a0232608e8fdada4da33752e"},
+ {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88eea8baf72f0ccf232c22124d122a7f26e8a24110a0273d9bcddcb0f7e1fa03"},
+ {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b6f7d74d094d1f3a4e157278da97752f16ee230080ae331fcc219056ca54f77"},
+ {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4be366220090d7c3424ac2b71c90d1044ea34fca8c0b88f250064fd06087e614"},
+ {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f66f600833af58bea694d5892453f2270695b92200280ee8c625ec5a477eed3"},
+ {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da3d6adadcf55a93c214d23941aef4abfd45652110aed6580e814152f385b862"},
+ {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e9fde97ecb7bb9c41261c2ce0da10323e9227555c674989f8d9eb7572fc2098d"},
+ {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:05c70f7f86be6f7bee53794d80050a28ae7e13e4a0087c1839dcdefd68eb36b6"},
+ {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f1d38cbe622039d111b69e9ca945e7e3efebb30ba998867908773183357f3ed"},
+ {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-win32.whl", hash = "sha256:fe239bdfdae2302e93bd6e8264bd9b71290218fff7084a9db250b55caaccf43f"},
+ {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-win_amd64.whl", hash = "sha256:468858e5cbde0198337e6a2a78eda8c3fb148bdf4c6498eaf4bc9ba3f8e780bd"},
+ {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c583229f336682b7212a43d2fa32c30e643d3076178fb9f7a6a14dde85a2d8bd"},
+ {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56ea19c157ed8c74b6be51b5fa1c3aff6e289a041575f0556f66e5fb848bb137"},
+ {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5fea0932358e18293407feb921d4f4457db837b67ec1837f87074667449f9401"},
+ {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef71831bd61fbdb7aa0399d5c4da06bea37107ab5c79ff884cc07f2450910262"},
+ {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:617d35dc765715fa86f8c3ccdae1e4229055832c452d4ec20856136acc75053f"},
+ {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b45498cc81a4724a2d42273d6cfc243c0547ad7c6b87b4f774cb7bcc131c98d"},
+ {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:def5663361f6771b18646620fca12968aae730132e104688766cf8a3b1d65922"},
+ {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:014181cdec565c8745b7cbc4de3bf2cc8ced05183d986e6d1200168e5bb59490"},
+ {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-win32.whl", hash = "sha256:d290eda8f6ada19e1771b54e5706b8f9807e6bb08e873900d5ba114ced13e02c"},
+ {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-win_amd64.whl", hash = "sha256:bdc06ad71173b915167702f55d0f3f027fc61abd975bd308a0968c02db4a4c3e"},
+ {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cb15a2e2a90c8475df45c0949793af1ff413acfb0a716b8b94e488ea95ce7cff"},
+ {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:64da03cbe93c1e91af133f5bec37fd24d0d4ba2418eaf970d7166b0a26a148a2"},
+ {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f6d3655e95a80325b84c4e14c080b2470fe4f33b6846f288379ce36154993fb1"},
+ {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71845d377c7a47afc6592aacfea738cc8a7e876d586dfba814501d8c53c1ba60"},
+ {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e5499db1ccbc7f4b41f0565e4f799d863ea720e01d3e99fa0b7b5fcd7802c9"},
+ {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4b293a37dc97e2b1e8a1aec62792d1e52027087c8eea4fc7b5abd2bdafdd6642"},
+ {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:512571ad41bba04eac7268fe33f7f4742210ca26a81fe0c75357fa682636c690"},
+ {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5e9f630c73a490b758bf14d859a39f375e6999aea5ddd2e2e9da89b9953486a"},
+ {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-win32.whl", hash = "sha256:f4421ab780c37210a07d138e56dd4b51f8642187cdfb433eb687fe8c11de0144"},
+ {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-win_amd64.whl", hash = "sha256:2b216904750889133d9222b7b873c199d48ecbb12912aca78970f84a5aa1a4bc"},
+ {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4dcec721fddbb62e60c2801ba08c87010bd6b700054a09998c4d09c08147b8fb"},
+ {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:65f48245279f9bb301d1276f9679b82e4c080a1ae25e679f682ac62446fac471"},
+ {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:46895c17ead5e22bea5e576f1db7e41cb273e8d062c04a6a49013d9f60996c25"},
+ {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3eb199178b08956e5be6288ee0b05b2fb0b5c1f309725ad25d9c6ea7e27f962a"},
+ {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d1032919280ebc04a80e4fb1e93f7a738129857eaec9448310e638c8bccefcf"},
+ {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab0df0648d86a7ecbd9c632e8f8d6b21bb21b5fc9d9e095c796cacf32a728d2d"},
+ {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:331fb180858dd8534f0e61aa243b944f25e73a4dae9962bd44c46d1761126bbf"},
+ {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fd4c928ddf6bce586285daa6d90680b9c291cfd045fc40aad34e445d57b1bf51"},
+ {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-win32.whl", hash = "sha256:bf0846d629e160223805db9fe8cc7aec16aaa11a07310c50c8c7164efa440aec"},
+ {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-win_amd64.whl", hash = "sha256:45702dfbea1420ba3450bb3dd9a80b33f0badd57539c6aac09f42584303e0db6"},
+ {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:753faf20b3a5906faf1fc50e4ddb8c074cb9b251e00b14c18b28492f933ac8ef"},
+ {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:480894aee0b29752560a9de46c0e5f84a82602f2bc5c6cde8db9a345319acfdf"},
+ {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d3b58ab2454b4747442ac76fab66739c72b1e2bb9bd173d7694b9f9dbc9c000"},
+ {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bfd309b316228acecfa30670c3887dcedf9b7a44ea39e2101e75d2654522acd4"},
+ {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2812ff359ec1f30129b62372e5f22a52936fac13d5d21e70373dbca5d64bb97c"},
+ {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7e74ea87307303ba91073b63e67f2c667e93f05a8c63079ee5b7a5c8d0d7b043"},
+ {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:713cd68af9dfbe0bb588e144a61aad8dcc00ef92a82d2e87183ca662d242f524"},
+ {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:542d77b72786a35563f97069b9379ce762944e67055bea293480f7734b2c7e5e"},
+ {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-win32.whl", hash = "sha256:424ead8cef3939d690c4b5c85ef5b52155a231ff8b252961b6516ed7cf05f6aa"},
+ {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-win_amd64.whl", hash = "sha256:ac9b8d5fa4bb7fd2917ab5027f60d4234345fd366fe39aa711d5dca090aa1467"},
+ {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:923816815974425fbb1f1bf57e85eca6e14d8adc313c66db21c094927ad01815"},
+ {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dcc7f3162d3711fd5d52e2267e44636e3e566d1e5675a5f0b30e98f2c4af7974"},
+ {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5d3c9210219cbc0f22706f19b154c9a798ff65a6beeafbf77fc9c057ec806f7d"},
+ {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bb7b728fd9f405aa00b4a0b17ba3f3b810d0ccc5f77f7373162e9b5f0ff75d5"},
+ {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3cb75a3c14f1d6c3c2a94631e362802f70e83e20d1f2b2ef3026c05b415c4900"},
+ {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:badd1d7283f3e5894779a6ea8944cc765138b96804496c91812b2829f70e18a7"},
+ {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0ba6604bbc3dfcef844631932d06a1a4dcac3fee904efccf582261948431628a"},
+ {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8220fd4c6f98485e97aea65e1df76d4fed1678ede1fe1d0eed2957230d287c4"},
+ {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-win32.whl", hash = "sha256:04d21dc9c57d9608225da28285900762befbb0165ae48482c15d8d4989d4af14"},
+ {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-win_amd64.whl", hash = "sha256:27dc656e84396e6d687f97c6e65fb284d100483628f02d95464fd731743a4afe"},
+ {file = "ruamel_yaml_clib-0.2.15.tar.gz", hash = "sha256:46e4cc8c43ef6a94885f72512094e482114a8a706d3c555a34ed4b0d20200600"},
]
[[package]]
name = "ruff"
-version = "0.14.3"
+version = "0.14.6"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
groups = ["lint-and-format"]
files = [
- {file = "ruff-0.14.3-py3-none-linux_armv6l.whl", hash = "sha256:876b21e6c824f519446715c1342b8e60f97f93264012de9d8d10314f8a79c371"},
- {file = "ruff-0.14.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6fd8c79b457bedd2abf2702b9b472147cd860ed7855c73a5247fa55c9117654"},
- {file = "ruff-0.14.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:71ff6edca490c308f083156938c0c1a66907151263c4abdcb588602c6e696a14"},
- {file = "ruff-0.14.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:786ee3ce6139772ff9272aaf43296d975c0217ee1b97538a98171bf0d21f87ed"},
- {file = "ruff-0.14.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cd6291d0061811c52b8e392f946889916757610d45d004e41140d81fb6cd5ddc"},
- {file = "ruff-0.14.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a497ec0c3d2c88561b6d90f9c29f5ae68221ac00d471f306fa21fa4264ce5fcd"},
- {file = "ruff-0.14.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e231e1be58fc568950a04fbe6887c8e4b85310e7889727e2b81db205c45059eb"},
- {file = "ruff-0.14.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:469e35872a09c0e45fecf48dd960bfbce056b5db2d5e6b50eca329b4f853ae20"},
- {file = "ruff-0.14.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d6bc90307c469cb9d28b7cfad90aaa600b10d67c6e22026869f585e1e8a2db0"},
- {file = "ruff-0.14.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2f8a0bbcffcfd895df39c9a4ecd59bb80dca03dc43f7fb63e647ed176b741e"},
- {file = "ruff-0.14.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:678fdd7c7d2d94851597c23ee6336d25f9930b460b55f8598e011b57c74fd8c5"},
- {file = "ruff-0.14.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1ec1ac071e7e37e0221d2f2dbaf90897a988c531a8592a6a5959f0603a1ecf5e"},
- {file = "ruff-0.14.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afcdc4b5335ef440d19e7df9e8ae2ad9f749352190e96d481dc501b753f0733e"},
- {file = "ruff-0.14.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:7bfc42f81862749a7136267a343990f865e71fe2f99cf8d2958f684d23ce3dfa"},
- {file = "ruff-0.14.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a65e448cfd7e9c59fae8cf37f9221585d3354febaad9a07f29158af1528e165f"},
- {file = "ruff-0.14.3-py3-none-win32.whl", hash = "sha256:f3d91857d023ba93e14ed2d462ab62c3428f9bbf2b4fbac50a03ca66d31991f7"},
- {file = "ruff-0.14.3-py3-none-win_amd64.whl", hash = "sha256:d7b7006ac0756306db212fd37116cce2bd307e1e109375e1c6c106002df0ae5f"},
- {file = "ruff-0.14.3-py3-none-win_arm64.whl", hash = "sha256:26eb477ede6d399d898791d01961e16b86f02bc2486d0d1a7a9bb2379d055dc1"},
- {file = "ruff-0.14.3.tar.gz", hash = "sha256:4ff876d2ab2b161b6de0aa1f5bd714e8e9b4033dc122ee006925fbacc4f62153"},
+ {file = "ruff-0.14.6-py3-none-linux_armv6l.whl", hash = "sha256:d724ac2f1c240dbd01a2ae98db5d1d9a5e1d9e96eba999d1c48e30062df578a3"},
+ {file = "ruff-0.14.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9f7539ea257aa4d07b7ce87aed580e485c40143f2473ff2f2b75aee003186004"},
+ {file = "ruff-0.14.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f6007e55b90a2a7e93083ba48a9f23c3158c433591c33ee2e99a49b889c6332"},
+ {file = "ruff-0.14.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8e7b9d73d8728b68f632aa8e824ef041d068d231d8dbc7808532d3629a6bef"},
+ {file = "ruff-0.14.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d50d45d4553a3ebcbd33e7c5e0fe6ca4aafd9a9122492de357205c2c48f00775"},
+ {file = "ruff-0.14.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:118548dd121f8a21bfa8ab2c5b80e5b4aed67ead4b7567790962554f38e598ce"},
+ {file = "ruff-0.14.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57256efafbfefcb8748df9d1d766062f62b20150691021f8ab79e2d919f7c11f"},
+ {file = "ruff-0.14.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff18134841e5c68f8e5df1999a64429a02d5549036b394fafbe410f886e1989d"},
+ {file = "ruff-0.14.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c4b7ec1e66a105d5c27bd57fa93203637d66a26d10ca9809dc7fc18ec58440"},
+ {file = "ruff-0.14.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167843a6f78680746d7e226f255d920aeed5e4ad9c03258094a2d49d3028b105"},
+ {file = "ruff-0.14.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:16a33af621c9c523b1ae006b1b99b159bf5ac7e4b1f20b85b2572455018e0821"},
+ {file = "ruff-0.14.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1432ab6e1ae2dc565a7eea707d3b03a0c234ef401482a6f1621bc1f427c2ff55"},
+ {file = "ruff-0.14.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c55cfbbe7abb61eb914bfd20683d14cdfb38a6d56c6c66efa55ec6570ee4e71"},
+ {file = "ruff-0.14.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:efea3c0f21901a685fff4befda6d61a1bf4cb43de16da87e8226a281d614350b"},
+ {file = "ruff-0.14.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:344d97172576d75dc6afc0e9243376dbe1668559c72de1864439c4fc95f78185"},
+ {file = "ruff-0.14.6-py3-none-win32.whl", hash = "sha256:00169c0c8b85396516fdd9ce3446c7ca20c2a8f90a77aa945ba6b8f2bfe99e85"},
+ {file = "ruff-0.14.6-py3-none-win_amd64.whl", hash = "sha256:390e6480c5e3659f8a4c8d6a0373027820419ac14fa0d2713bd8e6c3e125b8b9"},
+ {file = "ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2"},
+ {file = "ruff-0.14.6.tar.gz", hash = "sha256:6f0c742ca6a7783a736b867a263b9a7a80a45ce9bee391eeda296895f1b4e1cc"},
]
[[package]]
@@ -2171,14 +2118,14 @@ files = [
[[package]]
name = "starlette"
-version = "0.49.3"
+version = "0.50.0"
description = "The little ASGI library that shines."
optional = false
-python-versions = ">=3.9"
+python-versions = ">=3.10"
groups = ["dev"]
files = [
- {file = "starlette-0.49.3-py3-none-any.whl", hash = "sha256:b579b99715fdc2980cf88c8ec96d3bf1ce16f5a8051a7c2b84ef9b1cdecaea2f"},
- {file = "starlette-0.49.3.tar.gz", hash = "sha256:1c14546f299b5901a1ea0e34410575bc33bbd741377a10484a54445588d00284"},
+ {file = "starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca"},
+ {file = "starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca"},
]
[package.dependencies]
@@ -2194,7 +2141,7 @@ version = "2.2.1"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.8"
-groups = ["main", "dev", "lint-and-format", "type-checking"]
+groups = ["dev", "lint-and-format", "type-checking"]
markers = "python_version < \"3.11\""
files = [
{file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
@@ -2399,4 +2346,4 @@ watchdog = ["watchdog (>=2.3)"]
[metadata]
lock-version = "2.1"
python-versions = ">=3.10, <4"
-content-hash = "b7ea95a1a29ef5ce4aec7eb44e253601508ae6b67b5d1091db3e5dc0d4252e8c"
+content-hash = "1007af3ad0f6fd0278abe498547d74e90b5870a522754706c50f85673d11f88b"
diff --git a/pyproject.toml b/pyproject.toml
index 5794850..0b3c0fe 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -28,12 +28,11 @@ dependencies = [
"robotframework >= 6.0.0, !=7.0.0",
"robotframework-datadriver >= 1.10.0",
"requests >= 2.31.0",
- "prance[CLI] >= 23",
- "Faker >= 23.1.0",
+ "prance[CLI] >= 25",
+ "Faker >= 38.0.0",
"rstr >= 3.2.0",
"openapi-core >= 0.19.0",
"rich_click >= 1.7.0",
- "black >= 24.1.0",
"Jinja2 >= 3.1.2",
"pydantic >= 2.11.0",
]
@@ -42,23 +41,23 @@ dependencies = [
dev = [
"invoke >= 2.2.0",
"robotframework-stacktrace >= 0.4.0",
- "uvicorn >= 0.27.0",
- "fastapi >= 0.109.0",
+ "uvicorn >= 0.38.0",
+ "fastapi >= 0.122.0",
"coverage[toml] >= 7.2.0",
- "robotcode-runner >= 1.0.3",
+ "robotcode-runner >= 2.0.0",
"genbadge[coverage] >= 1.1.2",
]
type-checking = [
"mypy >= 1.14.1",
"types-requests >= 2.31.0",
"types-invoke >= 2.0.0.0",
- "pyright >= 1.1.350",
- "robotcode-analyze >= 1.0.3",
+ "pyright >= 1.1.400",
+ "robotcode-analyze >= 2.0.0",
]
lint-and-format = [
- "ruff >= 0.9.0",
- "pylint >= 3.3.3",
- "robotframework-robocop >= 5.7.0",
+ "ruff >= 0.14.0",
+ "pylint >= 4.0.0",
+ "robotframework-robocop >= 6.0.0",
]
[project.urls]
From ed27ecbdb19ca63f8c029a50c93e4a12c86acb47 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Mon, 1 Dec 2025 12:03:25 +0000
Subject: [PATCH 30/55] Additional tests on schema variations, model
discriminator refactor
---
.../data_generation/data_invalidation.py | 2 +-
src/OpenApiLibCore/models/oas_models.py | 48 ++++--
tests/files/request_data_variations.json | 149 ++++++++++++++++++
.../unittests/test_get_request_data.py | 86 ++++++++++
4 files changed, 269 insertions(+), 16 deletions(-)
create mode 100644 tests/files/request_data_variations.json
create mode 100644 tests/libcore/unittests/test_get_request_data.py
diff --git a/src/OpenApiLibCore/data_generation/data_invalidation.py b/src/OpenApiLibCore/data_generation/data_invalidation.py
index 8664f24..9b6ce50 100644
--- a/src/OpenApiLibCore/data_generation/data_invalidation.py
+++ b/src/OpenApiLibCore/data_generation/data_invalidation.py
@@ -15,7 +15,7 @@
from OpenApiLibCore.data_constraints.dto_base import (
Dto,
)
-from OpenApiLibCore.models import IGNORE, Ignore
+from OpenApiLibCore.models import IGNORE
from OpenApiLibCore.models.oas_models import (
ArraySchema,
ObjectSchema,
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index bbfb79a..2c15833 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -9,6 +9,7 @@
from random import choice, randint, sample, shuffle, uniform
from sys import float_info
from typing import (
+ Annotated,
Any,
Callable,
Generator,
@@ -19,12 +20,13 @@
TypeAlias,
TypeGuard,
TypeVar,
+ Union,
cast,
)
from uuid import uuid4
import rstr
-from pydantic import BaseModel, Field, RootModel
+from pydantic import BaseModel, Discriminator, Field, RootModel, Tag
from robot.api import logger
from robot.libraries.BuiltIn import BuiltIn
@@ -1043,17 +1045,33 @@ def annotation_string(self) -> str:
return "dict[str, JSON]"
-ResolvedSchemaObjectTypes: TypeAlias = (
- NullSchema
- | BooleanSchema
- | StringSchema
- | BytesSchema
- | IntegerSchema
- | NumberSchema
- | ArraySchema[AI]
- | ObjectSchema
-)
-RESOLVED_SCHEMA_INSTANCES = (
+def get_discriminator_property(data: Any) -> str:
+ if isinstance(data, dict):
+ format = data.get("format")
+ if format == "byte":
+ return "bytes"
+ return data.get("type", "unknown")
+ if hasattr(data, "format"):
+ if data.format == "byte":
+ return "bytes"
+ return getattr(data, "type", "unknown")
+
+
+ResolvedSchemaObjectTypes = Annotated[
+ Union[
+ Annotated[BytesSchema, Tag("bytes")],
+ Annotated[StringSchema, Tag("string")],
+ Annotated[NullSchema, Tag("null")],
+ Annotated[BooleanSchema, Tag("boolean")],
+ Annotated[IntegerSchema, Tag("integer")],
+ Annotated[NumberSchema, Tag("number")],
+ Annotated[ArraySchema, Tag("array")],
+ Annotated[ObjectSchema, Tag("object")],
+ ],
+ Discriminator(get_discriminator_property),
+]
+
+RESOLVED_SCHEMA_CLASS_TUPLE = (
NullSchema,
BooleanSchema,
StringSchema,
@@ -1121,7 +1139,7 @@ def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, No
additional_properties_types = []
for additional_properties_item in additional_properties_list:
if isinstance(
- additional_properties_item, RESOLVED_SCHEMA_INSTANCES
+ additional_properties_item, RESOLVED_SCHEMA_CLASS_TUPLE
):
additional_properties_types.append(additional_properties_item)
if not additional_properties_types:
@@ -1148,7 +1166,7 @@ def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, No
yield merged_schema
else:
for schema in self.anyOf + self.oneOf:
- if isinstance(schema, RESOLVED_SCHEMA_INSTANCES):
+ if isinstance(schema, RESOLVED_SCHEMA_CLASS_TUPLE):
yield schema
else:
yield from schema.resolved_schemas
@@ -1170,7 +1188,7 @@ def annotation_string(self) -> str:
SchemaObjectTypes: TypeAlias = ResolvedSchemaObjectTypes | UnionTypeSchema # type: ignore[type-arg]
-class PropertiesMapping(RootModel[dict[str, "SchemaObjectTypes"]], frozen=True): ...
+class PropertiesMapping(RootModel[dict[str, SchemaObjectTypes]], frozen=True): ...
def _get_empty_properties_mapping() -> PropertiesMapping:
diff --git a/tests/files/request_data_variations.json b/tests/files/request_data_variations.json
new file mode 100644
index 0000000..585f250
--- /dev/null
+++ b/tests/files/request_data_variations.json
@@ -0,0 +1,149 @@
+{
+ "openapi": "3.1.0",
+ "info": {
+ "title": "test_get_request_data",
+ "version": "0.1.0"
+ },
+ "paths": {
+ "/null_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "null"
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/boolean_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/integer_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "integer"
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/number_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "number"
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/string_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/bytes_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "string",
+ "format": "byte"
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/object_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/array_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ }
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/union_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "type": "null"
+ },
+ {
+ "type": "integer"
+ },
+ {
+ "type": "string"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/libcore/unittests/test_get_request_data.py b/tests/libcore/unittests/test_get_request_data.py
new file mode 100644
index 0000000..428f605
--- /dev/null
+++ b/tests/libcore/unittests/test_get_request_data.py
@@ -0,0 +1,86 @@
+# pylint: disable="missing-class-docstring", "missing-function-docstring"
+import json
+import pathlib
+import unittest
+from functools import partial
+
+from OpenApiLibCore.data_generation.data_generation_core import get_request_data
+from OpenApiLibCore.models.oas_models import (
+ ArraySchema,
+ BooleanSchema,
+ BytesSchema,
+ IntegerSchema,
+ NullSchema,
+ NumberSchema,
+ ObjectSchema,
+ OpenApiObject,
+ StringSchema,
+)
+
+unittest_folder = pathlib.Path(__file__).parent.resolve()
+spec_path = unittest_folder.parent.parent / "files" / "request_data_variations.json"
+
+
+class TestValidData(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls) -> None:
+ with open(file=spec_path) as json_file:
+ spec_dict = json.load(json_file)
+ cls.spec = OpenApiObject.model_validate(spec_dict)
+ cls._get_request_data = partial(
+ get_request_data, method="POST", openapi_spec=cls.spec
+ )
+
+ def test_null_schema(self) -> None:
+ request_data = self._get_request_data(path="/null_schema")
+ self.assertEqual(request_data.valid_data, None)
+ self.assertIsInstance(request_data.body_schema, NullSchema)
+
+ def test_bool_schema(self) -> None:
+ request_data = self._get_request_data(path="/boolean_schema")
+ self.assertIsInstance(request_data.valid_data, bool)
+ self.assertIsInstance(request_data.body_schema, BooleanSchema)
+
+ def test_int_schema(self) -> None:
+ request_data = self._get_request_data(path="/integer_schema")
+ self.assertIsInstance(request_data.valid_data, int)
+ self.assertIsInstance(request_data.body_schema, IntegerSchema)
+
+ def test_number_schema(self) -> None:
+ request_data = self._get_request_data(path="/number_schema")
+ self.assertIsInstance(request_data.valid_data, float)
+ self.assertIsInstance(request_data.body_schema, NumberSchema)
+
+ def test_string_schema(self) -> None:
+ request_data = self._get_request_data(path="/string_schema")
+ self.assertIsInstance(request_data.valid_data, str)
+ self.assertIsInstance(request_data.body_schema, StringSchema)
+
+ def test_bytes_schema(self) -> None:
+ request_data = self._get_request_data(path="/bytes_schema")
+ self.assertIsInstance(request_data.valid_data, bytes)
+ self.assertIsInstance(request_data.body_schema, BytesSchema)
+
+ def test_object_schema(self) -> None:
+ request_data = self._get_request_data(path="/object_schema")
+ self.assertIsInstance(request_data.valid_data, dict)
+ self.assertIsInstance(request_data.body_schema, ObjectSchema)
+
+ def test_array_schema(self) -> None:
+ request_data = self._get_request_data(path="/array_schema")
+ self.assertIsInstance(request_data.valid_data, list)
+ self.assertIsInstance(request_data.body_schema, ArraySchema)
+ self.assertIsInstance(request_data.body_schema.items, NumberSchema)
+
+ def test_union_schema(self) -> None:
+ request_data = self._get_request_data(path="/union_schema")
+ self.assertIsInstance(request_data.valid_data, (type(None), int, str))
+ self.assertTrue(
+ isinstance(
+ request_data.body_schema, (NullSchema, IntegerSchema, StringSchema)
+ )
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()
From 24deb083cbd950b85919c70592bb74ba2bb17c75 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Tue, 2 Dec 2025 15:28:02 +0000
Subject: [PATCH 31/55] Handle Content-Type case-insensitive
---
.../data_generation/data_generation_core.py | 10 +++++++---
src/OpenApiLibCore/keyword_logic/validation.py | 10 ++++++++--
tests/libcore/suites/test_get_request_data.robot | 2 +-
tests/libcore/suites/test_schema_variations.robot | 2 +-
4 files changed, 17 insertions(+), 7 deletions(-)
diff --git a/src/OpenApiLibCore/data_generation/data_generation_core.py b/src/OpenApiLibCore/data_generation/data_generation_core.py
index 0a772dc..8b75f7a 100644
--- a/src/OpenApiLibCore/data_generation/data_generation_core.py
+++ b/src/OpenApiLibCore/data_generation/data_generation_core.py
@@ -84,7 +84,11 @@ def get_request_data(
)
if operation_spec.requestBody.mime_type:
- headers.update({"content-type": operation_spec.requestBody.mime_type})
+ if "content-type" in headers:
+ key_value = "content-type" # pragma: no cover
+ else:
+ key_value = "Content-Type"
+ headers.update({key_value: operation_spec.requestBody.mime_type})
if isinstance(body_schema, UnionTypeSchema):
body_schema = choice(body_schema.resolved_schemas)
@@ -127,7 +131,7 @@ def _get_mapping_dataclass_for_empty_body(
def _get_mapping_dataclass_from_valid_data(
- schema: ResolvedSchemaObjectTypes, # type: ignore[type-arg]
+ schema: ResolvedSchemaObjectTypes,
constraint_mapping: type[IConstraintMapping] | None,
valid_data: JSON,
method_spec: OperationObject,
@@ -243,7 +247,7 @@ def get_parameter_data(
continue
if parameter.schema_ is None:
- continue
+ continue # pragma: no cover
value = parameter.schema_.get_valid_value()
result[parameter_name] = value
return result
diff --git a/src/OpenApiLibCore/keyword_logic/validation.py b/src/OpenApiLibCore/keyword_logic/validation.py
index 3b4f3d8..0776b10 100644
--- a/src/OpenApiLibCore/keyword_logic/validation.py
+++ b/src/OpenApiLibCore/keyword_logic/validation.py
@@ -365,9 +365,15 @@ def _validate_response(
) -> None:
try:
content_type = response.headers.get("Content-Type", "")
- if "json" in content_type:
+ if content_type:
+ key_value = "Content-Type"
+ else:
+ content_type = response.headers.get("content-type", "")
+ if content_type:
+ key_value = "content-type"
+ if "json" in content_type.lower():
content_type, _, _ = content_type.partition(";")
- response.headers.update({"Content-Type": content_type})
+ response.headers.update({key_value: content_type}) # pyright: ignore[reportPossiblyUnboundVariable]
validate_response_using_validator(
response=response,
response_validator=response_validator,
diff --git a/tests/libcore/suites/test_get_request_data.robot b/tests/libcore/suites/test_get_request_data.robot
index 77582ca..3784124 100644
--- a/tests/libcore/suites/test_get_request_data.robot
+++ b/tests/libcore/suites/test_get_request_data.robot
@@ -49,7 +49,7 @@ Test Get Request Data For Endpoint With Object RequestBody
Should Not Be Empty ${request_data.body_schema.properties.root}
Should Be Equal ${request_data.parameters} ${list}
Should Be Equal ${request_data.params} ${dict}
- VAR &{expected_headers}= content-type=application/json
+ VAR &{expected_headers}= Content-Type=application/json
Should Be Equal ${request_data.headers} ${expected_headers}
Should Be True ${request_data.has_body}
diff --git a/tests/libcore/suites/test_schema_variations.robot b/tests/libcore/suites/test_schema_variations.robot
index 835b90c..c1fed57 100644
--- a/tests/libcore/suites/test_schema_variations.robot
+++ b/tests/libcore/suites/test_schema_variations.robot
@@ -17,7 +17,7 @@ Test Get Request Data For Schema With allOf
${request_data}= Get Request Data path=/hypermedia method=post
VAR &{dict}= &{EMPTY}
VAR @{list}= @{EMPTY}
- VAR &{expected_headers}= content-type=application/hal+json
+ VAR &{expected_headers}= Content-Type=application/hal+json
Length Should Be ${request_data.valid_data}[isan] 36
Length Should Be ${request_data.valid_data}[tags] 1
Length Should Be ${request_data.valid_data}[tags][0] 36
From 96a057cee66d871cf6a62a60e6b898cd841954cd Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Tue, 2 Dec 2025 15:29:06 +0000
Subject: [PATCH 32/55] get_invalid_data on ObjectSchema refactored /
simplified
---
src/OpenApiLibCore/models/oas_models.py | 151 +++++++++++-------------
1 file changed, 71 insertions(+), 80 deletions(-)
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index 2c15833..9405f2e 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -948,85 +948,76 @@ def get_invalid_data(
# Remove duplicates, then shuffle the property_names so different properties in
# the data dict are invalidated when rerunning the test.
shuffle(list(set(property_names)))
- for property_name in property_names:
- # if possible, invalidate a constraint but send otherwise valid data
- id_dependencies = [
- r
- for r in relations
- if isinstance(r, IdDependency) and r.property_name == property_name
- ]
- if id_dependencies:
- invalid_id = uuid4().hex
- logger.debug(
- f"Breaking IdDependency for status_code {status_code}: setting "
- f"{property_name} to {invalid_id}"
- )
- properties[property_name] = invalid_id
- return properties
-
- invalid_value_from_constraint = [
- r.invalid_value
- for r in relations
- if isinstance(r, PropertyValueConstraint)
- and r.property_name == property_name
- and r.invalid_value_error_code == status_code
- ]
- if (
- invalid_value_from_constraint
- and invalid_value_from_constraint[0] is not NOT_SET
- ):
- properties[property_name] = invalid_value_from_constraint[0]
- logger.debug(
- f"Using invalid_value {invalid_value_from_constraint[0]} to "
- f"invalidate property {property_name}"
- )
- return properties
-
- value_schema = self.properties.root[property_name]
- if isinstance(value_schema, UnionTypeSchema):
- # Filter "type": "null" from the possible types since this indicates an
- # optional / nullable property that can only be invalidated by sending
- # invalid data of a non-null type
- non_null_schemas = [
- s
- for s in value_schema.resolved_schemas
- if not isinstance(s, NullSchema)
- ]
- value_schema = choice(non_null_schemas)
-
- # there may not be a current_value when invalidating an optional property
- current_value = properties.get(property_name, SENTINEL)
- if current_value is SENTINEL:
- # the current_value isn't very relevant as long as the type is correct
- # so no logic to handle Relations / objects / arrays here
- property_type = value_schema.type
- if property_type == "object":
- current_value = {}
- elif property_type == "array":
- current_value = []
- else:
- current_value = value_schema.get_valid_value()
-
- values_from_constraint = [
- r.values[0]
- for r in relations
- if isinstance(r, PropertyValueConstraint)
- and r.property_name == property_name
- ]
-
- invalid_value = value_schema.get_invalid_value(
- valid_value=current_value, # type: ignore[arg-type]
- values_from_constraint=values_from_constraint,
+ # The value of 1 property will be changed and since they are shuffled, take the first
+ property_name = property_names[0]
+ # if possible, invalidate a constraint but send otherwise valid data
+ id_dependencies = [
+ r
+ for r in relations
+ if isinstance(r, IdDependency) and r.property_name == property_name
+ ]
+ if id_dependencies:
+ invalid_id = uuid4().hex
+ logger.debug(
+ f"Breaking IdDependency for status_code {status_code}: setting "
+ f"{property_name} to {invalid_id}"
)
- if not isinstance(invalid_value, Ignore):
- properties[property_name] = invalid_value
+ properties[property_name] = invalid_id
+ return properties
+
+ invalid_value_from_constraint = [
+ r.invalid_value
+ for r in relations
+ if isinstance(r, PropertyValueConstraint)
+ and r.property_name == property_name
+ and r.invalid_value_error_code == status_code
+ ]
+ if (
+ invalid_value_from_constraint
+ and invalid_value_from_constraint[0] is not NOT_SET
+ ):
+ properties[property_name] = invalid_value_from_constraint[0]
logger.debug(
- f"Property {property_name} changed to {invalid_value!r} "
- f"(received from get_invalid_value)"
+ f"Using invalid_value {invalid_value_from_constraint[0]} to "
+ f"invalidate property {property_name}"
)
return properties
- logger.warn("get_invalidated_data returned unchanged properties")
- return properties # pragma: no cover
+
+ value_schema = self.properties.root[property_name]
+ if isinstance(value_schema, UnionTypeSchema):
+ # Filter "type": "null" from the possible types since this indicates an
+ # optional / nullable property that can only be invalidated by sending
+ # invalid data of a non-null type
+ non_null_schemas = [
+ s
+ for s in value_schema.resolved_schemas
+ if not isinstance(s, NullSchema)
+ ]
+ value_schema = choice(non_null_schemas)
+
+ # there may not be a current_value when invalidating an optional property
+ current_value = properties.get(property_name, SENTINEL)
+ if current_value is SENTINEL:
+ current_value = value_schema.get_valid_value()
+
+ values_from_constraint = [
+ r.values[0]
+ for r in relations
+ if isinstance(r, PropertyValueConstraint)
+ and r.property_name == property_name
+ ]
+
+ invalid_value = value_schema.get_invalid_value(
+ valid_value=current_value, # type: ignore[arg-type]
+ values_from_constraint=values_from_constraint,
+ )
+ if not isinstance(invalid_value, Ignore):
+ properties[property_name] = invalid_value
+ logger.debug(
+ f"Property {property_name} changed to {invalid_value!r} "
+ f"(received from get_invalid_value)"
+ )
+ return properties
@property
def can_be_invalidated(self) -> bool:
@@ -1050,7 +1041,7 @@ def get_discriminator_property(data: Any) -> str:
format = data.get("format")
if format == "byte":
return "bytes"
- return data.get("type", "unknown")
+ return data.get("type", "unknown") # type: ignore[no-any-return]
if hasattr(data, "format"):
if data.format == "byte":
return "bytes"
@@ -1065,7 +1056,7 @@ def get_discriminator_property(data: Any) -> str:
Annotated[BooleanSchema, Tag("boolean")],
Annotated[IntegerSchema, Tag("integer")],
Annotated[NumberSchema, Tag("number")],
- Annotated[ArraySchema, Tag("array")],
+ Annotated[ArraySchema, Tag("array")], # type: ignore[type-arg]
Annotated[ObjectSchema, Tag("object")],
],
Discriminator(get_discriminator_property),
@@ -1100,10 +1091,10 @@ def get_values_out_of_bounds(self, current_value: JSON) -> list[JSON]:
raise ValueError
@property
- def resolved_schemas(self) -> list[ResolvedSchemaObjectTypes]: # type: ignore[type-arg]
+ def resolved_schemas(self) -> list[ResolvedSchemaObjectTypes]:
return list(self._get_resolved_schemas())
- def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, None]: # type: ignore[type-arg]
+ def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, None]:
if self.allOf:
properties_list: list[PropertiesMapping] = []
additional_properties_list = []
@@ -1185,7 +1176,7 @@ def annotation_string(self) -> str:
return " | ".join(unique_annotations)
-SchemaObjectTypes: TypeAlias = ResolvedSchemaObjectTypes | UnionTypeSchema # type: ignore[type-arg]
+SchemaObjectTypes: TypeAlias = ResolvedSchemaObjectTypes | UnionTypeSchema
class PropertiesMapping(RootModel[dict[str, SchemaObjectTypes]], frozen=True): ...
From d2e7251ad26077840cfca167da7f435b78526099 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Tue, 2 Dec 2025 15:29:51 +0000
Subject: [PATCH 33/55] ignore markers and docuementation changes
---
src/OpenApiLibCore/models/request_data.py | 6 +++---
src/OpenApiLibCore/protocols.py | 2 ++
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/OpenApiLibCore/models/request_data.py b/src/OpenApiLibCore/models/request_data.py
index 164cb62..b4193e1 100644
--- a/src/OpenApiLibCore/models/request_data.py
+++ b/src/OpenApiLibCore/models/request_data.py
@@ -57,7 +57,7 @@ class RequestData:
valid_data: JSON
constraint_mapping: type[IConstraintMapping]
- body_schema: ResolvedSchemaObjectTypes | None = None # type: ignore[type-arg]
+ body_schema: ResolvedSchemaObjectTypes | None = None
parameters: list[ParameterObject] = field(default_factory=list)
params: dict[str, JSON] = field(default_factory=dict)
headers: dict[str, str] = field(default_factory=dict)
@@ -116,7 +116,7 @@ def params_that_can_be_invalidated(self) -> set[str]:
if param.schema_ is None:
continue
- possible_schemas: list[ResolvedSchemaObjectTypes] = [] # type: ignore[type-arg]
+ possible_schemas: list[ResolvedSchemaObjectTypes] = []
if isinstance(param.schema_, UnionTypeSchema):
possible_schemas = param.schema_.resolved_schemas
else:
@@ -157,7 +157,7 @@ def headers_that_can_be_invalidated(self) -> set[str]:
if header.schema_ is None:
continue
- possible_schemas: list[ResolvedSchemaObjectTypes] = [] # type: ignore[type-arg]
+ possible_schemas: list[ResolvedSchemaObjectTypes] = []
if isinstance(header.schema_, UnionTypeSchema):
possible_schemas = header.schema_.resolved_schemas
else:
diff --git a/src/OpenApiLibCore/protocols.py b/src/OpenApiLibCore/protocols.py
index 31d503f..2b7fbce 100644
--- a/src/OpenApiLibCore/protocols.py
+++ b/src/OpenApiLibCore/protocols.py
@@ -40,6 +40,8 @@ def id_mapping(
class IConstraintMapping(Protocol):
+ # NOTE: This Protocol is used as annotation in a number of the oas_models, which
+ # requires this method to prevent a PydanticSchemaGenerationError.
@classmethod
def __get_pydantic_core_schema__(
cls, source_type: Any, handler: GetCoreSchemaHandler
From d0c7bc777ffd657f347e322beae844d02d10ce5c Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Tue, 2 Dec 2025 15:30:50 +0000
Subject: [PATCH 34/55] unit tests restructured and improved
---
tests/files/request_data_variations.json | 52 +++++++++++++
tests/libcore/unittests/oas_model/__init__.py | 0
.../test_can_be_invalidated.py | 0
.../oas_model/test_get_invalid_data.py | 74 +++++++++++++++++++
.../oas_model/test_get_invalid_value.py | 39 ++++++++++
.../{ => oas_model}/test_get_request_data.py | 10 ++-
.../{ => oas_model}/test_get_valid_value.py | 0
.../test_get_values_out_of_bounds.py | 0
.../test_invalid_value_from_const_or_enum.py | 0
.../test_resolve_union_schema.py | 0
10 files changed, 174 insertions(+), 1 deletion(-)
create mode 100644 tests/libcore/unittests/oas_model/__init__.py
rename tests/libcore/unittests/{ => oas_model}/test_can_be_invalidated.py (100%)
create mode 100644 tests/libcore/unittests/oas_model/test_get_invalid_data.py
create mode 100644 tests/libcore/unittests/oas_model/test_get_invalid_value.py
rename tests/libcore/unittests/{ => oas_model}/test_get_request_data.py (88%)
rename tests/libcore/unittests/{ => oas_model}/test_get_valid_value.py (100%)
rename tests/libcore/unittests/{ => oas_model}/test_get_values_out_of_bounds.py (100%)
rename tests/libcore/unittests/{ => oas_model}/test_invalid_value_from_const_or_enum.py (100%)
rename tests/libcore/unittests/{ => oas_model}/test_resolve_union_schema.py (100%)
diff --git a/tests/files/request_data_variations.json b/tests/files/request_data_variations.json
index 585f250..49b5bb5 100644
--- a/tests/files/request_data_variations.json
+++ b/tests/files/request_data_variations.json
@@ -144,6 +144,58 @@
"required": true
}
}
+ },
+ "/array_with_union_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "allOf": [
+ {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string",
+ "format": "byte"
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "minItems": 1
+ }
+ }
+ },
+ "required": true
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/tests/libcore/unittests/oas_model/__init__.py b/tests/libcore/unittests/oas_model/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/libcore/unittests/test_can_be_invalidated.py b/tests/libcore/unittests/oas_model/test_can_be_invalidated.py
similarity index 100%
rename from tests/libcore/unittests/test_can_be_invalidated.py
rename to tests/libcore/unittests/oas_model/test_can_be_invalidated.py
diff --git a/tests/libcore/unittests/oas_model/test_get_invalid_data.py b/tests/libcore/unittests/oas_model/test_get_invalid_data.py
new file mode 100644
index 0000000..141f3c9
--- /dev/null
+++ b/tests/libcore/unittests/oas_model/test_get_invalid_data.py
@@ -0,0 +1,74 @@
+# pylint: disable="missing-class-docstring", "missing-function-docstring"
+# pyright: reportArgumentType=false
+import unittest
+
+from OpenApiLibCore import Dto, PropertyValueConstraint, ResourceRelation
+from OpenApiLibCore.models.oas_models import ArraySchema, IntegerSchema
+
+
+class ArrayConstraint(Dto):
+ @staticmethod
+ def get_relations() -> list[ResourceRelation]:
+ relations: list[ResourceRelation] = [
+ PropertyValueConstraint(
+ property_name="something",
+ values=[24, 42],
+ invalid_value=33,
+ invalid_value_error_code=422,
+ ),
+ ]
+ return relations
+
+
+class TestArraySchema(unittest.TestCase):
+ def test_raises_for_no_matching_status_code(self) -> None:
+ schema = ArraySchema(items=IntegerSchema())
+ with self.assertRaises(ValueError) as context:
+ _ = schema.get_invalid_data(
+ valid_data=[42],
+ constraint_mapping=ArrayConstraint,
+ status_code=500,
+ invalid_property_default_code=422,
+ )
+ self.assertEqual(
+ str(context.exception),
+ "No constraint can be broken to cause status_code 500",
+ )
+
+ def test_status_code_is_default_code_without_constraints_raises(self) -> None:
+ schema = ArraySchema(items=IntegerSchema(maximum=43))
+ with self.assertRaises(ValueError) as context:
+ _ = schema.get_invalid_data(
+ valid_data=[42],
+ constraint_mapping=Dto,
+ status_code=422,
+ invalid_property_default_code=422,
+ )
+ self.assertEqual(
+ str(context.exception),
+ "No constraint can be broken to cause status_code 422",
+ )
+
+ def test_status_code_is_default_code(self) -> None:
+ schema = ArraySchema(items=IntegerSchema(maximum=43), minItems=1)
+ invalid_data = schema.get_invalid_data(
+ valid_data=[42],
+ constraint_mapping=Dto,
+ status_code=422,
+ invalid_property_default_code=422,
+ )
+ self.assertEqual(invalid_data, [])
+
+ valid_value = [42]
+ schema = ArraySchema(items=IntegerSchema(maximum=43), const=valid_value)
+ invalid_data = schema.get_invalid_data(
+ valid_data=valid_value,
+ constraint_mapping=Dto,
+ status_code=422,
+ invalid_property_default_code=422,
+ )
+ self.assertNotEqual(invalid_data, valid_value)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/libcore/unittests/oas_model/test_get_invalid_value.py b/tests/libcore/unittests/oas_model/test_get_invalid_value.py
new file mode 100644
index 0000000..5a5d9e9
--- /dev/null
+++ b/tests/libcore/unittests/oas_model/test_get_invalid_value.py
@@ -0,0 +1,39 @@
+# pylint: disable="missing-class-docstring", "missing-function-docstring"
+import unittest
+
+from OpenApiLibCore.models.oas_models import (
+ BooleanSchema,
+ IntegerSchema,
+ StringSchema,
+)
+
+
+class TestGetInvalidValue(unittest.TestCase):
+ def test_value_error_handling(self) -> None:
+ values_from_constraints = [True, False]
+ schema = BooleanSchema()
+ invalid_value = schema.get_invalid_value(
+ valid_value=True, values_from_constraint=values_from_constraints
+ )
+ self.assertIsInstance(invalid_value, str)
+
+ def test_out_of_bounds(self) -> None:
+ schema = StringSchema(maxLength=3)
+ invalid_value = schema.get_invalid_value(valid_value="x")
+ self.assertIsInstance(invalid_value, (str, list))
+ if isinstance(invalid_value, str):
+ self.assertTrue(len(invalid_value) > 3)
+ else:
+ self.assertEqual(
+ invalid_value, [{"invalid": [None, False]}, "null", None, True]
+ )
+
+ schema = IntegerSchema(minimum=5)
+ invalid_value = schema.get_invalid_value(valid_value=7)
+ self.assertIsInstance(invalid_value, (int, str))
+ if isinstance(invalid_value, int):
+ self.assertTrue(invalid_value < 5)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/libcore/unittests/test_get_request_data.py b/tests/libcore/unittests/oas_model/test_get_request_data.py
similarity index 88%
rename from tests/libcore/unittests/test_get_request_data.py
rename to tests/libcore/unittests/oas_model/test_get_request_data.py
index 428f605..94cf1d4 100644
--- a/tests/libcore/unittests/test_get_request_data.py
+++ b/tests/libcore/unittests/oas_model/test_get_request_data.py
@@ -18,7 +18,9 @@
)
unittest_folder = pathlib.Path(__file__).parent.resolve()
-spec_path = unittest_folder.parent.parent / "files" / "request_data_variations.json"
+spec_path = (
+ unittest_folder.parent.parent.parent / "files" / "request_data_variations.json"
+)
class TestValidData(unittest.TestCase):
@@ -81,6 +83,12 @@ def test_union_schema(self) -> None:
)
)
+ def test_array_with_union_schema(self) -> None:
+ request_data = self._get_request_data(path="/array_with_union_schema")
+ self.assertIsInstance(request_data.valid_data, list)
+ self.assertIsInstance(request_data.valid_data[0], dict)
+ self.assertTrue(isinstance(request_data.body_schema, ArraySchema))
+
if __name__ == "__main__":
unittest.main()
diff --git a/tests/libcore/unittests/test_get_valid_value.py b/tests/libcore/unittests/oas_model/test_get_valid_value.py
similarity index 100%
rename from tests/libcore/unittests/test_get_valid_value.py
rename to tests/libcore/unittests/oas_model/test_get_valid_value.py
diff --git a/tests/libcore/unittests/test_get_values_out_of_bounds.py b/tests/libcore/unittests/oas_model/test_get_values_out_of_bounds.py
similarity index 100%
rename from tests/libcore/unittests/test_get_values_out_of_bounds.py
rename to tests/libcore/unittests/oas_model/test_get_values_out_of_bounds.py
diff --git a/tests/libcore/unittests/test_invalid_value_from_const_or_enum.py b/tests/libcore/unittests/oas_model/test_invalid_value_from_const_or_enum.py
similarity index 100%
rename from tests/libcore/unittests/test_invalid_value_from_const_or_enum.py
rename to tests/libcore/unittests/oas_model/test_invalid_value_from_const_or_enum.py
diff --git a/tests/libcore/unittests/test_resolve_union_schema.py b/tests/libcore/unittests/oas_model/test_resolve_union_schema.py
similarity index 100%
rename from tests/libcore/unittests/test_resolve_union_schema.py
rename to tests/libcore/unittests/oas_model/test_resolve_union_schema.py
From 9e64e0f1c7d8e8dccba0d0d20bf2dae29a059dc1 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Tue, 2 Dec 2025 15:31:30 +0000
Subject: [PATCH 35/55] example EtagListener improved header handling
---
tests/user_implemented/EtagListener.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/tests/user_implemented/EtagListener.py b/tests/user_implemented/EtagListener.py
index 557601a..88b2880 100644
--- a/tests/user_implemented/EtagListener.py
+++ b/tests/user_implemented/EtagListener.py
@@ -33,5 +33,8 @@ def start_keyword(self, data: KeywordData, result: KeywordResult) -> None:
return
get_result = run_keyword("authorized_request", url, "GET", params, headers)
- etag = get_result.headers.get("etag")
+ lower_case_headers = {
+ key.lower(): value for key, value in get_result.headers.items()
+ }
+ etag = lower_case_headers.get("etag")
result.args[3]["If-Match"] = etag
From 780438d64d7d21cbf4c24de25c459ba60c10350f Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Fri, 5 Dec 2025 14:16:54 +0000
Subject: [PATCH 36/55] Remove disfunctional byte string support
---
src/OpenApiLibCore/annotations.py | 2 +-
.../keyword_logic/resource_relations.py | 2 +-
.../keyword_logic/validation.py | 2 +-
src/OpenApiLibCore/models/oas_models.py | 155 +++---------------
tests/files/request_data_variations.json | 13 +-
.../oas_model/test_can_be_invalidated.py | 17 --
.../oas_model/test_get_request_data.py | 19 ++-
.../oas_model/test_get_valid_value.py | 66 --------
.../test_get_values_out_of_bounds.py | 33 ----
.../test_invalid_value_from_const_or_enum.py | 16 --
.../oas_model/test_resolve_union_schema.py | 5 -
.../test_invalid_value_from_constraint.py | 23 ---
12 files changed, 49 insertions(+), 304 deletions(-)
diff --git a/src/OpenApiLibCore/annotations.py b/src/OpenApiLibCore/annotations.py
index fa70867..2cb6a5e 100644
--- a/src/OpenApiLibCore/annotations.py
+++ b/src/OpenApiLibCore/annotations.py
@@ -6,5 +6,5 @@
JSON = TypeAliasType(
"JSON",
- "Union[dict[str, JSON], list[JSON], str, bytes, int, float, bool, None]",
+ "Union[dict[str, JSON], list[JSON], str, int, float, bool, None]",
)
diff --git a/src/OpenApiLibCore/keyword_logic/resource_relations.py b/src/OpenApiLibCore/keyword_logic/resource_relations.py
index 0cfeb7e..3605f4f 100644
--- a/src/OpenApiLibCore/keyword_logic/resource_relations.py
+++ b/src/OpenApiLibCore/keyword_logic/resource_relations.py
@@ -74,6 +74,6 @@ def ensure_in_use(
)
if not response.ok:
logger.debug(
- f"POST on {post_url} with json {json_data!r} failed: {response.json()}"
+ f"POST on {post_url} with json {json_data} failed: {response.json()}"
)
response.raise_for_status()
diff --git a/src/OpenApiLibCore/keyword_logic/validation.py b/src/OpenApiLibCore/keyword_logic/validation.py
index 0776b10..4b7f6fd 100644
--- a/src/OpenApiLibCore/keyword_logic/validation.py
+++ b/src/OpenApiLibCore/keyword_logic/validation.py
@@ -242,7 +242,7 @@ def assert_href_to_resource_is_valid(
headers = request_data.headers
get_response = _run_keyword("authorized_request", url, "GET", params, headers)
assert get_response.json() == referenced_resource, (
- f"{get_response.json()!r} not equal to original {referenced_resource!r}"
+ f"{get_response.json()} not equal to original {referenced_resource}"
)
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index 9405f2e..cd49efd 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -310,107 +310,6 @@ def annotation_string(self) -> str:
return "str"
-class BytesSchema(SchemaBase[bytes], frozen=True):
- type: Literal["string"] = "string"
- format: Literal["byte"] = "byte"
- pattern: str = ""
- maxLength: int | None = None
- minLength: int | None = None
- const: bytes | None = None
- enum: list[bytes] | None = None
- nullable: bool = False
-
- def get_valid_value(
- self,
- constraint_mapping: ConstraintMappingType | None = None,
- operation_id: str | None = None,
- ) -> bytes:
- """Generate a random string within the min/max length in the schema, if specified."""
- if self.const is not None:
- return self.const
- if self.enum is not None:
- return choice(self.enum)
- if self.pattern:
- logger.warn(
- "'pattern' is currently not supported for 'byte' format strings. "
- "To ensure a valid value is generated for this property, a "
- "PropertyValueConstraint can be configured. See the Advanced Use "
- "section of the OpenApiTools documentation for more details."
- )
- minimum = self.minLength if self.minLength is not None else 0
- maximum = self.maxLength if self.maxLength is not None else 36
- maximum = max(minimum, maximum)
-
- # use "uuid" for generated strings to prevent whitespace characters
- value = fake_string(string_format="uuid")
- while len(value) < minimum:
- value = value + fake_string(string_format="uuid")
- bytes_value = base64.b64encode(value.encode("utf-8"))
- if len(bytes_value) > maximum:
- bytes_value = bytes_value[:maximum]
- return bytes_value
-
- def get_values_out_of_bounds(self, current_value: bytes) -> list[bytes]:
- invalid_values: list[bytes] = []
- if self.minLength:
- invalid_values.append(current_value[0 : self.minLength - 1])
- # if there is a maximum length, send 1 character more
- if self.maxLength:
- current_str_value = current_value.decode(encoding="utf-8")
- invalid_string_value = current_str_value if current_str_value else "x"
- # add random characters from the current value to prevent adding new characters
- while len(invalid_string_value) <= self.maxLength:
- invalid_string_value += choice(invalid_string_value)
- invalid_value = base64.b64encode(invalid_string_value.encode("utf-8"))
- invalid_values.append(invalid_value[0 : self.maxLength + 1])
- if invalid_values:
- return invalid_values
- raise ValueError
-
- def get_invalid_value_from_const_or_enum(self) -> bytes:
- valid_values = []
- if self.const is not None:
- valid_values = [self.const]
- if self.enum is not None:
- valid_values = self.enum
-
- if not valid_values:
- raise ValueError
-
- invalid_value = b""
- for value in valid_values:
- invalid_value += value + value
-
- return invalid_value
-
- def get_invalid_value_from_constraint(
- self, values_from_constraint: list[bytes]
- ) -> bytes:
- invalid_values = 2 * values_from_constraint
- invalid_value = invalid_values.pop()
- for value in invalid_values:
- invalid_value = invalid_value + value
-
- if not invalid_value:
- raise ValueError("Value invalidation yielded an empty string.")
- return invalid_value
-
- @property
- def can_be_invalidated(self) -> bool:
- if (
- self.maxLength is not None
- or self.minLength is not None
- or self.const is not None
- or self.enum is not None
- ):
- return True
- return False
-
- @property
- def annotation_string(self) -> str:
- return "bytes"
-
-
class IntegerSchema(SchemaBase[int], frozen=True):
type: Literal["integer"] = "integer"
format: str = "int32"
@@ -801,16 +700,16 @@ def annotation_string(self) -> str:
# NOTE: Workaround for cyclic PropertiesMapping / SchemaObjectTypes annotations
-def _get_properties_mapping_default() -> "PropertiesMapping":
+def _get_properties_mapping_default() -> PropertiesMapping:
return _get_empty_properties_mapping()
class ObjectSchema(SchemaBase[dict[str, JSON]], frozen=True):
type: Literal["object"] = "object"
- properties: "PropertiesMapping" = Field(
+ properties: PropertiesMapping = Field(
default_factory=_get_properties_mapping_default
)
- additionalProperties: "bool | SchemaObjectTypes" = True
+ additionalProperties: SchemaObjectTypes | bool = True
required: list[str] = []
maxProperties: int | None = None
minProperties: int | None = None
@@ -1011,12 +910,18 @@ def get_invalid_data(
valid_value=current_value, # type: ignore[arg-type]
values_from_constraint=values_from_constraint,
)
- if not isinstance(invalid_value, Ignore):
+ if isinstance(invalid_value, Ignore):
+ properties.pop(property_name)
+ logger.debug(
+ f"Property {property_name} removed since the invalid_value "
+ f"was IGNORE (received from get_invalid_value)"
+ )
+ else:
properties[property_name] = invalid_value
- logger.debug(
- f"Property {property_name} changed to {invalid_value!r} "
- f"(received from get_invalid_value)"
- )
+ logger.debug(
+ f"Property {property_name} changed to {invalid_value} "
+ f"(received from get_invalid_value)"
+ )
return properties
@property
@@ -1036,37 +941,23 @@ def annotation_string(self) -> str:
return "dict[str, JSON]"
-def get_discriminator_property(data: Any) -> str:
- if isinstance(data, dict):
- format = data.get("format")
- if format == "byte":
- return "bytes"
- return data.get("type", "unknown") # type: ignore[no-any-return]
- if hasattr(data, "format"):
- if data.format == "byte":
- return "bytes"
- return getattr(data, "type", "unknown")
-
-
ResolvedSchemaObjectTypes = Annotated[
Union[
- Annotated[BytesSchema, Tag("bytes")],
- Annotated[StringSchema, Tag("string")],
- Annotated[NullSchema, Tag("null")],
- Annotated[BooleanSchema, Tag("boolean")],
- Annotated[IntegerSchema, Tag("integer")],
- Annotated[NumberSchema, Tag("number")],
- Annotated[ArraySchema, Tag("array")], # type: ignore[type-arg]
- Annotated[ObjectSchema, Tag("object")],
+ ArraySchema, # type: ignore[type-arg]
+ BooleanSchema,
+ IntegerSchema,
+ NullSchema,
+ NumberSchema,
+ ObjectSchema,
+ StringSchema,
],
- Discriminator(get_discriminator_property),
+ Field(discriminator="type"),
]
RESOLVED_SCHEMA_CLASS_TUPLE = (
NullSchema,
BooleanSchema,
StringSchema,
- BytesSchema,
IntegerSchema,
NumberSchema,
ArraySchema,
@@ -1113,7 +1004,7 @@ def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, No
if schema.enum:
raise ValueError("allOf and models with enums are not compatible")
- if schema.properties:
+ if schema.properties.root:
properties_list.append(schema.properties)
additional_properties_list.append(schema.additionalProperties)
required_list += schema.required
diff --git a/tests/files/request_data_variations.json b/tests/files/request_data_variations.json
index 49b5bb5..923df19 100644
--- a/tests/files/request_data_variations.json
+++ b/tests/files/request_data_variations.json
@@ -160,14 +160,16 @@
"name": {
"type": "string"
}
+ },
+ "required": ["name"],
+ "additionalProperties": {
+ "type": "boolean"
}
},
{
"type": "object",
- "properties": {
- "enabled": {
- "type": "boolean"
- }
+ "additionalProperties": {
+ "type": "integer"
}
},
{
@@ -185,7 +187,8 @@
}
}
}
- }
+ },
+ "additionalProperties": false
}
]
},
diff --git a/tests/libcore/unittests/oas_model/test_can_be_invalidated.py b/tests/libcore/unittests/oas_model/test_can_be_invalidated.py
index 0dea663..cde05b3 100644
--- a/tests/libcore/unittests/oas_model/test_can_be_invalidated.py
+++ b/tests/libcore/unittests/oas_model/test_can_be_invalidated.py
@@ -4,7 +4,6 @@
from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
- BytesSchema,
IntegerSchema,
NullSchema,
NumberSchema,
@@ -38,22 +37,6 @@ def test_string_schema(self) -> None:
schema = StringSchema(enum=["eggs", "spam"])
self.assertEqual(schema.can_be_invalidated, True)
- def test_bytes_schema(self) -> None:
- schema = BytesSchema()
- self.assertEqual(schema.can_be_invalidated, False)
-
- schema = BytesSchema(maxLength=1)
- self.assertEqual(schema.can_be_invalidated, True)
-
- schema = BytesSchema(minLength=1)
- self.assertEqual(schema.can_be_invalidated, True)
-
- schema = BytesSchema(const=b"foo")
- self.assertEqual(schema.can_be_invalidated, True)
-
- schema = BytesSchema(enum=[b"eggs", b"spam"])
- self.assertEqual(schema.can_be_invalidated, True)
-
def test_integer_schema(self) -> None:
schema = IntegerSchema()
self.assertEqual(schema.can_be_invalidated, True)
diff --git a/tests/libcore/unittests/oas_model/test_get_request_data.py b/tests/libcore/unittests/oas_model/test_get_request_data.py
index 94cf1d4..73e6b5f 100644
--- a/tests/libcore/unittests/oas_model/test_get_request_data.py
+++ b/tests/libcore/unittests/oas_model/test_get_request_data.py
@@ -8,13 +8,13 @@
from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
- BytesSchema,
IntegerSchema,
NullSchema,
NumberSchema,
ObjectSchema,
OpenApiObject,
StringSchema,
+ UnionTypeSchema,
)
unittest_folder = pathlib.Path(__file__).parent.resolve()
@@ -58,10 +58,10 @@ def test_string_schema(self) -> None:
self.assertIsInstance(request_data.valid_data, str)
self.assertIsInstance(request_data.body_schema, StringSchema)
- def test_bytes_schema(self) -> None:
+ def test_string_schema_for_byte_format(self) -> None:
request_data = self._get_request_data(path="/bytes_schema")
- self.assertIsInstance(request_data.valid_data, bytes)
- self.assertIsInstance(request_data.body_schema, BytesSchema)
+ self.assertIsInstance(request_data.valid_data, str)
+ self.assertIsInstance(request_data.body_schema, StringSchema)
def test_object_schema(self) -> None:
request_data = self._get_request_data(path="/object_schema")
@@ -88,6 +88,17 @@ def test_array_with_union_schema(self) -> None:
self.assertIsInstance(request_data.valid_data, list)
self.assertIsInstance(request_data.valid_data[0], dict)
self.assertTrue(isinstance(request_data.body_schema, ArraySchema))
+ items_schema = request_data.body_schema.items
+ self.assertIsInstance(items_schema, UnionTypeSchema)
+ [resolved_schema] = items_schema.resolved_schemas
+ self.assertEqual(resolved_schema.required, ["name"])
+ self.assertIsInstance(resolved_schema.additionalProperties, UnionTypeSchema)
+ additional_properties_schemas = (
+ resolved_schema.additionalProperties.resolved_schemas
+ )
+ self.assertEqual(
+ additional_properties_schemas, [BooleanSchema(), IntegerSchema()]
+ )
if __name__ == "__main__":
diff --git a/tests/libcore/unittests/oas_model/test_get_valid_value.py b/tests/libcore/unittests/oas_model/test_get_valid_value.py
index c5d40af..d971b41 100644
--- a/tests/libcore/unittests/oas_model/test_get_valid_value.py
+++ b/tests/libcore/unittests/oas_model/test_get_valid_value.py
@@ -4,7 +4,6 @@
from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
- BytesSchema,
IntegerSchema,
NullSchema,
NumberSchema,
@@ -27,10 +26,6 @@ def test_string_schema(self) -> None:
schema = StringSchema()
self.assertIsInstance(schema.get_valid_value(), str)
- def test_bytes_schema(self) -> None:
- schema = BytesSchema()
- self.assertIsInstance(schema.get_valid_value(), bytes)
-
def test_integer_schema(self) -> None:
schema = IntegerSchema()
self.assertIsInstance(schema.get_valid_value(), int)
@@ -67,11 +62,6 @@ def test_string_schema(self) -> None:
schema = StringSchema(const=const)
self.assertEqual(schema.get_valid_value(), const)
- def test_bytes_schema(self) -> None:
- const = b"Hello world!"
- schema = BytesSchema(const=const)
- self.assertEqual(schema.get_valid_value(), const)
-
def test_integer_schema(self) -> None:
const = 42
schema = IntegerSchema(const=const)
@@ -99,11 +89,6 @@ def test_string_schema(self) -> None:
schema = StringSchema(enum=enum)
self.assertIn(schema.get_valid_value(), enum)
- def test_bytes_schema(self) -> None:
- enum = [b"eggs", b"bacon", b"spam"]
- schema = BytesSchema(enum=enum)
- self.assertIn(schema.get_valid_value(), enum)
-
def test_integer_schema(self) -> None:
enum = [1, 3, 5, 7]
schema = IntegerSchema(enum=enum)
@@ -136,14 +121,6 @@ def test_default_min_max(self) -> None:
value = schema.get_valid_value()
self.assertEqual(len(value), 36)
- schema = BytesSchema(maxLength=0)
- value = schema.get_valid_value()
- self.assertEqual(value, b"")
-
- schema = BytesSchema(minLength=36)
- value = schema.get_valid_value()
- self.assertEqual(len(value), 36)
-
def test_min_max(self) -> None:
schema = StringSchema(minLength=42, maxLength=42)
value = schema.get_valid_value()
@@ -190,49 +167,6 @@ def test_pattern(self) -> None:
)
-class TestBytesSchemaVariations(unittest.TestCase):
- def test_default_min_max(self) -> None:
- schema = BytesSchema(maxLength=0)
- value = schema.get_valid_value()
- self.assertEqual(value, b"")
-
- schema = BytesSchema(minLength=36)
- value = schema.get_valid_value()
- self.assertEqual(len(value), 36)
-
- def test_min_max(self) -> None:
- schema = BytesSchema(minLength=42, maxLength=42)
- value = schema.get_valid_value()
- self.assertEqual(len(value), 42)
-
- schema = BytesSchema(minLength=42)
- value = schema.get_valid_value()
- self.assertEqual(len(value), 42)
-
- def test_pattern(self) -> None:
- pattern = r"^[1-9][0-9]{3} ?(?!sa|sd|ss|SA|SD|SS)[A-Za-z]{2}$"
- schema = BytesSchema(pattern=pattern)
-
- with self.assertLogs(level="WARN") as logs:
- value = schema.get_valid_value()
-
- self.assertTrue(len(logs.output) > 0)
- last_log_entry = logs.output[-1]
- self.assertTrue(
- last_log_entry.startswith(
- "WARNING:RobotFramework:'pattern' is currently not supported for "
- "'byte' format strings."
- ),
- last_log_entry,
- )
- self.assertIsInstance(value, bytes)
-
- def test_byte(self) -> None:
- schema = BytesSchema(format="byte")
- value = schema.get_valid_value()
- self.assertIsInstance(value, bytes)
-
-
class TestArraySchemaVariations(unittest.TestCase):
def test_default_min_max(self) -> None:
schema = ArraySchema(items=StringSchema())
diff --git a/tests/libcore/unittests/oas_model/test_get_values_out_of_bounds.py b/tests/libcore/unittests/oas_model/test_get_values_out_of_bounds.py
index 4c022fe..79a994f 100644
--- a/tests/libcore/unittests/oas_model/test_get_values_out_of_bounds.py
+++ b/tests/libcore/unittests/oas_model/test_get_values_out_of_bounds.py
@@ -5,7 +5,6 @@
from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
- BytesSchema,
IntegerSchema,
NullSchema,
NumberSchema,
@@ -60,38 +59,6 @@ def test_string_schema(self) -> None:
schema.get_values_out_of_bounds(current_value="TTTT"), ["TT", "TTTTTT"]
)
- def test_bytes_schema(self) -> None:
- schema = BytesSchema()
- with self.assertRaises(ValueError):
- schema.get_values_out_of_bounds(
- current_value=b"minLength and maxLength not set"
- )
-
- schema = BytesSchema(minLength=0)
- with self.assertRaises(ValueError):
- schema.get_values_out_of_bounds(
- current_value=b"minLength 0 and maxLength not set"
- )
-
- schema = BytesSchema(minLength=2)
- self.assertEqual(
- schema.get_values_out_of_bounds(
- current_value=b"minLength 2 and maxLength not set"
- ),
- [b"m"],
- )
-
- schema = BytesSchema(maxLength=5)
- self.assertEqual(
- schema.get_values_out_of_bounds(current_value=b""),
- [b"eHh4eH"],
- )
-
- schema = BytesSchema(minLength=3, maxLength=5)
- self.assertEqual(
- schema.get_values_out_of_bounds(current_value=b"TTTT"), [b"TT", b"VFRUVF"]
- )
-
def test_integer_schema(self) -> None:
schema = IntegerSchema()
with self.assertRaises(ValueError):
diff --git a/tests/libcore/unittests/oas_model/test_invalid_value_from_const_or_enum.py b/tests/libcore/unittests/oas_model/test_invalid_value_from_const_or_enum.py
index bb1e284..7fdc814 100644
--- a/tests/libcore/unittests/oas_model/test_invalid_value_from_const_or_enum.py
+++ b/tests/libcore/unittests/oas_model/test_invalid_value_from_const_or_enum.py
@@ -4,7 +4,6 @@
from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
- BytesSchema,
IntegerSchema,
NullSchema,
NumberSchema,
@@ -30,11 +29,6 @@ def test_string_schema(self) -> None:
with self.assertRaises(ValueError):
schema.get_invalid_value_from_const_or_enum()
- def test_bytes_schema(self) -> None:
- schema = BytesSchema()
- with self.assertRaises(ValueError):
- schema.get_invalid_value_from_const_or_enum()
-
def test_integer_schema(self) -> None:
schema = IntegerSchema()
with self.assertRaises(ValueError):
@@ -72,11 +66,6 @@ def test_string_schema(self) -> None:
schema = StringSchema(const=const)
self.assertNotEqual(schema.get_invalid_value_from_const_or_enum(), const)
- def test_bytes_schema(self) -> None:
- const = b"Hello world!"
- schema = BytesSchema(const=const)
- self.assertNotEqual(schema.get_invalid_value_from_const_or_enum(), const)
-
def test_integer_schema(self) -> None:
const = 42
schema = IntegerSchema(const=const)
@@ -105,11 +94,6 @@ def test_string_schema(self) -> None:
schema = StringSchema(enum=enum)
self.assertNotIn(schema.get_invalid_value_from_const_or_enum(), enum)
- def test_bytes_schema(self) -> None:
- enum = [b"eggs", b"bacon", b"spam"]
- schema = BytesSchema(enum=enum)
- self.assertNotIn(schema.get_invalid_value_from_const_or_enum(), enum)
-
def test_integer_schema(self) -> None:
enum = [-1, 0, 1]
schema = IntegerSchema(enum=enum)
diff --git a/tests/libcore/unittests/oas_model/test_resolve_union_schema.py b/tests/libcore/unittests/oas_model/test_resolve_union_schema.py
index 486ba70..f9b994f 100644
--- a/tests/libcore/unittests/oas_model/test_resolve_union_schema.py
+++ b/tests/libcore/unittests/oas_model/test_resolve_union_schema.py
@@ -4,7 +4,6 @@
from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
- BytesSchema,
IntegerSchema,
NullSchema,
NumberSchema,
@@ -28,10 +27,6 @@ def test_allof_only_supports_object_schemas(self) -> None:
with self.assertRaises(NotImplementedError):
schema.resolved_schemas
- schema = UnionTypeSchema(allOf=[BytesSchema()])
- with self.assertRaises(NotImplementedError):
- schema.resolved_schemas
-
schema = UnionTypeSchema(allOf=[IntegerSchema()])
with self.assertRaises(NotImplementedError):
schema.resolved_schemas
diff --git a/tests/libcore/unittests/value_utils/test_invalid_value_from_constraint.py b/tests/libcore/unittests/value_utils/test_invalid_value_from_constraint.py
index d937105..f7718e2 100644
--- a/tests/libcore/unittests/value_utils/test_invalid_value_from_constraint.py
+++ b/tests/libcore/unittests/value_utils/test_invalid_value_from_constraint.py
@@ -4,7 +4,6 @@
from OpenApiLibCore.models.oas_models import (
ArraySchema,
BooleanSchema,
- BytesSchema,
IntegerSchema,
NullSchema,
NumberSchema,
@@ -59,28 +58,6 @@ def test_string(self) -> None:
values_from_constraint=values,
)
- def test_bytes(self) -> None:
- schema = BytesSchema()
- values = [b"foo"]
- value = schema.get_invalid_value_from_constraint(
- values_from_constraint=values,
- )
- self.assertNotIn(value, values)
- self.assertIsInstance(value, bytes)
-
- values = [b"foo", b"bar", b"baz"]
- value = schema.get_invalid_value_from_constraint(
- values_from_constraint=values,
- )
- self.assertNotIn(value, values)
- self.assertIsInstance(value, bytes)
-
- values = [b""]
- with self.assertRaises(ValueError):
- _ = schema.get_invalid_value_from_constraint(
- values_from_constraint=values,
- )
-
def test_integer(self) -> None:
schema = IntegerSchema()
values = [0]
From 3a587a8dfd01079f780fb704343a19b56489b014 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Fri, 5 Dec 2025 14:17:41 +0000
Subject: [PATCH 37/55] coverage pragma tweaks
---
src/OpenApiDriver/openapi_executors.py | 2 +-
.../data_generation/data_generation_core.py | 8 ++++----
src/OpenApiLibCore/openapi_libcore.py | 2 +-
src/OpenApiLibCore/utils/parameter_utils.py | 6 ++----
4 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/src/OpenApiDriver/openapi_executors.py b/src/OpenApiDriver/openapi_executors.py
index ba8a5b1..843de01 100644
--- a/src/OpenApiDriver/openapi_executors.py
+++ b/src/OpenApiDriver/openapi_executors.py
@@ -357,6 +357,6 @@ def get_original_data(self, url: str) -> dict[str, JSON]:
@staticmethod
def get_keyword_names() -> list[str]:
"""Curated keywords for libdoc and libspec."""
- if getenv("HIDE_INHERITED_KEYWORDS") == "true": # pragma: no cover
+ if getenv("HIDE_INHERITED_KEYWORDS") == "true": # pragma: no branch
return KEYWORD_NAMES
return KEYWORD_NAMES + LIBCORE_KEYWORD_NAMES
diff --git a/src/OpenApiLibCore/data_generation/data_generation_core.py b/src/OpenApiLibCore/data_generation/data_generation_core.py
index 8b75f7a..3d2ff80 100644
--- a/src/OpenApiLibCore/data_generation/data_generation_core.py
+++ b/src/OpenApiLibCore/data_generation/data_generation_core.py
@@ -84,8 +84,8 @@ def get_request_data(
)
if operation_spec.requestBody.mime_type:
- if "content-type" in headers:
- key_value = "content-type" # pragma: no cover
+ if "content-type" in headers: # pragma: no branch
+ key_value = "content-type"
else:
key_value = "Content-Type"
headers.update({key_value: operation_spec.requestBody.mime_type})
@@ -246,8 +246,8 @@ def get_parameter_data(
result[parameter_name] = value
continue
- if parameter.schema_ is None:
- continue # pragma: no cover
+ if parameter.schema_ is None: # pragma: no branch
+ continue
value = parameter.schema_.get_valid_value()
result[parameter_name] = value
return result
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index 898f807..53e9176 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -689,7 +689,7 @@ def recursion_limit_handler(
recursion_limit_handler=recursion_limit_handler,
) # type: ignore[no-untyped-call]
- if parser.specification is None: # pragma: no cover
+ if parser.specification is None: # pragma: no branch
raise FatalError(
"Source was loaded, but no specification was present after parsing."
)
diff --git a/src/OpenApiLibCore/utils/parameter_utils.py b/src/OpenApiLibCore/utils/parameter_utils.py
index df8fde3..5b2d6fe 100644
--- a/src/OpenApiLibCore/utils/parameter_utils.py
+++ b/src/OpenApiLibCore/utils/parameter_utils.py
@@ -73,8 +73,6 @@ def _convert_string_to_python_identifier() -> Generator[str, None, None]:
return string # pragma: no cover
converted_string = "".join(_convert_string_to_python_identifier())
- if not _is_python_safe(converted_string):
- raise ValueError(
- f"Failed to convert '{string}' to Python identifier."
- ) # pragma: no cover
+ if not _is_python_safe(converted_string): # pragma: no cover
+ raise ValueError(f"Failed to convert '{string}' to Python identifier.")
return converted_string
From aa51dce7a83467124fb515c89aa91987721f0aab Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Tue, 9 Dec 2025 10:46:32 +0000
Subject: [PATCH 38/55] Push constraint mapping to Schemas
---
src/OpenApiDriver/openapi_reader.py | 2 +-
.../data_constraints/dto_base.py | 6 +-
.../data_generation/data_generation_core.py | 24 +-
.../data_generation/data_invalidation.py | 6 +-
.../keyword_logic/validation.py | 2 +-
src/OpenApiLibCore/models/oas_models.py | 375 +++++++++++-------
src/OpenApiLibCore/models/request_data.py | 4 +-
src/OpenApiLibCore/openapi_libcore.py | 6 +-
src/OpenApiLibCore/protocols.py | 4 +
src/openapi_libgen/spec_parser.py | 2 +-
.../oas_model/test_get_invalid_data.py | 8 +-
.../oas_model/test_get_valid_value.py | 64 +--
12 files changed, 286 insertions(+), 217 deletions(-)
diff --git a/src/OpenApiDriver/openapi_reader.py b/src/OpenApiDriver/openapi_reader.py
index 22366d0..44d8afe 100644
--- a/src/OpenApiDriver/openapi_reader.py
+++ b/src/OpenApiDriver/openapi_reader.py
@@ -45,7 +45,7 @@ def get_data_from_source(self) -> list[TestCaseData]:
ignored_tests = [Test(*test) for test in getattr(self, "ignored_testcases", [])]
for path, path_item in paths.items():
- path_operations = path_item.get_operations()
+ path_operations = path_item.operations
# by reseversing the items, post/put operations come before get and delete
for method, operation_data in reversed(path_operations.items()):
diff --git a/src/OpenApiLibCore/data_constraints/dto_base.py b/src/OpenApiLibCore/data_constraints/dto_base.py
index 5599511..5cec1c2 100644
--- a/src/OpenApiLibCore/data_constraints/dto_base.py
+++ b/src/OpenApiLibCore/data_constraints/dto_base.py
@@ -17,7 +17,7 @@
ResourceRelation,
)
from OpenApiLibCore.protocols import (
- IConstraintMapping,
+ ConstraintMappingType,
IGetIdPropertyName,
)
from OpenApiLibCore.utils.id_mapping import dummy_transformer
@@ -80,7 +80,7 @@ def get_body_relations_for_error_code(
def get_constraint_mapping_dict(
mappings_module_name: str,
-) -> dict[tuple[str, str], IConstraintMapping]:
+) -> dict[tuple[str, str], ConstraintMappingType]:
try:
mappings_module = import_module(mappings_module_name)
return mappings_module.DTO_MAPPING # type: ignore[no-any-return]
@@ -92,7 +92,7 @@ def get_constraint_mapping_dict(
def get_path_mapping_dict(
mappings_module_name: str,
-) -> dict[str, IConstraintMapping]:
+) -> dict[str, ConstraintMappingType]:
try:
mappings_module = import_module(mappings_module_name)
return mappings_module.PATH_MAPPING # type: ignore[no-any-return]
diff --git a/src/OpenApiLibCore/data_generation/data_generation_core.py b/src/OpenApiLibCore/data_generation/data_generation_core.py
index 3d2ff80..eb700fe 100644
--- a/src/OpenApiLibCore/data_generation/data_generation_core.py
+++ b/src/OpenApiLibCore/data_generation/data_generation_core.py
@@ -29,7 +29,7 @@
PropertyValueConstraint,
ResourceRelation,
)
-from OpenApiLibCore.protocols import IConstraintMapping
+from OpenApiLibCore.protocols import ConstraintMappingType
from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
@@ -90,16 +90,14 @@ def get_request_data(
key_value = "Content-Type"
headers.update({key_value: operation_spec.requestBody.mime_type})
- if isinstance(body_schema, UnionTypeSchema):
- body_schema = choice(body_schema.resolved_schemas)
-
- valid_data = get_valid_json_data(
+ valid_data, schema_used_for_data_generation = get_valid_json_data(
schema=body_schema,
constraint_mapping=constraint_mapping,
operation_id=operation_spec.operationId,
)
+
constraint_mapping = _get_mapping_dataclass_from_valid_data(
- schema=body_schema,
+ schema=schema_used_for_data_generation,
constraint_mapping=constraint_mapping,
valid_data=valid_data,
method_spec=operation_spec,
@@ -108,7 +106,7 @@ def get_request_data(
return RequestData(
valid_data=valid_data,
constraint_mapping=constraint_mapping,
- body_schema=body_schema,
+ body_schema=schema_used_for_data_generation,
parameters=parameters,
params=params,
headers=headers,
@@ -116,10 +114,10 @@ def get_request_data(
def _get_mapping_dataclass_for_empty_body(
- constraint_mapping: type[IConstraintMapping] | None,
+ constraint_mapping: ConstraintMappingType | None,
mapping_cls_name: str,
method_spec: OperationObject,
-) -> type[IConstraintMapping]:
+) -> ConstraintMappingType:
cls_name = method_spec.operationId if method_spec.operationId else mapping_cls_name
base = constraint_mapping if constraint_mapping else Dto
mapping_class = make_dataclass(
@@ -132,11 +130,11 @@ def _get_mapping_dataclass_for_empty_body(
def _get_mapping_dataclass_from_valid_data(
schema: ResolvedSchemaObjectTypes,
- constraint_mapping: type[IConstraintMapping] | None,
+ constraint_mapping: ConstraintMappingType | None,
valid_data: JSON,
method_spec: OperationObject,
mapping_cls_name: str,
-) -> type[IConstraintMapping]:
+) -> ConstraintMappingType:
if not isinstance(schema, (ObjectSchema, ArraySchema)):
return _get_mapping_dataclass_for_empty_body(
constraint_mapping=constraint_mapping,
@@ -208,7 +206,7 @@ def get_mapping_cls_name(path: str, method: str) -> str:
def get_request_parameters(
- constraint_mapping: type[IConstraintMapping] | None, method_spec: OperationObject
+ constraint_mapping: ConstraintMappingType | None, method_spec: OperationObject
) -> tuple[list[ParameterObject], dict[str, Any], dict[str, str]]:
"""Get the methods parameter spec and params and headers with valid data."""
parameters = method_spec.parameters if method_spec.parameters else []
@@ -248,6 +246,6 @@ def get_parameter_data(
if parameter.schema_ is None: # pragma: no branch
continue
- value = parameter.schema_.get_valid_value()
+ value = parameter.schema_.get_valid_value()[0]
result[parameter_name] = value
return result
diff --git a/src/OpenApiLibCore/data_generation/data_invalidation.py b/src/OpenApiLibCore/data_generation/data_invalidation.py
index 9b6ce50..f93bb27 100644
--- a/src/OpenApiLibCore/data_generation/data_invalidation.py
+++ b/src/OpenApiLibCore/data_generation/data_invalidation.py
@@ -86,7 +86,6 @@ def get_invalid_body_data(
raise ValueError("Type of valid_data does not match body_schema type.")
invalid_item_data: list[JSON] = request_data.body_schema.get_invalid_data(
valid_data=request_data.valid_data,
- constraint_mapping=request_data.constraint_mapping,
status_code=status_code,
invalid_property_default_code=invalid_data_default_response,
)
@@ -96,7 +95,6 @@ def get_invalid_body_data(
raise ValueError("Type of valid_data does not match body_schema type.")
json_data = request_data.body_schema.get_invalid_data(
valid_data=request_data.valid_data,
- constraint_mapping=request_data.constraint_mapping,
status_code=status_code,
invalid_property_default_code=invalid_data_default_response,
)
@@ -128,7 +126,6 @@ def get_invalid_body_data(
raise ValueError("Type of valid_data does not match body_schema type.")
invalid_item_data = request_data.body_schema.get_invalid_data(
valid_data=request_data.valid_data,
- constraint_mapping=request_data.constraint_mapping,
status_code=status_code,
invalid_property_default_code=invalid_data_default_response,
)
@@ -138,7 +135,6 @@ def get_invalid_body_data(
raise ValueError("Type of valid_data does not match body_schema type.")
return request_data.body_schema.get_invalid_data(
valid_data=request_data.valid_data,
- constraint_mapping=request_data.constraint_mapping,
status_code=status_code,
invalid_property_default_code=invalid_data_default_response,
)
@@ -312,7 +308,7 @@ def ensure_parameter_in_parameters(
if isinstance(value_schema, UnionTypeSchema):
value_schema = choice(value_schema.resolved_schemas)
- valid_value = value_schema.get_valid_value()
+ valid_value = value_schema.get_valid_value()[0]
if (
parameter_data.in_ == "query"
and parameter_to_invalidate not in params.keys()
diff --git a/src/OpenApiLibCore/keyword_logic/validation.py b/src/OpenApiLibCore/keyword_logic/validation.py
index 4b7f6fd..a84238c 100644
--- a/src/OpenApiLibCore/keyword_logic/validation.py
+++ b/src/OpenApiLibCore/keyword_logic/validation.py
@@ -420,7 +420,7 @@ def _get_response_object(
method = method.lower()
status = str(status_code)
path_item = openapi_spec.paths[path]
- path_operations = path_item.get_operations()
+ path_operations = path_item.operations
operation_data = path_operations.get(method)
if operation_data is None:
raise ValueError(f"method '{method}' not supported for {path}.")
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index cd49efd..46ad7d9 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -1,7 +1,5 @@
from __future__ import annotations
-import base64
-import builtins
from abc import abstractmethod
from collections import ChainMap
from copy import deepcopy
@@ -26,11 +24,12 @@
from uuid import uuid4
import rstr
-from pydantic import BaseModel, Discriminator, Field, RootModel, Tag
+from pydantic import BaseModel, Field, RootModel
from robot.api import logger
from robot.libraries.BuiltIn import BuiltIn
from OpenApiLibCore.annotations import JSON
+from OpenApiLibCore.data_constraints.dto_base import Dto
from OpenApiLibCore.data_generation.localized_faker import FAKE, fake_string
from OpenApiLibCore.data_generation.value_utils import (
json_type_name_of_python_type,
@@ -42,7 +41,7 @@
IdDependency,
PropertyValueConstraint,
)
-from OpenApiLibCore.protocols import IConstraintMapping
+from OpenApiLibCore.protocols import ConstraintMappingType
from OpenApiLibCore.utils.id_mapping import dummy_transformer
from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
@@ -54,7 +53,6 @@
O = TypeVar("O")
AI = TypeVar("AI", bound=JSON)
-ConstraintMappingType = builtins.type[IConstraintMapping]
def is_object_schema(schema: SchemaObjectTypes) -> TypeGuard[ObjectSchema]:
@@ -64,13 +62,13 @@ def is_object_schema(schema: SchemaObjectTypes) -> TypeGuard[ObjectSchema]:
class SchemaBase(BaseModel, Generic[O], frozen=True):
readOnly: bool = False
writeOnly: bool = False
+ constraint_mapping: ConstraintMappingType = Dto # type: ignore[assignment]
@abstractmethod
def get_valid_value(
self,
- constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
- ) -> O: ...
+ ) -> tuple[O, SchemaObjectTypes]: ...
@abstractmethod
def get_values_out_of_bounds(self, current_value: O) -> list[O]: ...
@@ -95,7 +93,7 @@ def get_invalid_value(
value_type = getattr(self, "type")
if not isinstance(valid_value, python_type_by_json_type_name(value_type)):
- valid_value = self.get_valid_value()
+ valid_value = self.get_valid_value()[0]
if values_from_constraint:
# if IGNORE is in the values_from_constraints, the parameter needs to be
@@ -137,16 +135,21 @@ def get_invalid_value(
return choice(invalid_values)
+ def attach_constraint_mapping(
+ self, constraint_mapping: ConstraintMappingType
+ ) -> None:
+ # NOTE: https://github.com/pydantic/pydantic/issues/11495
+ self.__dict__["constraint_mapping"] = constraint_mapping
+
class NullSchema(SchemaBase[None], frozen=True):
type: Literal["null"] = "null"
def get_valid_value(
self,
- constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
- ) -> None:
- return None
+ ) -> tuple[None, NullSchema]:
+ return None, self
def get_values_out_of_bounds(self, current_value: None) -> list[None]:
raise ValueError
@@ -175,12 +178,11 @@ class BooleanSchema(SchemaBase[bool], frozen=True):
def get_valid_value(
self,
- constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
- ) -> bool:
+ ) -> tuple[bool, BooleanSchema]:
if self.const is not None:
- return self.const
- return choice([True, False])
+ return self.const, self
+ return choice([True, False]), self
def get_values_out_of_bounds(self, current_value: bool) -> list[bool]:
raise ValueError
@@ -218,18 +220,17 @@ class StringSchema(SchemaBase[str], frozen=True):
def get_valid_value(
self,
- constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
- ) -> str:
+ ) -> tuple[str, StringSchema]:
"""Generate a random string within the min/max length in the schema, if specified."""
if self.const is not None:
- return self.const
+ return self.const, self
if self.enum is not None:
- return choice(self.enum)
+ return choice(self.enum), self
# if a pattern is provided, format and min/max length can be ignored
if pattern := self.pattern:
try:
- return rstr.xeger(pattern)
+ return rstr.xeger(pattern), self
except Exception as exception:
logger.warn(
f"An error occured trying to generate a string matching the "
@@ -249,7 +250,7 @@ def get_valid_value(
value = value + fake_string(string_format=format_)
if len(value) > maximum:
value = value[:maximum]
- return value
+ return value, self
def get_values_out_of_bounds(self, current_value: str) -> list[str]:
invalid_values: list[str] = []
@@ -368,16 +369,15 @@ def _min_value(self) -> int:
def get_valid_value(
self,
- constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
- ) -> int:
+ ) -> tuple[int, IntegerSchema]:
"""Generate a random int within the min/max range of the schema, if specified."""
if self.const is not None:
- return self.const
+ return self.const, self
if self.enum is not None:
- return choice(self.enum)
+ return choice(self.enum), self
- return randint(self._min_value, self._max_value)
+ return randint(self._min_value, self._max_value), self
def get_values_out_of_bounds(self, current_value: int) -> list[int]: # pylint: disable=unused-argument
invalid_values: list[int] = []
@@ -482,16 +482,15 @@ def _min_value(self) -> float:
def get_valid_value(
self,
- constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
- ) -> float:
+ ) -> tuple[float, NumberSchema]:
"""Generate a random float within the min/max range of the schema, if specified."""
if self.const is not None:
- return self.const
+ return self.const, self
if self.enum is not None:
- return choice(self.enum)
+ return choice(self.enum), self
- return uniform(self._min_value, self._max_value)
+ return uniform(self._min_value, self._max_value), self
def get_values_out_of_bounds(self, current_value: float) -> list[float]: # pylint: disable=unused-argument
invalid_values: list[float] = []
@@ -555,14 +554,13 @@ class ArraySchema(SchemaBase[list[AI]], frozen=True):
def get_valid_value(
self,
- constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
- ) -> list[AI]:
+ ) -> tuple[list[AI], ArraySchema[AI]]:
if self.const is not None:
- return self.const
+ return self.const, self
if self.enum is not None:
- return choice(self.enum)
+ return choice(self.enum), self
minimum = self.minItems if self.minItems is not None else 0
maximum = self.maxItems if self.maxItems is not None else 1
@@ -571,9 +569,9 @@ def get_valid_value(
value: list[AI] = []
number_of_items_to_generate = randint(minimum, maximum)
for _ in range(number_of_items_to_generate):
- item_value = cast("AI", self.items.get_valid_value())
+ item_value = cast("AI", self.items.get_valid_value()[0])
value.append(item_value)
- return value
+ return value, self
def get_values_out_of_bounds(self, current_value: list[AI]) -> list[list[AI]]:
invalid_values: list[list[AI]] = []
@@ -585,10 +583,10 @@ def get_values_out_of_bounds(self, current_value: list[AI]) -> list[list[AI]]:
if self.maxItems is not None:
invalid_value = []
if not current_value:
- current_value = self.get_valid_value()
+ current_value = self.get_valid_value()[0]
if not current_value:
- current_value = [self.items.get_valid_value()] # type: ignore[list-item]
+ current_value = [self.items.get_valid_value()[0]] # type: ignore[list-item]
while len(invalid_value) <= self.maxItems:
invalid_value.append(choice(current_value))
@@ -633,14 +631,13 @@ def get_invalid_value_from_constraint(
def get_invalid_data(
self,
valid_data: list[AI],
- constraint_mapping: ConstraintMappingType,
status_code: int,
invalid_property_default_code: int,
) -> list[AI]:
"""Return a data set with one of the properties set to an invalid value or type."""
invalid_values: list[list[AI]] = []
- relations = constraint_mapping.get_body_relations_for_error_code(
+ relations = self.constraint_mapping.get_body_relations_for_error_code(
error_code=status_code
)
# TODO: handle relations applicable to arrays / lists
@@ -663,11 +660,10 @@ def get_invalid_data(
valid_item = (
data_to_invalidate.pop()
if valid_data
- else self.items.get_valid_value()
+ else self.items.get_valid_value()[0]
)
invalid_item = self.items.get_invalid_data(
valid_data=valid_item, # type: ignore[arg-type]
- constraint_mapping=constraint_mapping,
status_code=status_code,
invalid_property_default_code=invalid_property_default_code,
)
@@ -719,47 +715,40 @@ class ObjectSchema(SchemaBase[dict[str, JSON]], frozen=True):
def get_valid_value(
self,
- constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
- ) -> dict[str, JSON]:
+ ) -> tuple[dict[str, JSON], ObjectSchema]:
if self.const is not None:
- return self.const
+ return self.const, self
if self.enum is not None:
- return choice(self.enum)
+ return choice(self.enum), self
json_data: dict[str, Any] = {}
- property_names = self._get_property_names_to_process(
- constraint_mapping=constraint_mapping
- )
+ property_names = self._get_property_names_to_process()
for property_name in property_names:
property_schema = self.properties.root[property_name]
if property_schema.readOnly:
continue
- json_data[property_name] = get_data_for_property(
+ json_data[property_name] = self._get_data_for_property(
property_name=property_name,
property_schema=property_schema,
- constraint_mapping=constraint_mapping,
operation_id=operation_id,
)
- return json_data
+ return json_data, self
- def _get_property_names_to_process(
- self,
- constraint_mapping: ConstraintMappingType | None,
- ) -> list[str]:
+ def _get_property_names_to_process(self) -> list[str]:
property_names = []
properties = {} if self.properties is None else self.properties.root
for property_name in properties:
# register the oas_name
_ = get_safe_name_for_oas_name(property_name)
- if constrained_values := get_constrained_values(
- constraint_mapping=constraint_mapping, property_name=property_name
+ if constrained_values := self._get_constrained_values(
+ property_name=property_name
):
# do not add properties that are configured to be ignored
if IGNORE in constrained_values: # type: ignore[comparison-overlap]
@@ -780,6 +769,59 @@ def _get_property_names_to_process(
return property_names
+ def _get_data_for_property(
+ self,
+ property_name: str,
+ property_schema: SchemaObjectTypes,
+ operation_id: str | None,
+ ) -> JSON:
+ if constrained_values := self._get_constrained_values(
+ property_name=property_name
+ ):
+ constrained_value = choice(constrained_values)
+ # Check if the chosen value is a nested constraint_mapping; since a
+ # mapping is never instantiated, we can use isinstance(..., type) for this.
+ if isinstance(constrained_value, type):
+ schema = get_schema_for_value_constrained_by_constraint_mapping(
+ property_schema=property_schema,
+ constraint_mapping=constrained_value,
+ )
+ return get_valid_json_data(
+ schema=schema,
+ constraint_mapping=constrained_value,
+ operation_id=operation_id,
+ )[0]
+ return constrained_value
+
+ if (
+ dependent_id := get_dependent_id(
+ constraint_mapping=self.constraint_mapping,
+ property_name=property_name,
+ operation_id=operation_id,
+ )
+ ) is not None:
+ return dependent_id
+
+ return get_valid_json_data(
+ schema=property_schema,
+ constraint_mapping=self.constraint_mapping,
+ )[0]
+
+ def _get_constrained_values(
+ self, property_name: str
+ ) -> list[JSON | ConstraintMappingType]:
+ relations = self.constraint_mapping.get_relations()
+ values_list = [
+ c.values
+ for c in relations
+ if (
+ isinstance(c, PropertyValueConstraint)
+ and c.property_name == property_name
+ )
+ ]
+ # values should be empty or contain 1 list of allowed values
+ return values_list.pop() if values_list else []
+
def get_values_out_of_bounds(
self, current_value: Mapping[str, JSON]
) -> list[dict[str, JSON]]:
@@ -826,17 +868,17 @@ def get_invalid_value_from_constraint(
def get_invalid_data(
self,
valid_data: dict[str, JSON],
- constraint_mapping: ConstraintMappingType,
status_code: int,
invalid_property_default_code: int,
) -> dict[str, JSON]:
"""Return a data set with one of the properties set to an invalid value or type."""
properties: dict[str, JSON] = deepcopy(valid_data)
- relations = constraint_mapping.get_body_relations_for_error_code(
+ relations = self.constraint_mapping.get_body_relations_for_error_code(
error_code=status_code
)
property_names = [r.property_name for r in relations]
+
if status_code == invalid_property_default_code:
# add all properties defined in the schema, including optional properties
property_names.extend((self.properties.root.keys()))
@@ -897,7 +939,7 @@ def get_invalid_data(
# there may not be a current_value when invalidating an optional property
current_value = properties.get(property_name, SENTINEL)
if current_value is SENTINEL:
- current_value = value_schema.get_valid_value()
+ current_value = value_schema.get_valid_value()[0]
values_from_constraint = [
r.values[0]
@@ -924,6 +966,14 @@ def get_invalid_data(
)
return properties
+ def contains_properties(self, property_names: list[str]) -> bool:
+ if self.properties is None:
+ return False
+ for property_name in property_names:
+ if property_name not in self.properties.root:
+ return False
+ return True
+
@property
def can_be_invalidated(self) -> bool:
if (
@@ -972,11 +1022,13 @@ class UnionTypeSchema(SchemaBase[JSON], frozen=True):
def get_valid_value(
self,
- constraint_mapping: ConstraintMappingType | None = None,
operation_id: str | None = None,
- ) -> JSON:
- chosen_schema = choice(self.resolved_schemas)
- return chosen_schema.get_valid_value()
+ ) -> tuple[JSON, ResolvedSchemaObjectTypes]:
+ schema = get_schema_for_value_constrained_by_constraint_mapping(
+ property_schema=self,
+ constraint_mapping=self.constraint_mapping,
+ )
+ return schema.get_valid_value(operation_id=operation_id)
def get_values_out_of_bounds(self, current_value: JSON) -> list[JSON]:
raise ValueError
@@ -1083,6 +1135,13 @@ class ParameterObject(BaseModel):
required: bool = False
description: str = ""
schema_: SchemaObjectTypes | None = Field(None, alias="schema")
+ constraint_mapping: ConstraintMappingType | None = None
+
+ def attach_constraint_mapping(
+ self, constraint_mapping: ConstraintMappingType
+ ) -> None:
+ if self.schema_:
+ self.schema_.attach_constraint_mapping(constraint_mapping)
class MediaTypeObject(BaseModel):
@@ -1122,6 +1181,13 @@ def _json_schemas(self) -> dict[str, SchemaObjectTypes]:
}
return json_schemas
+ def attach_constraint_mapping(
+ self, constraint_mapping: ConstraintMappingType
+ ) -> None:
+ for media_object_type in self.content.values():
+ if media_object_type and media_object_type.schema_:
+ media_object_type.schema_.attach_constraint_mapping(constraint_mapping)
+
class HeaderObject(BaseModel): ...
@@ -1144,11 +1210,21 @@ class OperationObject(BaseModel):
parameters: list[ParameterObject] = []
requestBody: RequestBodyObject | None = None
responses: dict[str, ResponseObject] = {}
- constraint_mapping: type[IConstraintMapping] | None = None
+ constraint_mapping: ConstraintMappingType | None = None
def update_parameters(self, parameters: list[ParameterObject]) -> None:
self.parameters.extend(parameters)
+ def attach_constraint_mappings(self) -> None:
+ if not self.constraint_mapping:
+ return
+
+ if self.requestBody:
+ self.requestBody.attach_constraint_mapping(self.constraint_mapping)
+
+ for parameter_object in self.parameters:
+ parameter_object.attach_constraint_mapping(self.constraint_mapping)
+
class PathItemObject(BaseModel):
get: OperationObject | None = None
@@ -1159,13 +1235,14 @@ class PathItemObject(BaseModel):
summary: str = ""
description: str = ""
parameters: list[ParameterObject] = []
- constraint_mapping: IConstraintMapping | None = None
+ constraint_mapping: ConstraintMappingType | None = None
id_mapper: tuple[str, Callable[[str], str]] = (
"id",
dummy_transformer,
)
- def get_operations(self) -> dict[str, OperationObject]:
+ @property
+ def operations(self) -> dict[str, OperationObject]:
return {
k: v for k, v in self.__dict__.items() if isinstance(v, OperationObject)
}
@@ -1174,10 +1251,14 @@ def update_operation_parameters(self) -> None:
if not self.parameters:
return
- operations_to_update = self.get_operations()
+ operations_to_update = self.operations
for operation_object in operations_to_update.values():
operation_object.update_parameters(self.parameters)
+ def attach_constraint_mappings(self) -> None:
+ for operation_object in self.operations.values():
+ operation_object.attach_constraint_mappings()
+
class InfoObject(BaseModel):
title: str
@@ -1190,64 +1271,91 @@ class OpenApiObject(BaseModel):
info: InfoObject
paths: dict[str, PathItemObject]
- def model_post_init(self, context: object) -> None:
- for path_object in self.paths.values():
- path_object.update_operation_parameters()
-
-# TODO: can this be refactored away?
def get_valid_json_data(
schema: SchemaObjectTypes,
- constraint_mapping: type[IConstraintMapping] | None,
+ constraint_mapping: ConstraintMappingType | None,
operation_id: str | None = None,
-) -> JSON:
- if isinstance(schema, UnionTypeSchema):
- chosen_schema = choice(schema.resolved_schemas)
- return get_valid_json_data(
- schema=chosen_schema,
+) -> tuple[JSON, ResolvedSchemaObjectTypes]:
+ """
+ Returns a tuple of valid JSON for the given schema and constraints and the schema
+ which was used to generate the JSON.
+
+ If the provided schema is not a UnionTypeSchema the schema returned will be the
+ same as the provided schema.
+
+ If the provided schema is a UnionTypeSchema, the resolved or selected schema that
+ was used to generated the JSON data is returned.
+ """
+ if constraint_mapping:
+ schema = get_schema_for_value_constrained_by_constraint_mapping(
+ property_schema=schema,
constraint_mapping=constraint_mapping,
- operation_id=operation_id,
)
- return schema.get_valid_value(
- constraint_mapping=constraint_mapping, operation_id=operation_id
+ return schema.get_valid_value(operation_id=operation_id)
+
+
+def get_schema_for_value_constrained_by_constraint_mapping(
+ property_schema: SchemaObjectTypes,
+ constraint_mapping: ConstraintMappingType,
+) -> SchemaObjectTypes:
+ relations = (
+ constraint_mapping.get_relations()
+ + constraint_mapping.get_parameter_relations()
+ )
+ mandatory_property_names = [
+ relation.property_name
+ for relation in relations
+ if getattr(relation, "treat_as_mandatory", False)
+ ]
+ return _get_schema_for_constraint_mapping(
+ property_schema=property_schema,
+ mandatory_property_names=mandatory_property_names,
)
-# TODO: move to ObjectSchema?
-def get_data_for_property(
- property_name: str,
+def _get_schema_for_constraint_mapping(
property_schema: SchemaObjectTypes,
- constraint_mapping: ConstraintMappingType | None,
- operation_id: str | None,
-) -> JSON:
- if constrained_values := get_constrained_values(
- constraint_mapping=constraint_mapping, property_name=property_name
- ):
- constrained_value = choice(constrained_values)
- # Check if the chosen value is a nested constraint_mapping; since a
- # mapping is never instantiated, we can use isinstance(..., type) for this.
- if isinstance(constrained_value, type):
- return get_value_constrained_by_nested_constraint_mapping(
- property_schema=property_schema,
- nested_constraint_mapping=constrained_value,
- operation_id=operation_id,
+ mandatory_property_names: list[str],
+) -> SchemaObjectTypes:
+ if not isinstance(property_schema, (ObjectSchema, UnionTypeSchema)):
+ return property_schema
+
+ if isinstance(property_schema, ObjectSchema):
+ if mandatory_property_names and not property_schema.contains_properties(
+ mandatory_property_names
+ ):
+ raise ValueError(
+ "No schema found that contains all properties that should be treated as mandatory."
)
- return constrained_value
+ return property_schema
- if (
- dependent_id := get_dependent_id(
- constraint_mapping=constraint_mapping,
- property_name=property_name,
- operation_id=operation_id,
- )
- ) is not None:
- return dependent_id
+ if not mandatory_property_names:
+ return choice(property_schema.resolved_schemas)
- return get_valid_json_data(
- schema=property_schema,
- constraint_mapping=None,
- )
+ valid_candidates: list[ObjectSchema] = []
+
+ object_schemas = [
+ schema
+ for schema in property_schema.resolved_schemas
+ if isinstance(schema, ObjectSchema)
+ ]
+ for candidate_schema in object_schemas:
+ try:
+ valid_candidate = _get_schema_for_constraint_mapping(
+ property_schema=candidate_schema,
+ mandatory_property_names=mandatory_property_names,
+ )
+ valid_candidates.append(valid_candidate) # type: ignore[arg-type]
+ except ValueError:
+ pass
+
+ if not valid_candidates:
+ raise ValueError(
+ "No schema found that contains all properties that should be treated as mandatory."
+ )
+ return choice(valid_candidates)
# TODO: move to keyword_logic?
@@ -1281,42 +1389,3 @@ def get_dependent_id(
)
logger.debug(f"get_dependent_id for {id_get_path} returned {valid_id}")
return valid_id
-
-
-def get_value_constrained_by_nested_constraint_mapping(
- property_schema: SchemaObjectTypes,
- nested_constraint_mapping: ConstraintMappingType,
- operation_id: str | None,
-) -> JSON:
- nested_schema = get_schema_for_nested_constraint_mapping(
- property_schema=property_schema
- )
- nested_value = get_valid_json_data(
- schema=nested_schema,
- constraint_mapping=nested_constraint_mapping,
- operation_id=operation_id,
- )
- return nested_value
-
-
-def get_schema_for_nested_constraint_mapping(
- property_schema: SchemaObjectTypes,
-) -> SchemaObjectTypes:
- if isinstance(property_schema, UnionTypeSchema):
- chosen_schema = choice(property_schema.resolved_schemas)
- return get_schema_for_nested_constraint_mapping(chosen_schema)
-
- return property_schema
-
-
-def get_constrained_values(
- constraint_mapping: ConstraintMappingType | None, property_name: str
-) -> list[JSON | ConstraintMappingType]:
- relations = constraint_mapping.get_relations() if constraint_mapping else []
- values_list = [
- c.values
- for c in relations
- if (isinstance(c, PropertyValueConstraint) and c.property_name == property_name)
- ]
- # values should be empty or contain 1 list of allowed values
- return values_list.pop() if values_list else []
diff --git a/src/OpenApiLibCore/models/request_data.py b/src/OpenApiLibCore/models/request_data.py
index b4193e1..b21711b 100644
--- a/src/OpenApiLibCore/models/request_data.py
+++ b/src/OpenApiLibCore/models/request_data.py
@@ -12,7 +12,7 @@
ResolvedSchemaObjectTypes,
UnionTypeSchema,
)
-from OpenApiLibCore.protocols import IConstraintMapping
+from OpenApiLibCore.protocols import ConstraintMappingType
@dataclass
@@ -56,7 +56,7 @@ class RequestData:
"""Helper class to manage parameters used when making requests."""
valid_data: JSON
- constraint_mapping: type[IConstraintMapping]
+ constraint_mapping: ConstraintMappingType
body_schema: ResolvedSchemaObjectTypes | None = None
parameters: list[ParameterObject] = field(default_factory=list)
params: dict[str, JSON] = field(default_factory=dict)
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index 53e9176..4f33d7e 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -328,7 +328,7 @@ def get_json_data_with_conflict(
base_url=self.base_url,
method=method,
json_data=json_data,
- constraint_mapping=constraint_mapping,
+ constraint_mapping=constraint_mapping, # FIXME: the model should have this information
conflict_status_code=conflict_status_code,
)
@@ -594,7 +594,7 @@ def _register_path_parameter(parameter_object: ParameterObject) -> None:
_ = get_safe_name_for_oas_name(parameter_object.name)
for path_item in paths_data.values():
- operations = path_item.get_operations()
+ operations = path_item.operations
for operation in operations.values():
if parameters := operation.parameters:
for parameter in parameters:
@@ -625,6 +625,8 @@ def _attach_user_mappings(self, spec_model: OpenApiObject) -> OpenApiObject:
for path, path_item in spec_model.paths.items():
mapper = self.get_id_property_name(path)
path_item.id_mapper = mapper
+ path_item.update_operation_parameters()
+ path_item.attach_constraint_mappings()
return spec_model
diff --git a/src/OpenApiLibCore/protocols.py b/src/OpenApiLibCore/protocols.py
index 2b7fbce..c050414 100644
--- a/src/OpenApiLibCore/protocols.py
+++ b/src/OpenApiLibCore/protocols.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+import builtins
from typing import Any, Callable, Protocol
from openapi_core.contrib.requests import (
@@ -66,3 +67,6 @@ def get_relations() -> list[ResourceRelation]: ...
def get_body_relations_for_error_code(
cls, error_code: int
) -> list[ResourceRelation]: ...
+
+
+ConstraintMappingType = builtins.type[IConstraintMapping]
diff --git a/src/openapi_libgen/spec_parser.py b/src/openapi_libgen/spec_parser.py
index 0900876..8b4ad8f 100644
--- a/src/openapi_libgen/spec_parser.py
+++ b/src/openapi_libgen/spec_parser.py
@@ -47,7 +47,7 @@ def get_path_items(
paths: dict[str, PathItemObject],
) -> Generator[OperationDetails, None, None]:
for path, path_item_object in paths.items():
- operations = path_item_object.get_operations()
+ operations = path_item_object.operations
for method, operation_object in operations.items():
operation_details = OperationDetails(
path=path,
diff --git a/tests/libcore/unittests/oas_model/test_get_invalid_data.py b/tests/libcore/unittests/oas_model/test_get_invalid_data.py
index 141f3c9..18a3b2f 100644
--- a/tests/libcore/unittests/oas_model/test_get_invalid_data.py
+++ b/tests/libcore/unittests/oas_model/test_get_invalid_data.py
@@ -23,10 +23,10 @@ def get_relations() -> list[ResourceRelation]:
class TestArraySchema(unittest.TestCase):
def test_raises_for_no_matching_status_code(self) -> None:
schema = ArraySchema(items=IntegerSchema())
+ schema.attach_constraint_mapping(ArrayConstraint)
with self.assertRaises(ValueError) as context:
_ = schema.get_invalid_data(
valid_data=[42],
- constraint_mapping=ArrayConstraint,
status_code=500,
invalid_property_default_code=422,
)
@@ -37,10 +37,10 @@ def test_raises_for_no_matching_status_code(self) -> None:
def test_status_code_is_default_code_without_constraints_raises(self) -> None:
schema = ArraySchema(items=IntegerSchema(maximum=43))
+ schema.attach_constraint_mapping(Dto)
with self.assertRaises(ValueError) as context:
_ = schema.get_invalid_data(
valid_data=[42],
- constraint_mapping=Dto,
status_code=422,
invalid_property_default_code=422,
)
@@ -51,9 +51,9 @@ def test_status_code_is_default_code_without_constraints_raises(self) -> None:
def test_status_code_is_default_code(self) -> None:
schema = ArraySchema(items=IntegerSchema(maximum=43), minItems=1)
+ schema.attach_constraint_mapping(Dto)
invalid_data = schema.get_invalid_data(
valid_data=[42],
- constraint_mapping=Dto,
status_code=422,
invalid_property_default_code=422,
)
@@ -61,9 +61,9 @@ def test_status_code_is_default_code(self) -> None:
valid_value = [42]
schema = ArraySchema(items=IntegerSchema(maximum=43), const=valid_value)
+ schema.attach_constraint_mapping(Dto)
invalid_data = schema.get_invalid_data(
valid_data=valid_value,
- constraint_mapping=Dto,
status_code=422,
invalid_property_default_code=422,
)
diff --git a/tests/libcore/unittests/oas_model/test_get_valid_value.py b/tests/libcore/unittests/oas_model/test_get_valid_value.py
index d971b41..28a4297 100644
--- a/tests/libcore/unittests/oas_model/test_get_valid_value.py
+++ b/tests/libcore/unittests/oas_model/test_get_valid_value.py
@@ -16,142 +16,142 @@
class TestDefaults(unittest.TestCase):
def test_null_schema(self) -> None:
schema = NullSchema()
- self.assertEqual(schema.get_valid_value(), None)
+ self.assertEqual(schema.get_valid_value()[0], None)
def test_boolean_schema(self) -> None:
schema = BooleanSchema()
- self.assertIsInstance(schema.get_valid_value(), bool)
+ self.assertIsInstance(schema.get_valid_value()[0], bool)
def test_string_schema(self) -> None:
schema = StringSchema()
- self.assertIsInstance(schema.get_valid_value(), str)
+ self.assertIsInstance(schema.get_valid_value()[0], str)
def test_integer_schema(self) -> None:
schema = IntegerSchema()
- self.assertIsInstance(schema.get_valid_value(), int)
+ self.assertIsInstance(schema.get_valid_value()[0], int)
def test_number_schema(self) -> None:
schema = NumberSchema()
- self.assertIsInstance(schema.get_valid_value(), float)
+ self.assertIsInstance(schema.get_valid_value()[0], float)
def test_array_schema(self) -> None:
schema = ArraySchema(items=IntegerSchema())
- value = schema.get_valid_value()
+ value = schema.get_valid_value()[0]
self.assertIsInstance(value, list)
if value:
self.assertIsInstance(value[0], int)
def test_object_schema(self) -> None:
schema = ObjectSchema()
- value = schema.get_valid_value()
+ value = schema.get_valid_value()[0]
self.assertIsInstance(value, dict)
def test_union_schema(self) -> None:
schema = UnionTypeSchema(oneOf=[BooleanSchema(), IntegerSchema()])
- self.assertIsInstance(schema.get_valid_value(), int)
+ self.assertIsInstance(schema.get_valid_value()[0], int)
class TestGetValidValueFromConst(unittest.TestCase):
def test_boolean_schema(self) -> None:
const = False
schema = BooleanSchema(const=const)
- self.assertEqual(schema.get_valid_value(), const)
+ self.assertEqual(schema.get_valid_value()[0], const)
def test_string_schema(self) -> None:
const = "Hello world!"
schema = StringSchema(const=const)
- self.assertEqual(schema.get_valid_value(), const)
+ self.assertEqual(schema.get_valid_value()[0], const)
def test_integer_schema(self) -> None:
const = 42
schema = IntegerSchema(const=const)
- self.assertEqual(schema.get_valid_value(), const)
+ self.assertEqual(schema.get_valid_value()[0], const)
def test_number_schema(self) -> None:
const = 3.14
schema = NumberSchema(const=const)
- self.assertEqual(schema.get_valid_value(), const)
+ self.assertEqual(schema.get_valid_value()[0], const)
def test_array_schema(self) -> None:
const = ["foo", "bar"]
schema = ArraySchema(items=StringSchema(), const=const)
- self.assertEqual(schema.get_valid_value(), const)
+ self.assertEqual(schema.get_valid_value()[0], const)
def test_object_schema(self) -> None:
const = {"foo": 42, "bar": 3.14}
schema = ObjectSchema(const=const)
- self.assertEqual(schema.get_valid_value(), const)
+ self.assertEqual(schema.get_valid_value()[0], const)
class TestGetValidValueFromEnum(unittest.TestCase):
def test_string_schema(self) -> None:
enum = ["eggs", "bacon", "spam"]
schema = StringSchema(enum=enum)
- self.assertIn(schema.get_valid_value(), enum)
+ self.assertIn(schema.get_valid_value()[0], enum)
def test_integer_schema(self) -> None:
enum = [1, 3, 5, 7]
schema = IntegerSchema(enum=enum)
- self.assertIn(schema.get_valid_value(), enum)
+ self.assertIn(schema.get_valid_value()[0], enum)
def test_number_schema(self) -> None:
enum = [0.1, 0.01, 0.001]
schema = NumberSchema(enum=enum)
- self.assertIn(schema.get_valid_value(), enum)
+ self.assertIn(schema.get_valid_value()[0], enum)
def test_array_schema(self) -> None:
enum = [["foo", "bar"], ["eggs", "bacon", "spam"]]
schema = ArraySchema(items=StringSchema(), enum=enum)
- self.assertIn(schema.get_valid_value(), enum)
+ self.assertIn(schema.get_valid_value()[0], enum)
def test_object_schema(self) -> None:
enum: list[dict[str, int | float]] = [{"foo": 42, "bar": 3.14}]
schema = ObjectSchema(enum=enum)
- value = schema.get_valid_value()
+ value = schema.get_valid_value()[0]
self.assertIn(value, enum)
class TestStringSchemaVariations(unittest.TestCase):
def test_default_min_max(self) -> None:
schema = StringSchema(maxLength=0)
- value = schema.get_valid_value()
+ value = schema.get_valid_value()[0]
self.assertEqual(value, "")
schema = StringSchema(minLength=36)
- value = schema.get_valid_value()
+ value = schema.get_valid_value()[0]
self.assertEqual(len(value), 36)
def test_min_max(self) -> None:
schema = StringSchema(minLength=42, maxLength=42)
- value = schema.get_valid_value()
+ value = schema.get_valid_value()[0]
self.assertEqual(len(value), 42)
schema = StringSchema(minLength=42)
- value = schema.get_valid_value()
+ value = schema.get_valid_value()[0]
self.assertEqual(len(value), 42)
def test_datetime(self) -> None:
schema = StringSchema(format="date-time")
- value = schema.get_valid_value()
+ value = schema.get_valid_value()[0]
matcher = r"^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)((-(\d{2}):(\d{2})|Z)?)$"
self.assertRegex(value, matcher)
def test_date(self) -> None:
schema = StringSchema(format="date")
- value = schema.get_valid_value()
+ value = schema.get_valid_value()[0]
matcher = r"^(\d{4})-(\d{2})-(\d{2})$"
self.assertRegex(value, matcher)
def test_pattern(self) -> None:
pattern = r"^[1-9][0-9]{3} ?(?!sa|sd|ss|SA|SD|SS)[A-Za-z]{2}$"
schema = StringSchema(pattern=pattern)
- value = schema.get_valid_value()
+ value = schema.get_valid_value()[0]
self.assertRegex(value, pattern)
pattern = r"^(?:[\p{L}\p{Mn}\p{Nd}.,()'-]+(?:['.’ ]|\s?[&\/\p{Pd}]\s?)?)+[\p{L}\p{Mn}\p{Nd}]\.?$"
schema = StringSchema(pattern=pattern)
with self.assertLogs(level="WARN") as logs:
- value = schema.get_valid_value()
+ value = schema.get_valid_value()[0]
self.assertTrue(len(logs.output) > 0)
last_log_entry = logs.output[-1]
@@ -170,25 +170,25 @@ def test_pattern(self) -> None:
class TestArraySchemaVariations(unittest.TestCase):
def test_default_min_max(self) -> None:
schema = ArraySchema(items=StringSchema())
- value = schema.get_valid_value()
+ value = schema.get_valid_value()[0]
self.assertIn(len(value), (0, 1))
schema = {"maxItems": 0, "items": {"type": "string"}}
schema = ArraySchema(items=StringSchema(), maxItems=0)
- value = schema.get_valid_value()
+ value = schema.get_valid_value()[0]
self.assertEqual(value, [])
def test_min_max(self) -> None:
schema = ArraySchema(items=StringSchema(), maxItems=3, minItems=2)
- value = schema.get_valid_value()
+ value = schema.get_valid_value()[0]
self.assertIn(len(value), (2, 3))
schema = ArraySchema(items=StringSchema(), minItems=5)
- value = schema.get_valid_value()
+ value = schema.get_valid_value()[0]
self.assertEqual(len(value), 5)
schema = ArraySchema(items=StringSchema(), minItems=7, maxItems=5)
- value = schema.get_valid_value()
+ value = schema.get_valid_value()[0]
self.assertEqual(len(value), 7)
From f956c418b96f14735a3887177e14e7e4cc82959c Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Tue, 9 Dec 2025 13:47:51 +0000
Subject: [PATCH 39/55] Further refactored, added tests
---
.../data_generation/data_generation_core.py | 7 +-
src/OpenApiLibCore/models/oas_models.py | 104 ++++++++++--------
.../suites/test_get_request_data.robot | 5 +
.../suites/test_validate_response.robot | 2 +-
tests/server/testserver.py | 7 +-
.../user_implemented/custom_user_mappings.py | 22 +++-
6 files changed, 91 insertions(+), 56 deletions(-)
diff --git a/src/OpenApiLibCore/data_generation/data_generation_core.py b/src/OpenApiLibCore/data_generation/data_generation_core.py
index eb700fe..27c2ead 100644
--- a/src/OpenApiLibCore/data_generation/data_generation_core.py
+++ b/src/OpenApiLibCore/data_generation/data_generation_core.py
@@ -22,7 +22,6 @@
ParameterObject,
ResolvedSchemaObjectTypes,
UnionTypeSchema,
- get_valid_json_data,
)
from OpenApiLibCore.models.request_data import RequestData
from OpenApiLibCore.models.resource_relations import (
@@ -90,10 +89,8 @@ def get_request_data(
key_value = "Content-Type"
headers.update({key_value: operation_spec.requestBody.mime_type})
- valid_data, schema_used_for_data_generation = get_valid_json_data(
- schema=body_schema,
- constraint_mapping=constraint_mapping,
- operation_id=operation_spec.operationId,
+ valid_data, schema_used_for_data_generation = body_schema.get_valid_value(
+ operation_id=operation_spec.operationId
)
constraint_mapping = _get_mapping_dataclass_from_valid_data(
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index 46ad7d9..8817d36 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -782,15 +782,10 @@ def _get_data_for_property(
# Check if the chosen value is a nested constraint_mapping; since a
# mapping is never instantiated, we can use isinstance(..., type) for this.
if isinstance(constrained_value, type):
- schema = get_schema_for_value_constrained_by_constraint_mapping(
- property_schema=property_schema,
- constraint_mapping=constrained_value,
- )
- return get_valid_json_data(
- schema=schema,
- constraint_mapping=constrained_value,
- operation_id=operation_id,
- )[0]
+ property_schema.attach_constraint_mapping(constrained_value)
+ valid_value, _ = property_schema.get_valid_value(operation_id=operation_id)
+ return valid_value
+
return constrained_value
if (
@@ -802,10 +797,10 @@ def _get_data_for_property(
) is not None:
return dependent_id
- return get_valid_json_data(
- schema=property_schema,
- constraint_mapping=self.constraint_mapping,
- )[0]
+ # Constraints are mapped to endpoints; they are not attached to the property
+ # value schemas so update the schema before value generation
+ property_schema.attach_constraint_mapping(self.constraint_mapping)
+ return property_schema.get_valid_value(operation_id=operation_id)[0]
def _get_constrained_values(
self, property_name: str
@@ -917,11 +912,19 @@ def get_invalid_data(
invalid_value_from_constraint
and invalid_value_from_constraint[0] is not NOT_SET
):
- properties[property_name] = invalid_value_from_constraint[0]
- logger.debug(
- f"Using invalid_value {invalid_value_from_constraint[0]} to "
- f"invalidate property {property_name}"
- )
+ invalid_value = invalid_value_from_constraint[0]
+ if isinstance(invalid_value, Ignore):
+ properties.pop(property_name)
+ logger.debug(
+ f"Property {property_name} removed since the invalid_value "
+ f"was IGNORE (received from get_invalid_value)"
+ )
+ else:
+ properties[property_name] = invalid_value
+ logger.debug(
+ f"Using invalid_value {invalid_value_from_constraint[0]} to "
+ f"invalidate property {property_name}"
+ )
return properties
value_schema = self.properties.root[property_name]
@@ -1024,11 +1027,44 @@ def get_valid_value(
self,
operation_id: str | None = None,
) -> tuple[JSON, ResolvedSchemaObjectTypes]:
- schema = get_schema_for_value_constrained_by_constraint_mapping(
- property_schema=self,
- constraint_mapping=self.constraint_mapping,
+ relations = (
+ self.constraint_mapping.get_relations()
+ + self.constraint_mapping.get_parameter_relations()
)
- return schema.get_valid_value(operation_id=operation_id)
+ constrained_property_names = [
+ relation.property_name
+ for relation in relations
+ ]
+
+ if not constrained_property_names:
+ chosen_schema = choice(self.resolved_schemas)
+ return chosen_schema.get_valid_value(operation_id=operation_id)
+
+ valid_values = []
+ valid_schemas = []
+ for candidate in self.resolved_schemas:
+ if isinstance(candidate, ObjectSchema):
+ if candidate.contains_properties(constrained_property_names):
+ valid_schemas.append(candidate)
+
+ if isinstance(candidate, UnionTypeSchema):
+ candidate.attach_constraint_mapping(self.constraint_mapping)
+ try:
+ valid_value = candidate.get_valid_value(operation_id=operation_id)
+ valid_values.append(valid_value)
+ except ValueError:
+ pass
+ for valid_schema in valid_schemas:
+ valid_value = valid_schema.get_valid_value(operation_id=operation_id)
+ valid_values.append(valid_value)
+
+ if valid_values:
+ return choice(valid_values)
+
+ # The constraints from the parent may not be applicable, resulting in no
+ # valid_values being generated. In that case, generated a random value as normal.
+ chosen_schema = choice(self.resolved_schemas)
+ return chosen_schema.get_valid_value(operation_id=operation_id)
def get_values_out_of_bounds(self, current_value: JSON) -> list[JSON]:
raise ValueError
@@ -1272,30 +1308,6 @@ class OpenApiObject(BaseModel):
paths: dict[str, PathItemObject]
-def get_valid_json_data(
- schema: SchemaObjectTypes,
- constraint_mapping: ConstraintMappingType | None,
- operation_id: str | None = None,
-) -> tuple[JSON, ResolvedSchemaObjectTypes]:
- """
- Returns a tuple of valid JSON for the given schema and constraints and the schema
- which was used to generate the JSON.
-
- If the provided schema is not a UnionTypeSchema the schema returned will be the
- same as the provided schema.
-
- If the provided schema is a UnionTypeSchema, the resolved or selected schema that
- was used to generated the JSON data is returned.
- """
- if constraint_mapping:
- schema = get_schema_for_value_constrained_by_constraint_mapping(
- property_schema=schema,
- constraint_mapping=constraint_mapping,
- )
-
- return schema.get_valid_value(operation_id=operation_id)
-
-
def get_schema_for_value_constrained_by_constraint_mapping(
property_schema: SchemaObjectTypes,
constraint_mapping: ConstraintMappingType,
diff --git a/tests/libcore/suites/test_get_request_data.robot b/tests/libcore/suites/test_get_request_data.robot
index 3784124..ad1505e 100644
--- a/tests/libcore/suites/test_get_request_data.robot
+++ b/tests/libcore/suites/test_get_request_data.robot
@@ -78,3 +78,8 @@ Test Get Request Data For Endpoint Without RequestBody But With Constraint Mappi
Should Be Equal ${request_data.params} ${dict}
Should Be Equal ${request_data.headers} ${dict}
Should Not Be True ${request_data.has_body}
+
+Test Get Request Data For Endpoint With Treat As Mandatory Constraint
+ ${request_data}= Get Request Data path=/employees method=post
+ VAR ${valid_data}= ${request_data.valid_data}
+ Should Not Be Equal ${valid_data["parttime_schedule"]} ${NONE}
diff --git a/tests/libcore/suites/test_validate_response.robot b/tests/libcore/suites/test_validate_response.robot
index 9cad393..8be967d 100644
--- a/tests/libcore/suites/test_validate_response.robot
+++ b/tests/libcore/suites/test_validate_response.robot
@@ -12,7 +12,7 @@ ${ORIGIN} http://localhost:8000
*** Test Cases ***
-Test Bool Response
+Test Boolean Response
${url}= Get Valid Url path=/employees/{employee_id}
${request_data}= Get Request Data path=/employees/{employee_id} method=patch
${response}= Authorized Request
diff --git a/tests/server/testserver.py b/tests/server/testserver.py
index 1341bef..2566248 100644
--- a/tests/server/testserver.py
+++ b/tests/server/testserver.py
@@ -301,7 +301,7 @@ def get_employees_in_wagegroup(wagegroup_id: str) -> list[EmployeeDetails]:
"/employees",
status_code=201,
response_model=EmployeeDetails,
- responses={403: {"model": Detail}, 451: {"model": Detail}},
+ responses={400: {"model": Detail}, 403: {"model": Detail}, 451: {"model": Detail}},
)
def post_employee(employee: Employee, response: Response) -> EmployeeDetails:
wagegroup_id = employee.wagegroup_id
@@ -316,8 +316,9 @@ def post_employee(employee: Employee, response: Response) -> EmployeeDetails:
status_code=403, detail="An employee must be at least 18 years old."
)
parttime_schedule = employee.parttime_schedule
- if parttime_schedule is not None:
- parttime_schedule = ParttimeSchedule.model_validate(parttime_schedule)
+ if parttime_schedule is None:
+ raise HTTPException(status_code=400, detail="Data error.")
+ parttime_schedule = ParttimeSchedule.model_validate(parttime_schedule)
new_employee = EmployeeDetails(
identification=uuid4().hex,
name=employee.name,
diff --git a/tests/user_implemented/custom_user_mappings.py b/tests/user_implemented/custom_user_mappings.py
index 0d69753..54f37b0 100644
--- a/tests/user_implemented/custom_user_mappings.py
+++ b/tests/user_implemented/custom_user_mappings.py
@@ -104,12 +104,14 @@ def get_relations() -> list[ResourceRelation]:
property_name="parttime_schedule",
values=[ParttimeScheduleDto],
treat_as_mandatory=True,
+ invalid_value=IGNORE,
+ invalid_value_error_code=400,
),
]
return relations
-class PatchEmployeeDto(EmployeeDto):
+class PatchEmployeeDto(Dto):
@staticmethod
def get_parameter_relations() -> list[ResourceRelation]:
relations: list[ResourceRelation] = [
@@ -128,6 +130,24 @@ def get_parameter_relations() -> list[ResourceRelation]:
]
return relations
+ @staticmethod
+ def get_relations() -> list[ResourceRelation]:
+ relations: list[ResourceRelation] = [
+ IdDependency(
+ property_name="wagegroup_id",
+ get_path="/wagegroups",
+ error_code=451,
+ ),
+ PropertyValueConstraint(
+ property_name="date_of_birth",
+ values=["1970-07-07", "1980-08-08", "1990-09-09"],
+ invalid_value="2020-02-20",
+ invalid_value_error_code=403,
+ error_code=422,
+ ),
+ ]
+ return relations
+
class EnergyLabelDto(Dto):
@staticmethod
From 767a201cadf0d4275ea91c1d9ffe77cdb17b40ca Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Tue, 9 Dec 2025 14:44:17 +0000
Subject: [PATCH 40/55] Removed unused code, updated coverage pragmas.
---
src/OpenApiDriver/openapi_executors.py | 4 +-
.../data_generation/data_generation_core.py | 8 +-
src/OpenApiLibCore/models/oas_models.py | 77 ++-----------------
src/OpenApiLibCore/openapi_libcore.py | 2 +-
4 files changed, 14 insertions(+), 77 deletions(-)
diff --git a/src/OpenApiDriver/openapi_executors.py b/src/OpenApiDriver/openapi_executors.py
index 843de01..b536d6e 100644
--- a/src/OpenApiDriver/openapi_executors.py
+++ b/src/OpenApiDriver/openapi_executors.py
@@ -357,6 +357,6 @@ def get_original_data(self, url: str) -> dict[str, JSON]:
@staticmethod
def get_keyword_names() -> list[str]:
"""Curated keywords for libdoc and libspec."""
- if getenv("HIDE_INHERITED_KEYWORDS") == "true": # pragma: no branch
- return KEYWORD_NAMES
+ if getenv("HIDE_INHERITED_KEYWORDS") == "true":
+ return KEYWORD_NAMES # pragma: no cover
return KEYWORD_NAMES + LIBCORE_KEYWORD_NAMES
diff --git a/src/OpenApiLibCore/data_generation/data_generation_core.py b/src/OpenApiLibCore/data_generation/data_generation_core.py
index 27c2ead..59849e9 100644
--- a/src/OpenApiLibCore/data_generation/data_generation_core.py
+++ b/src/OpenApiLibCore/data_generation/data_generation_core.py
@@ -82,8 +82,8 @@ def get_request_data(
f"No supported content schema found: {operation_spec.requestBody.content}"
)
- if operation_spec.requestBody.mime_type:
- if "content-type" in headers: # pragma: no branch
+ if operation_spec.requestBody.mime_type: # pragma: no branch
+ if "content-type" in headers: # pragma: no cover
key_value = "content-type"
else:
key_value = "Content-Type"
@@ -241,8 +241,8 @@ def get_parameter_data(
result[parameter_name] = value
continue
- if parameter.schema_ is None: # pragma: no branch
- continue
+ if parameter.schema_ is None:
+ continue # pragma: no cover
value = parameter.schema_.get_valid_value()[0]
result[parameter_name] = value
return result
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index 8817d36..619a633 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -783,7 +783,9 @@ def _get_data_for_property(
# mapping is never instantiated, we can use isinstance(..., type) for this.
if isinstance(constrained_value, type):
property_schema.attach_constraint_mapping(constrained_value)
- valid_value, _ = property_schema.get_valid_value(operation_id=operation_id)
+ valid_value, _ = property_schema.get_valid_value(
+ operation_id=operation_id
+ )
return valid_value
return constrained_value
@@ -971,7 +973,7 @@ def get_invalid_data(
def contains_properties(self, property_names: list[str]) -> bool:
if self.properties is None:
- return False
+ return False # pragma: no cover
for property_name in property_names:
if property_name not in self.properties.root:
return False
@@ -1031,10 +1033,7 @@ def get_valid_value(
self.constraint_mapping.get_relations()
+ self.constraint_mapping.get_parameter_relations()
)
- constrained_property_names = [
- relation.property_name
- for relation in relations
- ]
+ constrained_property_names = [relation.property_name for relation in relations]
if not constrained_property_names:
chosen_schema = choice(self.resolved_schemas)
@@ -1176,7 +1175,7 @@ class ParameterObject(BaseModel):
def attach_constraint_mapping(
self, constraint_mapping: ConstraintMappingType
) -> None:
- if self.schema_:
+ if self.schema_: # pragma: no branch
self.schema_.attach_constraint_mapping(constraint_mapping)
@@ -1221,7 +1220,7 @@ def attach_constraint_mapping(
self, constraint_mapping: ConstraintMappingType
) -> None:
for media_object_type in self.content.values():
- if media_object_type and media_object_type.schema_:
+ if media_object_type and media_object_type.schema_: # pragma: no branch
media_object_type.schema_.attach_constraint_mapping(constraint_mapping)
@@ -1308,68 +1307,6 @@ class OpenApiObject(BaseModel):
paths: dict[str, PathItemObject]
-def get_schema_for_value_constrained_by_constraint_mapping(
- property_schema: SchemaObjectTypes,
- constraint_mapping: ConstraintMappingType,
-) -> SchemaObjectTypes:
- relations = (
- constraint_mapping.get_relations()
- + constraint_mapping.get_parameter_relations()
- )
- mandatory_property_names = [
- relation.property_name
- for relation in relations
- if getattr(relation, "treat_as_mandatory", False)
- ]
- return _get_schema_for_constraint_mapping(
- property_schema=property_schema,
- mandatory_property_names=mandatory_property_names,
- )
-
-
-def _get_schema_for_constraint_mapping(
- property_schema: SchemaObjectTypes,
- mandatory_property_names: list[str],
-) -> SchemaObjectTypes:
- if not isinstance(property_schema, (ObjectSchema, UnionTypeSchema)):
- return property_schema
-
- if isinstance(property_schema, ObjectSchema):
- if mandatory_property_names and not property_schema.contains_properties(
- mandatory_property_names
- ):
- raise ValueError(
- "No schema found that contains all properties that should be treated as mandatory."
- )
- return property_schema
-
- if not mandatory_property_names:
- return choice(property_schema.resolved_schemas)
-
- valid_candidates: list[ObjectSchema] = []
-
- object_schemas = [
- schema
- for schema in property_schema.resolved_schemas
- if isinstance(schema, ObjectSchema)
- ]
- for candidate_schema in object_schemas:
- try:
- valid_candidate = _get_schema_for_constraint_mapping(
- property_schema=candidate_schema,
- mandatory_property_names=mandatory_property_names,
- )
- valid_candidates.append(valid_candidate) # type: ignore[arg-type]
- except ValueError:
- pass
-
- if not valid_candidates:
- raise ValueError(
- "No schema found that contains all properties that should be treated as mandatory."
- )
- return choice(valid_candidates)
-
-
# TODO: move to keyword_logic?
def get_dependent_id(
constraint_mapping: ConstraintMappingType | None,
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index 4f33d7e..74df855 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -691,7 +691,7 @@ def recursion_limit_handler(
recursion_limit_handler=recursion_limit_handler,
) # type: ignore[no-untyped-call]
- if parser.specification is None: # pragma: no branch
+ if parser.specification is None: # pragma: no cover
raise FatalError(
"Source was loaded, but no specification was present after parsing."
)
From c199bce3336be81c3976c11d5350798f134bab4b Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Wed, 10 Dec 2025 09:50:12 +0000
Subject: [PATCH 41/55] Fix unit tests under Python 3.14
---
tests/libcore/unittests/oas_model/test_get_request_data.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/libcore/unittests/oas_model/test_get_request_data.py b/tests/libcore/unittests/oas_model/test_get_request_data.py
index 73e6b5f..adabbc0 100644
--- a/tests/libcore/unittests/oas_model/test_get_request_data.py
+++ b/tests/libcore/unittests/oas_model/test_get_request_data.py
@@ -29,8 +29,8 @@ def setUpClass(cls) -> None:
with open(file=spec_path) as json_file:
spec_dict = json.load(json_file)
cls.spec = OpenApiObject.model_validate(spec_dict)
- cls._get_request_data = partial(
- get_request_data, method="POST", openapi_spec=cls.spec
+ cls._get_request_data = staticmethod(
+ partial(get_request_data, method="POST", openapi_spec=cls.spec)
)
def test_null_schema(self) -> None:
From e405b3b4978d0f523e875ee2b342c48cd4d2d421 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Thu, 11 Dec 2025 08:04:59 +0000
Subject: [PATCH 42/55] Initial release notes
---
docs/releases.md | 36 +++++++++++++++++++++++++++++++++---
1 file changed, 33 insertions(+), 3 deletions(-)
diff --git a/docs/releases.md b/docs/releases.md
index fc45a8a..441ce94 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -1,14 +1,44 @@
# Release notes
-## OpenApiTools v1.0.5
+## OpenApiTools v2.0.0
+
+### Major changes and new features
+- Request bodies now support all JSON types, not just `objects` (`dicts`).
+ - This closes issue _#9: No body generated when root is a list_.
+ - The `Relations` still need to be reworked to align with this change.
+- Improved handling of `treat_as_mandatory` on a `PropertyValueConstraint`.
+- Added support for using `IGNORE` as `invalid_value` on a `PropertyValueConstraint`.
### Bugfixes
-- `parameters` at path level are not taken into account at operation level
+- Fixed validation errors caused by `Content-Type` not being handled case-insensitive.
+- Fixed an exception during validation caused by `charset` being included in the `Content-Type` header for `application/json`.
+
+### Breaking changes
+- `invalid_property_default_response` library parameter renamed to `invalid_data_default_response`.
+- The `RequestData` class that is returned by a number of keywords has been changed:
+ - The `dto` property was removed.
+ - The `valid_data` property was added.
+ - The `constrait_mapping` property was added.
+
+### Additional changes
+- Special handling of `"format": "byte"` for `"type": "string"` (OAS 3.0) was removed.
+ - While some logic related to this worked, the result was never JSON-serializable.
+- The devcontainer setup was updated.
+- The GitHub pipeline was updated to include Python 3.14.
+- Updated minimum version markers for many dependencies.
+- Annotations are now complete (as far as possible under Python 3.10).
## Previous versions
+### OpenApiTools v1.0.5
+
+#### Bugfixes
+- `parameters` at path level are not taken into account at operation level.
+
+---
+
### OpenApiTools v1.0.4
#### Bugfixes
@@ -41,7 +71,7 @@
### OpenApiTools v1.0.1
#### Bugfixes
-- `openapitools_docs` was missing from package distribution
+- `openapitools_docs` was missing from package distribution.
---
From 1aa4e2635eb2e87919a0b65fbddd2e1c513441cb Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Mon, 15 Dec 2025 15:59:49 +0000
Subject: [PATCH 43/55] various fixes, tests green
---
.../data_generation/data_generation_core.py | 11 +-
src/OpenApiLibCore/models/oas_models.py | 96 +++++++-
src/OpenApiLibCore/openapi_libcore.py | 5 +-
tests/files/request_data_variations_3.0.json | 216 ++++++++++++++++++
....json => request_data_variations_3.1.json} | 0
.../oas_model/test_get_request_data.py | 98 +++++++-
6 files changed, 416 insertions(+), 10 deletions(-)
create mode 100644 tests/files/request_data_variations_3.0.json
rename tests/files/{request_data_variations.json => request_data_variations_3.1.json} (100%)
diff --git a/src/OpenApiLibCore/data_generation/data_generation_core.py b/src/OpenApiLibCore/data_generation/data_generation_core.py
index 59849e9..b66ed87 100644
--- a/src/OpenApiLibCore/data_generation/data_generation_core.py
+++ b/src/OpenApiLibCore/data_generation/data_generation_core.py
@@ -148,11 +148,18 @@ def _get_mapping_dataclass_from_valid_data(
)
first_item_data = valid_data[0]
item_object_schema = schema.items
+
if isinstance(item_object_schema, UnionTypeSchema):
resolved_schemas = item_object_schema.resolved_schemas
- item_object_schema = choice(resolved_schemas)
+ for resolved_schema in resolved_schemas:
+ matched_schema = resolved_schema
+ if isinstance(first_item_data, resolved_schema.python_type):
+ break
+ else:
+ matched_schema = item_object_schema
+
mapping_dataclass = _get_mapping_dataclass_from_valid_data(
- schema=item_object_schema,
+ schema=matched_schema,
constraint_mapping=constraint_mapping,
valid_data=first_item_data,
method_spec=method_spec,
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index 619a633..d927ae9 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -1,5 +1,6 @@
from __future__ import annotations
+import builtins
from abc import abstractmethod
from collections import ChainMap
from copy import deepcopy
@@ -144,6 +145,7 @@ def attach_constraint_mapping(
class NullSchema(SchemaBase[None], frozen=True):
type: Literal["null"] = "null"
+ nullable: bool = False
def get_valid_value(
self,
@@ -170,6 +172,10 @@ def can_be_invalidated(self) -> bool:
def annotation_string(self) -> str:
return "None"
+ @property
+ def python_type(self) -> builtins.type:
+ return type(None)
+
class BooleanSchema(SchemaBase[bool], frozen=True):
type: Literal["boolean"] = "boolean"
@@ -207,6 +213,10 @@ def can_be_invalidated(self) -> bool:
def annotation_string(self) -> str:
return "bool"
+ @property
+ def python_type(self) -> builtins.type:
+ return bool
+
class StringSchema(SchemaBase[str], frozen=True):
type: Literal["string"] = "string"
@@ -310,6 +320,10 @@ def can_be_invalidated(self) -> bool:
def annotation_string(self) -> str:
return "str"
+ @property
+ def python_type(self) -> builtins.type:
+ return str
+
class IntegerSchema(SchemaBase[int], frozen=True):
type: Literal["integer"] = "integer"
@@ -428,6 +442,10 @@ def can_be_invalidated(self) -> bool:
def annotation_string(self) -> str:
return "int"
+ @property
+ def python_type(self) -> builtins.type:
+ return int
+
class NumberSchema(SchemaBase[float], frozen=True):
type: Literal["number"] = "number"
@@ -541,6 +559,10 @@ def can_be_invalidated(self) -> bool:
def annotation_string(self) -> str:
return "float"
+ @property
+ def python_type(self) -> builtins.type:
+ return float
+
class ArraySchema(SchemaBase[list[AI]], frozen=True):
type: Literal["array"] = "array"
@@ -694,6 +716,10 @@ def can_be_invalidated(self) -> bool:
def annotation_string(self) -> str:
return f"list[{self.items.annotation_string}]"
+ @property
+ def python_type(self) -> builtins.type:
+ return list
+
# NOTE: Workaround for cyclic PropertiesMapping / SchemaObjectTypes annotations
def _get_properties_mapping_default() -> PropertiesMapping:
@@ -995,6 +1021,10 @@ def can_be_invalidated(self) -> bool:
def annotation_string(self) -> str:
return "dict[str, JSON]"
+ @property
+ def python_type(self) -> builtins.type:
+ return dict
+
ResolvedSchemaObjectTypes = Annotated[
Union[
@@ -1024,6 +1054,7 @@ class UnionTypeSchema(SchemaBase[JSON], frozen=True):
allOf: list["SchemaObjectTypes"] = []
anyOf: list["SchemaObjectTypes"] = []
oneOf: list["SchemaObjectTypes"] = []
+ nullable: bool = False
def get_valid_value(
self,
@@ -1070,7 +1101,17 @@ def get_values_out_of_bounds(self, current_value: JSON) -> list[JSON]:
@property
def resolved_schemas(self) -> list[ResolvedSchemaObjectTypes]:
- return list(self._get_resolved_schemas())
+ schemas_to_return = []
+ null_schema_added = False
+
+ resolved_schemas = list(self._get_resolved_schemas())
+ for schema in resolved_schemas:
+ # Prevent duplication of NullSchema when handling nullable models.
+ if isinstance(schema, NullSchema) and not null_schema_added:
+ null_schema_added = True
+ schemas_to_return.append(schema)
+
+ return schemas_to_return
def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, None]:
if self.allOf:
@@ -1111,6 +1152,10 @@ def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, No
additional_properties_item, RESOLVED_SCHEMA_CLASS_TUPLE
):
additional_properties_types.append(additional_properties_item)
+ if isinstance(additional_properties_item, UnionTypeSchema):
+ additional_properties_types.extend(
+ additional_properties_item.resolved_schemas
+ )
if not additional_properties_types:
additional_properties_value = False
else:
@@ -1130,12 +1175,22 @@ def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, No
required=required_list,
maxProperties=max_propeties_value,
minProperties=min_propeties_value,
- nullable=all(nullable_list),
+ nullable=False,
)
yield merged_schema
+ # If all schemas are nullable the merged schema is treated as nullable.
+ if all(nullable_list):
+ null_schema = NullSchema()
+ null_schema.attach_constraint_mapping(self.constraint_mapping)
+ yield null_schema
else:
for schema in self.anyOf + self.oneOf:
if isinstance(schema, RESOLVED_SCHEMA_CLASS_TUPLE):
+ if schema.nullable:
+ schema.__dict__["nullable"] = False
+ null_schema = NullSchema()
+ null_schema.attach_constraint_mapping(self.constraint_mapping)
+ yield null_schema
yield schema
else:
yield from schema.resolved_schemas
@@ -1178,6 +1233,11 @@ def attach_constraint_mapping(
if self.schema_: # pragma: no branch
self.schema_.attach_constraint_mapping(constraint_mapping)
+ def replace_nullable_with_union(self) -> None:
+ if self.schema_: # pragma: no branch
+ processed_schema = nullable_schema_to_union_schema(self.schema_)
+ self.schema_ = processed_schema
+
class MediaTypeObject(BaseModel):
schema_: SchemaObjectTypes | None = Field(None, alias="schema")
@@ -1223,6 +1283,14 @@ def attach_constraint_mapping(
if media_object_type and media_object_type.schema_: # pragma: no branch
media_object_type.schema_.attach_constraint_mapping(constraint_mapping)
+ def replace_nullable_with_union(self) -> None:
+ for media_object_type in self.content.values():
+ if media_object_type and media_object_type.schema_: # pragma: no branch
+ processed_schema = nullable_schema_to_union_schema(
+ media_object_type.schema_
+ )
+ media_object_type.schema_ = processed_schema
+
class HeaderObject(BaseModel): ...
@@ -1260,6 +1328,13 @@ def attach_constraint_mappings(self) -> None:
for parameter_object in self.parameters:
parameter_object.attach_constraint_mapping(self.constraint_mapping)
+ def replace_nullable_with_union(self) -> None:
+ if self.requestBody:
+ self.requestBody.replace_nullable_with_union()
+
+ for parameter_object in self.parameters:
+ parameter_object.replace_nullable_with_union()
+
class PathItemObject(BaseModel):
get: OperationObject | None = None
@@ -1294,6 +1369,11 @@ def attach_constraint_mappings(self) -> None:
for operation_object in self.operations.values():
operation_object.attach_constraint_mappings()
+ def replace_nullable_with_union(self) -> None:
+ for operation_object in self.operations.values():
+ operation_object.attach_constraint_mappings()
+ operation_object.replace_nullable_with_union()
+
class InfoObject(BaseModel):
title: str
@@ -1307,6 +1387,18 @@ class OpenApiObject(BaseModel):
paths: dict[str, PathItemObject]
+def nullable_schema_to_union_schema(schema: SchemaObjectTypes) -> SchemaObjectTypes:
+ if not schema.nullable:
+ return schema
+
+ schema.__dict__["nullable"] = False
+ union_schema = UnionTypeSchema(
+ oneOf=[schema, NullSchema(constraint_mapping=schema.constraint_mapping)],
+ constraint_mapping=schema.constraint_mapping,
+ )
+ return union_schema
+
+
# TODO: move to keyword_logic?
def get_dependent_id(
constraint_mapping: ConstraintMappingType | None,
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index 74df855..aed6342 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -584,7 +584,7 @@ def openapi_spec(self) -> OpenApiObject:
def _openapi_spec(self) -> OpenApiObject:
parser, _ = self._load_specs_and_validator()
spec_model = OpenApiObject.model_validate(parser.specification)
- spec_model = self._attach_user_mappings(spec_model=spec_model)
+ spec_model = self._perform_post_init_model_updates(spec_model=spec_model)
self._register_path_parameters(spec_model.paths)
return spec_model
@@ -600,7 +600,7 @@ def _register_path_parameter(parameter_object: ParameterObject) -> None:
for parameter in parameters:
_register_path_parameter(parameter_object=parameter)
- def _attach_user_mappings(self, spec_model: OpenApiObject) -> OpenApiObject:
+ def _perform_post_init_model_updates(self, spec_model: OpenApiObject) -> OpenApiObject:
for (
path,
operation,
@@ -627,6 +627,7 @@ def _attach_user_mappings(self, spec_model: OpenApiObject) -> OpenApiObject:
path_item.id_mapper = mapper
path_item.update_operation_parameters()
path_item.attach_constraint_mappings()
+ path_item.replace_nullable_with_union()
return spec_model
diff --git a/tests/files/request_data_variations_3.0.json b/tests/files/request_data_variations_3.0.json
new file mode 100644
index 0000000..bcab416
--- /dev/null
+++ b/tests/files/request_data_variations_3.0.json
@@ -0,0 +1,216 @@
+{
+ "openapi": "3.0.0",
+ "info": {
+ "title": "test_get_request_data",
+ "version": "0.1.0"
+ },
+ "paths": {
+ "/null_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "null"
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/boolean_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/integer_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "integer"
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/number_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "number"
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/string_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/bytes_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "string",
+ "format": "byte"
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/object_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object"
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/array_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ }
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/union_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "nullable": true
+ },
+ {
+ "type": "string",
+ "nullable": true
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/array_with_union_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "allOf": [
+ {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "additionalProperties": {
+ "type": "boolean"
+ }
+ },
+ {
+ "type": "object",
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "nullable": true
+ },
+ {
+ "type": "number",
+ "nullable": true
+ }
+ ]
+ }
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string",
+ "format": "byte"
+ }
+ }
+ },
+ "nullable": true
+ },
+ "additionalProperties": false
+ }
+ ]
+ },
+ "minItems": 1
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/files/request_data_variations.json b/tests/files/request_data_variations_3.1.json
similarity index 100%
rename from tests/files/request_data_variations.json
rename to tests/files/request_data_variations_3.1.json
diff --git a/tests/libcore/unittests/oas_model/test_get_request_data.py b/tests/libcore/unittests/oas_model/test_get_request_data.py
index adabbc0..588d090 100644
--- a/tests/libcore/unittests/oas_model/test_get_request_data.py
+++ b/tests/libcore/unittests/oas_model/test_get_request_data.py
@@ -18,17 +18,107 @@
)
unittest_folder = pathlib.Path(__file__).parent.resolve()
-spec_path = (
- unittest_folder.parent.parent.parent / "files" / "request_data_variations.json"
+spec_path_3_0 = (
+ unittest_folder.parent.parent.parent / "files" / "request_data_variations_3.0.json"
)
+spec_path_3_1 = (
+ unittest_folder.parent.parent.parent / "files" / "request_data_variations_3.1.json"
+)
+
+
+class TestValidData30(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls) -> None:
+ with open(file=spec_path_3_0) as json_file:
+ spec_dict = json.load(json_file)
+ cls.spec = OpenApiObject.model_validate(spec_dict)
+ for path_item in cls.spec.paths.values():
+ path_item.update_operation_parameters()
+ path_item.replace_nullable_with_union()
+ cls._get_request_data = staticmethod(
+ partial(get_request_data, method="POST", openapi_spec=cls.spec)
+ )
+
+ def test_null_schema(self) -> None:
+ request_data = self._get_request_data(path="/null_schema")
+ self.assertEqual(request_data.valid_data, None)
+ self.assertIsInstance(request_data.body_schema, NullSchema)
+
+ def test_bool_schema(self) -> None:
+ request_data = self._get_request_data(path="/boolean_schema")
+ self.assertIsInstance(request_data.valid_data, bool)
+ self.assertIsInstance(request_data.body_schema, BooleanSchema)
+
+ def test_int_schema(self) -> None:
+ request_data = self._get_request_data(path="/integer_schema")
+ self.assertIsInstance(request_data.valid_data, int)
+ self.assertIsInstance(request_data.body_schema, IntegerSchema)
+
+ def test_number_schema(self) -> None:
+ request_data = self._get_request_data(path="/number_schema")
+ self.assertIsInstance(request_data.valid_data, float)
+ self.assertIsInstance(request_data.body_schema, NumberSchema)
+
+ def test_string_schema(self) -> None:
+ request_data = self._get_request_data(path="/string_schema")
+ self.assertIsInstance(request_data.valid_data, str)
+ self.assertIsInstance(request_data.body_schema, StringSchema)
+
+ def test_string_schema_for_byte_format(self) -> None:
+ request_data = self._get_request_data(path="/bytes_schema")
+ self.assertIsInstance(request_data.valid_data, str)
+ self.assertIsInstance(request_data.body_schema, StringSchema)
+
+ def test_object_schema(self) -> None:
+ request_data = self._get_request_data(path="/object_schema")
+ self.assertIsInstance(request_data.valid_data, dict)
+ self.assertIsInstance(request_data.body_schema, ObjectSchema)
+
+ def test_array_schema(self) -> None:
+ request_data = self._get_request_data(path="/array_schema")
+ self.assertIsInstance(request_data.valid_data, list)
+ self.assertIsInstance(request_data.body_schema, ArraySchema)
+ self.assertIsInstance(request_data.body_schema.items, NumberSchema)
+
+ def test_union_schema(self) -> None:
+ request_data = self._get_request_data(path="/union_schema")
+ self.assertIsInstance(request_data.valid_data, (type(None), int, str))
+ self.assertTrue(
+ isinstance(
+ request_data.body_schema, (NullSchema, IntegerSchema, StringSchema)
+ )
+ )
+
+ def test_array_with_union_schema(self) -> None:
+ request_data = self._get_request_data(path="/array_with_union_schema")
+ self.assertIsInstance(request_data.valid_data, list)
+ self.assertIsInstance(request_data.valid_data[0], (dict, type(None)))
+ self.assertTrue(isinstance(request_data.body_schema, ArraySchema))
+ items_schema = request_data.body_schema.items
+ self.assertIsInstance(items_schema, UnionTypeSchema)
+ object_schema, null_schema = items_schema.resolved_schemas
+ self.assertIsInstance(object_schema, ObjectSchema)
+ self.assertIsInstance(null_schema, NullSchema)
+ self.assertEqual(object_schema.required, ["name"])
+ self.assertIsInstance(object_schema.additionalProperties, UnionTypeSchema)
+ additional_properties_schemas = (
+ object_schema.additionalProperties.resolved_schemas
+ )
+ self.assertIsInstance(additional_properties_schemas[0], BooleanSchema)
+ self.assertIsInstance(
+ additional_properties_schemas[1], (IntegerSchema, NumberSchema)
+ )
-class TestValidData(unittest.TestCase):
+class TestValidData31(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
- with open(file=spec_path) as json_file:
+ with open(file=spec_path_3_1) as json_file:
spec_dict = json.load(json_file)
cls.spec = OpenApiObject.model_validate(spec_dict)
+ for path_item in cls.spec.paths.values():
+ path_item.update_operation_parameters()
+ path_item.replace_nullable_with_union()
cls._get_request_data = staticmethod(
partial(get_request_data, method="POST", openapi_spec=cls.spec)
)
From 164fdc7c56731e2bfc175a8c79474568a3267492 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Thu, 18 Dec 2025 16:29:19 +0000
Subject: [PATCH 44/55] Fixes and additional tests
---
src/OpenApiLibCore/models/oas_models.py | 41 +-
src/OpenApiLibCore/openapi_libcore.py | 4 +-
tests/files/nullable_schema_variations.json | 372 ++++++++++++++++++
tests/files/request_data_variations_3.0.json | 34 +-
tests/files/request_data_variations_3.1.json | 13 +-
.../oas_model/test_get_request_data.py | 13 +-
.../oas_model/test_handle_nullable.py | 291 ++++++++++++++
.../oas_model/test_resolve_union_schema.py | 12 +-
8 files changed, 720 insertions(+), 60 deletions(-)
create mode 100644 tests/files/nullable_schema_variations.json
create mode 100644 tests/libcore/unittests/oas_model/test_handle_nullable.py
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index d927ae9..1014594 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -1067,7 +1067,8 @@ def get_valid_value(
constrained_property_names = [relation.property_name for relation in relations]
if not constrained_property_names:
- chosen_schema = choice(self.resolved_schemas)
+ resolved_schemas = self.resolved_schemas
+ chosen_schema = choice(resolved_schemas)
return chosen_schema.get_valid_value(operation_id=operation_id)
valid_values = []
@@ -1099,18 +1100,20 @@ def get_valid_value(
def get_values_out_of_bounds(self, current_value: JSON) -> list[JSON]:
raise ValueError
- @property
+ @cached_property
def resolved_schemas(self) -> list[ResolvedSchemaObjectTypes]:
- schemas_to_return = []
- null_schema_added = False
+ schemas_to_return: list[ResolvedSchemaObjectTypes] = []
+ null_schema = None
resolved_schemas = list(self._get_resolved_schemas())
for schema in resolved_schemas:
# Prevent duplication of NullSchema when handling nullable models.
- if isinstance(schema, NullSchema) and not null_schema_added:
- null_schema_added = True
- schemas_to_return.append(schema)
-
+ if isinstance(schema, NullSchema):
+ null_schema = schema
+ else:
+ schemas_to_return.append(schema)
+ if null_schema is not None:
+ schemas_to_return.append(null_schema)
return schemas_to_return
def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, None]:
@@ -1122,9 +1125,16 @@ def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, No
min_properties_list = []
nullable_list = []
+ schemas_to_process = []
for schema in self.allOf:
+ if isinstance(schema, UnionTypeSchema):
+ schemas_to_process.extend(schema.resolved_schemas)
+ else:
+ schemas_to_process.append(schema)
+
+ for schema in schemas_to_process:
if not isinstance(schema, ObjectSchema):
- raise NotImplementedError("allOf only supported for ObjectSchemas")
+ raise ValueError("allOf is only supported for ObjectSchemas")
if schema.const is not None:
raise ValueError("allOf and models with a const are not compatible")
@@ -1141,7 +1151,7 @@ def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, No
nullable_list.append(schema.nullable)
properties_dicts = [mapping.root for mapping in properties_list]
- properties = dict(ChainMap(*properties_dicts))
+ merged_properties = dict(ChainMap(*properties_dicts))
if True in additional_properties_list:
additional_properties_value: bool | SchemaObjectTypes = True
@@ -1170,13 +1180,14 @@ def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, No
merged_schema = ObjectSchema(
type="object",
- properties=PropertiesMapping(root=properties),
+ properties=PropertiesMapping(root=merged_properties),
additionalProperties=additional_properties_value,
required=required_list,
maxProperties=max_propeties_value,
minProperties=min_propeties_value,
nullable=False,
)
+ merged_schema.attach_constraint_mapping(self.constraint_mapping)
yield merged_schema
# If all schemas are nullable the merged schema is treated as nullable.
if all(nullable_list):
@@ -1392,10 +1403,10 @@ def nullable_schema_to_union_schema(schema: SchemaObjectTypes) -> SchemaObjectTy
return schema
schema.__dict__["nullable"] = False
- union_schema = UnionTypeSchema(
- oneOf=[schema, NullSchema(constraint_mapping=schema.constraint_mapping)],
- constraint_mapping=schema.constraint_mapping,
- )
+ null_schema = NullSchema()
+ null_schema.attach_constraint_mapping(schema.constraint_mapping)
+ union_schema = UnionTypeSchema(oneOf=[schema, null_schema])
+ union_schema.attach_constraint_mapping(schema.constraint_mapping)
return union_schema
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index aed6342..b7cacfd 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -600,7 +600,9 @@ def _register_path_parameter(parameter_object: ParameterObject) -> None:
for parameter in parameters:
_register_path_parameter(parameter_object=parameter)
- def _perform_post_init_model_updates(self, spec_model: OpenApiObject) -> OpenApiObject:
+ def _perform_post_init_model_updates(
+ self, spec_model: OpenApiObject
+ ) -> OpenApiObject:
for (
path,
operation,
diff --git a/tests/files/nullable_schema_variations.json b/tests/files/nullable_schema_variations.json
new file mode 100644
index 0000000..a91ef9b
--- /dev/null
+++ b/tests/files/nullable_schema_variations.json
@@ -0,0 +1,372 @@
+{
+ "openapi": "3.0.4",
+ "info": {
+ "title": "test_get_request_data",
+ "version": "0.1.0"
+ },
+ "paths": {
+ "/boolean_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "boolean",
+ "nullable": true
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/integer_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "integer",
+ "nullable": true
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/number_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "number",
+ "nullable": true
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/string_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "string",
+ "nullable": true
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/array_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "nullable": true,
+ "items": {
+ "type": "number",
+ "nullable": true
+ }
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/object_schema": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "nullable": true,
+ "properties": {
+ "id": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/oneof_first": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "nullable": true
+ },
+ {
+ "type": "string"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/oneof_second": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "string",
+ "nullable": true
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/oneof_both": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "nullable": true
+ },
+ {
+ "type": "string",
+ "nullable": true
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/anyof_first": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "anyOf": [
+ {
+ "type": "integer",
+ "nullable": true
+ },
+ {
+ "type": "string"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/anyof_second": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "string",
+ "nullable": true
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/anyof_both": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "anyOf": [
+ {
+ "type": "integer",
+ "nullable": true
+ },
+ {
+ "type": "string",
+ "nullable": true
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/allof_one": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {
+ "type": "object",
+ "nullable": true,
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "additionalProperties": {
+ "type": "boolean"
+ }
+ },
+ {
+ "type": "object",
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "nullable": true
+ },
+ {
+ "type": "number",
+ "nullable": true
+ }
+ ]
+ }
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string",
+ "format": "byte"
+ }
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ },
+ "/allof_all": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {
+ "type": "object",
+ "nullable": true,
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "additionalProperties": {
+ "type": "boolean"
+ }
+ },
+ {
+ "type": "object",
+ "nullable": true,
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "nullable": true
+ },
+ {
+ "type": "number",
+ "nullable": true
+ }
+ ]
+ }
+ },
+ {
+ "type": "object",
+ "nullable": true,
+ "properties": {
+ "data": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string",
+ "format": "byte"
+ }
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/files/request_data_variations_3.0.json b/tests/files/request_data_variations_3.0.json
index bcab416..71193c0 100644
--- a/tests/files/request_data_variations_3.0.json
+++ b/tests/files/request_data_variations_3.0.json
@@ -1,24 +1,10 @@
{
- "openapi": "3.0.0",
+ "openapi": "3.0.4",
"info": {
"title": "test_get_request_data",
"version": "0.1.0"
},
"paths": {
- "/null_schema": {
- "post": {
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "null"
- }
- }
- },
- "required": true
- }
- }
- },
"/boolean_schema": {
"post": {
"requestBody": {
@@ -129,12 +115,10 @@
"schema": {
"oneOf": [
{
- "type": "integer",
- "nullable": true
+ "type": "integer"
},
{
- "type": "string",
- "nullable": true
+ "type": "string"
}
]
}
@@ -157,8 +141,7 @@
"type": "object",
"properties": {
"name": {
- "type": "string",
- "nullable": true
+ "type": "string"
}
},
"required": [
@@ -173,12 +156,10 @@
"additionalProperties": {
"oneOf": [
{
- "type": "integer",
- "nullable": true
+ "type": "integer"
},
{
- "type": "number",
- "nullable": true
+ "type": "number"
}
]
}
@@ -197,8 +178,7 @@
"format": "byte"
}
}
- },
- "nullable": true
+ }
},
"additionalProperties": false
}
diff --git a/tests/files/request_data_variations_3.1.json b/tests/files/request_data_variations_3.1.json
index 923df19..05ac391 100644
--- a/tests/files/request_data_variations_3.1.json
+++ b/tests/files/request_data_variations_3.1.json
@@ -161,7 +161,9 @@
"type": "string"
}
},
- "required": ["name"],
+ "required": [
+ "name"
+ ],
"additionalProperties": {
"type": "boolean"
}
@@ -169,7 +171,14 @@
{
"type": "object",
"additionalProperties": {
- "type": "integer"
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "number"
+ }
+ ]
}
},
{
diff --git a/tests/libcore/unittests/oas_model/test_get_request_data.py b/tests/libcore/unittests/oas_model/test_get_request_data.py
index 588d090..90a8f2d 100644
--- a/tests/libcore/unittests/oas_model/test_get_request_data.py
+++ b/tests/libcore/unittests/oas_model/test_get_request_data.py
@@ -39,11 +39,6 @@ def setUpClass(cls) -> None:
partial(get_request_data, method="POST", openapi_spec=cls.spec)
)
- def test_null_schema(self) -> None:
- request_data = self._get_request_data(path="/null_schema")
- self.assertEqual(request_data.valid_data, None)
- self.assertIsInstance(request_data.body_schema, NullSchema)
-
def test_bool_schema(self) -> None:
request_data = self._get_request_data(path="/boolean_schema")
self.assertIsInstance(request_data.valid_data, bool)
@@ -96,9 +91,8 @@ def test_array_with_union_schema(self) -> None:
self.assertTrue(isinstance(request_data.body_schema, ArraySchema))
items_schema = request_data.body_schema.items
self.assertIsInstance(items_schema, UnionTypeSchema)
- object_schema, null_schema = items_schema.resolved_schemas
+ [object_schema] = items_schema.resolved_schemas
self.assertIsInstance(object_schema, ObjectSchema)
- self.assertIsInstance(null_schema, NullSchema)
self.assertEqual(object_schema.required, ["name"])
self.assertIsInstance(object_schema.additionalProperties, UnionTypeSchema)
additional_properties_schemas = (
@@ -186,8 +180,9 @@ def test_array_with_union_schema(self) -> None:
additional_properties_schemas = (
resolved_schema.additionalProperties.resolved_schemas
)
- self.assertEqual(
- additional_properties_schemas, [BooleanSchema(), IntegerSchema()]
+ self.assertIsInstance(additional_properties_schemas[0], BooleanSchema)
+ self.assertIsInstance(
+ additional_properties_schemas[1], (IntegerSchema, NumberSchema)
)
diff --git a/tests/libcore/unittests/oas_model/test_handle_nullable.py b/tests/libcore/unittests/oas_model/test_handle_nullable.py
new file mode 100644
index 0000000..029d578
--- /dev/null
+++ b/tests/libcore/unittests/oas_model/test_handle_nullable.py
@@ -0,0 +1,291 @@
+# pylint: disable="missing-class-docstring", "missing-function-docstring"
+import json
+import pathlib
+import unittest
+from functools import partial
+
+from OpenApiLibCore.data_generation.data_generation_core import get_request_data
+from OpenApiLibCore.models.oas_models import (
+ ArraySchema,
+ BooleanSchema,
+ IntegerSchema,
+ NullSchema,
+ NumberSchema,
+ ObjectSchema,
+ OpenApiObject,
+ SchemaObjectTypes,
+ StringSchema,
+ UnionTypeSchema,
+)
+
+unittest_folder = pathlib.Path(__file__).parent.resolve()
+spec_path = (
+ unittest_folder.parent.parent.parent / "files" / "nullable_schema_variations.json"
+)
+
+
+class TestValidData30(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls) -> None:
+ with open(file=spec_path) as json_file:
+ spec_dict = json.load(json_file)
+ cls.spec = OpenApiObject.model_validate(spec_dict)
+ for path_item in cls.spec.paths.values():
+ path_item.update_operation_parameters()
+ path_item.replace_nullable_with_union()
+ cls._get_request_data = staticmethod(
+ partial(get_request_data, method="POST", openapi_spec=cls.spec)
+ )
+
+ def get_body_schema_by_path(self, path: str) -> SchemaObjectTypes:
+ return (
+ self.spec.paths[path].post.requestBody.content["application/json"].schema_
+ )
+
+ def test_boolean_schema(self) -> None:
+ python_type = bool
+ path = "/boolean_schema"
+ schema_type = BooleanSchema
+ request_data = self._get_request_data(path)
+ self.assertIsInstance(request_data.valid_data, (python_type, type(None)))
+ self.assertIsInstance(request_data.body_schema, (schema_type, NullSchema))
+ if isinstance(request_data.body_schema, schema_type):
+ self.assertIsInstance(request_data.valid_data, python_type)
+ else:
+ self.assertEqual(request_data.valid_data, None)
+
+ body_schema = self.get_body_schema_by_path(path)
+ self.assertIsInstance(body_schema, UnionTypeSchema)
+ bool_schema, null_schema = body_schema.resolved_schemas
+ self.assertIsInstance(bool_schema, schema_type)
+ self.assertIsInstance(null_schema, NullSchema)
+
+ def test_integer_schema(self) -> None:
+ python_type = int
+ path = "/integer_schema"
+ schema_type = IntegerSchema
+ request_data = self._get_request_data(path)
+ self.assertIsInstance(request_data.valid_data, (python_type, type(None)))
+ self.assertIsInstance(request_data.body_schema, (schema_type, NullSchema))
+ if isinstance(request_data.body_schema, schema_type):
+ self.assertIsInstance(request_data.valid_data, python_type)
+ else:
+ self.assertEqual(request_data.valid_data, None)
+
+ body_schema = self.get_body_schema_by_path(path)
+ self.assertIsInstance(body_schema, UnionTypeSchema)
+ type_schema, null_schema = body_schema.resolved_schemas
+ self.assertIsInstance(type_schema, schema_type)
+ self.assertIsInstance(null_schema, NullSchema)
+
+ def test_number_schema(self) -> None:
+ python_type = float
+ path = "/number_schema"
+ schema_type = NumberSchema
+ request_data = self._get_request_data(path)
+ self.assertIsInstance(request_data.valid_data, (python_type, type(None)))
+ self.assertIsInstance(request_data.body_schema, (schema_type, NullSchema))
+ if isinstance(request_data.body_schema, schema_type):
+ self.assertIsInstance(request_data.valid_data, python_type)
+ else:
+ self.assertEqual(request_data.valid_data, None)
+
+ body_schema = self.get_body_schema_by_path(path)
+ self.assertIsInstance(body_schema, UnionTypeSchema)
+ type_schema, null_schema = body_schema.resolved_schemas
+ self.assertIsInstance(type_schema, schema_type)
+ self.assertIsInstance(null_schema, NullSchema)
+
+ def test_string_schema(self) -> None:
+ python_type = str
+ path = "/string_schema"
+ schema_type = StringSchema
+ request_data = self._get_request_data(path)
+ self.assertIsInstance(request_data.valid_data, (python_type, type(None)))
+ self.assertIsInstance(request_data.body_schema, (schema_type, NullSchema))
+ if isinstance(request_data.body_schema, schema_type):
+ self.assertIsInstance(request_data.valid_data, python_type)
+ else:
+ self.assertEqual(request_data.valid_data, None)
+
+ body_schema = self.get_body_schema_by_path(path)
+ self.assertIsInstance(body_schema, UnionTypeSchema)
+ type_schema, null_schema = body_schema.resolved_schemas
+ self.assertIsInstance(type_schema, schema_type)
+ self.assertIsInstance(null_schema, NullSchema)
+
+ def test_array_schema(self) -> None:
+ python_type = list
+ path = "/array_schema"
+ schema_type = ArraySchema
+ request_data = self._get_request_data(path)
+ self.assertIsInstance(request_data.valid_data, (python_type, type(None)))
+ self.assertIsInstance(request_data.body_schema, (schema_type, NullSchema))
+ if isinstance(request_data.body_schema, schema_type):
+ self.assertIsInstance(request_data.valid_data, python_type)
+ else:
+ self.assertEqual(request_data.valid_data, None)
+
+ body_schema = self.get_body_schema_by_path(path)
+ self.assertIsInstance(body_schema, UnionTypeSchema)
+ type_schema, null_schema = body_schema.resolved_schemas
+ self.assertIsInstance(type_schema, schema_type)
+ self.assertIsInstance(null_schema, NullSchema)
+
+ def test_object_schema(self) -> None:
+ python_type = dict
+ path = "/object_schema"
+ schema_type = ObjectSchema
+ request_data = self._get_request_data(path)
+ self.assertIsInstance(request_data.valid_data, (python_type, type(None)))
+ self.assertIsInstance(request_data.body_schema, (schema_type, NullSchema))
+ if isinstance(request_data.body_schema, schema_type):
+ self.assertIsInstance(request_data.valid_data, python_type)
+ else:
+ self.assertEqual(request_data.valid_data, None)
+
+ body_schema = self.get_body_schema_by_path(path)
+ self.assertIsInstance(body_schema, UnionTypeSchema)
+ type_schema, null_schema = body_schema.resolved_schemas
+ self.assertIsInstance(type_schema, schema_type)
+ self.assertIsInstance(null_schema, NullSchema)
+
+ def test_oneof_union_schema(self) -> None:
+ path = "/oneof_first"
+ request_data = self._get_request_data(path)
+ self.assertIsInstance(request_data.valid_data, (int, str, type(None)))
+ self.assertIsInstance(
+ request_data.body_schema, (IntegerSchema, StringSchema, NullSchema)
+ )
+ if isinstance(request_data.body_schema, (IntegerSchema, StringSchema)):
+ self.assertIsInstance(request_data.valid_data, (int, str))
+ else:
+ self.assertEqual(request_data.valid_data, None)
+
+ body_schema = self.get_body_schema_by_path(path)
+ self.assertIsInstance(body_schema, UnionTypeSchema)
+ integer_schema, string_schema, null_schema = body_schema.resolved_schemas
+ self.assertIsInstance(integer_schema, IntegerSchema)
+ self.assertIsInstance(string_schema, StringSchema)
+ self.assertIsInstance(null_schema, NullSchema)
+
+ path = "/oneof_second"
+ request_data = self._get_request_data(path)
+ self.assertIsInstance(request_data.valid_data, (int, str, type(None)))
+ self.assertIsInstance(
+ request_data.body_schema, (IntegerSchema, StringSchema, NullSchema)
+ )
+ if isinstance(request_data.body_schema, (IntegerSchema, StringSchema)):
+ self.assertIsInstance(request_data.valid_data, (int, str))
+ else:
+ self.assertEqual(request_data.valid_data, None)
+
+ body_schema = self.get_body_schema_by_path(path)
+ self.assertIsInstance(body_schema, UnionTypeSchema)
+ integer_schema, string_schema, null_schema = body_schema.resolved_schemas
+ self.assertIsInstance(integer_schema, IntegerSchema)
+ self.assertIsInstance(string_schema, StringSchema)
+ self.assertIsInstance(null_schema, NullSchema)
+
+ path = "/oneof_both"
+ request_data = self._get_request_data(path)
+ self.assertIsInstance(request_data.valid_data, (int, str, type(None)))
+ self.assertIsInstance(
+ request_data.body_schema, (IntegerSchema, StringSchema, NullSchema)
+ )
+ if isinstance(request_data.body_schema, (IntegerSchema, StringSchema)):
+ self.assertIsInstance(request_data.valid_data, (int, str))
+ else:
+ self.assertEqual(request_data.valid_data, None)
+
+ body_schema = self.get_body_schema_by_path(path)
+ self.assertIsInstance(body_schema, UnionTypeSchema)
+ integer_schema, string_schema, null_schema = body_schema.resolved_schemas
+ self.assertIsInstance(integer_schema, IntegerSchema)
+ self.assertIsInstance(string_schema, StringSchema)
+ self.assertIsInstance(null_schema, NullSchema)
+
+ def test_anyof_union_schema(self) -> None:
+ path = "/anyof_first"
+ request_data = self._get_request_data(path)
+ self.assertIsInstance(request_data.valid_data, (int, str, type(None)))
+ self.assertIsInstance(
+ request_data.body_schema, (IntegerSchema, StringSchema, NullSchema)
+ )
+ if isinstance(request_data.body_schema, (IntegerSchema, StringSchema)):
+ self.assertIsInstance(request_data.valid_data, (int, str))
+ else:
+ self.assertEqual(request_data.valid_data, None)
+
+ body_schema = self.get_body_schema_by_path(path)
+ self.assertIsInstance(body_schema, UnionTypeSchema)
+ integer_schema, string_schema, null_schema = body_schema.resolved_schemas
+ self.assertIsInstance(integer_schema, IntegerSchema)
+ self.assertIsInstance(string_schema, StringSchema)
+ self.assertIsInstance(null_schema, NullSchema)
+
+ path = "/anyof_second"
+ request_data = self._get_request_data(path)
+ self.assertIsInstance(request_data.valid_data, (int, str, type(None)))
+ self.assertIsInstance(
+ request_data.body_schema, (IntegerSchema, StringSchema, NullSchema)
+ )
+ if isinstance(request_data.body_schema, (IntegerSchema, StringSchema)):
+ self.assertIsInstance(request_data.valid_data, (int, str))
+ else:
+ self.assertEqual(request_data.valid_data, None)
+
+ body_schema = self.get_body_schema_by_path(path)
+ self.assertIsInstance(body_schema, UnionTypeSchema)
+ integer_schema, string_schema, null_schema = body_schema.resolved_schemas
+ self.assertIsInstance(integer_schema, IntegerSchema)
+ self.assertIsInstance(string_schema, StringSchema)
+ self.assertIsInstance(null_schema, NullSchema)
+
+ path = "/anyof_both"
+ request_data = self._get_request_data(path)
+ self.assertIsInstance(request_data.valid_data, (int, str, type(None)))
+ self.assertIsInstance(
+ request_data.body_schema, (IntegerSchema, StringSchema, NullSchema)
+ )
+ if isinstance(request_data.body_schema, (IntegerSchema, StringSchema)):
+ self.assertIsInstance(request_data.valid_data, (int, str))
+ else:
+ self.assertEqual(request_data.valid_data, None)
+
+ body_schema = self.get_body_schema_by_path(path)
+ self.assertIsInstance(body_schema, UnionTypeSchema)
+ integer_schema, string_schema, null_schema = body_schema.resolved_schemas
+ self.assertIsInstance(integer_schema, IntegerSchema)
+ self.assertIsInstance(string_schema, StringSchema)
+ self.assertIsInstance(null_schema, NullSchema)
+
+ def test_allof_union_schema(self) -> None:
+ path = "/allof_one"
+ request_data = self._get_request_data(path)
+ self.assertIsInstance(request_data.valid_data, dict)
+ self.assertIsInstance(request_data.body_schema, ObjectSchema)
+
+ body_schema = self.get_body_schema_by_path(path)
+ self.assertIsInstance(body_schema, UnionTypeSchema)
+ [object_schema] = body_schema.resolved_schemas
+ self.assertIsInstance(object_schema, ObjectSchema)
+
+ path = "/allof_all"
+ request_data = self._get_request_data(path)
+ self.assertIsInstance(request_data.valid_data, (dict, type(None)))
+ self.assertIsInstance(request_data.body_schema, (ObjectSchema, NullSchema))
+ if isinstance(request_data.body_schema, ObjectSchema):
+ self.assertIsInstance(request_data.valid_data, dict)
+ else:
+ self.assertEqual(request_data.valid_data, None)
+
+ body_schema = self.get_body_schema_by_path(path)
+ self.assertIsInstance(body_schema, UnionTypeSchema)
+ object_schema, null_schema = body_schema.resolved_schemas
+ self.assertIsInstance(object_schema, ObjectSchema)
+ self.assertIsInstance(null_schema, NullSchema)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/libcore/unittests/oas_model/test_resolve_union_schema.py b/tests/libcore/unittests/oas_model/test_resolve_union_schema.py
index f9b994f..a0fdbe6 100644
--- a/tests/libcore/unittests/oas_model/test_resolve_union_schema.py
+++ b/tests/libcore/unittests/oas_model/test_resolve_union_schema.py
@@ -16,27 +16,27 @@
class TestResolvedSchemasPropery(unittest.TestCase):
def test_allof_only_supports_object_schemas(self) -> None:
schema = UnionTypeSchema(allOf=[NullSchema()])
- with self.assertRaises(NotImplementedError):
+ with self.assertRaises(ValueError):
schema.resolved_schemas
schema = UnionTypeSchema(allOf=[BooleanSchema()])
- with self.assertRaises(NotImplementedError):
+ with self.assertRaises(ValueError):
schema.resolved_schemas
schema = UnionTypeSchema(allOf=[StringSchema()])
- with self.assertRaises(NotImplementedError):
+ with self.assertRaises(ValueError):
schema.resolved_schemas
schema = UnionTypeSchema(allOf=[IntegerSchema()])
- with self.assertRaises(NotImplementedError):
+ with self.assertRaises(ValueError):
schema.resolved_schemas
schema = UnionTypeSchema(allOf=[NumberSchema()])
- with self.assertRaises(NotImplementedError):
+ with self.assertRaises(ValueError):
schema.resolved_schemas
schema = UnionTypeSchema(allOf=[ArraySchema(items=StringSchema())])
- with self.assertRaises(NotImplementedError):
+ with self.assertRaises(ValueError):
schema.resolved_schemas
def test_allof_not_compatible_with_const(self) -> None:
From 661548421d91eff006355046280caeea0319aa02 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Thu, 18 Dec 2025 16:42:47 +0000
Subject: [PATCH 45/55] Update to release notes
---
docs/releases.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/docs/releases.md b/docs/releases.md
index 441ce94..1c6b2d1 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -4,12 +4,14 @@
### Major changes and new features
- Request bodies now support all JSON types, not just `objects` (`dicts`).
- - This closes issue _#9: No body generated when root is a list_.
+ - This closes issue *#9: No body generated when root is a list*.
- The `Relations` still need to be reworked to align with this change.
- Improved handling of `treat_as_mandatory` on a `PropertyValueConstraint`.
- Added support for using `IGNORE` as `invalid_value` on a `PropertyValueConstraint`.
### Bugfixes
+- Added support for the `nullable` property in OAS 3.0 schemas when generating data.
+ - This closes issue *#81: nullable not taken into account in get_valid_value*.
- Fixed validation errors caused by `Content-Type` not being handled case-insensitive.
- Fixed an exception during validation caused by `charset` being included in the `Content-Type` header for `application/json`.
From ae57c03f0b91112dfa42b792368bf6a0897a6238 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Fri, 19 Dec 2025 13:12:44 +0000
Subject: [PATCH 46/55] Use caps for encoding (consistency change)
---
src/openapitools_docs/documentation_generator.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/openapitools_docs/documentation_generator.py b/src/openapitools_docs/documentation_generator.py
index fde43b2..86a5e7e 100644
--- a/src/openapitools_docs/documentation_generator.py
+++ b/src/openapitools_docs/documentation_generator.py
@@ -26,7 +26,7 @@ def generate(output_folder: Path) -> None:
libcore_documentation=OPENAPILIBCORE_DOCUMENTATION,
advanced_use_documentation=ADVANCED_USE_DOCUMENTATION,
)
- with open(output_file_path, mode="w", encoding="utf-8") as html_file:
+ with open(output_file_path, mode="w", encoding="UTF-8") as html_file:
html_file.write(documentation_content)
From 8ffdf1e01d01a8cffcbdec30d1f5e93e71663265 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Fri, 19 Dec 2025 13:18:03 +0000
Subject: [PATCH 47/55] Boyscout: address warning due to non-unique generated
test name
---
...forbidden_and_test_unauthorized_raise_on_unsecured_api.robot | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/driver/suites/test_forbidden_and_test_unauthorized_raise_on_unsecured_api.robot b/tests/driver/suites/test_forbidden_and_test_unauthorized_raise_on_unsecured_api.robot
index 17ebb9d..c1416d1 100644
--- a/tests/driver/suites/test_forbidden_and_test_unauthorized_raise_on_unsecured_api.robot
+++ b/tests/driver/suites/test_forbidden_and_test_unauthorized_raise_on_unsecured_api.robot
@@ -12,7 +12,7 @@ Test Template Validate Unsecured Requests
*** Variables ***
-@{IGNORED_RESPONSES} 401 403 404 406 412 418 422 451
+@{IGNORED_RESPONSES} 400 401 403 404 406 412 418 422 451
*** Test Cases ***
From 0a2e25e6df867842589ae5130ba0cc933e087fff Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Fri, 19 Dec 2025 14:17:10 +0000
Subject: [PATCH 48/55] Refactor to prevent SSLErros from prance when cert is
set at library init.
---
src/OpenApiLibCore/openapi_libcore.py | 101 +++++++++++++++++++-------
src/OpenApiLibCore/utils/oas_cache.py | 12 +--
2 files changed, 80 insertions(+), 33 deletions(-)
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index b7cacfd..68ecabe 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -1,11 +1,12 @@
import json as _json
import sys
+import tempfile
from collections.abc import Mapping, MutableMapping
from copy import deepcopy
from functools import cached_property
from pathlib import Path
from types import MappingProxyType
-from typing import Any, Generator, Literal, overload
+from typing import Any, Callable, Generator, Literal, overload
from jsonschema_path import SchemaPath
from openapi_core import Config, OpenAPI
@@ -42,7 +43,7 @@
from OpenApiLibCore.models.request_data import RequestData, RequestValues
from OpenApiLibCore.models.resource_relations import IdReference
from OpenApiLibCore.protocols import IResponseValidator
-from OpenApiLibCore.utils.oas_cache import PARSER_CACHE, CachedParser
+from OpenApiLibCore.utils.oas_cache import SPEC_CACHE, CachedSpec
from OpenApiLibCore.utils.parameter_utils import (
get_oas_name_from_safe_name,
get_safe_name_for_oas_name,
@@ -582,8 +583,8 @@ def openapi_spec(self) -> OpenApiObject:
@cached_property
def _openapi_spec(self) -> OpenApiObject:
- parser, _ = self._load_specs_and_validator()
- spec_model = OpenApiObject.model_validate(parser.specification)
+ specification, _ = self._load_specs_and_validator()
+ spec_model = OpenApiObject.model_validate(specification)
spec_model = self._perform_post_init_model_updates(spec_model=spec_model)
self._register_path_parameters(spec_model.paths)
return spec_model
@@ -640,7 +641,7 @@ def response_validator(
_, response_validator = self._load_specs_and_validator()
return response_validator
- def _get_json_types_from_spec(self, spec: dict[str, JSON]) -> set[str]:
+ def _get_json_types_from_spec(self, spec: Mapping[str, JSON]) -> set[str]:
json_types: set[str] = set(self._get_json_types(spec))
return {json_type for json_type in json_types if json_type is not None}
@@ -664,7 +665,7 @@ def _get_json_types(self, item: object) -> Generator[str, None, None]:
def _load_specs_and_validator(
self,
) -> tuple[
- ResolvingParser,
+ Mapping[str, JSON],
IResponseValidator,
]:
def recursion_limit_handler(
@@ -675,34 +676,24 @@ def recursion_limit_handler(
return self._recursion_default # pragma: no cover
try:
- # Since parsing of the OAS and creating the Spec can take a long time,
+ # Since parsing of the OAS and the specification can take a long time,
# they are cached. This is done by storing them in an imported module that
# will have a global scope due to how the Python import system works. This
# ensures that in a Suite of Suites where multiple Suites use the same
# `source`, that OAS is only parsed / loaded once.
- cached_parser = PARSER_CACHE.get(self._source, None)
- if cached_parser:
+ cached_spec = SPEC_CACHE.get(self._source, None)
+ if cached_spec:
return (
- cached_parser.parser,
- cached_parser.response_validator,
+ cached_spec.specification,
+ cached_spec.response_validator,
)
- parser = ResolvingParser(
- self._source,
- backend="openapi-spec-validator",
- recursion_limit=self._recursion_limit,
- recursion_limit_handler=recursion_limit_handler,
- ) # type: ignore[no-untyped-call]
-
- if parser.specification is None: # pragma: no cover
- raise FatalError(
- "Source was loaded, but no specification was present after parsing."
- )
+ specification = self._get_specification(recursion_limit_handler)
- validation_spec = SchemaPath.from_dict(parser.specification) # pyright: ignore[reportArgumentType]
+ validation_spec = SchemaPath.from_dict(specification) # type: ignore[arg-type]
json_types_from_spec: set[str] = self._get_json_types_from_spec(
- parser.specification
+ specification
)
extra_deserializers = {
json_type: _json.loads for json_type in json_types_from_spec
@@ -711,12 +702,12 @@ def recursion_limit_handler(
openapi = OpenAPI(spec=validation_spec, config=config)
response_validator: IResponseValidator = openapi.validate_response
- PARSER_CACHE[self._source] = CachedParser(
- parser=parser,
+ SPEC_CACHE[self._source] = CachedSpec(
+ specification=specification,
response_validator=response_validator,
)
- return parser, response_validator
+ return specification, response_validator
except ResolutionError as exception: # pragma: no cover
raise FatalError(
@@ -727,6 +718,62 @@ def recursion_limit_handler(
f"ValidationError while trying to load openapi spec: {exception}"
) from exception
+ def _get_specification(
+ self, recursion_limit_handler: Callable[[int, str, JSON], JSON]
+ ) -> Mapping[str, JSON]:
+ if Path(self._source).is_file():
+ return self._load_specification(
+ filepath=self._source, recursion_limit_handler=recursion_limit_handler
+ )
+
+ try:
+ response = self.authorized_request(url=self._source, method="GET")
+ response.raise_for_status()
+ except Exception as exception: # pragma: no cover
+ raise FatalError(
+ f"Failed to download the OpenAPI spec using an authorized request."
+ f"\nThis download attempt was made since the provided `source` "
+ f"does not point to a file.\nPlease verify the source path is "
+ f"correct if you intent to reference a local file. "
+ f"\nMake sure the source url is correct and reachable if "
+ f"referencing a web resource."
+ f"\nThe exception was: {exception}"
+ )
+
+ _, _, filename = self._source.rpartition("/")
+ with tempfile.TemporaryDirectory() as tempdir:
+ filepath = Path(tempdir, filename)
+ with open(file=filepath, mode="w", encoding="UTF-8") as spec_file:
+ spec_file.write(response.text)
+
+ return self._load_specification(
+ filepath=filepath.as_posix(),
+ recursion_limit_handler=recursion_limit_handler,
+ )
+
+ def _load_specification(
+ self, filepath: str, recursion_limit_handler: Callable[[int, str, JSON], JSON]
+ ) -> Mapping[str, JSON]:
+ try:
+ parser = ResolvingParser(
+ filepath,
+ backend="openapi-spec-validator",
+ recursion_limit=self._recursion_limit,
+ recursion_limit_handler=recursion_limit_handler,
+ ) # type: ignore[no-untyped-call]
+ except Exception as exception: # pragma: no cover
+ raise FatalError(
+ f"Failed to parse the OpenAPI spec downloaded to {filepath}"
+ f"\nThe exception was: {exception}"
+ )
+
+ if parser.specification is None: # pragma: no cover
+ raise FatalError(
+ "Source was loaded, but no specification was present after parsing."
+ )
+
+ return parser.specification # type: ignore[no-any-return]
+
def read_paths(self) -> dict[str, PathItemObject]:
return self.openapi_spec.paths
diff --git a/src/OpenApiLibCore/utils/oas_cache.py b/src/OpenApiLibCore/utils/oas_cache.py
index 66f94b0..25bbd11 100644
--- a/src/OpenApiLibCore/utils/oas_cache.py
+++ b/src/OpenApiLibCore/utils/oas_cache.py
@@ -1,16 +1,16 @@
-"""Module holding the (global) parser cache."""
+"""Module holding the (global) spec cache."""
from dataclasses import dataclass
+from typing import Mapping
-from prance import ResolvingParser
-
+from OpenApiLibCore.annotations import JSON
from OpenApiLibCore.protocols import IResponseValidator
@dataclass
-class CachedParser:
- parser: ResolvingParser
+class CachedSpec:
+ specification: Mapping[str, JSON]
response_validator: IResponseValidator
-PARSER_CACHE: dict[str, CachedParser] = {}
+SPEC_CACHE: dict[str, CachedSpec] = {}
From 928b4d233e9b81c5c72384244a9322844d7a8c99 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Fri, 19 Dec 2025 14:20:20 +0000
Subject: [PATCH 49/55] Release notes update
---
docs/releases.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs/releases.md b/docs/releases.md
index 1c6b2d1..b10eae4 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -6,6 +6,8 @@
- Request bodies now support all JSON types, not just `objects` (`dicts`).
- This closes issue *#9: No body generated when root is a list*.
- The `Relations` still need to be reworked to align with this change.
+- Refactored retrieving / loading of the OpenAPI spec.
+ - This closes issue *#93: SSL error even if cert / verify is set*.
- Improved handling of `treat_as_mandatory` on a `PropertyValueConstraint`.
- Added support for using `IGNORE` as `invalid_value` on a `PropertyValueConstraint`.
From cb5b5310959e87817c90d549cb8d45b1ec502f3f Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Mon, 22 Dec 2025 13:22:27 +0000
Subject: [PATCH 50/55] Support for multiple OpenApiLibCore instances within a
suite added.
---
docs/releases.md | 8 +--
src/OpenApiDriver/openapi_executors.py | 20 +++++--
.../data_generation/data_invalidation.py | 2 +-
.../keyword_logic/path_functions.py | 2 +-
.../keyword_logic/path_invalidation.py | 2 +-
.../keyword_logic/resource_relations.py | 2 +-
.../keyword_logic/validation.py | 2 +-
src/OpenApiLibCore/models/oas_models.py | 3 +-
src/OpenApiLibCore/openapi_libcore.py | 23 ++++++--
src/openapi_libgen/templates/library.jinja | 2 +-
.../suites/test_multiple_libraries.robot | 53 +++++++++++++++++++
11 files changed, 102 insertions(+), 17 deletions(-)
create mode 100644 tests/libgen/suites/test_multiple_libraries.robot
diff --git a/docs/releases.md b/docs/releases.md
index b10eae4..eb2a2d7 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -4,16 +4,18 @@
### Major changes and new features
- Request bodies now support all JSON types, not just `objects` (`dicts`).
- - This closes issue *#9: No body generated when root is a list*.
+ - This closes [issue #9: No body generated when root is a list](https://github.com/MarketSquare/robotframework-openapitools/issues/9).
- The `Relations` still need to be reworked to align with this change.
- Refactored retrieving / loading of the OpenAPI spec.
- - This closes issue *#93: SSL error even if cert / verify is set*.
+ - This closes [issue #93: SSL error even if cert / verify is set](https://github.com/MarketSquare/robotframework-openapitools/issues/93).
- Improved handling of `treat_as_mandatory` on a `PropertyValueConstraint`.
- Added support for using `IGNORE` as `invalid_value` on a `PropertyValueConstraint`.
### Bugfixes
- Added support for the `nullable` property in OAS 3.0 schemas when generating data.
- - This closes issue *#81: nullable not taken into account in get_valid_value*.
+ - This closes [issue #81: nullable not taken into account in get_valid_value](https://github.com/MarketSquare/robotframework-openapitools/issues/81).
+- Support added for multiple instances of OpenApiLibCore within the same suite.
+ - This closes [issue #96: Multiple keywords with same name error when using multiple generated libraries](https://github.com/MarketSquare/robotframework-openapitools/issues/96).
- Fixed validation errors caused by `Content-Type` not being handled case-insensitive.
- Fixed an exception during validation caused by `charset` being included in the `Content-Type` header for `application/json`.
diff --git a/src/OpenApiDriver/openapi_executors.py b/src/OpenApiDriver/openapi_executors.py
index b536d6e..e127faa 100644
--- a/src/OpenApiDriver/openapi_executors.py
+++ b/src/OpenApiDriver/openapi_executors.py
@@ -46,6 +46,12 @@ def _run_keyword(
) -> str: ... # pragma: no cover
+@overload
+def _run_keyword(
+ keyword_name: Literal["get_invalidated_url"], *args: str | int
+) -> str: ... # pragma: no cover
+
+
@overload
def _run_keyword(
keyword_name: Literal["authorized_request"], *args: object
@@ -70,8 +76,14 @@ def _run_keyword(
) -> tuple[dict[str, JSON], dict[str, str]]: ... # pragma: no cover
+@overload
+def _run_keyword(
+ keyword_name: Literal["perform_validated_request"], *args: object
+) -> None: ... # pragma: no cover
+
+
def _run_keyword(keyword_name: str, *args: object) -> object:
- return run_keyword(keyword_name, *args)
+ return run_keyword(keyword_name, *args) # pyright: ignore[reportArgumentType]
@library(scope="SUITE", doc_format="ROBOT")
@@ -187,7 +199,7 @@ def test_invalid_url(
valid_url = _run_keyword("get_valid_url", path)
try:
- url = run_keyword("get_invalidated_url", valid_url, expected_status_code)
+ url = _run_keyword("get_invalidated_url", valid_url, expected_status_code)
except Exception as exception:
message = getattr(exception, "message", "")
if not message.startswith("ValueError"):
@@ -290,7 +302,7 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
raise AssertionError(
f"No constraint mapping found to cause status_code {status_code}."
)
- run_keyword(
+ _run_keyword(
"perform_validated_request",
path,
status_code,
@@ -322,7 +334,7 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
original_data = {}
if method == "PATCH":
original_data = self.get_original_data(url=url)
- run_keyword(
+ _run_keyword(
"perform_validated_request",
path,
status_code,
diff --git a/src/OpenApiLibCore/data_generation/data_invalidation.py b/src/OpenApiLibCore/data_generation/data_invalidation.py
index f93bb27..99f0666 100644
--- a/src/OpenApiLibCore/data_generation/data_invalidation.py
+++ b/src/OpenApiLibCore/data_generation/data_invalidation.py
@@ -58,7 +58,7 @@ def _run_keyword(
def _run_keyword(keyword_name: str, *args: object) -> object:
- return run_keyword(keyword_name, *args)
+ return run_keyword(keyword_name, *args) # pyright: ignore[reportArgumentType]
def get_invalid_body_data(
diff --git a/src/OpenApiLibCore/keyword_logic/path_functions.py b/src/OpenApiLibCore/keyword_logic/path_functions.py
index 3ac3f97..472e14b 100644
--- a/src/OpenApiLibCore/keyword_logic/path_functions.py
+++ b/src/OpenApiLibCore/keyword_logic/path_functions.py
@@ -51,7 +51,7 @@ def _run_keyword(
def _run_keyword(keyword_name: str, *args: object) -> object:
- return run_keyword(keyword_name, *args)
+ return run_keyword(keyword_name, *args) # pyright: ignore[reportArgumentType]
def match_parts(parts: list[str], spec_parts: list[str]) -> bool:
diff --git a/src/OpenApiLibCore/keyword_logic/path_invalidation.py b/src/OpenApiLibCore/keyword_logic/path_invalidation.py
index 83c7449..2e526d9 100644
--- a/src/OpenApiLibCore/keyword_logic/path_invalidation.py
+++ b/src/OpenApiLibCore/keyword_logic/path_invalidation.py
@@ -24,7 +24,7 @@ def _run_keyword(
def _run_keyword(keyword_name: str, *args: object) -> object:
- return run_keyword(keyword_name, *args)
+ return run_keyword(keyword_name, *args) # pyright: ignore[reportArgumentType]
def get_invalidated_url(
diff --git a/src/OpenApiLibCore/keyword_logic/resource_relations.py b/src/OpenApiLibCore/keyword_logic/resource_relations.py
index 3605f4f..0648cfe 100644
--- a/src/OpenApiLibCore/keyword_logic/resource_relations.py
+++ b/src/OpenApiLibCore/keyword_logic/resource_relations.py
@@ -33,7 +33,7 @@ def _run_keyword(
def _run_keyword(keyword_name: str, *args: object) -> object:
- return run_keyword(keyword_name, *args)
+ return run_keyword(keyword_name, *args) # pyright: ignore[reportArgumentType]
def ensure_in_use(
diff --git a/src/OpenApiLibCore/keyword_logic/validation.py b/src/OpenApiLibCore/keyword_logic/validation.py
index a84238c..b4e15eb 100644
--- a/src/OpenApiLibCore/keyword_logic/validation.py
+++ b/src/OpenApiLibCore/keyword_logic/validation.py
@@ -61,7 +61,7 @@ def _run_keyword(
def _run_keyword(keyword_name: str, *args: object) -> object:
- return run_keyword(keyword_name, *args)
+ return run_keyword(keyword_name, *args) # pyright: ignore[reportArgumentType]
class ValidationLevel(str, Enum):
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index 1014594..628d258 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -1437,7 +1437,8 @@ def get_dependent_id(
return None
valid_id = cast(
- str | int | float, run_keyword("get_valid_id_for_path", id_get_path)
+ str | int | float,
+ run_keyword("get_valid_id_for_path", id_get_path), # pyright: ignore[reportArgumentType]
)
logger.debug(f"get_dependent_id for {id_get_path} returned {valid_id}")
return valid_id
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index 68ecabe..600ae25 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -6,7 +6,7 @@
from functools import cached_property
from pathlib import Path
from types import MappingProxyType
-from typing import Any, Callable, Generator, Literal, overload
+from typing import Any, Callable, Generator, Literal, Sequence, overload
from jsonschema_path import SchemaPath
from openapi_core import Config, OpenAPI
@@ -71,10 +71,27 @@ def _run_keyword(
def _run_keyword(keyword_name: str, *args: object) -> object:
- return run_keyword(keyword_name, *args)
+ return run_keyword(keyword_name, *args) # pyright: ignore[reportArgumentType]
-@library(scope="SUITE", doc_format="HTML")
+class LibrarySearchOrderManager:
+ ROBOT_LISTENER_API_VERSION = 2
+ ROBOT_LIBRARY_SCOPE = "SUITE"
+
+ def __init__(self) -> None:
+ self.previous_search_order: dict[str, Sequence[str]] = {}
+
+ def start_keyword(self, name: str, attrs: dict[str, str]) -> None:
+ previous_search_order = BuiltIn().set_library_search_order(attrs["libname"])
+ self.previous_search_order[name] = previous_search_order
+
+ def end_keyword(self, name: str, attrs: dict[str, str]) -> None:
+ previous_search_order = self.previous_search_order.get(name, None)
+ if previous_search_order is not None:
+ _ = BuiltIn().set_library_search_order(*previous_search_order)
+
+
+@library(scope="SUITE", doc_format="HTML", listener=LibrarySearchOrderManager())
class OpenApiLibCore: # pylint: disable=too-many-public-methods
def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
self,
diff --git a/src/openapi_libgen/templates/library.jinja b/src/openapi_libgen/templates/library.jinja
index 303987e..11a91f2 100644
--- a/src/openapi_libgen/templates/library.jinja
+++ b/src/openapi_libgen/templates/library.jinja
@@ -21,7 +21,7 @@ class {{ library_name }}(OpenApiLibCore):
@staticmethod
def _perform_request(request_values: RequestValues) -> Response:
response: Response = run_keyword(
- "authorized_request",
+ "Authorized Request",
request_values.url,
request_values.method,
request_values.params,
diff --git a/tests/libgen/suites/test_multiple_libraries.robot b/tests/libgen/suites/test_multiple_libraries.robot
new file mode 100644
index 0000000..2764142
--- /dev/null
+++ b/tests/libgen/suites/test_multiple_libraries.robot
@@ -0,0 +1,53 @@
+*** Settings ***
+Library MyGeneratedLibrary
+... source=${ORIGIN}/openapi.json
+... origin=${ORIGIN}
+... base_path=${EMPTY}
+... mappings_path=${ROOT}/tests/user_implemented/custom_user_mappings.py
+Library MyGeneratedLibrary
+... source=${ORIGIN}/openapi.json
+... origin=${ORIGIN}
+... base_path=${EMPTY}
+... mappings_path=${ROOT}/tests/user_implemented/custom_user_mappings.py
+... AS MyDuplicate
+Library MyOtherGeneratedLibrary
+... source=${ORIGIN}/openapi.json
+... origin=${ORIGIN}
+... base_path=${EMPTY}
+... mappings_path=${ROOT}/tests/user_implemented/custom_user_mappings.py
+Library MyGeneratedEdgeCaseLibrary
+... source=${ROOT}/tests/files/schema_with_parameter_name_duplication.yaml
+... origin=${ORIGIN}
+... AS EdgeCases
+
+Test Tags rf7
+
+
+*** Variables ***
+${ORIGIN} http://localhost:8000
+
+
+*** Test Cases ***
+Test Libraries From Same Spec
+ ${first_url}= MyGeneratedLibrary.Get Valid Url path=/employees
+ ${second_url}= MyOtherGeneratedLibrary.Get Valid Url path=/employees
+ Should Be Equal As Strings ${first_url} ${second_url}
+
+ ${first_values}= MyGeneratedLibrary.Get Request Values path=/employees method=POST
+ VAR ${first_json}= ${first_values.json_data}
+ ${second_values}= MyDuplicate.Get Request Values path=/employees method=POST
+ VAR ${second_json}= ${second_values.json_data}
+ ${third_values}= MyOtherGeneratedLibrary.Get Request Values path=/employees method=POST
+ VAR ${third_json}= ${third_values.json_data}
+ Should Be Equal ${first_json.keys()} ${second_json.keys()}
+ Should Be Equal ${second_json.keys()} ${third_json.keys()}
+
+ Get Employees
+ Run Keyword And Expect Error Multiple keywords with name 'Get Employees Employees Get' found. *
+ ... Get Employees Employees Get
+ MyGeneratedLibrary.Get Employees Employees Get
+ MyDuplicate.Get Employees Employees Get
+
+Test Import Alias
+ Run Keyword And Expect Error Failed to get a valid id using GET on http://localhost:8000/hypermedia
+ ... Get Hypermedia Name
From 65ced71515a631aee8e348a0af493e7090d27405 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Mon, 22 Dec 2025 15:52:34 +0000
Subject: [PATCH 51/55] Dto renamed to RelationsMapping, constraint_mapping
renamed to relations_mapping, DTO_MAPPING renamed to RELATIONS_MAPPING.
Additional cleanup and documentation updates.
---
docs/releases.md | 8 +-
src/OpenApiDriver/__init__.py | 8 +-
src/OpenApiDriver/openapi_executors.py | 6 +-
src/OpenApiLibCore/__init__.py | 6 +-
.../data_generation/data_generation_core.py | 44 ++++-----
.../data_generation/data_invalidation.py | 22 ++---
.../__init__.py | 0
.../relations_base.py} | 16 +--
.../keyword_logic/path_functions.py | 4 +-
.../keyword_logic/path_invalidation.py | 4 +-
src/OpenApiLibCore/models/oas_models.py | 82 +++++++---------
src/OpenApiLibCore/models/request_data.py | 8 +-
src/OpenApiLibCore/openapi_libcore.py | 32 +++---
src/OpenApiLibCore/protocols.py | 4 +-
src/openapitools_docs/docstrings.py | 98 +++++++++----------
.../suites/test_endpoint_exceptions.robot | 2 +-
.../suites/test_get_invalid_json_data.robot | 4 +-
.../test_get_json_data_with_conflict.robot | 10 +-
.../suites/test_get_request_data.robot | 6 +-
.../oas_model/test_get_invalid_data.py | 12 +--
.../{test_dto_utils.py => test_mappings.py} | 28 +++---
.../user_implemented/custom_user_mappings.py | 46 ++++-----
tests/variables.py | 62 +-----------
23 files changed, 224 insertions(+), 288 deletions(-)
rename src/OpenApiLibCore/{data_constraints => data_relations}/__init__.py (100%)
rename src/OpenApiLibCore/{data_constraints/dto_base.py => data_relations/relations_base.py} (91%)
rename tests/libcore/unittests/{test_dto_utils.py => test_mappings.py} (82%)
diff --git a/docs/releases.md b/docs/releases.md
index eb2a2d7..9c8c37c 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -20,11 +20,15 @@
- Fixed an exception during validation caused by `charset` being included in the `Content-Type` header for `application/json`.
### Breaking changes
-- `invalid_property_default_response` library parameter renamed to `invalid_data_default_response`.
+- Addressing [issue #95: Refactor: better name for Dto](https://github.com/MarketSquare/robotframework-openapitools/issues/95) introduces a number breaking renames:
+ - `Dto` has been renamed to `RelationsMapping`.
+ - `constraint_mapping` has been renamed to `relations_mapping` in a number of places.
+ - `DTO_MAPPING` has been renamed to `RELATIONS_MAPPING`.
- The `RequestData` class that is returned by a number of keywords has been changed:
- The `dto` property was removed.
- The `valid_data` property was added.
- - The `constrait_mapping` property was added.
+ - The `relations_mapping` property was added.
+- `invalid_property_default_response` library parameter renamed to `invalid_data_default_response`.
### Additional changes
- Special handling of `"format": "byte"` for `"type": "string"` (OAS 3.0) was removed.
diff --git a/src/OpenApiDriver/__init__.py b/src/OpenApiDriver/__init__.py
index be0c6b7..c3e54a9 100644
--- a/src/OpenApiDriver/__init__.py
+++ b/src/OpenApiDriver/__init__.py
@@ -6,16 +6,14 @@
- IdDependency, IdReference, PathPropertiesConstraint, PropertyValueConstraint,
UniquePropertyValueConstraint: Classes to be subclassed by the library user
when implementing a custom mapping module (advanced use).
-- Dto, Relation: Base classes that can be used for type annotations.
+- RelationsMapping, Relation: Base classes that can be used for type annotations.
- IGNORE: A special constant that can be used as a value in the PropertyValueConstraint.
"""
from importlib.metadata import version
from OpenApiDriver.openapidriver import OpenApiDriver
-from OpenApiLibCore.data_constraints.dto_base import (
- Dto,
-)
+from OpenApiLibCore.data_relations.relations_base import RelationsMapping
from OpenApiLibCore.keyword_logic.validation import ValidationLevel
from OpenApiLibCore.models import IGNORE
from OpenApiLibCore.models.resource_relations import (
@@ -35,12 +33,12 @@
__all__ = [
"IGNORE",
- "Dto",
"IdDependency",
"IdReference",
"OpenApiDriver",
"PathPropertiesConstraint",
"PropertyValueConstraint",
+ "RelationsMapping",
"ResourceRelation",
"UniquePropertyValueConstraint",
"ValidationLevel",
diff --git a/src/OpenApiDriver/openapi_executors.py b/src/OpenApiDriver/openapi_executors.py
index e127faa..42da145 100644
--- a/src/OpenApiDriver/openapi_executors.py
+++ b/src/OpenApiDriver/openapi_executors.py
@@ -250,11 +250,11 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
if status_code >= int(HTTPStatus.BAD_REQUEST):
invalidation_keywords: list[str] = []
- if request_data.constraint_mapping.get_body_relations_for_error_code(
+ if request_data.relations_mapping.get_body_relations_for_error_code(
status_code
):
invalidation_keywords.append("get_invalid_body_data")
- if request_data.constraint_mapping.get_parameter_relations_for_error_code(
+ if request_data.relations_mapping.get_parameter_relations_for_error_code(
status_code
):
invalidation_keywords.append("get_invalidated_parameters")
@@ -300,7 +300,7 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
)
else:
raise AssertionError(
- f"No constraint mapping found to cause status_code {status_code}."
+ f"No relation found to cause status_code {status_code}."
)
_run_keyword(
"perform_validated_request",
diff --git a/src/OpenApiLibCore/__init__.py b/src/OpenApiLibCore/__init__.py
index 928debb..d91231d 100644
--- a/src/OpenApiLibCore/__init__.py
+++ b/src/OpenApiLibCore/__init__.py
@@ -7,13 +7,13 @@
- IdDependency, IdReference, PathPropertiesConstraint, PropertyValueConstraint,
UniquePropertyValueConstraint: Classes to be subclassed by the library user
when implementing a custom mapping module (advanced use).
-- Dto, Relation: Base classes that can be used for type annotations.
+- RelationsMapping, Relation: Base classes that can be used for type annotations.
- IGNORE: A special constant that can be used as a value in the PropertyValueConstraint.
"""
from importlib.metadata import version
-from OpenApiLibCore.data_constraints.dto_base import Dto
+from OpenApiLibCore.data_relations.relations_base import RelationsMapping
from OpenApiLibCore.keyword_logic.validation import ValidationLevel
from OpenApiLibCore.models import IGNORE, UNSET
from OpenApiLibCore.models.request_data import RequestData, RequestValues
@@ -62,12 +62,12 @@
__all__ = [
"IGNORE",
"UNSET",
- "Dto",
"IdDependency",
"IdReference",
"OpenApiLibCore",
"PathPropertiesConstraint",
"PropertyValueConstraint",
+ "RelationsMapping",
"RequestData",
"RequestValues",
"ResourceRelation",
diff --git a/src/OpenApiLibCore/data_generation/data_generation_core.py b/src/OpenApiLibCore/data_generation/data_generation_core.py
index b66ed87..a041cbb 100644
--- a/src/OpenApiLibCore/data_generation/data_generation_core.py
+++ b/src/OpenApiLibCore/data_generation/data_generation_core.py
@@ -12,7 +12,7 @@
import OpenApiLibCore.keyword_logic.path_functions as _path_functions
from OpenApiLibCore.annotations import JSON
-from OpenApiLibCore.data_constraints.dto_base import Dto
+from OpenApiLibCore.data_relations.relations_base import RelationsMapping
from OpenApiLibCore.models import IGNORE
from OpenApiLibCore.models.oas_models import (
ArraySchema,
@@ -28,7 +28,7 @@
PropertyValueConstraint,
ResourceRelation,
)
-from OpenApiLibCore.protocols import ConstraintMappingType
+from OpenApiLibCore.protocols import RelationsMappingType
from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
@@ -49,26 +49,26 @@ def get_request_data(
operation_spec: OperationObject | None = getattr(path_item, method)
if operation_spec is None:
raise AttributeError
- constraint_mapping = operation_spec.constraint_mapping
+ relations_mapping = operation_spec.relations_mapping
except AttributeError:
logger.info(
f"method '{method}' not supported on '{spec_path}, using empty spec."
)
operation_spec = OperationObject(operationId="")
- constraint_mapping = None
+ relations_mapping = None
parameters, params, headers = get_request_parameters(
- constraint_mapping=constraint_mapping, method_spec=operation_spec
+ relations_mapping=relations_mapping, method_spec=operation_spec
)
if operation_spec.requestBody is None:
- constraint_mapping = _get_mapping_dataclass_for_empty_body(
- constraint_mapping=constraint_mapping,
+ relations_mapping = _get_mapping_dataclass_for_empty_body(
+ relations_mapping=relations_mapping,
mapping_cls_name=mapping_cls_name,
method_spec=operation_spec,
)
return RequestData(
valid_data=None,
- constraint_mapping=constraint_mapping,
+ relations_mapping=relations_mapping,
parameters=parameters,
params=params,
headers=headers,
@@ -93,16 +93,16 @@ def get_request_data(
operation_id=operation_spec.operationId
)
- constraint_mapping = _get_mapping_dataclass_from_valid_data(
+ relations_mapping = _get_mapping_dataclass_from_valid_data(
schema=schema_used_for_data_generation,
- constraint_mapping=constraint_mapping,
+ relations_mapping=relations_mapping,
valid_data=valid_data,
method_spec=operation_spec,
mapping_cls_name=mapping_cls_name,
)
return RequestData(
valid_data=valid_data,
- constraint_mapping=constraint_mapping,
+ relations_mapping=relations_mapping,
body_schema=schema_used_for_data_generation,
parameters=parameters,
params=params,
@@ -111,12 +111,12 @@ def get_request_data(
def _get_mapping_dataclass_for_empty_body(
- constraint_mapping: ConstraintMappingType | None,
+ relations_mapping: RelationsMappingType | None,
mapping_cls_name: str,
method_spec: OperationObject,
-) -> ConstraintMappingType:
+) -> RelationsMappingType:
cls_name = method_spec.operationId if method_spec.operationId else mapping_cls_name
- base = constraint_mapping if constraint_mapping else Dto
+ base = relations_mapping if relations_mapping else RelationsMapping
mapping_class = make_dataclass(
cls_name=cls_name,
fields=[],
@@ -127,14 +127,14 @@ def _get_mapping_dataclass_for_empty_body(
def _get_mapping_dataclass_from_valid_data(
schema: ResolvedSchemaObjectTypes,
- constraint_mapping: ConstraintMappingType | None,
+ relations_mapping: RelationsMappingType | None,
valid_data: JSON,
method_spec: OperationObject,
mapping_cls_name: str,
-) -> ConstraintMappingType:
+) -> RelationsMappingType:
if not isinstance(schema, (ObjectSchema, ArraySchema)):
return _get_mapping_dataclass_for_empty_body(
- constraint_mapping=constraint_mapping,
+ relations_mapping=relations_mapping,
mapping_cls_name=mapping_cls_name,
method_spec=method_spec,
)
@@ -142,7 +142,7 @@ def _get_mapping_dataclass_from_valid_data(
if isinstance(schema, ArraySchema):
if not valid_data or not isinstance(valid_data, list):
return _get_mapping_dataclass_for_empty_body(
- constraint_mapping=constraint_mapping,
+ relations_mapping=relations_mapping,
mapping_cls_name=mapping_cls_name,
method_spec=method_spec,
)
@@ -160,7 +160,7 @@ def _get_mapping_dataclass_from_valid_data(
mapping_dataclass = _get_mapping_dataclass_from_valid_data(
schema=matched_schema,
- constraint_mapping=constraint_mapping,
+ relations_mapping=relations_mapping,
valid_data=first_item_data,
method_spec=method_spec,
mapping_cls_name=mapping_cls_name,
@@ -172,7 +172,7 @@ def _get_mapping_dataclass_from_valid_data(
)
fields = get_dataclass_fields(object_schema=schema, valid_data=valid_data)
cls_name = method_spec.operationId if method_spec.operationId else mapping_cls_name
- base = constraint_mapping if constraint_mapping else Dto
+ base = relations_mapping if relations_mapping else RelationsMapping
mapping_dataclass = make_dataclass(
cls_name=cls_name,
fields=fields,
@@ -210,12 +210,12 @@ def get_mapping_cls_name(path: str, method: str) -> str:
def get_request_parameters(
- constraint_mapping: ConstraintMappingType | None, method_spec: OperationObject
+ relations_mapping: RelationsMappingType | None, method_spec: OperationObject
) -> tuple[list[ParameterObject], dict[str, Any], dict[str, str]]:
"""Get the methods parameter spec and params and headers with valid data."""
parameters = method_spec.parameters if method_spec.parameters else []
parameter_relations = (
- constraint_mapping.get_parameter_relations() if constraint_mapping else []
+ relations_mapping.get_parameter_relations() if relations_mapping else []
)
query_params = [p for p in parameters if p.in_ == "query"]
header_params = [p for p in parameters if p.in_ == "header"]
diff --git a/src/OpenApiLibCore/data_generation/data_invalidation.py b/src/OpenApiLibCore/data_generation/data_invalidation.py
index 99f0666..e46107e 100644
--- a/src/OpenApiLibCore/data_generation/data_invalidation.py
+++ b/src/OpenApiLibCore/data_generation/data_invalidation.py
@@ -12,9 +12,7 @@
from robot.libraries.BuiltIn import BuiltIn
from OpenApiLibCore.annotations import JSON
-from OpenApiLibCore.data_constraints.dto_base import (
- Dto,
-)
+from OpenApiLibCore.data_relations.relations_base import RelationsMapping
from OpenApiLibCore.models import IGNORE
from OpenApiLibCore.models.oas_models import (
ArraySchema,
@@ -69,7 +67,7 @@ def get_invalid_body_data(
invalid_data_default_response: int,
) -> JSON:
method = method.lower()
- data_relations = request_data.constraint_mapping.get_body_relations_for_error_code(
+ data_relations = request_data.relations_mapping.get_body_relations_for_error_code(
status_code
)
if not data_relations:
@@ -107,7 +105,7 @@ def get_invalid_body_data(
url,
method,
request_data.valid_data,
- request_data.constraint_mapping,
+ request_data.relations_mapping,
status_code,
)
if isinstance(resource_relation, IdReference):
@@ -147,7 +145,7 @@ def get_invalidated_parameters(
raise ValueError("No params or headers to invalidate.")
# ensure the status_code can be triggered
- relations = request_data.constraint_mapping.get_parameter_relations_for_error_code(
+ relations = request_data.relations_mapping.get_parameter_relations_for_error_code(
status_code
)
relations_for_status_code = [
@@ -185,7 +183,7 @@ def get_invalidated_parameters(
# non-default status_codes can only be the result of a Relation
parameter_names = relation_property_names
- # Constraint mappings may contain generic mappings for properties that are
+ # Relation mappings may contain generic mappings for properties that are
# not present in this specific schema
request_data_parameter_names = [p.name for p in request_data.parameters]
additional_relation_property_names = {
@@ -235,7 +233,7 @@ def get_invalidated_parameters(
except ValueError:
invalid_value_for_error_code = NOT_SET
- # get the constraint values if available for the chosen parameter
+ # get the constrained values if available for the chosen parameter
try:
[values_from_constraint] = [
r.values
@@ -327,13 +325,13 @@ def get_json_data_with_conflict(
base_url: str,
method: str,
json_data: dict[str, JSON],
- constraint_mapping: type[Dto],
+ relations_mapping: type[RelationsMapping],
conflict_status_code: int,
) -> dict[str, Any]:
method = method.lower()
unique_property_value_constraints = [
r
- for r in constraint_mapping.get_relations()
+ for r in relations_mapping.get_relations()
if isinstance(r, UniquePropertyValueConstraint)
]
for relation in unique_property_value_constraints:
@@ -342,7 +340,7 @@ def get_json_data_with_conflict(
if method in ["patch", "put"]:
post_url_parts = url.split("/")[:-1]
post_url = "/".join(post_url_parts)
- # the PATCH or PUT may use a different constraint_mapping than required for
+ # the PATCH or PUT may use a different relations_mapping than required for
# POST so valid POST data must be constructed
path = post_url.replace(base_url, "")
request_data = _run_keyword("get_request_data", path, "post")
@@ -372,5 +370,5 @@ def get_json_data_with_conflict(
return json_data
raise ValueError(
f"No UniquePropertyValueConstraint in the get_relations list on "
- f"constraint_mapping {constraint_mapping}."
+ f"relations_mapping {relations_mapping}."
)
diff --git a/src/OpenApiLibCore/data_constraints/__init__.py b/src/OpenApiLibCore/data_relations/__init__.py
similarity index 100%
rename from src/OpenApiLibCore/data_constraints/__init__.py
rename to src/OpenApiLibCore/data_relations/__init__.py
diff --git a/src/OpenApiLibCore/data_constraints/dto_base.py b/src/OpenApiLibCore/data_relations/relations_base.py
similarity index 91%
rename from src/OpenApiLibCore/data_constraints/dto_base.py
rename to src/OpenApiLibCore/data_relations/relations_base.py
index 5cec1c2..3a4c071 100644
--- a/src/OpenApiLibCore/data_constraints/dto_base.py
+++ b/src/OpenApiLibCore/data_relations/relations_base.py
@@ -17,15 +17,15 @@
ResourceRelation,
)
from OpenApiLibCore.protocols import (
- ConstraintMappingType,
IGetIdPropertyName,
+ RelationsMappingType,
)
from OpenApiLibCore.utils.id_mapping import dummy_transformer
@dataclass
-class Dto(ABC):
- """Base class for the Dto class."""
+class RelationsMapping(ABC):
+ """Base class for the RelationsMapping classes."""
@staticmethod
def get_path_relations() -> list[PathPropertiesConstraint]:
@@ -78,21 +78,21 @@ def get_body_relations_for_error_code(
return relations
-def get_constraint_mapping_dict(
+def get_relations_mapping_dict(
mappings_module_name: str,
-) -> dict[tuple[str, str], ConstraintMappingType]:
+) -> dict[tuple[str, str], RelationsMappingType]:
try:
mappings_module = import_module(mappings_module_name)
- return mappings_module.DTO_MAPPING # type: ignore[no-any-return]
+ return mappings_module.RELATIONS_MAPPING # type: ignore[no-any-return]
except (ImportError, AttributeError, ValueError) as exception:
if mappings_module_name != "no mapping":
- logger.error(f"DTO_MAPPING was not imported: {exception}")
+ logger.error(f"RELATIONS_MAPPING was not imported: {exception}")
return {}
def get_path_mapping_dict(
mappings_module_name: str,
-) -> dict[str, ConstraintMappingType]:
+) -> dict[str, RelationsMappingType]:
try:
mappings_module = import_module(mappings_module_name)
return mappings_module.PATH_MAPPING # type: ignore[no-any-return]
diff --git a/src/OpenApiLibCore/keyword_logic/path_functions.py b/src/OpenApiLibCore/keyword_logic/path_functions.py
index 472e14b..13e5151 100644
--- a/src/OpenApiLibCore/keyword_logic/path_functions.py
+++ b/src/OpenApiLibCore/keyword_logic/path_functions.py
@@ -113,8 +113,8 @@ def get_valid_url(
f"{path} not found in paths section of the OpenAPI document."
) from None
- constraint_mapping = path_item.constraint_mapping
- relations = constraint_mapping.get_path_relations() if constraint_mapping else []
+ relations_mapping = path_item.relations_mapping
+ relations = relations_mapping.get_path_relations() if relations_mapping else []
paths = [p.path for p in relations]
if paths:
url = f"{base_url}{choice(paths)}"
diff --git a/src/OpenApiLibCore/keyword_logic/path_invalidation.py b/src/OpenApiLibCore/keyword_logic/path_invalidation.py
index 2e526d9..97fb377 100644
--- a/src/OpenApiLibCore/keyword_logic/path_invalidation.py
+++ b/src/OpenApiLibCore/keyword_logic/path_invalidation.py
@@ -36,8 +36,8 @@ def get_invalidated_url(
path = _run_keyword("get_parameterized_path_from_url", valid_url)
path_item = openapi_spec.paths[path]
- constraint_mapping = path_item.constraint_mapping
- relations = constraint_mapping.get_path_relations() if constraint_mapping else []
+ relations_mapping = path_item.relations_mapping
+ relations = relations_mapping.get_path_relations() if relations_mapping else []
paths = [
p.invalid_value
for p in relations
diff --git a/src/OpenApiLibCore/models/oas_models.py b/src/OpenApiLibCore/models/oas_models.py
index 628d258..cddc927 100644
--- a/src/OpenApiLibCore/models/oas_models.py
+++ b/src/OpenApiLibCore/models/oas_models.py
@@ -30,19 +30,19 @@
from robot.libraries.BuiltIn import BuiltIn
from OpenApiLibCore.annotations import JSON
-from OpenApiLibCore.data_constraints.dto_base import Dto
from OpenApiLibCore.data_generation.localized_faker import FAKE, fake_string
from OpenApiLibCore.data_generation.value_utils import (
json_type_name_of_python_type,
python_type_by_json_type_name,
)
+from OpenApiLibCore.data_relations.relations_base import RelationsMapping
from OpenApiLibCore.models import IGNORE, Ignore
from OpenApiLibCore.models.resource_relations import (
NOT_SET,
IdDependency,
PropertyValueConstraint,
)
-from OpenApiLibCore.protocols import ConstraintMappingType
+from OpenApiLibCore.protocols import RelationsMappingType
from OpenApiLibCore.utils.id_mapping import dummy_transformer
from OpenApiLibCore.utils.parameter_utils import get_safe_name_for_oas_name
@@ -63,7 +63,7 @@ def is_object_schema(schema: SchemaObjectTypes) -> TypeGuard[ObjectSchema]:
class SchemaBase(BaseModel, Generic[O], frozen=True):
readOnly: bool = False
writeOnly: bool = False
- constraint_mapping: ConstraintMappingType = Dto # type: ignore[assignment]
+ relations_mapping: RelationsMappingType = RelationsMapping # type: ignore[assignment]
@abstractmethod
def get_valid_value(
@@ -136,11 +136,9 @@ def get_invalid_value(
return choice(invalid_values)
- def attach_constraint_mapping(
- self, constraint_mapping: ConstraintMappingType
- ) -> None:
+ def attach_relations_mapping(self, relations_mapping: RelationsMappingType) -> None:
# NOTE: https://github.com/pydantic/pydantic/issues/11495
- self.__dict__["constraint_mapping"] = constraint_mapping
+ self.__dict__["relations_mapping"] = relations_mapping
class NullSchema(SchemaBase[None], frozen=True):
@@ -659,7 +657,7 @@ def get_invalid_data(
"""Return a data set with one of the properties set to an invalid value or type."""
invalid_values: list[list[AI]] = []
- relations = self.constraint_mapping.get_body_relations_for_error_code(
+ relations = self.relations_mapping.get_body_relations_for_error_code(
error_code=status_code
)
# TODO: handle relations applicable to arrays / lists
@@ -805,10 +803,10 @@ def _get_data_for_property(
property_name=property_name
):
constrained_value = choice(constrained_values)
- # Check if the chosen value is a nested constraint_mapping; since a
+ # Check if the chosen value is a nested relations_mapping; since a
# mapping is never instantiated, we can use isinstance(..., type) for this.
if isinstance(constrained_value, type):
- property_schema.attach_constraint_mapping(constrained_value)
+ property_schema.attach_relations_mapping(constrained_value)
valid_value, _ = property_schema.get_valid_value(
operation_id=operation_id
)
@@ -818,22 +816,22 @@ def _get_data_for_property(
if (
dependent_id := get_dependent_id(
- constraint_mapping=self.constraint_mapping,
+ relations_mapping=self.relations_mapping,
property_name=property_name,
operation_id=operation_id,
)
) is not None:
return dependent_id
- # Constraints are mapped to endpoints; they are not attached to the property
+ # Relations are mapped to endpoints; they are not attached to the property
# value schemas so update the schema before value generation
- property_schema.attach_constraint_mapping(self.constraint_mapping)
+ property_schema.attach_relations_mapping(self.relations_mapping)
return property_schema.get_valid_value(operation_id=operation_id)[0]
def _get_constrained_values(
self, property_name: str
- ) -> list[JSON | ConstraintMappingType]:
- relations = self.constraint_mapping.get_relations()
+ ) -> list[JSON | RelationsMappingType]:
+ relations = self.relations_mapping.get_relations()
values_list = [
c.values
for c in relations
@@ -897,7 +895,7 @@ def get_invalid_data(
"""Return a data set with one of the properties set to an invalid value or type."""
properties: dict[str, JSON] = deepcopy(valid_data)
- relations = self.constraint_mapping.get_body_relations_for_error_code(
+ relations = self.relations_mapping.get_body_relations_for_error_code(
error_code=status_code
)
property_names = [r.property_name for r in relations]
@@ -1061,8 +1059,8 @@ def get_valid_value(
operation_id: str | None = None,
) -> tuple[JSON, ResolvedSchemaObjectTypes]:
relations = (
- self.constraint_mapping.get_relations()
- + self.constraint_mapping.get_parameter_relations()
+ self.relations_mapping.get_relations()
+ + self.relations_mapping.get_parameter_relations()
)
constrained_property_names = [relation.property_name for relation in relations]
@@ -1079,7 +1077,7 @@ def get_valid_value(
valid_schemas.append(candidate)
if isinstance(candidate, UnionTypeSchema):
- candidate.attach_constraint_mapping(self.constraint_mapping)
+ candidate.attach_relations_mapping(self.relations_mapping)
try:
valid_value = candidate.get_valid_value(operation_id=operation_id)
valid_values.append(valid_value)
@@ -1187,12 +1185,12 @@ def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, No
minProperties=min_propeties_value,
nullable=False,
)
- merged_schema.attach_constraint_mapping(self.constraint_mapping)
+ merged_schema.attach_relations_mapping(self.relations_mapping)
yield merged_schema
# If all schemas are nullable the merged schema is treated as nullable.
if all(nullable_list):
null_schema = NullSchema()
- null_schema.attach_constraint_mapping(self.constraint_mapping)
+ null_schema.attach_relations_mapping(self.relations_mapping)
yield null_schema
else:
for schema in self.anyOf + self.oneOf:
@@ -1200,7 +1198,7 @@ def _get_resolved_schemas(self) -> Generator[ResolvedSchemaObjectTypes, None, No
if schema.nullable:
schema.__dict__["nullable"] = False
null_schema = NullSchema()
- null_schema.attach_constraint_mapping(self.constraint_mapping)
+ null_schema.attach_relations_mapping(self.relations_mapping)
yield null_schema
yield schema
else:
@@ -1236,13 +1234,11 @@ class ParameterObject(BaseModel):
required: bool = False
description: str = ""
schema_: SchemaObjectTypes | None = Field(None, alias="schema")
- constraint_mapping: ConstraintMappingType | None = None
+ relations_mapping: RelationsMappingType | None = None
- def attach_constraint_mapping(
- self, constraint_mapping: ConstraintMappingType
- ) -> None:
+ def attach_relations_mapping(self, relations_mapping: RelationsMappingType) -> None:
if self.schema_: # pragma: no branch
- self.schema_.attach_constraint_mapping(constraint_mapping)
+ self.schema_.attach_relations_mapping(relations_mapping)
def replace_nullable_with_union(self) -> None:
if self.schema_: # pragma: no branch
@@ -1287,12 +1283,10 @@ def _json_schemas(self) -> dict[str, SchemaObjectTypes]:
}
return json_schemas
- def attach_constraint_mapping(
- self, constraint_mapping: ConstraintMappingType
- ) -> None:
+ def attach_relations_mapping(self, relations_mapping: RelationsMappingType) -> None:
for media_object_type in self.content.values():
if media_object_type and media_object_type.schema_: # pragma: no branch
- media_object_type.schema_.attach_constraint_mapping(constraint_mapping)
+ media_object_type.schema_.attach_relations_mapping(relations_mapping)
def replace_nullable_with_union(self) -> None:
for media_object_type in self.content.values():
@@ -1324,20 +1318,20 @@ class OperationObject(BaseModel):
parameters: list[ParameterObject] = []
requestBody: RequestBodyObject | None = None
responses: dict[str, ResponseObject] = {}
- constraint_mapping: ConstraintMappingType | None = None
+ relations_mapping: RelationsMappingType | None = None
def update_parameters(self, parameters: list[ParameterObject]) -> None:
self.parameters.extend(parameters)
- def attach_constraint_mappings(self) -> None:
- if not self.constraint_mapping:
+ def attach_relations_mappings(self) -> None:
+ if not self.relations_mapping:
return
if self.requestBody:
- self.requestBody.attach_constraint_mapping(self.constraint_mapping)
+ self.requestBody.attach_relations_mapping(self.relations_mapping)
for parameter_object in self.parameters:
- parameter_object.attach_constraint_mapping(self.constraint_mapping)
+ parameter_object.attach_relations_mapping(self.relations_mapping)
def replace_nullable_with_union(self) -> None:
if self.requestBody:
@@ -1356,7 +1350,7 @@ class PathItemObject(BaseModel):
summary: str = ""
description: str = ""
parameters: list[ParameterObject] = []
- constraint_mapping: ConstraintMappingType | None = None
+ relations_mapping: RelationsMappingType | None = None
id_mapper: tuple[str, Callable[[str], str]] = (
"id",
dummy_transformer,
@@ -1376,13 +1370,13 @@ def update_operation_parameters(self) -> None:
for operation_object in operations_to_update.values():
operation_object.update_parameters(self.parameters)
- def attach_constraint_mappings(self) -> None:
+ def attach_relations_mappings(self) -> None:
for operation_object in self.operations.values():
- operation_object.attach_constraint_mappings()
+ operation_object.attach_relations_mappings()
def replace_nullable_with_union(self) -> None:
for operation_object in self.operations.values():
- operation_object.attach_constraint_mappings()
+ operation_object.attach_relations_mappings()
operation_object.replace_nullable_with_union()
@@ -1404,19 +1398,19 @@ def nullable_schema_to_union_schema(schema: SchemaObjectTypes) -> SchemaObjectTy
schema.__dict__["nullable"] = False
null_schema = NullSchema()
- null_schema.attach_constraint_mapping(schema.constraint_mapping)
+ null_schema.attach_relations_mapping(schema.relations_mapping)
union_schema = UnionTypeSchema(oneOf=[schema, null_schema])
- union_schema.attach_constraint_mapping(schema.constraint_mapping)
+ union_schema.attach_relations_mapping(schema.relations_mapping)
return union_schema
# TODO: move to keyword_logic?
def get_dependent_id(
- constraint_mapping: ConstraintMappingType | None,
+ relations_mapping: RelationsMappingType | None,
property_name: str,
operation_id: str | None,
) -> str | int | float | None:
- relations = constraint_mapping.get_relations() if constraint_mapping else []
+ relations = relations_mapping.get_relations() if relations_mapping else []
# multiple get paths are possible based on the operation being performed
id_get_paths = [
(d.get_path, d.operation_id)
diff --git a/src/OpenApiLibCore/models/request_data.py b/src/OpenApiLibCore/models/request_data.py
index b21711b..996dd30 100644
--- a/src/OpenApiLibCore/models/request_data.py
+++ b/src/OpenApiLibCore/models/request_data.py
@@ -12,7 +12,7 @@
ResolvedSchemaObjectTypes,
UnionTypeSchema,
)
-from OpenApiLibCore.protocols import ConstraintMappingType
+from OpenApiLibCore.protocols import RelationsMappingType
@dataclass
@@ -56,7 +56,7 @@ class RequestData:
"""Helper class to manage parameters used when making requests."""
valid_data: JSON
- constraint_mapping: ConstraintMappingType
+ relations_mapping: RelationsMappingType
body_schema: ResolvedSchemaObjectTypes | None = None
parameters: list[ParameterObject] = field(default_factory=list)
params: dict[str, JSON] = field(default_factory=dict)
@@ -171,7 +171,7 @@ def headers_that_can_be_invalidated(self) -> set[str]:
def get_required_properties_dict(self) -> dict[str, JSON]:
"""Get the json data containing only the required properties."""
- relations = self.constraint_mapping.get_relations()
+ relations = self.relations_mapping.get_relations()
mandatory_properties = [
relation.property_name
for relation in relations
@@ -247,7 +247,7 @@ def required_parameter_names(self) -> list[str]:
The names of the mandatory parameters, including the parameters configured to be
treated as mandatory using a PropertyValueConstraint.
"""
- relations = self.constraint_mapping.get_parameter_relations()
+ relations = self.relations_mapping.get_parameter_relations()
mandatory_property_names = [
relation.property_name
for relation in relations
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index 600ae25..c445ebb 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -28,13 +28,13 @@
import OpenApiLibCore.keyword_logic.resource_relations as _resource_relations
import OpenApiLibCore.keyword_logic.validation as _validation
from OpenApiLibCore.annotations import JSON
-from OpenApiLibCore.data_constraints.dto_base import (
- Dto,
- get_constraint_mapping_dict,
+from OpenApiLibCore.data_generation.localized_faker import FAKE
+from OpenApiLibCore.data_relations.relations_base import (
+ RelationsMapping,
get_id_property_name,
get_path_mapping_dict,
+ get_relations_mapping_dict,
)
-from OpenApiLibCore.data_generation.localized_faker import FAKE
from OpenApiLibCore.models.oas_models import (
OpenApiObject,
ParameterObject,
@@ -155,7 +155,7 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
mappings_folder = str(mappings_path.parent)
sys.path.append(mappings_folder)
mappings_module_name = mappings_path.stem
- self.constraint_mapping_dict = get_constraint_mapping_dict(
+ self.relations_mapping_dict = get_relations_mapping_dict(
mappings_module_name=mappings_module_name
)
self.path_mapping_dict = get_path_mapping_dict(
@@ -167,7 +167,7 @@ def __init__( # noqa: PLR0913, pylint: disable=dangerous-default-value
)
sys.path.pop()
else:
- self.constraint_mapping_dict = get_constraint_mapping_dict(
+ self.relations_mapping_dict = get_relations_mapping_dict(
mappings_module_name="no mapping"
)
self.path_mapping_dict = get_path_mapping_dict(
@@ -297,7 +297,7 @@ def get_invalid_body_data(
request_data: RequestData,
) -> JSON:
"""
- Return `json_data` based on the `constraint_mapping` on the `request_data` that
+ Return `json_data` based on the `relations_mapping` on the `request_data` that
will cause the provided `status_code` for the `method` operation on the `url`.
> Note: applicable UniquePropertyValueConstraint and IdReference Relations are
@@ -333,12 +333,12 @@ def get_json_data_with_conflict(
url: str,
method: str,
json_data: dict[str, JSON],
- constraint_mapping: type[Dto],
+ relations_mapping: type[RelationsMapping],
conflict_status_code: int,
) -> dict[str, JSON]:
"""
Return `json_data` based on the `UniquePropertyValueConstraint` that must be
- returned by the `get_relations` implementation on the `constraint_mapping` for
+ returned by the `get_relations` implementation on the `relations_mapping` for
the given `conflict_status_code`.
"""
return _data_invalidation.get_json_data_with_conflict(
@@ -346,7 +346,7 @@ def get_json_data_with_conflict(
base_url=self.base_url,
method=method,
json_data=json_data,
- constraint_mapping=constraint_mapping, # FIXME: the model should have this information
+ relations_mapping=relations_mapping, # FIXME: the model should have this information
conflict_status_code=conflict_status_code,
)
@@ -624,19 +624,19 @@ def _perform_post_init_model_updates(
for (
path,
operation,
- ), data_constraint in self.constraint_mapping_dict.items():
+ ), data_relations in self.relations_mapping_dict.items():
try:
operation_item = getattr(spec_model.paths[path], operation.lower())
- operation_item.constraint_mapping = data_constraint
+ operation_item.relations_mapping = data_relations
except KeyError:
logger.warn(
- f"The DTO_MAPPING contains a path that is not found in the OpenAPI spec: {path}"
+ f"The RELATIONS_MAPPING contains a path that is not found in the OpenAPI spec: {path}"
)
- for path, path_constraint in self.path_mapping_dict.items():
+ for path, path_relation in self.path_mapping_dict.items():
try:
path_item = spec_model.paths[path]
- path_item.constraint_mapping = path_constraint
+ path_item.relations_mapping = path_relation
except KeyError:
logger.warn(
f"The PATH_MAPPING contains a path that is not found in the OpenAPI spec: {path}"
@@ -646,7 +646,7 @@ def _perform_post_init_model_updates(
mapper = self.get_id_property_name(path)
path_item.id_mapper = mapper
path_item.update_operation_parameters()
- path_item.attach_constraint_mappings()
+ path_item.attach_relations_mappings()
path_item.replace_nullable_with_union()
return spec_model
diff --git a/src/OpenApiLibCore/protocols.py b/src/OpenApiLibCore/protocols.py
index c050414..ca9b877 100644
--- a/src/OpenApiLibCore/protocols.py
+++ b/src/OpenApiLibCore/protocols.py
@@ -40,7 +40,7 @@ def id_mapping(
) -> dict[str, str | tuple[str, Callable[[str], str]]]: ...
-class IConstraintMapping(Protocol):
+class IRelationsMapping(Protocol):
# NOTE: This Protocol is used as annotation in a number of the oas_models, which
# requires this method to prevent a PydanticSchemaGenerationError.
@classmethod
@@ -69,4 +69,4 @@ def get_body_relations_for_error_code(
) -> list[ResourceRelation]: ...
-ConstraintMappingType = builtins.type[IConstraintMapping]
+RelationsMappingType = builtins.type[IRelationsMapping]
diff --git a/src/openapitools_docs/docstrings.py b/src/openapitools_docs/docstrings.py
index 66db529..05102f7 100644
--- a/src/openapitools_docs/docstrings.py
+++ b/src/openapitools_docs/docstrings.py
@@ -498,10 +498,10 @@
from OpenApiLibCore import (
IGNORE,
- Dto,
IdDependency,
IdReference,
PathPropertiesConstraint,
+ RelationsMapping,
PropertyValueConstraint,
UniquePropertyValueConstraint,
)
@@ -512,7 +512,7 @@
}
-class MyDtoThatDoesNothing(Dto):
+class MyMappingThatDoesNothing(RelationsMapping):
@staticmethod
def get_relations():
relations = []
@@ -529,13 +529,13 @@ def get_parameter_relations():
return relations
-DTO_MAPPING = {
- ("/myspecialpath", "post"): MyDtoThatDoesNothing
+RELATIONS_MAPPING = {
+ ("/myspecialpath", "post"): MyMappingThatDoesNothing
}
PATH_MAPPING = {
- "/mypathwithexternalid/{external_id}": MyDtoThatDoesNothing
+ "/mypathwithexternalid/{external_id}": MyMappingThatDoesNothing
}
@@ -547,13 +547,13 @@ def get_parameter_relations():
Here the classes needed to implement custom mappings are imported.
This section can just be copied without changes.
The ID_MAPPING "constant" definition / assignment.
-The section defining the mapping Dtos. More on this later.
-The DTO_MAPPING "constant" definition / assignment.
+The section defining the RelationsMappings. More on this later.
+The RELATIONS_MAPPING "constant" definition / assignment.
The PATH_MAPPING "constant" definition / assignment.
-The ID_MAPPING, DTO_MAPPING and PATH_MAPPING
-When a custom mappings file is used, the OpenApiLibCore will attempt to import it and then import DTO_MAPPING, PATH_MAPPING and ID_MAPPING from it.
+The ID_MAPPING, RELATIONS_MAPPING and PATH_MAPPING
+When a custom mappings file is used, the OpenApiLibCore will attempt to import it and then import RELATIONS_MAPPING, PATH_MAPPING and ID_MAPPING from it.
For this reason, the exact same name must be used in a custom mappings file (capitilization matters).
The ID_MAPPING
@@ -575,18 +575,18 @@ def my_transformer(identifier_name: str) -> str:
-The DTO_MAPPING
-The DTO_MAPPING is a dictionary with a tuple as its key and a mappings Dto as its value.
+The RELATIONS_MAPPING
+The RELATIONS_MAPPING is a dictionary with a tuple as its key and a RelationsMapping as its value.
The tuple must be in the form ("path_from_the_paths_section", "method_supported_by_the_path").
The path_from_the_paths_section must be exactly as found in the openapi document.
The method_supported_by_the_path must be one of the methods supported by the path and must be in lowercase.
The PATH_MAPPING
-The PATH_MAPPING is a dictionary with a "path_from_the_paths_section" as its key and a mappings Dto as its value.
+The PATH_MAPPING is a dictionary with a "path_from_the_paths_section" as its key and a RelationsMapping as its value.
The path_from_the_paths_section must be exactly as found in the openapi document.
-Dto mapping classes
+RelationsMapping classes
As can be seen from the import section above, a number of classes are available to deal with relations between resources and / or constraints on properties.
Each of these classes is designed to handle a relation or constraint commonly seen in REST APIs.
@@ -611,7 +611,7 @@ def my_transformer(identifier_name: str) -> str:
This relation can be implemented as follows:
-class EmployeeDto(Dto):
+class EmployeeMapping(RelationsMapping):
@staticmethod
def get_relations():
relations = [
@@ -623,8 +623,8 @@ def get_relations():
]
return relations
-DTO_MAPPING = {
- ("/employees", "post"): EmployeeDto
+RELATIONS_MAPPING = {
+ ("/employees", "post"): EmployeeMapping
}
@@ -655,7 +655,7 @@ def get_relations():
To verify that the specified error_code indeed occurs when attempting to delete the Wagegroup, we can implement the following dependency:
-class WagegroupDto(Dto):
+class WagegroupMapping(RelationsMapping):
@staticmethod
def get_relations():
relations = [
@@ -667,8 +667,8 @@ def get_relations():
]
return relations
-DTO_MAPPING = {
- ("/wagegroups/{wagegroup_id}", "delete"): WagegroupDto
+RELATIONS_MAPPING = {
+ ("/wagegroups/{wagegroup_id}", "delete"): WagegroupMapping
}
@@ -687,7 +687,7 @@ def get_relations():
To verify that the specified error_code occurs when attempting to post an Employee with an employee_number that is already in use, we can implement the following dependency:
-class EmployeeDto(Dto):
+class EmployeeMapping(RelationsMapping):
@staticmethod
def get_relations():
relations = [
@@ -699,15 +699,15 @@ def get_relations():
]
return relations
-DTO_MAPPING = {
- ("/employees", "post"): EmployeeDto,
- ("/employees/${employee_id}", "put"): EmployeeDto,
- ("/employees/${employee_id}", "patch"): EmployeeDto,
+RELATIONS_MAPPING = {
+ ("/employees", "post"): EmployeeMapping,
+ ("/employees/${employee_id}", "put"): EmployeeMapping,
+ ("/employees/${employee_id}", "patch"): EmployeeMapping,
}
-Note how this example reuses the EmployeeDto to model the uniqueness constraint for all the operations (post, put and patch) that all relate to the same employee_number.
+Note how this example reuses the EmployeeMapping to model the uniqueness constraint for all the operations (post, put and patch) that all relate to the same employee_number.
@@ -721,7 +721,7 @@ def get_relations():
This type of constraint can be modeled as follows:
-class EmployeeDto(Dto):
+class EmployeeMapping(RelationsMapping):
@staticmethod
def get_relations():
relations = [
@@ -733,10 +733,10 @@ def get_relations():
]
return relations
-DTO_MAPPING = {
- ("/employees", "post"): EmployeeDto,
- ("/employees/${employee_id}", "put"): EmployeeDto,
- ("/employees/${employee_id}", "patch"): EmployeeDto,
+RELATIONS_MAPPING = {
+ ("/employees", "post"): EmployeeMapping,
+ ("/employees/${employee_id}", "put"): EmployeeMapping,
+ ("/employees/${employee_id}", "patch"): EmployeeMapping,
}
@@ -745,7 +745,7 @@ def get_relations():
To support additional restrictions like these, the PropertyValueConstraint supports two additional properties: error_value and invalid_value_error_code:
-class EmployeeDto(Dto):
+class EmployeeMapping(RelationsMapping):
@staticmethod
def get_relations():
relations = [
@@ -759,10 +759,10 @@ def get_relations():
]
return relations
-DTO_MAPPING = {
- ("/employees", "post"): EmployeeDto,
- ("/employees/${employee_id}", "put"): EmployeeDto,
- ("/employees/${employee_id}", "patch"): EmployeeDto,
+RELATIONS_MAPPING = {
+ ("/employees", "post"): EmployeeMapping,
+ ("/employees/${employee_id}", "put"): EmployeeMapping,
+ ("/employees/${employee_id}", "patch"): EmployeeMapping,
}
@@ -774,7 +774,7 @@ def get_relations():
This situation can be handled by use of the special IGNORE value (see below for other uses):
-class EmployeeDto(Dto):
+class EmployeeMapping(RelationsMapping):
@staticmethod
def get_relations():
relations = [
@@ -788,10 +788,10 @@ def get_relations():
]
return relations
-DTO_MAPPING = {
- ("/employees", "post"): EmployeeDto,
- ("/employees/${employee_id}", "put"): EmployeeDto,
- ("/employees/${employee_id}", "patch"): EmployeeDto,
+RELATIONS_MAPPING = {
+ ("/employees", "post"): EmployeeMapping,
+ ("/employees/${employee_id}", "put"): EmployeeMapping,
+ ("/employees/${employee_id}", "patch"): EmployeeMapping,
}
@@ -804,7 +804,7 @@ def get_relations():
Just use this for the path
-Note: The PathPropertiesConstraint is only applicable to the get_path_relations in a Dto and only the PATH_MAPPING uses the get_path_relations.
+Note: The PathPropertiesConstraint is only applicable to the get_path_relations in a RelationsMapping and only the PATH_MAPPING uses the get_path_relations.
To be able to automatically perform endpoint validations, the OpenApiLibCore has to construct the url for the resource from the path as found in the openapi document.
@@ -824,7 +824,7 @@ def get_relations():
It should be clear that the OpenApiLibCore won't be able to acquire a valid month and date. The PathPropertiesConstraint can be used in this case:
-class BirthdaysDto(Dto):
+class BirthdaysMapping(RelationsMapping):
@staticmethod
def get_path_relations():
relations = [
@@ -833,7 +833,7 @@ def get_path_relations():
return relations
PATH_MAPPING = {
- "/birthdays/{month}/{date}": BirthdaysDto
+ "/birthdays/{month}/{date}": BirthdaysMapping
}
@@ -853,7 +853,7 @@ def get_path_relations():
To prevent OpenApiLibCore from generating invalid combinations of path and query parameters in this type of endpoint, the IGNORE special value can be used to ensure the related query parameter is never send in a request.
-class EnergyLabelDto(Dto):
+class EnergyLabelMapping(RelationsMapping):
@staticmethod
def get_parameter_relations():
relations = [
@@ -872,8 +872,8 @@ def get_relations(:
]
return relations
-DTO_MAPPING = {
- ("/energy_label/{zipcode}/{home_number}", "get"): EnergyLabelDto,
+RELATIONS_MAPPING = {
+ ("/energy_label/{zipcode}/{home_number}", "get"): EnergyLabelMapping,
}
@@ -886,7 +886,7 @@ def get_relations(:
Such situations can be handled by a mapping as shown below:
-class PatchEmployeeDto(Dto):
+class PatchEmployeeMapping(RelationsMapping):
@staticmethod
def get_parameter_relations() -> list[ResourceRelation]:
relations: list[ResourceRelation] = [
@@ -905,8 +905,8 @@ def get_parameter_relations() -> list[ResourceRelation]:
]
return relations
-DTO_MAPPING = {
- ("/employees/{employee_id}", "patch"): PatchEmployeeDto,
+RELATIONS_MAPPING = {
+ ("/employees/{employee_id}", "patch"): PatchEmployeeMapping,
}
diff --git a/tests/driver/suites/test_endpoint_exceptions.robot b/tests/driver/suites/test_endpoint_exceptions.robot
index 94bf041..a14524c 100644
--- a/tests/driver/suites/test_endpoint_exceptions.robot
+++ b/tests/driver/suites/test_endpoint_exceptions.robot
@@ -25,6 +25,6 @@ Validate Test Endpoint Keyword
Run Keyword And Expect Error Response status_code 401 was not 200.
... Test Endpoint path=${path} method=${method} status_code=${status_code}
ELSE
- Run Keyword And Expect Error No constraint mapping found to cause status_code ${status_code}.
+ Run Keyword And Expect Error No relation found to cause status_code ${status_code}.
... Test Endpoint path=${path} method=${method} status_code=${status_code}
END
diff --git a/tests/libcore/suites/test_get_invalid_json_data.robot b/tests/libcore/suites/test_get_invalid_json_data.robot
index 7e081d6..f6b3fe7 100644
--- a/tests/libcore/suites/test_get_invalid_json_data.robot
+++ b/tests/libcore/suites/test_get_invalid_json_data.robot
@@ -34,7 +34,7 @@ Test Get Invalid Body Data Raises If Data Cannot Be Invalidated
Test Get Invalid Body Data Based On Schema
${request_data}= Get Request Data path=/events/ method=post
- Should Be Empty ${request_data.constraint_mapping.get_body_relations_for_error_code(422)}
+ Should Be Empty ${request_data.relations_mapping.get_body_relations_for_error_code(422)}
${invalid_json}= Get Invalid Body Data
... url=none
... method=none
@@ -83,7 +83,7 @@ Test Get Invalid Body Data For IdDependency
... url=${url} method=post json_data=${invalid_json}
Should Be Equal As Integers ${response.status_code} 451
-Test Get Invalid Body Data For Constraint Mappings With Other Relations
+Test Get Invalid Body Data For Relation Mappings With Other Relations
${request_data}= Get Request Data path=/employees method=post
${invalid_json}= Get Invalid Body Data
... url=${ORIGIN}/employees
diff --git a/tests/libcore/suites/test_get_json_data_with_conflict.robot b/tests/libcore/suites/test_get_json_data_with_conflict.robot
index 15b2cac..42e4198 100644
--- a/tests/libcore/suites/test_get_json_data_with_conflict.robot
+++ b/tests/libcore/suites/test_get_json_data_with_conflict.robot
@@ -13,7 +13,7 @@ ${ORIGIN} http://localhost:8000
*** Test Cases ***
Test Get Json Data With Conflict Raises For No UniquePropertyValueConstraint
- # No mapping for /wagegroups GET will yield a default constraint_mapping on the request_data
+ # No mapping for /wagegroups GET will yield a default relations_mapping on the request_data
${request_data}= Get Request Data path=/wagegroups method=get
${url}= Get Valid Url path=/wagegroups
Run Keyword And Expect Error ValueError: No UniquePropertyValueConstraint*
@@ -21,7 +21,7 @@ Test Get Json Data With Conflict Raises For No UniquePropertyValueConstraint
... url=${url}
... method=post
... json_data=&{EMPTY}
- ... constraint_mapping=${request_data.constraint_mapping}
+ ... relations_mapping=${request_data.relations_mapping}
... conflict_status_code=418
Test Get Json Data With Conflict For Post Request
@@ -31,7 +31,7 @@ Test Get Json Data With Conflict For Post Request
... url=${url}
... method=post
... json_data=${request_data.valid_data}
- ... constraint_mapping=${request_data.constraint_mapping}
+ ... relations_mapping=${request_data.relations_mapping}
... conflict_status_code=418
Should Not Be Empty ${invalid_data}
@@ -42,7 +42,7 @@ Test Get Json Data With Conflict For Put Request
... url=${url}
... method=put
... json_data=${request_data.valid_data}
- ... constraint_mapping=${request_data.constraint_mapping}
+ ... relations_mapping=${request_data.relations_mapping}
... conflict_status_code=418
${response}= Authorized Request
... url=${url} method=put json_data=${invalid_json}
@@ -54,7 +54,7 @@ Test Get Json Data With Conflict For Put Request
# ${invalid_json}= Get Json Data With Conflict
# ... url=${url}
# ... method=put
-# ... constraint_mapping=${request_data.constraint_mapping}
+# ... relations_mapping=${request_data.relations_mapping}
# ... conflict_status_code=418
# ${response}= Authorized Request
# ... url=${url} method=put json_data=${invalid_json}
diff --git a/tests/libcore/suites/test_get_request_data.robot b/tests/libcore/suites/test_get_request_data.robot
index ad1505e..8512e6a 100644
--- a/tests/libcore/suites/test_get_request_data.robot
+++ b/tests/libcore/suites/test_get_request_data.robot
@@ -19,7 +19,7 @@ Test Get Request Data For Invalid Method On Endpoint
${request_data}= Get Request Data path=/events/ method=delete
VAR &{dict}= &{EMPTY}
VAR @{list}= @{EMPTY}
- Should Be Equal ${request_data.constraint_mapping.__doc__} DeleteEvents()
+ Should Be Equal ${request_data.relations_mapping.__doc__} DeleteEvents()
Should Be Equal ${request_data.body_schema} ${NONE}
Should Be Equal ${request_data.parameters} ${list}
Should Be Equal ${request_data.params} ${dict}
@@ -70,7 +70,7 @@ Test Get Request Data For Endpoint With Array Request Body
END
END
-Test Get Request Data For Endpoint Without RequestBody But With Constraint Mapping
+Test Get Request Data For Endpoint Without RequestBody But With Relation Mapping
${request_data}= Get Request Data path=/wagegroups/{wagegroup_id} method=delete
VAR &{dict}= &{EMPTY}
Should Be Equal ${request_data.body_schema} ${NONE}
@@ -79,7 +79,7 @@ Test Get Request Data For Endpoint Without RequestBody But With Constraint Mappi
Should Be Equal ${request_data.headers} ${dict}
Should Not Be True ${request_data.has_body}
-Test Get Request Data For Endpoint With Treat As Mandatory Constraint
+Test Get Request Data For Endpoint With Treat As Mandatory Relation
${request_data}= Get Request Data path=/employees method=post
VAR ${valid_data}= ${request_data.valid_data}
Should Not Be Equal ${valid_data["parttime_schedule"]} ${NONE}
diff --git a/tests/libcore/unittests/oas_model/test_get_invalid_data.py b/tests/libcore/unittests/oas_model/test_get_invalid_data.py
index 18a3b2f..05b8fbd 100644
--- a/tests/libcore/unittests/oas_model/test_get_invalid_data.py
+++ b/tests/libcore/unittests/oas_model/test_get_invalid_data.py
@@ -2,11 +2,11 @@
# pyright: reportArgumentType=false
import unittest
-from OpenApiLibCore import Dto, PropertyValueConstraint, ResourceRelation
+from OpenApiLibCore import PropertyValueConstraint, RelationsMapping, ResourceRelation
from OpenApiLibCore.models.oas_models import ArraySchema, IntegerSchema
-class ArrayConstraint(Dto):
+class ArrayConstraint(RelationsMapping):
@staticmethod
def get_relations() -> list[ResourceRelation]:
relations: list[ResourceRelation] = [
@@ -23,7 +23,7 @@ def get_relations() -> list[ResourceRelation]:
class TestArraySchema(unittest.TestCase):
def test_raises_for_no_matching_status_code(self) -> None:
schema = ArraySchema(items=IntegerSchema())
- schema.attach_constraint_mapping(ArrayConstraint)
+ schema.attach_relations_mapping(ArrayConstraint)
with self.assertRaises(ValueError) as context:
_ = schema.get_invalid_data(
valid_data=[42],
@@ -37,7 +37,7 @@ def test_raises_for_no_matching_status_code(self) -> None:
def test_status_code_is_default_code_without_constraints_raises(self) -> None:
schema = ArraySchema(items=IntegerSchema(maximum=43))
- schema.attach_constraint_mapping(Dto)
+ schema.attach_relations_mapping(RelationsMapping)
with self.assertRaises(ValueError) as context:
_ = schema.get_invalid_data(
valid_data=[42],
@@ -51,7 +51,7 @@ def test_status_code_is_default_code_without_constraints_raises(self) -> None:
def test_status_code_is_default_code(self) -> None:
schema = ArraySchema(items=IntegerSchema(maximum=43), minItems=1)
- schema.attach_constraint_mapping(Dto)
+ schema.attach_relations_mapping(RelationsMapping)
invalid_data = schema.get_invalid_data(
valid_data=[42],
status_code=422,
@@ -61,7 +61,7 @@ def test_status_code_is_default_code(self) -> None:
valid_value = [42]
schema = ArraySchema(items=IntegerSchema(maximum=43), const=valid_value)
- schema.attach_constraint_mapping(Dto)
+ schema.attach_relations_mapping(RelationsMapping)
invalid_data = schema.get_invalid_data(
valid_data=valid_value,
status_code=422,
diff --git a/tests/libcore/unittests/test_dto_utils.py b/tests/libcore/unittests/test_mappings.py
similarity index 82%
rename from tests/libcore/unittests/test_dto_utils.py
rename to tests/libcore/unittests/test_mappings.py
index 078f09c..ffe9f3b 100644
--- a/tests/libcore/unittests/test_dto_utils.py
+++ b/tests/libcore/unittests/test_mappings.py
@@ -3,12 +3,12 @@
import sys
import unittest
-from OpenApiLibCore import Dto
-from OpenApiLibCore.data_constraints.dto_base import (
+from OpenApiLibCore import RelationsMapping
+from OpenApiLibCore.data_relations.relations_base import (
GetIdPropertyName,
- get_constraint_mapping_dict,
get_id_property_name,
get_path_mapping_dict,
+ get_relations_mapping_dict,
)
from OpenApiLibCore.utils.id_mapping import dummy_transformer
@@ -18,7 +18,7 @@
)
-class TestConstraintMapping(unittest.TestCase):
+class TestRelationsMapping(unittest.TestCase):
mappings_module_name = ""
@classmethod
@@ -37,25 +37,27 @@ def tearDownClass(cls) -> None:
print(f"removed {sys.path.pop()} from path")
def test_no_mapping(self) -> None:
- value_constraints_mapping_dict = get_constraint_mapping_dict("dummy")
- self.assertDictEqual(value_constraints_mapping_dict, {})
+ value_relations_mapping_dict = get_relations_mapping_dict("dummy")
+ self.assertDictEqual(value_relations_mapping_dict, {})
def test_valid_mapping(self) -> None:
- value_constraints_mapping_dict = get_constraint_mapping_dict(
+ value_relations_mapping_dict = get_relations_mapping_dict(
self.mappings_module_name
)
- self.assertIsInstance(value_constraints_mapping_dict, dict)
- self.assertGreater(len(value_constraints_mapping_dict.keys()), 0)
+ self.assertIsInstance(value_relations_mapping_dict, dict)
+ self.assertGreater(len(value_relations_mapping_dict.keys()), 0)
- def test_mapped_returns_dto_class(self) -> None:
- value_constraints_mapping_dict = get_constraint_mapping_dict(
+ def test_mapped_returns_relationsmapping_class(self) -> None:
+ value_relations_mapping_dict = get_relations_mapping_dict(
self.mappings_module_name
)
- keys = value_constraints_mapping_dict.keys()
+ keys = value_relations_mapping_dict.keys()
for key in keys:
self.assertIsInstance(key, tuple)
self.assertEqual(len(key), 2)
- self.assertTrue(issubclass(value_constraints_mapping_dict[key], Dto))
+ self.assertTrue(
+ issubclass(value_relations_mapping_dict[key], RelationsMapping)
+ )
class TestPathMapping(unittest.TestCase):
diff --git a/tests/user_implemented/custom_user_mappings.py b/tests/user_implemented/custom_user_mappings.py
index 54f37b0..ef56606 100644
--- a/tests/user_implemented/custom_user_mappings.py
+++ b/tests/user_implemented/custom_user_mappings.py
@@ -3,17 +3,17 @@
from OpenApiLibCore import (
IGNORE,
- Dto,
IdDependency,
IdReference,
PathPropertiesConstraint,
PropertyValueConstraint,
+ RelationsMapping,
ResourceRelation,
UniquePropertyValueConstraint,
)
-class WagegroupDto(Dto):
+class WagegroupMapping(RelationsMapping):
@staticmethod
def get_relations() -> list[ResourceRelation]:
relations: list[ResourceRelation] = [
@@ -42,7 +42,7 @@ def get_relations() -> list[ResourceRelation]:
return relations
-class WagegroupDeleteDto(Dto):
+class WagegroupDeleteMapping(RelationsMapping):
@staticmethod
def get_relations() -> list[ResourceRelation]:
relations: list[ResourceRelation] = [
@@ -60,7 +60,7 @@ def get_relations() -> list[ResourceRelation]:
return relations
-class ParttimeDayDto(Dto):
+class ParttimeDayMapping(RelationsMapping):
@staticmethod
def get_relations() -> list[ResourceRelation]:
relations: list[ResourceRelation] = [
@@ -72,19 +72,19 @@ def get_relations() -> list[ResourceRelation]:
return relations
-class ParttimeScheduleDto(Dto):
+class ParttimeScheduleMapping(RelationsMapping):
@staticmethod
def get_relations() -> list[ResourceRelation]:
relations: list[ResourceRelation] = [
PropertyValueConstraint(
property_name="parttime_days",
- values=[ParttimeDayDto],
+ values=[ParttimeDayMapping],
),
]
return relations
-class EmployeeDto(Dto):
+class EmployeeMapping(RelationsMapping):
@staticmethod
def get_relations() -> list[ResourceRelation]:
relations: list[ResourceRelation] = [
@@ -102,7 +102,7 @@ def get_relations() -> list[ResourceRelation]:
),
PropertyValueConstraint(
property_name="parttime_schedule",
- values=[ParttimeScheduleDto],
+ values=[ParttimeScheduleMapping],
treat_as_mandatory=True,
invalid_value=IGNORE,
invalid_value_error_code=400,
@@ -111,7 +111,7 @@ def get_relations() -> list[ResourceRelation]:
return relations
-class PatchEmployeeDto(Dto):
+class PatchEmployeeMapping(RelationsMapping):
@staticmethod
def get_parameter_relations() -> list[ResourceRelation]:
relations: list[ResourceRelation] = [
@@ -149,7 +149,7 @@ def get_relations() -> list[ResourceRelation]:
return relations
-class EnergyLabelDto(Dto):
+class EnergyLabelMapping(RelationsMapping):
@staticmethod
def get_path_relations() -> list[PathPropertiesConstraint]:
relations: list[PathPropertiesConstraint] = [
@@ -173,7 +173,7 @@ def get_parameter_relations() -> list[ResourceRelation]:
return relations
-class MessageDto(Dto):
+class MessageMapping(RelationsMapping):
@staticmethod
def get_parameter_relations() -> list[ResourceRelation]:
relations: list[ResourceRelation] = [
@@ -196,14 +196,14 @@ def get_parameter_relations() -> list[ResourceRelation]:
return relations
-DTO_MAPPING: dict[tuple[str, str], type[Dto]] = {
- ("/wagegroups", "post"): WagegroupDto,
- ("/wagegroups/{wagegroup_id}", "delete"): WagegroupDeleteDto,
- ("/wagegroups/{wagegroup_id}", "put"): WagegroupDto,
- ("/employees", "post"): EmployeeDto,
- ("/employees/{employee_id}", "patch"): PatchEmployeeDto,
- ("/energy_label/{zipcode}/{home_number}", "get"): EnergyLabelDto,
- ("/secret_message", "get"): MessageDto,
+RELATIONS_MAPPING: dict[tuple[str, str], type[RelationsMapping]] = {
+ ("/wagegroups", "post"): WagegroupMapping,
+ ("/wagegroups/{wagegroup_id}", "delete"): WagegroupDeleteMapping,
+ ("/wagegroups/{wagegroup_id}", "put"): WagegroupMapping,
+ ("/employees", "post"): EmployeeMapping,
+ ("/employees/{employee_id}", "patch"): PatchEmployeeMapping,
+ ("/energy_label/{zipcode}/{home_number}", "get"): EnergyLabelMapping,
+ ("/secret_message", "get"): MessageMapping,
}
@@ -220,8 +220,8 @@ def my_transformer(identifier_name: str) -> str:
"/wagegroups/{wagegroup_id}/employees": "identification",
}
-# NOTE: WagegroupDeleteDto does not have path mappings for testing purposes
-PATH_MAPPING: dict[str, type[Dto]] = {
- "/energy_label/{zipcode}/{home_number}": EnergyLabelDto,
- "/wagegroups/{wagegroup_id}": WagegroupDeleteDto,
+# NOTE: WagegroupDeleteMapping does not have path mappings for testing purposes
+PATH_MAPPING: dict[str, type[RelationsMapping]] = {
+ "/energy_label/{zipcode}/{home_number}": EnergyLabelMapping,
+ "/wagegroups/{wagegroup_id}": WagegroupDeleteMapping,
}
diff --git a/tests/variables.py b/tests/variables.py
index 5ac9347..be85c53 100644
--- a/tests/variables.py
+++ b/tests/variables.py
@@ -2,63 +2,7 @@
from requests.auth import HTTPDigestAuth
-from OpenApiLibCore import (
- IGNORE,
- Dto,
- IdDependency,
- IdReference,
- PropertyValueConstraint,
- ResourceRelation,
- UniquePropertyValueConstraint,
-)
-
-
-class WagegroupDto(Dto):
- @staticmethod
- def get_relations() -> list[ResourceRelation]:
- relations: list[ResourceRelation] = [
- UniquePropertyValueConstraint(
- property_name="id",
- value="Teapot",
- error_code=418,
- ),
- IdReference(
- property_name="wagegroup_id",
- post_path="/employees",
- error_code=406,
- ),
- PropertyValueConstraint(
- property_name="overtime_percentage",
- values=[IGNORE],
- invalid_value=110,
- invalid_value_error_code=422,
- ),
- PropertyValueConstraint(
- property_name="hourly_rate",
- values=[80.50, 90.95, 99.99],
- ),
- ]
- return relations
-
-
-class EmployeeDto(Dto):
- @staticmethod
- def get_relations() -> list[ResourceRelation]:
- relations: list[ResourceRelation] = [
- IdDependency(
- property_name="wagegroup_id",
- get_path="/wagegroups",
- error_code=451,
- ),
- PropertyValueConstraint(
- property_name="date_of_birth",
- values=["1970-07-07", "1980-08-08", "1990-09-09"],
- invalid_value="2020-02-20",
- invalid_value_error_code=403,
- error_code=422,
- ),
- ]
- return relations
+from OpenApiLibCore import IdReference
def get_variables() -> dict[str, Any]:
@@ -73,14 +17,10 @@ def get_variables() -> dict[str, Any]:
post_path="/employees/{employee_id}",
error_code=406,
)
- wagegroup_dto = WagegroupDto
- employee_dto = EmployeeDto
extra_headers: dict[str, str] = {"foo": "bar", "eggs": "bacon"}
return {
"ID_REFERENCE": id_reference,
"INVALID_ID_REFERENCE": invalid_id_reference,
- "WAGEGROUP_DTO": wagegroup_dto,
- "EMPLOYEE_DTO": employee_dto,
"EXTRA_HEADERS": extra_headers,
"API_KEY": {"api_key": "Super secret key"},
"DIGEST_AUTH": HTTPDigestAuth(username="Jane", password="Joe"),
From 713e3128572099f236366df6b7ab7c89ad643760 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Tue, 23 Dec 2025 10:15:46 +0000
Subject: [PATCH 52/55] Validated Request, Perform Authorized Request and Get
Request Values Object keywords added
---
src/OpenApiDriver/openapi_executors.py | 31 +++----
src/OpenApiLibCore/__init__.py | 3 +
src/OpenApiLibCore/openapi_libcore.py | 110 +++++++++++++++++++++++--
3 files changed, 117 insertions(+), 27 deletions(-)
diff --git a/src/OpenApiDriver/openapi_executors.py b/src/OpenApiDriver/openapi_executors.py
index 42da145..c56e4cb 100644
--- a/src/OpenApiDriver/openapi_executors.py
+++ b/src/OpenApiDriver/openapi_executors.py
@@ -22,7 +22,6 @@
from OpenApiLibCore import (
OpenApiLibCore,
RequestData,
- RequestValues,
ValidationLevel,
)
from OpenApiLibCore.annotations import JSON
@@ -78,7 +77,7 @@ def _run_keyword(
@overload
def _run_keyword(
- keyword_name: Literal["perform_validated_request"], *args: object
+ keyword_name: Literal["validated_request"], *args: object
) -> None: ... # pragma: no cover
@@ -303,16 +302,14 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
f"No relation found to cause status_code {status_code}."
)
_run_keyword(
- "perform_validated_request",
+ "validated_request",
path,
status_code,
- RequestValues(
- url=url,
- method=method,
- params=params,
- headers=headers,
- json_data=json_data,
- ),
+ url,
+ method,
+ params,
+ headers,
+ json_data,
original_data,
)
if status_code < int(HTTPStatus.MULTIPLE_CHOICES) and (
@@ -335,16 +332,14 @@ def test_endpoint(self, path: str, method: str, status_code: int) -> None:
if method == "PATCH":
original_data = self.get_original_data(url=url)
_run_keyword(
- "perform_validated_request",
+ "validated_request",
path,
status_code,
- RequestValues(
- url=url,
- method=method,
- params=params,
- headers=headers,
- json_data=json_data,
- ),
+ url,
+ method,
+ params,
+ headers,
+ json_data,
original_data,
)
diff --git a/src/OpenApiLibCore/__init__.py b/src/OpenApiLibCore/__init__.py
index d91231d..aae6887 100644
--- a/src/OpenApiLibCore/__init__.py
+++ b/src/OpenApiLibCore/__init__.py
@@ -40,6 +40,7 @@
"set_auth",
"set_extra_headers",
"get_request_values",
+ "get_request_values_object",
"get_request_data",
"get_invalid_body_data",
"get_invalidated_parameters",
@@ -51,6 +52,8 @@
"get_invalidated_url",
"ensure_in_use",
"authorized_request",
+ "perform_authorized_request",
+ "validated_request",
"perform_validated_request",
"validate_response_using_validator",
"assert_href_to_resource_is_valid",
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index c445ebb..e740677 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -243,7 +243,12 @@ def get_request_values(
method: str,
overrides: Mapping[str, JSON] = default_json_mapping,
) -> RequestValues:
- """Return an object with all (valid) request values needed to make a request."""
+ """
+ Return an object with all (valid) request values needed to make a request.
+
+ The `overrides` dictionary can be used to pass specific values for parameters
+ instead of having them be generated automatically.
+ """
json_data: JSON = {}
url = _run_keyword("get_valid_url", path)
@@ -454,15 +459,11 @@ def authorized_request( # pylint: disable=too-many-arguments
files: Any = None,
) -> Response:
"""
- Perform a request using the security token or authentication set in the library.
-
- `json_data`, `data` and `files` are passed to `requests.request`s `json`,
- `data` and `files` parameters unaltered.
- See the requests documentation for details:
- https://requests.readthedocs.io/en/latest/api/#requests.request
+ See the `Perform Authorized Request` keyword.
- > Note: provided username / password or auth objects take precedence over token
- based security
+ The difference between these keywords is that this keyword accepts separate
+ arguments where `Perform Validated Request` accepts a `RequestValues` object
+ (see the `Get Request Values` keyword for details).
"""
headers = deepcopy(headers) if headers else {}
if self.extra_headers:
@@ -489,8 +490,97 @@ def authorized_request( # pylint: disable=too-many-arguments
logger.debug(f"Response text: {response.text}")
return response
+ @keyword
+ def perform_authorized_request( # pylint: disable=too-many-arguments
+ self,
+ request_values: RequestValues,
+ data: Any = None,
+ files: Any = None,
+ ) -> Response:
+ """
+ Perform a request using the security token or authentication set in the library.
+
+ `json_data`, `data` and `files` are passed to `requests.request`s `json`,
+ `data` and `files` parameters unaltered.
+ See the requests documentation for details:
+ https://requests.readthedocs.io/en/latest/api/#requests.request
+
+ See also `Authorized Request` and `Get Request Values`.
+
+ > Note: provided username / password or auth objects take precedence over token
+ based security
+ """
+ return self.authorized_request(
+ url=request_values.url,
+ method=request_values.method,
+ params=request_values.params,
+ headers=request_values.headers,
+ json_data=request_values.json_data,
+ data=data,
+ files=files,
+ )
+
+ @keyword
+ def get_request_values_object(
+ self,
+ url: str,
+ method: str,
+ params: dict[str, JSON] = {},
+ headers: dict[str, str] = {},
+ json_data: JSON = None,
+ ) -> RequestValues:
+ """
+ This keyword can be used to instantiate a RequestValues object that can be used
+ with the `Perform Authorized Request` and `Perform Validated Request` keywords.
+
+ This is a utility keyword that can make certain test cases or keywords more
+ concise, but logging and debugging information will be more limited.
+
+ See also the `Get Request Values` keyword.
+ """
+ return RequestValues(
+ url=url,
+ method=method,
+ params=params,
+ headers=headers,
+ json_data=json_data,
+ )
+
# endregion
# region: validation keywords
+ @keyword
+ def validated_request(
+ self,
+ path: str,
+ status_code: int,
+ url: str,
+ method: str,
+ params: dict[str, JSON],
+ headers: dict[str, str],
+ json_data: JSON,
+ original_data: Mapping[str, JSON] = default_json_mapping,
+ ) -> None:
+ """
+ See the `Perform Validated Request` keyword.
+
+ The difference between these keywords is that this keyword accepts separate
+ arguments where `Perform Validated Request` accepts a `RequestValues` object
+ (see the `Get Request Values` keyword for details).
+ """
+ request_values = RequestValues(
+ url=url,
+ method=method,
+ params=params,
+ headers=headers,
+ json_data=json_data,
+ )
+ _validation.perform_validated_request(
+ path=path,
+ status_code=status_code,
+ request_values=request_values,
+ original_data=original_data,
+ )
+
@keyword
def perform_validated_request(
self,
@@ -503,6 +593,8 @@ def perform_validated_request(
This keyword first calls the Authorized Request keyword, then the Validate
Response keyword and finally validates, for `DELETE` operations, whether
the target resource was indeed deleted (OK response) or not (error responses).
+
+ See also `Validated Request` and `Get Request Values`.
"""
_validation.perform_validated_request(
path=path,
From 2490cdb66c39add09fd6d74f6e8734ac64795701 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Tue, 23 Dec 2025 17:07:18 +0000
Subject: [PATCH 53/55] Convert Request Values To Dict keyword adeed, tests
added, annotation tweaks, release notes updated.
---
docs/releases.md | 6 +++
src/OpenApiLibCore/__init__.py | 1 +
src/OpenApiLibCore/openapi_libcore.py | 31 +++++++++++--
.../libcore/suites/test_request_values.robot | 46 +++++++++++++++++++
4 files changed, 80 insertions(+), 4 deletions(-)
create mode 100644 tests/libcore/suites/test_request_values.robot
diff --git a/docs/releases.md b/docs/releases.md
index 9c8c37c..77136e4 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -8,6 +8,12 @@
- The `Relations` still need to be reworked to align with this change.
- Refactored retrieving / loading of the OpenAPI spec.
- This closes [issue #93: SSL error even if cert / verify is set](https://github.com/MarketSquare/robotframework-openapitools/issues/93).
+- Added keywords to make it easier to work with the `RequestValues` object:
+ - `Get Request Values Object` can be used to create a `RequestValues` instance from pre-defined values (where `Get Request Values` generates all values automatically).
+ - `Perform Authorized Request` is functionally the same as exisiting `Authorized Request` keyword, but it accepts a `RequestValues` instance as argument.
+ - `Validated Request` is functionally the same as the existing `Perform Validated Request` keyword, but it accepts the data as separate arguments instead of the `RequestValues`.
+ - `Convert Request Values To Dict` can be used to get a (Python) dict represenation of a `RequestValues` object that can be used with e.g. the Collections keywords for working with dictionaries.
+ - Thise closes [issue #98: Add keywords to simplify using Authorized Request and Perform Validated Request](https://github.com/MarketSquare/robotframework-openapitools/issues/98).
- Improved handling of `treat_as_mandatory` on a `PropertyValueConstraint`.
- Added support for using `IGNORE` as `invalid_value` on a `PropertyValueConstraint`.
diff --git a/src/OpenApiLibCore/__init__.py b/src/OpenApiLibCore/__init__.py
index aae6887..27d4e82 100644
--- a/src/OpenApiLibCore/__init__.py
+++ b/src/OpenApiLibCore/__init__.py
@@ -41,6 +41,7 @@
"set_extra_headers",
"get_request_values",
"get_request_values_object",
+ "convert_request_values_to_dict",
"get_request_data",
"get_invalid_body_data",
"get_invalidated_parameters",
diff --git a/src/OpenApiLibCore/openapi_libcore.py b/src/OpenApiLibCore/openapi_libcore.py
index e740677..47eb281 100644
--- a/src/OpenApiLibCore/openapi_libcore.py
+++ b/src/OpenApiLibCore/openapi_libcore.py
@@ -491,7 +491,7 @@ def authorized_request( # pylint: disable=too-many-arguments
return response
@keyword
- def perform_authorized_request( # pylint: disable=too-many-arguments
+ def perform_authorized_request(
self,
request_values: RequestValues,
data: Any = None,
@@ -538,6 +538,11 @@ def get_request_values_object(
See also the `Get Request Values` keyword.
"""
+ # RequestValues has methods that perform mutations on its values, so
+ # deepcopy to prevent mutation by reference.
+ params = deepcopy(params) if params else {}
+ headers = deepcopy(headers) if headers else {}
+ json_data = deepcopy(json_data)
return RequestValues(
url=url,
method=method,
@@ -546,6 +551,19 @@ def get_request_values_object(
json_data=json_data,
)
+ @keyword
+ def convert_request_values_to_dict(
+ self, request_values: RequestValues
+ ) -> dict[str, JSON]:
+ """Convert a RequestValues object to a dictionary."""
+ return {
+ "url": request_values.url,
+ "method": request_values.method,
+ "params": request_values.params,
+ "headers": request_values.headers,
+ "json_data": request_values.json_data,
+ }
+
# endregion
# region: validation keywords
@keyword
@@ -555,9 +573,9 @@ def validated_request(
status_code: int,
url: str,
method: str,
- params: dict[str, JSON],
- headers: dict[str, str],
- json_data: JSON,
+ params: dict[str, JSON] = {},
+ headers: dict[str, str] = {},
+ json_data: JSON = None,
original_data: Mapping[str, JSON] = default_json_mapping,
) -> None:
"""
@@ -567,6 +585,11 @@ def validated_request(
arguments where `Perform Validated Request` accepts a `RequestValues` object
(see the `Get Request Values` keyword for details).
"""
+ # RequestValues has methods that perform mutations on its values, so
+ # deepcopy to prevent mutation by reference.
+ params = deepcopy(params) if params else {}
+ headers = deepcopy(headers) if headers else {}
+ json_data = deepcopy(json_data)
request_values = RequestValues(
url=url,
method=method,
diff --git a/tests/libcore/suites/test_request_values.robot b/tests/libcore/suites/test_request_values.robot
new file mode 100644
index 0000000..72d7776
--- /dev/null
+++ b/tests/libcore/suites/test_request_values.robot
@@ -0,0 +1,46 @@
+*** Settings ***
+Variables ${ROOT}/tests/variables.py
+Library Collections
+Library OpenApiLibCore
+... source=${ORIGIN}/openapi.json
+... origin=${ORIGIN}
+... base_path=${EMPTY}
+... mappings_path=${ROOT}/tests/user_implemented/custom_user_mappings.py
+
+Test Tags rf7
+
+
+*** Variables ***
+${ORIGIN} http://localhost:8000
+
+
+*** Test Cases ***
+Test Requests Using RequestValues
+ ${request_values}= Get Request Values path=/employees method=POST
+ ${request_values_dict}= Convert Request Values To Dict ${request_values}
+
+ ${response_using_request_values}= Perform Authorized Request request_values=${request_values}
+ Should Be Equal As Integers ${response_using_request_values.status_code} 201
+
+ ${response_using_dict}= Authorized Request &{request_values_dict}
+ Should Be Equal As Integers ${response_using_dict.status_code} 201
+
+ VAR ${response_dict_using_request_values}= ${response_using_request_values.json()}
+ VAR ${response_dict_using_request_values_dict}= ${response_using_dict.json()}
+
+ Should Not Be Equal
+ ... ${response_dict_using_request_values}[identification]
+ ... ${response_dict_using_request_values_dict}[identification]
+ Should Not Be Equal
+ ... ${response_dict_using_request_values}[employee_number]
+ ... ${response_dict_using_request_values_dict}[employee_number]
+
+ Remove From Dictionary ${response_dict_using_request_values} identification employee_number
+ Remove From Dictionary ${response_dict_using_request_values_dict} identification employee_number
+
+ Should Be Equal ${response_dict_using_request_values} ${response_dict_using_request_values_dict}
+
+ ${request_values_object}= Get Request Values Object &{request_values_dict}
+
+ Perform Validated Request path=/employees status_code=201 request_values=${request_values_object}
+ Validated Request path=/employees status_code=201 &{request_values_dict}
From 8a2cee0e2f463efefc0a8af73d6294ffc11d2f01 Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Tue, 23 Dec 2025 17:25:43 +0000
Subject: [PATCH 54/55] Generated build artifacts for v2.0.0b1 release
---
docs/coverage-badge.svg | 2 +-
docs/index.html | 98 +--
docs/openapi_libcore.html | 779 ++++++++++-----------
docs/openapidriver.html | 779 ++++++++++-----------
src/OpenApiDriver/openapidriver.libspec | 81 ++-
src/OpenApiLibCore/openapi_libcore.libspec | 386 +++++++---
6 files changed, 1132 insertions(+), 993 deletions(-)
diff --git a/docs/coverage-badge.svg b/docs/coverage-badge.svg
index 04c7fe6..7b1e43f 100644
--- a/docs/coverage-badge.svg
+++ b/docs/coverage-badge.svg
@@ -1 +1 @@
-coverage: 93.36% coverage coverage 93.36% 93.36%
\ No newline at end of file
+coverage: 93.63% coverage coverage 93.63% 93.63%
\ No newline at end of file
diff --git a/docs/index.html b/docs/index.html
index cfef690..f60aa21 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -644,10 +644,10 @@ The custom mappings file
from OpenApiLibCore import (
IGNORE,
- Dto,
IdDependency,
IdReference,
PathPropertiesConstraint,
+ RelationsMapping,
PropertyValueConstraint,
UniquePropertyValueConstraint,
)
@@ -658,7 +658,7 @@ The custom mappings file
}
-class MyDtoThatDoesNothing(Dto):
+class MyMappingThatDoesNothing(RelationsMapping):
@staticmethod
def get_relations():
relations = []
@@ -675,13 +675,13 @@ The custom mappings file
return relations
-DTO_MAPPING = {
- ("/myspecialpath", "post"): MyDtoThatDoesNothing
+RELATIONS_MAPPING = {
+ ("/myspecialpath", "post"): MyMappingThatDoesNothing
}
PATH_MAPPING = {
- "/mypathwithexternalid/{external_id}": MyDtoThatDoesNothing
+ "/mypathwithexternalid/{external_id}": MyMappingThatDoesNothing
}
@@ -693,13 +693,13 @@ The custom mappings file
Here the classes needed to implement custom mappings are imported.
This section can just be copied without changes.
The ID_MAPPING "constant" definition / assignment.
-The section defining the mapping Dtos. More on this later.
-The DTO_MAPPING "constant" definition / assignment.
+The section defining the RelationsMappings. More on this later.
+The RELATIONS_MAPPING "constant" definition / assignment.
The PATH_MAPPING "constant" definition / assignment.
-The ID_MAPPING, DTO_MAPPING and PATH_MAPPING
-When a custom mappings file is used, the OpenApiLibCore will attempt to import it and then import DTO_MAPPING, PATH_MAPPING and ID_MAPPING from it.
+The ID_MAPPING, RELATIONS_MAPPING and PATH_MAPPING
+When a custom mappings file is used, the OpenApiLibCore will attempt to import it and then import RELATIONS_MAPPING, PATH_MAPPING and ID_MAPPING from it.
For this reason, the exact same name must be used in a custom mappings file (capitilization matters).
The ID_MAPPING
@@ -721,18 +721,18 @@ The ID_MAPPING
-The DTO_MAPPING
-The DTO_MAPPING is a dictionary with a tuple as its key and a mappings Dto as its value.
+The RELATIONS_MAPPING
+The RELATIONS_MAPPING is a dictionary with a tuple as its key and a RelationsMapping as its value.
The tuple must be in the form ("path_from_the_paths_section", "method_supported_by_the_path").
The path_from_the_paths_section must be exactly as found in the openapi document.
The method_supported_by_the_path must be one of the methods supported by the path and must be in lowercase.
The PATH_MAPPING
-The PATH_MAPPING is a dictionary with a "path_from_the_paths_section" as its key and a mappings Dto as its value.
+The PATH_MAPPING is a dictionary with a "path_from_the_paths_section" as its key and a RelationsMapping as its value.
The path_from_the_paths_section must be exactly as found in the openapi document.
-Dto mapping classes
+RelationsMapping classes
As can be seen from the import section above, a number of classes are available to deal with relations between resources and / or constraints on properties.
Each of these classes is designed to handle a relation or constraint commonly seen in REST APIs.
@@ -757,7 +757,7 @@ IdReference
This relation can be implemented as follows:
-class EmployeeDto(Dto):
+class EmployeeMapping(RelationsMapping):
@staticmethod
def get_relations():
relations = [
@@ -769,8 +769,8 @@ IdReference
]
return relations
-DTO_MAPPING = {
- ("/employees", "post"): EmployeeDto
+RELATIONS_MAPPING = {
+ ("/employees", "post"): EmployeeMapping
}
@@ -801,7 +801,7 @@ IdDependency
To verify that the specified error_code indeed occurs when attempting to delete the Wagegroup, we can implement the following dependency:
-class WagegroupDto(Dto):
+class WagegroupMapping(RelationsMapping):
@staticmethod
def get_relations():
relations = [
@@ -813,8 +813,8 @@ IdDependency
]
return relations
-DTO_MAPPING = {
- ("/wagegroups/{wagegroup_id}", "delete"): WagegroupDto
+RELATIONS_MAPPING = {
+ ("/wagegroups/{wagegroup_id}", "delete"): WagegroupMapping
}
@@ -833,7 +833,7 @@ UniquePropertyValueConstraint
To verify that the specified error_code occurs when attempting to post an Employee with an employee_number that is already in use, we can implement the following dependency:
-class EmployeeDto(Dto):
+class EmployeeMapping(RelationsMapping):
@staticmethod
def get_relations():
relations = [
@@ -845,15 +845,15 @@ UniquePropertyValueConstraint
]
return relations
-DTO_MAPPING = {
- ("/employees", "post"): EmployeeDto,
- ("/employees/${employee_id}", "put"): EmployeeDto,
- ("/employees/${employee_id}", "patch"): EmployeeDto,
+RELATIONS_MAPPING = {
+ ("/employees", "post"): EmployeeMapping,
+ ("/employees/${employee_id}", "put"): EmployeeMapping,
+ ("/employees/${employee_id}", "patch"): EmployeeMapping,
}
-Note how this example reuses the EmployeeDto to model the uniqueness constraint for all the operations (post, put and patch) that all relate to the same employee_number.
+Note how this example reuses the EmployeeMapping to model the uniqueness constraint for all the operations (post, put and patch) that all relate to the same employee_number.
@@ -867,7 +867,7 @@ PropertyValueConstraint
This type of constraint can be modeled as follows:
-class EmployeeDto(Dto):
+class EmployeeMapping(RelationsMapping):
@staticmethod
def get_relations():
relations = [
@@ -879,10 +879,10 @@ PropertyValueConstraint
]
return relations
-DTO_MAPPING = {
- ("/employees", "post"): EmployeeDto,
- ("/employees/${employee_id}", "put"): EmployeeDto,
- ("/employees/${employee_id}", "patch"): EmployeeDto,
+RELATIONS_MAPPING = {
+ ("/employees", "post"): EmployeeMapping,
+ ("/employees/${employee_id}", "put"): EmployeeMapping,
+ ("/employees/${employee_id}", "patch"): EmployeeMapping,
}
@@ -891,7 +891,7 @@ PropertyValueConstraint
To support additional restrictions like these, the PropertyValueConstraint supports two additional properties: error_value and invalid_value_error_code:
-class EmployeeDto(Dto):
+class EmployeeMapping(RelationsMapping):
@staticmethod
def get_relations():
relations = [
@@ -905,10 +905,10 @@ PropertyValueConstraint
]
return relations
-DTO_MAPPING = {
- ("/employees", "post"): EmployeeDto,
- ("/employees/${employee_id}", "put"): EmployeeDto,
- ("/employees/${employee_id}", "patch"): EmployeeDto,
+RELATIONS_MAPPING = {
+ ("/employees", "post"): EmployeeMapping,
+ ("/employees/${employee_id}", "put"): EmployeeMapping,
+ ("/employees/${employee_id}", "patch"): EmployeeMapping,
}
@@ -920,7 +920,7 @@ PropertyValueConstraint
This situation can be handled by use of the special IGNORE value (see below for other uses):
-class EmployeeDto(Dto):
+class EmployeeMapping(RelationsMapping):
@staticmethod
def get_relations():
relations = [
@@ -934,10 +934,10 @@ PropertyValueConstraint
]
return relations
-DTO_MAPPING = {
- ("/employees", "post"): EmployeeDto,
- ("/employees/${employee_id}", "put"): EmployeeDto,
- ("/employees/${employee_id}", "patch"): EmployeeDto,
+RELATIONS_MAPPING = {
+ ("/employees", "post"): EmployeeMapping,
+ ("/employees/${employee_id}", "put"): EmployeeMapping,
+ ("/employees/${employee_id}", "patch"): EmployeeMapping,
}
@@ -950,7 +950,7 @@ PathPropertiesConstraint
Just use this for the path
-Note: The PathPropertiesConstraint is only applicable to the get_path_relations in a Dto and only the PATH_MAPPING uses the get_path_relations.
+Note: The PathPropertiesConstraint is only applicable to the get_path_relations in a RelationsMapping and only the PATH_MAPPING uses the get_path_relations.
To be able to automatically perform endpoint validations, the OpenApiLibCore has to construct the url for the resource from the path as found in the openapi document.
@@ -970,7 +970,7 @@ PathPropertiesConstraint
It should be clear that the OpenApiLibCore won't be able to acquire a valid month and date. The PathPropertiesConstraint can be used in this case:
-class BirthdaysDto(Dto):
+class BirthdaysMapping(RelationsMapping):
@staticmethod
def get_path_relations():
relations = [
@@ -979,7 +979,7 @@ PathPropertiesConstraint
return relations
PATH_MAPPING = {
- "/birthdays/{month}/{date}": BirthdaysDto
+ "/birthdays/{month}/{date}": BirthdaysMapping
}
@@ -999,7 +999,7 @@ IGNORE
To prevent OpenApiLibCore from generating invalid combinations of path and query parameters in this type of endpoint, the IGNORE special value can be used to ensure the related query parameter is never send in a request.
-class EnergyLabelDto(Dto):
+class EnergyLabelMapping(RelationsMapping):
@staticmethod
def get_parameter_relations():
relations = [
@@ -1018,8 +1018,8 @@ IGNORE
]
return relations
-DTO_MAPPING = {
- ("/energy_label/{zipcode}/{home_number}", "get"): EnergyLabelDto,
+RELATIONS_MAPPING = {
+ ("/energy_label/{zipcode}/{home_number}", "get"): EnergyLabelMapping,
}
@@ -1032,7 +1032,7 @@ IGNORE
Such situations can be handled by a mapping as shown below:
-class PatchEmployeeDto(Dto):
+class PatchEmployeeMapping(RelationsMapping):
@staticmethod
def get_parameter_relations() -> list[ResourceRelation]:
relations: list[ResourceRelation] = [
@@ -1051,8 +1051,8 @@ IGNORE
]
return relations
-DTO_MAPPING = {
- ("/employees/{employee_id}", "patch"): PatchEmployeeDto,
+RELATIONS_MAPPING = {
+ ("/employees/{employee_id}", "patch"): PatchEmployeeMapping,
}
diff --git a/docs/openapi_libcore.html b/docs/openapi_libcore.html
index bc4c6cf..16e2c07 100644
--- a/docs/openapi_libcore.html
+++ b/docs/openapi_libcore.html
@@ -1,410 +1,387 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
Opening library documentation failed
-
- Verify that you have JavaScript enabled in your browser.
-
- Make sure you are using a modern enough browser . If using
- Internet Explorer, version 11 is required.
-
-
- Check are there messages in your browser's
- JavaScript error log . Please report the problem if you suspect
- you have encountered a bug.
-
-
-
+
+
Opening library documentation failed
+
+Verify that you have JavaScript enabled in your browser.
+
+Make sure you are using a modern enough browser . If using
+Internet Explorer, version 11 is required.
+
+
+Check are there messages in your browser's
+JavaScript error log . Please report the problem if you suspect
+you have encountered a bug.
+
+
+
-
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
+ data-v-2754030d="" fill="var(--text-color)">`,t.classList.add("modal-close-button");let r=document.createElement("div");r.classList.add("modal-close-button-container"),r.appendChild(t),t.addEventListener("click",()=>{rl()}),e.appendChild(r),r.addEventListener("click",()=>{rl()});let n=document.createElement("div");n.id="modal",n.classList.add("modal"),n.addEventListener("click",({target:e})=>{"A"===e.tagName.toUpperCase()&&rl()});let o=document.createElement("div");o.id="modal-content",o.classList.add("modal-content"),n.appendChild(o),e.appendChild(n),document.body.appendChild(e),document.addEventListener("keydown",({key:e})=>{"Escape"===e&&rl()})}()}renderTemplates(){this.renderLibdocTemplate("base",this.libdoc,"#root"),this.libdoc.inits.length>0&&this.renderImporting(),this.renderShortcuts(),this.renderKeywords(),this.renderLibdocTemplate("data-types"),this.renderLibdocTemplate("footer")}initHashEvents(){window.addEventListener("hashchange",function(){document.getElementsByClassName("hamburger-menu")[0].checked=!1},!1),window.addEventListener("hashchange",function(){if(0==window.location.hash.indexOf("#type-")){let e="#type-modal-"+decodeURI(window.location.hash.slice(6)),t=document.querySelector(".data-types").querySelector(e);t&&rs(t)}},!1),this.scrollToHash()}initTagSearch(){let e=new URLSearchParams(window.location.search),t="";e.has("tag")&&(t=e.get("tag"),this.tagSearch(t,window.location.hash)),this.libdoc.tags.length&&(this.libdoc.selectedTag=t,this.renderLibdocTemplate("tags-shortcuts"),document.getElementById("tags-shortcuts-container").onchange=e=>{let t=e.target.selectedOptions[0].value;""!=t?this.tagSearch(t):this.clearTagSearch()})}initLanguageMenu(){this.renderTemplate("language",{languages:this.translations.getLanguageCodes()}),document.querySelectorAll("#language-container ul a").forEach(e=>{e.innerHTML===this.translations.currentLanguage()&&e.classList.toggle("selected"),e.addEventListener("click",()=>{this.translations.setLanguage(e.innerHTML)&&this.render()})}),document.querySelector("#language-container button").addEventListener("click",()=>{document.querySelector("#language-container ul").classList.toggle("hidden")})}renderImporting(){this.renderLibdocTemplate("importing"),this.registerTypeDocHandlers("#importing-container")}renderShortcuts(){this.renderLibdocTemplate("shortcuts"),document.getElementById("toggle-keyword-shortcuts").addEventListener("click",()=>this.toggleShortcuts()),document.querySelector(".clear-search").addEventListener("click",()=>this.clearSearch()),document.querySelector(".search-input").addEventListener("keydown",()=>rc(()=>this.searching(),150)),this.renderLibdocTemplate("keyword-shortcuts"),document.querySelectorAll("a.match").forEach(e=>e.addEventListener("click",this.closeMenu))}registerTypeDocHandlers(e){document.querySelectorAll(`${e} a.type`).forEach(e=>e.addEventListener("click",e=>{let t=e.target.dataset.typedoc;rs(document.querySelector(`#type-modal-${t}`))}))}renderKeywords(e=null){null==e&&(e=this.libdoc),this.renderLibdocTemplate("keywords",e),document.querySelectorAll(".kw-tags span").forEach(e=>{e.addEventListener("click",e=>{this.tagSearch(e.target.innerText)})}),this.registerTypeDocHandlers("#keywords-container"),document.getElementById("keyword-statistics-header").innerText=""+this.libdoc.keywords.length}setTheme(){document.documentElement.setAttribute("data-theme",this.getTheme())}getTheme(){return null!=this.libdoc.theme?this.libdoc.theme:window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}scrollToHash(){if(window.location.hash){let e=window.location.hash.substring(1),t=document.getElementById(decodeURIComponent(e));null!=t&&t.scrollIntoView()}}tagSearch(e,t){document.getElementsByClassName("search-input")[0].value="";let r={tags:!0,tagsExact:!0},n=window.location.pathname+"?tag="+e+(t||"");this.markMatches(e,r),this.highlightMatches(e,r),history.replaceState&&history.replaceState(null,"",n),document.getElementById("keyword-shortcuts-container").scrollTop=0}clearTagSearch(){document.getElementsByClassName("search-input")[0].value="",history.replaceState&&history.replaceState(null,"",window.location.pathname),this.resetKeywords()}searching(){this.searchTime=Date.now();let e=document.getElementsByClassName("search-input")[0].value,t={name:!0,args:!0,doc:!0,tags:!0};e?requestAnimationFrame(()=>{this.markMatches(e,t,this.searchTime,()=>{this.highlightMatches(e,t,this.searchTime),document.getElementById("keyword-shortcuts-container").scrollTop=0})}):this.resetKeywords()}highlightMatches(e,t,n){if(n&&n!==this.searchTime)return;let o=document.querySelectorAll("#shortcuts-container .match"),i=document.querySelectorAll("#keywords-container .match");if(t.name&&(new(r(ef))(o).mark(e),new(r(ef))(i).mark(e)),t.args&&new(r(ef))(document.querySelectorAll("#keywords-container .match .args")).mark(e),t.doc&&new(r(ef))(document.querySelectorAll("#keywords-container .match .doc")).mark(e),t.tags){let n=document.querySelectorAll("#keywords-container .match .tags a, #tags-shortcuts-container .match .tags a");if(t.tagsExact){let t=[];n.forEach(r=>{r.textContent?.toUpperCase()==e.toUpperCase()&&t.push(r)}),new(r(ef))(t).mark(e)}else new(r(ef))(n).mark(e)}}markMatches(e,t,r,n){if(r&&r!==this.searchTime)return;let o=e.replace(/[-[\]{}()+?*.,\\^$|#]/g,"\\$&");t.tagsExact&&(o="^"+o+"$");let i=RegExp(o,"i"),a=i.test.bind(i),s={},l=0;s.keywords=this.libdoc.keywords.map(e=>{let r={...e};return r.hidden=!(t.name&&a(r.name))&&!(t.args&&a(r.args))&&!(t.doc&&a(r.doc))&&!(t.tags&&r.tags.some(a)),!r.hidden&&l++,r}),this.renderLibdocTemplate("keyword-shortcuts",s),this.renderKeywords(s),this.libdoc.tags.length&&(this.libdoc.selectedTag=t.tagsExact?e:"",this.renderLibdocTemplate("tags-shortcuts")),document.getElementById("keyword-statistics-header").innerText=l+" / "+s.keywords.length,0===l&&(document.querySelector("#keywords-container table").innerHTML=""),n&&requestAnimationFrame(n)}closeMenu(){document.getElementById("hamburger-menu-input").checked=!1}openKeywordWall(){document.getElementsByClassName("shortcuts")[0].classList.add("keyword-wall"),this.storage.set("keyword-wall","open"),document.getElementById("toggle-keyword-shortcuts").innerText="-"}closeKeywordWall(){document.getElementsByClassName("shortcuts")[0].classList.remove("keyword-wall"),this.storage.set("keyword-wall","close"),document.getElementById("toggle-keyword-shortcuts").innerText="+"}toggleShortcuts(){document.getElementsByClassName("shortcuts")[0].classList.contains("keyword-wall")?this.closeKeywordWall():this.openKeywordWall()}resetKeywords(){this.renderLibdocTemplate("keyword-shortcuts"),this.renderKeywords(),this.libdoc.tags.length&&(this.libdoc.selectedTag="",this.renderLibdocTemplate("tags-shortcuts")),history.replaceState&&history.replaceState(null,"",location.pathname)}clearSearch(){document.getElementsByClassName("search-input")[0].value="";let e=document.getElementById("tags-shortcuts-container");e&&(e.selectedIndex=0),this.resetKeywords()}renderLibdocTemplate(e,t=null,r=""){null==t&&(t=this.libdoc),this.renderTemplate(e,t,r)}renderTemplate(e,t,n=""){let o=document.getElementById(`${e}-template`)?.innerHTML,i=r(eg).compile(o);""===n&&(n=`#${e}-container`),document.body.querySelector(n).innerHTML=i(t)}},rh=libdoc;const rp=new eh("libdoc"),rd=ed.getInstance(rh.lang);new ru(rh,rp,rd).render();
+
+
+
diff --git a/docs/openapidriver.html b/docs/openapidriver.html
index 9bd63ad..2b53020 100644
--- a/docs/openapidriver.html
+++ b/docs/openapidriver.html
@@ -1,410 +1,387 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
Opening library documentation failed
-
- Verify that you have JavaScript enabled in your browser.
-
- Make sure you are using a modern enough browser . If using
- Internet Explorer, version 11 is required.
-
-
- Check are there messages in your browser's
- JavaScript error log . Please report the problem if you suspect
- you have encountered a bug.
-
-
-
+
+
Opening library documentation failed
+
+Verify that you have JavaScript enabled in your browser.
+
+Make sure you are using a modern enough browser . If using
+Internet Explorer, version 11 is required.
+
+
+Check are there messages in your browser's
+JavaScript error log . Please report the problem if you suspect
+you have encountered a bug.
+
+
+
-
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
+ data-v-2754030d="" fill="var(--text-color)">`,t.classList.add("modal-close-button");let r=document.createElement("div");r.classList.add("modal-close-button-container"),r.appendChild(t),t.addEventListener("click",()=>{rl()}),e.appendChild(r),r.addEventListener("click",()=>{rl()});let n=document.createElement("div");n.id="modal",n.classList.add("modal"),n.addEventListener("click",({target:e})=>{"A"===e.tagName.toUpperCase()&&rl()});let o=document.createElement("div");o.id="modal-content",o.classList.add("modal-content"),n.appendChild(o),e.appendChild(n),document.body.appendChild(e),document.addEventListener("keydown",({key:e})=>{"Escape"===e&&rl()})}()}renderTemplates(){this.renderLibdocTemplate("base",this.libdoc,"#root"),this.libdoc.inits.length>0&&this.renderImporting(),this.renderShortcuts(),this.renderKeywords(),this.renderLibdocTemplate("data-types"),this.renderLibdocTemplate("footer")}initHashEvents(){window.addEventListener("hashchange",function(){document.getElementsByClassName("hamburger-menu")[0].checked=!1},!1),window.addEventListener("hashchange",function(){if(0==window.location.hash.indexOf("#type-")){let e="#type-modal-"+decodeURI(window.location.hash.slice(6)),t=document.querySelector(".data-types").querySelector(e);t&&rs(t)}},!1),this.scrollToHash()}initTagSearch(){let e=new URLSearchParams(window.location.search),t="";e.has("tag")&&(t=e.get("tag"),this.tagSearch(t,window.location.hash)),this.libdoc.tags.length&&(this.libdoc.selectedTag=t,this.renderLibdocTemplate("tags-shortcuts"),document.getElementById("tags-shortcuts-container").onchange=e=>{let t=e.target.selectedOptions[0].value;""!=t?this.tagSearch(t):this.clearTagSearch()})}initLanguageMenu(){this.renderTemplate("language",{languages:this.translations.getLanguageCodes()}),document.querySelectorAll("#language-container ul a").forEach(e=>{e.innerHTML===this.translations.currentLanguage()&&e.classList.toggle("selected"),e.addEventListener("click",()=>{this.translations.setLanguage(e.innerHTML)&&this.render()})}),document.querySelector("#language-container button").addEventListener("click",()=>{document.querySelector("#language-container ul").classList.toggle("hidden")})}renderImporting(){this.renderLibdocTemplate("importing"),this.registerTypeDocHandlers("#importing-container")}renderShortcuts(){this.renderLibdocTemplate("shortcuts"),document.getElementById("toggle-keyword-shortcuts").addEventListener("click",()=>this.toggleShortcuts()),document.querySelector(".clear-search").addEventListener("click",()=>this.clearSearch()),document.querySelector(".search-input").addEventListener("keydown",()=>rc(()=>this.searching(),150)),this.renderLibdocTemplate("keyword-shortcuts"),document.querySelectorAll("a.match").forEach(e=>e.addEventListener("click",this.closeMenu))}registerTypeDocHandlers(e){document.querySelectorAll(`${e} a.type`).forEach(e=>e.addEventListener("click",e=>{let t=e.target.dataset.typedoc;rs(document.querySelector(`#type-modal-${t}`))}))}renderKeywords(e=null){null==e&&(e=this.libdoc),this.renderLibdocTemplate("keywords",e),document.querySelectorAll(".kw-tags span").forEach(e=>{e.addEventListener("click",e=>{this.tagSearch(e.target.innerText)})}),this.registerTypeDocHandlers("#keywords-container"),document.getElementById("keyword-statistics-header").innerText=""+this.libdoc.keywords.length}setTheme(){document.documentElement.setAttribute("data-theme",this.getTheme())}getTheme(){return null!=this.libdoc.theme?this.libdoc.theme:window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}scrollToHash(){if(window.location.hash){let e=window.location.hash.substring(1),t=document.getElementById(decodeURIComponent(e));null!=t&&t.scrollIntoView()}}tagSearch(e,t){document.getElementsByClassName("search-input")[0].value="";let r={tags:!0,tagsExact:!0},n=window.location.pathname+"?tag="+e+(t||"");this.markMatches(e,r),this.highlightMatches(e,r),history.replaceState&&history.replaceState(null,"",n),document.getElementById("keyword-shortcuts-container").scrollTop=0}clearTagSearch(){document.getElementsByClassName("search-input")[0].value="",history.replaceState&&history.replaceState(null,"",window.location.pathname),this.resetKeywords()}searching(){this.searchTime=Date.now();let e=document.getElementsByClassName("search-input")[0].value,t={name:!0,args:!0,doc:!0,tags:!0};e?requestAnimationFrame(()=>{this.markMatches(e,t,this.searchTime,()=>{this.highlightMatches(e,t,this.searchTime),document.getElementById("keyword-shortcuts-container").scrollTop=0})}):this.resetKeywords()}highlightMatches(e,t,n){if(n&&n!==this.searchTime)return;let o=document.querySelectorAll("#shortcuts-container .match"),i=document.querySelectorAll("#keywords-container .match");if(t.name&&(new(r(ef))(o).mark(e),new(r(ef))(i).mark(e)),t.args&&new(r(ef))(document.querySelectorAll("#keywords-container .match .args")).mark(e),t.doc&&new(r(ef))(document.querySelectorAll("#keywords-container .match .doc")).mark(e),t.tags){let n=document.querySelectorAll("#keywords-container .match .tags a, #tags-shortcuts-container .match .tags a");if(t.tagsExact){let t=[];n.forEach(r=>{r.textContent?.toUpperCase()==e.toUpperCase()&&t.push(r)}),new(r(ef))(t).mark(e)}else new(r(ef))(n).mark(e)}}markMatches(e,t,r,n){if(r&&r!==this.searchTime)return;let o=e.replace(/[-[\]{}()+?*.,\\^$|#]/g,"\\$&");t.tagsExact&&(o="^"+o+"$");let i=RegExp(o,"i"),a=i.test.bind(i),s={},l=0;s.keywords=this.libdoc.keywords.map(e=>{let r={...e};return r.hidden=!(t.name&&a(r.name))&&!(t.args&&a(r.args))&&!(t.doc&&a(r.doc))&&!(t.tags&&r.tags.some(a)),!r.hidden&&l++,r}),this.renderLibdocTemplate("keyword-shortcuts",s),this.renderKeywords(s),this.libdoc.tags.length&&(this.libdoc.selectedTag=t.tagsExact?e:"",this.renderLibdocTemplate("tags-shortcuts")),document.getElementById("keyword-statistics-header").innerText=l+" / "+s.keywords.length,0===l&&(document.querySelector("#keywords-container table").innerHTML=""),n&&requestAnimationFrame(n)}closeMenu(){document.getElementById("hamburger-menu-input").checked=!1}openKeywordWall(){document.getElementsByClassName("shortcuts")[0].classList.add("keyword-wall"),this.storage.set("keyword-wall","open"),document.getElementById("toggle-keyword-shortcuts").innerText="-"}closeKeywordWall(){document.getElementsByClassName("shortcuts")[0].classList.remove("keyword-wall"),this.storage.set("keyword-wall","close"),document.getElementById("toggle-keyword-shortcuts").innerText="+"}toggleShortcuts(){document.getElementsByClassName("shortcuts")[0].classList.contains("keyword-wall")?this.closeKeywordWall():this.openKeywordWall()}resetKeywords(){this.renderLibdocTemplate("keyword-shortcuts"),this.renderKeywords(),this.libdoc.tags.length&&(this.libdoc.selectedTag="",this.renderLibdocTemplate("tags-shortcuts")),history.replaceState&&history.replaceState(null,"",location.pathname)}clearSearch(){document.getElementsByClassName("search-input")[0].value="";let e=document.getElementById("tags-shortcuts-container");e&&(e.selectedIndex=0),this.resetKeywords()}renderLibdocTemplate(e,t=null,r=""){null==t&&(t=this.libdoc),this.renderTemplate(e,t,r)}renderTemplate(e,t,n=""){let o=document.getElementById(`${e}-template`)?.innerHTML,i=r(eg).compile(o);""===n&&(n=`#${e}-container`),document.body.querySelector(n).innerHTML=i(t)}},rh=libdoc;const rp=new eh("libdoc"),rd=ed.getInstance(rh.lang);new ru(rh,rp,rd).render();
+
+
+
diff --git a/src/OpenApiDriver/openapidriver.libspec b/src/OpenApiDriver/openapidriver.libspec
index ffcf5e6..f4d6fca 100644
--- a/src/OpenApiDriver/openapidriver.libspec
+++ b/src/OpenApiDriver/openapidriver.libspec
@@ -1,6 +1,6 @@
-
-1.0.5
+
+2.0.0b1
The OpenApiDriver library provides the keywords and logic for execution of generated test cases based on an OpenAPI document.
Visit the <a href="./index.html" target="_blank">OpenApiTools documentation</a> for an introduction.
@@ -8,7 +8,7 @@ Visit the <a href="./index.html" target="_blank">OpenApiTools documentatio
-
+
source
@@ -73,8 +73,8 @@ Visit the <a href="./index.html" target="_blank">OpenApiTools documentatio
-
-invalid_property_default_response
+
+invalid_data_default_response
422
@@ -152,7 +152,7 @@ Visit the <a href="./index.html" target="_blank">OpenApiTools documentatio
extra_headers
-
+
@@ -161,11 +161,11 @@ Visit the <a href="./index.html" target="_blank">OpenApiTools documentatio
cookies
-
+
-
+
None
@@ -173,7 +173,7 @@ Visit the <a href="./index.html" target="_blank">OpenApiTools documentatio
proxies
-
+
@@ -242,7 +242,7 @@ by Response validation.
<h3>mappings_path</h3>
See the Advanced Use tab for an in-depth explanation.
-<h3>invalid_property_default_response</h3>
+<h3>invalid_data_default_response</h3>
The default response code for requests with a JSON body that does not comply
with the schema.
Example: a value outside the specified range or a string value
@@ -325,7 +325,7 @@ A dictionary of <code>"protocol": "proxy url"</code> to use for all
-
+
path
@@ -350,7 +350,7 @@ The keyword calls other keywords to generate the neccesary data to perform
the desired operation and validate the response against the openapi document.
Validate that performing the `method` operation on `path` results in a `status_code` response.
-
+
path
@@ -371,7 +371,7 @@ library should grant insufficient access rights to the target endpoint.
reasons, the access rights validation should be checked first.
Perform a request for `method` on the `path`, with the provided authorization.
-
+
path
@@ -403,7 +403,7 @@ parameters are send with the request. The <span class="name">require_body_
parameter can be set to <span class="name">True</span> if needed.
Perform a request for the provided 'path' and 'method' where the url for the `path` is invalidated.
-
+
path
@@ -427,7 +427,7 @@ reasons, the authorization validation should be checked first.
-<p>Strings <code>TRUE</code>, <code>YES</code>, <code>ON</code> and <code>1</code> are converted to Boolean <code>True</code>, the empty string as well as strings <code>FALSE</code>, <code>NO</code>, <code>OFF</code> and <code>0</code> are converted to Boolean <code>False</code>, and the string <code>NONE</code> is converted to the Python <code>None</code> object. Other strings and other accepted values are passed as-is, allowing keywords to handle them specially if needed. All string comparisons are case-insensitive.</p>
+<p>Strings <code>TRUE</code>, <code>YES</code>, <code>ON</code>, <code>1</code> and possible localization specific "true strings" are converted to Boolean <code>True</code>, the empty string, strings <code>FALSE</code>, <code>NO</code>, <code>OFF</code> and <code>0</code> and possibly localization specific "false strings" are converted to Boolean <code>False</code>, and the string <code>NONE</code> is converted to the Python <code>None</code> object. Other strings and all other values are passed as-is, allowing keywords to handle them specially if needed. All string comparisons are case-insensitive.</p>
<p>Examples: <code>TRUE</code> (converted to <code>True</code>), <code>off</code> (converted to <code>False</code>), <code>example</code> (used as-is)</p>
string
@@ -439,22 +439,9 @@ reasons, the authorization validation should be checked first.
__init__
-
-<p>Strings must be Python <a href="https://docs.python.org/library/stdtypes.html#dict">dictionary</a> literals. They are converted to actual dictionaries using the <a href="https://docs.python.org/library/ast.html#ast.literal_eval">ast.literal_eval</a> function. They can contain any values <code>ast.literal_eval</code> supports, including dictionaries and other containers.</p>
-<p>If the type has nested types like <code>dict[str, int]</code>, items are converted to those types automatically. This in new in Robot Framework 6.0.</p>
-<p>Examples: <code>{'a': 1, 'b': 2}</code>, <code>{'key': 1, 'nested': {'key': 2}}</code></p>
-
-string
-Mapping
-
-
-__init__
-
-
<p>Conversion is done using Python's <a href="https://docs.python.org/library/functions.html#int">int</a> built-in function. Floating point numbers are accepted only if they can be represented as integers exactly. For example, <code>1.0</code> is accepted and <code>1.1</code> is not.</p>
-<p>Starting from RF 4.1, it is possible to use hexadecimal, octal and binary numbers by prefixing values with <code>0x</code>, <code>0o</code> and <code>0b</code>, respectively.</p>
-<p>Starting from RF 4.1, spaces and underscores can be used as visual separators for digit grouping purposes.</p>
+<p>It is possible to use hexadecimal, octal and binary numbers by prefixing values with <code>0x</code>, <code>0o</code> and <code>0b</code>, respectively. Spaces and underscores can be used as visual separators for digit grouping purposes.</p>
<p>Examples: <code>42</code>, <code>-1</code>, <code>0b1010</code>, <code>10 000 000</code>, <code>0xBAD_C0FFEE</code></p>
string
@@ -467,9 +454,11 @@ reasons, the authorization validation should be checked first.
-<p>Strings must be Python <a href="https://docs.python.org/library/stdtypes.html#list">list</a> literals. They are converted to actual lists using the <a href="https://docs.python.org/library/ast.html#ast.literal_eval">ast.literal_eval</a> function. They can contain any values <code>ast.literal_eval</code> supports, including lists and other containers.</p>
-<p>If the type has nested types like <code>list[int]</code>, items are converted to those types automatically. This in new in Robot Framework 6.0.</p>
-<p>Examples: <code>['one', 'two']</code>, <code>[('one', 1), ('two', 2)]</code></p>
+<p>Strings must be Python <a href="https://docs.python.org/library/stdtypes.html#list">list</a> or <a href="https://docs.python.org/library/stdtypes.html#tuple">tuple</a> literals. They are converted using the <a href="https://docs.python.org/library/ast.html#ast.literal_eval">ast.literal_eval</a> function and possible tuples converted further to lists. They can contain any values <code>ast.literal_eval</code> supports, including lists and other collections.</p>
+<p>If the argument is a list, it is used without conversion. Tuples and other sequences are converted to lists.</p>
+<p>If the type has nested types like <code>list[int]</code>, items are converted to those types automatically.</p>
+<p>Examples: <code>['one', 'two']</code>, <code>[('one', 1), ('two', 2)]</code></p>
+<p>Support to convert nested types is new in Robot Framework 6.0. Support for tuple literals is new in Robot Framework 7.4.</p>
string
Sequence
@@ -478,8 +467,22 @@ reasons, the authorization validation should be checked first.
__init__
+
+<p>Strings must be Python <a href="https://docs.python.org/library/stdtypes.html#dict">dictionary</a> literals. They are converted to actual dictionaries using the <a href="https://docs.python.org/library/ast.html#ast.literal_eval">ast.literal_eval</a> function. They can contain any values <code>ast.literal_eval</code> supports, including dictionaries and other collections.</p>
+<p>Any mapping is accepted without conversion. An exception is that if the type is <code>MutableMapping</code>, immutable values are converted to <code>dict</code>.</p>
+<p>If the type has nested types like <code>Mapping[str, int]</code>, items are converted to those types automatically. This in new in Robot Framework 6.0.</p>
+<p>Examples: <code>{'a': 1, 'b': 2}</code>, <code>{'key': 1, 'nested': {'key': 2}}</code></p>
+
+string
+Mapping
+
+
+__init__
+
+
-<p>String <code>NONE</code> (case-insensitive) is converted to Python <code>None</code> object. Other values cause an error.</p>
+<p>String <code>NONE</code> (case-insensitive) and the empty string are converted to the Python <code>None</code> object. Other values cause an error.</p>
+<p>Converting the empty string is new in Robot Framework 7.4.</p>
string
@@ -499,7 +502,9 @@ reasons, the authorization validation should be checked first.
-<p>All arguments are converted to Unicode strings.</p>
+<p>All arguments are converted to Unicode strings.</p>
+<p>Most values are converted simply by using <code>str(value)</code>. An exception is that bytes are mapped directly to Unicode code points with same ordinals. This means that, for example, <code>b"hyv\xe4"</code> becomes <code>"hyvä"</code>.</p>
+<p>Converting bytes specially is new Robot Framework 7.4.</p>
Any
@@ -512,9 +517,11 @@ reasons, the authorization validation should be checked first.
-<p>Strings must be Python <a href="https://docs.python.org/library/stdtypes.html#tuple">tuple</a> literals. They are converted to actual tuples using the <a href="https://docs.python.org/library/ast.html#ast.literal_eval">ast.literal_eval</a> function. They can contain any values <code>ast.literal_eval</code> supports, including tuples and other containers.</p>
-<p>If the type has nested types like <code>tuple[str, int, int]</code>, items are converted to those types automatically. This in new in Robot Framework 6.0.</p>
-<p>Examples: <code>('one', 'two')</code>, <code>(('one', 1), ('two', 2))</code></p>
+<p>Strings must be Python <a href="https://docs.python.org/library/stdtypes.html#tuple">tuple</a> or <a href="https://docs.python.org/library/stdtypes.html#list">list</a> literals. They are converted using the <a href="https://docs.python.org/library/ast.html#ast.literal_eval">ast.literal_eval</a> function and possible lists converted further to tuples. They can contain any values <code>ast.literal_eval</code> supports, including tuples and other collections.</p>
+<p>If the argument is a tuple, it is used without conversion. Lists and other sequences are converted to tuples.</p>
+<p>If the type has nested types like <code>tuple[str, int, int]</code>, items are converted to those types automatically.</p>
+<p>Examples: <code>('one', 'two')</code>, <code>(('one', 1), ('two', 2))</code></p>
+<p>Support to convert nested types is new in Robot Framework 6.0. Support for list literals is new in Robot Framework 7.4.</p>
string
Sequence
diff --git a/src/OpenApiLibCore/openapi_libcore.libspec b/src/OpenApiLibCore/openapi_libcore.libspec
index bf710d2..395c830 100644
--- a/src/OpenApiLibCore/openapi_libcore.libspec
+++ b/src/OpenApiLibCore/openapi_libcore.libspec
@@ -1,14 +1,14 @@
-
-1.0.5
+
+2.0.0b1
The OpenApiLibCore library provides the keywords and core logic to interact with an OpenAPI server.
Visit the <a href="./index.html" target="_blank">OpenApiTools documentation</a> for an introduction.
-
-
+
+
source
@@ -41,8 +41,8 @@ Visit the <a href="./index.html" target="_blank">OpenApiTools documentatio
-
-invalid_property_default_response
+
+invalid_data_default_response
422
@@ -120,7 +120,7 @@ Visit the <a href="./index.html" target="_blank">OpenApiTools documentatio
extra_headers
-
+
@@ -129,11 +129,11 @@ Visit the <a href="./index.html" target="_blank">OpenApiTools documentatio
cookies
-
+
-
+
None
@@ -141,7 +141,7 @@ Visit the <a href="./index.html" target="_blank">OpenApiTools documentatio
proxies
-
+
@@ -189,7 +189,7 @@ by Response validation.
<h3>mappings_path</h3>
See the Advanced Use tab for an in-depth explanation.
-<h3>invalid_property_default_response</h3>
+<h3>invalid_data_default_response</h3>
The default response code for requests with a JSON body that does not comply
with the schema.
Example: a value outside the specified range or a string value
@@ -272,25 +272,22 @@ A dictionary of <code>"protocol": "proxy url"</code> to use for all
-
-
+
+
href
-
+
referenced_resource
-
-
-
Attempt to GET the resource referenced by the <span class="name">href</span> and validate it's equal
-to the provided <span class="name">referenced_resource</span> object / dictionary.
-Attempt to GET the resource referenced by the `href` and validate it's equal to the provided `referenced_resource` object / dictionary.
+to the provided <span class="name">referenced_resource</span>.
+Attempt to GET the resource referenced by the `href` and validate it's equal to the provided `referenced_resource`.
-
+
url
@@ -348,18 +345,28 @@ to the provided <span class="name">referenced_resource</span> object
-Perform a request using the security token or authentication set in the library.
-
-<span class="name">json_data</span>, <span class="name">data</span> and <span class="name">files</span> are passed to <span class="name">requests.request</span>s <span class="name">json</span>,
-<span class="name">data</span> and <span class="name">files</span> parameters unaltered.
-See the requests documentation for details:
-https://requests.readthedocs.io/en/latest/api/#requests.request
+See the <a href="#Perform%20Authorized%20Request" class="name">Perform Authorized Request</a> keyword.
-> Note: provided username / password or auth objects take precedence over token
- based security
-Perform a request using the security token or authentication set in the library.
+The difference between these keywords is that this keyword accepts separate
+arguments where <a href="#Perform%20Validated%20Request" class="name">Perform Validated Request</a> accepts a <span class="name">RequestValues</span> object
+(see the <a href="#Get%20Request%20Values" class="name">Get Request Values</a> keyword for details).
+See the `Perform Authorized Request` keyword.
-
+
+
+
+request_values
+
+
+
+
+
+
+
+Convert a RequestValues object to a dictionary.
+Convert a RequestValues object to a dictionary.
+
+
url
@@ -374,7 +381,7 @@ https://requests.readthedocs.io/en/latest/api/#requests.request
is used by the resource defined by the <span class="name">resource_relation</span>.
Ensure that the (right-most) `id` of the resource referenced by the `url` is used by the resource defined by the `resource_relation`.
-
+
url
@@ -388,7 +395,7 @@ is used by the resource defined by the <span class="name">resource_relatio
<span class="name">ids</span> from the response.
Perform a GET request on the `url` and return the list of resource `ids` from the response.
-
+
url
@@ -407,18 +414,15 @@ is used by the resource defined by the <span class="name">resource_relatio
-
-
-
-
-Return <span class="name">json_data</span> based on the <span class="name">dto</span> on the <span class="name">request_data</span> that will cause
-the provided <span class="name">status_code</span> for the <span class="name">method</span> operation on the <span class="name">url</span>.
+
+Return <span class="name">json_data</span> based on the <span class="name">relations_mapping</span> on the <span class="name">request_data</span> that
+will cause the provided <span class="name">status_code</span> for the <span class="name">method</span> operation on the <span class="name">url</span>.
> Note: applicable UniquePropertyValueConstraint and IdReference Relations are
considered before changes to <span class="name">json_data</span> are made.
-Return `json_data` based on the `dto` on the `request_data` that will cause the provided `status_code` for the `method` operation on the `url`.
+Return `json_data` based on the `relations_mapping` on the `request_data` that will cause the provided `status_code` for the `method` operation on the `url`.
-
+
status_code
@@ -436,24 +440,19 @@ the provided <span class="name">status_code</span> for the <span
-
+
Returns a version of <span class="name">params, headers</span> as present on <span class="name">request_data</span> that has
been modified to cause the provided <span class="name">status_code</span>.
Returns a version of `params, headers` as present on `request_data` that has been modified to cause the provided `status_code`.
-
-
+
+
valid_url
-
-path
-
-
-
expected_status_code
@@ -463,14 +462,14 @@ been modified to cause the provided <span class="name">status_code</spa
Return an url with all the path parameters in the <span class="name">valid_url</span> replaced by a
random UUID if no PathPropertiesConstraint is mapped for the <span class="name">"get"</span> operation
-on the mapped <a href="#type-Path" class="name">path</a> and <span class="name">expected_status_code</span>.
+on the related <a href="#type-Path" class="name">path</a> and <span class="name">expected_status_code</span>.
If a PathPropertiesConstraint is mapped, the <span class="name">invalid_value</span> is returned.
Raises: ValueError if the valid_url cannot be invalidated.
-Return an url with all the path parameters in the `valid_url` replaced by a random UUID if no PathPropertiesConstraint is mapped for the `"get"` operation on the mapped `path` and `expected_status_code`. If a PathPropertiesConstraint is mapped, the `invalid_value` is returned.
+Return an url with all the path parameters in the `valid_url` replaced by a random UUID if no PathPropertiesConstraint is mapped for the `"get"` operation on the related `path` and `expected_status_code`. If a PathPropertiesConstraint is mapped, the `invalid_value` is returned.
-
-
+
+
url
@@ -479,9 +478,18 @@ Raises: ValueError if the valid_url cannot be invalidated.
method
-
-dto
-
+
+json_data
+
+
+
+
+
+
+relations_mapping
+
+
+
conflict_status_code
@@ -493,11 +501,11 @@ Raises: ValueError if the valid_url cannot be invalidated.
Return <span class="name">json_data</span> based on the <span class="name">UniquePropertyValueConstraint</span> that must be
-returned by the <span class="name">get_relations</span> implementation on the <span class="name">dto</span> for the given
-<span class="name">conflict_status_code</span>.
-Return `json_data` based on the `UniquePropertyValueConstraint` that must be returned by the `get_relations` implementation on the `dto` for the given `conflict_status_code`.
+returned by the <span class="name">get_relations</span> implementation on the <span class="name">relations_mapping</span> for
+the given <span class="name">conflict_status_code</span>.
+Return `json_data` based on the `UniquePropertyValueConstraint` that must be returned by the `get_relations` implementation on the `relations_mapping` for the given `conflict_status_code`.
-
+
url
@@ -508,7 +516,7 @@ returned by the <span class="name">get_relations</span> implementati
Return the path as found in the <span class="name">paths</span> section based on the given <span class="name">url</span>.
Return the path as found in the `paths` section based on the given `url`.
-
+
path
@@ -523,7 +531,7 @@ returned by the <span class="name">get_relations</span> implementati
Return an object with valid request data for body, headers and query params.
Return an object with valid request data for body, headers and query params.
-
+
path
@@ -535,7 +543,7 @@ returned by the <span class="name">get_relations</span> implementati
overrides
-
+
@@ -543,10 +551,58 @@ returned by the <span class="name">get_relations</span> implementati
-Return an object with all (valid) request values needed to make a request.
+Return an object with all (valid) request values needed to make a request.
+
+The <span class="name">overrides</span> dictionary can be used to pass specific values for parameters
+instead of having them be generated automatically.
Return an object with all (valid) request values needed to make a request.
-
+
+
+
+url
+
+
+
+method
+
+
+
+params
+
+
+
+
+{}
+
+
+headers
+
+
+
+
+{}
+
+
+json_data
+
+
+
+
+None
+
+
+
+This keyword can be used to instantiate a RequestValues object that can be used
+with the <a href="#Perform%20Authorized%20Request" class="name">Perform Authorized Request</a> and <a href="#Perform%20Validated%20Request" class="name">Perform Validated Request</a> keywords.
+
+This is a utility keyword that can make certain test cases or keywords more
+concise, but logging and debugging information will be more limited.
+
+See also the <a href="#Get%20Request%20Values" class="name">Get Request Values</a> keyword.
+This keyword can be used to instantiate a RequestValues object that can be used with the `Perform Authorized Request` and `Perform Validated Request` keywords.
+
+
path
@@ -564,7 +620,7 @@ To prevent resource conflicts with other test cases, a new resource is created
(by a POST operation) if possible.
Support keyword that returns the `id` for an existing resource at `path`.
-
+
path
@@ -582,7 +638,44 @@ keyword will be executed to retrieve valid ids for the path parameters.
[https://marketsquare.github.io/robotframework-openapitools/advanced_use.html | here].
This keyword returns a valid url for the given `path`.
-
+
+
+
+request_values
+
+
+
+data
+
+
+
+
+None
+
+
+files
+
+
+
+
+None
+
+
+
+Perform a request using the security token or authentication set in the library.
+
+<span class="name">json_data</span>, <span class="name">data</span> and <span class="name">files</span> are passed to <span class="name">requests.request</span>s <span class="name">json</span>,
+<span class="name">data</span> and <span class="name">files</span> parameters unaltered.
+See the requests documentation for details:
+https://requests.readthedocs.io/en/latest/api/#requests.request
+
+See also <a href="#Authorized%20Request" class="name">Authorized Request</a> and <a href="#Get%20Request%20Values" class="name">Get Request Values</a>.
+
+> Note: provided username / password or auth objects take precedence over token
+ based security
+Perform a request using the security token or authentication set in the library.
+
+
path
@@ -598,7 +691,7 @@ keyword will be executed to retrieve valid ids for the path parameters.
original_data
-
+
@@ -607,10 +700,12 @@ keyword will be executed to retrieve valid ids for the path parameters.
This keyword first calls the Authorized Request keyword, then the Validate
Response keyword and finally validates, for <span class="name">DELETE</span> operations, whether
-the target resource was indeed deleted (OK response) or not (error responses).
+the target resource was indeed deleted (OK response) or not (error responses).
+
+See also <a href="#Validated%20Request" class="name">Validated Request</a> and <a href="#Get%20Request%20Values" class="name">Get Request Values</a>.
This keyword first calls the Authorized Request keyword, then the Validate Response keyword and finally validates, for `DELETE` operations, whether the target resource was indeed deleted (OK response) or not (error responses).
-
+
auth
@@ -623,7 +718,7 @@ After calling this keyword, subsequent requests
will use the provided <span class="name">auth</span> instance.
Set the `auth` used for authentication after the library is imported.
-
+
username
@@ -641,7 +736,7 @@ After calling this keyword, subsequent requests
will use the provided credentials.
Set the `username` and `password` used for basic authentication after the library is imported.
-
+
extra_headers
@@ -657,7 +752,7 @@ After calling this keyword, subsequent requests
will use the provided <span class="name">extra_headers</span>.
Set the `extra_headers` used in requests after the library is imported.
-
+
origin
@@ -674,7 +769,7 @@ In combination with OpenApiLibCore, the <span class="name">origin</span
target another server that hosts an API that complies to the same OAS.
Set the `origin` after the library is imported.
-
+
security_token
@@ -686,8 +781,8 @@ target another server that hosts an API that complies to the same OAS.
After calling this keyword, subsequent requests will use the provided token.
Set the `security_token` after the library is imported.
-
-
+
+
path
@@ -696,12 +791,9 @@ After calling this keyword, subsequent requests will use the provided token.response
-
+
original_data
-
-
-
{}
@@ -715,18 +807,18 @@ After calling this keyword, subsequent requests will use the provided token.
Validate the `response` by performing the following validations: - validate the `response` against the openapi schema for the `path` - validate that the response does not contain extra properties - validate that a href, if present, refers to the correct resource - validate that the value for a property that is in the response is equal to the property value that was send - validate that no `original_data` is preserved when performing a PUT operation - validate that a PATCH operation only updates the provided properties
-
+
response
-Validate the <span class="name">response</span> against the OpenAPI Spec that is
+Validate the <span class="name">response</span> against the OpenAPI spec that is
loaded during library initialization.
-Validate the `response` against the OpenAPI Spec that is loaded during library initialization.
+Validate the `response` against the OpenAPI spec that is loaded during library initialization.
-
+
response
@@ -734,7 +826,7 @@ loaded during library initialization.
original_data
-
+
@@ -747,6 +839,64 @@ In case a PATCH request, validate that only the properties that were patched
have changed and that other properties are still at their pre-patch values.
Validate that each property that was send that is in the response has the value that was send. In case a PATCH request, validate that only the properties that were patched have changed and that other properties are still at their pre-patch values.
+
+
+
+path
+
+
+
+status_code
+
+
+
+url
+
+
+
+method
+
+
+
+params
+
+
+
+
+{}
+
+
+headers
+
+
+
+
+{}
+
+
+json_data
+
+
+
+
+None
+
+
+original_data
+
+
+
+
+{}
+
+
+See the <a href="#Perform%20Validated%20Request" class="name">Perform Validated Request</a> keyword.
+
+The difference between these keywords is that this keyword accepts separate
+arguments where <a href="#Perform%20Validated%20Request" class="name">Perform Validated Request</a> accepts a <span class="name">RequestValues</span> object
+(see the <a href="#Get%20Request%20Values" class="name">Get Request Values</a> keyword for details).
+See the `Perform Validated Request` keyword.
+
@@ -756,10 +906,11 @@ have changed and that other properties are still at their pre-patch values.
Authorized Request
+Perform Authorized Request
-<p>Strings <code>TRUE</code>, <code>YES</code>, <code>ON</code> and <code>1</code> are converted to Boolean <code>True</code>, the empty string as well as strings <code>FALSE</code>, <code>NO</code>, <code>OFF</code> and <code>0</code> are converted to Boolean <code>False</code>, and the string <code>NONE</code> is converted to the Python <code>None</code> object. Other strings and other accepted values are passed as-is, allowing keywords to handle them specially if needed. All string comparisons are case-insensitive.</p>
+<p>Strings <code>TRUE</code>, <code>YES</code>, <code>ON</code>, <code>1</code> and possible localization specific "true strings" are converted to Boolean <code>True</code>, the empty string, strings <code>FALSE</code>, <code>NO</code>, <code>OFF</code> and <code>0</code> and possibly localization specific "false strings" are converted to Boolean <code>False</code>, and the string <code>NONE</code> is converted to the Python <code>None</code> object. Other strings and all other values are passed as-is, allowing keywords to handle them specially if needed. All string comparisons are case-insensitive.</p>
<p>Examples: <code>TRUE</code> (converted to <code>True</code>), <code>off</code> (converted to <code>False</code>), <code>example</code> (used as-is)</p>
string
@@ -772,7 +923,8 @@ have changed and that other properties are still at their pre-patch values.
-<p>Strings must be Python <a href="https://docs.python.org/library/stdtypes.html#dict">dictionary</a> literals. They are converted to actual dictionaries using the <a href="https://docs.python.org/library/ast.html#ast.literal_eval">ast.literal_eval</a> function. They can contain any values <code>ast.literal_eval</code> supports, including dictionaries and other containers.</p>
+<p>Strings must be Python <a href="https://docs.python.org/library/stdtypes.html#dict">dictionary</a> literals. They are converted to actual dictionaries using the <a href="https://docs.python.org/library/ast.html#ast.literal_eval">ast.literal_eval</a> function. They can contain any values <code>ast.literal_eval</code> supports, including dictionaries and other collections.</p>
+<p>Any mapping is accepted and converted to a <code>dict</code>.</p>
<p>If the type has nested types like <code>dict[str, int]</code>, items are converted to those types automatically. This in new in Robot Framework 6.0.</p>
<p>Examples: <code>{'a': 1, 'b': 2}</code>, <code>{'key': 1, 'nested': {'key': 2}}</code></p>
@@ -780,22 +932,18 @@ have changed and that other properties are still at their pre-patch values. Mapping
-__init__
-Assert Href To Resource Is Valid
Authorized Request
-Get Invalid Body Data
+Convert Request Values To Dict
Get Invalidated Parameters
Get Json Data With Conflict
-Get Request Values
-Perform Validated Request
+Get Request Values Object
Set Extra Headers
-Validate Response
-Validate Send Response
+Validated Request
<p>Conversion is done using Python's <a href="https://docs.python.org/library/functions.html#float">float</a> built-in function.</p>
-<p>Starting from RF 4.1, spaces and underscores can be used as visual separators for digit grouping purposes.</p>
+<p>Spaces and underscores can be used as visual separators for digit grouping purposes.</p>
<p>Examples: <code>3.14</code>, <code>2.9979e8</code>, <code>10 000.000 01</code></p>
string
@@ -807,8 +955,7 @@ have changed and that other properties are still at their pre-patch values.
<p>Conversion is done using Python's <a href="https://docs.python.org/library/functions.html#int">int</a> built-in function. Floating point numbers are accepted only if they can be represented as integers exactly. For example, <code>1.0</code> is accepted and <code>1.1</code> is not.</p>
-<p>Starting from RF 4.1, it is possible to use hexadecimal, octal and binary numbers by prefixing values with <code>0x</code>, <code>0o</code> and <code>0b</code>, respectively.</p>
-<p>Starting from RF 4.1, spaces and underscores can be used as visual separators for digit grouping purposes.</p>
+<p>It is possible to use hexadecimal, octal and binary numbers by prefixing values with <code>0x</code>, <code>0o</code> and <code>0b</code>, respectively. Spaces and underscores can be used as visual separators for digit grouping purposes.</p>
<p>Examples: <code>42</code>, <code>-1</code>, <code>0b1010</code>, <code>10 000 000</code>, <code>0xBAD_C0FFEE</code></p>
string
@@ -822,12 +969,15 @@ have changed and that other properties are still at their pre-patch values.Get Json Data With Conflict
Get Valid Id For Path
Perform Validated Request
+Validated Request
-<p>Strings must be Python <a href="https://docs.python.org/library/stdtypes.html#list">list</a> literals. They are converted to actual lists using the <a href="https://docs.python.org/library/ast.html#ast.literal_eval">ast.literal_eval</a> function. They can contain any values <code>ast.literal_eval</code> supports, including lists and other containers.</p>
-<p>If the type has nested types like <code>list[int]</code>, items are converted to those types automatically. This in new in Robot Framework 6.0.</p>
-<p>Examples: <code>['one', 'two']</code>, <code>[('one', 1), ('two', 2)]</code></p>
+<p>Strings must be Python <a href="https://docs.python.org/library/stdtypes.html#list">list</a> or <a href="https://docs.python.org/library/stdtypes.html#tuple">tuple</a> literals. They are converted using the <a href="https://docs.python.org/library/ast.html#ast.literal_eval">ast.literal_eval</a> function and possible tuples converted further to lists. They can contain any values <code>ast.literal_eval</code> supports, including lists and other collections.</p>
+<p>If the argument is a list, it is used without conversion. Tuples and other sequences are converted to lists.</p>
+<p>If the type has nested types like <code>list[int]</code>, items are converted to those types automatically.</p>
+<p>Examples: <code>['one', 'two']</code>, <code>[('one', 1), ('two', 2)]</code></p>
+<p>Support to convert nested types is new in Robot Framework 6.0. Support for tuple literals is new in Robot Framework 7.4.</p>
string
Sequence
@@ -837,14 +987,35 @@ have changed and that other properties are still at their pre-patch values.Get Ids From Url
+
+<p>Strings must be Python <a href="https://docs.python.org/library/stdtypes.html#dict">dictionary</a> literals. They are converted to actual dictionaries using the <a href="https://docs.python.org/library/ast.html#ast.literal_eval">ast.literal_eval</a> function. They can contain any values <code>ast.literal_eval</code> supports, including dictionaries and other collections.</p>
+<p>Any mapping is accepted without conversion. An exception is that if the type is <code>MutableMapping</code>, immutable values are converted to <code>dict</code>.</p>
+<p>If the type has nested types like <code>Mapping[str, int]</code>, items are converted to those types automatically. This in new in Robot Framework 6.0.</p>
+<p>Examples: <code>{'a': 1, 'b': 2}</code>, <code>{'key': 1, 'nested': {'key': 2}}</code></p>
+
+string
+Mapping
+
+
+__init__
+Get Request Values
+Perform Validated Request
+Validate Send Response
+Validated Request
+
+
-<p>String <code>NONE</code> (case-insensitive) is converted to Python <code>None</code> object. Other values cause an error.</p>
+<p>String <code>NONE</code> (case-insensitive) and the empty string are converted to the Python <code>None</code> object. Other values cause an error.</p>
+<p>Converting the empty string is new in Robot Framework 7.4.</p>
string
__init__
Authorized Request
+Get Request Values Object
+Perform Authorized Request
+Validated Request
@@ -859,7 +1030,9 @@ have changed and that other properties are still at their pre-patch values.
-<p>All arguments are converted to Unicode strings.</p>
+<p>All arguments are converted to Unicode strings.</p>
+<p>Most values are converted simply by using <code>str(value)</code>. An exception is that bytes are mapped directly to Unicode code points with same ordinals. This means that, for example, <code>b"hyv\xe4"</code> becomes <code>"hyvä"</code>.</p>
+<p>Converting bytes specially is new Robot Framework 7.4.</p>
Any
@@ -867,6 +1040,7 @@ have changed and that other properties are still at their pre-patch values.__init__
Assert Href To Resource Is Valid
Authorized Request
+Convert Request Values To Dict
Ensure In Use
Get Ids From Url
Get Invalid Body Data
@@ -876,6 +1050,7 @@ have changed and that other properties are still at their pre-patch values.Get Parameterized Path From Url
Get Request Data
Get Request Values
+Get Request Values Object
Get Valid Id For Path
Get Valid Url
Perform Validated Request
@@ -885,12 +1060,15 @@ have changed and that other properties are still at their pre-patch values.Set Security Token
Validate Response
Validate Send Response
+Validated Request
-<p>Strings must be Python <a href="https://docs.python.org/library/stdtypes.html#tuple">tuple</a> literals. They are converted to actual tuples using the <a href="https://docs.python.org/library/ast.html#ast.literal_eval">ast.literal_eval</a> function. They can contain any values <code>ast.literal_eval</code> supports, including tuples and other containers.</p>
-<p>If the type has nested types like <code>tuple[str, int, int]</code>, items are converted to those types automatically. This in new in Robot Framework 6.0.</p>
-<p>Examples: <code>('one', 'two')</code>, <code>(('one', 1), ('two', 2))</code></p>
+<p>Strings must be Python <a href="https://docs.python.org/library/stdtypes.html#tuple">tuple</a> or <a href="https://docs.python.org/library/stdtypes.html#list">list</a> literals. They are converted using the <a href="https://docs.python.org/library/ast.html#ast.literal_eval">ast.literal_eval</a> function and possible lists converted further to tuples. They can contain any values <code>ast.literal_eval</code> supports, including tuples and other collections.</p>
+<p>If the argument is a tuple, it is used without conversion. Lists and other sequences are converted to tuples.</p>
+<p>If the type has nested types like <code>tuple[str, int, int]</code>, items are converted to those types automatically.</p>
+<p>Examples: <code>('one', 'two')</code>, <code>(('one', 1), ('two', 2))</code></p>
+<p>Support to convert nested types is new in Robot Framework 6.0. Support for list literals is new in Robot Framework 7.4.</p>
string
Sequence
From f979b665fa239712fed1fd75690905787c68b5dd Mon Sep 17 00:00:00 2001
From: Robin Mackaij
Date: Tue, 23 Dec 2025 17:43:08 +0000
Subject: [PATCH 55/55] Updated dependencies, test matrix updated to latest RF
release
---
.github/workflows/on-push.yml | 4 +-
poetry.lock | 874 ++++++++++++++++++----------------
2 files changed, 470 insertions(+), 408 deletions(-)
diff --git a/.github/workflows/on-push.yml b/.github/workflows/on-push.yml
index 7ba4cc4..ed5896f 100644
--- a/.github/workflows/on-push.yml
+++ b/.github/workflows/on-push.yml
@@ -54,7 +54,7 @@ jobs:
matrix:
os: [ 'ubuntu-latest', 'windows-latest']
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
- robot-version: ['6.1.1', '7.3.2']
+ robot-version: ['6.1.1', '7.4.1']
exclude:
- os: 'windows-latest'
python-version: '3.10'
@@ -101,7 +101,7 @@ jobs:
tail: true
wait-for: 1m
- name: Run tests on latest RF 7 version
- if: matrix.robot-version == '7.3.2'
+ if: matrix.robot-version == '7.4.1'
run: |
inv tests
- name: Run tests on latest RF 6 version
diff --git a/poetry.lock b/poetry.lock
index 964681b..af9639b 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -26,24 +26,23 @@ files = [
[[package]]
name = "anyio"
-version = "4.11.0"
+version = "4.12.0"
description = "High-level concurrency and networking framework on top of asyncio or Trio"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
- {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"},
- {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"},
+ {file = "anyio-4.12.0-py3-none-any.whl", hash = "sha256:dad2376a628f98eeca4881fc56cd06affd18f659b17a747d3ff0307ced94b1bb"},
+ {file = "anyio-4.12.0.tar.gz", hash = "sha256:73c693b567b0c55130c104d0b43a9baf3aa6a31fc6110116509f27bf75e21ec0"},
]
[package.dependencies]
exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""}
idna = ">=2.8"
-sniffio = ">=1.1"
typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""}
[package.extras]
-trio = ["trio (>=0.31.0)"]
+trio = ["trio (>=0.31.0) ; python_version < \"3.10\"", "trio (>=0.32.0) ; python_version >= \"3.10\""]
[[package]]
name = "astroid"
@@ -249,104 +248,104 @@ markers = {main = "platform_system == \"Windows\"", lint-and-format = "platform_
[[package]]
name = "coverage"
-version = "7.12.0"
+version = "7.13.0"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
- {file = "coverage-7.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:32b75c2ba3f324ee37af3ccee5b30458038c50b349ad9b88cee85096132a575b"},
- {file = "coverage-7.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cb2a1b6ab9fe833714a483a915de350abc624a37149649297624c8d57add089c"},
- {file = "coverage-7.12.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5734b5d913c3755e72f70bf6cc37a0518d4f4745cde760c5d8e12005e62f9832"},
- {file = "coverage-7.12.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b527a08cdf15753279b7afb2339a12073620b761d79b81cbe2cdebdb43d90daa"},
- {file = "coverage-7.12.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9bb44c889fb68004e94cab71f6a021ec83eac9aeabdbb5a5a88821ec46e1da73"},
- {file = "coverage-7.12.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4b59b501455535e2e5dde5881739897967b272ba25988c89145c12d772810ccb"},
- {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8842f17095b9868a05837b7b1b73495293091bed870e099521ada176aa3e00e"},
- {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5a6f20bf48b8866095c6820641e7ffbe23f2ac84a2efc218d91235e404c7777"},
- {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:5f3738279524e988d9da2893f307c2093815c623f8d05a8f79e3eff3a7a9e553"},
- {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0d68c1f7eabbc8abe582d11fa393ea483caf4f44b0af86881174769f185c94d"},
- {file = "coverage-7.12.0-cp310-cp310-win32.whl", hash = "sha256:7670d860e18b1e3ee5930b17a7d55ae6287ec6e55d9799982aa103a2cc1fa2ef"},
- {file = "coverage-7.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:f999813dddeb2a56aab5841e687b68169da0d3f6fc78ccf50952fa2463746022"},
- {file = "coverage-7.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa124a3683d2af98bd9d9c2bfa7a5076ca7e5ab09fdb96b81fa7d89376ae928f"},
- {file = "coverage-7.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d93fbf446c31c0140208dcd07c5d882029832e8ed7891a39d6d44bd65f2316c3"},
- {file = "coverage-7.12.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:52ca620260bd8cd6027317bdd8b8ba929be1d741764ee765b42c4d79a408601e"},
- {file = "coverage-7.12.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f3433ffd541380f3a0e423cff0f4926d55b0cc8c1d160fdc3be24a4c03aa65f7"},
- {file = "coverage-7.12.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7bbb321d4adc9f65e402c677cd1c8e4c2d0105d3ce285b51b4d87f1d5db5245"},
- {file = "coverage-7.12.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22a7aade354a72dff3b59c577bfd18d6945c61f97393bc5fb7bd293a4237024b"},
- {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3ff651dcd36d2fea66877cd4a82de478004c59b849945446acb5baf9379a1b64"},
- {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:31b8b2e38391a56e3cea39d22a23faaa7c3fc911751756ef6d2621d2a9daf742"},
- {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:297bc2da28440f5ae51c845a47c8175a4db0553a53827886e4fb25c66633000c"},
- {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6ff7651cc01a246908eac162a6a86fc0dbab6de1ad165dfb9a1e2ec660b44984"},
- {file = "coverage-7.12.0-cp311-cp311-win32.whl", hash = "sha256:313672140638b6ddb2c6455ddeda41c6a0b208298034544cfca138978c6baed6"},
- {file = "coverage-7.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a1783ed5bd0d5938d4435014626568dc7f93e3cb99bc59188cc18857c47aa3c4"},
- {file = "coverage-7.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:4648158fd8dd9381b5847622df1c90ff314efbfc1df4550092ab6013c238a5fc"},
- {file = "coverage-7.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:29644c928772c78512b48e14156b81255000dcfd4817574ff69def189bcb3647"},
- {file = "coverage-7.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8638cbb002eaa5d7c8d04da667813ce1067080b9a91099801a0053086e52b736"},
- {file = "coverage-7.12.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:083631eeff5eb9992c923e14b810a179798bb598e6a0dd60586819fc23be6e60"},
- {file = "coverage-7.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:99d5415c73ca12d558e07776bd957c4222c687b9f1d26fa0e1b57e3598bdcde8"},
- {file = "coverage-7.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e949ebf60c717c3df63adb4a1a366c096c8d7fd8472608cd09359e1bd48ef59f"},
- {file = "coverage-7.12.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d907ddccbca819afa2cd014bc69983b146cca2735a0b1e6259b2a6c10be1e70"},
- {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b1518ecbad4e6173f4c6e6c4a46e49555ea5679bf3feda5edb1b935c7c44e8a0"},
- {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51777647a749abdf6f6fd8c7cffab12de68ab93aab15efc72fbbb83036c2a068"},
- {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:42435d46d6461a3b305cdfcad7cdd3248787771f53fe18305548cba474e6523b"},
- {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5bcead88c8423e1855e64b8057d0544e33e4080b95b240c2a355334bb7ced937"},
- {file = "coverage-7.12.0-cp312-cp312-win32.whl", hash = "sha256:dcbb630ab034e86d2a0f79aefd2be07e583202f41e037602d438c80044957baa"},
- {file = "coverage-7.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:2fd8354ed5d69775ac42986a691fbf68b4084278710cee9d7c3eaa0c28fa982a"},
- {file = "coverage-7.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:737c3814903be30695b2de20d22bcc5428fdae305c61ba44cdc8b3252984c49c"},
- {file = "coverage-7.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47324fffca8d8eae7e185b5bb20c14645f23350f870c1649003618ea91a78941"},
- {file = "coverage-7.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ccf3b2ede91decd2fb53ec73c1f949c3e034129d1e0b07798ff1d02ea0c8fa4a"},
- {file = "coverage-7.12.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b365adc70a6936c6b0582dc38746b33b2454148c02349345412c6e743efb646d"},
- {file = "coverage-7.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bc13baf85cd8a4cfcf4a35c7bc9d795837ad809775f782f697bf630b7e200211"},
- {file = "coverage-7.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:099d11698385d572ceafb3288a5b80fe1fc58bf665b3f9d362389de488361d3d"},
- {file = "coverage-7.12.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:473dc45d69694069adb7680c405fb1e81f60b2aff42c81e2f2c3feaf544d878c"},
- {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:583f9adbefd278e9de33c33d6846aa8f5d164fa49b47144180a0e037f0688bb9"},
- {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2089cc445f2dc0af6f801f0d1355c025b76c24481935303cf1af28f636688f0"},
- {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:950411f1eb5d579999c5f66c62a40961f126fc71e5e14419f004471957b51508"},
- {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b1aab7302a87bafebfe76b12af681b56ff446dc6f32ed178ff9c092ca776e6bc"},
- {file = "coverage-7.12.0-cp313-cp313-win32.whl", hash = "sha256:d7e0d0303c13b54db495eb636bc2465b2fb8475d4c8bcec8fe4b5ca454dfbae8"},
- {file = "coverage-7.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:ce61969812d6a98a981d147d9ac583a36ac7db7766f2e64a9d4d059c2fe29d07"},
- {file = "coverage-7.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bcec6f47e4cb8a4c2dc91ce507f6eefc6a1b10f58df32cdc61dff65455031dfc"},
- {file = "coverage-7.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:459443346509476170d553035e4a3eed7b860f4fe5242f02de1010501956ce87"},
- {file = "coverage-7.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:04a79245ab2b7a61688958f7a855275997134bc84f4a03bc240cf64ff132abf6"},
- {file = "coverage-7.12.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:09a86acaaa8455f13d6a99221d9654df249b33937b4e212b4e5a822065f12aa7"},
- {file = "coverage-7.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:907e0df1b71ba77463687a74149c6122c3f6aac56c2510a5d906b2f368208560"},
- {file = "coverage-7.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b57e2d0ddd5f0582bae5437c04ee71c46cd908e7bc5d4d0391f9a41e812dd12"},
- {file = "coverage-7.12.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:58c1c6aa677f3a1411fe6fb28ec3a942e4f665df036a3608816e0847fad23296"},
- {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4c589361263ab2953e3c4cd2a94db94c4ad4a8e572776ecfbad2389c626e4507"},
- {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:91b810a163ccad2e43b1faa11d70d3cf4b6f3d83f9fd5f2df82a32d47b648e0d"},
- {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:40c867af715f22592e0d0fb533a33a71ec9e0f73a6945f722a0c85c8c1cbe3a2"},
- {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:68b0d0a2d84f333de875666259dadf28cc67858bc8fd8b3f1eae84d3c2bec455"},
- {file = "coverage-7.12.0-cp313-cp313t-win32.whl", hash = "sha256:73f9e7fbd51a221818fd11b7090eaa835a353ddd59c236c57b2199486b116c6d"},
- {file = "coverage-7.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:24cff9d1f5743f67db7ba46ff284018a6e9aeb649b67aa1e70c396aa1b7cb23c"},
- {file = "coverage-7.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c87395744f5c77c866d0f5a43d97cc39e17c7f1cb0115e54a2fe67ca75c5d14d"},
- {file = "coverage-7.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a1c59b7dc169809a88b21a936eccf71c3895a78f5592051b1af8f4d59c2b4f92"},
- {file = "coverage-7.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8787b0f982e020adb732b9f051f3e49dd5054cebbc3f3432061278512a2b1360"},
- {file = "coverage-7.12.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5ea5a9f7dc8877455b13dd1effd3202e0bca72f6f3ab09f9036b1bcf728f69ac"},
- {file = "coverage-7.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fdba9f15849534594f60b47c9a30bc70409b54947319a7c4fd0e8e3d8d2f355d"},
- {file = "coverage-7.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a00594770eb715854fb1c57e0dea08cce6720cfbc531accdb9850d7c7770396c"},
- {file = "coverage-7.12.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5560c7e0d82b42eb1951e4f68f071f8017c824ebfd5a6ebe42c60ac16c6c2434"},
- {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2e26b481c9159c2773a37947a9718cfdc58893029cdfb177531793e375cfc"},
- {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6e1a8c066dabcde56d5d9fed6a66bc19a2883a3fe051f0c397a41fc42aedd4cc"},
- {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f7ba9da4726e446d8dd8aae5a6cd872511184a5d861de80a86ef970b5dacce3e"},
- {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e0f483ab4f749039894abaf80c2f9e7ed77bbf3c737517fb88c8e8e305896a17"},
- {file = "coverage-7.12.0-cp314-cp314-win32.whl", hash = "sha256:76336c19a9ef4a94b2f8dc79f8ac2da3f193f625bb5d6f51a328cd19bfc19933"},
- {file = "coverage-7.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7c1059b600aec6ef090721f8f633f60ed70afaffe8ecab85b59df748f24b31fe"},
- {file = "coverage-7.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:172cf3a34bfef42611963e2b661302a8931f44df31629e5b1050567d6b90287d"},
- {file = "coverage-7.12.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:aa7d48520a32cb21c7a9b31f81799e8eaec7239db36c3b670be0fa2403828d1d"},
- {file = "coverage-7.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:90d58ac63bc85e0fb919f14d09d6caa63f35a5512a2205284b7816cafd21bb03"},
- {file = "coverage-7.12.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ca8ecfa283764fdda3eae1bdb6afe58bf78c2c3ec2b2edcb05a671f0bba7b3f9"},
- {file = "coverage-7.12.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:874fe69a0785d96bd066059cd4368022cebbec1a8958f224f0016979183916e6"},
- {file = "coverage-7.12.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3c889c0b8b283a24d721a9eabc8ccafcfc3aebf167e4cd0d0e23bf8ec4e339"},
- {file = "coverage-7.12.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bb5b894b3ec09dcd6d3743229dc7f2c42ef7787dc40596ae04c0edda487371e"},
- {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:79a44421cd5fba96aa57b5e3b5a4d3274c449d4c622e8f76882d76635501fd13"},
- {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:33baadc0efd5c7294f436a632566ccc1f72c867f82833eb59820ee37dc811c6f"},
- {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c406a71f544800ef7e9e0000af706b88465f3573ae8b8de37e5f96c59f689ad1"},
- {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e71bba6a40883b00c6d571599b4627f50c360b3d0d02bfc658168936be74027b"},
- {file = "coverage-7.12.0-cp314-cp314t-win32.whl", hash = "sha256:9157a5e233c40ce6613dead4c131a006adfda70e557b6856b97aceed01b0e27a"},
- {file = "coverage-7.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e84da3a0fd233aeec797b981c51af1cabac74f9bd67be42458365b30d11b5291"},
- {file = "coverage-7.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:01d24af36fedda51c2b1aca56e4330a3710f83b02a5ff3743a6b015ffa7c9384"},
- {file = "coverage-7.12.0-py3-none-any.whl", hash = "sha256:159d50c0b12e060b15ed3d39f87ed43d4f7f7ad40b8a534f4dd331adbb51104a"},
- {file = "coverage-7.12.0.tar.gz", hash = "sha256:fc11e0a4e372cb5f282f16ef90d4a585034050ccda536451901abfb19a57f40c"},
+ {file = "coverage-7.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:02d9fb9eccd48f6843c98a37bd6817462f130b86da8660461e8f5e54d4c06070"},
+ {file = "coverage-7.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:367449cf07d33dc216c083f2036bb7d976c6e4903ab31be400ad74ad9f85ce98"},
+ {file = "coverage-7.13.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cdb3c9f8fef0a954c632f64328a3935988d33a6604ce4bf67ec3e39670f12ae5"},
+ {file = "coverage-7.13.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d10fd186aac2316f9bbb46ef91977f9d394ded67050ad6d84d94ed6ea2e8e54e"},
+ {file = "coverage-7.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f88ae3e69df2ab62fb0bc5219a597cb890ba5c438190ffa87490b315190bb33"},
+ {file = "coverage-7.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4be718e51e86f553bcf515305a158a1cd180d23b72f07ae76d6017c3cc5d791"},
+ {file = "coverage-7.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a00d3a393207ae12f7c49bb1c113190883b500f48979abb118d8b72b8c95c032"},
+ {file = "coverage-7.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a7b1cd820e1b6116f92c6128f1188e7afe421c7e1b35fa9836b11444e53ebd9"},
+ {file = "coverage-7.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:37eee4e552a65866f15dedd917d5e5f3d59805994260720821e2c1b51ac3248f"},
+ {file = "coverage-7.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:62d7c4f13102148c78d7353c6052af6d899a7f6df66a32bddcc0c0eb7c5326f8"},
+ {file = "coverage-7.13.0-cp310-cp310-win32.whl", hash = "sha256:24e4e56304fdb56f96f80eabf840eab043b3afea9348b88be680ec5986780a0f"},
+ {file = "coverage-7.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:74c136e4093627cf04b26a35dab8cbfc9b37c647f0502fc313376e11726ba303"},
+ {file = "coverage-7.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0dfa3855031070058add1a59fdfda0192fd3e8f97e7c81de0596c145dea51820"},
+ {file = "coverage-7.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fdb6f54f38e334db97f72fa0c701e66d8479af0bc3f9bfb5b90f1c30f54500f"},
+ {file = "coverage-7.13.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7e442c013447d1d8d195be62852270b78b6e255b79b8675bad8479641e21fd96"},
+ {file = "coverage-7.13.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ed5630d946859de835a85e9a43b721123a8a44ec26e2830b296d478c7fd4259"},
+ {file = "coverage-7.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f15a931a668e58087bc39d05d2b4bf4b14ff2875b49c994bbdb1c2217a8daeb"},
+ {file = "coverage-7.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30a3a201a127ea57f7e14ba43c93c9c4be8b7d17a26e03bb49e6966d019eede9"},
+ {file = "coverage-7.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a485ff48fbd231efa32d58f479befce52dcb6bfb2a88bb7bf9a0b89b1bc8030"},
+ {file = "coverage-7.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:22486cdafba4f9e471c816a2a5745337742a617fef68e890d8baf9f3036d7833"},
+ {file = "coverage-7.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:263c3dbccc78e2e331e59e90115941b5f53e85cfcc6b3b2fbff1fd4e3d2c6ea8"},
+ {file = "coverage-7.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e5330fa0cc1f5c3c4c3bb8e101b742025933e7848989370a1d4c8c5e401ea753"},
+ {file = "coverage-7.13.0-cp311-cp311-win32.whl", hash = "sha256:0f4872f5d6c54419c94c25dd6ae1d015deeb337d06e448cd890a1e89a8ee7f3b"},
+ {file = "coverage-7.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51a202e0f80f241ccb68e3e26e19ab5b3bf0f813314f2c967642f13ebcf1ddfe"},
+ {file = "coverage-7.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:d2a9d7f1c11487b1c69367ab3ac2d81b9b3721f097aa409a3191c3e90f8f3dd7"},
+ {file = "coverage-7.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0b3d67d31383c4c68e19a88e28fc4c2e29517580f1b0ebec4a069d502ce1e0bf"},
+ {file = "coverage-7.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:581f086833d24a22c89ae0fe2142cfaa1c92c930adf637ddf122d55083fb5a0f"},
+ {file = "coverage-7.13.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0a3a30f0e257df382f5f9534d4ce3d4cf06eafaf5192beb1a7bd066cb10e78fb"},
+ {file = "coverage-7.13.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:583221913fbc8f53b88c42e8dbb8fca1d0f2e597cb190ce45916662b8b9d9621"},
+ {file = "coverage-7.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f5d9bd30756fff3e7216491a0d6d520c448d5124d3d8e8f56446d6412499e74"},
+ {file = "coverage-7.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a23e5a1f8b982d56fa64f8e442e037f6ce29322f1f9e6c2344cd9e9f4407ee57"},
+ {file = "coverage-7.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b01c22bc74a7fb44066aaf765224c0d933ddf1f5047d6cdfe4795504a4493f8"},
+ {file = "coverage-7.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:898cce66d0836973f48dda4e3514d863d70142bdf6dfab932b9b6a90ea5b222d"},
+ {file = "coverage-7.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:3ab483ea0e251b5790c2aac03acde31bff0c736bf8a86829b89382b407cd1c3b"},
+ {file = "coverage-7.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d84e91521c5e4cb6602fe11ece3e1de03b2760e14ae4fcf1a4b56fa3c801fcd"},
+ {file = "coverage-7.13.0-cp312-cp312-win32.whl", hash = "sha256:193c3887285eec1dbdb3f2bd7fbc351d570ca9c02ca756c3afbc71b3c98af6ef"},
+ {file = "coverage-7.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:4f3e223b2b2db5e0db0c2b97286aba0036ca000f06aca9b12112eaa9af3d92ae"},
+ {file = "coverage-7.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:086cede306d96202e15a4b77ace8472e39d9f4e5f9fd92dd4fecdfb2313b2080"},
+ {file = "coverage-7.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:28ee1c96109974af104028a8ef57cec21447d42d0e937c0275329272e370ebcf"},
+ {file = "coverage-7.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e97353dcc5587b85986cda4ff3ec98081d7e84dd95e8b2a6d59820f0545f8a"},
+ {file = "coverage-7.13.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:99acd4dfdfeb58e1937629eb1ab6ab0899b131f183ee5f23e0b5da5cba2fec74"},
+ {file = "coverage-7.13.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff45e0cd8451e293b63ced93161e189780baf444119391b3e7d25315060368a6"},
+ {file = "coverage-7.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f4f72a85316d8e13234cafe0a9f81b40418ad7a082792fa4165bd7d45d96066b"},
+ {file = "coverage-7.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:11c21557d0e0a5a38632cbbaca5f008723b26a89d70db6315523df6df77d6232"},
+ {file = "coverage-7.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76541dc8d53715fb4f7a3a06b34b0dc6846e3c69bc6204c55653a85dd6220971"},
+ {file = "coverage-7.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6e9e451dee940a86789134b6b0ffbe31c454ade3b849bb8a9d2cca2541a8e91d"},
+ {file = "coverage-7.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5c67dace46f361125e6b9cace8fe0b729ed8479f47e70c89b838d319375c8137"},
+ {file = "coverage-7.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f59883c643cb19630500f57016f76cfdcd6845ca8c5b5ea1f6e17f74c8e5f511"},
+ {file = "coverage-7.13.0-cp313-cp313-win32.whl", hash = "sha256:58632b187be6f0be500f553be41e277712baa278147ecb7559983c6d9faf7ae1"},
+ {file = "coverage-7.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:73419b89f812f498aca53f757dd834919b48ce4799f9d5cad33ca0ae442bdb1a"},
+ {file = "coverage-7.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:eb76670874fdd6091eedcc856128ee48c41a9bbbb9c3f1c7c3cf169290e3ffd6"},
+ {file = "coverage-7.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6e63ccc6e0ad8986386461c3c4b737540f20426e7ec932f42e030320896c311a"},
+ {file = "coverage-7.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:494f5459ffa1bd45e18558cd98710c36c0b8fbfa82a5eabcbe671d80ecffbfe8"},
+ {file = "coverage-7.13.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:06cac81bf10f74034e055e903f5f946e3e26fc51c09fc9f584e4a1605d977053"},
+ {file = "coverage-7.13.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f2ffc92b46ed6e6760f1d47a71e56b5664781bc68986dbd1836b2b70c0ce2071"},
+ {file = "coverage-7.13.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0602f701057c6823e5db1b74530ce85f17c3c5be5c85fc042ac939cbd909426e"},
+ {file = "coverage-7.13.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:25dc33618d45456ccb1d37bce44bc78cf269909aa14c4db2e03d63146a8a1493"},
+ {file = "coverage-7.13.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:71936a8b3b977ddd0b694c28c6a34f4fff2e9dd201969a4ff5d5fc7742d614b0"},
+ {file = "coverage-7.13.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:936bc20503ce24770c71938d1369461f0c5320830800933bc3956e2a4ded930e"},
+ {file = "coverage-7.13.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:af0a583efaacc52ae2521f8d7910aff65cdb093091d76291ac5820d5e947fc1c"},
+ {file = "coverage-7.13.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f1c23e24a7000da892a312fb17e33c5f94f8b001de44b7cf8ba2e36fbd15859e"},
+ {file = "coverage-7.13.0-cp313-cp313t-win32.whl", hash = "sha256:5f8a0297355e652001015e93be345ee54393e45dc3050af4a0475c5a2b767d46"},
+ {file = "coverage-7.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6abb3a4c52f05e08460bd9acf04fec027f8718ecaa0d09c40ffbc3fbd70ecc39"},
+ {file = "coverage-7.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:3ad968d1e3aa6ce5be295ab5fe3ae1bf5bb4769d0f98a80a0252d543a2ef2e9e"},
+ {file = "coverage-7.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:453b7ec753cf5e4356e14fe858064e5520c460d3bbbcb9c35e55c0d21155c256"},
+ {file = "coverage-7.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:af827b7cbb303e1befa6c4f94fd2bf72f108089cfa0f8abab8f4ca553cf5ca5a"},
+ {file = "coverage-7.13.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9987a9e4f8197a1000280f7cc089e3ea2c8b3c0a64d750537809879a7b4ceaf9"},
+ {file = "coverage-7.13.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3188936845cd0cb114fa6a51842a304cdbac2958145d03be2377ec41eb285d19"},
+ {file = "coverage-7.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2bdb3babb74079f021696cb46b8bb5f5661165c385d3a238712b031a12355be"},
+ {file = "coverage-7.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7464663eaca6adba4175f6c19354feea61ebbdd735563a03d1e472c7072d27bb"},
+ {file = "coverage-7.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8069e831f205d2ff1f3d355e82f511eb7c5522d7d413f5db5756b772ec8697f8"},
+ {file = "coverage-7.13.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6fb2d5d272341565f08e962cce14cdf843a08ac43bd621783527adb06b089c4b"},
+ {file = "coverage-7.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5e70f92ef89bac1ac8a99b3324923b4749f008fdbd7aa9cb35e01d7a284a04f9"},
+ {file = "coverage-7.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4b5de7d4583e60d5fd246dd57fcd3a8aa23c6e118a8c72b38adf666ba8e7e927"},
+ {file = "coverage-7.13.0-cp314-cp314-win32.whl", hash = "sha256:a6c6e16b663be828a8f0b6c5027d36471d4a9f90d28444aa4ced4d48d7d6ae8f"},
+ {file = "coverage-7.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:0900872f2fdb3ee5646b557918d02279dc3af3dfb39029ac4e945458b13f73bc"},
+ {file = "coverage-7.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:3a10260e6a152e5f03f26db4a407c4c62d3830b9af9b7c0450b183615f05d43b"},
+ {file = "coverage-7.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9097818b6cc1cfb5f174e3263eba4a62a17683bcfe5c4b5d07f4c97fa51fbf28"},
+ {file = "coverage-7.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0018f73dfb4301a89292c73be6ba5f58722ff79f51593352759c1790ded1cabe"},
+ {file = "coverage-7.13.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:166ad2a22ee770f5656e1257703139d3533b4a0b6909af67c6b4a3adc1c98657"},
+ {file = "coverage-7.13.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f6aaef16d65d1787280943f1c8718dc32e9cf141014e4634d64446702d26e0ff"},
+ {file = "coverage-7.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e999e2dcc094002d6e2c7bbc1fb85b58ba4f465a760a8014d97619330cdbbbf3"},
+ {file = "coverage-7.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:00c3d22cf6fb1cf3bf662aaaa4e563be8243a5ed2630339069799835a9cc7f9b"},
+ {file = "coverage-7.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22ccfe8d9bb0d6134892cbe1262493a8c70d736b9df930f3f3afae0fe3ac924d"},
+ {file = "coverage-7.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9372dff5ea15930fea0445eaf37bbbafbc771a49e70c0aeed8b4e2c2614cc00e"},
+ {file = "coverage-7.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:69ac2c492918c2461bc6ace42d0479638e60719f2a4ef3f0815fa2df88e9f940"},
+ {file = "coverage-7.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:739c6c051a7540608d097b8e13c76cfa85263ced467168dc6b477bae3df7d0e2"},
+ {file = "coverage-7.13.0-cp314-cp314t-win32.whl", hash = "sha256:fe81055d8c6c9de76d60c94ddea73c290b416e061d40d542b24a5871bad498b7"},
+ {file = "coverage-7.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:445badb539005283825959ac9fa4a28f712c214b65af3a2c464f1adc90f5fcbc"},
+ {file = "coverage-7.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:de7f6748b890708578fc4b7bb967d810aeb6fcc9bff4bb77dbca77dab2f9df6a"},
+ {file = "coverage-7.13.0-py3-none-any.whl", hash = "sha256:850d2998f380b1e266459ca5b47bc9e7daf9af1d070f66317972f382d46f1904"},
+ {file = "coverage-7.13.0.tar.gz", hash = "sha256:a394aa27f2d7ff9bc04cf703817773a59ad6dfbd577032e690f961d2460ee936"},
]
[package.dependencies]
@@ -385,14 +384,14 @@ profile = ["gprof2dot (>=2022.7.29)"]
[[package]]
name = "docutils"
-version = "0.22.3"
+version = "0.22.4"
description = "Docutils -- Python Documentation Utilities"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "docutils-0.22.3-py3-none-any.whl", hash = "sha256:bd772e4aca73aff037958d44f2be5229ded4c09927fcf8690c577b66234d6ceb"},
- {file = "docutils-0.22.3.tar.gz", hash = "sha256:21486ae730e4ca9f622677b1412b879af1791efcfba517e4c6f60be543fc8cdd"},
+ {file = "docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de"},
+ {file = "docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968"},
]
[[package]]
@@ -416,14 +415,14 @@ test = ["pytest (>=6)"]
[[package]]
name = "faker"
-version = "38.2.0"
+version = "39.0.0"
description = "Faker is a Python package that generates fake data for you."
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "faker-38.2.0-py3-none-any.whl", hash = "sha256:35fe4a0a79dee0dc4103a6083ee9224941e7d3594811a50e3969e547b0d2ee65"},
- {file = "faker-38.2.0.tar.gz", hash = "sha256:20672803db9c7cb97f9b56c18c54b915b6f1d8991f63d1d673642dc43f5ce7ab"},
+ {file = "faker-39.0.0-py3-none-any.whl", hash = "sha256:c72f1fca8f1a24b8da10fcaa45739135a19772218ddd61b86b7ea1b8c790dce7"},
+ {file = "faker-39.0.0.tar.gz", hash = "sha256:ddae46d3b27e01cea7894651d687b33bcbe19a45ef044042c721ceac6d3da0ff"},
]
[package.dependencies]
@@ -431,26 +430,26 @@ tzdata = "*"
[[package]]
name = "fastapi"
-version = "0.122.0"
+version = "0.127.0"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
groups = ["dev"]
files = [
- {file = "fastapi-0.122.0-py3-none-any.whl", hash = "sha256:a456e8915dfc6c8914a50d9651133bd47ec96d331c5b44600baa635538a30d67"},
- {file = "fastapi-0.122.0.tar.gz", hash = "sha256:cd9b5352031f93773228af8b4c443eedc2ac2aa74b27780387b853c3726fb94b"},
+ {file = "fastapi-0.127.0-py3-none-any.whl", hash = "sha256:725aa2bb904e2eff8031557cf4b9b77459bfedd63cae8427634744fd199f6a49"},
+ {file = "fastapi-0.127.0.tar.gz", hash = "sha256:5a9246e03dcd1fdb19f1396db30894867c1d630f5107dc167dcbc5ed1ea7d259"},
]
[package.dependencies]
annotated-doc = ">=0.0.2"
-pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
+pydantic = ">=2.7.0"
starlette = ">=0.40.0,<0.51.0"
typing-extensions = ">=4.8.0"
[package.extras]
all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
-standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
-standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[standard-no-fastapi-cloud-cli] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
+standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
+standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[standard-no-fastapi-cloud-cli] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"]
[[package]]
name = "genbadge"
@@ -671,6 +670,93 @@ files = [
{file = "lazy_object_proxy-1.12.0.tar.gz", hash = "sha256:1f5a462d92fd0cfb82f1fab28b51bfb209fabbe6aabf7f0d51472c0c124c0c61"},
]
+[[package]]
+name = "librt"
+version = "0.7.4"
+description = "Mypyc runtime library"
+optional = false
+python-versions = ">=3.9"
+groups = ["type-checking"]
+markers = "platform_python_implementation != \"PyPy\""
+files = [
+ {file = "librt-0.7.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dc300cb5a5a01947b1ee8099233156fdccd5001739e5f596ecfbc0dab07b5a3b"},
+ {file = "librt-0.7.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee8d3323d921e0f6919918a97f9b5445a7dfe647270b2629ec1008aa676c0bc0"},
+ {file = "librt-0.7.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:95cb80854a355b284c55f79674f6187cc9574df4dc362524e0cce98c89ee8331"},
+ {file = "librt-0.7.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ca1caedf8331d8ad6027f93b52d68ed8f8009f5c420c246a46fe9d3be06be0f"},
+ {file = "librt-0.7.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2a6f1236151e6fe1da289351b5b5bce49651c91554ecc7b70a947bced6fe212"},
+ {file = "librt-0.7.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7766b57aeebaf3f1dac14fdd4a75c9a61f2ed56d8ebeefe4189db1cb9d2a3783"},
+ {file = "librt-0.7.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1c4c89fb01157dd0a3bfe9e75cd6253b0a1678922befcd664eca0772a4c6c979"},
+ {file = "librt-0.7.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f7fa8beef580091c02b4fd26542de046b2abfe0aaefa02e8bcf68acb7618f2b3"},
+ {file = "librt-0.7.4-cp310-cp310-win32.whl", hash = "sha256:543c42fa242faae0466fe72d297976f3c710a357a219b1efde3a0539a68a6997"},
+ {file = "librt-0.7.4-cp310-cp310-win_amd64.whl", hash = "sha256:25cc40d8eb63f0a7ea4c8f49f524989b9df901969cb860a2bc0e4bad4b8cb8a8"},
+ {file = "librt-0.7.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3485b9bb7dfa66167d5500ffdafdc35415b45f0da06c75eb7df131f3357b174a"},
+ {file = "librt-0.7.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:188b4b1a770f7f95ea035d5bbb9d7367248fc9d12321deef78a269ebf46a5729"},
+ {file = "librt-0.7.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1b668b1c840183e4e38ed5a99f62fac44c3a3eef16870f7f17cfdfb8b47550ed"},
+ {file = "librt-0.7.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0e8f864b521f6cfedb314d171630f827efee08f5c3462bcbc2244ab8e1768cd6"},
+ {file = "librt-0.7.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df7c9def4fc619a9c2ab402d73a0c5b53899abe090e0100323b13ccb5a3dd82"},
+ {file = "librt-0.7.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f79bc3595b6ed159a1bf0cdc70ed6ebec393a874565cab7088a219cca14da727"},
+ {file = "librt-0.7.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77772a4b8b5f77d47d883846928c36d730b6e612a6388c74cba33ad9eb149c11"},
+ {file = "librt-0.7.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:064a286e6ab0b4c900e228ab4fa9cb3811b4b83d3e0cc5cd816b2d0f548cb61c"},
+ {file = "librt-0.7.4-cp311-cp311-win32.whl", hash = "sha256:42da201c47c77b6cc91fc17e0e2b330154428d35d6024f3278aa2683e7e2daf2"},
+ {file = "librt-0.7.4-cp311-cp311-win_amd64.whl", hash = "sha256:d31acb5886c16ae1711741f22504195af46edec8315fe69b77e477682a87a83e"},
+ {file = "librt-0.7.4-cp311-cp311-win_arm64.whl", hash = "sha256:114722f35093da080a333b3834fff04ef43147577ed99dd4db574b03a5f7d170"},
+ {file = "librt-0.7.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7dd3b5c37e0fb6666c27cf4e2c88ae43da904f2155c4cfc1e5a2fdce3b9fcf92"},
+ {file = "librt-0.7.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9c5de1928c486201b23ed0cc4ac92e6e07be5cd7f3abc57c88a9cf4f0f32108"},
+ {file = "librt-0.7.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:078ae52ffb3f036396cc4aed558e5b61faedd504a3c1f62b8ae34bf95ae39d94"},
+ {file = "librt-0.7.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce58420e25097b2fc201aef9b9f6d65df1eb8438e51154e1a7feb8847e4a55ab"},
+ {file = "librt-0.7.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b719c8730c02a606dc0e8413287e8e94ac2d32a51153b300baf1f62347858fba"},
+ {file = "librt-0.7.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3749ef74c170809e6dee68addec9d2458700a8de703de081c888e92a8b015cf9"},
+ {file = "librt-0.7.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b35c63f557653c05b5b1b6559a074dbabe0afee28ee2a05b6c9ba21ad0d16a74"},
+ {file = "librt-0.7.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1ef704e01cb6ad39ad7af668d51677557ca7e5d377663286f0ee1b6b27c28e5f"},
+ {file = "librt-0.7.4-cp312-cp312-win32.whl", hash = "sha256:c66c2b245926ec15188aead25d395091cb5c9df008d3b3207268cd65557d6286"},
+ {file = "librt-0.7.4-cp312-cp312-win_amd64.whl", hash = "sha256:71a56f4671f7ff723451f26a6131754d7c1809e04e22ebfbac1db8c9e6767a20"},
+ {file = "librt-0.7.4-cp312-cp312-win_arm64.whl", hash = "sha256:419eea245e7ec0fe664eb7e85e7ff97dcdb2513ca4f6b45a8ec4a3346904f95a"},
+ {file = "librt-0.7.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d44a1b1ba44cbd2fc3cb77992bef6d6fdb1028849824e1dd5e4d746e1f7f7f0b"},
+ {file = "librt-0.7.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c9cab4b3de1f55e6c30a84c8cee20e4d3b2476f4d547256694a1b0163da4fe32"},
+ {file = "librt-0.7.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2857c875f1edd1feef3c371fbf830a61b632fb4d1e57160bb1e6a3206e6abe67"},
+ {file = "librt-0.7.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b370a77be0a16e1ad0270822c12c21462dc40496e891d3b0caf1617c8cc57e20"},
+ {file = "librt-0.7.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d05acd46b9a52087bfc50c59dfdf96a2c480a601e8898a44821c7fd676598f74"},
+ {file = "librt-0.7.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:70969229cb23d9c1a80e14225838d56e464dc71fa34c8342c954fc50e7516dee"},
+ {file = "librt-0.7.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4450c354b89dbb266730893862dbff06006c9ed5b06b6016d529b2bf644fc681"},
+ {file = "librt-0.7.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:adefe0d48ad35b90b6f361f6ff5a1bd95af80c17d18619c093c60a20e7a5b60c"},
+ {file = "librt-0.7.4-cp313-cp313-win32.whl", hash = "sha256:21ea710e96c1e050635700695095962a22ea420d4b3755a25e4909f2172b4ff2"},
+ {file = "librt-0.7.4-cp313-cp313-win_amd64.whl", hash = "sha256:772e18696cf5a64afee908662fbcb1f907460ddc851336ee3a848ef7684c8e1e"},
+ {file = "librt-0.7.4-cp313-cp313-win_arm64.whl", hash = "sha256:52e34c6af84e12921748c8354aa6acf1912ca98ba60cdaa6920e34793f1a0788"},
+ {file = "librt-0.7.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4f1ee004942eaaed6e06c087d93ebc1c67e9a293e5f6b9b5da558df6bf23dc5d"},
+ {file = "librt-0.7.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d854c6dc0f689bad7ed452d2a3ecff58029d80612d336a45b62c35e917f42d23"},
+ {file = "librt-0.7.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a4f7339d9e445280f23d63dea842c0c77379c4a47471c538fc8feedab9d8d063"},
+ {file = "librt-0.7.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39003fc73f925e684f8521b2dbf34f61a5deb8a20a15dcf53e0d823190ce8848"},
+ {file = "librt-0.7.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6bb15ee29d95875ad697d449fe6071b67f730f15a6961913a2b0205015ca0843"},
+ {file = "librt-0.7.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:02a69369862099e37d00765583052a99d6a68af7e19b887e1b78fee0146b755a"},
+ {file = "librt-0.7.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ec72342cc4d62f38b25a94e28b9efefce41839aecdecf5e9627473ed04b7be16"},
+ {file = "librt-0.7.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:776dbb9bfa0fc5ce64234b446995d8d9f04badf64f544ca036bd6cff6f0732ce"},
+ {file = "librt-0.7.4-cp314-cp314-win32.whl", hash = "sha256:0f8cac84196d0ffcadf8469d9ded4d4e3a8b1c666095c2a291e22bf58e1e8a9f"},
+ {file = "librt-0.7.4-cp314-cp314-win_amd64.whl", hash = "sha256:037f5cb6fe5abe23f1dc058054d50e9699fcc90d0677eee4e4f74a8677636a1a"},
+ {file = "librt-0.7.4-cp314-cp314-win_arm64.whl", hash = "sha256:a5deebb53d7a4d7e2e758a96befcd8edaaca0633ae71857995a0f16033289e44"},
+ {file = "librt-0.7.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b4c25312c7f4e6ab35ab16211bdf819e6e4eddcba3b2ea632fb51c9a2a97e105"},
+ {file = "librt-0.7.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:618b7459bb392bdf373f2327e477597fff8f9e6a1878fffc1b711c013d1b0da4"},
+ {file = "librt-0.7.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1437c3f72a30c7047f16fd3e972ea58b90172c3c6ca309645c1c68984f05526a"},
+ {file = "librt-0.7.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c96cb76f055b33308f6858b9b594618f1b46e147a4d03a4d7f0c449e304b9b95"},
+ {file = "librt-0.7.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28f990e6821204f516d09dc39966ef8b84556ffd648d5926c9a3f681e8de8906"},
+ {file = "librt-0.7.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc4aebecc79781a1b77d7d4e7d9fe080385a439e198d993b557b60f9117addaf"},
+ {file = "librt-0.7.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:022cc673e69283a42621dd453e2407cf1647e77f8bd857d7ad7499901e62376f"},
+ {file = "librt-0.7.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2b3ca211ae8ea540569e9c513da052699b7b06928dcda61247cb4f318122bdb5"},
+ {file = "librt-0.7.4-cp314-cp314t-win32.whl", hash = "sha256:8a461f6456981d8c8e971ff5a55f2e34f4e60871e665d2f5fde23ee74dea4eeb"},
+ {file = "librt-0.7.4-cp314-cp314t-win_amd64.whl", hash = "sha256:721a7b125a817d60bf4924e1eec2a7867bfcf64cfc333045de1df7a0629e4481"},
+ {file = "librt-0.7.4-cp314-cp314t-win_arm64.whl", hash = "sha256:76b2ba71265c0102d11458879b4d53ccd0b32b0164d14deb8d2b598a018e502f"},
+ {file = "librt-0.7.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6fc4aa67fedd827a601f97f0e61cc72711d0a9165f2c518e9a7c38fc1568b9ad"},
+ {file = "librt-0.7.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e710c983d29d9cc4da29113b323647db286eaf384746344f4a233708cca1a82c"},
+ {file = "librt-0.7.4-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:43a2515a33f2bc17b15f7fb49ff6426e49cb1d5b2539bc7f8126b9c5c7f37164"},
+ {file = "librt-0.7.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0fd766bb9ace3498f6b93d32f30c0e7c8ce6b727fecbc84d28160e217bb66254"},
+ {file = "librt-0.7.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce1b44091355b68cffd16e2abac07c1cafa953fa935852d3a4dd8975044ca3bf"},
+ {file = "librt-0.7.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5a72b905420c4bb2c10c87b5c09fe6faf4a76d64730e3802feef255e43dfbf5a"},
+ {file = "librt-0.7.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:07c4d7c9305e75a0edd3427b79c7bd1d019cd7eddaa7c89dbb10e0c7946bffbb"},
+ {file = "librt-0.7.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2e734c2c54423c6dcc77f58a8585ba83b9f72e422f9edf09cab1096d4a4bdc82"},
+ {file = "librt-0.7.4-cp39-cp39-win32.whl", hash = "sha256:a34ae11315d4e26326aaf04e21ccd8d9b7de983635fba38d73e203a9c8e3fe3d"},
+ {file = "librt-0.7.4-cp39-cp39-win_amd64.whl", hash = "sha256:7e4b5ffa1614ad4f32237d739699be444be28de95071bfa4e66a8da9fa777798"},
+ {file = "librt-0.7.4.tar.gz", hash = "sha256:3871af56c59864d5fd21d1ac001eb2fb3b140d52ba0454720f2e4a19812404ba"},
+]
+
[[package]]
name = "markdown-it-py"
version = "4.0.0"
@@ -832,53 +918,54 @@ files = [
[[package]]
name = "mypy"
-version = "1.18.2"
+version = "1.19.1"
description = "Optional static typing for Python"
optional = false
python-versions = ">=3.9"
groups = ["type-checking"]
files = [
- {file = "mypy-1.18.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eab0cf6294dafe397c261a75f96dc2c31bffe3b944faa24db5def4e2b0f77c"},
- {file = "mypy-1.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a780ca61fc239e4865968ebc5240bb3bf610ef59ac398de9a7421b54e4a207e"},
- {file = "mypy-1.18.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448acd386266989ef11662ce3c8011fd2a7b632e0ec7d61a98edd8e27472225b"},
- {file = "mypy-1.18.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9e171c465ad3901dc652643ee4bffa8e9fef4d7d0eece23b428908c77a76a66"},
- {file = "mypy-1.18.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:592ec214750bc00741af1f80cbf96b5013d81486b7bb24cb052382c19e40b428"},
- {file = "mypy-1.18.2-cp310-cp310-win_amd64.whl", hash = "sha256:7fb95f97199ea11769ebe3638c29b550b5221e997c63b14ef93d2e971606ebed"},
- {file = "mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f"},
- {file = "mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341"},
- {file = "mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d"},
- {file = "mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86"},
- {file = "mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37"},
- {file = "mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8"},
- {file = "mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34"},
- {file = "mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764"},
- {file = "mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893"},
- {file = "mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914"},
- {file = "mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8"},
- {file = "mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074"},
- {file = "mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc"},
- {file = "mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e"},
- {file = "mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986"},
- {file = "mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d"},
- {file = "mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba"},
- {file = "mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544"},
- {file = "mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce"},
- {file = "mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d"},
- {file = "mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c"},
- {file = "mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb"},
- {file = "mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075"},
- {file = "mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf"},
- {file = "mypy-1.18.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25a9c8fb67b00599f839cf472713f54249a62efd53a54b565eb61956a7e3296b"},
- {file = "mypy-1.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2b9c7e284ee20e7598d6f42e13ca40b4928e6957ed6813d1ab6348aa3f47133"},
- {file = "mypy-1.18.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6985ed057513e344e43a26cc1cd815c7a94602fb6a3130a34798625bc2f07b6"},
- {file = "mypy-1.18.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22f27105f1525ec024b5c630c0b9f36d5c1cc4d447d61fe51ff4bd60633f47ac"},
- {file = "mypy-1.18.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:030c52d0ea8144e721e49b1f68391e39553d7451f0c3f8a7565b59e19fcb608b"},
- {file = "mypy-1.18.2-cp39-cp39-win_amd64.whl", hash = "sha256:aa5e07ac1a60a253445797e42b8b2963c9675563a94f11291ab40718b016a7a0"},
- {file = "mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e"},
- {file = "mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b"},
+ {file = "mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec"},
+ {file = "mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b"},
+ {file = "mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6"},
+ {file = "mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74"},
+ {file = "mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1"},
+ {file = "mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac"},
+ {file = "mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288"},
+ {file = "mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab"},
+ {file = "mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6"},
+ {file = "mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331"},
+ {file = "mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925"},
+ {file = "mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042"},
+ {file = "mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1"},
+ {file = "mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e"},
+ {file = "mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2"},
+ {file = "mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8"},
+ {file = "mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a"},
+ {file = "mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13"},
+ {file = "mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250"},
+ {file = "mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b"},
+ {file = "mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e"},
+ {file = "mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef"},
+ {file = "mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75"},
+ {file = "mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd"},
+ {file = "mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1"},
+ {file = "mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718"},
+ {file = "mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b"},
+ {file = "mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045"},
+ {file = "mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957"},
+ {file = "mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f"},
+ {file = "mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3"},
+ {file = "mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a"},
+ {file = "mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67"},
+ {file = "mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e"},
+ {file = "mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376"},
+ {file = "mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24"},
+ {file = "mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247"},
+ {file = "mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba"},
]
[package.dependencies]
+librt = {version = ">=0.6.2", markers = "platform_python_implementation != \"PyPy\""}
mypy_extensions = ">=1.0.0"
pathspec = ">=0.9.0"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
@@ -905,47 +992,46 @@ files = [
[[package]]
name = "nodeenv"
-version = "1.9.1"
+version = "1.10.0"
description = "Node.js virtual environment builder"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
groups = ["type-checking"]
files = [
- {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
- {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
+ {file = "nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827"},
+ {file = "nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb"},
]
[[package]]
name = "openapi-core"
-version = "0.19.5"
+version = "0.22.0"
description = "client-side and server-side support for the OpenAPI Specification v3"
optional = false
-python-versions = "<4.0.0,>=3.8.0"
+python-versions = "<4.0.0,>=3.9.0"
groups = ["main"]
files = [
- {file = "openapi_core-0.19.5-py3-none-any.whl", hash = "sha256:ef7210e83a59394f46ce282639d8d26ad6fc8094aa904c9c16eb1bac8908911f"},
- {file = "openapi_core-0.19.5.tar.gz", hash = "sha256:421e753da56c391704454e66afe4803a290108590ac8fa6f4a4487f4ec11f2d3"},
+ {file = "openapi_core-0.22.0-py3-none-any.whl", hash = "sha256:8fb7c325f2db4ef6c60584b1870f90eeb3183aa47e30643715c5003b7677a149"},
+ {file = "openapi_core-0.22.0.tar.gz", hash = "sha256:b30490dfa74e3aac2276105525590135212352f5dd7e5acf8f62f6a89ed6f2d0"},
]
[package.dependencies]
isodate = "*"
-jsonschema = ">=4.18.0,<5.0.0"
-jsonschema-path = ">=0.3.1,<0.4.0"
+jsonschema = ">=4.23.0,<5.0.0"
+jsonschema-path = ">=0.3.4,<0.4.0"
more-itertools = "*"
openapi-schema-validator = ">=0.6.0,<0.7.0"
openapi-spec-validator = ">=0.7.1,<0.8.0"
-parse = "*"
typing-extensions = ">=4.8.0,<5.0.0"
-werkzeug = "<3.1.2"
+werkzeug = ">=2.1.0"
[package.extras]
aiohttp = ["aiohttp (>=3.0)", "multidict (>=6.0.4,<7.0.0)"]
django = ["django (>=3.0)"]
falcon = ["falcon (>=3.0)"]
-fastapi = ["fastapi (>=0.111,<0.116)"]
+fastapi = ["fastapi (>=0.111,<0.125)"]
flask = ["flask"]
requests = ["requests"]
-starlette = ["aioitertools (>=0.11,<0.13)", "starlette (>=0.26.1,<0.45.0)"]
+starlette = ["aioitertools (>=0.11,<0.14)", "starlette (>=0.26.1,<0.50.0)"]
[[package]]
name = "openapi-schema-validator"
@@ -994,18 +1080,6 @@ files = [
{file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"},
]
-[[package]]
-name = "parse"
-version = "1.20.2"
-description = "parse() is the opposite of format()"
-optional = false
-python-versions = "*"
-groups = ["main"]
-files = [
- {file = "parse-1.20.2-py2.py3-none-any.whl", hash = "sha256:967095588cb802add9177d0c0b6133b5ba33b1ea9007ca800e526f42a85af558"},
- {file = "parse-1.20.2.tar.gz", hash = "sha256:b41d604d16503c79d81af5165155c0b20f6c8d6c559efa66b4b695c3e5a0a0ce"},
-]
-
[[package]]
name = "pathable"
version = "0.4.4"
@@ -1141,14 +1215,14 @@ xmp = ["defusedxml"]
[[package]]
name = "platformdirs"
-version = "4.5.0"
+version = "4.5.1"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
optional = false
python-versions = ">=3.10"
groups = ["dev", "lint-and-format", "type-checking"]
files = [
- {file = "platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3"},
- {file = "platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312"},
+ {file = "platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31"},
+ {file = "platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda"},
]
[package.extras]
@@ -1372,14 +1446,14 @@ windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pylint"
-version = "4.0.3"
+version = "4.0.4"
description = "python code static checker"
optional = false
python-versions = ">=3.10.0"
groups = ["lint-and-format"]
files = [
- {file = "pylint-4.0.3-py3-none-any.whl", hash = "sha256:896d09afb0e78bbf2e030cd1f3d8dc92771a51f7e46828cbc3948a89cd03433a"},
- {file = "pylint-4.0.3.tar.gz", hash = "sha256:a427fe76e0e5355e9fb9b604fd106c419cafb395886ba7f3cebebb03f30e081d"},
+ {file = "pylint-4.0.4-py3-none-any.whl", hash = "sha256:63e06a37d5922555ee2c20963eb42559918c20bd2b21244e4ef426e7c43b92e0"},
+ {file = "pylint-4.0.4.tar.gz", hash = "sha256:d9b71674e19b1c36d79265b5887bf8e55278cbe236c9e95d22dc82cf044fdbd2"},
]
[package.dependencies]
@@ -1591,14 +1665,14 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]]
name = "rich-click"
-version = "1.9.4"
+version = "1.9.5"
description = "Format click help output nicely with rich"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
- {file = "rich_click-1.9.4-py3-none-any.whl", hash = "sha256:d70f39938bcecaf5543e8750828cbea94ef51853f7d0e174cda1e10543767389"},
- {file = "rich_click-1.9.4.tar.gz", hash = "sha256:af73dc68e85f3bebb80ce302a642b9fe3b65f3df0ceb42eb9a27c467c1b678c8"},
+ {file = "rich_click-1.9.5-py3-none-any.whl", hash = "sha256:9b195721a773b1acf0e16ff9ec68cef1e7d237e53471e6e3f7ade462f86c403a"},
+ {file = "rich_click-1.9.5.tar.gz", hash = "sha256:48120531493f1533828da80e13e839d471979ec8d7d0ca7b35f86a1379cc74b6"},
]
[package.dependencies]
@@ -1613,62 +1687,62 @@ docs = ["markdown-include (>=0.8.1)", "mike (>=2.1.3)", "mkdocs-github-admonitio
[[package]]
name = "robotcode"
-version = "2.0.4"
+version = "2.1.0"
description = "Command line interface for RobotCode"
optional = false
python-versions = ">=3.10"
groups = ["dev", "type-checking"]
files = [
- {file = "robotcode-2.0.4-py3-none-any.whl", hash = "sha256:bed5fcceb69cf2b65547cfc2cf9a2e4493992b448e3ef5da0b301f09c594b3ff"},
- {file = "robotcode-2.0.4.tar.gz", hash = "sha256:d58d602c02899a65de6dc4fff393a2b667b1c7c04b7c0fccb526b4fb1ed586ed"},
+ {file = "robotcode-2.1.0-py3-none-any.whl", hash = "sha256:5e10b76b445b9818b92e5e242b58e1fbdfdc2c3c0ebf9e692b9e999211d66ba7"},
+ {file = "robotcode-2.1.0.tar.gz", hash = "sha256:c476dfac87b11ecc9223bd6a14790c7211bef7896ef62125bc8ff58ebd2b208f"},
]
[package.dependencies]
-robotcode-core = "2.0.4"
-robotcode-plugin = "2.0.4"
-robotcode-robot = "2.0.4"
+robotcode-core = "*"
+robotcode-plugin = "*"
+robotcode-robot = "*"
[package.extras]
-all = ["docutils", "pyyaml (>=5.4)", "rich", "robotcode-analyze (==2.0.4)", "robotcode-debugger (==2.0.4)", "robotcode-language-server (==2.0.4)", "robotcode-repl (==2.0.4)", "robotcode-repl-server (==2.0.4)", "robotcode-runner (==2.0.4)", "robotframework-robocop (>=2.0.0)"]
-analyze = ["robotcode-analyze (==2.0.4)"]
+all = ["docutils", "pyyaml (>=5.4)", "rich", "robotcode-analyze", "robotcode-debugger", "robotcode-language-server", "robotcode-repl", "robotcode-repl-server", "robotcode-runner", "robotframework-robocop (>=6.0.0)"]
+analyze = ["robotcode-analyze (==2.1.0)"]
colored = ["rich"]
-debugger = ["robotcode-debugger (==2.0.4)"]
-languageserver = ["robotcode-language-server (==2.0.4)"]
+debugger = ["robotcode-debugger (==2.1.0)"]
+languageserver = ["robotcode-language-server (==2.1.0)"]
lint = ["robotframework-robocop (>=2.0.0)"]
-repl = ["robotcode-repl (==2.0.4)"]
-replserver = ["robotcode-repl-server (==2.0.4)"]
+repl = ["robotcode-repl (==2.1.0)"]
+replserver = ["robotcode-repl-server (==2.1.0)"]
rest = ["docutils"]
-runner = ["robotcode-runner (==2.0.4)"]
+runner = ["robotcode-runner (==2.1.0)"]
yaml = ["pyyaml (>=5.4)"]
[[package]]
name = "robotcode-analyze"
-version = "2.0.4"
+version = "2.1.0"
description = "RobotCode analyze plugin for Robot Framework"
optional = false
python-versions = ">=3.10"
groups = ["type-checking"]
files = [
- {file = "robotcode_analyze-2.0.4-py3-none-any.whl", hash = "sha256:75660bbe9d8c3c83968e92f8492aaf2011eaac65b6f915f173f1e08564afae79"},
- {file = "robotcode_analyze-2.0.4.tar.gz", hash = "sha256:c7d76f544b4e27cb970f2cec030c375e1765627db91ff1b26c6bf45a360b6534"},
+ {file = "robotcode_analyze-2.1.0-py3-none-any.whl", hash = "sha256:08eba0935c01c8ccb74b193043463c114c392363dd2c37dbf79f0fc44909ffe6"},
+ {file = "robotcode_analyze-2.1.0.tar.gz", hash = "sha256:9d6e29c67302f38a92963c85d9523f72bbd1cae6494658cee283c02c4095e333"},
]
[package.dependencies]
-robotcode = "2.0.4"
-robotcode-plugin = "2.0.4"
-robotcode-robot = "2.0.4"
-robotframework = ">=4.1.0"
+robotcode = "*"
+robotcode-plugin = "*"
+robotcode-robot = "*"
+robotframework = ">=5.0.0"
[[package]]
name = "robotcode-core"
-version = "2.0.4"
+version = "2.1.0"
description = "Some core classes for RobotCode"
optional = false
python-versions = ">=3.10"
groups = ["dev", "type-checking"]
files = [
- {file = "robotcode_core-2.0.4-py3-none-any.whl", hash = "sha256:2100975759c6bc385643fc3ee578740f735619a92febd318f07a2910833a6a67"},
- {file = "robotcode_core-2.0.4.tar.gz", hash = "sha256:99e17b824dd1d37c8e2c013b5ed8920af8aaa83fdd7a8938326db952a669683a"},
+ {file = "robotcode_core-2.1.0-py3-none-any.whl", hash = "sha256:fc6603553adef020fe43db962dc5145d60aee3983ede9b587cf9b2ce54ac21b9"},
+ {file = "robotcode_core-2.1.0.tar.gz", hash = "sha256:e8c100e037d72dde2da737f6e4ab0b0084e6db4205fe4dd852d63213dd319ff3"},
]
[package.dependencies]
@@ -1676,29 +1750,29 @@ typing-extensions = ">=4.4.0"
[[package]]
name = "robotcode-modifiers"
-version = "2.0.4"
+version = "2.1.0"
description = "Some Robot Framework Modifiers for RobotCode"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
- {file = "robotcode_modifiers-2.0.4-py3-none-any.whl", hash = "sha256:9abbf02d7da5e01bfd0b9f440b5a005366b9f4079001467b066b1797da6bac7c"},
- {file = "robotcode_modifiers-2.0.4.tar.gz", hash = "sha256:36dec0d624eb8366f2db9b52de337f5647ae0b0b821dc6d0e8da74999be5ea6c"},
+ {file = "robotcode_modifiers-2.1.0-py3-none-any.whl", hash = "sha256:2b941b7c21d96c0cbe060f3e88dda57ab72ddbb88b1951acf6d2fbfbb4b0b078"},
+ {file = "robotcode_modifiers-2.1.0.tar.gz", hash = "sha256:a31a6be68ab6c6dd02f7a3c6a86dc1c4381c9cb9e8bc40146a562f59187f2c7e"},
]
[package.dependencies]
-robotframework = ">=4.1.0"
+robotframework = ">=5.0.0"
[[package]]
name = "robotcode-plugin"
-version = "2.0.4"
+version = "2.1.0"
description = "Some classes for RobotCode plugin management"
optional = false
python-versions = ">=3.10"
groups = ["dev", "type-checking"]
files = [
- {file = "robotcode_plugin-2.0.4-py3-none-any.whl", hash = "sha256:a3cf6b73741a03feeb1e570010ba07519b4ecda05e3a65213388e8110c5b875a"},
- {file = "robotcode_plugin-2.0.4.tar.gz", hash = "sha256:d58661344a0f4b547e93d611945032b01ae8a53f2e8b5db2d02d27aeaa3f09aa"},
+ {file = "robotcode_plugin-2.1.0-py3-none-any.whl", hash = "sha256:140323c767ef96c34527807176eff0dd67e8b1680fb8086886596cab5a17f0fe"},
+ {file = "robotcode_plugin-2.1.0.tar.gz", hash = "sha256:8bfc2eb78ec72c78f0bad7efa9eebb7345a121abc75a24882d1f2af80f14b838"},
]
[package.dependencies]
@@ -1709,51 +1783,51 @@ tomli-w = ">=1.0.0"
[[package]]
name = "robotcode-robot"
-version = "2.0.4"
+version = "2.1.0"
description = "Support classes for RobotCode for handling Robot Framework projects."
optional = false
python-versions = ">=3.10"
groups = ["dev", "type-checking"]
files = [
- {file = "robotcode_robot-2.0.4-py3-none-any.whl", hash = "sha256:6df93aec89bd412e3c675044d0eec5d969c4c2392058dc41b170831296b229ac"},
- {file = "robotcode_robot-2.0.4.tar.gz", hash = "sha256:6fd4a5dd41ca6133dadb71bdb0e94952203dca9d7f7055a3c8af99db68ef5dce"},
+ {file = "robotcode_robot-2.1.0-py3-none-any.whl", hash = "sha256:5d07364576119fd5c580ddf0ee12eb42b661e9ecaf07c9b030ae44de38b01c36"},
+ {file = "robotcode_robot-2.1.0.tar.gz", hash = "sha256:048389b8f81677b5cb9356d6fa1ea68f4254e984efc17c8409737d6a53d2cb62"},
]
[package.dependencies]
platformdirs = ">=4.3"
-robotcode-core = "2.0.4"
-robotframework = ">=4.1.0"
+robotcode-core = "*"
+robotframework = ">=5.0.0"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
[[package]]
name = "robotcode-runner"
-version = "2.0.4"
+version = "2.1.0"
description = "RobotCode runner for Robot Framework"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
- {file = "robotcode_runner-2.0.4-py3-none-any.whl", hash = "sha256:e835aad189c5702b1a2d061b96bb4e4e0acee9056ca453e8ced2572a9da58978"},
- {file = "robotcode_runner-2.0.4.tar.gz", hash = "sha256:93c846f7524e0e00f72a38fcaa2ed7ad0c6d5d3b92a62ca0338d58866cd8cc15"},
+ {file = "robotcode_runner-2.1.0-py3-none-any.whl", hash = "sha256:02287c14e15d4e8b38409522eabf0c67dcc004b83aac7d8e8f55125eec78297c"},
+ {file = "robotcode_runner-2.1.0.tar.gz", hash = "sha256:c22bbb1ec842d741673332e87777c2458975324d3960eb86df9cb7a498952f72"},
]
[package.dependencies]
-robotcode = "2.0.4"
-robotcode-modifiers = "2.0.4"
-robotcode-plugin = "2.0.4"
-robotcode-robot = "2.0.4"
-robotframework = ">=4.1.0"
+robotcode = "*"
+robotcode-modifiers = "*"
+robotcode-plugin = "*"
+robotcode-robot = "*"
+robotframework = ">=5.0.0"
[[package]]
name = "robotframework"
-version = "7.3.2"
+version = "7.4.1"
description = "Generic automation framework for acceptance testing and robotic process automation (RPA)"
optional = false
python-versions = ">=3.8"
groups = ["main", "dev", "lint-and-format", "type-checking"]
files = [
- {file = "robotframework-7.3.2-py3-none-any.whl", hash = "sha256:14ef2afa905285cc073df6ce06d0cd3af4a113df6f815532718079e00c98cca4"},
- {file = "robotframework-7.3.2.tar.gz", hash = "sha256:3bb3e299831ecb1664f3d5082f6ff9f08ba82d61a745bef2227328ef3049e93a"},
+ {file = "robotframework-7.4.1-py3-none-any.whl", hash = "sha256:3f75cae8a24797f1953efdb51399569767d742bb31f549ee9856db5545062728"},
+ {file = "robotframework-7.4.1.tar.gz", hash = "sha256:d645487248a86db1e1a865ce792502792edf5342873f4e4f35d333219cd246c5"},
]
[[package]]
@@ -1778,14 +1852,14 @@ xls = ["openpyxl", "pandas", "xlrd (>=1.2.0)"]
[[package]]
name = "robotframework-robocop"
-version = "6.12.0"
+version = "7.0.0"
description = "Static code analysis tool (linter) and code formatter for Robot Framework"
optional = false
python-versions = ">=3.9"
groups = ["lint-and-format"]
files = [
- {file = "robotframework_robocop-6.12.0-py3-none-any.whl", hash = "sha256:ee1146ff4fccf3bd01f98a7965947ec878fa6bb794dec137e49948726eb116e0"},
- {file = "robotframework_robocop-6.12.0.tar.gz", hash = "sha256:b49a9677f5da514c40bb334a2cc97badab09754e5c09ee31df1e9b8c5d595852"},
+ {file = "robotframework_robocop-7.0.0-py3-none-any.whl", hash = "sha256:966cc703183fc562c6f8b75a3342cd005bd0a54da0e4172fab8ee3e3f70d34c2"},
+ {file = "robotframework_robocop-7.0.0.tar.gz", hash = "sha256:1533c613c69f9e264b051da473aab018fa03f509ad51bd441b54ca5cd7a10fea"},
]
[package.dependencies]
@@ -1816,127 +1890,127 @@ robotframework = ">=3.2"
[[package]]
name = "rpds-py"
-version = "0.29.0"
+version = "0.30.0"
description = "Python bindings to Rust's persistent data structures (rpds)"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "rpds_py-0.29.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4ae4b88c6617e1b9e5038ab3fccd7bac0842fdda2b703117b2aa99bc85379113"},
- {file = "rpds_py-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7d9128ec9d8cecda6f044001fde4fb71ea7c24325336612ef8179091eb9596b9"},
- {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37812c3da8e06f2bb35b3cf10e4a7b68e776a706c13058997238762b4e07f4f"},
- {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66786c3fb1d8de416a7fa8e1cb1ec6ba0a745b2b0eee42f9b7daa26f1a495545"},
- {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58f5c77f1af888b5fd1876c9a0d9858f6f88a39c9dd7c073a88e57e577da66d"},
- {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:799156ef1f3529ed82c36eb012b5d7a4cf4b6ef556dd7cc192148991d07206ae"},
- {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:453783477aa4f2d9104c4b59b08c871431647cb7af51b549bbf2d9eb9c827756"},
- {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:24a7231493e3c4a4b30138b50cca089a598e52c34cf60b2f35cebf62f274fdea"},
- {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7033c1010b1f57bb44d8067e8c25aa6fa2e944dbf46ccc8c92b25043839c3fd2"},
- {file = "rpds_py-0.29.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0248b19405422573621172ab8e3a1f29141362d13d9f72bafa2e28ea0cdca5a2"},
- {file = "rpds_py-0.29.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f9f436aee28d13b9ad2c764fc273e0457e37c2e61529a07b928346b219fcde3b"},
- {file = "rpds_py-0.29.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24a16cb7163933906c62c272de20ea3c228e4542c8c45c1d7dc2b9913e17369a"},
- {file = "rpds_py-0.29.0-cp310-cp310-win32.whl", hash = "sha256:1a409b0310a566bfd1be82119891fefbdce615ccc8aa558aff7835c27988cbef"},
- {file = "rpds_py-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5523b0009e7c3c1263471b69d8da1c7d41b3ecb4cb62ef72be206b92040a950"},
- {file = "rpds_py-0.29.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9b9c764a11fd637e0322a488560533112837f5334ffeb48b1be20f6d98a7b437"},
- {file = "rpds_py-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fd2164d73812026ce970d44c3ebd51e019d2a26a4425a5dcbdfa93a34abc383"},
- {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a097b7f7f7274164566ae90a221fd725363c0e9d243e2e9ed43d195ccc5495c"},
- {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cdc0490374e31cedefefaa1520d5fe38e82fde8748cbc926e7284574c714d6b"},
- {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89ca2e673ddd5bde9b386da9a0aac0cab0e76f40c8f0aaf0d6311b6bbf2aa311"},
- {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5d9da3ff5af1ca1249b1adb8ef0573b94c76e6ae880ba1852f033bf429d4588"},
- {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8238d1d310283e87376c12f658b61e1ee23a14c0e54c7c0ce953efdbdc72deed"},
- {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2d6fb2ad1c36f91c4646989811e84b1ea5e0c3cf9690b826b6e32b7965853a63"},
- {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:534dc9df211387547267ccdb42253aa30527482acb38dd9b21c5c115d66a96d2"},
- {file = "rpds_py-0.29.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d456e64724a075441e4ed648d7f154dc62e9aabff29bcdf723d0c00e9e1d352f"},
- {file = "rpds_py-0.29.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a738f2da2f565989401bd6fd0b15990a4d1523c6d7fe83f300b7e7d17212feca"},
- {file = "rpds_py-0.29.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a110e14508fd26fd2e472bb541f37c209409876ba601cf57e739e87d8a53cf95"},
- {file = "rpds_py-0.29.0-cp311-cp311-win32.whl", hash = "sha256:923248a56dd8d158389a28934f6f69ebf89f218ef96a6b216a9be6861804d3f4"},
- {file = "rpds_py-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:539eb77eb043afcc45314d1be09ea6d6cafb3addc73e0547c171c6d636957f60"},
- {file = "rpds_py-0.29.0-cp311-cp311-win_arm64.whl", hash = "sha256:bdb67151ea81fcf02d8f494703fb728d4d34d24556cbff5f417d74f6f5792e7c"},
- {file = "rpds_py-0.29.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0891cfd8db43e085c0ab93ab7e9b0c8fee84780d436d3b266b113e51e79f954"},
- {file = "rpds_py-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3897924d3f9a0361472d884051f9a2460358f9a45b1d85a39a158d2f8f1ad71c"},
- {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21deb8e0d1571508c6491ce5ea5e25669b1dd4adf1c9d64b6314842f708b5d"},
- {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9efe71687d6427737a0a2de9ca1c0a216510e6cd08925c44162be23ed7bed2d5"},
- {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:40f65470919dc189c833e86b2c4bd21bd355f98436a2cef9e0a9a92aebc8e57e"},
- {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:def48ff59f181130f1a2cb7c517d16328efac3ec03951cca40c1dc2049747e83"},
- {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7bd570be92695d89285a4b373006930715b78d96449f686af422debb4d3949"},
- {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:5a572911cd053137bbff8e3a52d31c5d2dba51d3a67ad902629c70185f3f2181"},
- {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d583d4403bcbf10cffc3ab5cee23d7643fcc960dff85973fd3c2d6c86e8dbb0c"},
- {file = "rpds_py-0.29.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:070befbb868f257d24c3bb350dbd6e2f645e83731f31264b19d7231dd5c396c7"},
- {file = "rpds_py-0.29.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fc935f6b20b0c9f919a8ff024739174522abd331978f750a74bb68abd117bd19"},
- {file = "rpds_py-0.29.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c5a8ecaa44ce2d8d9d20a68a2483a74c07f05d72e94a4dff88906c8807e77b0"},
- {file = "rpds_py-0.29.0-cp312-cp312-win32.whl", hash = "sha256:ba5e1aeaf8dd6d8f6caba1f5539cddda87d511331714b7b5fc908b6cfc3636b7"},
- {file = "rpds_py-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:b5f6134faf54b3cb83375db0f113506f8b7770785be1f95a631e7e2892101977"},
- {file = "rpds_py-0.29.0-cp312-cp312-win_arm64.whl", hash = "sha256:b016eddf00dca7944721bf0cd85b6af7f6c4efaf83ee0b37c4133bd39757a8c7"},
- {file = "rpds_py-0.29.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1585648d0760b88292eecab5181f5651111a69d90eff35d6b78aa32998886a61"},
- {file = "rpds_py-0.29.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:521807963971a23996ddaf764c682b3e46459b3c58ccd79fefbe16718db43154"},
- {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8896986efaa243ab713c69e6491a4138410f0fe36f2f4c71e18bd5501e8014"},
- {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d24564a700ef41480a984c5ebed62b74e6ce5860429b98b1fede76049e953e6"},
- {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6596b93c010d386ae46c9fba9bfc9fc5965fa8228edeac51576299182c2e31c"},
- {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5cc58aac218826d054c7da7f95821eba94125d88be673ff44267bb89d12a5866"},
- {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de73e40ebc04dd5d9556f50180395322193a78ec247e637e741c1b954810f295"},
- {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:295ce5ac7f0cf69a651ea75c8f76d02a31f98e5698e82a50a5f4d4982fbbae3b"},
- {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ea59b23ea931d494459c8338056fe7d93458c0bf3ecc061cd03916505369d55"},
- {file = "rpds_py-0.29.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f49d41559cebd608042fdcf54ba597a4a7555b49ad5c1c0c03e0af82692661cd"},
- {file = "rpds_py-0.29.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:05a2bd42768ea988294ca328206efbcc66e220d2d9b7836ee5712c07ad6340ea"},
- {file = "rpds_py-0.29.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33ca7bdfedd83339ca55da3a5e1527ee5870d4b8369456b5777b197756f3ca22"},
- {file = "rpds_py-0.29.0-cp313-cp313-win32.whl", hash = "sha256:20c51ae86a0bb9accc9ad4e6cdeec58d5ebb7f1b09dd4466331fc65e1766aae7"},
- {file = "rpds_py-0.29.0-cp313-cp313-win_amd64.whl", hash = "sha256:6410e66f02803600edb0b1889541f4b5cc298a5ccda0ad789cc50ef23b54813e"},
- {file = "rpds_py-0.29.0-cp313-cp313-win_arm64.whl", hash = "sha256:56838e1cd9174dc23c5691ee29f1d1be9eab357f27efef6bded1328b23e1ced2"},
- {file = "rpds_py-0.29.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:37d94eadf764d16b9a04307f2ab1d7af6dc28774bbe0535c9323101e14877b4c"},
- {file = "rpds_py-0.29.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d472cf73efe5726a067dce63eebe8215b14beabea7c12606fd9994267b3cfe2b"},
- {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72fdfd5ff8992e4636621826371e3ac5f3e3b8323e9d0e48378e9c13c3dac9d0"},
- {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2549d833abdf8275c901313b9e8ff8fba57e50f6a495035a2a4e30621a2f7cc4"},
- {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4448dad428f28a6a767c3e3b80cde3446a22a0efbddaa2360f4bb4dc836d0688"},
- {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:115f48170fd4296a33938d8c11f697f5f26e0472e43d28f35624764173a60e4d"},
- {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e5bb73ffc029820f4348e9b66b3027493ae00bca6629129cd433fd7a76308ee"},
- {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:b1581fcde18fcdf42ea2403a16a6b646f8eb1e58d7f90a0ce693da441f76942e"},
- {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16e9da2bda9eb17ea318b4c335ec9ac1818e88922cbe03a5743ea0da9ecf74fb"},
- {file = "rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:28fd300326dd21198f311534bdb6d7e989dd09b3418b3a91d54a0f384c700967"},
- {file = "rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2aba991e041d031c7939e1358f583ae405a7bf04804ca806b97a5c0e0af1ea5e"},
- {file = "rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f437026dbbc3f08c99cc41a5b2570c6e1a1ddbe48ab19a9b814254128d4ea7a"},
- {file = "rpds_py-0.29.0-cp313-cp313t-win32.whl", hash = "sha256:6e97846e9800a5d0fe7be4d008f0c93d0feeb2700da7b1f7528dabafb31dfadb"},
- {file = "rpds_py-0.29.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f49196aec7c4b406495f60e6f947ad71f317a765f956d74bbd83996b9edc0352"},
- {file = "rpds_py-0.29.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:394d27e4453d3b4d82bb85665dc1fcf4b0badc30fc84282defed71643b50e1a1"},
- {file = "rpds_py-0.29.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55d827b2ae95425d3be9bc9a5838b6c29d664924f98146557f7715e331d06df8"},
- {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc31a07ed352e5462d3ee1b22e89285f4ce97d5266f6d1169da1142e78045626"},
- {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c4695dd224212f6105db7ea62197144230b808d6b2bba52238906a2762f1d1e7"},
- {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcae1770b401167f8b9e1e3f566562e6966ffa9ce63639916248a9e25fa8a244"},
- {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:90f30d15f45048448b8da21c41703b31c61119c06c216a1bf8c245812a0f0c17"},
- {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44a91e0ab77bdc0004b43261a4b8cd6d6b451e8d443754cfda830002b5745b32"},
- {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:4aa195e5804d32c682e453b34474f411ca108e4291c6a0f824ebdc30a91c973c"},
- {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7971bdb7bf4ee0f7e6f67fa4c7fbc6019d9850cc977d126904392d363f6f8318"},
- {file = "rpds_py-0.29.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8ae33ad9ce580c7a47452c3b3f7d8a9095ef6208e0a0c7e4e2384f9fc5bf8212"},
- {file = "rpds_py-0.29.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c661132ab2fb4eeede2ef69670fd60da5235209874d001a98f1542f31f2a8a94"},
- {file = "rpds_py-0.29.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bb78b3a0d31ac1bde132c67015a809948db751cb4e92cdb3f0b242e430b6ed0d"},
- {file = "rpds_py-0.29.0-cp314-cp314-win32.whl", hash = "sha256:f475f103488312e9bd4000bc890a95955a07b2d0b6e8884aef4be56132adbbf1"},
- {file = "rpds_py-0.29.0-cp314-cp314-win_amd64.whl", hash = "sha256:b9cf2359a4fca87cfb6801fae83a76aedf66ee1254a7a151f1341632acf67f1b"},
- {file = "rpds_py-0.29.0-cp314-cp314-win_arm64.whl", hash = "sha256:9ba8028597e824854f0f1733d8b964e914ae3003b22a10c2c664cb6927e0feb9"},
- {file = "rpds_py-0.29.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:e71136fd0612556b35c575dc2726ae04a1669e6a6c378f2240312cf5d1a2ab10"},
- {file = "rpds_py-0.29.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:76fe96632d53f3bf0ea31ede2f53bbe3540cc2736d4aec3b3801b0458499ef3a"},
- {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9459a33f077130dbb2c7c3cea72ee9932271fb3126404ba2a2661e4fe9eb7b79"},
- {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5c9546cfdd5d45e562cc0444b6dddc191e625c62e866bf567a2c69487c7ad28a"},
- {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12597d11d97b8f7e376c88929a6e17acb980e234547c92992f9f7c058f1a7310"},
- {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28de03cf48b8a9e6ec10318f2197b83946ed91e2891f651a109611be4106ac4b"},
- {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd7951c964069039acc9d67a8ff1f0a7f34845ae180ca542b17dc1456b1f1808"},
- {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:c07d107b7316088f1ac0177a7661ca0c6670d443f6fe72e836069025e6266761"},
- {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1de2345af363d25696969befc0c1688a6cb5e8b1d32b515ef84fc245c6cddba3"},
- {file = "rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:00e56b12d2199ca96068057e1ae7f9998ab6e99cda82431afafd32f3ec98cca9"},
- {file = "rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3919a3bbecee589300ed25000b6944174e07cd20db70552159207b3f4bbb45b8"},
- {file = "rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7fa2ccc312bbd91e43aa5e0869e46bc03278a3dddb8d58833150a18b0f0283a"},
- {file = "rpds_py-0.29.0-cp314-cp314t-win32.whl", hash = "sha256:97c817863ffc397f1e6a6e9d2d89fe5408c0a9922dac0329672fb0f35c867ea5"},
- {file = "rpds_py-0.29.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2023473f444752f0f82a58dfcbee040d0a1b3d1b3c2ec40e884bd25db6d117d2"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:acd82a9e39082dc5f4492d15a6b6c8599aa21db5c35aaf7d6889aea16502c07d"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:715b67eac317bf1c7657508170a3e011a1ea6ccb1c9d5f296e20ba14196be6b3"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3b1b87a237cb2dba4db18bcfaaa44ba4cd5936b91121b62292ff21df577fc43"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c3c3e8101bb06e337c88eb0c0ede3187131f19d97d43ea0e1c5407ea74c0cbf"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8e54d6e61f3ecd3abe032065ce83ea63417a24f437e4a3d73d2f85ce7b7cfe"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fbd4e9aebf110473a420dea85a238b254cf8a15acb04b22a5a6b5ce8925b760"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80fdf53d36e6c72819993e35d1ebeeb8e8fc688d0c6c2b391b55e335b3afba5a"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:ea7173df5d86f625f8dde6d5929629ad811ed8decda3b60ae603903839ac9ac0"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:76054d540061eda273274f3d13a21a4abdde90e13eaefdc205db37c05230efce"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:9f84c549746a5be3bc7415830747a3a0312573afc9f95785eb35228bb17742ec"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:0ea962671af5cb9a260489e311fa22b2e97103e3f9f0caaea6f81390af96a9ed"},
- {file = "rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:f7728653900035fb7b8d06e1e5900545d8088efc9d5d4545782da7df03ec803f"},
- {file = "rpds_py-0.29.0.tar.gz", hash = "sha256:fe55fe686908f50154d1dc599232016e50c243b438c3b7432f24e2895b0e5359"},
+ {file = "rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288"},
+ {file = "rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00"},
+ {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6"},
+ {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7"},
+ {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324"},
+ {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df"},
+ {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3"},
+ {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221"},
+ {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7"},
+ {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff"},
+ {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7"},
+ {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139"},
+ {file = "rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464"},
+ {file = "rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169"},
+ {file = "rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425"},
+ {file = "rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d"},
+ {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4"},
+ {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f"},
+ {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4"},
+ {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97"},
+ {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89"},
+ {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d"},
+ {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038"},
+ {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7"},
+ {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed"},
+ {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85"},
+ {file = "rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c"},
+ {file = "rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825"},
+ {file = "rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229"},
+ {file = "rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad"},
+ {file = "rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05"},
+ {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28"},
+ {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd"},
+ {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f"},
+ {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1"},
+ {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23"},
+ {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6"},
+ {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51"},
+ {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5"},
+ {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e"},
+ {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394"},
+ {file = "rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf"},
+ {file = "rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b"},
+ {file = "rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e"},
+ {file = "rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2"},
+ {file = "rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8"},
+ {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4"},
+ {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136"},
+ {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7"},
+ {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2"},
+ {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6"},
+ {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e"},
+ {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d"},
+ {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7"},
+ {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31"},
+ {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95"},
+ {file = "rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d"},
+ {file = "rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15"},
+ {file = "rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6"},
+ {file = "rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d"},
+ {file = "rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0"},
+ {file = "rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be"},
+ {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f"},
+ {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f"},
+ {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87"},
+ {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18"},
+ {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad"},
+ {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07"},
+ {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f"},
+ {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65"},
+ {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f"},
+ {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53"},
+ {file = "rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed"},
+ {file = "rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950"},
+ {file = "rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0"},
+ {file = "rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4"},
+ {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e"},
+ {file = "rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84"},
]
[[package]]
@@ -1953,18 +2027,18 @@ files = [
[[package]]
name = "ruamel-yaml"
-version = "0.18.16"
+version = "0.18.17"
description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "ruamel.yaml-0.18.16-py3-none-any.whl", hash = "sha256:048f26d64245bae57a4f9ef6feb5b552a386830ef7a826f235ffb804c59efbba"},
- {file = "ruamel.yaml-0.18.16.tar.gz", hash = "sha256:a6e587512f3c998b2225d68aa1f35111c29fad14aed561a26e73fab729ec5e5a"},
+ {file = "ruamel_yaml-0.18.17-py3-none-any.whl", hash = "sha256:9c8ba9eb3e793efdf924b60d521820869d5bf0cb9c6f1b82d82de8295e290b9d"},
+ {file = "ruamel_yaml-0.18.17.tar.gz", hash = "sha256:9091cd6e2d93a3a4b157ddb8fabf348c3de7f1fb1381346d985b6b247dcd8d3c"},
]
[package.dependencies]
-"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.14\""}
+"ruamel.yaml.clib" = {version = ">=0.2.15", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.15\""}
[package.extras]
docs = ["mercurial (>5.7)", "ryd"]
@@ -1977,7 +2051,7 @@ description = "C version of reader, parser and emitter for ruamel.yaml derived f
optional = false
python-versions = ">=3.9"
groups = ["main"]
-markers = "platform_python_implementation == \"CPython\" and python_version < \"3.14\""
+markers = "platform_python_implementation == \"CPython\" and python_version < \"3.15\""
files = [
{file = "ruamel_yaml_clib-0.2.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88eea8baf72f0ccf232c22124d122a7f26e8a24110a0273d9bcddcb0f7e1fa03"},
{file = "ruamel_yaml_clib-0.2.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b6f7d74d094d1f3a4e157278da97752f16ee230080ae331fcc219056ca54f77"},
@@ -2044,31 +2118,31 @@ files = [
[[package]]
name = "ruff"
-version = "0.14.6"
+version = "0.14.10"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
groups = ["lint-and-format"]
files = [
- {file = "ruff-0.14.6-py3-none-linux_armv6l.whl", hash = "sha256:d724ac2f1c240dbd01a2ae98db5d1d9a5e1d9e96eba999d1c48e30062df578a3"},
- {file = "ruff-0.14.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9f7539ea257aa4d07b7ce87aed580e485c40143f2473ff2f2b75aee003186004"},
- {file = "ruff-0.14.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f6007e55b90a2a7e93083ba48a9f23c3158c433591c33ee2e99a49b889c6332"},
- {file = "ruff-0.14.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8e7b9d73d8728b68f632aa8e824ef041d068d231d8dbc7808532d3629a6bef"},
- {file = "ruff-0.14.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d50d45d4553a3ebcbd33e7c5e0fe6ca4aafd9a9122492de357205c2c48f00775"},
- {file = "ruff-0.14.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:118548dd121f8a21bfa8ab2c5b80e5b4aed67ead4b7567790962554f38e598ce"},
- {file = "ruff-0.14.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57256efafbfefcb8748df9d1d766062f62b20150691021f8ab79e2d919f7c11f"},
- {file = "ruff-0.14.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff18134841e5c68f8e5df1999a64429a02d5549036b394fafbe410f886e1989d"},
- {file = "ruff-0.14.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c4b7ec1e66a105d5c27bd57fa93203637d66a26d10ca9809dc7fc18ec58440"},
- {file = "ruff-0.14.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167843a6f78680746d7e226f255d920aeed5e4ad9c03258094a2d49d3028b105"},
- {file = "ruff-0.14.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:16a33af621c9c523b1ae006b1b99b159bf5ac7e4b1f20b85b2572455018e0821"},
- {file = "ruff-0.14.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1432ab6e1ae2dc565a7eea707d3b03a0c234ef401482a6f1621bc1f427c2ff55"},
- {file = "ruff-0.14.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c55cfbbe7abb61eb914bfd20683d14cdfb38a6d56c6c66efa55ec6570ee4e71"},
- {file = "ruff-0.14.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:efea3c0f21901a685fff4befda6d61a1bf4cb43de16da87e8226a281d614350b"},
- {file = "ruff-0.14.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:344d97172576d75dc6afc0e9243376dbe1668559c72de1864439c4fc95f78185"},
- {file = "ruff-0.14.6-py3-none-win32.whl", hash = "sha256:00169c0c8b85396516fdd9ce3446c7ca20c2a8f90a77aa945ba6b8f2bfe99e85"},
- {file = "ruff-0.14.6-py3-none-win_amd64.whl", hash = "sha256:390e6480c5e3659f8a4c8d6a0373027820419ac14fa0d2713bd8e6c3e125b8b9"},
- {file = "ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2"},
- {file = "ruff-0.14.6.tar.gz", hash = "sha256:6f0c742ca6a7783a736b867a263b9a7a80a45ce9bee391eeda296895f1b4e1cc"},
+ {file = "ruff-0.14.10-py3-none-linux_armv6l.whl", hash = "sha256:7a3ce585f2ade3e1f29ec1b92df13e3da262178df8c8bdf876f48fa0e8316c49"},
+ {file = "ruff-0.14.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:674f9be9372907f7257c51f1d4fc902cb7cf014b9980152b802794317941f08f"},
+ {file = "ruff-0.14.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d85713d522348837ef9df8efca33ccb8bd6fcfc86a2cde3ccb4bc9d28a18003d"},
+ {file = "ruff-0.14.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6987ebe0501ae4f4308d7d24e2d0fe3d7a98430f5adfd0f1fead050a740a3a77"},
+ {file = "ruff-0.14.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16a01dfb7b9e4eee556fbfd5392806b1b8550c9b4a9f6acd3dbe6812b193c70a"},
+ {file = "ruff-0.14.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7165d31a925b7a294465fa81be8c12a0e9b60fb02bf177e79067c867e71f8b1f"},
+ {file = "ruff-0.14.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c561695675b972effb0c0a45db233f2c816ff3da8dcfbe7dfc7eed625f218935"},
+ {file = "ruff-0.14.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bb98fcbbc61725968893682fd4df8966a34611239c9fd07a1f6a07e7103d08e"},
+ {file = "ruff-0.14.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f24b47993a9d8cb858429e97bdf8544c78029f09b520af615c1d261bf827001d"},
+ {file = "ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59aabd2e2c4fd614d2862e7939c34a532c04f1084476d6833dddef4afab87e9f"},
+ {file = "ruff-0.14.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:213db2b2e44be8625002dbea33bb9c60c66ea2c07c084a00d55732689d697a7f"},
+ {file = "ruff-0.14.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b914c40ab64865a17a9a5b67911d14df72346a634527240039eb3bd650e5979d"},
+ {file = "ruff-0.14.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1484983559f026788e3a5c07c81ef7d1e97c1c78ed03041a18f75df104c45405"},
+ {file = "ruff-0.14.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c70427132db492d25f982fffc8d6c7535cc2fd2c83fc8888f05caaa248521e60"},
+ {file = "ruff-0.14.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5bcf45b681e9f1ee6445d317ce1fa9d6cba9a6049542d1c3d5b5958986be8830"},
+ {file = "ruff-0.14.10-py3-none-win32.whl", hash = "sha256:104c49fc7ab73f3f3a758039adea978869a918f31b73280db175b43a2d9b51d6"},
+ {file = "ruff-0.14.10-py3-none-win_amd64.whl", hash = "sha256:466297bd73638c6bdf06485683e812db1c00c7ac96d4ddd0294a338c62fdc154"},
+ {file = "ruff-0.14.10-py3-none-win_arm64.whl", hash = "sha256:e51d046cf6dda98a4633b8a8a771451107413b0f07183b2bef03f075599e44e6"},
+ {file = "ruff-0.14.10.tar.gz", hash = "sha256:9a2e830f075d1a42cd28420d7809ace390832a490ed0966fe373ba288e77aaf4"},
]
[[package]]
@@ -2104,18 +2178,6 @@ files = [
{file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
]
-[[package]]
-name = "sniffio"
-version = "1.3.1"
-description = "Sniff out which async library your code is running under"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-files = [
- {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
- {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
-]
-
[[package]]
name = "starlette"
version = "0.50.0"
@@ -2204,14 +2266,14 @@ files = [
[[package]]
name = "typer-slim"
-version = "0.20.0"
+version = "0.20.1"
description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
optional = false
python-versions = ">=3.8"
groups = ["lint-and-format"]
files = [
- {file = "typer_slim-0.20.0-py3-none-any.whl", hash = "sha256:f42a9b7571a12b97dddf364745d29f12221865acef7a2680065f9bb29c7dc89d"},
- {file = "typer_slim-0.20.0.tar.gz", hash = "sha256:9fc6607b3c6c20f5c33ea9590cbeb17848667c51feee27d9e314a579ab07d1a3"},
+ {file = "typer_slim-0.20.1-py3-none-any.whl", hash = "sha256:8e89c5dbaffe87a4f86f4c7a9e2f7059b5b68c66f558f298969d42ce34f10122"},
+ {file = "typer_slim-0.20.1.tar.gz", hash = "sha256:bb9e4f7e6dc31551c8a201383df322b81b0ce37239a5ead302598a2ebb6f7c9c"},
]
[package.dependencies]
@@ -2277,44 +2339,44 @@ typing-extensions = ">=4.12.0"
[[package]]
name = "tzdata"
-version = "2025.2"
+version = "2025.3"
description = "Provider of IANA time zone data"
optional = false
python-versions = ">=2"
groups = ["main"]
files = [
- {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"},
- {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"},
+ {file = "tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1"},
+ {file = "tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7"},
]
[[package]]
name = "urllib3"
-version = "2.5.0"
+version = "2.6.2"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.9"
groups = ["main", "dev", "type-checking"]
files = [
- {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"},
- {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"},
+ {file = "urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd"},
+ {file = "urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797"},
]
[package.extras]
-brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""]
+brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""]
h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
-zstd = ["zstandard (>=0.18.0)"]
+zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""]
[[package]]
name = "uvicorn"
-version = "0.38.0"
+version = "0.40.0"
description = "The lightning-fast ASGI server."
optional = false
-python-versions = ">=3.9"
+python-versions = ">=3.10"
groups = ["dev"]
files = [
- {file = "uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02"},
- {file = "uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d"},
+ {file = "uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee"},
+ {file = "uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea"},
]
[package.dependencies]
@@ -2327,18 +2389,18 @@ standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.6.3)
[[package]]
name = "werkzeug"
-version = "3.1.1"
+version = "3.1.4"
description = "The comprehensive WSGI web application library."
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "werkzeug-3.1.1-py3-none-any.whl", hash = "sha256:a71124d1ef06008baafa3d266c02f56e1836a5984afd6dd6c9230669d60d9fb5"},
- {file = "werkzeug-3.1.1.tar.gz", hash = "sha256:8cd39dfbdfc1e051965f156163e2974e52c210f130810e9ad36858f0fd3edad4"},
+ {file = "werkzeug-3.1.4-py3-none-any.whl", hash = "sha256:2ad50fb9ed09cc3af22c54698351027ace879a0b60a3b5edf5730b2f7d876905"},
+ {file = "werkzeug-3.1.4.tar.gz", hash = "sha256:cd3cd98b1b92dc3b7b3995038826c68097dcb16f9baa63abe35f20eafeb9fe5e"},
]
[package.dependencies]
-MarkupSafe = ">=2.1.1"
+markupsafe = ">=2.1.1"
[package.extras]
watchdog = ["watchdog (>=2.3)"]