from contextlib import contextmanager from os import environ from pathlib import Path from tempfile import TemporaryDirectory from textwrap import dedent import pytest from sanic import Sanic from sanic.config import DEFAULT_CONFIG, Config from sanic.exceptions import PyFileError @contextmanager def temp_path(): """ a simple cross platform replacement for NamedTemporaryFile """ with TemporaryDirectory() as td: yield Path(td, "file") class ConfigTest: not_for_config = 'should not be used' CONFIG_VALUE = 'should be used' def test_load_from_object(app): app.config.from_object(ConfigTest) assert "CONFIG_VALUE" in app.config assert app.config.CONFIG_VALUE == "should be used" assert "not_for_config" not in app.config def test_load_from_object_string(app): app.config.from_object('test_config.ConfigTest') assert 'CONFIG_VALUE' in app.config assert app.config.CONFIG_VALUE == 'should be used' assert 'not_for_config' not in app.config def test_load_from_object_string_exception(app): with pytest.raises(ImportError): app.config.from_object('test_config.Config.test') def test_auto_load_env(): environ["SANIC_TEST_ANSWER"] = "42" app = Sanic() assert app.config.TEST_ANSWER == 42 del environ["SANIC_TEST_ANSWER"] def test_auto_load_bool_env(): environ["SANIC_TEST_ANSWER"] = "True" app = Sanic() assert app.config.TEST_ANSWER == True del environ["SANIC_TEST_ANSWER"] def test_dont_load_env(): environ["SANIC_TEST_ANSWER"] = "42" app = Sanic(load_env=False) assert getattr(app.config, "TEST_ANSWER", None) is None del environ["SANIC_TEST_ANSWER"] def test_load_env_prefix(): environ["MYAPP_TEST_ANSWER"] = "42" app = Sanic(load_env="MYAPP_") assert app.config.TEST_ANSWER == 42 del environ["MYAPP_TEST_ANSWER"] def test_load_env_prefix_float_values(): environ["MYAPP_TEST_ROI"] = "2.3" app = Sanic(load_env="MYAPP_") assert app.config.TEST_ROI == 2.3 del environ["MYAPP_TEST_ROI"] def test_load_env_prefix_string_value(): environ["MYAPP_TEST_TOKEN"] = "somerandomtesttoken" app = Sanic(load_env="MYAPP_") assert app.config.TEST_TOKEN == "somerandomtesttoken" del environ["MYAPP_TEST_TOKEN"] def test_load_from_file(app): config = dedent( """ VALUE = 'some value' condition = 1 == 1 if condition: CONDITIONAL = 'should be set' """ ) with temp_path() as config_path: config_path.write_text(config) app.config.from_pyfile(str(config_path)) assert "VALUE" in app.config assert app.config.VALUE == "some value" assert "CONDITIONAL" in app.config assert app.config.CONDITIONAL == "should be set" assert "condition" not in app.config def test_load_from_missing_file(app): with pytest.raises(IOError): app.config.from_pyfile("non-existent file") def test_load_from_envvar(app): config = "VALUE = 'some value'" with temp_path() as config_path: config_path.write_text(config) environ["APP_CONFIG"] = str(config_path) app.config.from_envvar("APP_CONFIG") assert "VALUE" in app.config assert app.config.VALUE == "some value" def test_load_from_missing_envvar(app): with pytest.raises(RuntimeError) as e: app.config.from_envvar("non-existent variable") assert str(e.value) == ( "The environment variable 'non-existent " "variable' is not set and thus configuration " "could not be loaded." ) def test_load_config_from_file_invalid_syntax(app): config = "VALUE = some value" with temp_path() as config_path: config_path.write_text(config) with pytest.raises(PyFileError): app.config.from_pyfile(config_path) def test_overwrite_exisiting_config(app): app.config.DEFAULT = 1 class Config: DEFAULT = 2 app.config.from_object(Config) assert app.config.DEFAULT == 2 def test_overwrite_exisiting_config_ignore_lowercase(app): app.config.default = 1 class Config: default = 2 app.config.from_object(Config) assert app.config.default == 1 def test_missing_config(app): with pytest.raises( AttributeError, match="Config has no 'NON_EXISTENT'" ) as e: _ = app.config.NON_EXISTENT def test_config_defaults(): """ load DEFAULT_CONFIG """ conf = Config() for key, value in DEFAULT_CONFIG.items(): assert getattr(conf, key) == value def test_config_custom_defaults(): """ we should have all the variables from defaults rewriting them with custom defaults passed in Config """ custom_defaults = { "REQUEST_MAX_SIZE": 1, "KEEP_ALIVE": False, "ACCESS_LOG": False, } conf = Config(defaults=custom_defaults) for key, value in DEFAULT_CONFIG.items(): if key in custom_defaults.keys(): value = custom_defaults[key] assert getattr(conf, key) == value def test_config_custom_defaults_with_env(): """ test that environment variables has higher priority than DEFAULT_CONFIG and passed defaults dict """ custom_defaults = { "REQUEST_MAX_SIZE123": 1, "KEEP_ALIVE123": False, "ACCESS_LOG123": False, } environ_defaults = { "SANIC_REQUEST_MAX_SIZE123": "2", "SANIC_KEEP_ALIVE123": "True", "SANIC_ACCESS_LOG123": "False", } for key, value in environ_defaults.items(): environ[key] = value conf = Config(defaults=custom_defaults) for key, value in DEFAULT_CONFIG.items(): if "SANIC_" + key in environ_defaults.keys(): value = environ_defaults["SANIC_" + key] try: value = int(value) except ValueError: if value in ["True", "False"]: value = value == "True" assert getattr(conf, key) == value for key, value in environ_defaults.items(): del environ[key] def test_config_access_log_passing_in_run(app): assert app.config.ACCESS_LOG == True @app.listener("after_server_start") async def _request(sanic, loop): app.stop() app.run(port=1340, access_log=False) assert app.config.ACCESS_LOG == False app.run(port=1340, access_log=True) assert app.config.ACCESS_LOG == True @pytest.mark.asyncio async def test_config_access_log_passing_in_create_server(app): assert app.config.ACCESS_LOG == True @app.listener("after_server_start") async def _request(sanic, loop): app.stop() await app.create_server( port=1341, access_log=False, return_asyncio_server=True ) assert app.config.ACCESS_LOG == False await app.create_server( port=1342, access_log=True, return_asyncio_server=True ) assert app.config.ACCESS_LOG == True def test_config_rewrite_keep_alive(): config = Config() assert config.KEEP_ALIVE == DEFAULT_CONFIG["KEEP_ALIVE"] config = Config(keep_alive=True) assert config.KEEP_ALIVE == True config = Config(keep_alive=False) assert config.KEEP_ALIVE == False # use defaults config = Config(defaults={"KEEP_ALIVE": False}) assert config.KEEP_ALIVE == False config = Config(defaults={"KEEP_ALIVE": True}) assert config.KEEP_ALIVE == True