Skip to content

Commit

Permalink
Change import dynamic reader mechanism to simplify to custom dynamic …
Browse files Browse the repository at this point in the history
…readers
  • Loading branch information
drgarcia1986 committed Oct 2, 2019
1 parent e645bb4 commit 13fbed9
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 10 deletions.
44 changes: 44 additions & 0 deletions README.rst
Expand Up @@ -538,6 +538,45 @@ the ``DYNAMIC_SETTINGS`` special setting with the ``memcached`` backend
To install with memcached dependencies use: ``pip install simple-settings[memcached]``


Custom
^^^^^^
You can easily create your own dynamic settings reader. To do that you need to
create a class than inherit from ``simple_settings.dynamic_settings.base.BaseReader``
and implement ``_get`` and ``_set`` methods, f.ex:

.. code:: python
from simple_settings.dynamic_settings.base import BaseReader
class Reader(BaseReader):
def __init__(self, conf):
super(Reader, self).__init__(conf)
self._dict = {}
def _get(self, key):
return self._dict.get(key)
def _set(self, key, value):
self._dict[key] = value
..
To use it, just configure ``SIMPLE_SETINGS`` special setting with the full path
of the reader, f.ex:

.. code:: python
'SIMPLE_SETTINGS': {
'DYNAMIC_SETTINGS': {
'backend': 'path.of.module.ClassName'
}
}
..
Any other config of dynamic settings will be pass to reader backend on argument ``config``

Utils
-----

Expand Down Expand Up @@ -600,6 +639,11 @@ To implement a custom strategy:
Changelog
---------

[NEXT_RELEASE]
~~~~~~~~~~~~~~

- Change import dynamic reader mechanism to using full class path with dot notation

[0.18.0] - 2019-07-14
~~~~~~~~~~~~~~~~~~~~~

Expand Down
35 changes: 27 additions & 8 deletions simple_settings/dynamic_settings/__init__.py
Expand Up @@ -7,14 +7,26 @@
)

DYNAMIC_SETTINGS_MAPPING = {
'consul': 'simple_settings.dynamic_settings.consul_reader',
'database': 'simple_settings.dynamic_settings.database_reader',
'memcached': 'simple_settings.dynamic_settings.memcached_reader',
'redis': 'simple_settings.dynamic_settings.redis_reader',
's3': 'simple_settings.dynamic_settings.s3_reader'
'consul': 'simple_settings.dynamic_settings.consul_reader.Reader',
'database': 'simple_settings.dynamic_settings.database_reader.Reader',
'memcached': 'simple_settings.dynamic_settings.memcached_reader.Reader',
'redis': 'simple_settings.dynamic_settings.redis_reader.Reader',
's3': 'simple_settings.dynamic_settings.s3_reader.Reader'
}


class InvalidDynamicSettingsReaderPath(RuntimeError):

def __init__(self, path):
super(InvalidDynamicSettingsReaderPath, self).__init__(self)
self.path = path

def __str__(self):
return 'The path of dynamic settings reader [{}] is invalid'.format(
self.path
)


def get_dynamic_reader(settings_dict):
dynamic_settings_conf = (
settings_dict.get(SPECIAL_SETTINGS_KEY, {}).get(DYNAMIC_SETTINGS_KEY)
Expand All @@ -26,7 +38,14 @@ def get_dynamic_reader(settings_dict):
if reader_backend in DYNAMIC_SETTINGS_MAPPING:
reader_backend = DYNAMIC_SETTINGS_MAPPING[reader_backend]

reader_module = importlib.import_module(reader_backend)
reader = reader_module.Reader(dynamic_settings_conf)
reader_class = get_dynamic_reader_class(reader_backend)
return reader_class(dynamic_settings_conf)


return reader
def get_dynamic_reader_class(reader_backend_path):
try:
module_path, class_name = reader_backend_path.rsplit('.', 1)
reader_module = importlib.import_module(module_path)
return getattr(reader_module, class_name)
except (ValueError, ImportError, AttributeError):
raise InvalidDynamicSettingsReaderPath(reader_backend_path)
22 changes: 20 additions & 2 deletions tests/dynamic_settings/test_dynamic_settings.py
Expand Up @@ -2,7 +2,10 @@
import pytest

from simple_settings.core import LazySettings
from simple_settings.dynamic_settings import get_dynamic_reader
from simple_settings.dynamic_settings import (
InvalidDynamicSettingsReaderPath,
get_dynamic_reader
)
from simple_settings.dynamic_settings.base import BaseReader


Expand All @@ -25,14 +28,29 @@ class TestDynamicSettings(object):
def settings_dict(self):
return {
'SIMPLE_SETTINGS': {
'DYNAMIC_SETTINGS': {'backend': __name__}
'DYNAMIC_SETTINGS': {'backend': '{}.Reader'.format(__name__)}
}
}

@pytest.fixture
def reader(self, settings_dict):
return get_dynamic_reader(settings_dict)

@pytest.mark.parametrize('path', (
'invalid.path',
'{}.Invalid'.format(__name__)
))
def test_should_raise_runtime_error_for_invalid_dynamic_settings(
self,
path
):
with pytest.raises(InvalidDynamicSettingsReaderPath) as ex:
get_dynamic_reader({
'SIMPLE_SETTINGS': {'DYNAMIC_SETTINGS': {'backend': path}}
})

assert path in str(ex)

def test_should_return_instance_of_fake_dynamic_settings(
self, settings_dict
):
Expand Down

0 comments on commit 13fbed9

Please sign in to comment.