Add memory backend (#394)

* Add simple backend

* Add test case for simple backend

* Add tests for mget backend method

* Fix redis mock mget implementation

* Make sure memory backend is thread safe

* Add docs section for memory backend

* Add test usage examples to docs

* Update docs for memory backend in testing

* Share memory storage between threads
This commit is contained in:
Misha Behersky 2020-06-10 20:49:42 +03:00 committed by GitHub
parent 6b44a52933
commit 9eccfe0386
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 99 additions and 7 deletions

View file

@ -0,0 +1,38 @@
from threading import Lock
from . import Backend
from .. import signals, config
class MemoryBackend(Backend):
"""
Simple in-memory backend that should be mostly used for testing purposes
"""
_storage = {}
_lock = Lock()
def __init__(self):
super().__init__()
def get(self, key):
with self._lock:
return self._storage.get(key)
def mget(self, keys):
if not keys:
return
result = []
with self._lock:
for key in keys:
value = self._storage.get(key)
if value is not None:
result.append((key, value))
return result
def set(self, key, value):
with self._lock:
old_value = self._storage.get(key)
self._storage[key] = value
signals.config_updated.send(
sender=config, key=key, old_value=old_value, new_value=value
)

View file

@ -144,3 +144,15 @@ configured cache backend to enable this feature, e.g. "default"::
setting to ``None``.
.. _django-picklefield: http://pypi.python.org/pypi/django-picklefield/
Memory
------
The configuration values are stored in a memory and do not persist between process
restarts. In order to use this backend you must set the ``CONSTANCE_BACKEND``
Django setting to::
CONSTANCE_BACKEND = 'constance.backends.memory.MemoryBackend'
The main purpose of this one is to be used mostly for testing/developing means,
so make sure you intentionally use it on production environments.

View file

@ -41,7 +41,7 @@ method level and also as a
Pytest usage
~~~~~
~~~~~~~~~~~~
Django-constance provides pytest plugin that adds marker
:class:`@pytest.mark.override_config()`. It handles config override for
@ -108,3 +108,16 @@ Any scope, auto-used fixture alternative can also be implemented like this
with override_config(API_URL="/awesome/url/"):
yield
Memory backend
~~~~~~~~~~~~~~
If you don't want to rely on any external services such as Redis or database when
running your unittests you can select :class:`MemoryBackend` for a test Django settings file
.. code-block:: python
CONSTANCE_BACKEND = 'constance.backends.memory.MemoryBackend'
It will provide simple thread-safe backend which will reset to default values after each
test run.

0
test_threads.py Normal file
View file

View file

View file

@ -0,0 +1,18 @@
from django.test import TestCase
from constance import settings
from tests.storage import StorageTestsMixin
class TestMemory(StorageTestsMixin, TestCase):
def setUp(self):
self.old_backend = settings.BACKEND
settings.BACKEND = 'constance.backends.memory.MemoryBackend'
super().setUp()
self.config._backend._storage = {}
def tearDown(self):
self.config._backend._storage = {}
settings.BACKEND = self.old_backend

View file

@ -3,9 +3,4 @@ class Connection(dict):
self[key] = value
def mget(self, keys):
values = []
for key in keys:
value = self.get(key, None)
if value is not None:
values.append(value)
return values
return [self.get(key) for key in keys]

View file

@ -1,6 +1,7 @@
from datetime import datetime, date, time, timedelta
from decimal import Decimal
from constance import settings
from constance.base import Config
@ -77,3 +78,18 @@ class StorageTestsMixin:
self.assertEqual(self.config.DATE_VALUE, date(2001, 12, 20))
self.assertEqual(self.config.TIME_VALUE, time(1, 59, 0))
self.assertEqual(self.config.TIMEDELTA_VALUE, timedelta(days=1, hours=2, minutes=3))
def test_backend_retrieves_multiple_values(self):
# Check corner cases such as falsy values
self.config.INT_VALUE = 0
self.config.BOOL_VALUE = False
self.config.STRING_VALUE = ''
values = dict(self.config._backend.mget(settings.CONFIG))
self.assertEqual(values['INT_VALUE'], 0)
self.assertEqual(values['BOOL_VALUE'], False)
self.assertEqual(values['STRING_VALUE'], '')
def test_backend_does_not_return_none_values(self):
result = dict(self.config._backend.mget(settings.CONFIG))
self.assertEqual(result, {})