From 11ad4b949a362a7a65f53233a35a483d44423b64 Mon Sep 17 00:00:00 2001 From: tarsil Date: Thu, 20 Feb 2025 18:05:50 +0100 Subject: [PATCH] Fix override settings and prepare release --- docs/en/docs/release-notes.md | 4 +++ esmerald/__init__.py | 2 +- esmerald/testclient.py | 52 +++++++++++++++++++++++++++------ pyproject.toml | 4 +-- tests/test_override_settings.py | 36 +++++++++++++++++++++++ 5 files changed, 86 insertions(+), 12 deletions(-) create mode 100644 tests/test_override_settings.py diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 6573874a..45ea10ce 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -18,6 +18,10 @@ for consistency reasons of the framework. - Reverse order on Gateway `after_request`. +### Fixed + +- `override_settings` was not taking into account async functions. + ### 3.6.6 ### Added diff --git a/esmerald/__init__.py b/esmerald/__init__.py index c178e23d..936e4927 100644 --- a/esmerald/__init__.py +++ b/esmerald/__init__.py @@ -1,4 +1,4 @@ -__version__ = "3.6.6" +__version__ = "3.6.7" from lilya import status diff --git a/esmerald/testclient.py b/esmerald/testclient.py index f311bddc..dc2f3ba0 100644 --- a/esmerald/testclient.py +++ b/esmerald/testclient.py @@ -1,3 +1,4 @@ +import asyncio import sys from functools import wraps from typing import ( @@ -20,7 +21,6 @@ from esmerald.applications import Esmerald from esmerald.conf import settings -from esmerald.conf.global_settings import EsmeraldAPISettings as Settings from esmerald.contrib.schedulers import SchedulerConfig from esmerald.encoders import Encoder from esmerald.openapi.schemas.v3_1_0 import Contact, License, SecurityScheme @@ -203,7 +203,7 @@ def create_client( class override_settings: """ - A context manager that allows overriding Esmerald settings temporarily. + A context manager that allows overriding Lilya settings temporarily. Usage: ``` @@ -233,6 +233,30 @@ def __init__(self, **kwargs: Any) -> None: """ self.app = kwargs.pop("app", None) self.options = kwargs + self._original_settings = None + + async def __aenter__(self) -> None: + """ + Enter the context manager and set the modified settings. + + Saves the original settings and sets the modified settings + based on the provided options. + + Returns: + None + """ + self.__enter__() + + async def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: + """ + Restores the original settings and sets them up again. + + Args: + exc_type (Any): The type of the exception raised, if any. + exc_value (Any): The exception instance raised, if any. + traceback (Any): The traceback for the exception raised, if any. + """ + self.__exit__(exc_type, exc_value, traceback) def __enter__(self) -> None: """ @@ -244,8 +268,9 @@ def __enter__(self) -> None: Returns: None """ + settings._setup() self._original_settings = settings._wrapped - settings._wrapped = Settings(settings._wrapped, **self.options) # type: ignore + settings._wrapped = self._original_settings.__class__(settings._wrapped, **self.options) set_override_settings(True) def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: @@ -261,7 +286,7 @@ def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: settings._setup() set_override_settings(False) - def __call__(self, test_func: Any) -> Any: + def __call__(self, func: Callable[..., Any]) -> Callable[..., Any]: """ Decorator that wraps a test function and executes it within a context manager. @@ -272,10 +297,19 @@ def __call__(self, test_func: Any) -> Any: Any: The result of the test function. """ + if asyncio.iscoroutinefunction(func): + + @wraps(func) + async def async_wrapper(*args: Any, **kwargs: Any) -> Any: + async with self: + return await func(*args, **kwargs) + + return async_wrapper + else: - @wraps(test_func) - def inner(*args: Any, **kwargs: Any) -> Any: - with self: - return test_func(*args, **kwargs) + @wraps(func) + def sync_wrapper(*args: Any, **kwargs: Any) -> Any: + with self: + return func(*args, **kwargs) - return inner + return sync_wrapper diff --git a/pyproject.toml b/pyproject.toml index 5bd74242..7422fd90 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ dependencies = [ "email-validator >=2.2.0,<3.0.0", "itsdangerous>=2.1.2,<3.0.0", "jinja2>=3.1.2,<4.0.0", - "lilya>=0.12.9", + "lilya>=0.12.10", "loguru>=0.7.0,<0.8.0", "pydantic>=2.10,<3.0.0", "pydantic-settings>=2.0.0,<3.0.0", @@ -127,7 +127,7 @@ docs = [ "a2wsgi>=1.9.0", "griffe-typingdoc>=0.2.2", "httpx>=0.25.0", - "lilya>=0.12.9", + "lilya>=0.12.10", "mkautodoc>=0.2.0", "mkdocs>=1.6.0", "mkdocs-material>=9.5.25", diff --git a/tests/test_override_settings.py b/tests/test_override_settings.py new file mode 100644 index 00000000..7328aabd --- /dev/null +++ b/tests/test_override_settings.py @@ -0,0 +1,36 @@ +import pytest + +from esmerald.conf import settings +from esmerald.testclient import override_settings + + +@override_settings(environment="test_func") +def test_can_override_settings(): + assert settings.environment == "test_func" + + +@override_settings(environment="test_func") +def test_name_of_settings(): + assert settings.__class__.__name__ == "TestSettings" + + +class TestInClass: + @override_settings(environment="test_func") + def test_can_override_settings(self): + assert settings.environment == "test_func" + + @override_settings(environment="test_func") + def test_name_of_settings(self): + assert settings.__class__.__name__ == "TestSettings" + + +class TestInClassAsync: + @override_settings(environment="test_func") + @pytest.mark.asyncio + async def test_can_override_settings(self, test_client_factory): + assert settings.environment == "test_func" + + @override_settings(environment="test_func") + @pytest.mark.asyncio + async def test_name_of_settings(self, test_client_factory): + assert settings.__class__.__name__ == "TestSettings"