# Copyright 2011-2018, Damian Johnson and The Tor Project # See LICENSE for licensing information """ Basic enumeration, providing ordered types for collections. These can be constructed as simple type listings... :: >>> from stem.util import enum >>> insects = enum.Enum('ANT', 'WASP', 'LADYBUG', 'FIREFLY') >>> insects.ANT 'Ant' >>> tuple(insects) ('Ant', 'Wasp', 'Ladybug', 'Firefly') ... or with overwritten string counterparts... :: >>> from stem.util import enum >>> pets = enum.Enum(('DOG', 'Skippy'), 'CAT', ('FISH', 'Nemo')) >>> pets.DOG 'Skippy' >>> pets.CAT 'Cat' **Module Overview:** :: UppercaseEnum - Provides an enum instance with capitalized values Enum - Provides a basic, ordered enumeration |- keys - string representation of our enum keys |- index_of - index of an enum value |- next - provides the enum after a given enum value |- previous - provides the enum before a given value |- __getitem__ - provides the value for an enum key +- __iter__ - iterator over our enum keys """ import stem.util def UppercaseEnum(*args): """ Provides an :class:`~stem.util.enum.Enum` instance where the values are identical to the keys. Since the keys are uppercase by convention this means the values are too. For instance... :: >>> from stem.util import enum >>> runlevels = enum.UppercaseEnum('DEBUG', 'INFO', 'NOTICE', 'WARN', 'ERROR') >>> runlevels.DEBUG 'DEBUG' :param list args: enum keys to initialize with :returns: :class:`~stem.util.enum.Enum` instance with the given keys """ return Enum(*[(v, v) for v in args]) class Enum(object): """ Basic enumeration. """ def __init__(self, *args): from stem.util.str_tools import _to_camel_case # ordered listings of our keys and values keys, values = [], [] for entry in args: if stem.util._is_str(entry): key, val = entry, _to_camel_case(entry) elif isinstance(entry, tuple) and len(entry) == 2: key, val = entry else: raise ValueError('Unrecognized input: %s' % args) keys.append(key) values.append(val) setattr(self, key, val) self._keys = tuple(keys) self._values = tuple(values) def keys(self): """ Provides an ordered listing of the enumeration keys in this set. :returns: **list** with our enum keys """ return list(self._keys) def index_of(self, value): """ Provides the index of the given value in the collection. :param str value: entry to be looked up :returns: **int** index of the given entry :raises: **ValueError** if no such element exists """ return self._values.index(value) def next(self, value): """ Provides the next enumeration after the given value. :param str value: enumeration for which to get the next entry :returns: enum value following the given entry :raises: **ValueError** if no such element exists """ if value not in self._values: raise ValueError('No such enumeration exists: %s (options: %s)' % (value, ', '.join(self._values))) next_index = (self._values.index(value) + 1) % len(self._values) return self._values[next_index] def previous(self, value): """ Provides the previous enumeration before the given value. :param str value: enumeration for which to get the previous entry :returns: enum value proceeding the given entry :raises: **ValueError** if no such element exists """ if value not in self._values: raise ValueError('No such enumeration exists: %s (options: %s)' % (value, ', '.join(self._values))) prev_index = (self._values.index(value) - 1) % len(self._values) return self._values[prev_index] def __getitem__(self, item): """ Provides the values for the given key. :param str item: key to be looked up :returns: **str** with the value for the given key :raises: **ValueError** if the key doesn't exist """ if item in vars(self): return getattr(self, item) else: keys = ', '.join(self.keys()) raise ValueError("'%s' isn't among our enumeration keys, which includes: %s" % (item, keys)) def __iter__(self): """ Provides an ordered listing of the enums in this set. """ for entry in self._values: yield entry