Skip to content

Commit

Permalink
Merge pull request #1436 from jotagesales/config_from_object_string
Browse files Browse the repository at this point in the history
Config from object string
  • Loading branch information
yunstanford authored Jun 16, 2019
2 parents 322cf89 + b534df2 commit 8fbbe94
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 5 deletions.
7 changes: 7 additions & 0 deletions docs/sanic/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ import myapp.default_settings
app = Sanic('myapp')
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.

Expand Down
6 changes: 6 additions & 0 deletions sanic/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import types

from sanic.exceptions import PyFileError
from sanic.helpers import import_string


SANIC_PREFIX = "SANIC_"
Expand Down Expand Up @@ -102,13 +103,18 @@ def from_object(self, obj):
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
"""
if isinstance(obj, str):
obj = import_string(obj)
for key in dir(obj):
if key.isupper():
self[key] = getattr(obj, key)
Expand Down
22 changes: 22 additions & 0 deletions sanic/helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
"""Defines basics of HTTP standard."""

from importlib import import_module
from inspect import ismodule


STATUS_CODES = {
100: b"Continue",
101: b"Switching Protocols",
Expand Down Expand Up @@ -131,3 +135,21 @@ def remove_entity_headers(headers, allowed=("content-location", "expires")):
if not is_entity_header(header) or header.lower() in allowed
}
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()
23 changes: 18 additions & 5 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,30 @@ def temp_path():
yield Path(td, "file")


def test_load_from_object(app):
class Config:
not_for_config = "should not be used"
CONFIG_VALUE = "should be used"
class ConfigTest:
not_for_config = 'should not 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 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()
Expand Down
19 changes: 19 additions & 0 deletions tests/test_helpers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import inspect

from sanic import helpers
from sanic.config import Config
import pytest


def test_has_message_body():
Expand Down Expand Up @@ -56,3 +60,18 @@ def test_remove_entity_headers():

for header, expected in tests:
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')

0 comments on commit 8fbbe94

Please sign in to comment.