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