diff --git a/.gitignore b/.gitignore index 6d4a42fa..502fcebc 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ build/* .DS_Store dist/* pip-wheel-metadata/ +.pytest_cache/* +.venv/* +.vscode/* diff --git a/sanic/app.py b/sanic/app.py index 3b3a45cf..5d97c9ce 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -43,7 +43,7 @@ from sanic.asgi import ASGIApp from sanic.base import BaseSanic from sanic.blueprint_group import BlueprintGroup from sanic.blueprints import Blueprint -from sanic.config import BASE_LOGO, Config +from sanic.config import BASE_LOGO, SANIC_PREFIX, Config from sanic.exceptions import ( InvalidUsage, SanicException, @@ -125,7 +125,8 @@ class Sanic(BaseSanic): router: Optional[Router] = None, signal_router: Optional[SignalRouter] = None, error_handler: Optional[ErrorHandler] = None, - load_env: bool = True, + load_env: Union[bool, str] = True, + env_prefix: Optional[str] = SANIC_PREFIX, request_class: Optional[Type[Request]] = None, strict_slashes: bool = False, log_config: Optional[Dict[str, Any]] = None, @@ -150,7 +151,7 @@ class Sanic(BaseSanic): self._test_manager = None self.asgi = False self.blueprints: Dict[str, Blueprint] = {} - self.config = Config(load_env=load_env) + self.config = Config(load_env=load_env, env_prefix=env_prefix) self.configure_logging = configure_logging self.ctx = SimpleNamespace() self.debug = None diff --git a/sanic/config.py b/sanic/config.py index 80402c24..c8121da9 100644 --- a/sanic/config.py +++ b/sanic/config.py @@ -1,7 +1,8 @@ from inspect import isclass from os import environ from pathlib import Path -from typing import Any, Union +from typing import Any, Dict, Optional, Union +from warnings import warn from .utils import load_module_from_file_location, str_to_bool @@ -41,7 +42,13 @@ DEFAULT_CONFIG = { class Config(dict): - def __init__(self, defaults=None, load_env=True, keep_alive=None): + def __init__( + self, + defaults: Dict[str, Union[str, bool, int, float, None]] = None, + load_env: Optional[Union[bool, str]] = True, + env_prefix: Optional[str] = SANIC_PREFIX, + keep_alive: Optional[int] = None, + ): defaults = defaults or {} super().__init__({**DEFAULT_CONFIG, **defaults}) @@ -50,9 +57,20 @@ class Config(dict): if keep_alive is not None: self.KEEP_ALIVE = keep_alive - if load_env: - prefix = SANIC_PREFIX if load_env is True else load_env - self.load_environment_vars(prefix=prefix) + if env_prefix != SANIC_PREFIX: + if env_prefix: + self.load_environment_vars(env_prefix) + elif load_env is not True: + if load_env: + self.load_environment_vars(prefix=load_env) + warn( + "Use of load_env is deprecated and will be removed in " + "21.12. Modify the configuration prefix by passing " + "env_prefix instead.", + DeprecationWarning, + ) + else: + self.load_environment_vars(SANIC_PREFIX) def __getattr__(self, attr): try: diff --git a/tests/test_config.py b/tests/test_config.py index 2a54aa9f..b1336497 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -59,14 +59,14 @@ def test_load_from_object_string_exception(app): app.config.load("test_config.Config.test") -def test_auto_load_env(): +def test_auto_env_prefix(): environ["SANIC_TEST_ANSWER"] = "42" app = Sanic(name=__name__) assert app.config.TEST_ANSWER == 42 del environ["SANIC_TEST_ANSWER"] -def test_auto_load_bool_env(): +def test_auto_bool_env_prefix(): environ["SANIC_TEST_ANSWER"] = "True" app = Sanic(name=__name__) assert app.config.TEST_ANSWER is True @@ -80,6 +80,12 @@ def test_dont_load_env(): del environ["SANIC_TEST_ANSWER"] +@pytest.mark.parametrize('load_env', [None, False, "", "MYAPP_"]) +def test_load_env_deprecation(load_env): + with pytest.warns(DeprecationWarning, match=r"21\.12"): + _ = Sanic(name=__name__, load_env=load_env) + + def test_load_env_prefix(): environ["MYAPP_TEST_ANSWER"] = "42" app = Sanic(name=__name__, load_env="MYAPP_") @@ -87,6 +93,14 @@ def test_load_env_prefix(): del environ["MYAPP_TEST_ANSWER"] +@pytest.mark.parametrize('env_prefix', [None, ""]) +def test_empty_load_env_prefix(env_prefix): + environ["SANIC_TEST_ANSWER"] = "42" + app = Sanic(name=__name__, env_prefix=env_prefix) + assert getattr(app.config, "TEST_ANSWER", None) is None + del environ["SANIC_TEST_ANSWER"] + + def test_load_env_prefix_float_values(): environ["MYAPP_TEST_ROI"] = "2.3" app = Sanic(name=__name__, load_env="MYAPP_") @@ -101,6 +115,27 @@ def test_load_env_prefix_string_value(): del environ["MYAPP_TEST_TOKEN"] +def test_env_prefix(): + environ["MYAPP_TEST_ANSWER"] = "42" + app = Sanic(name=__name__, env_prefix="MYAPP_") + assert app.config.TEST_ANSWER == 42 + del environ["MYAPP_TEST_ANSWER"] + + +def test_env_prefix_float_values(): + environ["MYAPP_TEST_ROI"] = "2.3" + app = Sanic(name=__name__, env_prefix="MYAPP_") + assert app.config.TEST_ROI == 2.3 + del environ["MYAPP_TEST_ROI"] + + +def test_env_prefix_string_value(): + environ["MYAPP_TEST_TOKEN"] = "somerandomtesttoken" + app = Sanic(name=__name__, env_prefix="MYAPP_") + assert app.config.TEST_TOKEN == "somerandomtesttoken" + del environ["MYAPP_TEST_TOKEN"] + + def test_load_from_file(app): config = dedent( """ diff --git a/tests/test_keep_alive_timeout.py b/tests/test_keep_alive_timeout.py index 6f88abd2..e777de2e 100644 --- a/tests/test_keep_alive_timeout.py +++ b/tests/test_keep_alive_timeout.py @@ -1,4 +1,5 @@ import asyncio +import platform from asyncio import sleep as aio_sleep from json import JSONDecodeError @@ -6,7 +7,6 @@ from os import environ import httpcore import httpx -import platform import pytest from sanic_testing.testing import HOST, SanicTestClient