enum.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. # -*- coding: utf-8 -*-
  2. # enum.py
  3. # Part of enum, a package providing enumerated types for Python.
  4. #
  5. # Copyright © 2007–2009 Ben Finney <ben+python@benfinney.id.au>
  6. # This is free software; you may copy, modify and/or distribute this work
  7. # under the terms of the GNU General Public License, version 2 or later
  8. # or, at your option, the terms of the Python license.
  9. """ Robust enumerated type support in Python.
  10. This package provides a module for robust enumerations in Python.
  11. An enumeration object is created with a sequence of string arguments
  12. to the Enum() constructor::
  13. >>> from enum import Enum
  14. >>> Colours = Enum('red', 'blue', 'green')
  15. >>> Weekdays = Enum('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun')
  16. The return value is an immutable sequence object with a value for each
  17. of the string arguments. Each value is also available as an attribute
  18. named from the corresponding string argument::
  19. >>> pizza_night = Weekdays[4]
  20. >>> shirt_colour = Colours.green
  21. The values are constants that can be compared only with values from
  22. the same enumeration; comparison with other values will invoke
  23. Python's fallback comparisons::
  24. >>> pizza_night == Weekdays.fri
  25. True
  26. >>> shirt_colour > Colours.red
  27. True
  28. >>> shirt_colour == "green"
  29. False
  30. Each value from an enumeration exports its sequence index
  31. as an integer, and can be coerced to a simple string matching the
  32. original arguments used to create the enumeration::
  33. >>> str(pizza_night)
  34. 'fri'
  35. >>> shirt_colour.index
  36. 2
  37. """
  38. __author_name__ = "Ben Finney"
  39. __author_email__ = "ben+python@benfinney.id.au"
  40. __author__ = "%(__author_name__)s <%(__author_email__)s>" % vars()
  41. _copyright_year_begin = "2007"
  42. __date__ = "2009-08-26"
  43. _copyright_year_latest = __date__.split('-')[0]
  44. _copyright_year_range = _copyright_year_begin
  45. if _copyright_year_latest > _copyright_year_begin:
  46. _copyright_year_range += "–%(_copyright_year_latest)s" % vars()
  47. __copyright__ = (
  48. "Copyright © %(_copyright_year_range)s"
  49. " %(__author_name__)s") % vars()
  50. __license__ = "Choice of GPL or Python license"
  51. __url__ = "http://pypi.python.org/pypi/enum/"
  52. __version__ = "0.4.4"
  53. class EnumException(Exception):
  54. """ Base class for all exceptions in this module. """
  55. def __init__(self, *args, **kwargs):
  56. if self.__class__ is EnumException:
  57. class_name = self.__class__.__name__
  58. raise NotImplementedError(
  59. "%(class_name)s is an abstract base class" % vars())
  60. super(EnumException, self).__init__(*args, **kwargs)
  61. class EnumEmptyError(AssertionError, EnumException):
  62. """ Raised when attempting to create an empty enumeration. """
  63. def __str__(self):
  64. return "Enumerations cannot be empty"
  65. class EnumBadKeyError(TypeError, EnumException):
  66. """ Raised when creating an Enum with non-string keys. """
  67. def __init__(self, key):
  68. self.key = key
  69. def __str__(self):
  70. return "Enumeration keys must be strings: %(key)r" % vars(self)
  71. class EnumImmutableError(TypeError, EnumException):
  72. """ Raised when attempting to modify an Enum. """
  73. def __init__(self, *args):
  74. self.args = args
  75. def __str__(self):
  76. return "Enumeration does not allow modification"
  77. def _comparator(func):
  78. """ Decorator for EnumValue rich comparison methods. """
  79. def comparator_wrapper(self, other):
  80. try:
  81. assert self.enumtype == other.enumtype
  82. result = func(self.index, other.index)
  83. except (AssertionError, AttributeError):
  84. result = NotImplemented
  85. return result
  86. comparator_wrapper.__name__ = func.__name__
  87. comparator_wrapper.__doc__ = getattr(float, func.__name__).__doc__
  88. return comparator_wrapper
  89. class EnumValue(object):
  90. """ A specific value of an enumerated type. """
  91. def __init__(self, enumtype, index, key):
  92. """ Set up a new instance. """
  93. self._enumtype = enumtype
  94. self._index = index
  95. self._key = key
  96. @property
  97. def enumtype(self):
  98. return self._enumtype
  99. @property
  100. def key(self):
  101. return self._key
  102. def __str__(self):
  103. return str(self.key)
  104. @property
  105. def index(self):
  106. return self._index
  107. def __repr__(self):
  108. return "EnumValue(%(_enumtype)r, %(_index)r, %(_key)r)" % vars(self)
  109. def __hash__(self):
  110. return hash(self._index)
  111. @_comparator
  112. def __eq__(self, other):
  113. return (self == other)
  114. @_comparator
  115. def __ne__(self, other):
  116. return (self != other)
  117. @_comparator
  118. def __lt__(self, other):
  119. return (self < other)
  120. @_comparator
  121. def __le__(self, other):
  122. return (self <= other)
  123. @_comparator
  124. def __gt__(self, other):
  125. return (self > other)
  126. @_comparator
  127. def __ge__(self, other):
  128. return (self >= other)
  129. class Enum(object):
  130. """ Enumerated type. """
  131. def __init__(self, *keys, **kwargs):
  132. """ Create an enumeration instance. """
  133. value_type = kwargs.get('value_type', EnumValue)
  134. if not keys:
  135. raise EnumEmptyError()
  136. keys = tuple(keys)
  137. values = [None] * len(keys)
  138. for i, key in enumerate(keys):
  139. value = value_type(self, i, key)
  140. values[i] = value
  141. try:
  142. super(Enum, self).__setattr__(key, value)
  143. except TypeError:
  144. raise EnumBadKeyError(key)
  145. self.__dict__['_keys'] = keys
  146. self.__dict__['_values'] = values
  147. def __setattr__(self, name, value):
  148. raise EnumImmutableError(name)
  149. def __delattr__(self, name):
  150. raise EnumImmutableError(name)
  151. def __len__(self):
  152. return len(self._values)
  153. def __getitem__(self, index):
  154. return self._values[index]
  155. def __setitem__(self, index, value):
  156. raise EnumImmutableError(index)
  157. def __delitem__(self, index):
  158. raise EnumImmutableError(index)
  159. def __iter__(self):
  160. return iter(self._values)
  161. def __contains__(self, value):
  162. is_member = False
  163. if isinstance(value, basestring):
  164. is_member = (value in self._keys)
  165. else:
  166. is_member = (value in self._values)
  167. return is_member
  168. # Local variables:
  169. # mode: python
  170. # time-stamp-format: "%:y-%02m-%02d"
  171. # time-stamp-start: "__date__ = \""
  172. # time-stamp-end: "\"$"
  173. # time-stamp-line-limit: 200
  174. # End:
  175. # vim: filetype=python fileencoding=utf-8 :