diff options
Diffstat (limited to 'ipalib')
-rw-r--r-- | ipalib/plugable.py | 88 | ||||
-rw-r--r-- | ipalib/tests/test_plugable.py | 112 |
2 files changed, 87 insertions, 113 deletions
diff --git a/ipalib/plugable.py b/ipalib/plugable.py index 0d8286a4f..ba9b69739 100644 --- a/ipalib/plugable.py +++ b/ipalib/plugable.py @@ -123,6 +123,13 @@ def lock(readonly): class SetProxy(ReadOnly): + """ + A read-only proxy to an underlying set. + + Although the underlying set cannot be changed through the SetProxy, + the set can change and is expected to (unless the underlying set is a + frozen set). + """ def __init__(self, s): allowed = (set, frozenset, dict) if type(s) not in allowed: @@ -142,6 +149,9 @@ class SetProxy(ReadOnly): class DictProxy(SetProxy): + """ + A read-only proxy to an underlying dict. + """ def __init__(self, d): if type(d) is not dict: raise TypeError('%r is not %r' % (type(d), dict)) @@ -150,11 +160,31 @@ class DictProxy(SetProxy): def __getitem__(self, key): """ - Returns the value + Returns the value corresponding to ``key``. """ return self.__d[key] +class MagicDict(DictProxy): + """ + A read-only dict whose items can also be accessed as attributes. + + Although a MagicDict is read-only, the underlying dict can change (and is + assumed to). + + One of these is created for each allowed base in a `Registrar` instance. + """ + + def __getattr__(self, name): + """ + Returns the value corresponding to ``name``. + """ + try: + return self[name] + except KeyError: + raise AttributeError('no attribute %r' % name) + + class Plugin(ReadOnly): """ Base class for all plugins. @@ -525,63 +555,7 @@ class NameSpace(ReadOnly): return '%s(<%d members>)' % (self.__class__.__name__, len(self)) -class MagicDict(ReadOnly): - """ - A read-only dict whose items can also be accessed as attributes. - - Although a MagicDict is read-only, the underlying dict can change (and is - assumed to). - One of these is created for each allowed base in a `Registrar` instance. - """ - def __init__(self, d): - """ - :param d: The ``dict`` instance to proxy. - """ - assert type(d) is dict, '`d` must be %r, got %r' % (dict, type(d)) - self.__d = d - lock(self) - - def __len__(self): - """ - Returns number of items in underlying ``dict``. - """ - return len(self.__d) - - def __iter__(self): - """ - Iterates through keys of underlying ``dict`` in ascending order. - """ - for name in sorted(self.__d): - yield name - - def __contains__(self, key): - """ - Returns True if underlying dict contains ``key``, False otherwise. - - :param key: The key to query upon. - """ - return key in self.__d - - def __getitem__(self, key): - """ - Returns value from underlying dict corresponding to ``key``. - - :param key: The key of the value to retrieve. - """ - if key in self.__d: - return self.__d[key] - raise KeyError('no item at key %r' % key) - - def __getattr__(self, name): - """ - Returns value from underlying dict corresponding to ``name``. - - :param name: The name of the attribute to retrieve. - """ - if name in self.__d: - return self.__d[name] - raise AttributeError('no attribute %r' % name) class Registrar(ReadOnly): diff --git a/ipalib/tests/test_plugable.py b/ipalib/tests/test_plugable.py index 0410bf317..6f2385c92 100644 --- a/ipalib/tests/test_plugable.py +++ b/ipalib/tests/test_plugable.py @@ -205,6 +205,62 @@ class test_DictProxy(ClassChecker): raises(TypeError, delitem, proxy, key) +class test_MagicDict(ClassChecker): + """ + Tests the `plugable.MagicDict` class. + """ + _cls = plugable.MagicDict + + def test_class(self): + assert self.cls.__bases__ == (plugable.DictProxy,) + for non_dict in ('hello', 69, object): + raises(TypeError, self.cls, non_dict) + + def test_MagicDict(self): + cnt = 10 + keys = [] + d = dict() + dictproxy = self.cls(d) + for i in xrange(cnt): + key = 'key_%d' % i + val = 'val_%d' % i + keys.append(key) + + # Test thet key does not yet exist + assert len(dictproxy) == i + assert key not in dictproxy + assert not hasattr(dictproxy, key) + raises(KeyError, getitem, dictproxy, key) + raises(AttributeError, getattr, dictproxy, key) + + # Test that items/attributes cannot be set on dictproxy: + raises(TypeError, setitem, dictproxy, key, val) + raises(AttributeError, setattr, dictproxy, key, val) + + # Test that additions in d are reflected in dictproxy: + d[key] = val + assert len(dictproxy) == i + 1 + assert key in dictproxy + assert hasattr(dictproxy, key) + assert dictproxy[key] is val + assert read_only(dictproxy, key) is val + + # Test __iter__ + assert list(dictproxy) == keys + + for key in keys: + # Test that items cannot be deleted through dictproxy: + raises(TypeError, delitem, dictproxy, key) + raises(AttributeError, delattr, dictproxy, key) + + # Test that deletions in d are reflected in dictproxy + del d[key] + assert len(dictproxy) == len(d) + assert key not in dictproxy + raises(KeyError, getitem, dictproxy, key) + raises(AttributeError, getattr, dictproxy, key) + + class test_Plugin(ClassChecker): """ Tests the `plugable.Plugin` class. @@ -570,62 +626,6 @@ class test_NameSpace(ClassChecker): no_set(ns, name) -class test_MagicDict(ClassChecker): - """ - Tests the `plugable.MagicDict` class. - """ - _cls = plugable.MagicDict - - def test_class(self): - assert self.cls.__bases__ == (plugable.ReadOnly,) - for non_dict in ('hello', 69, object): - raises(AssertionError, self.cls, non_dict) - - def test_MagicDict(self): - cnt = 10 - keys = [] - d = dict() - dictproxy = self.cls(d) - for i in xrange(cnt): - key = 'key_%d' % i - val = 'val_%d' % i - keys.append(key) - - # Test thet key does not yet exist - assert len(dictproxy) == i - assert key not in dictproxy - assert not hasattr(dictproxy, key) - raises(KeyError, getitem, dictproxy, key) - raises(AttributeError, getattr, dictproxy, key) - - # Test that items/attributes cannot be set on dictproxy: - raises(TypeError, setitem, dictproxy, key, val) - raises(AttributeError, setattr, dictproxy, key, val) - - # Test that additions in d are reflected in dictproxy: - d[key] = val - assert len(dictproxy) == i + 1 - assert key in dictproxy - assert hasattr(dictproxy, key) - assert dictproxy[key] is val - assert read_only(dictproxy, key) is val - - # Test __iter__ - assert list(dictproxy) == keys - - for key in keys: - # Test that items cannot be deleted through dictproxy: - raises(TypeError, delitem, dictproxy, key) - raises(AttributeError, delattr, dictproxy, key) - - # Test that deletions in d are reflected in dictproxy - del d[key] - assert len(dictproxy) == len(d) - assert key not in dictproxy - raises(KeyError, getitem, dictproxy, key) - raises(AttributeError, getattr, dictproxy, key) - - def test_Registrar(): class Base1(object): pass |