Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix override settings and prepare release #496

Merged
merged 1 commit into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/en/docs/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion esmerald/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "3.6.6"
__version__ = "3.6.7"


from lilya import status
Expand Down
52 changes: 43 additions & 9 deletions esmerald/testclient.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import asyncio
import sys
from functools import wraps
from typing import (
Expand All @@ -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
Expand Down Expand Up @@ -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:
```
Expand Down Expand Up @@ -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:
"""
Expand All @@ -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:
Expand All @@ -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.

Expand All @@ -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
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
36 changes: 36 additions & 0 deletions tests/test_override_settings.py
Original file line number Diff line number Diff line change
@@ -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"