diff --git a/docs/integrations.rst b/docs/integrations.rst
index 5beb7f26..acb8aef8 100644
--- a/docs/integrations.rst
+++ b/docs/integrations.rst
@@ -3,6 +3,59 @@ Integrations
Openapi-core integrates with your popular libraries and frameworks. Each integration offers different levels of integration that help validate and unmarshal your request and response data.
+Amazon API Gateway
+------------------
+
+This section describes integration with `Amazon API Gateway `__.
+
+It is useful for:
+ * `AWS Lambda integrations `__ where Lambda functions handle events from API Gateway (Amazon API Gateway event format version 1.0 and 2.0).
+ * `AWS Lambda function URLs `__ where Lambda functions handle events from dedicated HTTP(S) endpoint (Amazon API Gateway event format version 2.0).
+
+Low level
+~~~~~~~~~
+
+You can use ``APIGatewayEventV2OpenAPIRequest`` as an API Gateway event (format version 2.0) request factory:
+
+.. code-block:: python
+
+ from openapi_core import unmarshal_request
+ from openapi_core.contrib.aws import APIGatewayEventV2OpenAPIRequest
+
+ openapi_request = APIGatewayEventV2OpenAPIRequest(event)
+ result = unmarshal_request(openapi_request, spec=spec)
+
+If you use format version 1.0, then import and use ``APIGatewayEventOpenAPIRequest`` as an API Gateway event (format version 1.0) request factory.
+
+You can use ``APIGatewayEventV2ResponseOpenAPIResponse`` as an API Gateway event (format version 2.0) response factory:
+
+.. code-block:: python
+
+ from openapi_core import unmarshal_response
+ from openapi_core.contrib.aws import APIGatewayEventV2ResponseOpenAPIResponse
+
+ openapi_response = APIGatewayEventV2ResponseOpenAPIResponse(response)
+ result = unmarshal_response(openapi_request, openapi_response, spec=spec)
+
+If you use format version 1.0, then import and use ``APIGatewayEventResponseOpenAPIResponse`` as an API Gateway event (format version 1.0) response factory.
+
+ANY method
+~~~~~~~~~~
+
+API Gateway have special ``ANY`` method that catches all HTTP methods. It's specified as `x-amazon-apigateway-any-method `__ OpenAPI extension. If you use the extension, you want to define ``path_finder_cls`` to be ``APIGatewayPathFinder``:
+
+.. code-block:: python
+
+ from openapi_core.contrib.aws import APIGatewayPathFinder
+
+ result = unmarshal_response(
+ openapi_request,
+ openapi_response,
+ spec=spec,
+ path_finder_cls=APIGatewayPathFinder,
+ )
+
+
Bottle
------
diff --git a/openapi_core/contrib/aws/__init__.py b/openapi_core/contrib/aws/__init__.py
new file mode 100644
index 00000000..ed398a23
--- /dev/null
+++ b/openapi_core/contrib/aws/__init__.py
@@ -0,0 +1,22 @@
+"""OpenAPI core contrib aws module"""
+from openapi_core.contrib.aws.decorators import (
+ APIGatewayEventV2OpenAPIHandleDecorator,
+)
+from openapi_core.contrib.aws.finders import APIGatewayPathFinder
+from openapi_core.contrib.aws.requests import APIGatewayEventOpenAPIRequest
+from openapi_core.contrib.aws.requests import APIGatewayEventV2OpenAPIRequest
+from openapi_core.contrib.aws.responses import (
+ APIGatewayEventResponseOpenAPIResponse,
+)
+from openapi_core.contrib.aws.responses import (
+ APIGatewayEventV2ResponseOpenAPIResponse,
+)
+
+__all__ = [
+ "APIGatewayEventOpenAPIRequest",
+ "APIGatewayEventResponseOpenAPIResponse",
+ "APIGatewayEventV2OpenAPIHandleDecorator",
+ "APIGatewayEventV2OpenAPIRequest",
+ "APIGatewayEventV2ResponseOpenAPIResponse",
+ "APIGatewayPathFinder",
+]
diff --git a/openapi_core/contrib/aws/datatypes.py b/openapi_core/contrib/aws/datatypes.py
new file mode 100644
index 00000000..772fbc52
--- /dev/null
+++ b/openapi_core/contrib/aws/datatypes.py
@@ -0,0 +1,74 @@
+from typing import Dict
+from typing import List
+from typing import Optional
+
+from pydantic import Field
+from pydantic.dataclasses import dataclass
+
+
+class APIGatewayEventConfig:
+ extra = "allow"
+
+
+@dataclass(config=APIGatewayEventConfig, frozen=True)
+class APIGatewayEvent:
+ """AWS API Gateway event"""
+
+ headers: Dict[str, str]
+
+ path: str
+ httpMethod: str
+ resource: str
+
+ queryStringParameters: Optional[Dict[str, str]] = None
+ isBase64Encoded: Optional[bool] = None
+ body: Optional[str] = None
+ pathParameters: Optional[Dict[str, str]] = None
+ stageVariables: Optional[Dict[str, str]] = None
+
+ multiValueHeaders: Optional[Dict[str, List[str]]] = None
+ version: Optional[str] = "1.0"
+ multiValueQueryStringParameters: Optional[Dict[str, List[str]]] = None
+
+
+@dataclass(config=APIGatewayEventConfig, frozen=True)
+class APIGatewayEventV2:
+ """AWS API Gateway event v2"""
+
+ headers: Dict[str, str]
+
+ version: str
+ routeKey: str
+ rawPath: str
+ rawQueryString: str
+
+ queryStringParameters: Optional[Dict[str, str]] = None
+ isBase64Encoded: Optional[bool] = None
+ body: Optional[str] = None
+ pathParameters: Optional[Dict[str, str]] = None
+ stageVariables: Optional[Dict[str, str]] = None
+
+ cookies: Optional[List[str]] = None
+
+
+@dataclass(config=APIGatewayEventConfig, frozen=True)
+class APIGatewayEventResponse:
+ """AWS API Gateway event response"""
+
+ body: str
+ isBase64Encoded: bool
+ statusCode: int
+ headers: Dict[str, str]
+ multiValueHeaders: Dict[str, List[str]]
+
+
+@dataclass(config=APIGatewayEventConfig, frozen=True)
+class APIGatewayEventV2Response:
+ """AWS API Gateway event v2 response"""
+
+ body: str
+ isBase64Encoded: bool = False
+ statusCode: int = 200
+ headers: Dict[str, str] = Field(
+ default_factory=lambda: {"content-type": "application/json"}
+ )
diff --git a/openapi_core/contrib/aws/finders.py b/openapi_core/contrib/aws/finders.py
new file mode 100644
index 00000000..15dda3eb
--- /dev/null
+++ b/openapi_core/contrib/aws/finders.py
@@ -0,0 +1,18 @@
+from openapi_core.templating.paths.finders import APICallPathFinder
+from openapi_core.templating.paths.iterators import (
+ CatchAllMethodOperationsIterator,
+)
+
+
+class APIGatewayPathFinder(APICallPathFinder):
+ operations_iterator = CatchAllMethodOperationsIterator(
+ "any",
+ "x-amazon-apigateway-any-method",
+ )
+
+
+class APIGatewayIntegrationPathFinder(APICallPathFinder):
+ operations_iterator = CatchAllMethodOperationsIterator(
+ "any",
+ "x-amazon-apigateway-any-method",
+ )
diff --git a/openapi_core/contrib/aws/requests.py b/openapi_core/contrib/aws/requests.py
new file mode 100644
index 00000000..8b7400be
--- /dev/null
+++ b/openapi_core/contrib/aws/requests.py
@@ -0,0 +1,151 @@
+from typing import Dict
+from typing import Optional
+
+from werkzeug.datastructures import Headers
+from werkzeug.datastructures import ImmutableMultiDict
+
+from openapi_core.contrib.aws.datatypes import APIGatewayEvent
+from openapi_core.contrib.aws.datatypes import APIGatewayEventV2
+from openapi_core.contrib.aws.types import APIGatewayEventPayload
+from openapi_core.datatypes import RequestParameters
+
+
+class APIGatewayEventOpenAPIRequest:
+ """
+ Converts an API Gateway event payload to an OpenAPI request.
+
+ Designed to be used with API Gateway REST API specification exports for
+ integrations that use event v1 payload. Uses API Gateway event v1 httpMethod
+ and path data. Requires APIGatewayPathFinder to resolve ANY methods.
+ """
+
+ def __init__(self, payload: APIGatewayEventPayload):
+ self.event = APIGatewayEvent(**payload)
+
+ self.parameters = RequestParameters(
+ path=self.path_params,
+ query=ImmutableMultiDict(self.query_params),
+ header=Headers(self.event.headers),
+ cookie=ImmutableMultiDict(),
+ )
+
+ @property
+ def path_params(self) -> Dict[str, str]:
+ params = self.event.pathParameters
+ if params is None:
+ return {}
+ return params
+
+ @property
+ def query_params(self) -> Dict[str, str]:
+ params = self.event.queryStringParameters
+ if params is None:
+ return {}
+ return params
+
+ @property
+ def proto(self) -> str:
+ return self.event.headers.get("X-Forwarded-Proto", "https")
+
+ @property
+ def host(self) -> str:
+ return self.event.headers["Host"]
+
+ @property
+ def host_url(self) -> str:
+ return "://".join([self.proto, self.host])
+
+ @property
+ def path(self) -> str:
+ return self.event.path
+
+ @property
+ def method(self) -> str:
+ return self.event.httpMethod.lower()
+
+ @property
+ def body(self) -> Optional[str]:
+ return self.event.body
+
+ @property
+ def mimetype(self) -> str:
+ return self.event.headers.get("Content-Type", "")
+
+
+class APIGatewayEventV2OpenAPIRequest:
+ """
+ Converts an API Gateway event v2 payload to an OpenAPI request.
+
+ Designed to be used with API Gateway HTTP API specification exports for
+ integrations that use event v2 payload. Uses API Gateway event v2 routeKey
+ and rawPath data. Requires APIGatewayPathFinder to resolve ANY methods.
+
+ .. note::
+ API Gateway HTTP APIs don't support request validation
+ """
+
+ def __init__(self, payload: APIGatewayEventPayload):
+ self.event = APIGatewayEventV2(**payload)
+
+ self.parameters = RequestParameters(
+ path=self.path_params,
+ query=ImmutableMultiDict(self.query_params),
+ header=Headers(self.event.headers),
+ cookie=ImmutableMultiDict(),
+ )
+
+ @property
+ def path_params(self) -> Dict[str, str]:
+ if self.event.pathParameters is None:
+ return {}
+ return self.event.pathParameters
+
+ @property
+ def query_params(self) -> Dict[str, str]:
+ if self.event.queryStringParameters is None:
+ return {}
+ return self.event.queryStringParameters
+
+ @property
+ def proto(self) -> str:
+ return self.event.headers.get("x-forwarded-proto", "https")
+
+ @property
+ def host(self) -> str:
+ return self.event.headers["host"]
+
+ @property
+ def host_url(self) -> str:
+ return "://".join([self.proto, self.host])
+
+ @property
+ def path(self) -> str:
+ return self.event.rawPath
+
+ @property
+ def method(self) -> str:
+ return self.event.routeKey.split(" ")[0].lower()
+
+ @property
+ def body(self) -> Optional[str]:
+ return self.event.body
+
+ @property
+ def mimetype(self) -> str:
+ return self.event.headers.get("content-type", "")
+
+
+class APIGatewayEventV2HTTPOpenAPIRequest(APIGatewayEventV2OpenAPIRequest):
+ """
+ Converts an API Gateway event v2 payload to an OpenAPI request.
+
+ Uses http integration path and method data.
+ """
+
+ @property
+ def path(self) -> str:
+ return self.event.http.path
+
+ @property
+ def method(self) -> str:
+ return self.event.http.method.lower()
diff --git a/openapi_core/contrib/aws/responses.py b/openapi_core/contrib/aws/responses.py
new file mode 100644
index 00000000..0c19510b
--- /dev/null
+++ b/openapi_core/contrib/aws/responses.py
@@ -0,0 +1,83 @@
+from json import dumps
+from typing import Union
+
+from werkzeug.datastructures import Headers
+
+from openapi_core.contrib.aws.datatypes import APIGatewayEventResponse
+from openapi_core.contrib.aws.datatypes import APIGatewayEventV2Response
+from openapi_core.contrib.aws.types import APIGatewayEventResponsePayload
+
+APIGatewayEventV2ResponseType = Union[APIGatewayEventV2Response, dict, str]
+
+
+class APIGatewayEventResponseOpenAPIResponse:
+ """
+ Converts an API Gateway event response payload to an OpenAPI request
+ """
+
+ def __init__(self, payload: APIGatewayEventResponsePayload):
+ self.response = APIGatewayEventResponse(**payload)
+
+ @property
+ def data(self) -> str:
+ return self.response.body
+
+ @property
+ def status_code(self) -> int:
+ return self.response.statusCode
+
+ @property
+ def headers(self) -> Headers:
+ return Headers(self.response.headers)
+
+ @property
+ def mimetype(self) -> str:
+ content_type = self.response.headers.get("Content-Type", "")
+ assert isinstance(content_type, str)
+ return content_type
+
+
+class APIGatewayEventV2ResponseOpenAPIResponse:
+ """
+ Converts an API Gateway event v2 response payload to an OpenAPI request
+ """
+
+ def __init__(self, payload: Union[APIGatewayEventResponsePayload, str]):
+ if not isinstance(payload, dict):
+ payload = self._construct_payload(payload)
+ elif "statusCode" not in payload:
+ body = dumps(payload)
+ payload = self._construct_payload(body)
+
+ self.response = APIGatewayEventV2Response(**payload)
+
+ @staticmethod
+ def _construct_payload(body: str) -> APIGatewayEventResponsePayload:
+ return {
+ "isBase64Encoded": False,
+ "statusCode": 200,
+ "headers": {
+ "content-type": "application/json",
+ },
+ "body": body,
+ }
+
+ @property
+ def data(self) -> str:
+ return self.response.body
+
+ @property
+ def status_code(self) -> int:
+ return self.response.statusCode
+
+ @property
+ def headers(self) -> Headers:
+ return Headers(self.response.headers)
+
+ @property
+ def mimetype(self) -> str:
+ content_type = self.response.headers.get(
+ "content-type", "application/json"
+ )
+ assert isinstance(content_type, str)
+ return content_type
diff --git a/openapi_core/contrib/aws/types.py b/openapi_core/contrib/aws/types.py
new file mode 100644
index 00000000..aa5a6b2e
--- /dev/null
+++ b/openapi_core/contrib/aws/types.py
@@ -0,0 +1,5 @@
+from typing import Any
+from typing import Dict
+
+APIGatewayEventPayload = Dict[str, Any]
+APIGatewayEventResponsePayload = Dict[str, Any]
diff --git a/openapi_core/templating/paths/__init__.py b/openapi_core/templating/paths/__init__.py
index e69de29b..93e94f74 100644
--- a/openapi_core/templating/paths/__init__.py
+++ b/openapi_core/templating/paths/__init__.py
@@ -0,0 +1,7 @@
+from openapi_core.templating.paths.finders import APICallPathFinder
+from openapi_core.templating.paths.finders import WebhookPathFinder
+
+__all__ = [
+ "APICallPathFinder",
+ "WebhookPathFinder",
+]
diff --git a/openapi_core/templating/paths/finders.py b/openapi_core/templating/paths/finders.py
index e6f70841..74c0e5e0 100644
--- a/openapi_core/templating/paths/finders.py
+++ b/openapi_core/templating/paths/finders.py
@@ -17,32 +17,52 @@
from openapi_core.templating.paths.exceptions import PathNotFound
from openapi_core.templating.paths.exceptions import PathsNotFound
from openapi_core.templating.paths.exceptions import ServerNotFound
+from openapi_core.templating.paths.iterators import SimpleOperationsIterator
+from openapi_core.templating.paths.iterators import SimplePathsIterator
+from openapi_core.templating.paths.iterators import SimpleServersIterator
+from openapi_core.templating.paths.iterators import TemplatePathsIterator
+from openapi_core.templating.paths.iterators import TemplateServersIterator
+from openapi_core.templating.paths.protocols import OperationsIterator
+from openapi_core.templating.paths.protocols import PathsIterator
+from openapi_core.templating.paths.protocols import ServersIterator
from openapi_core.templating.paths.util import template_path_len
from openapi_core.templating.util import parse
from openapi_core.templating.util import search
-class BasePathFinder:
+class PathFinder:
+ paths_iterator: PathsIterator = NotImplemented
+ operations_iterator: OperationsIterator = NotImplemented
+ servers_iterator: ServersIterator = NotImplemented
+
def __init__(self, spec: Spec, base_url: Optional[str] = None):
self.spec = spec
self.base_url = base_url
def find(self, method: str, name: str) -> PathOperationServer:
- paths_iter = self._get_paths_iter(name)
+ paths_iter = self.paths_iterator(
+ name,
+ self.spec,
+ base_url=self.base_url,
+ )
paths_iter_peek = peekable(paths_iter)
if not paths_iter_peek:
raise PathNotFound(name)
- operations_iter = self._get_operations_iter(method, paths_iter_peek)
+ operations_iter = self.operations_iterator(
+ method,
+ paths_iter_peek,
+ self.spec,
+ base_url=self.base_url,
+ )
operations_iter_peek = peekable(operations_iter)
if not operations_iter_peek:
raise OperationNotFound(name, method)
- servers_iter = self._get_servers_iter(
- name,
- operations_iter_peek,
+ servers_iter = self.servers_iterator(
+ name, operations_iter_peek, self.spec, base_url=self.base_url
)
try:
@@ -50,115 +70,13 @@ def find(self, method: str, name: str) -> PathOperationServer:
except StopIteration:
raise ServerNotFound(name)
- def _get_paths_iter(self, name: str) -> Iterator[Path]:
- raise NotImplementedError
-
- def _get_operations_iter(
- self, method: str, paths_iter: Iterator[Path]
- ) -> Iterator[PathOperation]:
- for path, path_result in paths_iter:
- if method not in path:
- continue
- operation = path / method
- yield PathOperation(path, operation, path_result)
-
- def _get_servers_iter(
- self, name: str, operations_iter: Iterator[PathOperation]
- ) -> Iterator[PathOperationServer]:
- raise NotImplementedError
-
-
-class APICallPathFinder(BasePathFinder):
- def __init__(self, spec: Spec, base_url: Optional[str] = None):
- self.spec = spec
- self.base_url = base_url
-
- def _get_paths_iter(self, name: str) -> Iterator[Path]:
- paths = self.spec / "paths"
- if not paths.exists():
- raise PathsNotFound(paths.uri())
- template_paths: List[Path] = []
- for path_pattern, path in list(paths.items()):
- # simple path.
- # Return right away since it is always the most concrete
- if name.endswith(path_pattern):
- path_result = TemplateResult(path_pattern, {})
- yield Path(path, path_result)
- # template path
- else:
- result = search(path_pattern, name)
- if result:
- path_result = TemplateResult(path_pattern, result.named)
- template_paths.append(Path(path, path_result))
-
- # Fewer variables -> more concrete path
- yield from sorted(template_paths, key=template_path_len)
-
- def _get_servers_iter(
- self, name: str, operations_iter: Iterator[PathOperation]
- ) -> Iterator[PathOperationServer]:
- for path, operation, path_result in operations_iter:
- servers = (
- path.get("servers", None)
- or operation.get("servers", None)
- or self.spec.get("servers", [{"url": "/"}])
- )
- for server in servers:
- server_url_pattern = name.rsplit(path_result.resolved, 1)[0]
- server_url = server["url"]
- if not is_absolute(server_url):
- # relative to absolute url
- if self.base_url is not None:
- server_url = urljoin(self.base_url, server["url"])
- # if no base url check only path part
- else:
- server_url_pattern = urlparse(server_url_pattern).path
- if server_url.endswith("/"):
- server_url = server_url[:-1]
- # simple path
- if server_url_pattern == server_url:
- server_result = TemplateResult(server["url"], {})
- yield PathOperationServer(
- path,
- operation,
- server,
- path_result,
- server_result,
- )
- # template path
- else:
- result = parse(server["url"], server_url_pattern)
- if result:
- server_result = TemplateResult(
- server["url"], result.named
- )
- yield PathOperationServer(
- path,
- operation,
- server,
- path_result,
- server_result,
- )
+class APICallPathFinder(PathFinder):
+ paths_iterator: PathsIterator = TemplatePathsIterator("paths")
+ operations_iterator: OperationsIterator = SimpleOperationsIterator()
+ servers_iterator: ServersIterator = TemplateServersIterator()
-class WebhookPathFinder(BasePathFinder):
- def _get_paths_iter(self, name: str) -> Iterator[Path]:
- webhooks = self.spec / "webhooks"
- if not webhooks.exists():
- raise PathsNotFound(webhooks.uri())
- for webhook_name, path in list(webhooks.items()):
- if name == webhook_name:
- path_result = TemplateResult(webhook_name, {})
- yield Path(path, path_result)
- def _get_servers_iter(
- self, name: str, operations_iter: Iterator[PathOperation]
- ) -> Iterator[PathOperationServer]:
- for path, operation, path_result in operations_iter:
- yield PathOperationServer(
- path,
- operation,
- None,
- path_result,
- {},
- )
+class WebhookPathFinder(APICallPathFinder):
+ paths_iterator = SimplePathsIterator("webhooks")
+ servers_iterator = SimpleServersIterator()
diff --git a/openapi_core/templating/paths/iterators.py b/openapi_core/templating/paths/iterators.py
new file mode 100644
index 00000000..5e22145f
--- /dev/null
+++ b/openapi_core/templating/paths/iterators.py
@@ -0,0 +1,188 @@
+from itertools import tee
+from typing import Iterator
+from typing import List
+from typing import Optional
+from urllib.parse import urljoin
+from urllib.parse import urlparse
+
+from more_itertools import peekable
+
+from openapi_core.schema.servers import is_absolute
+from openapi_core.spec import Spec
+from openapi_core.templating.datatypes import TemplateResult
+from openapi_core.templating.paths.datatypes import Path
+from openapi_core.templating.paths.datatypes import PathOperation
+from openapi_core.templating.paths.datatypes import PathOperationServer
+from openapi_core.templating.paths.exceptions import OperationNotFound
+from openapi_core.templating.paths.exceptions import PathNotFound
+from openapi_core.templating.paths.exceptions import PathsNotFound
+from openapi_core.templating.paths.exceptions import ServerNotFound
+from openapi_core.templating.paths.util import template_path_len
+from openapi_core.templating.util import parse
+from openapi_core.templating.util import search
+
+
+class SimplePathsIterator:
+ def __init__(self, paths_part: str):
+ self.paths_part = paths_part
+
+ def __call__(
+ self, name: str, spec: Spec, base_url: Optional[str] = None
+ ) -> Iterator[Path]:
+ paths = spec / self.paths_part
+ if not paths.exists():
+ raise PathsNotFound(paths.uri())
+ for path_name, path in list(paths.items()):
+ if name == path_name:
+ path_result = TemplateResult(path_name, {})
+ yield Path(path, path_result)
+
+
+class TemplatePathsIterator:
+ def __init__(self, paths_part: str):
+ self.paths_part = paths_part
+
+ def __call__(
+ self, name: str, spec: Spec, base_url: Optional[str] = None
+ ) -> Iterator[Path]:
+ paths = spec / self.paths_part
+ if not paths.exists():
+ raise PathsNotFound(paths.uri())
+ template_paths: List[Path] = []
+ for path_pattern, path in list(paths.items()):
+ # simple path.
+ # Return right away since it is always the most concrete
+ if name.endswith(path_pattern):
+ path_result = TemplateResult(path_pattern, {})
+ yield Path(path, path_result)
+ # template path
+ else:
+ result = search(path_pattern, name)
+ if result:
+ path_result = TemplateResult(path_pattern, result.named)
+ template_paths.append(Path(path, path_result))
+
+ # Fewer variables -> more concrete path
+ yield from sorted(template_paths, key=template_path_len)
+
+
+class SimpleOperationsIterator:
+ def __call__(
+ self,
+ method: str,
+ paths_iter: Iterator[Path],
+ spec: Spec,
+ base_url: Optional[str] = None,
+ ) -> Iterator[PathOperation]:
+ for path, path_result in paths_iter:
+ if method not in path:
+ continue
+ operation = path / method
+ yield PathOperation(path, operation, path_result)
+
+
+class CatchAllMethodOperationsIterator(SimpleOperationsIterator):
+ def __init__(self, ca_method_name: str, ca_operation_name: str):
+ self.ca_method_name = ca_method_name
+ self.ca_operation_name = ca_operation_name
+
+ def __call__(
+ self,
+ method: str,
+ paths_iter: Iterator[Path],
+ spec: Spec,
+ base_url: Optional[str] = None,
+ ) -> Iterator[PathOperation]:
+ if method == self.ca_method_name:
+ yield from super().__call__(
+ self.ca_operation_name, paths_iter, spec, base_url=base_url
+ )
+ else:
+ yield from super().__call__(
+ method, paths_iter, spec, base_url=base_url
+ )
+
+
+class SimpleServersIterator:
+ def __call__(
+ self,
+ name: str,
+ operations_iter: Iterator[PathOperation],
+ spec: Spec,
+ base_url: Optional[str] = None,
+ ) -> Iterator[PathOperationServer]:
+ for path, operation, path_result in operations_iter:
+ yield PathOperationServer(
+ path,
+ operation,
+ None,
+ path_result,
+ {},
+ )
+
+
+class TemplateServersIterator:
+ def __call__(
+ self,
+ name: str,
+ operations_iter: Iterator[PathOperation],
+ spec: Spec,
+ base_url: Optional[str] = None,
+ ) -> Iterator[PathOperationServer]:
+ for path, operation, path_result in operations_iter:
+ servers = (
+ path.get("servers", None)
+ or operation.get("servers", None)
+ or spec.get("servers", [{"url": "/"}])
+ )
+ for server in servers:
+ server_url_pattern = name.rsplit(path_result.resolved, 1)[0]
+ server_url = server["url"]
+ if not is_absolute(server_url):
+ # relative to absolute url
+ if base_url is not None:
+ server_url = urljoin(base_url, server["url"])
+ # if no base url check only path part
+ else:
+ server_url_pattern = urlparse(server_url_pattern).path
+ if server_url.endswith("/"):
+ server_url = server_url[:-1]
+ # simple path
+ if server_url_pattern == server_url:
+ server_result = TemplateResult(server["url"], {})
+ yield PathOperationServer(
+ path,
+ operation,
+ server,
+ path_result,
+ server_result,
+ )
+ # template path
+ else:
+ result = parse(server["url"], server_url_pattern)
+ if result:
+ server_result = TemplateResult(
+ server["url"], result.named
+ )
+ yield PathOperationServer(
+ path,
+ operation,
+ server,
+ path_result,
+ server_result,
+ )
+ # servers should'n end with tailing slash
+ # but let's search for this too
+ server_url_pattern += "/"
+ result = parse(server["url"], server_url_pattern)
+ if result:
+ server_result = TemplateResult(
+ server["url"], result.named
+ )
+ yield PathOperationServer(
+ path,
+ operation,
+ server,
+ path_result,
+ server_result,
+ )
diff --git a/openapi_core/templating/paths/protocols.py b/openapi_core/templating/paths/protocols.py
new file mode 100644
index 00000000..bfc72b06
--- /dev/null
+++ b/openapi_core/templating/paths/protocols.py
@@ -0,0 +1,47 @@
+import sys
+from typing import Iterator
+from typing import Optional
+
+if sys.version_info >= (3, 8):
+ from typing import Protocol
+ from typing import runtime_checkable
+else:
+ from typing_extensions import Protocol
+ from typing_extensions import runtime_checkable
+
+from openapi_core.spec import Spec
+from openapi_core.templating.paths.datatypes import Path
+from openapi_core.templating.paths.datatypes import PathOperation
+from openapi_core.templating.paths.datatypes import PathOperationServer
+
+
+@runtime_checkable
+class PathsIterator(Protocol):
+ def __call__(
+ self, name: str, spec: Spec, base_url: Optional[str] = None
+ ) -> Iterator[Path]:
+ ...
+
+
+@runtime_checkable
+class OperationsIterator(Protocol):
+ def __call__(
+ self,
+ method: str,
+ paths_iter: Iterator[Path],
+ spec: Spec,
+ base_url: Optional[str] = None,
+ ) -> Iterator[PathOperation]:
+ ...
+
+
+@runtime_checkable
+class ServersIterator(Protocol):
+ def __call__(
+ self,
+ name: str,
+ operations_iter: Iterator[PathOperation],
+ spec: Spec,
+ base_url: Optional[str] = None,
+ ) -> Iterator[PathOperationServer]:
+ ...
diff --git a/openapi_core/templating/paths/types.py b/openapi_core/templating/paths/types.py
new file mode 100644
index 00000000..201fc40f
--- /dev/null
+++ b/openapi_core/templating/paths/types.py
@@ -0,0 +1,5 @@
+from typing import Type
+
+from openapi_core.templating.paths.finders import PathFinder
+
+PathFinderType = Type[PathFinder]
diff --git a/openapi_core/unmarshalling/processors.py b/openapi_core/unmarshalling/processors.py
index b2200a90..167b62bb 100644
--- a/openapi_core/unmarshalling/processors.py
+++ b/openapi_core/unmarshalling/processors.py
@@ -1,4 +1,5 @@
"""OpenAPI core unmarshalling processors module"""
+from typing import Any
from typing import Optional
from typing import Type
@@ -20,6 +21,7 @@ def __init__(
spec: Spec,
request_unmarshaller_cls: Optional[RequestUnmarshallerType] = None,
response_unmarshaller_cls: Optional[ResponseUnmarshallerType] = None,
+ **unmarshallers_kwargs: Any,
):
self.spec = spec
if (
@@ -31,8 +33,12 @@ def __init__(
request_unmarshaller_cls = classes.request_unmarshaller_cls
if response_unmarshaller_cls is None:
response_unmarshaller_cls = classes.response_unmarshaller_cls
- self.request_unmarshaller = request_unmarshaller_cls(self.spec)
- self.response_unmarshaller = response_unmarshaller_cls(self.spec)
+ self.request_unmarshaller = request_unmarshaller_cls(
+ self.spec, **unmarshallers_kwargs
+ )
+ self.response_unmarshaller = response_unmarshaller_cls(
+ self.spec, **unmarshallers_kwargs
+ )
def process_request(self, request: Request) -> RequestUnmarshalResult:
return self.request_unmarshaller.unmarshal(request)
diff --git a/openapi_core/unmarshalling/request/unmarshallers.py b/openapi_core/unmarshalling/request/unmarshallers.py
index 96b0b76e..7bd87a25 100644
--- a/openapi_core/unmarshalling/request/unmarshallers.py
+++ b/openapi_core/unmarshalling/request/unmarshallers.py
@@ -25,6 +25,7 @@
from openapi_core.security.factories import SecurityProviderFactory
from openapi_core.spec import Spec
from openapi_core.templating.paths.exceptions import PathError
+from openapi_core.templating.paths.types import PathFinderType
from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult
from openapi_core.unmarshalling.request.proxies import (
SpecRequestValidatorProxy,
@@ -92,6 +93,7 @@ def __init__(
schema_casters_factory: SchemaCastersFactory = schema_casters_factory,
parameter_deserializers_factory: ParameterDeserializersFactory = parameter_deserializers_factory,
media_type_deserializers_factory: MediaTypeDeserializersFactory = media_type_deserializers_factory,
+ path_finder_cls: Optional[PathFinderType] = None,
schema_validators_factory: Optional[SchemaValidatorsFactory] = None,
format_validators: Optional[FormatValidatorsDict] = None,
extra_format_validators: Optional[FormatValidatorsDict] = None,
@@ -112,6 +114,7 @@ def __init__(
schema_casters_factory=schema_casters_factory,
parameter_deserializers_factory=parameter_deserializers_factory,
media_type_deserializers_factory=media_type_deserializers_factory,
+ path_finder_cls=path_finder_cls,
schema_validators_factory=schema_validators_factory,
format_validators=format_validators,
extra_format_validators=extra_format_validators,
@@ -127,6 +130,7 @@ def __init__(
schema_casters_factory=schema_casters_factory,
parameter_deserializers_factory=parameter_deserializers_factory,
media_type_deserializers_factory=media_type_deserializers_factory,
+ path_finder_cls=path_finder_cls,
schema_validators_factory=schema_validators_factory,
format_validators=format_validators,
extra_format_validators=extra_format_validators,
diff --git a/openapi_core/unmarshalling/unmarshallers.py b/openapi_core/unmarshalling/unmarshallers.py
index af857906..cbcaebca 100644
--- a/openapi_core/unmarshalling/unmarshallers.py
+++ b/openapi_core/unmarshalling/unmarshallers.py
@@ -21,6 +21,7 @@
ParameterDeserializersFactory,
)
from openapi_core.spec import Spec
+from openapi_core.templating.paths.types import PathFinderType
from openapi_core.unmarshalling.schemas.datatypes import (
FormatUnmarshallersDict,
)
@@ -42,6 +43,7 @@ def __init__(
schema_casters_factory: SchemaCastersFactory = schema_casters_factory,
parameter_deserializers_factory: ParameterDeserializersFactory = parameter_deserializers_factory,
media_type_deserializers_factory: MediaTypeDeserializersFactory = media_type_deserializers_factory,
+ path_finder_cls: Optional[PathFinderType] = None,
schema_validators_factory: Optional[SchemaValidatorsFactory] = None,
format_validators: Optional[FormatValidatorsDict] = None,
extra_format_validators: Optional[FormatValidatorsDict] = None,
@@ -64,6 +66,7 @@ def __init__(
schema_casters_factory=schema_casters_factory,
parameter_deserializers_factory=parameter_deserializers_factory,
media_type_deserializers_factory=media_type_deserializers_factory,
+ path_finder_cls=path_finder_cls,
schema_validators_factory=schema_validators_factory,
format_validators=format_validators,
extra_format_validators=extra_format_validators,
diff --git a/openapi_core/validation/request/validators.py b/openapi_core/validation/request/validators.py
index d0bf3609..994f7cdc 100644
--- a/openapi_core/validation/request/validators.py
+++ b/openapi_core/validation/request/validators.py
@@ -33,6 +33,7 @@
from openapi_core.spec.paths import Spec
from openapi_core.templating.paths.exceptions import PathError
from openapi_core.templating.paths.finders import WebhookPathFinder
+from openapi_core.templating.paths.types import PathFinderType
from openapi_core.templating.security.exceptions import SecurityNotFound
from openapi_core.util import chainiters
from openapi_core.validation.decorators import ValidationErrorWrapper
@@ -70,6 +71,7 @@ def __init__(
schema_casters_factory: SchemaCastersFactory = schema_casters_factory,
parameter_deserializers_factory: ParameterDeserializersFactory = parameter_deserializers_factory,
media_type_deserializers_factory: MediaTypeDeserializersFactory = media_type_deserializers_factory,
+ path_finder_cls: Optional[PathFinderType] = None,
schema_validators_factory: Optional[SchemaValidatorsFactory] = None,
format_validators: Optional[FormatValidatorsDict] = None,
extra_format_validators: Optional[FormatValidatorsDict] = None,
@@ -84,6 +86,7 @@ def __init__(
schema_casters_factory=schema_casters_factory,
parameter_deserializers_factory=parameter_deserializers_factory,
media_type_deserializers_factory=media_type_deserializers_factory,
+ path_finder_cls=path_finder_cls,
schema_validators_factory=schema_validators_factory,
format_validators=format_validators,
extra_format_validators=extra_format_validators,
@@ -414,24 +417,19 @@ class V31RequestSecurityValidator(APICallRequestSecurityValidator):
class V31RequestValidator(APICallRequestValidator):
schema_validators_factory = oas31_schema_validators_factory
- path_finder_cls = WebhookPathFinder
class V31WebhookRequestBodyValidator(WebhookRequestBodyValidator):
schema_validators_factory = oas31_schema_validators_factory
- path_finder_cls = WebhookPathFinder
class V31WebhookRequestParametersValidator(WebhookRequestParametersValidator):
schema_validators_factory = oas31_schema_validators_factory
- path_finder_cls = WebhookPathFinder
class V31WebhookRequestSecurityValidator(WebhookRequestSecurityValidator):
schema_validators_factory = oas31_schema_validators_factory
- path_finder_cls = WebhookPathFinder
class V31WebhookRequestValidator(WebhookRequestValidator):
schema_validators_factory = oas31_schema_validators_factory
- path_finder_cls = WebhookPathFinder
diff --git a/openapi_core/validation/validators.py b/openapi_core/validation/validators.py
index b307d97c..f4a4569b 100644
--- a/openapi_core/validation/validators.py
+++ b/openapi_core/validation/validators.py
@@ -4,6 +4,7 @@
from typing import Mapping
from typing import Optional
from typing import Tuple
+from typing import Type
from urllib.parse import urljoin
if sys.version_info >= (3, 8):
@@ -34,13 +35,15 @@
from openapi_core.templating.media_types.datatypes import MediaType
from openapi_core.templating.paths.datatypes import PathOperationServer
from openapi_core.templating.paths.finders import APICallPathFinder
-from openapi_core.templating.paths.finders import BasePathFinder
+from openapi_core.templating.paths.finders import PathFinder
from openapi_core.templating.paths.finders import WebhookPathFinder
+from openapi_core.templating.paths.types import PathFinderType
from openapi_core.validation.schemas.datatypes import FormatValidatorsDict
from openapi_core.validation.schemas.factories import SchemaValidatorsFactory
class BaseValidator:
+ path_finder_cls: PathFinderType = NotImplemented
schema_validators_factory: SchemaValidatorsFactory = NotImplemented
def __init__(
@@ -50,6 +53,7 @@ def __init__(
schema_casters_factory: SchemaCastersFactory = schema_casters_factory,
parameter_deserializers_factory: ParameterDeserializersFactory = parameter_deserializers_factory,
media_type_deserializers_factory: MediaTypeDeserializersFactory = media_type_deserializers_factory,
+ path_finder_cls: Optional[PathFinderType] = None,
schema_validators_factory: Optional[SchemaValidatorsFactory] = None,
format_validators: Optional[FormatValidatorsDict] = None,
extra_format_validators: Optional[FormatValidatorsDict] = None,
@@ -65,6 +69,9 @@ def __init__(
self.media_type_deserializers_factory = (
media_type_deserializers_factory
)
+ self.path_finder_cls = path_finder_cls or self.path_finder_cls
+ if self.path_finder_cls is NotImplemented: # type: ignore[comparison-overlap]
+ raise NotImplementedError("path_finder_cls is not assigned")
self.schema_validators_factory = (
schema_validators_factory or self.schema_validators_factory
)
@@ -76,6 +83,10 @@ def __init__(
self.extra_format_validators = extra_format_validators
self.extra_media_type_deserializers = extra_media_type_deserializers
+ @cached_property
+ def path_finder(self) -> PathFinder:
+ return self.path_finder_cls(self.spec, base_url=self.base_url)
+
def _get_media_type(self, content: Spec, mimetype: str) -> MediaType:
from openapi_core.templating.media_types.finders import MediaTypeFinder
@@ -176,9 +187,7 @@ def _get_content_value_and_schema(
class BaseAPICallValidator(BaseValidator):
- @cached_property
- def path_finder(self) -> BasePathFinder:
- return APICallPathFinder(self.spec, base_url=self.base_url)
+ path_finder_cls = APICallPathFinder
def _find_path(self, request: Request) -> PathOperationServer:
path_pattern = getattr(request, "path_pattern", None) or request.path
@@ -187,9 +196,7 @@ def _find_path(self, request: Request) -> PathOperationServer:
class BaseWebhookValidator(BaseValidator):
- @cached_property
- def path_finder(self) -> BasePathFinder:
- return WebhookPathFinder(self.spec, base_url=self.base_url)
+ path_finder_cls = WebhookPathFinder
def _find_path(self, request: WebhookRequest) -> PathOperationServer:
return self.path_finder.find(request.method, request.name)
diff --git a/poetry.lock b/poetry.lock
index 2a0edf65..fc7e7beb 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -161,6 +161,46 @@ d = ["aiohttp (>=3.7.4)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]
+[[package]]
+name = "boto3"
+version = "1.26.96"
+description = "The AWS SDK for Python"
+category = "dev"
+optional = false
+python-versions = ">= 3.7"
+files = [
+ {file = "boto3-1.26.96-py3-none-any.whl", hash = "sha256:f961aa704bd7aeefc186ede52cabc3ef4c336979bb4098d3aad7ca922d55fc27"},
+ {file = "boto3-1.26.96.tar.gz", hash = "sha256:7017102c58b9984749bef3b9f476940593c311504354b9ee9dd7bb0b4657a77d"},
+]
+
+[package.dependencies]
+botocore = ">=1.29.96,<1.30.0"
+jmespath = ">=0.7.1,<2.0.0"
+s3transfer = ">=0.6.0,<0.7.0"
+
+[package.extras]
+crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
+
+[[package]]
+name = "botocore"
+version = "1.29.96"
+description = "Low-level, data-driven core of boto 3."
+category = "dev"
+optional = false
+python-versions = ">= 3.7"
+files = [
+ {file = "botocore-1.29.96-py3-none-any.whl", hash = "sha256:c449d7050e9bc4a8b8a62ae492cbdc931b786bf5752b792867f1276967fadaed"},
+ {file = "botocore-1.29.96.tar.gz", hash = "sha256:b9781108810e33f8406942c3e3aab748650c59d5cddb7c9d323f4e2682e7b0b6"},
+]
+
+[package.dependencies]
+jmespath = ">=0.7.1,<2.0.0"
+python-dateutil = ">=2.1,<3.0.0"
+urllib3 = ">=1.25.4,<1.27"
+
+[package.extras]
+crt = ["awscrt (==0.16.9)"]
+
[[package]]
name = "certifi"
version = "2022.12.7"
@@ -173,6 +213,83 @@ files = [
{file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"},
]
+[[package]]
+name = "cffi"
+version = "1.15.1"
+description = "Foreign Function Interface for Python calling C code."
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+ {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
+ {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
+ {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"},
+ {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"},
+ {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"},
+ {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"},
+ {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"},
+ {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"},
+ {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"},
+ {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"},
+ {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"},
+ {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"},
+ {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"},
+ {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"},
+ {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"},
+ {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"},
+ {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"},
+ {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"},
+ {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"},
+ {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"},
+ {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"},
+ {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"},
+ {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"},
+ {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"},
+ {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"},
+ {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"},
+ {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"},
+ {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"},
+ {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"},
+ {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"},
+ {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"},
+ {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"},
+ {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"},
+ {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"},
+ {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"},
+]
+
+[package.dependencies]
+pycparser = "*"
+
[[package]]
name = "cfgv"
version = "3.3.1"
@@ -365,6 +482,52 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1
[package.extras]
toml = ["tomli"]
+[[package]]
+name = "cryptography"
+version = "39.0.2"
+description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "cryptography-39.0.2-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:2725672bb53bb92dc7b4150d233cd4b8c59615cd8288d495eaa86db00d4e5c06"},
+ {file = "cryptography-39.0.2-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:23df8ca3f24699167daf3e23e51f7ba7334d504af63a94af468f468b975b7dd7"},
+ {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:eb40fe69cfc6f5cdab9a5ebd022131ba21453cf7b8a7fd3631f45bbf52bed612"},
+ {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc0521cce2c1d541634b19f3ac661d7a64f9555135e9d8af3980965be717fd4a"},
+ {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffd394c7896ed7821a6d13b24657c6a34b6e2650bd84ae063cf11ccffa4f1a97"},
+ {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:e8a0772016feeb106efd28d4a328e77dc2edae84dfbac06061319fdb669ff828"},
+ {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8f35c17bd4faed2bc7797d2a66cbb4f986242ce2e30340ab832e5d99ae60e011"},
+ {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b49a88ff802e1993b7f749b1eeb31134f03c8d5c956e3c125c75558955cda536"},
+ {file = "cryptography-39.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c682e736513db7d04349b4f6693690170f95aac449c56f97415c6980edef5"},
+ {file = "cryptography-39.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:d7d84a512a59f4412ca8549b01f94be4161c94efc598bf09d027d67826beddc0"},
+ {file = "cryptography-39.0.2-cp36-abi3-win32.whl", hash = "sha256:c43ac224aabcbf83a947eeb8b17eaf1547bce3767ee2d70093b461f31729a480"},
+ {file = "cryptography-39.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:788b3921d763ee35dfdb04248d0e3de11e3ca8eb22e2e48fef880c42e1f3c8f9"},
+ {file = "cryptography-39.0.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d15809e0dbdad486f4ad0979753518f47980020b7a34e9fc56e8be4f60702fac"},
+ {file = "cryptography-39.0.2-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:50cadb9b2f961757e712a9737ef33d89b8190c3ea34d0fb6675e00edbe35d074"},
+ {file = "cryptography-39.0.2-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:103e8f7155f3ce2ffa0049fe60169878d47a4364b277906386f8de21c9234aa1"},
+ {file = "cryptography-39.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6236a9610c912b129610eb1a274bdc1350b5df834d124fa84729ebeaf7da42c3"},
+ {file = "cryptography-39.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e944fe07b6f229f4c1a06a7ef906a19652bdd9fd54c761b0ff87e83ae7a30354"},
+ {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:35d658536b0a4117c885728d1a7032bdc9a5974722ae298d6c533755a6ee3915"},
+ {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:30b1d1bfd00f6fc80d11300a29f1d8ab2b8d9febb6ed4a38a76880ec564fae84"},
+ {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e029b844c21116564b8b61216befabca4b500e6816fa9f0ba49527653cae2108"},
+ {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fa507318e427169ade4e9eccef39e9011cdc19534f55ca2f36ec3f388c1f70f3"},
+ {file = "cryptography-39.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8bc0008ef798231fac03fe7d26e82d601d15bd16f3afaad1c6113771566570f3"},
+ {file = "cryptography-39.0.2.tar.gz", hash = "sha256:bc5b871e977c8ee5a1bbc42fa8d19bcc08baf0c51cbf1586b0e87a2694dde42f"},
+]
+
+[package.dependencies]
+cffi = ">=1.12"
+
+[package.extras]
+docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"]
+docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
+pep8test = ["black", "check-manifest", "mypy", "ruff", "types-pytz", "types-requests"]
+sdist = ["setuptools-rust (>=0.11.4)"]
+ssh = ["bcrypt (>=3.1.5)"]
+test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-shard (>=0.1.2)", "pytest-subtests", "pytest-xdist", "pytz"]
+test-randomorder = ["pytest-randomly"]
+tox = ["tox"]
+
[[package]]
name = "distlib"
version = "0.3.6"
@@ -755,6 +918,18 @@ MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
+[[package]]
+name = "jmespath"
+version = "1.0.1"
+description = "JSON Matching Expressions"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"},
+ {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"},
+]
+
[[package]]
name = "jsonschema"
version = "4.17.3"
@@ -927,6 +1102,54 @@ files = [
{file = "more_itertools-9.1.0-py3-none-any.whl", hash = "sha256:d2bc7f02446e86a68911e58ded76d6561eea00cddfb2a91e7019bbb586c799f3"},
]
+[[package]]
+name = "moto"
+version = "4.1.5"
+description = ""
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "moto-4.1.5-py2.py3-none-any.whl", hash = "sha256:363577f7a0cdf639852420f6ba5caa9aa3c90a688feae6315f8ee4bf324b8c27"},
+ {file = "moto-4.1.5.tar.gz", hash = "sha256:63542b7b9f307b00fae460b42d15cf9346de3ad3b1287fba38fc68f3c05e4da4"},
+]
+
+[package.dependencies]
+boto3 = ">=1.9.201"
+botocore = ">=1.12.201"
+cryptography = ">=3.3.1"
+importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
+Jinja2 = ">=2.10.1"
+python-dateutil = ">=2.1,<3.0.0"
+requests = ">=2.5"
+responses = ">=0.13.0"
+werkzeug = ">=0.5,<2.2.0 || >2.2.0,<2.2.1 || >2.2.1"
+xmltodict = "*"
+
+[package.extras]
+all = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.2.8)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"]
+apigateway = ["PyYAML (>=5.1)", "ecdsa (!=0.15)", "openapi-spec-validator (>=0.2.8)", "python-jose[cryptography] (>=3.1.0,<4.0.0)"]
+apigatewayv2 = ["PyYAML (>=5.1)"]
+appsync = ["graphql-core"]
+awslambda = ["docker (>=3.0.0)"]
+batch = ["docker (>=3.0.0)"]
+cloudformation = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.2.8)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"]
+cognitoidp = ["ecdsa (!=0.15)", "python-jose[cryptography] (>=3.1.0,<4.0.0)"]
+ds = ["sshpubkeys (>=3.1.0)"]
+dynamodb = ["docker (>=3.0.0)"]
+dynamodbstreams = ["docker (>=3.0.0)"]
+ebs = ["sshpubkeys (>=3.1.0)"]
+ec2 = ["sshpubkeys (>=3.1.0)"]
+efs = ["sshpubkeys (>=3.1.0)"]
+eks = ["sshpubkeys (>=3.1.0)"]
+glue = ["pyparsing (>=3.0.7)"]
+iotdata = ["jsondiff (>=1.1.2)"]
+route53resolver = ["sshpubkeys (>=3.1.0)"]
+s3 = ["PyYAML (>=5.1)"]
+server = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "flask (!=2.2.0,!=2.2.1)", "flask-cors", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.2.8)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"]
+ssm = ["PyYAML (>=5.1)"]
+xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"]
+
[[package]]
name = "mypy"
version = "1.1.1"
@@ -1169,50 +1392,62 @@ files = [
{file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"},
]
+[[package]]
+name = "pycparser"
+version = "2.21"
+description = "C parser in Python"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
+ {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
+]
+
[[package]]
name = "pydantic"
-version = "1.10.5"
+version = "1.10.7"
description = "Data validation and settings management using python type hints"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "pydantic-1.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5920824fe1e21cbb3e38cf0f3dd24857c8959801d1031ce1fac1d50857a03bfb"},
- {file = "pydantic-1.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3bb99cf9655b377db1a9e47fa4479e3330ea96f4123c6c8200e482704bf1eda2"},
- {file = "pydantic-1.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2185a3b3d98ab4506a3f6707569802d2d92c3a7ba3a9a35683a7709ea6c2aaa2"},
- {file = "pydantic-1.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f582cac9d11c227c652d3ce8ee223d94eb06f4228b52a8adaafa9fa62e73d5c9"},
- {file = "pydantic-1.10.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c9e5b778b6842f135902e2d82624008c6a79710207e28e86966cd136c621bfee"},
- {file = "pydantic-1.10.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:72ef3783be8cbdef6bca034606a5de3862be6b72415dc5cb1fb8ddbac110049a"},
- {file = "pydantic-1.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:45edea10b75d3da43cfda12f3792833a3fa70b6eee4db1ed6aed528cef17c74e"},
- {file = "pydantic-1.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:63200cd8af1af2c07964546b7bc8f217e8bda9d0a2ef0ee0c797b36353914984"},
- {file = "pydantic-1.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:305d0376c516b0dfa1dbefeae8c21042b57b496892d721905a6ec6b79494a66d"},
- {file = "pydantic-1.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd326aff5d6c36f05735c7c9b3d5b0e933b4ca52ad0b6e4b38038d82703d35b"},
- {file = "pydantic-1.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bb0452d7b8516178c969d305d9630a3c9b8cf16fcf4713261c9ebd465af0d73"},
- {file = "pydantic-1.10.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9a9d9155e2a9f38b2eb9374c88f02fd4d6851ae17b65ee786a87d032f87008f8"},
- {file = "pydantic-1.10.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f836444b4c5ece128b23ec36a446c9ab7f9b0f7981d0d27e13a7c366ee163f8a"},
- {file = "pydantic-1.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:8481dca324e1c7b715ce091a698b181054d22072e848b6fc7895cd86f79b4449"},
- {file = "pydantic-1.10.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:87f831e81ea0589cd18257f84386bf30154c5f4bed373b7b75e5cb0b5d53ea87"},
- {file = "pydantic-1.10.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ce1612e98c6326f10888df951a26ec1a577d8df49ddcaea87773bfbe23ba5cc"},
- {file = "pydantic-1.10.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58e41dd1e977531ac6073b11baac8c013f3cd8706a01d3dc74e86955be8b2c0c"},
- {file = "pydantic-1.10.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6a4b0aab29061262065bbdede617ef99cc5914d1bf0ddc8bcd8e3d7928d85bd6"},
- {file = "pydantic-1.10.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:36e44a4de37b8aecffa81c081dbfe42c4d2bf9f6dff34d03dce157ec65eb0f15"},
- {file = "pydantic-1.10.5-cp37-cp37m-win_amd64.whl", hash = "sha256:261f357f0aecda005934e413dfd7aa4077004a174dafe414a8325e6098a8e419"},
- {file = "pydantic-1.10.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b429f7c457aebb7fbe7cd69c418d1cd7c6fdc4d3c8697f45af78b8d5a7955760"},
- {file = "pydantic-1.10.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:663d2dd78596c5fa3eb996bc3f34b8c2a592648ad10008f98d1348be7ae212fb"},
- {file = "pydantic-1.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51782fd81f09edcf265823c3bf43ff36d00db246eca39ee765ef58dc8421a642"},
- {file = "pydantic-1.10.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c428c0f64a86661fb4873495c4fac430ec7a7cef2b8c1c28f3d1a7277f9ea5ab"},
- {file = "pydantic-1.10.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:76c930ad0746c70f0368c4596020b736ab65b473c1f9b3872310a835d852eb19"},
- {file = "pydantic-1.10.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3257bd714de9db2102b742570a56bf7978e90441193acac109b1f500290f5718"},
- {file = "pydantic-1.10.5-cp38-cp38-win_amd64.whl", hash = "sha256:f5bee6c523d13944a1fdc6f0525bc86dbbd94372f17b83fa6331aabacc8fd08e"},
- {file = "pydantic-1.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:532e97c35719f137ee5405bd3eeddc5c06eb91a032bc755a44e34a712420daf3"},
- {file = "pydantic-1.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ca9075ab3de9e48b75fa8ccb897c34ccc1519177ad8841d99f7fd74cf43be5bf"},
- {file = "pydantic-1.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd46a0e6296346c477e59a954da57beaf9c538da37b9df482e50f836e4a7d4bb"},
- {file = "pydantic-1.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3353072625ea2a9a6c81ad01b91e5c07fa70deb06368c71307529abf70d23325"},
- {file = "pydantic-1.10.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3f9d9b2be177c3cb6027cd67fbf323586417868c06c3c85d0d101703136e6b31"},
- {file = "pydantic-1.10.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b473d00ccd5c2061fd896ac127b7755baad233f8d996ea288af14ae09f8e0d1e"},
- {file = "pydantic-1.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:5f3bc8f103b56a8c88021d481410874b1f13edf6e838da607dcb57ecff9b4594"},
- {file = "pydantic-1.10.5-py3-none-any.whl", hash = "sha256:7c5b94d598c90f2f46b3a983ffb46ab806a67099d118ae0da7ef21a2a4033b28"},
- {file = "pydantic-1.10.5.tar.gz", hash = "sha256:9e337ac83686645a46db0e825acceea8e02fca4062483f40e9ae178e8bd1103a"},
+ {file = "pydantic-1.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d"},
+ {file = "pydantic-1.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e"},
+ {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a"},
+ {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f"},
+ {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209"},
+ {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af"},
+ {file = "pydantic-1.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a"},
+ {file = "pydantic-1.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1"},
+ {file = "pydantic-1.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe"},
+ {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd"},
+ {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb"},
+ {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b"},
+ {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca"},
+ {file = "pydantic-1.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d"},
+ {file = "pydantic-1.10.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918"},
+ {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe"},
+ {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee"},
+ {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1"},
+ {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a"},
+ {file = "pydantic-1.10.7-cp37-cp37m-win_amd64.whl", hash = "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914"},
+ {file = "pydantic-1.10.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd"},
+ {file = "pydantic-1.10.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245"},
+ {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d"},
+ {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3"},
+ {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52"},
+ {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209"},
+ {file = "pydantic-1.10.7-cp38-cp38-win_amd64.whl", hash = "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e"},
+ {file = "pydantic-1.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143"},
+ {file = "pydantic-1.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e"},
+ {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d"},
+ {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f"},
+ {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd"},
+ {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5"},
+ {file = "pydantic-1.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e"},
+ {file = "pydantic-1.10.7-py3-none-any.whl", hash = "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6"},
+ {file = "pydantic-1.10.7.tar.gz", hash = "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e"},
]
[package.dependencies]
@@ -1346,6 +1581,21 @@ files = [
flake8 = ">=3.5"
pytest = ">=3.5"
+[[package]]
+name = "python-dateutil"
+version = "2.8.2"
+description = "Extensions to the standard Python datetime module"
+category = "dev"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+files = [
+ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
+ {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
+]
+
+[package.dependencies]
+six = ">=1.5"
+
[[package]]
name = "pytz"
version = "2022.7.1"
@@ -1485,6 +1735,24 @@ idna = {version = "*", optional = true, markers = "extra == \"idna2008\""}
[package.extras]
idna2008 = ["idna"]
+[[package]]
+name = "s3transfer"
+version = "0.6.0"
+description = "An Amazon S3 Transfer Manager"
+category = "dev"
+optional = false
+python-versions = ">= 3.7"
+files = [
+ {file = "s3transfer-0.6.0-py3-none-any.whl", hash = "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd"},
+ {file = "s3transfer-0.6.0.tar.gz", hash = "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947"},
+]
+
+[package.dependencies]
+botocore = ">=1.12.36,<2.0a.0"
+
+[package.extras]
+crt = ["botocore[crt] (>=1.20.29,<2.0a.0)"]
+
[[package]]
name = "setuptools"
version = "67.5.0"
@@ -1881,6 +2149,18 @@ MarkupSafe = ">=2.1.1"
[package.extras]
watchdog = ["watchdog"]
+[[package]]
+name = "xmltodict"
+version = "0.13.0"
+description = "Makes working with XML feel like you are working with JSON"
+category = "dev"
+optional = false
+python-versions = ">=3.4"
+files = [
+ {file = "xmltodict-0.13.0-py2.py3-none-any.whl", hash = "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852"},
+ {file = "xmltodict-0.13.0.tar.gz", hash = "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56"},
+]
+
[[package]]
name = "zipp"
version = "3.15.0"
@@ -1908,4 +2188,4 @@ starlette = []
[metadata]
lock-version = "2.0"
python-versions = "^3.7.0"
-content-hash = "93e3ce63cf5a2e72870e4da2b22904dc0c80679522371c346bdf87f8e5e05a85"
+content-hash = "4b1ab95b5de3e714dc00c53801626d696eeaecb734cf672289d226e41c3fe271"
diff --git a/pyproject.toml b/pyproject.toml
index 964b6589..1b2e2118 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -11,6 +11,9 @@ output = "reports/coverage.xml"
[tool.mypy]
files = "openapi_core"
+plugins = [
+ "pydantic.mypy"
+]
strict = true
[[tool.mypy.overrides]]
@@ -72,6 +75,7 @@ jsonschema-spec = "^0.1.1"
backports-cached-property = {version = "^1.0.2", python = "<3.8" }
sphinx = {version = "^5.3.0", optional = true}
sphinx-immaterial = {version = "^0.11.0", optional = true}
+pydantic = "^1.10.7"
[tool.poetry.extras]
docs = ["sphinx", "sphinx-immaterial"]
@@ -83,11 +87,13 @@ starlette = ["starlette", "httpx"]
[tool.poetry.dev-dependencies]
black = "^23.1.0"
+boto3 = "^1.26.96"
django = ">=3.0"
djangorestframework = "^3.11.2"
falcon = ">=3.0"
flask = "*"
isort = "^5.11.5"
+moto = "^4.1.5"
pre-commit = "*"
pytest = "^7"
pytest-flake8 = "*"