Add Settings.copy, freeze and frozencopy method

This commit is contained in:
Julia Medina 2014-07-29 18:47:49 -03:00
parent a995727117
commit 3ae971468f
3 changed files with 76 additions and 0 deletions

View File

@ -264,6 +264,30 @@ Settings API
:param default: the value to return if no setting is found
:type default: any
.. method:: copy()
Make a deep copy of current settings.
This method returns a new instance of the :class:`Settings` class,
populated with the same values and their priorities.
Modifications to the new object won't be reflected on the original
settings.
.. method:: freeze()
Disable further changes to the current settings.
After calling this method, the present state of the settings will become
immutable. Trying to change values through the :meth:`~set` method and
its variants won't be possible and will be alerted.
.. method:: frozencopy()
Return an immutable copy of the current settings.
Alias for a :meth:`~freeze` call in the object returned by :meth:`copy`
.. _topics-api-signals:
Signals API

View File

@ -1,5 +1,6 @@
import six
import json
import copy
import warnings
from collections import MutableMapping
from importlib import import_module
@ -46,6 +47,7 @@ class SettingsAttribute(object):
class Settings(object):
def __init__(self, values=None, priority='project'):
self.frozen = False
self.attributes = {}
self.setmodule(default_settings, priority='default')
if values is not None:
@ -93,6 +95,7 @@ class Settings(object):
raise ValueError("Cannot convert value for setting '%s' to dict: '%s'" % (name, value))
def set(self, name, value, priority='project'):
assert not self.frozen, "Trying to modify an immutable Settings object"
if isinstance(priority, six.string_types):
priority = SETTINGS_PRIORITIES[priority]
if name not in self.attributes:
@ -101,16 +104,29 @@ class Settings(object):
self.attributes[name].set(value, priority)
def setdict(self, values, priority='project'):
assert not self.frozen, "Trying to modify an immutable Settings object"
for name, value in six.iteritems(values):
self.set(name, value, priority)
def setmodule(self, module, priority='project'):
assert not self.frozen, "Trying to modify an immutable Settings object"
if isinstance(module, six.string_types):
module = import_module(module)
for key in dir(module):
if key.isupper():
self.set(key, getattr(module, key), priority)
def copy(self):
return copy.deepcopy(self)
def freeze(self):
self.frozen = True
def frozencopy(self):
copy = self.copy()
copy.freeze()
return copy
@property
def overrides(self):
warnings.warn("`Settings.overrides` attribute is deprecated and won't "

View File

@ -190,6 +190,42 @@ class SettingsTest(unittest.TestCase):
self.assertEqual(settings.getdict('TEST_DICT3', {'key1': 5}), {'key1': 5})
self.assertRaises(ValueError, settings.getdict, 'TEST_LIST1')
def test_copy(self):
values = {
'TEST_BOOL': True,
'TEST_LIST': ['one', 'two'],
'TEST_LIST_OF_LISTS': [['first_one', 'first_two'],
['second_one', 'second_two']]
}
self.settings.setdict(values)
copy = self.settings.copy()
self.settings.set('TEST_BOOL', False)
self.assertTrue(copy.get('TEST_BOOL'))
test_list = self.settings.get('TEST_LIST')
test_list.append('three')
self.assertListEqual(copy.get('TEST_LIST'), ['one', 'two'])
test_list_of_lists = self.settings.get('TEST_LIST_OF_LISTS')
test_list_of_lists[0].append('first_three')
self.assertListEqual(copy.get('TEST_LIST_OF_LISTS')[0],
['first_one', 'first_two'])
def test_freeze(self):
self.settings.freeze()
with self.assertRaises(AssertionError) as cm:
self.settings.set('TEST_BOOL', False)
self.assertEqual(str(cm.exception),
"Trying to modify an immutable Settings object")
def test_frozencopy(self):
with mock.patch.object(self.settings, 'copy') as mock_copy:
with mock.patch.object(mock_copy, 'freeze') as mock_freeze:
mock_object = self.settings.frozencopy()
mock_copy.assert_call_once()
mock_freeze.assert_call_once()
self.assertEqual(mock_object, mock_copy.return_value)
def test_deprecated_attribute_overrides(self):
self.settings.set('BAR', 'fuz', priority='cmdline')
with warnings.catch_warnings(record=True) as w: