text: defer calls to setStyleSheet()
[git-cola.git] / cola / ordered_dict.py
blob0181c7485c62ef356fe85e4cb3e710bfacb803e4
1 """Backport of OrderedDict() class
3 Runs on Python 2.4, 2.5, 2.6, 2.7, 3.x and pypy.
5 Passes Python2.7's test suite and incorporates all the latest updates.
6 Copyright 2009 Raymond Hettinger, released under the MIT License.
7 http://code.activestate.com/recipes/576693/
9 """
10 from __future__ import absolute_import, division, unicode_literals
11 try:
12 # Python2's "thread" module must be tried first
13 from thread import get_ident as _get_ident
14 except ImportError:
15 # Python2 also contains a "threading" module, but it does not
16 # contain get_ident(), so this part would fail if it were done first.
17 # get_ident() become available in the consolidated "threading" module
18 # until Python3.
19 from threading import get_ident as _get_ident
22 class OrderedDict(dict):
23 'Dictionary that remembers insertion order'
24 # An inherited dict maps keys to values.
25 # The inherited dict provides __getitem__, __len__, __contains__, and get.
26 # The remaining methods are order-aware.
27 # Big-O runtimes for all methods are the same as for regular dictionaries.
29 # The self.__map dictionary maps keys to links in a doubly linked list.
30 # The circular doubly linked list starts and ends with a sentinel element.
31 # The sentinel element never gets deleted (this simplifies the algorithm).
32 # Each link is stored as a list of length three: [PREV, NEXT, KEY].
34 # pylint: disable=super-init-not-called
35 def __init__(self, *args, **kwds):
36 '''Initialize an ordered dictionary. Signature is the same as for
37 regular dictionaries, but keyword arguments are not recommended
38 because their insertion order is arbitrary.
40 '''
41 if len(args) > 1:
42 raise TypeError('expected at most 1 arguments, got %d' % len(args))
43 try:
44 self.__root
45 except AttributeError:
46 self.__root = root = [] # sentinel node
47 root[:] = [root, root, None]
48 self.__map = {}
49 self.__update(*args, **kwds)
51 def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
52 'od.__setitem__(i, y) <==> od[i]=y'
53 # Setting a new item creates a new link which at the end of the linked
54 # list, and the inherited dictionary is updated.
55 if key not in self:
56 root = self.__root
57 last = root[0]
58 last[1] = root[0] = self.__map[key] = [last, root, key]
59 dict_setitem(self, key, value)
61 def __delitem__(self, key, dict_delitem=dict.__delitem__):
62 'od.__delitem__(y) <==> del od[y]'
63 # Deleting an existing item uses self.__map to find the link which is
64 # then removed by updating the links in the predecessor and
65 # successor nodes.
66 dict_delitem(self, key)
67 link_prev, link_next, key = self.__map.pop(key)
68 link_prev[1] = link_next
69 link_next[0] = link_prev
71 def __iter__(self):
72 'od.__iter__() <==> iter(od)'
73 root = self.__root
74 curr = root[1]
75 while curr is not root:
76 yield curr[2]
77 curr = curr[1]
79 def __reversed__(self):
80 'od.__reversed__() <==> reversed(od)'
81 root = self.__root
82 curr = root[0]
83 while curr is not root:
84 yield curr[2]
85 curr = curr[0]
87 def clear(self):
88 'od.clear() -> None. Remove all items from od.'
89 try:
90 for node in self.__map.values():
91 del node[:]
92 root = self.__root
93 root[:] = [root, root, None]
94 self.__map.clear()
95 except AttributeError:
96 pass
97 dict.clear(self)
99 def popitem(self, last=True):
100 '''od.popitem() -> (k, v), return and remove a (key, value) pair.
101 Pairs are returned in LIFO order if last is true or FIFO order if false.
104 if not self:
105 raise KeyError('dictionary is empty')
106 root = self.__root
107 if last:
108 link = root[0]
109 link_prev = link[0]
110 link_prev[1] = root
111 root[0] = link_prev
112 else:
113 link = root[1]
114 link_next = link[1]
115 root[1] = link_next
116 link_next[0] = root
117 key = link[2]
118 del self.__map[key]
119 value = dict.pop(self, key)
120 return key, value
122 # -- the following methods do not depend on the internal structure --
124 def keys(self):
125 'od.keys() -> list of keys in od'
126 return list(self)
128 def values(self):
129 'od.values() -> list of values in od'
130 return [self[key] for key in self]
132 def items(self):
133 'od.items() -> list of (key, value) pairs in od'
134 return [(key, self[key]) for key in self]
136 def iterkeys(self):
137 'od.iterkeys() -> an iterator over the keys in od'
138 return iter(self)
140 def itervalues(self):
141 'od.itervalues -> an iterator over the values in od'
142 for k in self:
143 yield self[k]
145 def iteritems(self):
146 'od.iteritems -> an iterator over the (key, value) items in od'
147 for k in self:
148 yield (k, self[k])
150 # pylint: disable=no-method-argument
151 def update(*args, **kwds):
152 '''od.update(E, **F) -> None. Update od from dict/iterable E and F.
154 If E is a dict instance, does: for k in E: od[k] = E[k]
155 If E has a .keys() method, does: for k in E.keys(): od[k] = E[k]
156 Or if E is an iterable of items, does: for k, v in E: od[k] = v
157 In either case, this is followed by: for k, v in F.items(): od[k] = v
160 if len(args) > 2:
161 raise TypeError('update() takes at most 2 positional '
162 'arguments (%d given)' % (len(args),))
163 elif not args:
164 raise TypeError('update() takes at least 1 argument (0 given)')
165 self = args[0]
166 # Make progressively weaker assumptions about "other"
167 other = ()
168 if len(args) == 2:
169 other = args[1]
170 if isinstance(other, dict):
171 for key in other:
172 self[key] = other[key]
173 elif hasattr(other, 'keys'):
174 for key in other.keys():
175 self[key] = other[key]
176 else:
177 for key, value in other:
178 self[key] = value
179 for key, value in kwds.items():
180 self[key] = value
182 # let subclasses override update without breaking __init__
183 __update = update
185 __marker = object()
187 def pop(self, key, default=__marker):
188 '''od.pop(k[,d]) -> v, remove specified key and return the value.
190 If key is not found, d is returned if given, otherwise
191 KeyError is raised.
194 if key in self:
195 result = self[key]
196 del self[key]
197 return result
198 if default is self.__marker:
199 raise KeyError(key)
200 return default
202 def setdefault(self, key, default=None):
203 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
204 if key in self:
205 return self[key]
206 self[key] = default
207 return default
209 # pylint: disable=dangerous-default-value
210 def __repr__(self, _repr_running={}):
211 'od.__repr__() <==> repr(od)'
212 call_key = id(self), _get_ident()
213 if call_key in _repr_running:
214 return '...'
215 _repr_running[call_key] = 1
216 try:
217 if not self:
218 return '%s()' % (self.__class__.__name__,)
219 items = list(self.items())
220 return '%s(%r)' % (self.__class__.__name__, items)
221 finally:
222 del _repr_running[call_key]
224 def __reduce__(self):
225 'Return state information for pickling'
226 items = [[k, self[k]] for k in self]
227 inst_dict = vars(self).copy()
228 for k in vars(OrderedDict()):
229 inst_dict.pop(k, None)
230 if inst_dict:
231 return (self.__class__, (items,), inst_dict)
232 return self.__class__, (items,)
234 def copy(self):
235 'od.copy() -> a shallow copy of od'
236 return self.__class__(self)
238 @classmethod
239 def fromkeys(cls, iterable, value=None):
240 '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
241 and values equal to v (which defaults to None).
244 d = cls()
245 for key in iterable:
246 d[key] = value
247 return d
249 def __hash__(self):
250 """Stable hash value"""
251 # pylint: disable=dict-items-not-iterating
252 return hash(frozenset(self.items()))
254 def __eq__(self, other):
255 '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive
256 while comparison to a regular mapping is order-insensitive.
259 if isinstance(other, OrderedDict):
260 return (len(self) == len(other)
261 and list(self.items()) == list(other.items()))
262 return dict.__eq__(self, other)
264 def __ne__(self, other):
265 return not self == other