summaryrefslogtreecommitdiffstats
path: root/ipalib
diff options
context:
space:
mode:
authorJason Gerard DeRose <jderose@redhat.com>2008-08-15 01:24:51 +0000
committerJason Gerard DeRose <jderose@redhat.com>2008-08-15 01:24:51 +0000
commite43a5c642e1717c9309e8747e5433ab85abf2779 (patch)
tree1adb20f0dcf34ab21c08bc05eb100a0d4122e036 /ipalib
parentf6c2181eebf6e6bd794eaca8b78d3b35ad3be4e4 (diff)
downloadfreeipa-e43a5c642e1717c9309e8747e5433ab85abf2779.tar.gz
freeipa-e43a5c642e1717c9309e8747e5433ab85abf2779.tar.xz
freeipa-e43a5c642e1717c9309e8747e5433ab85abf2779.zip
171: MagicDict now subclasses from DictProxy; updated unit tests
Diffstat (limited to 'ipalib')
-rw-r--r--ipalib/plugable.py88
-rw-r--r--ipalib/tests/test_plugable.py112
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