123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- # -*- coding: utf-8 -*-
- import copy
- import warnings
- from types import GeneratorType
- import six
- class SortedDict(dict):
- """
- A dictionary that keeps its keys in the order in which they're inserted.
- """
- def __new__(cls, *args, **kwargs):
- instance = super(SortedDict, cls).__new__(cls, *args, **kwargs)
- instance.keyOrder = []
- return instance
- def __init__(self, data=None):
- if data is None:
- data = {}
- elif isinstance(data, GeneratorType):
- # Unfortunately we need to be able to read a generator twice. Once
- # to get the data into self with our super().__init__ call and a
- # second time to setup keyOrder correctly
- data = list(data)
- super(SortedDict, self).__init__(data)
- if isinstance(data, dict):
- self.keyOrder = list(data)
- else:
- self.keyOrder = []
- seen = set()
- for key, value in data:
- if key not in seen:
- self.keyOrder.append(key)
- seen.add(key)
- def __deepcopy__(self, memo):
- return self.__class__([(key, copy.deepcopy(value, memo))
- for key, value in self.iteritems()])
- def __copy__(self):
- # The Python's default copy implementation will alter the state
- # of self. The reason for this seems complex but is likely related to
- # subclassing dict.
- return self.copy()
- def __setitem__(self, key, value):
- if key not in self:
- self.keyOrder.append(key)
- super(SortedDict, self).__setitem__(key, value)
- def __delitem__(self, key):
- super(SortedDict, self).__delitem__(key)
- self.keyOrder.remove(key)
- def __iter__(self):
- return iter(self.keyOrder)
- def pop(self, k, *args):
- result = super(SortedDict, self).pop(k, *args)
- try:
- self.keyOrder.remove(k)
- except ValueError:
- # Key wasn't in the dictionary in the first place. No problem.
- pass
- return result
- def popitem(self):
- result = super(SortedDict, self).popitem()
- self.keyOrder.remove(result[0])
- return result
- def _iteritems(self):
- for key in self.keyOrder:
- yield key, self[key]
- def _iterkeys(self):
- for key in self.keyOrder:
- yield key
- def _itervalues(self):
- for key in self.keyOrder:
- yield self[key]
- iteritems = _iteritems
- iterkeys = _iterkeys
- itervalues = _itervalues
- def items(self):
- return list(self.iteritems())
- def keys(self):
- return list(self.iterkeys())
- def values(self):
- return list(self.itervalues())
- def update(self, dict_):
- for k, v in six.iteritems(dict_):
- self[k] = v
- def setdefault(self, key, default):
- if key not in self:
- self.keyOrder.append(key)
- return super(SortedDict, self).setdefault(key, default)
- def value_for_index(self, index):
- """Returns the value of the item at the given zero-based index."""
- # This, and insert() are deprecated because they cannot be implemented
- # using collections.OrderedDict (Python 2.7 and up), which we'll
- # eventually switch to
- warnings.warn(
- "SortedDict.value_for_index is deprecated", PendingDeprecationWarning,
- stacklevel=2
- )
- return self[self.keyOrder[index]]
- def insert(self, index, key, value):
- """Inserts the key, value pair before the item with the given index."""
- warnings.warn(
- "SortedDict.insert is deprecated", PendingDeprecationWarning,
- stacklevel=2
- )
- if key in self.keyOrder:
- n = self.keyOrder.index(key)
- del self.keyOrder[n]
- if n < index:
- index -= 1
- self.keyOrder.insert(index, key)
- super(SortedDict, self).__setitem__(key, value)
- def copy(self):
- """Returns a copy of this object."""
- # This way of initializing the copy means it works for subclasses, too.
- return self.__class__(self)
- def __repr__(self):
- """
- Replaces the normal dict.__repr__ with a version that returns the keys
- in their sorted order.
- """
- return '{%s}' % ', '.join(['%r: %r' % (k, v) for k, v in self.iteritems()])
- def clear(self):
- super(SortedDict, self).clear()
- self.keyOrder = []
- class ConstType(type):
- def __new__(cls, name, bases, attrs):
- attrs_value = {}
- attrs_label = {}
- new_attrs = {}
- labels_to_values = {}
- for k, v in attrs.items():
- if k.startswith('__'):
- continue
- if isinstance(v, tuple):
- attrs_value[k] = v[0]
- attrs_label[k] = v[1]
- new_attrs[v[0]] = v[1]
- labels_to_values[v[1]] = v[0]
- elif isinstance(v, dict) and 'label' in v:
- attrs_value[k] = v['value']
- attrs_label[k] = v['label']
- labels_to_values[v['label']] = v['value']
- new_attrs[v['value']] = v['label']
- else:
- attrs_value[k] = v
- attrs_label[k] = v
- sort_new_attrs = sorted(six.iteritems(new_attrs), key=lambda kv: k[0])
- new_attrs = SortedDict(sort_new_attrs)
- obj = type.__new__(cls, name, bases, attrs_value)
- obj.values = attrs_value
- obj.labels = attrs_label
- obj.labels_to_values = labels_to_values
- obj.attrs = new_attrs
- return obj
- class Const(six.with_metaclass(ConstType)):
- pass
|