/topp/utils/orderedpersistentmapping.py

https://github.com/socialplanning/topp.utils
Python | 201 lines | 176 code | 16 blank | 9 comment | 8 complexity | 6218b22ab8e820cfc07da93d89a1277d MD5 | raw file
  1. from persistent.mapping import PersistentMapping as BaseDict
  2. _marker = "__marker__"
  3. class OrderedPersistentMapping(BaseDict):
  4. """A subclass of PersistentMapping that provides an ordering for
  5. keys() and items(). Almost entirely taken from Archetypes's
  6. utils.OrderedDict class."""
  7. def __init__(self, dict=None):
  8. self._keys = []
  9. BaseDict.__init__(self, dict)
  10. if dict is not None:
  11. self._keys = self.data.keys()
  12. def __setitem__(self, key, item):
  13. if not self.data.has_key(key):
  14. self._keys.append(key)
  15. return BaseDict.__setitem__(self, key, item)
  16. def __delitem__(self, key):
  17. BaseDict.__delitem__(self, key)
  18. self._keys.remove(key)
  19. def clear(self):
  20. BaseDict.clear(self)
  21. self._keys = []
  22. def keys(self):
  23. return self._keys
  24. def items(self):
  25. return [(k, self.get(k)) for k in self._keys]
  26. def reverse(self):
  27. items = list(self.items())
  28. items.reverse()
  29. return items
  30. def values(self):
  31. return [self.get(k) for k in self._keys]
  32. def update(self, dict):
  33. for k in dict.keys():
  34. if not self.data.has_key(k):
  35. self._keys.append(k)
  36. return BaseDict.update(self, dict)
  37. def copy(self):
  38. if self.__class__ is OrderedPersistentMapping:
  39. c = OrderedPersistentMapping()
  40. for k, v in self.items():
  41. c[k] = v
  42. return c
  43. import copy
  44. c = copy.copy(self)
  45. return c
  46. def setdefault(self, key, failobj=None):
  47. if not self.data.has_key(key):
  48. self._keys.append(key)
  49. return BaseDict.setdefault(self, key, failobj)
  50. def popitem(self):
  51. if not self.data:
  52. raise KeyError, 'dictionary is empty'
  53. k = self._keys.pop()
  54. v = self.data.get(k)
  55. del self.data[k]
  56. return (k, v)
  57. def pop(self, key, default=_marker):
  58. if default is not _marker:
  59. v = self.data.pop(key, default)
  60. if key in self._keys:
  61. self._keys.remove(key)
  62. self._p_changed = True
  63. else:
  64. v = self.data.pop(key) # will raise KeyError if needed
  65. self._keys.remove(key)
  66. self._p_changed = True
  67. return v
  68. def __iter__(self):
  69. return iter(self._keys)
  70. # A non-persistent version.
  71. # Copied from Django, which is licensed under a BSD-ish license:
  72. # see http://code.djangoproject.com/browser/django/trunk/LICENSE
  73. # and http://code.djangoproject.com/browser/django/trunk/django/utils/datastructures.py
  74. class SortedDict(dict):
  75. """
  76. A dictionary that keeps its keys in the order in which they're inserted.
  77. """
  78. def __init__(self, data=None):
  79. if data is None:
  80. data = {}
  81. super(SortedDict, self).__init__(data)
  82. if isinstance(data, dict):
  83. self.keyOrder = data.keys()
  84. else:
  85. self.keyOrder = []
  86. for key, value in data:
  87. if key not in self.keyOrder:
  88. self.keyOrder.append(key)
  89. def __deepcopy__(self, memo):
  90. from copy import deepcopy
  91. return self.__class__([(key, deepcopy(value, memo))
  92. for key, value in self.iteritems()])
  93. def __setitem__(self, key, value):
  94. super(SortedDict, self).__setitem__(key, value)
  95. if key not in self.keyOrder:
  96. self.keyOrder.append(key)
  97. def __delitem__(self, key):
  98. super(SortedDict, self).__delitem__(key)
  99. self.keyOrder.remove(key)
  100. def __iter__(self):
  101. for k in self.keyOrder:
  102. yield k
  103. def pop(self, k, *args):
  104. result = super(SortedDict, self).pop(k, *args)
  105. try:
  106. self.keyOrder.remove(k)
  107. except ValueError:
  108. # Key wasn't in the dictionary in the first place. No problem.
  109. pass
  110. return result
  111. def popitem(self):
  112. result = super(SortedDict, self).popitem()
  113. self.keyOrder.remove(result[0])
  114. return result
  115. def items(self):
  116. return zip(self.keyOrder, self.values())
  117. def iteritems(self):
  118. for key in self.keyOrder:
  119. yield key, super(SortedDict, self).__getitem__(key)
  120. def keys(self):
  121. return self.keyOrder[:]
  122. def iterkeys(self):
  123. return iter(self.keyOrder)
  124. def values(self):
  125. return [super(SortedDict, self).__getitem__(k) for k in self.keyOrder]
  126. def itervalues(self):
  127. for key in self.keyOrder:
  128. yield super(SortedDict, self).__getitem__(key)
  129. def update(self, dict_):
  130. for k, v in dict_.items():
  131. self.__setitem__(k, v)
  132. def setdefault(self, key, default):
  133. if key not in self.keyOrder:
  134. self.keyOrder.append(key)
  135. return super(SortedDict, self).setdefault(key, default)
  136. def value_for_index(self, index):
  137. """Returns the value of the item at the given zero-based index."""
  138. return self[self.keyOrder[index]]
  139. def insert(self, index, key, value):
  140. """Inserts the key, value pair before the item with the given index."""
  141. if key in self.keyOrder:
  142. n = self.keyOrder.index(key)
  143. del self.keyOrder[n]
  144. if n < index:
  145. index -= 1
  146. self.keyOrder.insert(index, key)
  147. super(SortedDict, self).__setitem__(key, value)
  148. def copy(self):
  149. """Returns a copy of this object."""
  150. # This way of initializing the copy means it works for subclasses, too.
  151. obj = self.__class__(self)
  152. obj.keyOrder = self.keyOrder[:]
  153. return obj
  154. def __repr__(self):
  155. """
  156. Replaces the normal dict.__repr__ with a version that returns the keys
  157. in their sorted order.
  158. """
  159. return '{%s}' % ', '.join(['%r: %r' % (k, v) for k, v in self.items()])
  160. def clear(self):
  161. super(SortedDict, self).clear()
  162. self.keyOrder = []