diff --git a/sanic/config.py b/sanic/config.py index 406c44e6..ec9972e6 100644 --- a/sanic/config.py +++ b/sanic/config.py @@ -1,7 +1,12 @@ import os - +import errno import types +try: + import ujson as json +except ImportError: + import json + SANIC_PREFIX = 'SANIC_' @@ -97,12 +102,53 @@ class Config(dict): if key.isupper(): self[key] = getattr(obj, key) + def from_json(self, filename, silent=False): + """Updates the values in the config from a JSON file. This function + behaves as if the JSON object was a dictionary and passed to the + :meth:`from_mapping` function. + + :param filename: the filename of the JSON file. This can either be an + absolute filename or a filename relative to the + root path. + :param silent: set to ``True`` if you want silent failure for missing + files. + """ + try: + with open(filename) as json_file: + obj = json.loads(json_file.read()) + except IOError as e: + if silent and e.errno in (errno.ENOENT, errno.EISDIR): + return False + e.strerror = 'Unable to load configuration file (%s)' % e.strerror + raise + return self.from_mapping(obj) + + def from_mapping(self, *mapping, **kwargs): + """Updates the config like :meth:`update` ignoring items with non-upper + keys. + """ + mappings = [] + if len(mapping) == 1: + if hasattr(mapping[0], 'items'): + mappings.append(mapping[0].items()) + else: + mappings.append(mapping[0]) + elif len(mapping) > 1: + raise TypeError( + 'expected at most 1 positional argument, got %d' % len(mapping) + ) + mappings.append(kwargs.items()) + for mapping in mappings: + for (key, value) in mapping: + if key.isupper(): + self[key] = value + return True + def load_environment_vars(self): + """Looks for any SANIC_ prefixed environment variables and applies + them to the configuration if present. + """ for k, v in os.environ.items(): - """ - Looks for any SANIC_ prefixed environment variables and applies - them to the configuration if present. - """ if k.startswith(SANIC_PREFIX): _, config_key = k.split(SANIC_PREFIX, 1) self[config_key] = v diff --git a/tests/test_config.py b/tests/test_config.py index aa7a0e4d..1f395e63 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -16,18 +16,21 @@ def test_load_from_object(): assert app.config.CONFIG_VALUE == 'should be used' assert 'not_for_config' not in app.config + 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_env(): environ["SANIC_TEST_ANSWER"] = "42" app = Sanic(load_env=False) assert getattr(app.config, 'TEST_ANSWER', None) == None del environ["SANIC_TEST_ANSWER"] + def test_load_from_file(): app = Sanic('test_load_from_file') config = b""" @@ -74,6 +77,7 @@ def test_load_from_missing_envvar(): def test_overwrite_exisiting_config(): app = Sanic('test_overwrite_exisiting_config') app.config.DEFAULT = 1 + class Config: DEFAULT = 2 @@ -85,3 +89,22 @@ def test_missing_config(): app = Sanic('test_missing_config') with pytest.raises(AttributeError): app.config.NON_EXISTENT + + +def test_load_from_json(): + app = Sanic('test_load_from_file') + config = b'{"VALUE": "some value"}' + with NamedTemporaryFile(mode='wb') as config_file: + config_file.write(config) + config_file.seek(0) + app.config.from_json(config_file.name) + assert 'VALUE' in app.config + assert app.config.VALUE == 'some value' + + +def test_load_from_mapping(): + app = Sanic('test_load_from_file') + config = {"VALUE": "some value"} + app.config.from_mapping(config) + assert 'VALUE' in app.config + assert app.config.VALUE == 'some value'