Fix type hinting for load_env (#2107)

* Deprecate `load_env` in favor of `env_prefix`

`load_env` both enabled/disabled environment variable parsing, while
also letting the user modify the env prefix. Deprecate the ability to
disable environment parsing, and add a new config variable to track the
user's desired prefix for environment-based configuration.

Resolves: #2102

* Add a few common .gitignore patterns
This commit is contained in:
Johnathan Raymond 2021-04-12 11:31:35 -07:00 committed by GitHub
parent 30479765cb
commit e21521f45c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 68 additions and 11 deletions

3
.gitignore vendored
View File

@ -18,3 +18,6 @@ build/*
.DS_Store .DS_Store
dist/* dist/*
pip-wheel-metadata/ pip-wheel-metadata/
.pytest_cache/*
.venv/*
.vscode/*

View File

@ -43,7 +43,7 @@ from sanic.asgi import ASGIApp
from sanic.base import BaseSanic from sanic.base import BaseSanic
from sanic.blueprint_group import BlueprintGroup from sanic.blueprint_group import BlueprintGroup
from sanic.blueprints import Blueprint 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 ( from sanic.exceptions import (
InvalidUsage, InvalidUsage,
SanicException, SanicException,
@ -125,7 +125,8 @@ class Sanic(BaseSanic):
router: Optional[Router] = None, router: Optional[Router] = None,
signal_router: Optional[SignalRouter] = None, signal_router: Optional[SignalRouter] = None,
error_handler: Optional[ErrorHandler] = 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, request_class: Optional[Type[Request]] = None,
strict_slashes: bool = False, strict_slashes: bool = False,
log_config: Optional[Dict[str, Any]] = None, log_config: Optional[Dict[str, Any]] = None,
@ -150,7 +151,7 @@ class Sanic(BaseSanic):
self._test_manager = None self._test_manager = None
self.asgi = False self.asgi = False
self.blueprints: Dict[str, Blueprint] = {} 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.configure_logging = configure_logging
self.ctx = SimpleNamespace() self.ctx = SimpleNamespace()
self.debug = None self.debug = None

View File

@ -1,7 +1,8 @@
from inspect import isclass from inspect import isclass
from os import environ from os import environ
from pathlib import Path 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 from .utils import load_module_from_file_location, str_to_bool
@ -41,7 +42,13 @@ DEFAULT_CONFIG = {
class Config(dict): 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 {} defaults = defaults or {}
super().__init__({**DEFAULT_CONFIG, **defaults}) super().__init__({**DEFAULT_CONFIG, **defaults})
@ -50,9 +57,20 @@ class Config(dict):
if keep_alive is not None: if keep_alive is not None:
self.KEEP_ALIVE = keep_alive self.KEEP_ALIVE = keep_alive
if load_env: if env_prefix != SANIC_PREFIX:
prefix = SANIC_PREFIX if load_env is True else load_env if env_prefix:
self.load_environment_vars(prefix=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): def __getattr__(self, attr):
try: try:

View File

@ -59,14 +59,14 @@ def test_load_from_object_string_exception(app):
app.config.load("test_config.Config.test") app.config.load("test_config.Config.test")
def test_auto_load_env(): def test_auto_env_prefix():
environ["SANIC_TEST_ANSWER"] = "42" environ["SANIC_TEST_ANSWER"] = "42"
app = Sanic(name=__name__) app = Sanic(name=__name__)
assert app.config.TEST_ANSWER == 42 assert app.config.TEST_ANSWER == 42
del environ["SANIC_TEST_ANSWER"] del environ["SANIC_TEST_ANSWER"]
def test_auto_load_bool_env(): def test_auto_bool_env_prefix():
environ["SANIC_TEST_ANSWER"] = "True" environ["SANIC_TEST_ANSWER"] = "True"
app = Sanic(name=__name__) app = Sanic(name=__name__)
assert app.config.TEST_ANSWER is True assert app.config.TEST_ANSWER is True
@ -80,6 +80,12 @@ def test_dont_load_env():
del environ["SANIC_TEST_ANSWER"] 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(): def test_load_env_prefix():
environ["MYAPP_TEST_ANSWER"] = "42" environ["MYAPP_TEST_ANSWER"] = "42"
app = Sanic(name=__name__, load_env="MYAPP_") app = Sanic(name=__name__, load_env="MYAPP_")
@ -87,6 +93,14 @@ def test_load_env_prefix():
del environ["MYAPP_TEST_ANSWER"] 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(): def test_load_env_prefix_float_values():
environ["MYAPP_TEST_ROI"] = "2.3" environ["MYAPP_TEST_ROI"] = "2.3"
app = Sanic(name=__name__, load_env="MYAPP_") app = Sanic(name=__name__, load_env="MYAPP_")
@ -101,6 +115,27 @@ def test_load_env_prefix_string_value():
del environ["MYAPP_TEST_TOKEN"] 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): def test_load_from_file(app):
config = dedent( config = dedent(
""" """

View File

@ -1,4 +1,5 @@
import asyncio import asyncio
import platform
from asyncio import sleep as aio_sleep from asyncio import sleep as aio_sleep
from json import JSONDecodeError from json import JSONDecodeError
@ -6,7 +7,6 @@ from os import environ
import httpcore import httpcore
import httpx import httpx
import platform
import pytest import pytest
from sanic_testing.testing import HOST, SanicTestClient from sanic_testing.testing import HOST, SanicTestClient