summaryrefslogtreecommitdiffstats
path: root/ipalib
diff options
context:
space:
mode:
authorJason Gerard DeRose <jderose@redhat.com>2008-08-14 17:21:21 +0000
committerJason Gerard DeRose <jderose@redhat.com>2008-08-14 17:21:21 +0000
commitb403fd822b76a7deffe8110fbeb7993ef3cac3a5 (patch)
tree47cd3700b22c1e995484c35e68a5c140fd617886 /ipalib
parentca53615dddd487230c3e40231cb02467e19388d7 (diff)
downloadfreeipa-b403fd822b76a7deffe8110fbeb7993ef3cac3a5.tar.gz
freeipa-b403fd822b76a7deffe8110fbeb7993ef3cac3a5.tar.xz
freeipa-b403fd822b76a7deffe8110fbeb7993ef3cac3a5.zip
159: Added plugable.DictProxy class; added corresponding unit tests; added setitem(), delitem() functions to tstutil
Diffstat (limited to 'ipalib')
-rw-r--r--ipalib/plugable.py70
-rw-r--r--ipalib/tests/test_plugable.py57
-rw-r--r--ipalib/tests/tstutil.py16
3 files changed, 138 insertions, 5 deletions
diff --git a/ipalib/plugable.py b/ipalib/plugable.py
index 1df3f836c..66cb18fe5 100644
--- a/ipalib/plugable.py
+++ b/ipalib/plugable.py
@@ -426,8 +426,8 @@ class NameSpace(ReadOnly):
def __contains__(self, name):
"""
- Returns True if this NameSpace contains a member named ``name``; returns
- False otherwise.
+ Returns True if instance contains a member named ``name``, otherwise
+ False.
:param name: The name of a potential member
"""
@@ -435,8 +435,10 @@ class NameSpace(ReadOnly):
def __getitem__(self, name):
"""
- If this NameSpace contains a member named ``name``, returns that member;
- otherwise raises KeyError.
+ Returns the member named ``name``.
+
+ Raises KeyError if this NameSpace does not contain a member named
+ ``name``.
:param name: The name of member to retrieve
"""
@@ -468,6 +470,66 @@ class NameSpace(ReadOnly):
return '%s(<%d members>)' % (self.__class__.__name__, len(self))
+class DictProxy(ReadOnly):
+ """
+ A read-only dict whose items can also be accessed as attributes.
+
+ Although a DictProxy is read-only, the underlying dict can change (and is
+ assumed to).
+
+ One of these is created for each allowed base class in a `Registrar`
+ instance.
+ """
+ def __init__(self, d):
+ """
+ :param d: The ``dict`` instance to proxy.
+ """
+ self.__d = d
+ self.__lock__()
+ assert self.__islocked__()
+
+ 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):
def __init__(self, *allowed):
"""
diff --git a/ipalib/tests/test_plugable.py b/ipalib/tests/test_plugable.py
index c65db0154..5c907dc7a 100644
--- a/ipalib/tests/test_plugable.py
+++ b/ipalib/tests/test_plugable.py
@@ -21,7 +21,8 @@
Unit tests for `ipalib.plugable` module.
"""
-from tstutil import raises, getitem, no_set, no_del, read_only
+from tstutil import raises, no_set, no_del, read_only
+from tstutil import getitem, setitem, delitem
from tstutil import ClassChecker
from ipalib import plugable, errors
@@ -452,6 +453,60 @@ class test_NameSpace(ClassChecker):
no_set(ns, name)
+class test_DictProxy(ClassChecker):
+ """
+ Tests the `plugable.DictProxy` class.
+ """
+ _cls = plugable.DictProxy
+
+ def test_class(self):
+ assert self.cls.__bases__ == (plugable.ReadOnly,)
+
+ def test_DictProxy(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
diff --git a/ipalib/tests/tstutil.py b/ipalib/tests/tstutil.py
index 7b3a2d5ea..79e8ae383 100644
--- a/ipalib/tests/tstutil.py
+++ b/ipalib/tests/tstutil.py
@@ -60,6 +60,22 @@ def getitem(obj, key):
return obj[key]
+def setitem(obj, key, value):
+ """
+ Works like setattr but for dictionary interface. Uses this in combination
+ with raises() to test that, for example, TypeError is raised.
+ """
+ obj[key] = value
+
+
+def delitem(obj, key):
+ """
+ Works like delattr but for dictionary interface. Uses this in combination
+ with raises() to test that, for example, TypeError is raised.
+ """
+ del obj[key]
+
+
def no_set(obj, name, value='some_new_obj'):
"""
Tests that attribute cannot be set.