mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-01-11 00:31:56 -06:00
159: Added plugable.DictProxy class; added corresponding unit tests; added setitem(), delitem() functions to tstutil
This commit is contained in:
parent
ca53615ddd
commit
b403fd822b
@ -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):
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user