codekingpro/portable-devtools
114k
1"""passlib.utils.compat._ordered_dict -- backport of collections.OrderedDict for py262 3taken from stdlib-suggested recipe at http://code.activestate.com/recipes/576693/4 5this should be imported from passlib.utils.compat.OrderedDict, not here.6"""7 8try:9 from thread import get_ident as _get_ident10except ImportError:11 from dummy_thread import get_ident as _get_ident12 13class OrderedDict(dict):14 """Dictionary that remembers insertion order"""15 # An inherited dict maps keys to values.16 # The inherited dict provides __getitem__, __len__, __contains__, and get.17 # The remaining methods are order-aware.18 # Big-O running times for all methods are the same as for regular dictionaries.19 20 # The internal self.__map dictionary maps keys to links in a doubly linked list.21 # The circular doubly linked list starts and ends with a sentinel element.22 # The sentinel element never gets deleted (this simplifies the algorithm).23 # Each link is stored as a list of length three: [PREV, NEXT, KEY].24 25 def __init__(self, *args, **kwds):26 '''Initialize an ordered dictionary. Signature is the same as for27 regular dictionaries, but keyword arguments are not recommended28 because their insertion order is arbitrary.29 30 '''31 if len(args) > 1:32 raise TypeError('expected at most 1 arguments, got %d' % len(args))33 try:34 self.__root35 except AttributeError:36 self.__root = root = [] # sentinel node37 root[:] = [root, root, None]38 self.__map = {}39 self.__update(*args, **kwds)40 41 def __setitem__(self, key, value, dict_setitem=dict.__setitem__):42 'od.__setitem__(i, y) <==> od[i]=y'43 # Setting a new item creates a new link which goes at the end of the linked44 # list, and the inherited dictionary is updated with the new key/value pair.45 if key not in self:46 root = self.__root47 last = root[0]48 last[1] = root[0] = self.__map[key] = [last, root, key]49 dict_setitem(self, key, value)50 51 def __delitem__(self, key, dict_delitem=dict.__delitem__):52 'od.__delitem__(y) <==> del od[y]'53 # Deleting an existing item uses self.__map to find the link which is54 # then removed by updating the links in the predecessor and successor nodes.55 dict_delitem(self, key)56 link_prev, link_next, key = self.__map.pop(key)57 link_prev[1] = link_next58 link_next[0] = link_prev59 60 def __iter__(self):61 'od.__iter__() <==> iter(od)'62 root = self.__root63 curr = root[1]64 while curr is not root:65 yield curr[2]66 curr = curr[1]67 68 def __reversed__(self):69 'od.__reversed__() <==> reversed(od)'70 root = self.__root71 curr = root[0]72 while curr is not root:73 yield curr[2]74 curr = curr[0]75 76 def clear(self):77 'od.clear() -> None. Remove all items from od.'78 try:79 for node in self.__map.itervalues():80 del node[:]81 root = self.__root82 root[:] = [root, root, None]83 self.__map.clear()84 except AttributeError:85 pass86 dict.clear(self)87 88 def popitem(self, last=True):89 '''od.popitem() -> (k, v), return and remove a (key, value) pair.90 Pairs are returned in LIFO order if last is true or FIFO order if false.91 92 '''93 if not self:94 raise KeyError('dictionary is empty')95 root = self.__root96 if last:97 link = root[0]98 link_prev = link[0]99 link_prev[1] = root100 root[0] = link_prev101 else:102 link = root[1]103 link_next = link[1]104 root[1] = link_next105 link_next[0] = root106 key = link[2]107 del self.__map[key]108 value = dict.pop(self, key)109 return key, value110 111 # -- the following methods do not depend on the internal structure --112 113 def keys(self):114 'od.keys() -> list of keys in od'115 return list(self)116 117 def values(self):118 'od.values() -> list of values in od'119 return [self[key] for key in self]120 121 def items(self):122 'od.items() -> list of (key, value) pairs in od'123 return [(key, self[key]) for key in self]124 125 def iterkeys(self):126 'od.iterkeys() -> an iterator over the keys in od'127 return iter(self)128 129 def itervalues(self):130 'od.itervalues -> an iterator over the values in od'131 for k in self:132 yield self[k]133 134 def iteritems(self):135 'od.iteritems -> an iterator over the (key, value) items in od'136 for k in self:137 yield (k, self[k])138 139 def update(*args, **kwds):140 '''od.update(E, **F) -> None. Update od from dict/iterable E and F.141 142 If E is a dict instance, does: for k in E: od[k] = E[k]143 If E has a .keys() method, does: for k in E.keys(): od[k] = E[k]144 Or if E is an iterable of items, does: for k, v in E: od[k] = v145 In either case, this is followed by: for k, v in F.items(): od[k] = v146 147 '''148 if len(args) > 2:149 raise TypeError('update() takes at most 2 positional '150 'arguments (%d given)' % (len(args),))151 elif not args:152 raise TypeError('update() takes at least 1 argument (0 given)')153 self = args[0]154 # Make progressively weaker assumptions about "other"155 other = ()156 if len(args) == 2:157 other = args[1]158 if isinstance(other, dict):159 for key in other:160 self[key] = other[key]161 elif hasattr(other, 'keys'):162 for key in other.keys():163 self[key] = other[key]164 else:165 for key, value in other:166 self[key] = value167 for key, value in kwds.items():168 self[key] = value169 170 __update = update # let subclasses override update without breaking __init__171 172 __marker = object()173 174 def pop(self, key, default=__marker):175 '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.176 If key is not found, d is returned if given, otherwise KeyError is raised.177 178 '''179 if key in self:180 result = self[key]181 del self[key]182 return result183 if default is self.__marker:184 raise KeyError(key)185 return default186 187 def setdefault(self, key, default=None):188 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'189 if key in self:190 return self[key]191 self[key] = default192 return default193 194 def __repr__(self, _repr_running={}):195 'od.__repr__() <==> repr(od)'196 call_key = id(self), _get_ident()197 if call_key in _repr_running:198 return '...'199 _repr_running[call_key] = 1200 try:201 if not self:202 return '%s()' % (self.__class__.__name__,)203 return '%s(%r)' % (self.__class__.__name__, self.items())204 finally:205 del _repr_running[call_key]206 207 def __reduce__(self):208 'Return state information for pickling'209 items = [[k, self[k]] for k in self]210 inst_dict = vars(self).copy()211 for k in vars(OrderedDict()):212 inst_dict.pop(k, None)213 if inst_dict:214 return (self.__class__, (items,), inst_dict)215 return self.__class__, (items,)216 217 def copy(self):218 'od.copy() -> a shallow copy of od'219 return self.__class__(self)220 221 @classmethod222 def fromkeys(cls, iterable, value=None):223 '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S224 and values equal to v (which defaults to None).225 226 '''227 d = cls()228 for key in iterable:229 d[key] = value230 return d231 232 def __eq__(self, other):233 '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive234 while comparison to a regular mapping is order-insensitive.235 236 '''237 if isinstance(other, OrderedDict):238 return len(self)==len(other) and self.items() == other.items()239 return dict.__eq__(self, other)240 241 def __ne__(self, other):242 return not self == other243 