From bc25733766b9cf0938cdf07a86b2d3e49dd98b4d Mon Sep 17 00:00:00 2001 From: vyuroshchin Date: Mon, 10 Mar 2025 01:14:59 +0300 Subject: [PATCH 1/2] improve logging in shortcuts.py and turn to pytest in test_shortcut.py --- docs/index.rst | 2 +- openapi_schema_validator/_keywords.py | 5 +-- openapi_schema_validator/shortcuts.py | 25 +++++++++++---- openapi_schema_validator/validators.py | 4 --- tests/unit/test_shortcut.py | 42 +++++++++++++++++--------- 5 files changed, 48 insertions(+), 30 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 1b9b6fe..f89daba 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,7 +20,7 @@ Installation .. md-tab-set:: - .. md-tab-item:: Pip + PyPI (recommented) + .. md-tab-item:: Pip + PyPI (recommended) .. code-block:: console diff --git a/openapi_schema_validator/_keywords.py b/openapi_schema_validator/_keywords.py index 6692580..a5de04e 100644 --- a/openapi_schema_validator/_keywords.py +++ b/openapi_schema_validator/_keywords.py @@ -1,8 +1,5 @@ -from copy import deepcopy from typing import Any -from typing import Dict from typing import Hashable -from typing import ItemsView from typing import Iterator from typing import List from typing import Mapping @@ -112,7 +109,7 @@ def type( # * nullable: true is only meaningful in combination with a type # assertion specified in the same Schema Object. # * nullable: true operates within a single Schema Object - if "nullable" in schema and schema["nullable"] == True: + if "nullable" in schema and schema["nullable"] is True: return yield ValidationError("None for not nullable") diff --git a/openapi_schema_validator/shortcuts.py b/openapi_schema_validator/shortcuts.py index d922bb8..a826d4e 100644 --- a/openapi_schema_validator/shortcuts.py +++ b/openapi_schema_validator/shortcuts.py @@ -1,7 +1,4 @@ -from typing import Any -from typing import Hashable -from typing import Mapping -from typing import Type +from typing import Any, Hashable, Mapping, Type from jsonschema.exceptions import best_match from jsonschema.protocols import Validator @@ -16,8 +13,24 @@ def validate( *args: Any, **kwargs: Any ) -> None: + """ + Validate an instance against a given schema using the specified validator class + + Args: + instance: The instance to validate. + schema: The schema to validate against + cls: The validator class to use (defaults to OAS31Validator) + *args: Additional positional arguments to pass to the validator + **kwargs: Additional keyword arguments to pass to the validator + + Raises: + jsonschema.exceptions.ValidationError: If the instance is invalid according to the schema + """ cls.check_schema(schema) validator = cls(schema, *args, **kwargs) - error = best_match(validator.evolve(schema=schema).iter_errors(instance)) - if error is not None: + errors = list(validator.evolve(schema=schema).iter_errors(instance)) + + if errors: + error = best_match(errors) + error.message = f"Validation failed: {error.message}" raise error diff --git a/openapi_schema_validator/validators.py b/openapi_schema_validator/validators.py index 44b5a42..8c5195e 100644 --- a/openapi_schema_validator/validators.py +++ b/openapi_schema_validator/validators.py @@ -1,7 +1,3 @@ -import warnings -from typing import Any -from typing import Type - from jsonschema import _keywords from jsonschema import _legacy_keywords from jsonschema.validators import Draft202012Validator diff --git a/tests/unit/test_shortcut.py b/tests/unit/test_shortcut.py index 0ec80c7..0ceaa3e 100644 --- a/tests/unit/test_shortcut.py +++ b/tests/unit/test_shortcut.py @@ -1,21 +1,33 @@ -from unittest import TestCase - +import pytest from openapi_schema_validator import validate -class ValidateTest(TestCase): - def test_validate_does_not_mutate_schema_adding_nullable_key(self): - schema = { - "type": "object", - "properties": { - "email": {"type": "string"}, - "enabled": { - "type": "boolean", - }, +@pytest.fixture(scope="function") +def schema(): + return { + "type": "object", + "properties": { + "email": {"type": "string"}, + "enabled": { + "type": "boolean", }, - "example": {"enabled": False, "email": "foo@bar.com"}, - } + }, + "example": {"enabled": False, "email": "foo@bar.com"}, + } + + +def test_validate_does_not_add_nullable_to_schema(schema): + """ + Verify that calling validate does not add the 'nullable' key to the schema + """ + validate({"email": "foo@bar.com"}, schema) + assert "nullable" not in schema["properties"]["email"].keys() - validate({"email": "foo@bar.com"}, schema) - self.assertTrue("nullable" not in schema["properties"]["email"].keys()) +def test_validate_does_not_mutate_schema(schema): + """ + Verify that calling validate does not mutate the schema + """ + original_schema = schema.copy() + validate({"email": "foo@bar.com"}, schema) + assert schema == original_schema From ef2ba4a46d5e9b55330bc61146582756d4ecd9ae Mon Sep 17 00:00:00 2001 From: vyuroshchin Date: Sat, 22 Mar 2025 18:43:28 +0300 Subject: [PATCH 2/2] fix review --- openapi_schema_validator/shortcuts.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openapi_schema_validator/shortcuts.py b/openapi_schema_validator/shortcuts.py index a826d4e..fee0196 100644 --- a/openapi_schema_validator/shortcuts.py +++ b/openapi_schema_validator/shortcuts.py @@ -1,4 +1,7 @@ -from typing import Any, Hashable, Mapping, Type +from typing import Any +from typing import Hashable +from typing import Mapping +from typing import Type from jsonschema.exceptions import best_match from jsonschema.protocols import Validator @@ -28,9 +31,6 @@ def validate( """ cls.check_schema(schema) validator = cls(schema, *args, **kwargs) - errors = list(validator.evolve(schema=schema).iter_errors(instance)) - - if errors: - error = best_match(errors) - error.message = f"Validation failed: {error.message}" + error = best_match(validator.evolve(schema=schema).iter_errors(instance)) + if error is not None: raise error