# -*- coding: utf-8 -*- # enum.py # Part of ‘enum’, a package providing enumerated types for Python. # # Copyright © 2007–2018 Ben Finney # This is free software: you may copy, modify, and/or distribute this work # under the terms of the GNU General Public License as published by the # Free Software Foundation; version 3 of that license or any later version. # No warranty expressed or implied. See the file ‘LICENSE.GPL-3’ for details. """ Robust enumerated type support in Python. This package provides a module for robust enumerations in Python. An enumeration object is created with a sequence of string arguments to the Enum() constructor:: >>> from enum import Enum >>> Colours = Enum('red', 'blue', 'green') >>> Weekdays = Enum('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun') The return value is an immutable sequence object with a value for each of the string arguments. Each value is also available as an attribute named from the corresponding string argument:: >>> pizza_night = Weekdays[4] >>> shirt_colour = Colours.green The values are constants that can be compared only with values from the same enumeration; comparison with other values will invoke Python's fallback comparisons:: >>> pizza_night == Weekdays.fri True >>> shirt_colour > Colours.red True >>> shirt_colour == "green" False Each value from an enumeration exports its sequence index as an integer, and can be coerced to a simple string matching the original arguments used to create the enumeration:: >>> str(pizza_night) 'fri' >>> shirt_colour.index 2 """ __author_name__ = "Ben Finney" __author_email__ = "ben+python@benfinney.id.au" __author__ = "%(__author_name__)s <%(__author_email__)s>" % vars() _copyright_year_begin = "2007" __date__ = "2018-08-22" _copyright_year_latest = __date__.split('-')[0] _copyright_year_range = _copyright_year_begin if _copyright_year_latest > _copyright_year_begin: _copyright_year_range += "–%(_copyright_year_latest)s" % vars() __copyright__ = ( "Copyright © %(_copyright_year_range)s" " %(__author_name__)s") % vars() __license__ = "GPL-3.0+" __url__ = "https://pypi.org/project/enum/" __version__ = "0.4.7" class EnumException(Exception): """ Base class for all exceptions in this module. """ def __init__(self, *args, **kwargs): if self.__class__ is EnumException: class_name = self.__class__.__name__ raise NotImplementedError( "%(class_name)s is an abstract base class" % vars()) super(EnumException, self).__init__(*args, **kwargs) class EnumEmptyError(AssertionError, EnumException): """ Raised when attempting to create an empty enumeration. """ def __str__(self): return "Enumerations cannot be empty" class EnumBadKeyError(TypeError, EnumException): """ Raised when creating an Enum with non-string keys. """ def __init__(self, key): self.key = key def __str__(self): return "Enumeration keys must be strings: %(key)r" % vars(self) class EnumImmutableError(TypeError, EnumException): """ Raised when attempting to modify an Enum. """ def __init__(self, *args): self.args = args def __str__(self): return "Enumeration does not allow modification" def _comparator(func): """ Decorator for EnumValue rich comparison methods. """ def comparator_wrapper(self, other): try: assert self.enumtype == other.enumtype result = func(self.index, other.index) except (AssertionError, AttributeError): result = NotImplemented return result comparator_wrapper.__name__ = func.__name__ comparator_wrapper.__doc__ = getattr(float, func.__name__).__doc__ return comparator_wrapper class EnumValue(object): """ A specific value of an enumerated type. """ def __init__(self, enumtype, index, key): """ Set up a new instance. """ self._enumtype = enumtype self._index = index self._key = key @property def enumtype(self): return self._enumtype @property def key(self): return self._key def __str__(self): return str(self.key) @property def index(self): return self._index def __repr__(self): return "EnumValue(%(_enumtype)r, %(_index)r, %(_key)r)" % vars(self) def __hash__(self): return hash(self._index) @_comparator def __eq__(self, other): return (self == other) @_comparator def __ne__(self, other): return (self != other) @_comparator def __lt__(self, other): return (self < other) @_comparator def __le__(self, other): return (self <= other) @_comparator def __gt__(self, other): return (self > other) @_comparator def __ge__(self, other): return (self >= other) class Enum(object): """ Enumerated type. """ def __init__(self, *keys, **kwargs): """ Create an enumeration instance. """ value_type = kwargs.get('value_type', EnumValue) if not keys: raise EnumEmptyError() keys = tuple(keys) values = [None] * len(keys) for i, key in enumerate(keys): value = value_type(self, i, key) values[i] = value try: super(Enum, self).__setattr__(key, value) except TypeError: raise EnumBadKeyError(key) self.__dict__['_keys'] = keys self.__dict__['_values'] = values def __setattr__(self, name, value): raise EnumImmutableError(name) def __delattr__(self, name): raise EnumImmutableError(name) def __len__(self): return len(self._values) def __getitem__(self, index): return self._values[index] def __setitem__(self, index, value): raise EnumImmutableError(index) def __delitem__(self, index): raise EnumImmutableError(index) def __iter__(self): return iter(self._values) def __contains__(self, value): is_member = False if isinstance(value, basestring): is_member = (value in self._keys) else: is_member = (value in self._values) return is_member # Local variables: # mode: python # time-stamp-format: "%:y-%02m-%02d" # time-stamp-start: "__date__ = \"" # time-stamp-end: "\"$" # time-stamp-line-limit: 200 # End: # vim: filetype=python fileencoding=utf-8 :