diff options
author | Petr Viktorin <pviktori@redhat.com> | 2013-02-05 10:24:46 -0500 |
---|---|---|
committer | Petr Viktorin <pviktori@redhat.com> | 2013-09-25 10:13:56 +0200 |
commit | 0226064baced57f09b899370752c63cce8009b61 (patch) | |
tree | 89e0cb981f255e4c4c1ca47ff6258742382d1621 | |
parent | 468e5e40ccc9251cdb6b2d1ad95e32dc588be332 (diff) | |
download | freeipa-0226064baced57f09b899370752c63cce8009b61.tar.gz freeipa-0226064baced57f09b899370752c63cce8009b61.tar.xz freeipa-0226064baced57f09b899370752c63cce8009b61.zip |
Add missing dict methods to CIDict
Make the CIDict interface match standard dict (except view* methods).
Add __contains__, __iter__, clear.
Add keyword and iterable support for __init__, update.
Also add values() and itervalues(). Previously the dict versions were
used; the new ones guarantee that the order matches keys().
Mark view* methods as not implemented.
CIDict.copy() now returns a CIDict.
Test the above additions, and fromkeys() which worked but wasn't tested.
-rw-r--r-- | ipapython/ipautil.py | 67 | ||||
-rw-r--r-- | ipatests/test_ipapython/test_ipautil.py | 69 |
2 files changed, 115 insertions, 21 deletions
diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py index 92569c3b4..c5b47f5b4 100644 --- a/ipapython/ipautil.py +++ b/ipapython/ipautil.py @@ -444,10 +444,13 @@ class CIDict(dict): If you extend UserDict, isinstance(foo, dict) returns false. """ - def __init__(self, default=None): + def __init__(self, default=None, **kwargs): super(CIDict, self).__init__() - self._keys = {} - self.update(default or {}) + self._keys = {} # mapping of lowercased keys to proper case + if default: + self.update(default) + if kwargs: + self.update(kwargs) def __getitem__(self, key): return super(CIDict, self).__getitem__(key.lower()) @@ -460,11 +463,22 @@ class CIDict(dict): def __delitem__(self, key): lower_key = key.lower() del self._keys[lower_key] - return super(CIDict, self).__delitem__(key.lower()) + return super(CIDict, self).__delitem__(lower_key) + + def update(self, new=None, **kwargs): + if new: + try: + keys = new.keys + except AttributeError: + self.update(dict(new)) + else: + for key in keys(): + self[key] = new[key] + for key, value in kwargs.iteritems(): + self[key] = value - def update(self, dict): - for key in dict.keys(): - self[key] = dict[key] + def __contains__(self, key): + return super(CIDict, self).__contains__(key.lower()) def has_key(self, key): return super(CIDict, self).has_key(key.lower()) @@ -475,26 +489,30 @@ class CIDict(dict): except KeyError: return failobj + def __iter__(self): + return self._keys.itervalues() + def keys(self): - return self._keys.values() + return list(self.iterkeys()) def items(self): - result = [] - for k in self._keys.values(): - result.append((k, self[k])) - return result + return list(self.iteritems()) + + def values(self): + return list(self.itervalues()) def copy(self): - copy = {} - for k in self._keys.values(): - copy[k] = self[k] - return copy + """Returns a shallow copy of this CIDict""" + return CIDict(self.items()) def iteritems(self): - return self.copy().iteritems() + return ((k, self[k]) for k in self._keys.itervalues()) def iterkeys(self): - return self.copy().iterkeys() + return self._keys.itervalues() + + def itervalues(self): + return (v for k, v in self.iteritems()) def setdefault(self, key, value=None): try: @@ -520,6 +538,19 @@ class CIDict(dict): return (key, value) + def clear(self): + self._keys.clear() + return super(CIDict, self).clear() + + def viewitems(self): + raise NotImplementedError('CIDict.viewitems is not implemented') + + def viewkeys(self): + raise NotImplementedError('CIDict.viewkeys is not implemented') + + def viewvvalues(self): + raise NotImplementedError('CIDict.viewvvalues is not implemented') + class GeneralizedTimeZone(datetime.tzinfo): """This class is a basic timezone wrapper for the offset specified diff --git a/ipatests/test_ipapython/test_ipautil.py b/ipatests/test_ipapython/test_ipautil.py index 95933581f..a8a73edcd 100644 --- a/ipatests/test_ipapython/test_ipautil.py +++ b/ipatests/test_ipapython/test_ipautil.py @@ -76,6 +76,18 @@ class TestCIDict(object): self.cidict["key2"] = "val2" self.cidict["KEY3"] = "VAL3" + def test_init(self): + cidict = ipautil.CIDict() + assert dict(cidict.items()) == {} + cidict = ipautil.CIDict([('a', 2), ('b', 3), ('C', 4)]) + assert dict(cidict.items()) == {'a': 2, 'b': 3, 'C': 4} + cidict = ipautil.CIDict([('a', 2), ('b', None)], b=3, C=4) + assert dict(cidict.items()) == {'a': 2, 'b': 3, 'C': 4} + cidict = ipautil.CIDict({'a': 2, 'b': None}, b=3, C=4) + assert dict(cidict.items()) == {'a': 2, 'b': 3, 'C': 4} + cidict = ipautil.CIDict(a=2, b=3, C=4) + assert dict(cidict.items()) == {'a': 2, 'b': 3, 'C': 4} + def test_len(self): nose.tools.assert_equal(3, len(self.cidict)) @@ -117,19 +129,27 @@ class TestCIDict(object): nose.tools.assert_equal(0, len(self.cidict)) def test_copy(self): - """A copy is no longer a CIDict, but should preserve the case of - the keys as they were inserted.""" copy = self.cidict.copy() + assert copy == self.cidict nose.tools.assert_equal(3, len(copy)) assert copy.has_key("Key1") + assert copy.has_key("key1") nose.tools.assert_equal("val1", copy["Key1"]) - assert not copy.has_key("key1") def test_haskey(self): assert self.cidict.has_key("KEY1") assert self.cidict.has_key("key2") assert self.cidict.has_key("key3") + assert not self.cidict.has_key("Key4") + + def test_contains(self): + assert "KEY1" in self.cidict + assert "key2" in self.cidict + assert "key3" in self.cidict + + assert "Key4" not in self.cidict + def test_items(self): items = self.cidict.items() nose.tools.assert_equal(3, len(items)) @@ -138,6 +158,14 @@ class TestCIDict(object): assert ("key2", "val2") in items_set assert ("KEY3", "VAL3") in items_set + assert self.cidict.items() == list(self.cidict.iteritems()) == zip( + self.cidict.iterkeys(), self.cidict.itervalues()) + + def test_iter(self): + items = [] + assert list(self.cidict) == list(self.cidict.keys()) + assert sorted(self.cidict) == sorted(['Key1', 'key2', 'KEY3']) + def test_iteritems(self): items = [] for (k,v) in self.cidict.iteritems(): @@ -176,6 +204,8 @@ class TestCIDict(object): assert "key2" in keys_set assert "KEY3" in keys_set + assert self.cidict.keys() == list(self.cidict.iterkeys()) + def test_values(self): values = self.cidict.values() nose.tools.assert_equal(3, len(values)) @@ -184,6 +214,8 @@ class TestCIDict(object): assert "val2" in values_set assert "VAL3" in values_set + assert self.cidict.values() == list(self.cidict.itervalues()) + def test_update(self): newdict = { "KEY2": "newval2", "key4": "val4" } @@ -199,6 +231,23 @@ class TestCIDict(object): assert ("KEY3", "VAL3") in items_set assert ("key4", "val4") in items_set + def test_update_dict_and_kwargs(self): + self.cidict.update({'a': 'va', 'b': None}, b='vb', key2='v2') + assert dict(self.cidict.items()) == { + 'a': 'va', 'b': 'vb', + 'Key1': 'val1', 'key2': 'v2', 'KEY3': 'VAL3'} + + def test_update_list_and_kwargs(self): + self.cidict.update([('a', 'va'), ('b', None)], b='vb', key2='val2') + assert dict(self.cidict.items()) == { + 'a': 'va', 'b': 'vb', + 'Key1': 'val1', 'key2': 'val2', 'KEY3': 'VAL3'} + + def test_update_kwargs(self): + self.cidict.update(b='vb', key2='val2') + assert dict(self.cidict.items()) == { + 'b': 'vb', 'Key1': 'val1', 'key2': 'val2', 'KEY3': 'VAL3'} + def test_setdefault(self): nose.tools.assert_equal("val1", self.cidict.setdefault("KEY1", "default")) @@ -242,6 +291,20 @@ class TestCIDict(object): assert item in items items.discard(item) + def test_fromkeys(self): + dct = ipautil.CIDict.fromkeys(('A', 'b', 'C')) + assert sorted(dct.keys()) == sorted(['A', 'b', 'C']) + assert sorted(dct.values()) == [None] * 3 + + def test_clear(self): + self.cidict.clear() + assert self.cidict == {} + assert self.cidict.keys() == [] + assert self.cidict.values() == [] + assert self.cidict.items() == [] + assert self.cidict._keys == {} + + class TestTimeParser(object): def test_simple(self): timestr = "20070803" |