Merge pull request #1436 from jotagesales/config_from_object_string

Config from object string
This commit is contained in:
7 2019-06-16 16:58:43 -07:00 committed by GitHub
commit 8fbbe94fe1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 72 additions and 5 deletions

View File

@ -53,6 +53,13 @@ import myapp.default_settings
app = Sanic('myapp') app = Sanic('myapp')
app.config.from_object(myapp.default_settings) app.config.from_object(myapp.default_settings)
``` ```
or also by path to config:
```
app = Sanic('myapp')
app.config.from_object('config.path.config.Class')
```
You could use a class or any other object as well. You could use a class or any other object as well.

View File

@ -2,6 +2,7 @@ import os
import types import types
from sanic.exceptions import PyFileError from sanic.exceptions import PyFileError
from sanic.helpers import import_string
SANIC_PREFIX = "SANIC_" SANIC_PREFIX = "SANIC_"
@ -102,6 +103,9 @@ class Config(dict):
from yourapplication import default_config from yourapplication import default_config
app.config.from_object(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 You should not use this function to load the actual configuration but
rather configuration defaults. The actual config should be loaded rather configuration defaults. The actual config should be loaded
with :meth:`from_pyfile` and ideally from a location not within the with :meth:`from_pyfile` and ideally from a location not within the
@ -109,6 +113,8 @@ class Config(dict):
:param obj: an object holding the configuration :param obj: an object holding the configuration
""" """
if isinstance(obj, str):
obj = import_string(obj)
for key in dir(obj): for key in dir(obj):
if key.isupper(): if key.isupper():
self[key] = getattr(obj, key) self[key] = getattr(obj, key)

View File

@ -1,5 +1,9 @@
"""Defines basics of HTTP standard.""" """Defines basics of HTTP standard."""
from importlib import import_module
from inspect import ismodule
STATUS_CODES = { STATUS_CODES = {
100: b"Continue", 100: b"Continue",
101: b"Switching Protocols", 101: b"Switching Protocols",
@ -131,3 +135,21 @@ def remove_entity_headers(headers, allowed=("content-location", "expires")):
if not is_entity_header(header) or header.lower() in allowed if not is_entity_header(header) or header.lower() in allowed
} }
return headers return headers
def import_string(module_name, package=None):
"""
import a module or class by string path.
:module_name: str with path of module or path to import and
instanciate a class
:returns: a module object or one instance from class if
module_name is a valid path to class
"""
module, klass = module_name.rsplit(".", 1)
module = import_module(module, package=package)
obj = getattr(module, klass)
if ismodule(obj):
return obj
return obj()

View File

@ -18,17 +18,30 @@ def temp_path():
yield Path(td, "file") yield Path(td, "file")
def test_load_from_object(app): class ConfigTest:
class Config: not_for_config = 'should not be used'
not_for_config = "should not be used" CONFIG_VALUE = 'should be used'
CONFIG_VALUE = "should be used"
app.config.from_object(Config)
def test_load_from_object(app):
app.config.from_object(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):
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(): def test_auto_load_env():
environ["SANIC_TEST_ANSWER"] = "42" environ["SANIC_TEST_ANSWER"] = "42"
app = Sanic() app = Sanic()

View File

@ -1,4 +1,8 @@
import inspect
from sanic import helpers from sanic import helpers
from sanic.config import Config
import pytest
def test_has_message_body(): def test_has_message_body():
@ -56,3 +60,18 @@ def test_remove_entity_headers():
for header, expected in tests: for header, expected in tests:
assert helpers.remove_entity_headers(header) == expected assert helpers.remove_entity_headers(header) == expected
def test_import_string_class():
obj = helpers.import_string('sanic.config.Config')
assert isinstance(obj, Config)
def test_import_string_module():
module = helpers.import_string('sanic.config')
assert inspect.ismodule(module)
def test_import_string_exception():
with pytest.raises(ImportError):
helpers.import_string('test.test.test')