21.3 deprecations (#2007)
* Cleanup deprecations * Remove config deprecations and fix config compat * Add some tests and remove unneeded dependency * Add some tests and remove unneeded dependency * Remove pytest-dependency
This commit is contained in:
parent
8f4e0ad3c8
commit
0c252e7904
|
@ -1,14 +1,8 @@
|
||||||
|
from inspect import isclass
|
||||||
from os import environ
|
from os import environ
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any, Union
|
from typing import Any, Union
|
||||||
|
|
||||||
# NOTE(tomaszdrozdz): remove in version: 21.3
|
|
||||||
# We replace from_envvar(), from_object(), from_pyfile() config object methods
|
|
||||||
# with one simpler update_config() method.
|
|
||||||
# We also replace "loading module from file code" in from_pyfile()
|
|
||||||
# in a favour of load_module_from_file_location().
|
|
||||||
# Please see pull request: 1903
|
|
||||||
# and issue: 1895
|
|
||||||
from .deprecated import from_envvar, from_object, from_pyfile # noqa
|
|
||||||
from .utils import load_module_from_file_location, str_to_bool
|
from .utils import load_module_from_file_location, str_to_bool
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,17 +62,6 @@ class Config(dict):
|
||||||
def __setattr__(self, attr, value):
|
def __setattr__(self, attr, value):
|
||||||
self[attr] = value
|
self[attr] = value
|
||||||
|
|
||||||
# NOTE(tomaszdrozdz): remove in version: 21.3
|
|
||||||
# We replace from_envvar(), from_object(), from_pyfile() config object
|
|
||||||
# methods with one simpler update_config() method.
|
|
||||||
# We also replace "loading module from file code" in from_pyfile()
|
|
||||||
# in a favour of load_module_from_file_location().
|
|
||||||
# Please see pull request: 1903
|
|
||||||
# and issue: 1895
|
|
||||||
from_envvar = from_envvar
|
|
||||||
from_pyfile = from_pyfile
|
|
||||||
from_object = from_object
|
|
||||||
|
|
||||||
def load_environment_vars(self, prefix=SANIC_PREFIX):
|
def load_environment_vars(self, prefix=SANIC_PREFIX):
|
||||||
"""
|
"""
|
||||||
Looks for prefixed environment variables and applies
|
Looks for prefixed environment variables and applies
|
||||||
|
@ -99,18 +82,23 @@ class Config(dict):
|
||||||
self[config_key] = v
|
self[config_key] = v
|
||||||
|
|
||||||
def update_config(self, config: Union[bytes, str, dict, Any]):
|
def update_config(self, config: Union[bytes, str, dict, Any]):
|
||||||
"""Update app.config.
|
"""
|
||||||
|
Update app.config.
|
||||||
|
|
||||||
Note:: only upper case settings are considered.
|
Note:: only upper case settings are considered.
|
||||||
|
|
||||||
You can upload app config by providing path to py file
|
You can upload app config by providing path to py file
|
||||||
holding settings.
|
holding settings.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
# /some/py/file
|
# /some/py/file
|
||||||
A = 1
|
A = 1
|
||||||
B = 2
|
B = 2
|
||||||
|
|
||||||
config.update_config("${some}/py/file")
|
.. code-block:: python
|
||||||
|
|
||||||
|
config.update_config("${some}/py/file")
|
||||||
|
|
||||||
Yes you can put environment variable here, but they must be provided
|
Yes you can put environment variable here, but they must be provided
|
||||||
in format: ${some_env_var}, and mark that $some_env_var is treated
|
in format: ${some_env_var}, and mark that $some_env_var is treated
|
||||||
|
@ -118,23 +106,41 @@ class Config(dict):
|
||||||
|
|
||||||
You can upload app config by providing dict holding settings.
|
You can upload app config by providing dict holding settings.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
d = {"A": 1, "B": 2}
|
d = {"A": 1, "B": 2}
|
||||||
config.update_config(d)
|
config.update_config(d)
|
||||||
|
|
||||||
You can upload app config by providing any object holding settings,
|
You can upload app config by providing any object holding settings,
|
||||||
but in such case config.__dict__ will be used as dict holding settings.
|
but in such case config.__dict__ will be used as dict holding settings.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
class C:
|
class C:
|
||||||
A = 1
|
A = 1
|
||||||
B = 2
|
B = 2
|
||||||
config.update_config(C)"""
|
|
||||||
|
|
||||||
if isinstance(config, (bytes, str)):
|
config.update_config(C)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if isinstance(config, (bytes, str, Path)):
|
||||||
config = load_module_from_file_location(location=config)
|
config = load_module_from_file_location(location=config)
|
||||||
|
|
||||||
if not isinstance(config, dict):
|
if not isinstance(config, dict):
|
||||||
config = config.__dict__
|
cfg = {}
|
||||||
|
if not isclass(config):
|
||||||
|
cfg.update(
|
||||||
|
{
|
||||||
|
key: getattr(config, key)
|
||||||
|
for key in config.__class__.__dict__.keys()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
config = dict(config.__dict__)
|
||||||
|
config.update(cfg)
|
||||||
|
|
||||||
config = dict(filter(lambda i: i[0].isupper(), config.items()))
|
config = dict(filter(lambda i: i[0].isupper(), config.items()))
|
||||||
|
|
||||||
self.update(config)
|
self.update(config)
|
||||||
|
|
||||||
|
load = update_config
|
||||||
|
|
|
@ -1,106 +0,0 @@
|
||||||
# NOTE(tomaszdrozdz): remove in version: 21.3
|
|
||||||
# We replace from_envvar(), from_object(), from_pyfile() config object methods
|
|
||||||
# with one simpler update_config() method.
|
|
||||||
# We also replace "loading module from file code" in from_pyfile()
|
|
||||||
# in a favour of load_module_from_file_location().
|
|
||||||
# Please see pull request: 1903
|
|
||||||
# and issue: 1895
|
|
||||||
import types
|
|
||||||
|
|
||||||
from os import environ
|
|
||||||
from typing import Any
|
|
||||||
from warnings import warn
|
|
||||||
|
|
||||||
from sanic.exceptions import PyFileError
|
|
||||||
from sanic.helpers import import_string
|
|
||||||
|
|
||||||
|
|
||||||
def from_envvar(self, variable_name: str) -> bool:
|
|
||||||
"""Load a configuration from an environment variable pointing to
|
|
||||||
a configuration file.
|
|
||||||
|
|
||||||
:param variable_name: name of the environment variable
|
|
||||||
:return: bool. ``True`` if able to load config, ``False`` otherwise.
|
|
||||||
"""
|
|
||||||
|
|
||||||
warn(
|
|
||||||
"Using `from_envvar` method is deprecated and will be removed in "
|
|
||||||
"v21.3, use `app.update_config` method instead.",
|
|
||||||
DeprecationWarning,
|
|
||||||
stacklevel=2,
|
|
||||||
)
|
|
||||||
|
|
||||||
config_file = environ.get(variable_name)
|
|
||||||
if not config_file:
|
|
||||||
raise RuntimeError(
|
|
||||||
f"The environment variable {variable_name} is not set and "
|
|
||||||
f"thus configuration could not be loaded."
|
|
||||||
)
|
|
||||||
return self.from_pyfile(config_file)
|
|
||||||
|
|
||||||
|
|
||||||
def from_pyfile(self, filename: str) -> bool:
|
|
||||||
"""Update the values in the config from a Python file.
|
|
||||||
Only the uppercase variables in that module are stored in the config.
|
|
||||||
|
|
||||||
:param filename: an absolute path to the config file
|
|
||||||
"""
|
|
||||||
|
|
||||||
warn(
|
|
||||||
"Using `from_pyfile` method is deprecated and will be removed in "
|
|
||||||
"v21.3, use `app.update_config` method instead.",
|
|
||||||
DeprecationWarning,
|
|
||||||
stacklevel=2,
|
|
||||||
)
|
|
||||||
|
|
||||||
module = types.ModuleType("config")
|
|
||||||
module.__file__ = filename
|
|
||||||
try:
|
|
||||||
with open(filename) as config_file:
|
|
||||||
exec( # nosec
|
|
||||||
compile(config_file.read(), filename, "exec"),
|
|
||||||
module.__dict__,
|
|
||||||
)
|
|
||||||
except IOError as e:
|
|
||||||
e.strerror = "Unable to load configuration file (e.strerror)"
|
|
||||||
raise
|
|
||||||
except Exception as e:
|
|
||||||
raise PyFileError(filename) from e
|
|
||||||
|
|
||||||
self.from_object(module)
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def from_object(self, obj: Any) -> None:
|
|
||||||
"""Update the values from the given object.
|
|
||||||
Objects are usually either modules or classes.
|
|
||||||
|
|
||||||
Just the uppercase variables in that object are stored in the config.
|
|
||||||
Example usage::
|
|
||||||
|
|
||||||
from yourapplication import default_config
|
|
||||||
app.config.from_object(default_config)
|
|
||||||
|
|
||||||
or also:
|
|
||||||
app.config.from_object('myproject.config.MyConfigClass')
|
|
||||||
|
|
||||||
You should not use this function to load the actual configuration but
|
|
||||||
rather configuration defaults. The actual config should be loaded
|
|
||||||
with :meth:`from_pyfile` and ideally from a location not within the
|
|
||||||
package because the package might be installed system wide.
|
|
||||||
|
|
||||||
:param obj: an object holding the configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
warn(
|
|
||||||
"Using `from_object` method is deprecated and will be removed in "
|
|
||||||
"v21.3, use `app.update_config` method instead.",
|
|
||||||
DeprecationWarning,
|
|
||||||
stacklevel=2,
|
|
||||||
)
|
|
||||||
|
|
||||||
if isinstance(obj, str):
|
|
||||||
obj = import_string(obj)
|
|
||||||
for key in dir(obj):
|
|
||||||
if key.isupper():
|
|
||||||
self[key] = getattr(obj, key)
|
|
|
@ -1,9 +1,13 @@
|
||||||
|
import types
|
||||||
|
|
||||||
from importlib.util import module_from_spec, spec_from_file_location
|
from importlib.util import module_from_spec, spec_from_file_location
|
||||||
from os import environ as os_environ
|
from os import environ as os_environ
|
||||||
|
from pathlib import Path
|
||||||
from re import findall as re_findall
|
from re import findall as re_findall
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from .exceptions import LoadFileException
|
from sanic.exceptions import LoadFileException, PyFileError
|
||||||
|
from sanic.helpers import import_string
|
||||||
|
|
||||||
|
|
||||||
def str_to_bool(val: str) -> bool:
|
def str_to_bool(val: str) -> bool:
|
||||||
|
@ -39,7 +43,7 @@ def str_to_bool(val: str) -> bool:
|
||||||
|
|
||||||
|
|
||||||
def load_module_from_file_location(
|
def load_module_from_file_location(
|
||||||
location: Union[bytes, str], encoding: str = "utf8", *args, **kwargs
|
location: Union[bytes, str, Path], encoding: str = "utf8", *args, **kwargs
|
||||||
):
|
):
|
||||||
"""Returns loaded module provided as a file path.
|
"""Returns loaded module provided as a file path.
|
||||||
|
|
||||||
|
@ -67,33 +71,61 @@ def load_module_from_file_location(
|
||||||
"/some/path/${some_env_var}"
|
"/some/path/${some_env_var}"
|
||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 1) Parse location.
|
|
||||||
if isinstance(location, bytes):
|
if isinstance(location, bytes):
|
||||||
location = location.decode(encoding)
|
location = location.decode(encoding)
|
||||||
|
|
||||||
# A) Check if location contains any environment variables
|
if isinstance(location, Path) or "/" in location or "$" in location:
|
||||||
# in format ${some_env_var}.
|
|
||||||
env_vars_in_location = set(re_findall(r"\${(.+?)}", location))
|
|
||||||
|
|
||||||
# B) Check these variables exists in environment.
|
if not isinstance(location, Path):
|
||||||
not_defined_env_vars = env_vars_in_location.difference(os_environ.keys())
|
# A) Check if location contains any environment variables
|
||||||
if not_defined_env_vars:
|
# in format ${some_env_var}.
|
||||||
raise LoadFileException(
|
env_vars_in_location = set(re_findall(r"\${(.+?)}", location))
|
||||||
"The following environment variables are not set: "
|
|
||||||
f"{', '.join(not_defined_env_vars)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# C) Substitute them in location.
|
# B) Check these variables exists in environment.
|
||||||
for env_var in env_vars_in_location:
|
not_defined_env_vars = env_vars_in_location.difference(
|
||||||
location = location.replace("${" + env_var + "}", os_environ[env_var])
|
os_environ.keys()
|
||||||
|
)
|
||||||
|
if not_defined_env_vars:
|
||||||
|
raise LoadFileException(
|
||||||
|
"The following environment variables are not set: "
|
||||||
|
f"{', '.join(not_defined_env_vars)}"
|
||||||
|
)
|
||||||
|
|
||||||
# 2) Load and return module.
|
# C) Substitute them in location.
|
||||||
name = location.split("/")[-1].split(".")[
|
for env_var in env_vars_in_location:
|
||||||
0
|
location = location.replace(
|
||||||
] # get just the file name without path and .py extension
|
"${" + env_var + "}", os_environ[env_var]
|
||||||
_mod_spec = spec_from_file_location(name, location, *args, **kwargs)
|
)
|
||||||
module = module_from_spec(_mod_spec)
|
|
||||||
_mod_spec.loader.exec_module(module) # type: ignore
|
|
||||||
|
|
||||||
return module
|
location = str(location)
|
||||||
|
if ".py" in location:
|
||||||
|
name = location.split("/")[-1].split(".")[
|
||||||
|
0
|
||||||
|
] # get just the file name without path and .py extension
|
||||||
|
_mod_spec = spec_from_file_location(
|
||||||
|
name, location, *args, **kwargs
|
||||||
|
)
|
||||||
|
module = module_from_spec(_mod_spec)
|
||||||
|
_mod_spec.loader.exec_module(module) # type: ignore
|
||||||
|
|
||||||
|
else:
|
||||||
|
module = types.ModuleType("config")
|
||||||
|
module.__file__ = str(location)
|
||||||
|
try:
|
||||||
|
with open(location) as config_file:
|
||||||
|
exec( # nosec
|
||||||
|
compile(config_file.read(), location, "exec"),
|
||||||
|
module.__dict__,
|
||||||
|
)
|
||||||
|
except IOError as e:
|
||||||
|
e.strerror = "Unable to load configuration file (e.strerror)"
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
raise PyFileError(location) from e
|
||||||
|
|
||||||
|
return module
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
return import_string(location)
|
||||||
|
except ValueError:
|
||||||
|
raise IOError("Unable to load configuration %s" % str(location))
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -104,7 +104,6 @@ tests_require = [
|
||||||
"pytest-sanic",
|
"pytest-sanic",
|
||||||
"pytest-sugar",
|
"pytest-sugar",
|
||||||
"pytest-benchmark",
|
"pytest-benchmark",
|
||||||
"pytest-dependency",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
docs_require = [
|
docs_require = [
|
||||||
|
|
|
@ -22,24 +22,41 @@ class ConfigTest:
|
||||||
not_for_config = "should not be used"
|
not_for_config = "should not be used"
|
||||||
CONFIG_VALUE = "should be used"
|
CONFIG_VALUE = "should be used"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ANOTHER_VALUE(self):
|
||||||
|
return self.CONFIG_VALUE
|
||||||
|
|
||||||
|
@property
|
||||||
|
def another_not_for_config(self):
|
||||||
|
return self.not_for_config
|
||||||
|
|
||||||
|
|
||||||
def test_load_from_object(app):
|
def test_load_from_object(app):
|
||||||
app.config.from_object(ConfigTest)
|
app.config.load(ConfigTest)
|
||||||
assert "CONFIG_VALUE" in app.config
|
assert "CONFIG_VALUE" in app.config
|
||||||
assert app.config.CONFIG_VALUE == "should be used"
|
assert app.config.CONFIG_VALUE == "should be used"
|
||||||
assert "not_for_config" not in app.config
|
assert "not_for_config" not in app.config
|
||||||
|
|
||||||
|
|
||||||
def test_load_from_object_string(app):
|
def test_load_from_object_string(app):
|
||||||
app.config.from_object("test_config.ConfigTest")
|
app.config.load("test_config.ConfigTest")
|
||||||
assert "CONFIG_VALUE" in app.config
|
assert "CONFIG_VALUE" in app.config
|
||||||
assert app.config.CONFIG_VALUE == "should be used"
|
assert app.config.CONFIG_VALUE == "should be used"
|
||||||
assert "not_for_config" not in app.config
|
assert "not_for_config" not in app.config
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_from_instance(app):
|
||||||
|
app.config.load(ConfigTest())
|
||||||
|
assert "CONFIG_VALUE" in app.config
|
||||||
|
assert app.config.CONFIG_VALUE == "should be used"
|
||||||
|
assert app.config.ANOTHER_VALUE == "should be used"
|
||||||
|
assert "not_for_config" not in app.config
|
||||||
|
assert "another_not_for_config" not in app.config
|
||||||
|
|
||||||
|
|
||||||
def test_load_from_object_string_exception(app):
|
def test_load_from_object_string_exception(app):
|
||||||
with pytest.raises(ImportError):
|
with pytest.raises(ImportError):
|
||||||
app.config.from_object("test_config.Config.test")
|
app.config.load("test_config.Config.test")
|
||||||
|
|
||||||
|
|
||||||
def test_auto_load_env():
|
def test_auto_load_env():
|
||||||
|
@ -52,7 +69,7 @@ def test_auto_load_env():
|
||||||
def test_auto_load_bool_env():
|
def test_auto_load_bool_env():
|
||||||
environ["SANIC_TEST_ANSWER"] = "True"
|
environ["SANIC_TEST_ANSWER"] = "True"
|
||||||
app = Sanic(name=__name__)
|
app = Sanic(name=__name__)
|
||||||
assert app.config.TEST_ANSWER == True
|
assert app.config.TEST_ANSWER is True
|
||||||
del environ["SANIC_TEST_ANSWER"]
|
del environ["SANIC_TEST_ANSWER"]
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,7 +112,7 @@ def test_load_from_file(app):
|
||||||
)
|
)
|
||||||
with temp_path() as config_path:
|
with temp_path() as config_path:
|
||||||
config_path.write_text(config)
|
config_path.write_text(config)
|
||||||
app.config.from_pyfile(str(config_path))
|
app.config.load(str(config_path))
|
||||||
assert "VALUE" in app.config
|
assert "VALUE" in app.config
|
||||||
assert app.config.VALUE == "some value"
|
assert app.config.VALUE == "some value"
|
||||||
assert "CONDITIONAL" in app.config
|
assert "CONDITIONAL" in app.config
|
||||||
|
@ -105,7 +122,7 @@ def test_load_from_file(app):
|
||||||
|
|
||||||
def test_load_from_missing_file(app):
|
def test_load_from_missing_file(app):
|
||||||
with pytest.raises(IOError):
|
with pytest.raises(IOError):
|
||||||
app.config.from_pyfile("non-existent file")
|
app.config.load("non-existent file")
|
||||||
|
|
||||||
|
|
||||||
def test_load_from_envvar(app):
|
def test_load_from_envvar(app):
|
||||||
|
@ -113,14 +130,14 @@ def test_load_from_envvar(app):
|
||||||
with temp_path() as config_path:
|
with temp_path() as config_path:
|
||||||
config_path.write_text(config)
|
config_path.write_text(config)
|
||||||
environ["APP_CONFIG"] = str(config_path)
|
environ["APP_CONFIG"] = str(config_path)
|
||||||
app.config.from_envvar("APP_CONFIG")
|
app.config.load("${APP_CONFIG}")
|
||||||
assert "VALUE" in app.config
|
assert "VALUE" in app.config
|
||||||
assert app.config.VALUE == "some value"
|
assert app.config.VALUE == "some value"
|
||||||
|
|
||||||
|
|
||||||
def test_load_from_missing_envvar(app):
|
def test_load_from_missing_envvar(app):
|
||||||
with pytest.raises(RuntimeError) as e:
|
with pytest.raises(IOError) as e:
|
||||||
app.config.from_envvar("non-existent variable")
|
app.config.load("non-existent variable")
|
||||||
assert str(e.value) == (
|
assert str(e.value) == (
|
||||||
"The environment variable 'non-existent "
|
"The environment variable 'non-existent "
|
||||||
"variable' is not set and thus configuration "
|
"variable' is not set and thus configuration "
|
||||||
|
@ -134,7 +151,7 @@ def test_load_config_from_file_invalid_syntax(app):
|
||||||
config_path.write_text(config)
|
config_path.write_text(config)
|
||||||
|
|
||||||
with pytest.raises(PyFileError):
|
with pytest.raises(PyFileError):
|
||||||
app.config.from_pyfile(config_path)
|
app.config.load(config_path)
|
||||||
|
|
||||||
|
|
||||||
def test_overwrite_exisiting_config(app):
|
def test_overwrite_exisiting_config(app):
|
||||||
|
@ -143,7 +160,7 @@ def test_overwrite_exisiting_config(app):
|
||||||
class Config:
|
class Config:
|
||||||
DEFAULT = 2
|
DEFAULT = 2
|
||||||
|
|
||||||
app.config.from_object(Config)
|
app.config.load(Config)
|
||||||
assert app.config.DEFAULT == 2
|
assert app.config.DEFAULT == 2
|
||||||
|
|
||||||
|
|
||||||
|
@ -153,14 +170,12 @@ def test_overwrite_exisiting_config_ignore_lowercase(app):
|
||||||
class Config:
|
class Config:
|
||||||
default = 2
|
default = 2
|
||||||
|
|
||||||
app.config.from_object(Config)
|
app.config.load(Config)
|
||||||
assert app.config.default == 1
|
assert app.config.default == 1
|
||||||
|
|
||||||
|
|
||||||
def test_missing_config(app):
|
def test_missing_config(app):
|
||||||
with pytest.raises(
|
with pytest.raises(AttributeError, match="Config has no 'NON_EXISTENT'"):
|
||||||
AttributeError, match="Config has no 'NON_EXISTENT'"
|
|
||||||
) as e:
|
|
||||||
_ = app.config.NON_EXISTENT
|
_ = app.config.NON_EXISTENT
|
||||||
|
|
||||||
|
|
||||||
|
@ -175,7 +190,8 @@ def test_config_defaults():
|
||||||
|
|
||||||
def test_config_custom_defaults():
|
def test_config_custom_defaults():
|
||||||
"""
|
"""
|
||||||
we should have all the variables from defaults rewriting them with custom defaults passed in
|
we should have all the variables from defaults rewriting them with
|
||||||
|
custom defaults passed in
|
||||||
Config
|
Config
|
||||||
"""
|
"""
|
||||||
custom_defaults = {
|
custom_defaults = {
|
||||||
|
@ -192,7 +208,8 @@ def test_config_custom_defaults():
|
||||||
|
|
||||||
def test_config_custom_defaults_with_env():
|
def test_config_custom_defaults_with_env():
|
||||||
"""
|
"""
|
||||||
test that environment variables has higher priority than DEFAULT_CONFIG and passed defaults dict
|
test that environment variables has higher priority than DEFAULT_CONFIG
|
||||||
|
and passed defaults dict
|
||||||
"""
|
"""
|
||||||
custom_defaults = {
|
custom_defaults = {
|
||||||
"REQUEST_MAX_SIZE123": 1,
|
"REQUEST_MAX_SIZE123": 1,
|
||||||
|
@ -226,22 +243,22 @@ def test_config_custom_defaults_with_env():
|
||||||
|
|
||||||
|
|
||||||
def test_config_access_log_passing_in_run(app):
|
def test_config_access_log_passing_in_run(app):
|
||||||
assert app.config.ACCESS_LOG == True
|
assert app.config.ACCESS_LOG is True
|
||||||
|
|
||||||
@app.listener("after_server_start")
|
@app.listener("after_server_start")
|
||||||
async def _request(sanic, loop):
|
async def _request(sanic, loop):
|
||||||
app.stop()
|
app.stop()
|
||||||
|
|
||||||
app.run(port=1340, access_log=False)
|
app.run(port=1340, access_log=False)
|
||||||
assert app.config.ACCESS_LOG == False
|
assert app.config.ACCESS_LOG is False
|
||||||
|
|
||||||
app.run(port=1340, access_log=True)
|
app.run(port=1340, access_log=True)
|
||||||
assert app.config.ACCESS_LOG == True
|
assert app.config.ACCESS_LOG is True
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_config_access_log_passing_in_create_server(app):
|
async def test_config_access_log_passing_in_create_server(app):
|
||||||
assert app.config.ACCESS_LOG == True
|
assert app.config.ACCESS_LOG is True
|
||||||
|
|
||||||
@app.listener("after_server_start")
|
@app.listener("after_server_start")
|
||||||
async def _request(sanic, loop):
|
async def _request(sanic, loop):
|
||||||
|
@ -250,24 +267,51 @@ async def test_config_access_log_passing_in_create_server(app):
|
||||||
await app.create_server(
|
await app.create_server(
|
||||||
port=1341, access_log=False, return_asyncio_server=True
|
port=1341, access_log=False, return_asyncio_server=True
|
||||||
)
|
)
|
||||||
assert app.config.ACCESS_LOG == False
|
assert app.config.ACCESS_LOG is False
|
||||||
|
|
||||||
await app.create_server(
|
await app.create_server(
|
||||||
port=1342, access_log=True, return_asyncio_server=True
|
port=1342, access_log=True, return_asyncio_server=True
|
||||||
)
|
)
|
||||||
assert app.config.ACCESS_LOG == True
|
assert app.config.ACCESS_LOG is True
|
||||||
|
|
||||||
|
|
||||||
def test_config_rewrite_keep_alive():
|
def test_config_rewrite_keep_alive():
|
||||||
config = Config()
|
config = Config()
|
||||||
assert config.KEEP_ALIVE == DEFAULT_CONFIG["KEEP_ALIVE"]
|
assert config.KEEP_ALIVE == DEFAULT_CONFIG["KEEP_ALIVE"]
|
||||||
config = Config(keep_alive=True)
|
config = Config(keep_alive=True)
|
||||||
assert config.KEEP_ALIVE == True
|
assert config.KEEP_ALIVE is True
|
||||||
config = Config(keep_alive=False)
|
config = Config(keep_alive=False)
|
||||||
assert config.KEEP_ALIVE == False
|
assert config.KEEP_ALIVE is False
|
||||||
|
|
||||||
# use defaults
|
# use defaults
|
||||||
config = Config(defaults={"KEEP_ALIVE": False})
|
config = Config(defaults={"KEEP_ALIVE": False})
|
||||||
assert config.KEEP_ALIVE == False
|
assert config.KEEP_ALIVE is False
|
||||||
config = Config(defaults={"KEEP_ALIVE": True})
|
config = Config(defaults={"KEEP_ALIVE": True})
|
||||||
assert config.KEEP_ALIVE == True
|
assert config.KEEP_ALIVE is True
|
||||||
|
|
||||||
|
|
||||||
|
_test_setting_as_dict = {"TEST_SETTING_VALUE": 1}
|
||||||
|
_test_setting_as_class = type("C", (), {"TEST_SETTING_VALUE": 1})
|
||||||
|
_test_setting_as_module = str(
|
||||||
|
Path(__file__).parent / "static/app_test_config.py"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"conf_object",
|
||||||
|
[
|
||||||
|
_test_setting_as_dict,
|
||||||
|
_test_setting_as_class,
|
||||||
|
_test_setting_as_module,
|
||||||
|
],
|
||||||
|
ids=["from_dict", "from_class", "from_file"],
|
||||||
|
)
|
||||||
|
def test_update(app, conf_object):
|
||||||
|
app.update_config(conf_object)
|
||||||
|
assert app.config["TEST_SETTING_VALUE"] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_from_lowercase_key(app):
|
||||||
|
d = {"test_setting_value": 1}
|
||||||
|
app.update_config(d)
|
||||||
|
assert "test_setting_value" not in app.config
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
from pathlib import Path
|
|
||||||
from types import ModuleType
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from sanic.exceptions import LoadFileException
|
|
||||||
from sanic.utils import load_module_from_file_location
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def loaded_module_from_file_location():
|
|
||||||
return load_module_from_file_location(
|
|
||||||
str(Path(__file__).parent / "static" / "app_test_config.py")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.dependency(name="test_load_module_from_file_location")
|
|
||||||
def test_load_module_from_file_location(loaded_module_from_file_location):
|
|
||||||
assert isinstance(loaded_module_from_file_location, ModuleType)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.dependency(depends=["test_load_module_from_file_location"])
|
|
||||||
def test_loaded_module_from_file_location_name(
|
|
||||||
loaded_module_from_file_location,
|
|
||||||
):
|
|
||||||
name = loaded_module_from_file_location.__name__
|
|
||||||
if "C:\\" in name:
|
|
||||||
name = name.split("\\")[-1]
|
|
||||||
assert name == "app_test_config"
|
|
||||||
|
|
||||||
|
|
||||||
def test_load_module_from_file_location_with_non_existing_env_variable():
|
|
||||||
with pytest.raises(
|
|
||||||
LoadFileException,
|
|
||||||
match="The following environment variables are not set: MuuMilk",
|
|
||||||
):
|
|
||||||
|
|
||||||
load_module_from_file_location("${MuuMilk}")
|
|
|
@ -1,36 +0,0 @@
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
_test_setting_as_dict = {"TEST_SETTING_VALUE": 1}
|
|
||||||
_test_setting_as_class = type("C", (), {"TEST_SETTING_VALUE": 1})
|
|
||||||
_test_setting_as_module = str(
|
|
||||||
Path(__file__).parent / "static/app_test_config.py"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"conf_object",
|
|
||||||
[
|
|
||||||
_test_setting_as_dict,
|
|
||||||
_test_setting_as_class,
|
|
||||||
pytest.param(
|
|
||||||
_test_setting_as_module,
|
|
||||||
marks=pytest.mark.dependency(
|
|
||||||
depends=["test_load_module_from_file_location"],
|
|
||||||
scope="session",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
ids=["from_dict", "from_class", "from_file"],
|
|
||||||
)
|
|
||||||
def test_update(app, conf_object):
|
|
||||||
app.update_config(conf_object)
|
|
||||||
assert app.config["TEST_SETTING_VALUE"] == 1
|
|
||||||
|
|
||||||
|
|
||||||
def test_update_from_lowercase_key(app):
|
|
||||||
d = {"test_setting_value": 1}
|
|
||||||
app.update_config(d)
|
|
||||||
assert "test_setting_value" not in app.config
|
|
50
tests/test_utils.py
Normal file
50
tests/test_utils.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
from os import environ
|
||||||
|
from pathlib import Path
|
||||||
|
from types import ModuleType
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from sanic.exceptions import LoadFileException
|
||||||
|
from sanic.utils import load_module_from_file_location
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"location",
|
||||||
|
(
|
||||||
|
Path(__file__).parent / "static" / "app_test_config.py",
|
||||||
|
str(Path(__file__).parent / "static" / "app_test_config.py"),
|
||||||
|
str(Path(__file__).parent / "static" / "app_test_config.py").encode(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_load_module_from_file_location(location):
|
||||||
|
module = load_module_from_file_location(location)
|
||||||
|
|
||||||
|
assert isinstance(module, ModuleType)
|
||||||
|
|
||||||
|
|
||||||
|
def test_loaded_module_from_file_location_name():
|
||||||
|
module = load_module_from_file_location(
|
||||||
|
str(Path(__file__).parent / "static" / "app_test_config.py")
|
||||||
|
)
|
||||||
|
|
||||||
|
name = module.__name__
|
||||||
|
if "C:\\" in name:
|
||||||
|
name = name.split("\\")[-1]
|
||||||
|
assert name == "app_test_config"
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_module_from_file_location_with_non_existing_env_variable():
|
||||||
|
with pytest.raises(
|
||||||
|
LoadFileException,
|
||||||
|
match="The following environment variables are not set: MuuMilk",
|
||||||
|
):
|
||||||
|
|
||||||
|
load_module_from_file_location("${MuuMilk}")
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_module_from_file_location_using_env():
|
||||||
|
environ["APP_TEST_CONFIG"] = "static/app_test_config.py"
|
||||||
|
location = str(Path(__file__).parent / "${APP_TEST_CONFIG}")
|
||||||
|
module = load_module_from_file_location(location)
|
||||||
|
|
||||||
|
assert isinstance(module, ModuleType)
|
Loading…
Reference in New Issue
Block a user