1"""A more or less complete user-defined wrapper around dictionary objects."""
2
3class UserDict:
4    def __init__(self, dict=None, **kwargs):
5        self.data = {}
6        if dict is not None:
7            self.update(dict)
8        if len(kwargs):
9            self.update(kwargs)
10    def __repr__(self): return repr(self.data)
11    def __cmp__(self, dict):
12        if isinstance(dict, UserDict):
13            return cmp(self.data, dict.data)
14        else:
15            return cmp(self.data, dict)
16    __hash__ = None # Avoid Py3k warning
17    def __len__(self): return len(self.data)
18    def __getitem__(self, key):
19        if key in self.data:
20            return self.data[key]
21        if hasattr(self.__class__, "__missing__"):
22            return self.__class__.__missing__(self, key)
23        raise KeyError(key)
24    def __setitem__(self, key, item): self.data[key] = item
25    def __delitem__(self, key): del self.data[key]
26    def clear(self): self.data.clear()
27    def copy(self):
28        if self.__class__ is UserDict:
29            return UserDict(self.data.copy())
30        import copy
31        data = self.data
32        try:
33            self.data = {}
34            c = copy.copy(self)
35        finally:
36            self.data = data
37        c.update(self)
38        return c
39    def keys(self): return self.data.keys()
40    def items(self): return self.data.items()
41    def iteritems(self): return self.data.iteritems()
42    def iterkeys(self): return self.data.iterkeys()
43    def itervalues(self): return self.data.itervalues()
44    def values(self): return self.data.values()
45    def has_key(self, key): return key in self.data
46    def update(self, dict=None, **kwargs):
47        if dict is None:
48            pass
49        elif isinstance(dict, UserDict):
50            self.data.update(dict.data)
51        elif isinstance(dict, type({})) or not hasattr(dict, 'items'):
52            self.data.update(dict)
53        else:
54            for k, v in dict.items():
55                self[k] = v
56        if len(kwargs):
57            self.data.update(kwargs)
58    def get(self, key, failobj=None):
59        if key not in self:
60            return failobj
61        return self[key]
62    def setdefault(self, key, failobj=None):
63        if key not in self:
64            self[key] = failobj
65        return self[key]
66    def pop(self, key, *args):
67        return self.data.pop(key, *args)
68    def popitem(self):
69        return self.data.popitem()
70    def __contains__(self, key):
71        return key in self.data
72    @classmethod
73    def fromkeys(cls, iterable, value=None):
74        d = cls()
75        for key in iterable:
76            d[key] = value
77        return d
78
79class IterableUserDict(UserDict):
80    def __iter__(self):
81        return iter(self.data)
82
83import _abcoll
84_abcoll.MutableMapping.register(IterableUserDict)
85
86
87class DictMixin:
88    # Mixin defining all dictionary methods for classes that already have
89    # a minimum dictionary interface including getitem, setitem, delitem,
90    # and keys. Without knowledge of the subclass constructor, the mixin
91    # does not define __init__() or copy().  In addition to the four base
92    # methods, progressively more efficiency comes with defining
93    # __contains__(), __iter__(), and iteritems().
94
95    # second level definitions support higher levels
96    def __iter__(self):
97        for k in self.keys():
98            yield k
99    def has_key(self, key):
100        try:
101            self[key]
102        except KeyError:
103            return False
104        return True
105    def __contains__(self, key):
106        return self.has_key(key)
107
108    # third level takes advantage of second level definitions
109    def iteritems(self):
110        for k in self:
111            yield (k, self[k])
112    def iterkeys(self):
113        return self.__iter__()
114
115    # fourth level uses definitions from lower levels
116    def itervalues(self):
117        for _, v in self.iteritems():
118            yield v
119    def values(self):
120        return [v for _, v in self.iteritems()]
121    def items(self):
122        return list(self.iteritems())
123    def clear(self):
124        for key in self.keys():
125            del self[key]
126    def setdefault(self, key, default=None):
127        try:
128            return self[key]
129        except KeyError:
130            self[key] = default
131        return default
132    def pop(self, key, *args):
133        if len(args) > 1:
134            raise TypeError, "pop expected at most 2 arguments, got "\
135                              + repr(1 + len(args))
136        try:
137            value = self[key]
138        except KeyError:
139            if args:
140                return args[0]
141            raise
142        del self[key]
143        return value
144    def popitem(self):
145        try:
146            k, v = self.iteritems().next()
147        except StopIteration:
148            raise KeyError, 'container is empty'
149        del self[k]
150        return (k, v)
151    def update(self, other=None, **kwargs):
152        # Make progressively weaker assumptions about "other"
153        if other is None:
154            pass
155        elif hasattr(other, 'iteritems'):  # iteritems saves memory and lookups
156            for k, v in other.iteritems():
157                self[k] = v
158        elif hasattr(other, 'keys'):
159            for k in other.keys():
160                self[k] = other[k]
161        else:
162            for k, v in other:
163                self[k] = v
164        if kwargs:
165            self.update(kwargs)
166    def get(self, key, default=None):
167        try:
168            return self[key]
169        except KeyError:
170            return default
171    def __repr__(self):
172        return repr(dict(self.iteritems()))
173    def __cmp__(self, other):
174        if other is None:
175            return 1
176        if isinstance(other, DictMixin):
177            other = dict(other.iteritems())
178        return cmp(dict(self.iteritems()), other)
179    def __len__(self):
180        return len(self.keys())
181