diff --git a/openapi_core/casting/schemas/factories.py b/openapi_core/casting/schemas/factories.py index 39c7832b..6e95925e 100644 --- a/openapi_core/casting/schemas/factories.py +++ b/openapi_core/casting/schemas/factories.py @@ -19,11 +19,13 @@ def __init__( def create( self, + spec: SchemaPath, schema: SchemaPath, format_validators: Optional[FormatValidatorsDict] = None, extra_format_validators: Optional[FormatValidatorsDict] = None, ) -> SchemaCaster: schema_validator = self.schema_validators_factory.create( + spec, schema, format_validators=format_validators, extra_format_validators=extra_format_validators, diff --git a/openapi_core/deserializing/media_types/deserializers.py b/openapi_core/deserializing/media_types/deserializers.py index 9d03f187..b9809af0 100644 --- a/openapi_core/deserializing/media_types/deserializers.py +++ b/openapi_core/deserializing/media_types/deserializers.py @@ -66,6 +66,7 @@ def get_deserializer_callable( class MediaTypeDeserializer: def __init__( self, + spec: SchemaPath, style_deserializers_factory: StyleDeserializersFactory, media_types_deserializer: MediaTypesDeserializer, mimetype: str, @@ -75,6 +76,7 @@ def __init__( encoding: Optional[SchemaPath] = None, **parameters: str, ): + self.spec = spec self.style_deserializers_factory = style_deserializers_factory self.media_types_deserializer = media_types_deserializer self.mimetype = mimetype @@ -117,6 +119,7 @@ def evolve( schema_caster = self.schema_caster.evolve(schema) return cls( + self.spec, self.style_deserializers_factory, self.media_types_deserializer, mimetype=mimetype or self.mimetype, @@ -221,7 +224,7 @@ def decode_property_style( prep_encoding, default_location="query" ) prop_deserializer = self.style_deserializers_factory.create( - prop_style, prop_explode, prop_schema, name=prop_name + self.spec, prop_schema, prop_style, prop_explode, name=prop_name ) return prop_deserializer.deserialize(location) diff --git a/openapi_core/deserializing/media_types/factories.py b/openapi_core/deserializing/media_types/factories.py index 2f740c1e..33fb7369 100644 --- a/openapi_core/deserializing/media_types/factories.py +++ b/openapi_core/deserializing/media_types/factories.py @@ -58,6 +58,7 @@ def from_schema_casters_factory( def create( self, + spec: SchemaPath, mimetype: str, schema: Optional[SchemaPath] = None, schema_validator: Optional[SchemaValidator] = None, @@ -89,11 +90,12 @@ def create( ): schema_caster = ( self.style_deserializers_factory.schema_casters_factory.create( - schema + spec, schema ) ) return MediaTypeDeserializer( + spec, self.style_deserializers_factory, media_types_deserializer, mimetype, diff --git a/openapi_core/deserializing/styles/deserializers.py b/openapi_core/deserializing/styles/deserializers.py index 1e5f16a3..59565603 100644 --- a/openapi_core/deserializing/styles/deserializers.py +++ b/openapi_core/deserializing/styles/deserializers.py @@ -3,8 +3,6 @@ from typing import Mapping from typing import Optional -from jsonschema_path import SchemaPath - from openapi_core.casting.schemas.casters import SchemaCaster from openapi_core.casting.schemas.exceptions import CastError from openapi_core.deserializing.exceptions import DeserializeError @@ -17,15 +15,14 @@ def __init__( style: str, explode: bool, name: str, - schema: SchemaPath, + schema_type: str, caster: SchemaCaster, deserializer_callable: Optional[DeserializerCallable] = None, ): self.style = style self.explode = explode self.name = name - self.schema = schema - self.schema_type = (schema / "type").read_str("") + self.schema_type = schema_type self.caster = caster self.deserializer_callable = deserializer_callable diff --git a/openapi_core/deserializing/styles/factories.py b/openapi_core/deserializing/styles/factories.py index d184cd3a..2d4504c5 100644 --- a/openapi_core/deserializing/styles/factories.py +++ b/openapi_core/deserializing/styles/factories.py @@ -20,13 +20,15 @@ def __init__( def create( self, + spec: SchemaPath, + schema: SchemaPath, style: str, explode: bool, - schema: SchemaPath, name: str, ) -> StyleDeserializer: deserialize_callable = self.style_deserializers.get(style) - caster = self.schema_casters_factory.create(schema) + caster = self.schema_casters_factory.create(spec, schema) + schema_type = (schema / "type").read_str("") return StyleDeserializer( - style, explode, name, schema, caster, deserialize_callable + style, explode, name, schema_type, caster, deserialize_callable ) diff --git a/openapi_core/unmarshalling/schemas/factories.py b/openapi_core/unmarshalling/schemas/factories.py index 3e0f144b..8bd3a061 100644 --- a/openapi_core/unmarshalling/schemas/factories.py +++ b/openapi_core/unmarshalling/schemas/factories.py @@ -33,6 +33,7 @@ def __init__( def create( self, + spec: SchemaPath, schema: SchemaPath, format_validators: Optional[FormatValidatorsDict] = None, format_unmarshallers: Optional[FormatUnmarshallersDict] = None, @@ -51,6 +52,7 @@ def create( if extra_format_validators is None: extra_format_validators = {} schema_validator = self.schema_validators_factory.create( + spec, schema, 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 bf1a3420..4fc7d501 100644 --- a/openapi_core/unmarshalling/unmarshallers.py +++ b/openapi_core/unmarshalling/unmarshallers.py @@ -90,6 +90,7 @@ def __init__( def _unmarshal_schema(self, schema: SchemaPath, value: Any) -> Any: unmarshaller = self.schema_unmarshallers_factory.create( + self.spec, schema, format_validators=self.format_validators, extra_format_validators=self.extra_format_validators, diff --git a/openapi_core/validation/schemas/__init__.py b/openapi_core/validation/schemas/__init__.py index 5eec14e6..2d57bd97 100644 --- a/openapi_core/validation/schemas/__init__.py +++ b/openapi_core/validation/schemas/__init__.py @@ -1,13 +1,12 @@ -from functools import partial - -from lazy_object_proxy import Proxy +from openapi_schema_validator import OAS31_BASE_DIALECT_ID +from openapi_schema_validator import OAS32_BASE_DIALECT_ID from openapi_schema_validator import OAS30ReadValidator from openapi_schema_validator import OAS30WriteValidator from openapi_schema_validator import OAS31Validator from openapi_schema_validator import OAS32Validator -from openapi_core.validation.schemas._validators import ( - build_forbid_unspecified_additional_properties_validator, +from openapi_core.validation.schemas.factories import ( + DialectSchemaValidatorsFactory, ) from openapi_core.validation.schemas.factories import SchemaValidatorsFactory @@ -20,44 +19,22 @@ oas30_write_schema_validators_factory = SchemaValidatorsFactory( OAS30WriteValidator, - Proxy( - partial( - build_forbid_unspecified_additional_properties_validator, - OAS30WriteValidator, - ) - ), ) oas30_read_schema_validators_factory = SchemaValidatorsFactory( OAS30ReadValidator, - Proxy( - partial( - build_forbid_unspecified_additional_properties_validator, - OAS30ReadValidator, - ) - ), ) -oas31_schema_validators_factory = SchemaValidatorsFactory( +oas31_schema_validators_factory = DialectSchemaValidatorsFactory( OAS31Validator, - Proxy( - partial( - build_forbid_unspecified_additional_properties_validator, - OAS31Validator, - ) - ), + OAS31_BASE_DIALECT_ID, # NOTE: Intentionally use OAS 3.0 format checker for OAS 3.1 to preserve # backward compatibility for `byte`/`binary` formats. # See https://github.com/python-openapi/openapi-core/issues/506 format_checker=OAS30ReadValidator.FORMAT_CHECKER, ) -oas32_schema_validators_factory = SchemaValidatorsFactory( +oas32_schema_validators_factory = DialectSchemaValidatorsFactory( OAS32Validator, - Proxy( - partial( - build_forbid_unspecified_additional_properties_validator, - OAS32Validator, - ) - ), + OAS32_BASE_DIALECT_ID, ) diff --git a/openapi_core/validation/schemas/factories.py b/openapi_core/validation/schemas/factories.py index 32082c38..49020bdc 100644 --- a/openapi_core/validation/schemas/factories.py +++ b/openapi_core/validation/schemas/factories.py @@ -1,14 +1,20 @@ from copy import deepcopy +from functools import lru_cache +from typing import Any from typing import Optional from typing import cast from jsonschema._format import FormatChecker from jsonschema.protocols import Validator +from jsonschema.validators import validator_for from jsonschema_path import SchemaPath from openapi_core.validation.schemas._validators import ( build_enforce_properties_required_validator, ) +from openapi_core.validation.schemas._validators import ( + build_forbid_unspecified_additional_properties_validator, +) from openapi_core.validation.schemas.datatypes import FormatValidatorsDict from openapi_core.validation.schemas.validators import SchemaValidator @@ -16,17 +22,20 @@ class SchemaValidatorsFactory: def __init__( self, - schema_validator_class: type[Validator], - strict_schema_validator_class: Optional[type[Validator]] = None, + schema_validator_cls: type[Validator], format_checker: Optional[FormatChecker] = None, ): - self.schema_validator_class = schema_validator_class - self.strict_schema_validator_class = strict_schema_validator_class + self.schema_validator_cls = schema_validator_cls if format_checker is None: - format_checker = self.schema_validator_class.FORMAT_CHECKER + format_checker = self.schema_validator_cls.FORMAT_CHECKER assert format_checker is not None self.format_checker = format_checker + def get_validator_cls( + self, spec: SchemaPath, schema: SchemaPath + ) -> type[Validator]: + return self.schema_validator_cls + def get_format_checker( self, format_validators: Optional[FormatValidatorsDict] = None, @@ -57,34 +66,82 @@ def _add_validators( def create( self, + spec: SchemaPath, schema: SchemaPath, format_validators: Optional[FormatValidatorsDict] = None, extra_format_validators: Optional[FormatValidatorsDict] = None, forbid_unspecified_additional_properties: bool = False, enforce_properties_required: bool = False, ) -> SchemaValidator: - validator_class: type[Validator] = self.schema_validator_class + validator_cls: type[Validator] = self.get_validator_cls(spec, schema) + if enforce_properties_required: + validator_cls = build_enforce_properties_required_validator( + validator_cls + ) if forbid_unspecified_additional_properties: - if self.strict_schema_validator_class is None: - raise ValueError( - "Strict additional properties validation is not supported " - "by this factory." + validator_cls = ( + build_forbid_unspecified_additional_properties_validator( + validator_cls ) - validator_class = self.strict_schema_validator_class - - if enforce_properties_required: - validator_class = build_enforce_properties_required_validator( - validator_class ) format_checker = self.get_format_checker( format_validators, extra_format_validators ) with schema.resolve() as resolved: - jsonschema_validator = validator_class( + jsonschema_validator = validator_cls( resolved.contents, _resolver=resolved.resolver, format_checker=format_checker, ) return SchemaValidator(schema, jsonschema_validator) + + +class DialectSchemaValidatorsFactory(SchemaValidatorsFactory): + def __init__( + self, + schema_validator_cls: type[Validator], + default_jsonschema_dialect_id: str, + format_checker: Optional[FormatChecker] = None, + ): + super().__init__(schema_validator_cls, format_checker) + self.default_jsonschema_dialect_id = default_jsonschema_dialect_id + + def get_validator_cls( + self, spec: SchemaPath, schema: SchemaPath + ) -> type[Validator]: + dialect_id = self._get_dialect_id(spec, schema) + + validator_cls = self._get_validator_class_for_dialect(dialect_id) + if validator_cls is None: + raise ValueError(f"Unknown JSON Schema dialect: {dialect_id!r}") + + return validator_cls + + def _get_dialect_id( + self, + spec: SchemaPath, + schema: SchemaPath, + ) -> str: + try: + return (schema / "$schema").read_str() + except KeyError: + return self._get_default_jsonschema_dialect_id(spec) + + def _get_default_jsonschema_dialect_id(self, spec: SchemaPath) -> str: + return (spec / "jsonSchemaDialect").read_str( + default=self.default_jsonschema_dialect_id + ) + + @lru_cache + def _get_validator_class_for_dialect( + self, dialect_id: str + ) -> type[Validator] | None: + return cast( + type[Validator] | None, + validator_for( + {"$schema": dialect_id}, + default=cast(Any, None), + ), + ) diff --git a/openapi_core/validation/validators.py b/openapi_core/validation/validators.py index 5d9ecb42..595ef674 100644 --- a/openapi_core/validation/validators.py +++ b/openapi_core/validation/validators.py @@ -143,6 +143,7 @@ def _deserialise_media_type( schema_validator = None if schema is not None: schema_validator = self.schema_validators_factory.create( + self.spec, schema, format_validators=self.format_validators, extra_format_validators=self.extra_format_validators, @@ -150,6 +151,7 @@ def _deserialise_media_type( enforce_properties_required=self.enforce_properties_required, ) deserializer = self.media_type_deserializers_factory.create( + self.spec, mimetype, schema=schema, schema_validator=schema_validator, @@ -169,12 +171,13 @@ def _deserialise_style( style, explode = get_style_and_explode(param_or_header) schema = param_or_header / "schema" deserializer = self.style_deserializers_factory.create( - style, explode, schema, name=name + self.spec, schema, style, explode, name=name ) return deserializer.deserialize(location) def _validate_schema(self, schema: SchemaPath, value: Any) -> None: validator = self.schema_validators_factory.create( + self.spec, schema, format_validators=self.format_validators, extra_format_validators=self.extra_format_validators, diff --git a/poetry.lock b/poetry.lock index 2a3dc996..12f93553 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -223,7 +223,7 @@ files = [ {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, ] -markers = {main = "extra == \"fastapi\" or extra == \"starlette\""} +markers = {main = "extra == \"starlette\" or extra == \"fastapi\""} [package.dependencies] exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} @@ -992,7 +992,7 @@ files = [ {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, ] -markers = {main = "(extra == \"fastapi\" or extra == \"starlette\") and python_version < \"3.11\"", dev = "python_version < \"3.11\""} +markers = {main = "(extra == \"starlette\" or extra == \"fastapi\") and python_version < \"3.11\"", dev = "python_version < \"3.11\""} [package.extras] test = ["pytest (>=6)"] @@ -1394,7 +1394,7 @@ files = [ {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] -markers = {main = "extra == \"aiohttp\" or extra == \"fastapi\" or extra == \"starlette\" or extra == \"requests\""} +markers = {main = "extra == \"aiohttp\" or extra == \"starlette\" or extra == \"fastapi\" or extra == \"requests\""} [[package]] name = "iniconfig" @@ -1574,7 +1574,7 @@ files = [ [package.dependencies] attrs = ">=22.2.0" -jsonschema-specifications = ">=2023.03.6" +jsonschema-specifications = ">=2023.3.6" referencing = ">=0.28.4" rpds-py = ">=0.7.1" @@ -3646,7 +3646,7 @@ files = [ {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] -markers = {main = "extra == \"fastapi\" or extra == \"starlette\""} +markers = {main = "extra == \"starlette\" or extra == \"fastapi\""} [[package]] name = "sqlparse" @@ -3692,7 +3692,7 @@ description = "The little ASGI library that shines." optional = true python-versions = ">=3.10" groups = ["main"] -markers = "extra == \"fastapi\" or extra == \"starlette\"" +markers = "extra == \"starlette\" or extra == \"fastapi\"" files = [ {file = "starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca"}, {file = "starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca"}, @@ -4113,4 +4113,4 @@ werkzeug = [] [metadata] lock-version = "2.1" python-versions = "^3.10.0" -content-hash = "46e52ba0d6a796e20441b1cb81f9dd272a5912063d9418d7ef33b4cb94296540" +content-hash = "0cf5b3d42d3e5ebf160eb77df35fea6f5df9ac760e219fa664a8a94ddf16eb81" diff --git a/pyproject.toml b/pyproject.toml index d6382727..a342c68e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,7 +77,6 @@ multidict = {version = "^6.0.4", optional = true} aioitertools = {version = ">=0.11,<0.14", optional = true} fastapi = {version = ">=0.111,<0.130", optional = true} typing-extensions = "^4.8.0" -lazy-object-proxy = "^1.12.0" [tool.poetry.extras] django = ["django"] diff --git a/tests/integration/unmarshalling/test_unmarshallers.py b/tests/integration/unmarshalling/test_unmarshallers.py index 060f19cb..ddb322be 100644 --- a/tests/integration/unmarshalling/test_unmarshallers.py +++ b/tests/integration/unmarshalling/test_unmarshallers.py @@ -26,31 +26,36 @@ class BaseTestOASSchemaUnmarshallersFactoryCall: - def test_create_no_schema(self, unmarshallers_factory): + @pytest.fixture + def spec(self): + spec_dict = {} + return SchemaPath.from_dict(spec_dict) + + def test_create_no_schema(self, spec, unmarshallers_factory): with pytest.raises(TypeError): unmarshallers_factory.create(None) - def test_create_schema_deprecated(self, unmarshallers_factory): - schema = { + def test_create_schema_deprecated(self, spec, unmarshallers_factory): + schema_dict = { "deprecated": True, } - spec = SchemaPath.from_dict(schema) + schema = SchemaPath.from_dict(schema_dict) with pytest.warns(DeprecationWarning): - unmarshallers_factory.create(spec) + unmarshallers_factory.create(spec, schema) - def test_create_formatter_not_found(self, unmarshallers_factory): + def test_create_formatter_not_found(self, spec, unmarshallers_factory): custom_format = "custom" - schema = { + schema_dict = { "type": "string", "format": custom_format, } - spec = SchemaPath.from_dict(schema) + schema = SchemaPath.from_dict(schema_dict) with pytest.raises( FormatterNotFoundError, match="Formatter not found for custom format", ): - unmarshallers_factory.create(spec) + unmarshallers_factory.create(spec, schema) @pytest.mark.parametrize( "value", @@ -64,10 +69,10 @@ def test_create_formatter_not_found(self, unmarshallers_factory): False, ], ) - def test_no_type(self, unmarshallers_factory, value): - schema = {} - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + def test_no_type(self, spec, unmarshallers_factory, value): + schema_dict = {} + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -80,10 +85,10 @@ def test_no_type(self, unmarshallers_factory, value): oas31_schema_unmarshallers_factory, ], ) - def test_no_type_object_with_array_of_null(self, factory): - schema = {} - spec = SchemaPath.from_dict(schema) - unmarshaller = factory.create(spec) + def test_no_type_object_with_array_of_null(self, spec, factory): + schema_dict = {} + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = factory.create(spec, schema) value = {"foo": [None]} result = unmarshaller.unmarshal(value) @@ -102,12 +107,12 @@ def test_no_type_object_with_array_of_null(self, factory): ("boolean", False), ], ) - def test_basic_types(self, unmarshallers_factory, type, value): - schema = { + def test_basic_types(self, spec, unmarshallers_factory, type, value): + schema_dict = { "type": type, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -157,12 +162,14 @@ def test_basic_types(self, unmarshallers_factory, type, value): ("object", ["one", "two"]), ], ) - def test_basic_types_invalid(self, unmarshallers_factory, type, value): - schema = { + def test_basic_types_invalid( + self, spec, unmarshallers_factory, type, value + ): + schema_dict = { "type": type, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises( InvalidSchemaValue, @@ -202,13 +209,13 @@ def test_basic_types_invalid(self, unmarshallers_factory, type, value): ], ) def test_basic_formats( - self, unmarshallers_factory, format, value, unmarshalled + self, spec, unmarshallers_factory, format, value, unmarshalled ): - schema = { + schema_dict = { "format": format, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -244,14 +251,14 @@ def test_basic_formats( ], ) def test_basic_type_formats( - self, unmarshallers_factory, type, format, value, unmarshalled + self, spec, unmarshallers_factory, type, format, value, unmarshalled ): - schema = { + schema_dict = { "type": type, "format": format, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -268,14 +275,14 @@ def test_basic_type_formats( ], ) def test_basic_type_formats_ignored( - self, unmarshallers_factory, type, format, value + self, spec, unmarshallers_factory, type, format, value ): - schema = { + schema_dict = { "type": type, "format": format, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -290,14 +297,14 @@ def test_basic_type_formats_ignored( ], ) def test_basic_type_formats_invalid( - self, unmarshallers_factory, type, format, value + self, spec, unmarshallers_factory, type, format, value ): - schema = { + schema_dict = { "type": type, "format": format, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue) as exc_info: unmarshaller.unmarshal(value) @@ -312,25 +319,25 @@ def test_basic_type_formats_invalid( ("dGVzdA==", "test"), ], ) - def test_string_byte(self, unmarshallers_factory, value, expected): - schema = { + def test_string_byte(self, spec, unmarshallers_factory, value, expected): + schema_dict = { "type": "string", "format": "byte", } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) assert result == expected - def test_string_date(self, unmarshallers_factory): - schema = { + def test_string_date(self, spec, unmarshallers_factory): + schema_dict = { "type": "string", "format": "date", } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = "2018-01-02" result = unmarshaller.unmarshal(value) @@ -347,25 +354,27 @@ def test_string_date(self, unmarshallers_factory): ), ], ) - def test_string_datetime(self, unmarshallers_factory, value, expected): - schema = { + def test_string_datetime( + self, spec, unmarshallers_factory, value, expected + ): + schema_dict = { "type": "string", "format": "date-time", } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) assert result == expected - def test_string_datetime_invalid(self, unmarshallers_factory): - schema = { + def test_string_datetime_invalid(self, spec, unmarshallers_factory): + schema_dict = { "type": "string", "format": "date-time", } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = "2018-01-02T00:00:00" with pytest.raises(InvalidSchemaValue) as exc_info: @@ -375,39 +384,39 @@ def test_string_datetime_invalid(self, unmarshallers_factory): "is not a 'date-time'" in exc_info.value.schema_errors[0].message ) - def test_string_password(self, unmarshallers_factory): - schema = { + def test_string_password(self, spec, unmarshallers_factory): + schema_dict = { "type": "string", "format": "password", } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = "passwd" result = unmarshaller.unmarshal(value) assert result == value - def test_string_uuid(self, unmarshallers_factory): - schema = { + def test_string_uuid(self, spec, unmarshallers_factory): + schema_dict = { "type": "string", "format": "uuid", } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = str(uuid4()) result = unmarshaller.unmarshal(value) assert result == UUID(value) - def test_string_uuid_invalid(self, unmarshallers_factory): - schema = { + def test_string_uuid_invalid(self, spec, unmarshallers_factory): + schema_dict = { "type": "string", "format": "uuid", } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = "test" with pytest.raises(InvalidSchemaValue) as exc_info: @@ -429,27 +438,27 @@ def test_string_uuid_invalid(self, unmarshallers_factory): ], ) def test_formats_ignored( - self, unmarshallers_factory, type, format, value, expected + self, spec, unmarshallers_factory, type, format, value, expected ): - schema = { + schema_dict = { "type": type, "format": format, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) assert result == expected @pytest.mark.parametrize("value", ["bar", "foobar"]) - def test_string_pattern(self, unmarshallers_factory, value): - schema = { + def test_string_pattern(self, spec, unmarshallers_factory, value): + schema_dict = { "type": "string", "pattern": "bar", } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -463,14 +472,14 @@ def test_string_pattern(self, unmarshallers_factory, value): ], ) def test_string_pattern_invalid( - self, unmarshallers_factory, value, pattern + self, spec, unmarshallers_factory, value, pattern ): - schema = { + schema_dict = { "type": "string", "pattern": pattern, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue) as exc_info: unmarshaller.unmarshal(value) @@ -481,26 +490,28 @@ def test_string_pattern_invalid( ) @pytest.mark.parametrize("value", ["abc", "abcd"]) - def test_string_min_length(self, unmarshallers_factory, value): - schema = { + def test_string_min_length(self, spec, unmarshallers_factory, value): + schema_dict = { "type": "string", "minLength": 3, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) assert result == value @pytest.mark.parametrize("value", ["", "a", "ab"]) - def test_string_min_length_invalid(self, unmarshallers_factory, value): - schema = { + def test_string_min_length_invalid( + self, spec, unmarshallers_factory, value + ): + schema_dict = { "type": "string", "minLength": 3, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue) as exc_info: unmarshaller.unmarshal(value) @@ -511,26 +522,28 @@ def test_string_min_length_invalid(self, unmarshallers_factory, value): ) @pytest.mark.parametrize("value", ["", "a"]) - def test_string_max_length(self, unmarshallers_factory, value): - schema = { + def test_string_max_length(self, spec, unmarshallers_factory, value): + schema_dict = { "type": "string", "maxLength": 1, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) assert result == value @pytest.mark.parametrize("value", ["ab", "abc"]) - def test_string_max_length_invalid(self, unmarshallers_factory, value): - schema = { + def test_string_max_length_invalid( + self, spec, unmarshallers_factory, value + ): + schema_dict = { "type": "string", "maxLength": 1, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue) as exc_info: unmarshaller.unmarshal(value) @@ -546,39 +559,39 @@ def test_string_max_length_invalid(self, unmarshallers_factory, value): ], ) def test_string_max_length_invalid_schema( - self, unmarshallers_factory, value + self, spec, unmarshallers_factory, value ): - schema = { + schema_dict = { "type": "string", "maxLength": -1, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue): unmarshaller.unmarshal(value) - def test_integer_enum(self, unmarshallers_factory): - schema = { + def test_integer_enum(self, spec, unmarshallers_factory): + schema_dict = { "type": "integer", "enum": [1, 2, 3], } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = 2 result = unmarshaller.unmarshal(value) assert result == int(value) - def test_integer_enum_invalid(self, unmarshallers_factory): + def test_integer_enum_invalid(self, spec, unmarshallers_factory): enum = [1, 2, 3] - schema = { + schema_dict = { "type": "integer", "enum": enum, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = 12 with pytest.raises(InvalidSchemaValue) as exc_info: @@ -601,15 +614,15 @@ def test_integer_enum_invalid(self, unmarshallers_factory): ("boolean", False), ], ) - def test_array(self, unmarshallers_factory, type, value): - schema = { + def test_array(self, spec, unmarshallers_factory, type, value): + schema_dict = { "type": "array", "items": { "type": type, }, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value_list = [value] * 3 result = unmarshaller.unmarshal(value_list) @@ -627,15 +640,15 @@ def test_array(self, unmarshallers_factory, type, value): ("boolean", "123"), ], ) - def test_array_invalid(self, unmarshallers_factory, type, value): - schema = { + def test_array_invalid(self, spec, unmarshallers_factory, type, value): + schema_dict = { "type": "array", "items": { "type": type, }, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue) as exc_info: unmarshaller.unmarshal([value]) @@ -646,16 +659,16 @@ def test_array_invalid(self, unmarshallers_factory, type, value): ) @pytest.mark.parametrize("value", [[], [1], [1, 2]]) - def test_array_min_items_invalid(self, unmarshallers_factory, value): - schema = { + def test_array_min_items_invalid(self, spec, unmarshallers_factory, value): + schema_dict = { "type": "array", "items": { "type": "number", }, "minItems": 3, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue) as exc_info: unmarshaller.unmarshal(value) @@ -665,16 +678,16 @@ def test_array_min_items_invalid(self, unmarshallers_factory, value): ) @pytest.mark.parametrize("value", [[], [1], [1, 2]]) - def test_array_min_items(self, unmarshallers_factory, value): - schema = { + def test_array_min_items(self, spec, unmarshallers_factory, value): + schema_dict = { "type": "array", "items": { "type": "number", }, "minItems": 0, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -687,32 +700,32 @@ def test_array_min_items(self, unmarshallers_factory, value): ], ) def test_array_max_items_invalid_schema( - self, unmarshallers_factory, value + self, spec, unmarshallers_factory, value ): - schema = { + schema_dict = { "type": "array", "items": { "type": "number", }, "maxItems": -1, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue): unmarshaller.unmarshal(value) @pytest.mark.parametrize("value", [[1, 2], [2, 3, 4]]) - def test_array_max_items_invalid(self, unmarshallers_factory, value): - schema = { + def test_array_max_items_invalid(self, spec, unmarshallers_factory, value): + schema_dict = { "type": "array", "items": { "type": "number", }, "maxItems": 1, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue) as exc_info: unmarshaller.unmarshal(value) @@ -722,16 +735,18 @@ def test_array_max_items_invalid(self, unmarshallers_factory, value): ) @pytest.mark.parametrize("value", [[1, 2, 1], [2, 2]]) - def test_array_unique_items_invalid(self, unmarshallers_factory, value): - schema = { + def test_array_unique_items_invalid( + self, spec, unmarshallers_factory, value + ): + schema_dict = { "type": "array", "items": { "type": "number", }, "uniqueItems": True, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue) as exc_info: unmarshaller.unmarshal(value) @@ -741,8 +756,8 @@ def test_array_unique_items_invalid(self, unmarshallers_factory, value): in exc_info.value.schema_errors[0].message ) - def test_object_any_of(self, unmarshallers_factory): - schema = { + def test_object_any_of(self, spec, unmarshallers_factory): + schema_dict = { "type": "object", "anyOf": [ { @@ -757,16 +772,16 @@ def test_object_any_of(self, unmarshallers_factory): }, ], } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = {"someint": 1} result = unmarshaller.unmarshal(value) assert result == value - def test_object_any_of_invalid(self, unmarshallers_factory): - schema = { + def test_object_any_of_invalid(self, spec, unmarshallers_factory): + schema_dict = { "type": "object", "anyOf": [ { @@ -781,14 +796,14 @@ def test_object_any_of_invalid(self, unmarshallers_factory): }, ], } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue): unmarshaller.unmarshal({"someint": "1"}) - def test_object_one_of_default(self, unmarshallers_factory): - schema = { + def test_object_one_of_default(self, spec, unmarshallers_factory): + schema_dict = { "type": "object", "oneOf": [ { @@ -816,16 +831,16 @@ def test_object_one_of_default(self, unmarshallers_factory): }, }, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) assert unmarshaller.unmarshal({"someint": 1}) == { "someint": 1, "somestr": "defaultstring", } - def test_object_any_of_default(self, unmarshallers_factory): - schema = { + def test_object_any_of_default(self, spec, unmarshallers_factory): + schema_dict = { "type": "object", "anyOf": [ { @@ -847,16 +862,16 @@ def test_object_any_of_default(self, unmarshallers_factory): }, ], } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) assert unmarshaller.unmarshal({"someint": "1"}) == { "someint": "1", "somestr": "defaultstring", } - def test_object_all_of_default(self, unmarshallers_factory): - schema = { + def test_object_all_of_default(self, spec, unmarshallers_factory): + schema_dict = { "type": "object", "allOf": [ { @@ -879,8 +894,8 @@ def test_object_all_of_default(self, unmarshallers_factory): }, ], } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) assert unmarshaller.unmarshal({}) == { "someint": 1, @@ -902,8 +917,8 @@ def test_object_all_of_default(self, unmarshallers_factory): }, ], ) - def test_object_with_properties(self, unmarshallers_factory, value): - schema = { + def test_object_with_properties(self, spec, unmarshallers_factory, value): + schema_dict = { "type": "object", "properties": { "somestr": { @@ -914,8 +929,8 @@ def test_object_with_properties(self, unmarshallers_factory, value): }, }, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -944,9 +959,9 @@ def test_object_with_properties(self, unmarshallers_factory, value): ], ) def test_object_with_properties_invalid( - self, unmarshallers_factory, value + self, spec, unmarshallers_factory, value ): - schema = { + schema_dict = { "type": "object", "properties": { "somestr": { @@ -958,8 +973,8 @@ def test_object_with_properties_invalid( }, "additionalProperties": False, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue): unmarshaller.unmarshal(value) @@ -970,8 +985,8 @@ def test_object_with_properties_invalid( {}, ], ) - def test_object_default_property(self, unmarshallers_factory, value): - schema = { + def test_object_default_property(self, spec, unmarshallers_factory, value): + schema_dict = { "type": "object", "properties": { "prop": { @@ -980,8 +995,8 @@ def test_object_default_property(self, unmarshallers_factory, value): } }, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -994,14 +1009,14 @@ def test_object_default_property(self, unmarshallers_factory, value): ], ) def test_object_additional_properties_false( - self, unmarshallers_factory, value + self, spec, unmarshallers_factory, value ): - schema = { + schema_dict = { "type": "object", "additionalProperties": False, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue): unmarshaller.unmarshal(value) @@ -1016,23 +1031,25 @@ def test_object_additional_properties_false( ) @pytest.mark.parametrize("additional_properties", [True, {}]) def test_object_additional_properties_free_form_object( - self, value, additional_properties, unmarshallers_factory + self, value, additional_properties, spec, unmarshallers_factory ): - schema = { + schema_dict = { "type": "object", "additionalProperties": additional_properties, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) assert result == value - def test_object_additional_properties_list(self, unmarshallers_factory): - schema = {"type": "object"} - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + def test_object_additional_properties_list( + self, spec, unmarshallers_factory + ): + schema_dict = {"type": "object"} + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal({"user_ids": [1, 2, 3, 4]}) @@ -1046,12 +1063,14 @@ def test_object_additional_properties_list(self, unmarshallers_factory): {"additional": 1}, ], ) - def test_object_additional_properties(self, unmarshallers_factory, value): - schema = { + def test_object_additional_properties( + self, spec, unmarshallers_factory, value + ): + schema_dict = { "type": "object", } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -1064,17 +1083,17 @@ def test_object_additional_properties(self, unmarshallers_factory, value): ], ) def test_object_additional_properties_object( - self, unmarshallers_factory, value + self, spec, unmarshallers_factory, value ): additional_properties = { "type": "integer", } - schema = { + schema_dict = { "type": "object", "additionalProperties": additional_properties, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -1088,14 +1107,14 @@ def test_object_additional_properties_object( {"a": 1, "b": 2, "c": 3}, ], ) - def test_object_min_properties(self, unmarshallers_factory, value): - schema = { + def test_object_min_properties(self, spec, unmarshallers_factory, value): + schema_dict = { "type": "object", "properties": {k: {"type": "number"} for k in ["a", "b", "c"]}, "minProperties": 1, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -1109,14 +1128,16 @@ def test_object_min_properties(self, unmarshallers_factory, value): {"a": 1, "b": 2, "c": 3}, ], ) - def test_object_min_properties_invalid(self, unmarshallers_factory, value): - schema = { + def test_object_min_properties_invalid( + self, spec, unmarshallers_factory, value + ): + schema_dict = { "type": "object", "properties": {k: {"type": "number"} for k in ["a", "b", "c"]}, "minProperties": 4, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue): unmarshaller.unmarshal(value) @@ -1128,14 +1149,14 @@ def test_object_min_properties_invalid(self, unmarshallers_factory, value): ], ) def test_object_min_properties_invalid_schema( - self, unmarshallers_factory, value + self, spec, unmarshallers_factory, value ): - schema = { + schema_dict = { "type": "object", "minProperties": 2, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue): unmarshaller.unmarshal(value) @@ -1148,14 +1169,14 @@ def test_object_min_properties_invalid_schema( {"a": 1, "b": 2, "c": 3}, ], ) - def test_object_max_properties(self, unmarshallers_factory, value): - schema = { + def test_object_max_properties(self, spec, unmarshallers_factory, value): + schema_dict = { "type": "object", "properties": {k: {"type": "number"} for k in ["a", "b", "c"]}, "maxProperties": 3, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -1169,14 +1190,16 @@ def test_object_max_properties(self, unmarshallers_factory, value): {"a": 1, "b": 2, "c": 3}, ], ) - def test_object_max_properties_invalid(self, unmarshallers_factory, value): - schema = { + def test_object_max_properties_invalid( + self, spec, unmarshallers_factory, value + ): + schema_dict = { "type": "object", "properties": {k: {"type": "number"} for k in ["a", "b", "c"]}, "maxProperties": 0, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue): unmarshaller.unmarshal(value) @@ -1188,20 +1211,20 @@ def test_object_max_properties_invalid(self, unmarshallers_factory, value): ], ) def test_object_max_properties_invalid_schema( - self, unmarshallers_factory, value + self, spec, unmarshallers_factory, value ): - schema = { + schema_dict = { "type": "object", "maxProperties": -1, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue): unmarshaller.unmarshal(value) - def test_any_one_of(self, unmarshallers_factory): - schema = { + def test_any_one_of(self, spec, unmarshallers_factory): + schema_dict = { "oneOf": [ { "type": "string", @@ -1214,16 +1237,16 @@ def test_any_one_of(self, unmarshallers_factory): }, ], } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = ["hello"] result = unmarshaller.unmarshal(value) assert result == value - def test_any_any_of(self, unmarshallers_factory): - schema = { + def test_any_any_of(self, spec, unmarshallers_factory): + schema_dict = { "anyOf": [ { "type": "string", @@ -1236,16 +1259,16 @@ def test_any_any_of(self, unmarshallers_factory): }, ], } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = ["hello"] result = unmarshaller.unmarshal(value) assert result == value - def test_any_all_of(self, unmarshallers_factory): - schema = { + def test_any_all_of(self, spec, unmarshallers_factory): + schema_dict = { "allOf": [ { "type": "array", @@ -1255,8 +1278,8 @@ def test_any_all_of(self, unmarshallers_factory): } ], } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = ["hello"] result = unmarshaller.unmarshal(value) @@ -1285,8 +1308,10 @@ def test_any_all_of(self, unmarshallers_factory): }, ], ) - def test_any_all_of_invalid_properties(self, value, unmarshallers_factory): - schema = { + def test_any_all_of_invalid_properties( + self, value, spec, unmarshallers_factory + ): + schema_dict = { "allOf": [ { "type": "object", @@ -1309,14 +1334,14 @@ def test_any_all_of_invalid_properties(self, value, unmarshallers_factory): ], "additionalProperties": False, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue): unmarshaller.unmarshal(value) - def test_any_format_one_of(self, unmarshallers_factory): - schema = { + def test_any_format_one_of(self, spec, unmarshallers_factory): + schema_dict = { "format": "date", "oneOf": [ {"type": "integer"}, @@ -1325,16 +1350,16 @@ def test_any_format_one_of(self, unmarshallers_factory): }, ], } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = "2018-01-02" result = unmarshaller.unmarshal(value) assert result == date(2018, 1, 2) - def test_any_one_of_any(self, unmarshallers_factory): - schema = { + def test_any_one_of_any(self, spec, unmarshallers_factory): + schema_dict = { "oneOf": [ {"type": "integer"}, { @@ -1343,16 +1368,16 @@ def test_any_one_of_any(self, unmarshallers_factory): }, ], } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = "2018-01-02" result = unmarshaller.unmarshal(value) assert result == date(2018, 1, 2) - def test_any_any_of_any(self, unmarshallers_factory): - schema = { + def test_any_any_of_any(self, spec, unmarshallers_factory): + schema_dict = { "anyOf": [ {}, { @@ -1361,16 +1386,16 @@ def test_any_any_of_any(self, unmarshallers_factory): }, ], } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = "2018-01-02" result = unmarshaller.unmarshal(value) assert result == date(2018, 1, 2) - def test_any_all_of_any(self, unmarshallers_factory): - schema = { + def test_any_all_of_any(self, spec, unmarshallers_factory): + schema_dict = { "allOf": [ {}, { @@ -1379,8 +1404,8 @@ def test_any_all_of_any(self, unmarshallers_factory): }, ], } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = "2018-01-02" result = unmarshaller.unmarshal(value) @@ -1393,7 +1418,7 @@ def test_any_all_of_any(self, unmarshallers_factory): {}, ], ) - def test_any_of_no_valid(self, unmarshallers_factory, value): + def test_any_of_no_valid(self, spec, unmarshallers_factory, value): any_of = [ { "type": "object", @@ -1414,11 +1439,11 @@ def test_any_of_no_valid(self, unmarshallers_factory, value): }, }, ] - schema = { + schema_dict = { "anyOf": any_of, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue): unmarshaller.unmarshal(value) @@ -1429,7 +1454,7 @@ def test_any_of_no_valid(self, unmarshallers_factory, value): {}, ], ) - def test_any_one_of_no_valid(self, unmarshallers_factory, value): + def test_any_one_of_no_valid(self, spec, unmarshallers_factory, value): one_of = [ { "type": "object", @@ -1454,11 +1479,11 @@ def test_any_one_of_no_valid(self, unmarshallers_factory, value): }, }, ] - schema = { + schema_dict = { "oneOf": one_of, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue): unmarshaller.unmarshal(value) @@ -1469,13 +1494,15 @@ def test_any_one_of_no_valid(self, unmarshallers_factory, value): {}, ], ) - def test_any_any_of_different_type(self, unmarshallers_factory, value): + def test_any_any_of_different_type( + self, spec, unmarshallers_factory, value + ): any_of = [{"type": "integer"}, {"type": "string"}] - schema = { + schema_dict = { "anyOf": any_of, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue): unmarshaller.unmarshal(value) @@ -1486,7 +1513,9 @@ def test_any_any_of_different_type(self, unmarshallers_factory, value): {}, ], ) - def test_any_one_of_different_type(self, unmarshallers_factory, value): + def test_any_one_of_different_type( + self, spec, unmarshallers_factory, value + ): one_of = [ { "type": "integer", @@ -1495,11 +1524,11 @@ def test_any_one_of_different_type(self, unmarshallers_factory, value): "type": "string", }, ] - schema = { + schema_dict = { "oneOf": one_of, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue): unmarshaller.unmarshal(value) @@ -1516,7 +1545,7 @@ def test_any_one_of_different_type(self, unmarshallers_factory, value): }, ], ) - def test_any_any_of_unambiguous(self, unmarshallers_factory, value): + def test_any_any_of_unambiguous(self, spec, unmarshallers_factory, value): any_of = [ { "type": "object", @@ -1542,11 +1571,11 @@ def test_any_any_of_unambiguous(self, unmarshallers_factory, value): "additionalProperties": False, }, ] - schema = { + schema_dict = { "anyOf": any_of, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -1558,7 +1587,7 @@ def test_any_any_of_unambiguous(self, unmarshallers_factory, value): {}, ], ) - def test_object_multiple_any_of(self, unmarshallers_factory, value): + def test_object_multiple_any_of(self, spec, unmarshallers_factory, value): any_of = [ { "type": "object", @@ -1567,12 +1596,12 @@ def test_object_multiple_any_of(self, unmarshallers_factory, value): "type": "object", }, ] - schema = { + schema_dict = { "type": "object", "anyOf": any_of, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -1584,7 +1613,7 @@ def test_object_multiple_any_of(self, unmarshallers_factory, value): dict(), ], ) - def test_object_multiple_one_of(self, unmarshallers_factory, value): + def test_object_multiple_one_of(self, spec, unmarshallers_factory, value): one_of = [ { "type": "object", @@ -1593,12 +1622,12 @@ def test_object_multiple_one_of(self, unmarshallers_factory, value): "type": "object", }, ] - schema = { + schema_dict = { "type": "object", "oneOf": one_of, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue): unmarshaller.unmarshal(value) @@ -1615,7 +1644,7 @@ def test_object_multiple_one_of(self, unmarshallers_factory, value): }, ], ) - def test_any_one_of_unambiguous(self, unmarshallers_factory, value): + def test_any_one_of_unambiguous(self, spec, unmarshallers_factory, value): one_of = [ { "type": "object", @@ -1643,11 +1672,11 @@ def test_any_one_of_unambiguous(self, unmarshallers_factory, value): "additionalProperties": False, }, ] - schema = { + schema_dict = { "oneOf": one_of, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -1655,10 +1684,10 @@ def test_any_one_of_unambiguous(self, unmarshallers_factory, value): class BaseTestOASS30chemaUnmarshallersFactoryCall: - def test_null_undefined(self, unmarshallers_factory): - schema = {"type": "null"} - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + def test_null_undefined(self, spec, unmarshallers_factory): + schema_dict = {"type": "null"} + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(UnknownType): unmarshaller.unmarshal(None) @@ -1673,10 +1702,10 @@ def test_null_undefined(self, unmarshallers_factory): "string", ], ) - def test_nullable(self, unmarshallers_factory, type): - schema = {"type": type, "nullable": True} - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + def test_nullable(self, spec, unmarshallers_factory, type): + schema_dict = {"type": type, "nullable": True} + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(None) @@ -1692,10 +1721,10 @@ def test_nullable(self, unmarshallers_factory, type): "string", ], ) - def test_not_nullable(self, unmarshallers_factory, type): - schema = {"type": type} - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + def test_not_nullable(self, spec, unmarshallers_factory, type): + schema_dict = {"type": type} + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises( InvalidSchemaValue, @@ -1719,14 +1748,14 @@ def test_not_nullable(self, unmarshallers_factory, type): ], ) def test_basic_type_oas30_formats( - self, unmarshallers_factory, type, format, value, unmarshalled + self, spec, unmarshallers_factory, type, format, value, unmarshalled ): - schema = { + schema_dict = { "type": type, "format": format, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -1739,14 +1768,14 @@ def test_basic_type_oas30_formats( ], ) def test_basic_type_oas30_formats_invalid( - self, unmarshallers_factory, type, format, value + self, spec, unmarshallers_factory, type, format, value ): - schema = { + schema_dict = { "type": type, "format": format, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises( InvalidSchemaValue, @@ -1765,12 +1794,12 @@ def test_basic_type_oas30_formats_invalid( ), strict=True, ) - def test_string_format_binary_invalid(self, unmarshallers_factory): - schema = { + def test_string_format_binary_invalid(self, spec, unmarshallers_factory): + schema_dict = { "type": "string", } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = b"true" with pytest.raises( @@ -1798,48 +1827,48 @@ def test_string_format_binary_invalid(self, unmarshallers_factory): ], ) def test_nultiple_types_undefined( - self, unmarshallers_factory, types, value + self, spec, unmarshallers_factory, types, value ): - schema = {"type": types} - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema_dict = {"type": types} + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(SchemaError): unmarshaller.unmarshal(value) - def test_integer_default_nullable(self, unmarshallers_factory): + def test_integer_default_nullable(self, spec, unmarshallers_factory): default_value = 123 - schema = { + schema_dict = { "type": "integer", "default": default_value, "nullable": True, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = None result = unmarshaller.unmarshal(value) assert result is None - def test_array_nullable(self, unmarshallers_factory): - schema = { + def test_array_nullable(self, spec, unmarshallers_factory): + schema_dict = { "type": "array", "items": { "type": "integer", }, "nullable": True, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = None result = unmarshaller.unmarshal(value) assert result is None - def test_object_property_nullable(self, unmarshallers_factory): - schema = { + def test_object_property_nullable(self, spec, unmarshallers_factory): + schema_dict = { "type": "object", "properties": { "foo": { @@ -1848,16 +1877,16 @@ def test_object_property_nullable(self, unmarshallers_factory): } }, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = {"foo": None} result = unmarshaller.unmarshal(value) assert result == value - def test_subschema_nullable(self, unmarshallers_factory): - schema = { + def test_subschema_nullable(self, spec, unmarshallers_factory): + schema_dict = { "oneOf": [ { "type": "integer", @@ -1867,8 +1896,8 @@ def test_subschema_nullable(self, unmarshallers_factory): }, ] } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = None result = unmarshaller.unmarshal(value) @@ -1884,8 +1913,8 @@ class TestOAS30RequestSchemaUnmarshallersFactory( def unmarshallers_factory(self): return oas30_write_schema_unmarshallers_factory - def test_write_only_properties(self, unmarshallers_factory): - schema = { + def test_write_only_properties(self, spec, unmarshallers_factory): + schema_dict = { "type": "object", "required": ["id"], "properties": { @@ -1895,8 +1924,8 @@ def test_write_only_properties(self, unmarshallers_factory): } }, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = {"id": 10} # readOnly properties may be admitted in a Response context @@ -1904,8 +1933,8 @@ def test_write_only_properties(self, unmarshallers_factory): assert result == value - def test_read_only_properties_invalid(self, unmarshallers_factory): - schema = { + def test_read_only_properties_invalid(self, spec, unmarshallers_factory): + schema_dict = { "type": "object", "required": ["id"], "properties": { @@ -1915,8 +1944,8 @@ def test_read_only_properties_invalid(self, unmarshallers_factory): } }, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = {"id": 10} # readOnly properties are not admitted on a Request context @@ -1932,8 +1961,8 @@ class TestOAS30ResponseSchemaUnmarshallersFactory( def unmarshallers_factory(self): return oas30_read_schema_unmarshallers_factory - def test_read_only_properties(self, unmarshallers_factory): - schema = { + def test_read_only_properties(self, spec, unmarshallers_factory): + schema_dict = { "type": "object", "required": ["id"], "properties": { @@ -1943,8 +1972,8 @@ def test_read_only_properties(self, unmarshallers_factory): } }, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) # readOnly properties may be admitted in a Response context result = unmarshaller.unmarshal({"id": 10}) @@ -1953,8 +1982,8 @@ def test_read_only_properties(self, unmarshallers_factory): "id": 10, } - def test_write_only_properties_invalid(self, unmarshallers_factory): - schema = { + def test_write_only_properties_invalid(self, spec, unmarshallers_factory): + schema_dict = { "type": "object", "required": ["id"], "properties": { @@ -1964,8 +1993,8 @@ def test_write_only_properties_invalid(self, unmarshallers_factory): } }, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) # readOnly properties are not admitted on a Request context with pytest.raises(InvalidSchemaValue): @@ -1995,16 +2024,16 @@ def unmarshallers_factory(self): ], ) def test_create_oas30_formatter_not_found( - self, unmarshallers_factory, type, format + self, spec, unmarshallers_factory, type, format ): - schema = { + schema_dict = { "type": type, "format": format, } - spec = SchemaPath.from_dict(schema) + schema = SchemaPath.from_dict(schema_dict) with pytest.raises(FormatterNotFoundError): - unmarshallers_factory.create(spec) + unmarshallers_factory.create(spec, schema) @pytest.mark.parametrize( "type,value", @@ -2017,12 +2046,14 @@ def test_create_oas30_formatter_not_found( ("object", b"test"), ], ) - def test_basic_types_invalid(self, unmarshallers_factory, type, value): - schema = { + def test_basic_types_invalid( + self, spec, unmarshallers_factory, type, value + ): + schema_dict = { "type": type, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises( InvalidSchemaValue, @@ -2030,20 +2061,20 @@ def test_basic_types_invalid(self, unmarshallers_factory, type, value): ): unmarshaller.unmarshal(value) - def test_null(self, unmarshallers_factory): - schema = {"type": "null"} - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + def test_null(self, spec, unmarshallers_factory): + schema_dict = {"type": "null"} + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(None) assert result is None @pytest.mark.parametrize("value", ["string", 2, 3.14, True, [1, 2], {}]) - def test_null_invalid(self, unmarshallers_factory, value): - schema = {"type": "null"} - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + def test_null_invalid(self, spec, unmarshallers_factory, value): + schema_dict = {"type": "null"} + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue) as exc_info: unmarshaller.unmarshal(value) @@ -2063,10 +2094,10 @@ def test_null_invalid(self, unmarshallers_factory, value): (["object", "null"], {}), ], ) - def test_nultiple_types(self, unmarshallers_factory, types, value): - schema = {"type": types} - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + def test_nultiple_types(self, spec, unmarshallers_factory, types, value): + schema_dict = {"type": types} + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) @@ -2083,10 +2114,12 @@ def test_nultiple_types(self, unmarshallers_factory, types, value): (["object", "null"], [1, 2]), ], ) - def test_nultiple_types_invalid(self, unmarshallers_factory, types, value): - schema = {"type": types} - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + def test_nultiple_types_invalid( + self, spec, unmarshallers_factory, types, value + ): + schema_dict = {"type": types} + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) with pytest.raises(InvalidSchemaValue) as exc_info: unmarshaller.unmarshal(value) @@ -2101,30 +2134,30 @@ def test_nultiple_types_invalid(self, unmarshallers_factory, types, value): ], ) def test_multiple_types_format_valid_or_ignored( - self, unmarshallers_factory, types, format, value, expected + self, spec, unmarshallers_factory, types, format, value, expected ): - schema = { + schema_dict = { "type": types, "format": format, } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(value) assert result == expected - def test_any_null(self, unmarshallers_factory): - schema = {} - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + def test_any_null(self, spec, unmarshallers_factory): + schema_dict = {} + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) result = unmarshaller.unmarshal(None) assert result is None - def test_subschema_null(self, unmarshallers_factory): - schema = { + def test_subschema_null(self, spec, unmarshallers_factory): + schema_dict = { "oneOf": [ { "type": "integer", @@ -2134,8 +2167,8 @@ def test_subschema_null(self, unmarshallers_factory): }, ] } - spec = SchemaPath.from_dict(schema) - unmarshaller = unmarshallers_factory.create(spec) + schema = SchemaPath.from_dict(schema_dict) + unmarshaller = unmarshallers_factory.create(spec, schema) value = None result = unmarshaller.unmarshal(value) diff --git a/tests/integration/validation/test_dialect_validators.py b/tests/integration/validation/test_dialect_validators.py new file mode 100644 index 00000000..d2929c28 --- /dev/null +++ b/tests/integration/validation/test_dialect_validators.py @@ -0,0 +1,155 @@ +from typing import Any +from typing import Dict +from typing import Optional +from typing import Type + +import pytest +from jsonschema_path import SchemaPath + +from openapi_core import V31RequestValidator +from openapi_core import V32RequestValidator +from openapi_core.testing import MockRequest +from openapi_core.validation.request.exceptions import InvalidRequestBody + + +def _spec_dict( + openapi_version: str, + dialect: Optional[str] = None, + schema_dialect: Optional[str] = None, +) -> Dict[str, Any]: + schema = {"type": "integer", "minimum": 10, "exclusiveMinimum": True} + if schema_dialect is not None: + schema["$schema"] = schema_dialect + + spec = { + "openapi": openapi_version, + "info": {"title": "Dialect Validation", "version": "1.0.0"}, + "servers": [{"url": "http://example.com"}], + "paths": { + "/users": { + "post": { + "requestBody": { + "required": True, + "content": {"application/json": {"schema": schema}}, + }, + "responses": {"200": {"description": "OK"}}, + } + } + }, + } + if dialect is not None: + spec["jsonSchemaDialect"] = dialect + + return spec + + +@pytest.mark.parametrize( + "openapi_version, validator_cls", + [ + ("3.1.0", V31RequestValidator), + ("3.2.0", V32RequestValidator), + ], +) +class TestDialectValidators: + def test_default_dialect_valid( + self, openapi_version: str, validator_cls: Type[Any] + ) -> None: + spec = _spec_dict(openapi_version=openapi_version) + spec_path = SchemaPath.from_dict(spec) + validator = validator_cls(spec_path) + + request = MockRequest( + "http://example.com", + "POST", + "/users", + data=b"10", + content_type="application/json", + ) + validator.validate(request) + + def test_unsupported_json_schema_dialect( + self, openapi_version: str, validator_cls: Type[Any] + ) -> None: + spec = _spec_dict( + openapi_version=openapi_version, + dialect="http://unsupported.dialect", + ) + spec_path = SchemaPath.from_dict(spec) + + validator = validator_cls(spec_path) + request = MockRequest( + "http://example.com", + "POST", + "/users", + data=b"10", + content_type="application/json", + ) + with pytest.raises( + ValueError, + match="Unknown JSON Schema dialect: 'http://unsupported.dialect'", + ): + validator.validate(request) + + def test_unsupported_schema_dialect( + self, openapi_version: str, validator_cls: Type[Any] + ) -> None: + spec = _spec_dict( + openapi_version=openapi_version, + schema_dialect="http://unsupported.dialect", + ) + spec_path = SchemaPath.from_dict(spec) + + validator = validator_cls(spec_path) + request = MockRequest( + "http://example.com", + "POST", + "/users", + data=b"10", + content_type="application/json", + ) + with pytest.raises( + ValueError, + match="Unknown JSON Schema dialect: 'http://unsupported.dialect'", + ): + validator.validate(request) + + def test_valid_json_schema_dialect( + self, openapi_version: str, validator_cls: Type[Any] + ) -> None: + # Using draft-04 dialect + spec = _spec_dict( + openapi_version=openapi_version, + dialect="http://json-schema.org/draft-04/schema#", + ) + spec_path = SchemaPath.from_dict(spec) + + validator = validator_cls(spec_path) + request = MockRequest( + "http://example.com", + "POST", + "/users", + data=b"15", + content_type="application/json", + ) + validator.validate(request) + + def test_valid_json_schema_dialect_invalid_data( + self, openapi_version: str, validator_cls: Type[Any] + ) -> None: + # Using draft-04 dialect, where `exclusiveMinimum: true` makes 10 invalid + spec = _spec_dict( + openapi_version=openapi_version, + dialect="http://json-schema.org/draft-04/schema#", + ) + spec_path = SchemaPath.from_dict(spec) + + validator = validator_cls(spec_path) + request = MockRequest( + "http://example.com", + "POST", + "/users", + data=b"10", + content_type="application/json", + ) + with pytest.raises(InvalidRequestBody): + validator.validate(request) diff --git a/tests/unit/casting/test_schema_casters.py b/tests/unit/casting/test_schema_casters.py index 39c0235c..4e765cc8 100644 --- a/tests/unit/casting/test_schema_casters.py +++ b/tests/unit/casting/test_schema_casters.py @@ -7,9 +7,14 @@ class TestSchemaCaster: @pytest.fixture - def caster_factory(self): + def spec(self): + spec_dict = {} + return SchemaPath.from_dict(spec_dict) + + @pytest.fixture + def caster_factory(self, spec): def create_caster(schema): - return oas31_schema_casters_factory.create(schema) + return oas31_schema_casters_factory.create(spec, schema) return create_caster diff --git a/tests/unit/deserializing/test_media_types_deserializers.py b/tests/unit/deserializing/test_media_types_deserializers.py index 4ccc3a9d..47966213 100644 --- a/tests/unit/deserializing/test_media_types_deserializers.py +++ b/tests/unit/deserializing/test_media_types_deserializers.py @@ -16,7 +16,12 @@ class TestMediaTypeDeserializer: @pytest.fixture - def deserializer_factory(self): + def spec(self): + spec_dict = {} + return SchemaPath.from_dict(spec_dict) + + @pytest.fixture + def deserializer_factory(self, spec): def create_deserializer( mimetype, schema=None, @@ -31,6 +36,7 @@ def create_deserializer( oas31_schema_casters_factory, media_type_deserializers=media_type_deserializers, ).create( + spec, mimetype, schema=schema, schema_validator=schema_validator, @@ -483,7 +489,7 @@ def custom_deserializer(value): assert result == deserialized - def test_urlencoded_oneof_integer_field(self, deserializer_factory): + def test_urlencoded_oneof_integer_field(self, spec, deserializer_factory): """Test issue #932: oneOf with urlencoded should match schema with integer field""" mimetype = "application/x-www-form-urlencoded" schema_dict = { @@ -507,7 +513,7 @@ def test_urlencoded_oneof_integer_field(self, deserializer_factory): ] } schema = SchemaPath.from_dict(schema_dict) - schema_validator = oas31_schema_validators_factory.create(schema) + schema_validator = oas31_schema_validators_factory.create(spec, schema) deserializer = deserializer_factory( mimetype, schema=schema, schema_validator=schema_validator ) @@ -521,7 +527,7 @@ def test_urlencoded_oneof_integer_field(self, deserializer_factory): "fieldB": 123, } - def test_urlencoded_oneof_string_field(self, deserializer_factory): + def test_urlencoded_oneof_string_field(self, spec, deserializer_factory): """Test issue #932: oneOf with urlencoded should match schema with string fields""" mimetype = "application/x-www-form-urlencoded" schema_dict = { @@ -545,7 +551,7 @@ def test_urlencoded_oneof_string_field(self, deserializer_factory): ] } schema = SchemaPath.from_dict(schema_dict) - schema_validator = oas31_schema_validators_factory.create(schema) + schema_validator = oas31_schema_validators_factory.create(spec, schema) deserializer = deserializer_factory( mimetype, schema=schema, schema_validator=schema_validator ) @@ -558,7 +564,7 @@ def test_urlencoded_oneof_string_field(self, deserializer_factory): "fieldA": "value", } - def test_urlencoded_anyof_with_types(self, deserializer_factory): + def test_urlencoded_anyof_with_types(self, spec, deserializer_factory): """Test anyOf with urlencoded and type coercion""" mimetype = "application/x-www-form-urlencoded" schema_dict = { @@ -579,7 +585,7 @@ def test_urlencoded_anyof_with_types(self, deserializer_factory): ] } schema = SchemaPath.from_dict(schema_dict) - schema_validator = oas31_schema_validators_factory.create(schema) + schema_validator = oas31_schema_validators_factory.create(spec, schema) deserializer = deserializer_factory( mimetype, schema=schema, schema_validator=schema_validator ) @@ -594,7 +600,7 @@ def test_urlencoded_anyof_with_types(self, deserializer_factory): "name": "test", } - def test_urlencoded_oneof_boolean_field(self, deserializer_factory): + def test_urlencoded_oneof_boolean_field(self, spec, deserializer_factory): """Test oneOf with boolean field requiring type coercion""" mimetype = "application/x-www-form-urlencoded" schema_dict = { @@ -618,7 +624,7 @@ def test_urlencoded_oneof_boolean_field(self, deserializer_factory): ] } schema = SchemaPath.from_dict(schema_dict) - schema_validator = oas31_schema_validators_factory.create(schema) + schema_validator = oas31_schema_validators_factory.create(spec, schema) deserializer = deserializer_factory( mimetype, schema=schema, schema_validator=schema_validator ) diff --git a/tests/unit/deserializing/test_styles_deserializers.py b/tests/unit/deserializing/test_styles_deserializers.py index 8a3d4142..2262fcd5 100644 --- a/tests/unit/deserializing/test_styles_deserializers.py +++ b/tests/unit/deserializing/test_styles_deserializers.py @@ -13,7 +13,12 @@ class TestParameterStyleDeserializer: @pytest.fixture - def deserializer_factory(self): + def spec(self): + spec_dict = {} + return SchemaPath.from_dict(spec_dict) + + @pytest.fixture + def deserializer_factory(self, spec): style_deserializers_factory = StyleDeserializersFactory( oas31_schema_casters_factory, style_deserializers=style_deserializers, @@ -24,7 +29,7 @@ def create_deserializer(param, name=None): style, explode = get_style_and_explode(param) schema = param / "schema" return style_deserializers_factory.create( - style, explode, schema, name=name + spec, schema, style, explode, name=name ) return create_deserializer diff --git a/tests/unit/unmarshalling/test_schema_unmarshallers.py b/tests/unit/unmarshalling/test_schema_unmarshallers.py index 5a8fe12e..e86e06da 100644 --- a/tests/unit/unmarshalling/test_schema_unmarshallers.py +++ b/tests/unit/unmarshalling/test_schema_unmarshallers.py @@ -19,7 +19,13 @@ @pytest.fixture -def schema_unmarshaller_factory(): +def spec(): + spec_dict = {} + return SchemaPath.from_dict(spec_dict) + + +@pytest.fixture +def schema_unmarshaller_factory(spec): def create_unmarshaller( validators_factory, schema, @@ -31,6 +37,7 @@ def create_unmarshaller( validators_factory, oas30_types_unmarshaller, ).create( + spec, schema, format_validators=format_validators, extra_format_validators=extra_format_validators, diff --git a/tests/unit/validation/schemas/test_schemas_factories.py b/tests/unit/validation/schemas/test_schemas_factories.py new file mode 100644 index 00000000..896236d8 --- /dev/null +++ b/tests/unit/validation/schemas/test_schemas_factories.py @@ -0,0 +1,59 @@ +from typing import cast +from unittest.mock import patch + +from jsonschema._format import FormatChecker +from jsonschema.protocols import Validator + +from openapi_core.validation.schemas.factories import ( + DialectSchemaValidatorsFactory, +) + + +class MockValidator: + FORMAT_CHECKER = FormatChecker() + + +class TestDialectSchemaValidatorsFactoryCaching: + def test_get_validator_class_for_dialect_is_cached(self): + factory = DialectSchemaValidatorsFactory( + schema_validator_cls=cast(type[Validator], MockValidator), + default_jsonschema_dialect_id="http://json-schema.org/draft-04/schema#", + format_checker=FormatChecker(), + ) + + with patch( + "openapi_core.validation.schemas.factories.validator_for" + ) as mock_validator_for: + mock_validator_for.return_value = "MockedClass" + + # Call first time + result1 = factory._get_validator_class_for_dialect( + "http://json-schema.org/draft-04/schema#" + ) + + # Call second time with same dialect + result2 = factory._get_validator_class_for_dialect( + "http://json-schema.org/draft-04/schema#" + ) + + # Assert results are same + assert result1 == "MockedClass" + assert result2 == "MockedClass" + + # Assert `validator_for` was only called once because of cache + mock_validator_for.assert_called_once_with( + {"$schema": "http://json-schema.org/draft-04/schema#"}, + default=None, + ) + + # Let's also check with another dialect + with patch( + "openapi_core.validation.schemas.factories.validator_for" + ) as mock_validator_for2: + mock_validator_for2.return_value = "MockedClass2" + + result3 = factory._get_validator_class_for_dialect( + "https://json-schema.org/draft/2020-12/schema" + ) + assert result3 == "MockedClass2" + mock_validator_for2.assert_called_once() diff --git a/tests/unit/validation/test_schema_validators.py b/tests/unit/validation/test_schema_validators.py index 9b9c83a8..2dea1e10 100644 --- a/tests/unit/validation/test_schema_validators.py +++ b/tests/unit/validation/test_schema_validators.py @@ -9,9 +9,14 @@ class TestSchemaValidate: @pytest.fixture - def validator_factory(self): + def spec(self): + spec_dict = {} + return SchemaPath.from_dict(spec_dict) + + @pytest.fixture + def validator_factory(self, spec): def create_validator(schema): - return oas30_write_schema_validators_factory.create(schema) + return oas30_write_schema_validators_factory.create(spec, schema) return create_validator @@ -214,35 +219,35 @@ def test_number_multiple_of(self, value, validator_factory): assert result is None - def test_additional_properties_omitted_default_allows_extra(self): - schema = { + def test_additional_properties_omitted_default_allows_extra(self, spec): + schema_dict = { "type": "object", "properties": { "name": {"type": "string"}, }, "required": ["name"], } - spec = SchemaPath.from_dict(schema) + schema = SchemaPath.from_dict(schema_dict) value = { "name": "openapi-core", "extra": "allowed by default", } - result = oas30_write_schema_validators_factory.create(spec).validate( - value - ) + result = oas30_write_schema_validators_factory.create( + spec, schema + ).validate(value) assert result is None - def test_additional_properties_omitted_strict_rejects_extra(self): - schema = { + def test_additional_properties_omitted_strict_rejects_extra(self, spec): + schema_dict = { "type": "object", "properties": { "name": {"type": "string"}, }, "required": ["name"], } - spec = SchemaPath.from_dict(schema) + schema = SchemaPath.from_dict(schema_dict) value = { "name": "openapi-core", "extra": "not allowed in strict mode", @@ -251,11 +256,12 @@ def test_additional_properties_omitted_strict_rejects_extra(self): with pytest.raises(InvalidSchemaValue): oas30_write_schema_validators_factory.create( spec, + schema, forbid_unspecified_additional_properties=True, ).validate(value) - def test_additional_properties_true_strict_allows_extra(self): - schema = { + def test_additional_properties_true_strict_allows_extra(self, spec): + schema_dict = { "type": "object", "properties": { "name": {"type": "string"}, @@ -263,7 +269,7 @@ def test_additional_properties_true_strict_allows_extra(self): "required": ["name"], "additionalProperties": True, } - spec = SchemaPath.from_dict(schema) + schema = SchemaPath.from_dict(schema_dict) value = { "name": "openapi-core", "extra": "explicitly allowed", @@ -271,13 +277,14 @@ def test_additional_properties_true_strict_allows_extra(self): result = oas30_write_schema_validators_factory.create( spec, + schema, forbid_unspecified_additional_properties=True, ).validate(value) assert result is None - def test_enforce_properties_required_rejects_missing_property(self): - schema = { + def test_enforce_properties_required_rejects_missing_property(self, spec): + schema_dict = { "type": "object", "properties": { "name": {"type": "string"}, @@ -285,16 +292,17 @@ def test_enforce_properties_required_rejects_missing_property(self): }, "required": ["name"], } - spec = SchemaPath.from_dict(schema) + schema = SchemaPath.from_dict(schema_dict) with pytest.raises(InvalidSchemaValue): oas30_write_schema_validators_factory.create( spec, + schema, enforce_properties_required=True, ).validate({"name": "openapi-core"}) - def test_enforce_properties_required_ignores_write_only_fields(self): - schema = { + def test_enforce_properties_required_ignores_write_only_fields(self, spec): + schema_dict = { "type": "object", "properties": { "name": {"type": "string"}, @@ -305,10 +313,11 @@ def test_enforce_properties_required_ignores_write_only_fields(self): }, "required": ["name"], } - spec = SchemaPath.from_dict(schema) + schema = SchemaPath.from_dict(schema_dict) result = oas30_write_schema_validators_factory.create( spec, + schema, enforce_properties_required=True, ).validate({"name": "openapi-core"}) @@ -316,8 +325,9 @@ def test_enforce_properties_required_ignores_write_only_fields(self): def test_enforce_properties_required_applies_to_nested_composed_schemas( self, + spec, ): - schema = { + schema_dict = { "allOf": [ { "type": "object", @@ -338,10 +348,11 @@ def test_enforce_properties_required_applies_to_nested_composed_schemas( }, ] } - spec = SchemaPath.from_dict(schema) + schema = SchemaPath.from_dict(schema_dict) with pytest.raises(InvalidSchemaValue): oas30_write_schema_validators_factory.create( spec, + schema, enforce_properties_required=True, ).validate({"name": "openapi-core", "meta": {}})