OLD | NEW |
| (Empty) |
1 from __future__ import unicode_literals | |
2 from __future__ import absolute_import | |
3 from . import util | |
4 from copy import deepcopy | |
5 | |
6 | |
7 class OrderedDict(dict): | |
8 """ | |
9 A dictionary that keeps its keys in the order in which they're inserted. | |
10 | |
11 Copied from Django's SortedDict with some modifications. | |
12 | |
13 """ | |
14 def __new__(cls, *args, **kwargs): | |
15 instance = super(OrderedDict, cls).__new__(cls, *args, **kwargs) | |
16 instance.keyOrder = [] | |
17 return instance | |
18 | |
19 def __init__(self, data=None): | |
20 if data is None or isinstance(data, dict): | |
21 data = data or [] | |
22 super(OrderedDict, self).__init__(data) | |
23 self.keyOrder = list(data) if data else [] | |
24 else: | |
25 super(OrderedDict, self).__init__() | |
26 super_set = super(OrderedDict, self).__setitem__ | |
27 for key, value in data: | |
28 # Take the ordering from first key | |
29 if key not in self: | |
30 self.keyOrder.append(key) | |
31 # But override with last value in data (dict() does this) | |
32 super_set(key, value) | |
33 | |
34 def __deepcopy__(self, memo): | |
35 return self.__class__([(key, deepcopy(value, memo)) | |
36 for key, value in self.items()]) | |
37 | |
38 def __copy__(self): | |
39 # The Python's default copy implementation will alter the state | |
40 # of self. The reason for this seems complex but is likely related to | |
41 # subclassing dict. | |
42 return self.copy() | |
43 | |
44 def __setitem__(self, key, value): | |
45 if key not in self: | |
46 self.keyOrder.append(key) | |
47 super(OrderedDict, self).__setitem__(key, value) | |
48 | |
49 def __delitem__(self, key): | |
50 super(OrderedDict, self).__delitem__(key) | |
51 self.keyOrder.remove(key) | |
52 | |
53 def __iter__(self): | |
54 return iter(self.keyOrder) | |
55 | |
56 def __reversed__(self): | |
57 return reversed(self.keyOrder) | |
58 | |
59 def pop(self, k, *args): | |
60 result = super(OrderedDict, self).pop(k, *args) | |
61 try: | |
62 self.keyOrder.remove(k) | |
63 except ValueError: | |
64 # Key wasn't in the dictionary in the first place. No problem. | |
65 pass | |
66 return result | |
67 | |
68 def popitem(self): | |
69 result = super(OrderedDict, self).popitem() | |
70 self.keyOrder.remove(result[0]) | |
71 return result | |
72 | |
73 def _iteritems(self): | |
74 for key in self.keyOrder: | |
75 yield key, self[key] | |
76 | |
77 def _iterkeys(self): | |
78 for key in self.keyOrder: | |
79 yield key | |
80 | |
81 def _itervalues(self): | |
82 for key in self.keyOrder: | |
83 yield self[key] | |
84 | |
85 if util.PY3: # pragma: no cover | |
86 items = _iteritems | |
87 keys = _iterkeys | |
88 values = _itervalues | |
89 else: # pragma: no cover | |
90 iteritems = _iteritems | |
91 iterkeys = _iterkeys | |
92 itervalues = _itervalues | |
93 | |
94 def items(self): | |
95 return [(k, self[k]) for k in self.keyOrder] | |
96 | |
97 def keys(self): | |
98 return self.keyOrder[:] | |
99 | |
100 def values(self): | |
101 return [self[k] for k in self.keyOrder] | |
102 | |
103 def update(self, dict_): | |
104 for k in dict_: | |
105 self[k] = dict_[k] | |
106 | |
107 def setdefault(self, key, default): | |
108 if key not in self: | |
109 self.keyOrder.append(key) | |
110 return super(OrderedDict, self).setdefault(key, default) | |
111 | |
112 def value_for_index(self, index): | |
113 """Returns the value of the item at the given zero-based index.""" | |
114 return self[self.keyOrder[index]] | |
115 | |
116 def insert(self, index, key, value): | |
117 """Inserts the key, value pair before the item with the given index.""" | |
118 if key in self.keyOrder: | |
119 n = self.keyOrder.index(key) | |
120 del self.keyOrder[n] | |
121 if n < index: | |
122 index -= 1 | |
123 self.keyOrder.insert(index, key) | |
124 super(OrderedDict, self).__setitem__(key, value) | |
125 | |
126 def copy(self): | |
127 """Returns a copy of this object.""" | |
128 # This way of initializing the copy means it works for subclasses, too. | |
129 return self.__class__(self) | |
130 | |
131 def __repr__(self): | |
132 """ | |
133 Replaces the normal dict.__repr__ with a version that returns the keys | |
134 in their Ordered order. | |
135 """ | |
136 return '{%s}' % ', '.join( | |
137 ['%r: %r' % (k, v) for k, v in self._iteritems()] | |
138 ) | |
139 | |
140 def clear(self): | |
141 super(OrderedDict, self).clear() | |
142 self.keyOrder = [] | |
143 | |
144 def index(self, key): | |
145 """ Return the index of a given key. """ | |
146 try: | |
147 return self.keyOrder.index(key) | |
148 except ValueError: | |
149 raise ValueError("Element '%s' was not found in OrderedDict" % key) | |
150 | |
151 def index_for_location(self, location): | |
152 """ Return index or None for a given location. """ | |
153 if location == '_begin': | |
154 i = 0 | |
155 elif location == '_end': | |
156 i = None | |
157 elif location.startswith('<') or location.startswith('>'): | |
158 i = self.index(location[1:]) | |
159 if location.startswith('>'): | |
160 if i >= len(self): | |
161 # last item | |
162 i = None | |
163 else: | |
164 i += 1 | |
165 else: | |
166 raise ValueError('Not a valid location: "%s". Location key ' | |
167 'must start with a ">" or "<".' % location) | |
168 return i | |
169 | |
170 def add(self, key, value, location): | |
171 """ Insert by key location. """ | |
172 i = self.index_for_location(location) | |
173 if i is not None: | |
174 self.insert(i, key, value) | |
175 else: | |
176 self.__setitem__(key, value) | |
177 | |
178 def link(self, key, location): | |
179 """ Change location of an existing item. """ | |
180 n = self.keyOrder.index(key) | |
181 del self.keyOrder[n] | |
182 try: | |
183 i = self.index_for_location(location) | |
184 if i is not None: | |
185 self.keyOrder.insert(i, key) | |
186 else: | |
187 self.keyOrder.append(key) | |
188 except Exception as e: | |
189 # restore to prevent data loss and reraise | |
190 self.keyOrder.insert(n, key) | |
191 raise e | |
OLD | NEW |