From 5090a36147fbc6a21c701ecba243eb0d9d3de16b Mon Sep 17 00:00:00 2001 From: tarsil Date: Wed, 19 Jul 2023 17:54:32 +0100 Subject: [PATCH 1/6] =?UTF-8?q?=E2=9E=95=20Custom=20exception=20handler=20?= =?UTF-8?q?for=20Validation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixed validation error handler for pydantic 2.0 * Added custom value error handler * Added custom validation error for pydantic outside of handler. --- esmerald/exception_handlers.py | 59 +++++----- esmerald/transformers/datastructures.py | 3 +- esmerald/transformers/helpers.py | 31 ------ esmerald/transformers/signature.py | 4 - esmerald/transformers/types.py | 28 ----- tests/exception_handlers/__init__.py | 0 .../test_custom_exception_handlers.py | 105 ++++++++++++++++++ .../test_exception_handlers.py | 0 8 files changed, 137 insertions(+), 93 deletions(-) delete mode 100644 esmerald/transformers/helpers.py delete mode 100644 esmerald/transformers/types.py create mode 100644 tests/exception_handlers/__init__.py create mode 100644 tests/exception_handlers/test_custom_exception_handlers.py rename tests/{ => exception_handlers}/test_exception_handlers.py (100%) diff --git a/esmerald/exception_handlers.py b/esmerald/exception_handlers.py index b4d7f361..3dda08f1 100644 --- a/esmerald/exception_handlers.py +++ b/esmerald/exception_handlers.py @@ -1,6 +1,6 @@ -from typing import Any, List, Union +from typing import Union -from orjson import JSONDecodeError, loads +from orjson import loads from pydantic import ValidationError from starlette import status from starlette.exceptions import HTTPException as StarletteHTTPException @@ -38,27 +38,6 @@ async def http_exception_handler( return JSONResponse({"detail": exc.detail}, status_code=exc.status_code) -def parse_non_serializable_objects_from_validation_error(values: List[Any]) -> List[Any]: - """ - Parses non serializable objects from the validation error extras. - """ - details = [] - for detail in values: - detail_inputs = detail.get("input", None) - if not isinstance(detail_inputs, list): - details.append(detail) - continue - - inputs = [] - for input in detail_inputs: - if isinstance(input, object): - inputs.append(str(input.__class__.__name__)) - detail["input"] = inputs - details.append(detail) - - return details - - async def validation_error_exception_handler( request: Request, exc: ValidationError ) -> JSONResponse: @@ -66,14 +45,10 @@ async def validation_error_exception_handler( status_code = status.HTTP_400_BAD_REQUEST if extra: + breakpoint() errors_extra = exc.extra.get("extra", {}) - try: - details = loads(errors_extra) - except (TypeError, JSONDecodeError): - details = parse_non_serializable_objects_from_validation_error(errors_extra) - return JSONResponse( - {"detail": exc.detail, "errors": details}, + {"detail": exc.detail, "errors": errors_extra}, status_code=status_code, ) else: @@ -90,6 +65,9 @@ async def http_error_handler(_: Request, exc: ExceptionErrorMap) -> JSONResponse async def improperly_configured_exception_handler( request: Request, exc: ImproperlyConfigured ) -> StarletteResponse: + """ + When an ImproperlyConfiguredException is raised. + """ status_code = ( exc.status_code if isinstance(exc, StarletteHTTPException) @@ -109,3 +87,26 @@ async def improperly_configured_exception_handler( status_code=status_code, headers=headers, ) + + +async def pydantic_validation_error_handler( + request: Request, exc: ValidationError +) -> JSONResponse: + """ + This handler is to be used when a pydantic validation error is triggered during the logic + of a code block and not the definition of a handler. + + This is different from validation_error_exception_handler + """ + status_code = status.HTTP_422_UNPROCESSABLE_ENTITY + return JSONResponse({"detail": loads(exc.json())}, status_code=status_code) + + +async def value_error_handler(request: Request, exc: ValueError) -> JSONResponse: + """ + Simple handler that manages all the ValueError exceptions thrown to the user properly + formatted. + """ + status_code = status.HTTP_400_BAD_REQUEST + details = loads(exc.json()) if hasattr(exc, "json") else exc.args[0] + return JSONResponse({"detail": details}, status_code=status_code) diff --git a/esmerald/transformers/datastructures.py b/esmerald/transformers/datastructures.py index 13804b27..d25ee546 100644 --- a/esmerald/transformers/datastructures.py +++ b/esmerald/transformers/datastructures.py @@ -2,6 +2,7 @@ from inspect import Signature from typing import Any, ClassVar, Optional, Set, Union +from orjson import loads from pydantic import ValidationError from esmerald.exceptions import ImproperlyConfigured, InternalServerError, ValidationErrorException @@ -37,7 +38,7 @@ def build_exception( server_errors = [] client_errors = [] - for err in exception.errors(): + for err in loads(exception.json()): if not cls.is_server_error(err): client_errors.append(err) else: diff --git a/esmerald/transformers/helpers.py b/esmerald/transformers/helpers.py deleted file mode 100644 index f1d3f653..00000000 --- a/esmerald/transformers/helpers.py +++ /dev/null @@ -1,31 +0,0 @@ -import inspect -from typing import Any - -from pydantic.v1 import ( - ConstrainedBytes, - ConstrainedDate, - ConstrainedDecimal, - ConstrainedFloat, - ConstrainedFrozenSet, - ConstrainedInt, - ConstrainedList, - ConstrainedSet, - ConstrainedStr, -) - - -def is_pydantic_constrained_field(value: Any) -> Any: - return inspect.isclass(value) and any( - issubclass(value, _type) - for _type in ( - ConstrainedBytes, - ConstrainedDate, - ConstrainedDecimal, - ConstrainedFloat, - ConstrainedFrozenSet, - ConstrainedInt, - ConstrainedList, - ConstrainedSet, - ConstrainedStr, - ) - ) diff --git a/esmerald/transformers/signature.py b/esmerald/transformers/signature.py index 3d6c5259..e1982362 100644 --- a/esmerald/transformers/signature.py +++ b/esmerald/transformers/signature.py @@ -7,7 +7,6 @@ from esmerald.parsers import ArbitraryExtraBaseModel from esmerald.transformers.constants import CLASS_SPECIAL_WORDS, VALIDATION_NAMES from esmerald.transformers.datastructures import EsmeraldSignature, Parameter -from esmerald.transformers.helpers import is_pydantic_constrained_field from esmerald.transformers.utils import get_field_definition_from_param from esmerald.typing import Undefined from esmerald.utils.dependency import is_dependency_field, should_skip_dependency_validation @@ -75,9 +74,6 @@ def create_signature(self) -> Type[EsmeraldSignature]: if self.skip_parameter_validation(param): self.field_definitions[param.name] = (Any, ...) continue - if is_pydantic_constrained_field(param.default): - self.field_definitions[param.name] = (param.default, ...) - continue self.field_definitions[param.name] = get_field_definition_from_param(param) model: Type["EsmeraldSignature"] = create_model( diff --git a/esmerald/transformers/types.py b/esmerald/transformers/types.py deleted file mode 100644 index b30a68be..00000000 --- a/esmerald/transformers/types.py +++ /dev/null @@ -1,28 +0,0 @@ -from typing import Type, Union - -from pydantic.v1 import ( - ConstrainedBytes, - ConstrainedDate, - ConstrainedDecimal, - ConstrainedFloat, - ConstrainedFrozenSet, - ConstrainedInt, - ConstrainedList, - ConstrainedSet, - ConstrainedStr, -) -from typing_extensions import TypeGuard - -ConstrainedField = TypeGuard[ - Union[ - Type[ConstrainedBytes], - Type[ConstrainedDate], - Type[ConstrainedDecimal], - Type[ConstrainedFloat], - Type[ConstrainedFrozenSet], - Type[ConstrainedInt], - Type[ConstrainedList], - Type[ConstrainedSet], - Type[ConstrainedStr], - ] -] diff --git a/tests/exception_handlers/__init__.py b/tests/exception_handlers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/exception_handlers/test_custom_exception_handlers.py b/tests/exception_handlers/test_custom_exception_handlers.py new file mode 100644 index 00000000..3c52c73c --- /dev/null +++ b/tests/exception_handlers/test_custom_exception_handlers.py @@ -0,0 +1,105 @@ +from pydantic import BaseModel, ValidationError, field_validator + +from esmerald import JSON, Gateway, post, put +from esmerald.exception_handlers import pydantic_validation_error_handler, value_error_handler +from esmerald.testclient import create_client + + +class DataIn(BaseModel): + """ + Model example with DataIn for custom cases + and testing purposes. + """ + + name: str + email: str + + @field_validator("name") + def validate_name(cls, name: str) -> str: + raise ValueError(f"The name: {name} was successfully passed") + + +@post("/create") +async def create() -> JSON: + DataIn() + + +@post("/raise-error") +async def raised() -> JSON: + raise ValueError("Error raised here.") + + +@put("/update") +async def update() -> JSON: + DataIn(name="Esmerald", email="test@esmerald.dev") + + +def test_pydantic_validation_error_handler_return_500(test_client_factory): + with create_client( + routes=[Gateway(handler=create)], + ) as client: + response = client.post("/create") + assert response.status_code == 500 + + +def test_pydantic_validation_error_handler(test_client_factory): + with create_client( + routes=[Gateway(handler=create)], + exception_handlers={ValidationError: pydantic_validation_error_handler}, + ) as client: + response = client.post("/create") + assert response.status_code == 422 + + details = response.json()["detail"] + + assert len(details) == 2 + + for detail in details: + assert detail["type"] == "missing" + assert detail["msg"] == "Field required" + + locs = [detail["loc"][0] for detail in details] + + assert "name" in locs + assert "email" in locs + + +def test_value_error_handler_return_500(test_client_factory): + with create_client( + routes=[Gateway(handler=update)], + ) as client: + response = client.put("/update") + assert response.status_code == 500 + + +def test_value_error_handler(test_client_factory): + with create_client( + routes=[Gateway(handler=update)], + exception_handlers={ValueError: value_error_handler}, + ) as client: + response = client.put("/update") + assert response.status_code == 400 + + assert ( + response.json()["detail"][0]["msg"] + == "Value error, The name: Esmerald was successfully passed" + ) + + +def test_value_error_handler_return_500_on_function(test_client_factory): + with create_client( + routes=[Gateway(handler=raised)], + ) as client: + response = client.post("/raise-error") + assert response.status_code == 500 + + +def test_value_error_handler_simple(test_client_factory): + with create_client( + routes=[Gateway(handler=raised)], + exception_handlers={ValueError: value_error_handler}, + ) as client: + response = client.post("/raise-error") + assert response.status_code == 400 + + assert response.json()["detail"] == "Error raised here." diff --git a/tests/test_exception_handlers.py b/tests/exception_handlers/test_exception_handlers.py similarity index 100% rename from tests/test_exception_handlers.py rename to tests/exception_handlers/test_exception_handlers.py From f26f36a398b7ffb180d1b7123df3fc808b6f535f Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Wed, 19 Jul 2023 19:05:42 +0100 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=AA=9B=20Remove=20breakpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- esmerald/exception_handlers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/esmerald/exception_handlers.py b/esmerald/exception_handlers.py index 3dda08f1..64999626 100644 --- a/esmerald/exception_handlers.py +++ b/esmerald/exception_handlers.py @@ -45,7 +45,6 @@ async def validation_error_exception_handler( status_code = status.HTTP_400_BAD_REQUEST if extra: - breakpoint() errors_extra = exc.extra.get("extra", {}) return JSONResponse( {"detail": exc.detail, "errors": errors_extra}, From e89fa8bbc767caa6de57cada9c8e14db4a783b56 Mon Sep 17 00:00:00 2001 From: tarsil Date: Thu, 20 Jul 2023 10:11:26 +0100 Subject: [PATCH 3/6] =?UTF-8?q?=E2=9E=95=20Custom=20exception=20handler=20?= =?UTF-8?q?docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add a new default application exception handler --- docs/exception-handlers.md | 45 +++++++++++++++++++ docs_src/exception_handlers/example.py | 18 ++++++++ docs_src/exception_handlers/example_use.py | 24 ++++++++++ .../exception_handlers/example_use_gateway.py | 28 ++++++++++++ .../exception_handlers/example_use_handler.py | 26 +++++++++++ esmerald/applications.py | 5 ++- .../test_custom_exception_handlers.py | 27 ++--------- 7 files changed, 148 insertions(+), 25 deletions(-) create mode 100644 docs_src/exception_handlers/example.py create mode 100644 docs_src/exception_handlers/example_use.py create mode 100644 docs_src/exception_handlers/example_use_gateway.py create mode 100644 docs_src/exception_handlers/example_use_handler.py diff --git a/docs/exception-handlers.md b/docs/exception-handlers.md index 442ce567..f0886ce7 100644 --- a/docs/exception-handlers.md +++ b/docs/exception-handlers.md @@ -30,3 +30,48 @@ endpoint is called and the exception is raised. {! ../docs_src/_shared/exceptions.md !} The same is applied also to [dependencies](./dependencies.md). + + +### Custom exception handlers + +We all know that Esmerald handles really well the exceptions by design but sometimes we might also +want to throw an error while doing some code logic that is not directly related with a `data` of +an handler. + +For example. + +```python +{!> ../docs_src/exception_handlers/example.py !} +``` + +This example is a not usual at all but it serves to show where an exception is raised. + +Esmerald offers **one** out of the box **custom exception handlers**: + +* **value_error_handler** - When you want the `ValueError` exception to be automatically parsed +into a JSON. + +```python +from esmerald.exception_handlers import value_error_handler +``` + +How it would look like the previous example using this custom exception handler? + +```python hl_lines="21-23" +{!> ../docs_src/exception_handlers/example_use.py !} +``` + +Or if you prefer to place it on a Gateway level. + +```python hl_lines="22-25" +{!> ../docs_src/exception_handlers/example_use_gateway.py !} +``` + +Or even specific only to the handler itself. + +```python hl_lines="14-16" +{!> ../docs_src/exception_handlers/example_use_handler.py !} +``` + +As you can see, you can use this exception handler directly or as usual, you can create one of +your own and apply on every [application level](./application/levels.md). diff --git a/docs_src/exception_handlers/example.py b/docs_src/exception_handlers/example.py new file mode 100644 index 00000000..c5876ec5 --- /dev/null +++ b/docs_src/exception_handlers/example.py @@ -0,0 +1,18 @@ +from pydantic import BaseModel + +from esmerald import Esmerald, Gateway, JSONResponse, post + + +class DataIn(BaseModel): + id: int + name: str + + +@post("/create") +async def create(data: DataIn) -> JSONResponse: + # Simple validation to raise ValueError + if data.id > 20: + raise ValueError("The ID must be less than 20.") + + +app = Esmerald(routes=[Gateway(handler=create)]) diff --git a/docs_src/exception_handlers/example_use.py b/docs_src/exception_handlers/example_use.py new file mode 100644 index 00000000..34b308aa --- /dev/null +++ b/docs_src/exception_handlers/example_use.py @@ -0,0 +1,24 @@ +from pydantic import BaseModel, ValidationError + +from esmerald import Esmerald, Gateway, JSONResponse, post +from esmerald.exception_handlers import pydantic_validation_error_handler, value_error_handler + + +class DataIn(BaseModel): + id: int + name: str + + +@post("/create") +async def create(data: DataIn) -> JSONResponse: + # Simple validation to raise ValueError + if data.id > 20: + raise ValueError("The ID must be less than 20.") + + +app = Esmerald( + routes=[Gateway(handler=create)], + exception_handlers={ + ValueError: value_error_handler, + }, +) diff --git a/docs_src/exception_handlers/example_use_gateway.py b/docs_src/exception_handlers/example_use_gateway.py new file mode 100644 index 00000000..8e718805 --- /dev/null +++ b/docs_src/exception_handlers/example_use_gateway.py @@ -0,0 +1,28 @@ +from pydantic import BaseModel, ValidationError + +from esmerald import Esmerald, Gateway, JSONResponse, post +from esmerald.exception_handlers import pydantic_validation_error_handler, value_error_handler + + +class DataIn(BaseModel): + id: int + name: str + + +@post("/create") +async def create(data: DataIn) -> JSONResponse: + # Simple validation to raise ValueError + if data.id > 20: + raise ValueError("The ID must be less than 20.") + + +app = Esmerald( + routes=[ + Gateway( + handler=create, + exception_handlers={ + ValueError: value_error_handler, + }, + ) + ], +) diff --git a/docs_src/exception_handlers/example_use_handler.py b/docs_src/exception_handlers/example_use_handler.py new file mode 100644 index 00000000..183a26af --- /dev/null +++ b/docs_src/exception_handlers/example_use_handler.py @@ -0,0 +1,26 @@ +from pydantic import BaseModel, ValidationError + +from esmerald import Esmerald, Gateway, JSONResponse, post +from esmerald.exception_handlers import pydantic_validation_error_handler, value_error_handler + + +class DataIn(BaseModel): + id: int + name: str + + +@post( + "/create", + exception_handlers={ + ValueError: value_error_handler, + }, +) +async def create(data: DataIn) -> JSONResponse: + # Simple validation to raise ValueError + if data.id > 20: + raise ValueError("The ID must be less than 20.") + + +app = Esmerald( + routes=[Gateway(handler=create)], +) diff --git a/esmerald/applications.py b/esmerald/applications.py index 4f7cedd6..90fe96bb 100644 --- a/esmerald/applications.py +++ b/esmerald/applications.py @@ -15,7 +15,7 @@ from openapi_schemas_pydantic.v3_1_0 import Contact, License, SecurityScheme, Tag from openapi_schemas_pydantic.v3_1_0.open_api import OpenAPI -from pydantic import AnyUrl +from pydantic import AnyUrl, ValidationError from starlette.applications import Starlette from starlette.middleware import Middleware as StarletteMiddleware # noqa from starlette.types import Lifespan, Receive, Scope, Send @@ -28,6 +28,7 @@ from esmerald.datastructures import State from esmerald.exception_handlers import ( improperly_configured_exception_handler, + pydantic_validation_error_handler, validation_error_exception_handler, ) from esmerald.exceptions import ImproperlyConfigured, ValidationErrorException @@ -775,6 +776,8 @@ def get_default_exception_handlers(self) -> None: ValidationErrorException, validation_error_exception_handler ) + self.exception_handlers.setdefault(ValidationError, pydantic_validation_error_handler) + def build_routes_middleware( self, route: "RouteParent", middlewares: Optional[List["Middleware"]] = None ) -> List["Middleware"]: diff --git a/tests/exception_handlers/test_custom_exception_handlers.py b/tests/exception_handlers/test_custom_exception_handlers.py index 3c52c73c..76a2b48b 100644 --- a/tests/exception_handlers/test_custom_exception_handlers.py +++ b/tests/exception_handlers/test_custom_exception_handlers.py @@ -34,12 +34,12 @@ async def update() -> JSON: DataIn(name="Esmerald", email="test@esmerald.dev") -def test_pydantic_validation_error_handler_return_500(test_client_factory): +def test_pydantic_validation_error_handler_return_422(test_client_factory): with create_client( routes=[Gateway(handler=create)], ) as client: response = client.post("/create") - assert response.status_code == 500 + assert response.status_code == 422 def test_pydantic_validation_error_handler(test_client_factory): @@ -64,33 +64,12 @@ def test_pydantic_validation_error_handler(test_client_factory): assert "email" in locs -def test_value_error_handler_return_500(test_client_factory): - with create_client( - routes=[Gateway(handler=update)], - ) as client: - response = client.put("/update") - assert response.status_code == 500 - - -def test_value_error_handler(test_client_factory): - with create_client( - routes=[Gateway(handler=update)], - exception_handlers={ValueError: value_error_handler}, - ) as client: - response = client.put("/update") - assert response.status_code == 400 - - assert ( - response.json()["detail"][0]["msg"] - == "Value error, The name: Esmerald was successfully passed" - ) - - def test_value_error_handler_return_500_on_function(test_client_factory): with create_client( routes=[Gateway(handler=raised)], ) as client: response = client.post("/raise-error") + assert response.status_code == 500 From 1b53922d4689654e66d827952de43075410a58c6 Mon Sep 17 00:00:00 2001 From: tarsil Date: Thu, 20 Jul 2023 10:13:30 +0100 Subject: [PATCH 4/6] Re-activate tests for apiviews --- tests/{_test_apiviews.py => test_apiviews.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{_test_apiviews.py => test_apiviews.py} (100%) diff --git a/tests/_test_apiviews.py b/tests/test_apiviews.py similarity index 100% rename from tests/_test_apiviews.py rename to tests/test_apiviews.py From 26a2508a245e3280f562f223e74eb973084910a1 Mon Sep 17 00:00:00 2001 From: tarsil Date: Thu, 20 Jul 2023 10:24:17 +0100 Subject: [PATCH 5/6] Remove deprecated pydantic syntax --- esmerald/applications.py | 6 ++++-- esmerald/middleware/exceptions.py | 2 +- esmerald/routing/base.py | 4 +++- esmerald/security/jwt/token.py | 2 +- tests/handlers/test_to_response.py | 4 ++-- tests/middleware/test_wsgi_middleware.py | 3 ++- tests/test_apiviews.py | 8 ++++---- tests/test_settings.py | 14 +++++++------- 8 files changed, 24 insertions(+), 19 deletions(-) diff --git a/esmerald/applications.py b/esmerald/applications.py index 90fe96bb..36915772 100644 --- a/esmerald/applications.py +++ b/esmerald/applications.py @@ -849,13 +849,15 @@ def build_user_middleware_stack(self) -> List["StarletteMiddleware"]: StarletteMiddleware(TrustedHostMiddleware, allowed_hosts=self.allowed_hosts) ) if self.cors_config: - user_middleware.append(StarletteMiddleware(CORSMiddleware, **self.cors_config.dict())) + user_middleware.append( + StarletteMiddleware(CORSMiddleware, **self.cors_config.model_dump()) + ) if self.csrf_config: user_middleware.append(StarletteMiddleware(CSRFMiddleware, config=self.csrf_config)) if self.session_config: user_middleware.append( - StarletteMiddleware(SessionMiddleware, **self.session_config.dict()) + StarletteMiddleware(SessionMiddleware, **self.session_config.model_dump()) ) handlers_middleware += self.router.middleware diff --git a/esmerald/middleware/exceptions.py b/esmerald/middleware/exceptions.py index e66066d2..b158e33f 100644 --- a/esmerald/middleware/exceptions.py +++ b/esmerald/middleware/exceptions.py @@ -132,7 +132,7 @@ def create_exception_response(self, exc: Exception) -> Response: content = ResponseContent(detail=repr(exc)) return Response( media_type=MediaType.JSON, - content=content.dict(exclude_none=True), + content=content.model_dump(exclude_none=True), status_code=content.status_code, headers=exc.headers if isinstance(exc, (HTTPException, StarletteHTTPException)) diff --git a/esmerald/routing/base.py b/esmerald/routing/base.py index c9883072..d3ee55b3 100644 --- a/esmerald/routing/base.py +++ b/esmerald/routing/base.py @@ -570,7 +570,9 @@ def get_cookies( filtered_cookies.append(cookie) normalized_cookies: List[Dict[str, Any]] = [] for cookie in filtered_cookies: - normalized_cookies.append(cookie.dict(exclude_none=True, exclude={"description"})) + normalized_cookies.append( + cookie.model_dump(exclude_none=True, exclude={"description"}) + ) return normalized_cookies def get_headers(self, headers: "ResponseHeaders") -> Dict[str, Any]: diff --git a/esmerald/security/jwt/token.py b/esmerald/security/jwt/token.py index f61e0fee..8741e76e 100644 --- a/esmerald/security/jwt/token.py +++ b/esmerald/security/jwt/token.py @@ -52,7 +52,7 @@ def encode(self, key: str, algorithm: str) -> Union[str, Any]: """ try: return jwt.encode( - claims=self.dict(exclude_none=True), + claims=self.model_dump(exclude_none=True), key=key, algorithm=algorithm, ) diff --git a/tests/handlers/test_to_response.py b/tests/handlers/test_to_response.py index 0383093a..353d9bf3 100644 --- a/tests/handlers/test_to_response.py +++ b/tests/handlers/test_to_response.py @@ -102,7 +102,7 @@ async def test_function(data: Individual) -> Individual: response = await test_function.to_response( data=test_function.fn(data=person_instance), app=None # type: ignore ) - assert loads(response.body) == person_instance.dict() + assert loads(response.body) == person_instance.model_dump() @pytest.mark.asyncio() @@ -120,7 +120,7 @@ async def test_function(data: Individual) -> Individual: response = await test_function.to_response( data=test_function.fn(data=person_instance), app=None # type: ignore ) - assert loads(response.body) == person_instance.dict() + assert loads(response.body) == person_instance.model_dump() @pytest.mark.asyncio() diff --git a/tests/middleware/test_wsgi_middleware.py b/tests/middleware/test_wsgi_middleware.py index 86ebd51a..819b77c4 100644 --- a/tests/middleware/test_wsgi_middleware.py +++ b/tests/middleware/test_wsgi_middleware.py @@ -1,4 +1,5 @@ -from flask import Flask, escape, request +from flask import Flask, request +from markupsafe import escape from esmerald import Esmerald, Gateway, Include, Request, get from esmerald.middleware import WSGIMiddleware diff --git a/tests/test_apiviews.py b/tests/test_apiviews.py index 820024ff..9c1aa1e3 100644 --- a/tests/test_apiviews.py +++ b/tests/test_apiviews.py @@ -57,7 +57,7 @@ def test_method(self) -> return_annotation: # type: ignore[valid-type] assert response.status_code == expected_status_code if return_value: assert ( - response.json() == return_value.dict() + response.json() == return_value.model_dump() if isinstance(return_value, BaseModel) else return_value ) @@ -107,7 +107,7 @@ def test_method(self) -> return_annotation: # type: ignore[valid-type] assert response.status_code == expected_status_code if return_value: assert ( - response.json() == return_value.dict() + response.json() == return_value.model_dump() if isinstance(return_value, BaseModel) else return_value ) @@ -162,7 +162,7 @@ def test_method(self) -> return_annotation: # type: ignore[valid-type] assert response.status_code == expected_status_code if return_value: assert ( - response.json() == return_value.dict() + response.json() == return_value.model_dump() if isinstance(return_value, BaseModel) else return_value ) @@ -272,7 +272,7 @@ def test_method(self) -> return_annotation: # type: ignore[valid-type] assert response.status_code == expected_status_code if return_value: assert ( - response.json() == return_value.dict() + response.json() == return_value.model_dump() if isinstance(return_value, BaseModel) else return_value ) diff --git a/tests/test_settings.py b/tests/test_settings.py index afe5476f..82a40d10 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -103,8 +103,8 @@ def test_settings_global(test_client_factory): assert client.app.app_name == "my app" assert settings.app_name == "test_client" - assert request_settings.json()["global_settings"] == "test_client" - assert app_settings.json() == "test_client" + assert request_settings.model_dump_json()["global_settings"] == "test_client" + assert app_settings.model_dump_json() == "test_client" def test_settings_global_without_parameters(test_client_factory): @@ -117,8 +117,8 @@ def test_settings_global_without_parameters(test_client_factory): assert settings.app_name == "test_client" assert client.app.app_name == "test_client" - assert request_settings.json()["global_settings"] == "test_client" - assert app_settings.json() == "test_client" + assert request_settings.model_dump_json()["global_settings"] == "test_client" + assert app_settings.model_dump_json() == "test_client" def test_inner_settings_config(test_client_factory): @@ -162,7 +162,7 @@ class ChildSettings(DisableOpenAPI): @get("/app-settings") async def _app_settings(request: Request) -> Dict[Any, Any]: - return request.app_settings.json() + return request.app_settings.model_dump_json() child = ChildEsmerald( routes=[Gateway(handler=_app_settings)], @@ -198,7 +198,7 @@ def cors_config(self) -> CORSConfig: @get("/app-settings") async def _app_settings(request: Request) -> Dict[Any, Any]: - return request.app_settings.json() + return request.app_settings.model_dump_json() secret = get_random_secret_key() child = ChildEsmerald( @@ -234,7 +234,7 @@ class ChildSettings(DisableOpenAPI): @get("/app-settings") async def _app_settings(request: Request) -> Dict[Any, Any]: - return request.app_settings.json() + return request.app_settings.model_dump_json() child = ChildEsmerald( routes=[Gateway(handler=_app_settings)], From e6863805f057e85b8b1f67aa5c144d32d616c1dc Mon Sep 17 00:00:00 2001 From: tarsil Date: Thu, 20 Jul 2023 10:31:01 +0100 Subject: [PATCH 6/6] Fix tests --- tests/test_settings.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_settings.py b/tests/test_settings.py index 82a40d10..f26cb3e6 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -103,8 +103,8 @@ def test_settings_global(test_client_factory): assert client.app.app_name == "my app" assert settings.app_name == "test_client" - assert request_settings.model_dump_json()["global_settings"] == "test_client" - assert app_settings.model_dump_json() == "test_client" + assert request_settings.json()["global_settings"] == "test_client" + assert app_settings.json() == "test_client" def test_settings_global_without_parameters(test_client_factory): @@ -117,8 +117,8 @@ def test_settings_global_without_parameters(test_client_factory): assert settings.app_name == "test_client" assert client.app.app_name == "test_client" - assert request_settings.model_dump_json()["global_settings"] == "test_client" - assert app_settings.model_dump_json() == "test_client" + assert request_settings.json()["global_settings"] == "test_client" + assert app_settings.json() == "test_client" def test_inner_settings_config(test_client_factory):