const.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. # -*- coding: utf-8 -*-
  2. import copy
  3. import warnings
  4. from types import GeneratorType
  5. import six
  6. class SortedDict(dict):
  7. """
  8. A dictionary that keeps its keys in the order in which they're inserted.
  9. """
  10. def __new__(cls, *args, **kwargs):
  11. instance = super(SortedDict, cls).__new__(cls, *args, **kwargs)
  12. instance.keyOrder = []
  13. return instance
  14. def __init__(self, data=None):
  15. if data is None:
  16. data = {}
  17. elif isinstance(data, GeneratorType):
  18. # Unfortunately we need to be able to read a generator twice. Once
  19. # to get the data into self with our super().__init__ call and a
  20. # second time to setup keyOrder correctly
  21. data = list(data)
  22. super(SortedDict, self).__init__(data)
  23. if isinstance(data, dict):
  24. self.keyOrder = list(data)
  25. else:
  26. self.keyOrder = []
  27. seen = set()
  28. for key, value in data:
  29. if key not in seen:
  30. self.keyOrder.append(key)
  31. seen.add(key)
  32. def __deepcopy__(self, memo):
  33. return self.__class__([(key, copy.deepcopy(value, memo))
  34. for key, value in self.iteritems()])
  35. def __copy__(self):
  36. # The Python's default copy implementation will alter the state
  37. # of self. The reason for this seems complex but is likely related to
  38. # subclassing dict.
  39. return self.copy()
  40. def __setitem__(self, key, value):
  41. if key not in self:
  42. self.keyOrder.append(key)
  43. super(SortedDict, self).__setitem__(key, value)
  44. def __delitem__(self, key):
  45. super(SortedDict, self).__delitem__(key)
  46. self.keyOrder.remove(key)
  47. def __iter__(self):
  48. return iter(self.keyOrder)
  49. def pop(self, k, *args):
  50. result = super(SortedDict, self).pop(k, *args)
  51. try:
  52. self.keyOrder.remove(k)
  53. except ValueError:
  54. # Key wasn't in the dictionary in the first place. No problem.
  55. pass
  56. return result
  57. def popitem(self):
  58. result = super(SortedDict, self).popitem()
  59. self.keyOrder.remove(result[0])
  60. return result
  61. def _iteritems(self):
  62. for key in self.keyOrder:
  63. yield key, self[key]
  64. def _iterkeys(self):
  65. for key in self.keyOrder:
  66. yield key
  67. def _itervalues(self):
  68. for key in self.keyOrder:
  69. yield self[key]
  70. iteritems = _iteritems
  71. iterkeys = _iterkeys
  72. itervalues = _itervalues
  73. def items(self):
  74. return list(self.iteritems())
  75. def keys(self):
  76. return list(self.iterkeys())
  77. def values(self):
  78. return list(self.itervalues())
  79. def update(self, dict_):
  80. for k, v in six.iteritems(dict_):
  81. self[k] = v
  82. def setdefault(self, key, default):
  83. if key not in self:
  84. self.keyOrder.append(key)
  85. return super(SortedDict, self).setdefault(key, default)
  86. def value_for_index(self, index):
  87. """Returns the value of the item at the given zero-based index."""
  88. # This, and insert() are deprecated because they cannot be implemented
  89. # using collections.OrderedDict (Python 2.7 and up), which we'll
  90. # eventually switch to
  91. warnings.warn(
  92. "SortedDict.value_for_index is deprecated", PendingDeprecationWarning,
  93. stacklevel=2
  94. )
  95. return self[self.keyOrder[index]]
  96. def insert(self, index, key, value):
  97. """Inserts the key, value pair before the item with the given index."""
  98. warnings.warn(
  99. "SortedDict.insert is deprecated", PendingDeprecationWarning,
  100. stacklevel=2
  101. )
  102. if key in self.keyOrder:
  103. n = self.keyOrder.index(key)
  104. del self.keyOrder[n]
  105. if n < index:
  106. index -= 1
  107. self.keyOrder.insert(index, key)
  108. super(SortedDict, self).__setitem__(key, value)
  109. def copy(self):
  110. """Returns a copy of this object."""
  111. # This way of initializing the copy means it works for subclasses, too.
  112. return self.__class__(self)
  113. def __repr__(self):
  114. """
  115. Replaces the normal dict.__repr__ with a version that returns the keys
  116. in their sorted order.
  117. """
  118. return '{%s}' % ', '.join(['%r: %r' % (k, v) for k, v in self.iteritems()])
  119. def clear(self):
  120. super(SortedDict, self).clear()
  121. self.keyOrder = []
  122. class ConstType(type):
  123. def __new__(cls, name, bases, attrs):
  124. attrs_value = {}
  125. attrs_label = {}
  126. new_attrs = {}
  127. labels_to_values = {}
  128. for k, v in attrs.items():
  129. if k.startswith('__'):
  130. continue
  131. if isinstance(v, tuple):
  132. attrs_value[k] = v[0]
  133. attrs_label[k] = v[1]
  134. new_attrs[v[0]] = v[1]
  135. labels_to_values[v[1]] = v[0]
  136. elif isinstance(v, dict) and 'label' in v:
  137. attrs_value[k] = v['value']
  138. attrs_label[k] = v['label']
  139. labels_to_values[v['label']] = v['value']
  140. new_attrs[v['value']] = v['label']
  141. else:
  142. attrs_value[k] = v
  143. attrs_label[k] = v
  144. sort_new_attrs = sorted(six.iteritems(new_attrs), key=lambda kv: k[0])
  145. new_attrs = SortedDict(sort_new_attrs)
  146. obj = type.__new__(cls, name, bases, attrs_value)
  147. obj.values = attrs_value
  148. obj.labels = attrs_label
  149. obj.labels_to_values = labels_to_values
  150. obj.attrs = new_attrs
  151. return obj
  152. class Const(six.with_metaclass(ConstType)):
  153. pass