Issue26988: remove AutoEnum
This commit is contained in:
parent
1cce732fcf
commit
332dbc7325
|
@ -37,13 +37,6 @@ one decorator, :func:`unique`.
|
||||||
Base class for creating enumerated constants that are also
|
Base class for creating enumerated constants that are also
|
||||||
subclasses of :class:`int`.
|
subclasses of :class:`int`.
|
||||||
|
|
||||||
.. class:: AutoEnum
|
|
||||||
|
|
||||||
Base class for creating automatically numbered members (may
|
|
||||||
be combined with IntEnum if desired).
|
|
||||||
|
|
||||||
.. versionadded:: 3.6
|
|
||||||
|
|
||||||
.. function:: unique
|
.. function:: unique
|
||||||
|
|
||||||
Enum class decorator that ensures only one name is bound to any one value.
|
Enum class decorator that ensures only one name is bound to any one value.
|
||||||
|
@ -54,14 +47,14 @@ Creating an Enum
|
||||||
|
|
||||||
Enumerations are created using the :keyword:`class` syntax, which makes them
|
Enumerations are created using the :keyword:`class` syntax, which makes them
|
||||||
easy to read and write. An alternative creation method is described in
|
easy to read and write. An alternative creation method is described in
|
||||||
`Functional API`_. To define a simple enumeration, subclass :class:`AutoEnum`
|
`Functional API`_. To define an enumeration, subclass :class:`Enum` as
|
||||||
as follows::
|
follows::
|
||||||
|
|
||||||
>>> from enum import AutoEnum
|
>>> from enum import Enum
|
||||||
>>> class Color(AutoEnum):
|
>>> class Color(Enum):
|
||||||
... red
|
... red = 1
|
||||||
... green
|
... green = 2
|
||||||
... blue
|
... blue = 3
|
||||||
...
|
...
|
||||||
|
|
||||||
.. note:: Nomenclature
|
.. note:: Nomenclature
|
||||||
|
@ -79,33 +72,6 @@ as follows::
|
||||||
are not normal Python classes. See `How are Enums different?`_ for
|
are not normal Python classes. See `How are Enums different?`_ for
|
||||||
more details.
|
more details.
|
||||||
|
|
||||||
To create your own automatic :class:`Enum` classes, you need to add a
|
|
||||||
:meth:`_generate_next_value_` method; it will be used to create missing values
|
|
||||||
for any members after its definition.
|
|
||||||
|
|
||||||
.. versionadded:: 3.6
|
|
||||||
|
|
||||||
If you need full control of the member values, use :class:`Enum` as the base
|
|
||||||
class and specify the values manually::
|
|
||||||
|
|
||||||
>>> from enum import Enum
|
|
||||||
>>> class Color(Enum):
|
|
||||||
... red = 19
|
|
||||||
... green = 7.9182
|
|
||||||
... blue = 'periwinkle'
|
|
||||||
...
|
|
||||||
|
|
||||||
We'll use the following Enum for the examples below::
|
|
||||||
|
|
||||||
>>> class Color(Enum):
|
|
||||||
... red = 1
|
|
||||||
... green = 2
|
|
||||||
... blue = 3
|
|
||||||
...
|
|
||||||
|
|
||||||
Enum Details
|
|
||||||
------------
|
|
||||||
|
|
||||||
Enumeration members have human readable string representations::
|
Enumeration members have human readable string representations::
|
||||||
|
|
||||||
>>> print(Color.red)
|
>>> print(Color.red)
|
||||||
|
@ -269,11 +235,7 @@ aliases::
|
||||||
The ``__members__`` attribute can be used for detailed programmatic access to
|
The ``__members__`` attribute can be used for detailed programmatic access to
|
||||||
the enumeration members. For example, finding all the aliases::
|
the enumeration members. For example, finding all the aliases::
|
||||||
|
|
||||||
>>> [
|
>>> [name for name, member in Shape.__members__.items() if member.name != name]
|
||||||
... name
|
|
||||||
... for name, member in Shape.__members__.items()
|
|
||||||
... if member.name != name
|
|
||||||
... ]
|
|
||||||
['alias_for_square']
|
['alias_for_square']
|
||||||
|
|
||||||
|
|
||||||
|
@ -295,7 +257,7 @@ members are not integers (but see `IntEnum`_ below)::
|
||||||
>>> Color.red < Color.blue
|
>>> Color.red < Color.blue
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
File "<stdin>", line 1, in <module>
|
File "<stdin>", line 1, in <module>
|
||||||
TypeError: '<' not supported between instances of 'Color' and 'Color'
|
TypeError: unorderable types: Color() < Color()
|
||||||
|
|
||||||
Equality comparisons are defined though::
|
Equality comparisons are defined though::
|
||||||
|
|
||||||
|
@ -318,10 +280,10 @@ Allowed members and attributes of enumerations
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
The examples above use integers for enumeration values. Using integers is
|
The examples above use integers for enumeration values. Using integers is
|
||||||
short and handy (and provided by default by :class:`AutoEnum` and the
|
short and handy (and provided by default by the `Functional API`_), but not
|
||||||
`Functional API`_), but not strictly enforced. In the vast majority of
|
strictly enforced. In the vast majority of use-cases, one doesn't care what
|
||||||
use-cases, one doesn't care what the actual value of an enumeration is.
|
the actual value of an enumeration is. But if the value *is* important,
|
||||||
But if the value *is* important, enumerations can have arbitrary values.
|
enumerations can have arbitrary values.
|
||||||
|
|
||||||
Enumerations are Python classes, and can have methods and special methods as
|
Enumerations are Python classes, and can have methods and special methods as
|
||||||
usual. If we have this enumeration::
|
usual. If we have this enumeration::
|
||||||
|
@ -431,21 +393,17 @@ The :class:`Enum` class is callable, providing the following functional API::
|
||||||
>>> list(Animal)
|
>>> list(Animal)
|
||||||
[<Animal.ant: 1>, <Animal.bee: 2>, <Animal.cat: 3>, <Animal.dog: 4>]
|
[<Animal.ant: 1>, <Animal.bee: 2>, <Animal.cat: 3>, <Animal.dog: 4>]
|
||||||
|
|
||||||
The semantics of this API resemble :class:`~collections.namedtuple`.
|
The semantics of this API resemble :class:`~collections.namedtuple`. The first
|
||||||
|
argument of the call to :class:`Enum` is the name of the enumeration.
|
||||||
|
|
||||||
- the first argument of the call to :class:`Enum` is the name of the
|
The second argument is the *source* of enumeration member names. It can be a
|
||||||
enumeration;
|
whitespace-separated string of names, a sequence of names, a sequence of
|
||||||
|
2-tuples with key/value pairs, or a mapping (e.g. dictionary) of names to
|
||||||
- the second argument is the *source* of enumeration member names. It can be a
|
values. The last two options enable assigning arbitrary values to
|
||||||
whitespace-separated string of names, a sequence of names, a sequence of
|
enumerations; the others auto-assign increasing integers starting with 1 (use
|
||||||
2-tuples with key/value pairs, or a mapping (e.g. dictionary) of names to
|
the ``start`` parameter to specify a different starting value). A
|
||||||
values;
|
new class derived from :class:`Enum` is returned. In other words, the above
|
||||||
|
assignment to :class:`Animal` is equivalent to::
|
||||||
- the last two options enable assigning arbitrary values to enumerations; the
|
|
||||||
others auto-assign increasing integers starting with 1 (use the ``start``
|
|
||||||
parameter to specify a different starting value). A new class derived from
|
|
||||||
:class:`Enum` is returned. In other words, the above assignment to
|
|
||||||
:class:`Animal` is equivalent to::
|
|
||||||
|
|
||||||
>>> class Animal(Enum):
|
>>> class Animal(Enum):
|
||||||
... ant = 1
|
... ant = 1
|
||||||
|
@ -461,7 +419,7 @@ to ``True``.
|
||||||
Pickling enums created with the functional API can be tricky as frame stack
|
Pickling enums created with the functional API can be tricky as frame stack
|
||||||
implementation details are used to try and figure out which module the
|
implementation details are used to try and figure out which module the
|
||||||
enumeration is being created in (e.g. it will fail if you use a utility
|
enumeration is being created in (e.g. it will fail if you use a utility
|
||||||
function in a separate module, and also may not work on IronPython or Jython).
|
function in separate module, and also may not work on IronPython or Jython).
|
||||||
The solution is to specify the module name explicitly as follows::
|
The solution is to specify the module name explicitly as follows::
|
||||||
|
|
||||||
>>> Animal = Enum('Animal', 'ant bee cat dog', module=__name__)
|
>>> Animal = Enum('Animal', 'ant bee cat dog', module=__name__)
|
||||||
|
@ -481,15 +439,7 @@ SomeData in the global scope::
|
||||||
|
|
||||||
The complete signature is::
|
The complete signature is::
|
||||||
|
|
||||||
Enum(
|
Enum(value='NewEnumName', names=<...>, *, module='...', qualname='...', type=<mixed-in class>, start=1)
|
||||||
value='NewEnumName',
|
|
||||||
names=<...>,
|
|
||||||
*,
|
|
||||||
module='...',
|
|
||||||
qualname='...',
|
|
||||||
type=<mixed-in class>,
|
|
||||||
start=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
:value: What the new Enum class will record as its name.
|
:value: What the new Enum class will record as its name.
|
||||||
|
|
||||||
|
@ -525,41 +475,10 @@ The complete signature is::
|
||||||
Derived Enumerations
|
Derived Enumerations
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
AutoEnum
|
|
||||||
^^^^^^^^
|
|
||||||
|
|
||||||
This version of :class:`Enum` automatically assigns numbers as the values
|
|
||||||
for the enumeration members, while still allowing values to be specified
|
|
||||||
when needed::
|
|
||||||
|
|
||||||
>>> from enum import AutoEnum
|
|
||||||
>>> class Color(AutoEnum):
|
|
||||||
... red
|
|
||||||
... green = 5
|
|
||||||
... blue
|
|
||||||
...
|
|
||||||
>>> list(Color)
|
|
||||||
[<Color.red: 1>, <Color.green: 5>, <Color.blue: 6>]
|
|
||||||
|
|
||||||
.. note:: Name Lookup
|
|
||||||
|
|
||||||
By default the names :func:`property`, :func:`classmethod`, and
|
|
||||||
:func:`staticmethod` are shielded from becoming members. To enable
|
|
||||||
them, or to specify a different set of shielded names, specify the
|
|
||||||
ignore parameter::
|
|
||||||
|
|
||||||
>>> class AddressType(AutoEnum, ignore='classmethod staticmethod'):
|
|
||||||
... pobox
|
|
||||||
... mailbox
|
|
||||||
... property
|
|
||||||
...
|
|
||||||
|
|
||||||
.. versionadded:: 3.6
|
|
||||||
|
|
||||||
IntEnum
|
IntEnum
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|
||||||
Another variation of :class:`Enum` which is also a subclass of
|
A variation of :class:`Enum` is provided which is also a subclass of
|
||||||
:class:`int`. Members of an :class:`IntEnum` can be compared to integers;
|
:class:`int`. Members of an :class:`IntEnum` can be compared to integers;
|
||||||
by extension, integer enumerations of different types can also be compared
|
by extension, integer enumerations of different types can also be compared
|
||||||
to each other::
|
to each other::
|
||||||
|
@ -602,13 +521,14 @@ However, they still can't be compared to standard :class:`Enum` enumerations::
|
||||||
>>> [i for i in range(Shape.square)]
|
>>> [i for i in range(Shape.square)]
|
||||||
[0, 1]
|
[0, 1]
|
||||||
|
|
||||||
For the vast majority of code, :class:`Enum` and :class:`AutoEnum` are strongly
|
For the vast majority of code, :class:`Enum` is strongly recommended,
|
||||||
recommended, since :class:`IntEnum` breaks some semantic promises of an
|
since :class:`IntEnum` breaks some semantic promises of an enumeration (by
|
||||||
enumeration (by being comparable to integers, and thus by transitivity to other
|
being comparable to integers, and thus by transitivity to other
|
||||||
unrelated ``IntEnum`` enumerations). It should be used only in special cases
|
unrelated enumerations). It should be used only in special cases where
|
||||||
where there's no other choice; for example, when integer constants are replaced
|
there's no other choice; for example, when integer constants are
|
||||||
with enumerations and backwards compatibility is required with code that still
|
replaced with enumerations and backwards compatibility is required with code
|
||||||
expects integers.
|
that still expects integers.
|
||||||
|
|
||||||
|
|
||||||
Others
|
Others
|
||||||
^^^^^^
|
^^^^^^
|
||||||
|
@ -620,9 +540,7 @@ simple to implement independently::
|
||||||
pass
|
pass
|
||||||
|
|
||||||
This demonstrates how similar derived enumerations can be defined; for example
|
This demonstrates how similar derived enumerations can be defined; for example
|
||||||
an :class:`AutoIntEnum` that mixes in :class:`int` with :class:`AutoEnum`
|
a :class:`StrEnum` that mixes in :class:`str` instead of :class:`int`.
|
||||||
to get members that are :class:`int` (but keep in mind the warnings for
|
|
||||||
:class:`IntEnum`).
|
|
||||||
|
|
||||||
Some rules:
|
Some rules:
|
||||||
|
|
||||||
|
@ -649,35 +567,31 @@ Some rules:
|
||||||
Interesting examples
|
Interesting examples
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
While :class:`Enum`, :class:`AutoEnum`, and :class:`IntEnum` are expected
|
While :class:`Enum` and :class:`IntEnum` are expected to cover the majority of
|
||||||
to cover the majority of use-cases, they cannot cover them all. Here are
|
use-cases, they cannot cover them all. Here are recipes for some different
|
||||||
recipes for some different types of enumerations that can be used directly,
|
types of enumerations that can be used directly, or as examples for creating
|
||||||
or as examples for creating one's own.
|
one's own.
|
||||||
|
|
||||||
|
|
||||||
AutoDocEnum
|
AutoNumber
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
|
|
||||||
Automatically numbers the members, and uses the given value as the
|
Avoids having to specify the value for each enumeration member::
|
||||||
:attr:`__doc__` string::
|
|
||||||
|
|
||||||
>>> class AutoDocEnum(Enum):
|
>>> class AutoNumber(Enum):
|
||||||
... def __new__(cls, doc):
|
... def __new__(cls):
|
||||||
... value = len(cls.__members__) + 1
|
... value = len(cls.__members__) + 1
|
||||||
... obj = object.__new__(cls)
|
... obj = object.__new__(cls)
|
||||||
... obj._value_ = value
|
... obj._value_ = value
|
||||||
... obj.__doc__ = doc
|
|
||||||
... return obj
|
... return obj
|
||||||
...
|
...
|
||||||
>>> class Color(AutoDocEnum):
|
>>> class Color(AutoNumber):
|
||||||
... red = 'stop'
|
... red = ()
|
||||||
... green = 'go'
|
... green = ()
|
||||||
... blue = 'what?'
|
... blue = ()
|
||||||
...
|
...
|
||||||
>>> Color.green.value == 2
|
>>> Color.green.value == 2
|
||||||
True
|
True
|
||||||
>>> Color.green.__doc__
|
|
||||||
'go'
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
@ -685,23 +599,6 @@ Automatically numbers the members, and uses the given value as the
|
||||||
members; it is then replaced by Enum's :meth:`__new__` which is used after
|
members; it is then replaced by Enum's :meth:`__new__` which is used after
|
||||||
class creation for lookup of existing members.
|
class creation for lookup of existing members.
|
||||||
|
|
||||||
AutoNameEnum
|
|
||||||
^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Automatically sets the member's value to its name::
|
|
||||||
|
|
||||||
>>> class AutoNameEnum(Enum):
|
|
||||||
... def _generate_next_value_(name, start, count, last_value):
|
|
||||||
... return name
|
|
||||||
...
|
|
||||||
>>> class Color(AutoNameEnum):
|
|
||||||
... red
|
|
||||||
... green
|
|
||||||
... blue
|
|
||||||
...
|
|
||||||
>>> Color.green.value == 'green'
|
|
||||||
True
|
|
||||||
|
|
||||||
|
|
||||||
OrderedEnum
|
OrderedEnum
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|
@ -834,63 +731,10 @@ member instances.
|
||||||
Finer Points
|
Finer Points
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
Enum class signature
|
:class:`Enum` members are instances of an :class:`Enum` class, and even
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
though they are accessible as `EnumClass.member`, they should not be accessed
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
class SomeName(
|
|
||||||
AnEnum,
|
|
||||||
start=None,
|
|
||||||
ignore='staticmethod classmethod property',
|
|
||||||
):
|
|
||||||
|
|
||||||
*start* can be used by a :meth:`_generate_next_value_` method to specify a
|
|
||||||
starting value.
|
|
||||||
|
|
||||||
*ignore* specifies which names, if any, will not attempt to auto-generate
|
|
||||||
a new value (they will also be removed from the class body).
|
|
||||||
|
|
||||||
|
|
||||||
Supported ``__dunder__`` names
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The :attr:`__members__` attribute is only available on the class.
|
|
||||||
|
|
||||||
:meth:`__new__`, if specified, must create and return the enum members; it is
|
|
||||||
also a very good idea to set the member's :attr:`_value_` appropriately. Once
|
|
||||||
all the members are created it is no longer used.
|
|
||||||
|
|
||||||
|
|
||||||
Supported ``_sunder_`` names
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
- ``_order_`` -- used in Python 2/3 code to ensure member order is consistent [class attribute]
|
|
||||||
|
|
||||||
- ``_name_`` -- name of the member (but use ``name`` for normal access)
|
|
||||||
- ``_value_`` -- value of the member; can be set / modified in ``__new__`` (see ``_name_``)
|
|
||||||
- ``_missing_`` -- a lookup function used when a value is not found (only after class creation)
|
|
||||||
- ``_generate_next_value_`` -- a function to generate missing values (only during class creation)
|
|
||||||
|
|
||||||
|
|
||||||
:meth:`_generate_next_value_` signature
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
``def _generate_next_value_(name, start, count, last_value):``
|
|
||||||
|
|
||||||
- ``name`` is the name of the member
|
|
||||||
- ``start`` is the initital start value (if any) or None
|
|
||||||
- ``count`` is the number of existing members in the enumeration
|
|
||||||
- ``last_value`` is the value of the last enum member (if any) or None
|
|
||||||
|
|
||||||
|
|
||||||
Enum member type
|
|
||||||
~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
``Enum`` members are instances of an ``Enum`` class, and even
|
|
||||||
though they are accessible as ``EnumClass.member``, they should not be accessed
|
|
||||||
directly from the member as that lookup may fail or, worse, return something
|
directly from the member as that lookup may fail or, worse, return something
|
||||||
besides the ``Enum`` member you are looking for::
|
besides the :class:`Enum` member you looking for::
|
||||||
|
|
||||||
>>> class FieldTypes(Enum):
|
>>> class FieldTypes(Enum):
|
||||||
... name = 0
|
... name = 0
|
||||||
|
@ -904,24 +748,18 @@ besides the ``Enum`` member you are looking for::
|
||||||
|
|
||||||
.. versionchanged:: 3.5
|
.. versionchanged:: 3.5
|
||||||
|
|
||||||
|
Boolean evaluation: Enum classes that are mixed with non-Enum types (such as
|
||||||
Boolean value of ``Enum`` classes and members
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Enum classes that are mixed with non-Enum types (such as
|
|
||||||
:class:`int`, :class:`str`, etc.) are evaluated according to the mixed-in
|
:class:`int`, :class:`str`, etc.) are evaluated according to the mixed-in
|
||||||
type's rules; otherwise, all members evaluate as :data:`True`. To make your own
|
type's rules; otherwise, all members evaluate as ``True``. To make your own
|
||||||
Enum's boolean evaluation depend on the member's value add the following to
|
Enum's boolean evaluation depend on the member's value add the following to
|
||||||
your class::
|
your class::
|
||||||
|
|
||||||
def __bool__(self):
|
def __bool__(self):
|
||||||
return bool(self.value)
|
return bool(self.value)
|
||||||
|
|
||||||
|
The :attr:`__members__` attribute is only available on the class.
|
||||||
|
|
||||||
Enum classes with methods
|
If you give your :class:`Enum` subclass extra methods, like the `Planet`_
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
If you give your ``Enum`` subclass extra methods, like the `Planet`_
|
|
||||||
class above, those methods will show up in a :func:`dir` of the member,
|
class above, those methods will show up in a :func:`dir` of the member,
|
||||||
but not of the class::
|
but not of the class::
|
||||||
|
|
||||||
|
@ -929,3 +767,12 @@ but not of the class::
|
||||||
['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__']
|
['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__']
|
||||||
>>> dir(Planet.EARTH)
|
>>> dir(Planet.EARTH)
|
||||||
['__class__', '__doc__', '__module__', 'name', 'surface_gravity', 'value']
|
['__class__', '__doc__', '__module__', 'name', 'surface_gravity', 'value']
|
||||||
|
|
||||||
|
The :meth:`__new__` method will only be used for the creation of the
|
||||||
|
:class:`Enum` members -- after that it is replaced. Any custom :meth:`__new__`
|
||||||
|
method must create the object and set the :attr:`_value_` attribute
|
||||||
|
appropriately.
|
||||||
|
|
||||||
|
If you wish to change how :class:`Enum` members are looked up you should either
|
||||||
|
write a helper function or a :func:`classmethod` for the :class:`Enum`
|
||||||
|
subclass.
|
||||||
|
|
133
Lib/enum.py
133
Lib/enum.py
|
@ -8,9 +8,7 @@ except ImportError:
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = ['EnumMeta', 'Enum', 'IntEnum', 'unique']
|
||||||
'EnumMeta', 'Enum', 'IntEnum', 'AutoEnum', 'unique',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def _is_descriptor(obj):
|
def _is_descriptor(obj):
|
||||||
|
@ -54,30 +52,7 @@ class _EnumDict(dict):
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
# list of enum members
|
|
||||||
self._member_names = []
|
self._member_names = []
|
||||||
# starting value
|
|
||||||
self._start = None
|
|
||||||
# last assigned value
|
|
||||||
self._last_value = None
|
|
||||||
# when the magic turns off
|
|
||||||
self._locked = True
|
|
||||||
# list of temporary names
|
|
||||||
self._ignore = []
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
if (
|
|
||||||
self._generate_next_value_ is None
|
|
||||||
or self._locked
|
|
||||||
or key in self
|
|
||||||
or key in self._ignore
|
|
||||||
or _is_sunder(key)
|
|
||||||
or _is_dunder(key)
|
|
||||||
):
|
|
||||||
return super(_EnumDict, self).__getitem__(key)
|
|
||||||
next_value = self._generate_next_value_(key, self._start, len(self._member_names), self._last_value)
|
|
||||||
self[key] = next_value
|
|
||||||
return next_value
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
"""Changes anything not dundered or not a descriptor.
|
"""Changes anything not dundered or not a descriptor.
|
||||||
|
@ -89,55 +64,19 @@ class _EnumDict(dict):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if _is_sunder(key):
|
if _is_sunder(key):
|
||||||
if key not in ('_settings_', '_order_', '_ignore_', '_start_', '_generate_next_value_'):
|
raise ValueError('_names_ are reserved for future Enum use')
|
||||||
raise ValueError('_names_ are reserved for future Enum use')
|
|
||||||
elif key == '_generate_next_value_':
|
|
||||||
if isinstance(value, staticmethod):
|
|
||||||
value = value.__get__(None, self)
|
|
||||||
self._generate_next_value_ = value
|
|
||||||
self._locked = False
|
|
||||||
elif key == '_ignore_':
|
|
||||||
if isinstance(value, str):
|
|
||||||
value = value.split()
|
|
||||||
else:
|
|
||||||
value = list(value)
|
|
||||||
self._ignore = value
|
|
||||||
already = set(value) & set(self._member_names)
|
|
||||||
if already:
|
|
||||||
raise ValueError(
|
|
||||||
'_ignore_ cannot specify already set names: %r'
|
|
||||||
% (already, ))
|
|
||||||
elif key == '_start_':
|
|
||||||
self._start = value
|
|
||||||
self._locked = False
|
|
||||||
elif _is_dunder(key):
|
elif _is_dunder(key):
|
||||||
if key == '__order__':
|
pass
|
||||||
key = '_order_'
|
|
||||||
if _is_descriptor(value):
|
|
||||||
self._locked = True
|
|
||||||
elif key in self._member_names:
|
elif key in self._member_names:
|
||||||
# descriptor overwriting an enum?
|
# descriptor overwriting an enum?
|
||||||
raise TypeError('Attempted to reuse key: %r' % key)
|
raise TypeError('Attempted to reuse key: %r' % key)
|
||||||
elif key in self._ignore:
|
|
||||||
pass
|
|
||||||
elif not _is_descriptor(value):
|
elif not _is_descriptor(value):
|
||||||
if key in self:
|
if key in self:
|
||||||
# enum overwriting a descriptor?
|
# enum overwriting a descriptor?
|
||||||
raise TypeError('%r already defined as: %r' % (key, self[key]))
|
raise TypeError('Key already defined as: %r' % self[key])
|
||||||
self._member_names.append(key)
|
self._member_names.append(key)
|
||||||
if self._generate_next_value_ is not None:
|
|
||||||
self._last_value = value
|
|
||||||
else:
|
|
||||||
# not a new member, turn off the autoassign magic
|
|
||||||
self._locked = True
|
|
||||||
super().__setitem__(key, value)
|
super().__setitem__(key, value)
|
||||||
|
|
||||||
# for magic "auto values" an Enum class should specify a `_generate_next_value_`
|
|
||||||
# method; that method will be used to generate missing values, and is
|
|
||||||
# implicitly a staticmethod;
|
|
||||||
# the signature should be `def _generate_next_value_(name, last_value)`
|
|
||||||
# last_value will be the last value created and/or assigned, or None
|
|
||||||
_generate_next_value_ = None
|
|
||||||
|
|
||||||
|
|
||||||
# Dummy value for Enum as EnumMeta explicitly checks for it, but of course
|
# Dummy value for Enum as EnumMeta explicitly checks for it, but of course
|
||||||
|
@ -145,31 +84,14 @@ class _EnumDict(dict):
|
||||||
# This is also why there are checks in EnumMeta like `if Enum is not None`
|
# This is also why there are checks in EnumMeta like `if Enum is not None`
|
||||||
Enum = None
|
Enum = None
|
||||||
|
|
||||||
_ignore_sentinel = object()
|
|
||||||
class EnumMeta(type):
|
class EnumMeta(type):
|
||||||
"""Metaclass for Enum"""
|
"""Metaclass for Enum"""
|
||||||
@classmethod
|
@classmethod
|
||||||
def __prepare__(metacls, cls, bases, start=None, ignore=_ignore_sentinel):
|
def __prepare__(metacls, cls, bases):
|
||||||
# create the namespace dict
|
return _EnumDict()
|
||||||
enum_dict = _EnumDict()
|
|
||||||
# inherit previous flags and _generate_next_value_ function
|
|
||||||
member_type, first_enum = metacls._get_mixins_(bases)
|
|
||||||
if first_enum is not None:
|
|
||||||
enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None)
|
|
||||||
if start is None:
|
|
||||||
start = getattr(first_enum, '_start_', None)
|
|
||||||
if ignore is _ignore_sentinel:
|
|
||||||
enum_dict['_ignore_'] = 'property classmethod staticmethod'.split()
|
|
||||||
elif ignore:
|
|
||||||
enum_dict['_ignore_'] = ignore
|
|
||||||
if start is not None:
|
|
||||||
enum_dict['_start_'] = start
|
|
||||||
return enum_dict
|
|
||||||
|
|
||||||
def __init__(cls, *args , **kwds):
|
def __new__(metacls, cls, bases, classdict):
|
||||||
super(EnumMeta, cls).__init__(*args)
|
|
||||||
|
|
||||||
def __new__(metacls, cls, bases, classdict, **kwds):
|
|
||||||
# an Enum class is final once enumeration items have been defined; it
|
# an Enum class is final once enumeration items have been defined; it
|
||||||
# cannot be mixed with other types (int, float, etc.) if it has an
|
# cannot be mixed with other types (int, float, etc.) if it has an
|
||||||
# inherited __new__ unless a new __new__ is defined (or the resulting
|
# inherited __new__ unless a new __new__ is defined (or the resulting
|
||||||
|
@ -180,24 +102,12 @@ class EnumMeta(type):
|
||||||
|
|
||||||
# save enum items into separate mapping so they don't get baked into
|
# save enum items into separate mapping so they don't get baked into
|
||||||
# the new class
|
# the new class
|
||||||
enum_members = {k: classdict[k] for k in classdict._member_names}
|
members = {k: classdict[k] for k in classdict._member_names}
|
||||||
for name in classdict._member_names:
|
for name in classdict._member_names:
|
||||||
del classdict[name]
|
del classdict[name]
|
||||||
|
|
||||||
# adjust the sunders
|
|
||||||
_order_ = classdict.pop('_order_', None)
|
|
||||||
classdict.pop('_ignore_', None)
|
|
||||||
|
|
||||||
# py3 support for definition order (helps keep py2/py3 code in sync)
|
|
||||||
if _order_ is not None:
|
|
||||||
if isinstance(_order_, str):
|
|
||||||
_order_ = _order_.replace(',', ' ').split()
|
|
||||||
unique_members = [n for n in clsdict._member_names if n in _order_]
|
|
||||||
if _order_ != unique_members:
|
|
||||||
raise TypeError('member order does not match _order_')
|
|
||||||
|
|
||||||
# check for illegal enum names (any others?)
|
# check for illegal enum names (any others?)
|
||||||
invalid_names = set(enum_members) & {'mro', }
|
invalid_names = set(members) & {'mro', }
|
||||||
if invalid_names:
|
if invalid_names:
|
||||||
raise ValueError('Invalid enum member name: {0}'.format(
|
raise ValueError('Invalid enum member name: {0}'.format(
|
||||||
','.join(invalid_names)))
|
','.join(invalid_names)))
|
||||||
|
@ -241,7 +151,7 @@ class EnumMeta(type):
|
||||||
# a custom __new__ is doing something funky with the values -- such as
|
# a custom __new__ is doing something funky with the values -- such as
|
||||||
# auto-numbering ;)
|
# auto-numbering ;)
|
||||||
for member_name in classdict._member_names:
|
for member_name in classdict._member_names:
|
||||||
value = enum_members[member_name]
|
value = members[member_name]
|
||||||
if not isinstance(value, tuple):
|
if not isinstance(value, tuple):
|
||||||
args = (value, )
|
args = (value, )
|
||||||
else:
|
else:
|
||||||
|
@ -255,10 +165,7 @@ class EnumMeta(type):
|
||||||
else:
|
else:
|
||||||
enum_member = __new__(enum_class, *args)
|
enum_member = __new__(enum_class, *args)
|
||||||
if not hasattr(enum_member, '_value_'):
|
if not hasattr(enum_member, '_value_'):
|
||||||
if member_type is object:
|
enum_member._value_ = member_type(*args)
|
||||||
enum_member._value_ = value
|
|
||||||
else:
|
|
||||||
enum_member._value_ = member_type(*args)
|
|
||||||
value = enum_member._value_
|
value = enum_member._value_
|
||||||
enum_member._name_ = member_name
|
enum_member._name_ = member_name
|
||||||
enum_member.__objclass__ = enum_class
|
enum_member.__objclass__ = enum_class
|
||||||
|
@ -665,22 +572,6 @@ class IntEnum(int, Enum):
|
||||||
def _reduce_ex_by_name(self, proto):
|
def _reduce_ex_by_name(self, proto):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
class AutoEnum(Enum):
|
|
||||||
"""Enum where values are automatically assigned."""
|
|
||||||
def _generate_next_value_(name, start, count, last_value):
|
|
||||||
"""
|
|
||||||
Generate the next value when not given.
|
|
||||||
|
|
||||||
name: the name of the member
|
|
||||||
start: the initital start value or None
|
|
||||||
count: the number of existing members
|
|
||||||
last_value: the last value assigned or None
|
|
||||||
"""
|
|
||||||
# add one to the last assigned value
|
|
||||||
if not count:
|
|
||||||
return start if start is not None else 1
|
|
||||||
return last_value + 1
|
|
||||||
|
|
||||||
def unique(enumeration):
|
def unique(enumeration):
|
||||||
"""Class decorator for enumerations ensuring unique member values."""
|
"""Class decorator for enumerations ensuring unique member values."""
|
||||||
duplicates = []
|
duplicates = []
|
||||||
|
|
|
@ -3,7 +3,7 @@ import inspect
|
||||||
import pydoc
|
import pydoc
|
||||||
import unittest
|
import unittest
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from enum import EnumMeta, Enum, IntEnum, AutoEnum, unique
|
from enum import Enum, IntEnum, EnumMeta, unique
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL
|
from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL
|
||||||
from test import support
|
from test import support
|
||||||
|
@ -1570,328 +1570,6 @@ class TestEnum(unittest.TestCase):
|
||||||
self.assertEqual(LabelledList.unprocessed, 1)
|
self.assertEqual(LabelledList.unprocessed, 1)
|
||||||
self.assertEqual(LabelledList(1), LabelledList.unprocessed)
|
self.assertEqual(LabelledList(1), LabelledList.unprocessed)
|
||||||
|
|
||||||
def test_ignore_as_str(self):
|
|
||||||
from datetime import timedelta
|
|
||||||
class Period(Enum, ignore='Period i'):
|
|
||||||
"""
|
|
||||||
different lengths of time
|
|
||||||
"""
|
|
||||||
def __new__(cls, value, period):
|
|
||||||
obj = object.__new__(cls)
|
|
||||||
obj._value_ = value
|
|
||||||
obj.period = period
|
|
||||||
return obj
|
|
||||||
Period = vars()
|
|
||||||
for i in range(367):
|
|
||||||
Period['Day%d' % i] = timedelta(days=i), 'day'
|
|
||||||
for i in range(53):
|
|
||||||
Period['Week%d' % i] = timedelta(days=i*7), 'week'
|
|
||||||
for i in range(13):
|
|
||||||
Period['Month%d' % i] = i, 'month'
|
|
||||||
OneDay = Day1
|
|
||||||
OneWeek = Week1
|
|
||||||
self.assertEqual(Period.Day7.value, timedelta(days=7))
|
|
||||||
self.assertEqual(Period.Day7.period, 'day')
|
|
||||||
|
|
||||||
def test_ignore_as_list(self):
|
|
||||||
from datetime import timedelta
|
|
||||||
class Period(Enum, ignore=['Period', 'i']):
|
|
||||||
"""
|
|
||||||
different lengths of time
|
|
||||||
"""
|
|
||||||
def __new__(cls, value, period):
|
|
||||||
obj = object.__new__(cls)
|
|
||||||
obj._value_ = value
|
|
||||||
obj.period = period
|
|
||||||
return obj
|
|
||||||
Period = vars()
|
|
||||||
for i in range(367):
|
|
||||||
Period['Day%d' % i] = timedelta(days=i), 'day'
|
|
||||||
for i in range(53):
|
|
||||||
Period['Week%d' % i] = timedelta(days=i*7), 'week'
|
|
||||||
for i in range(13):
|
|
||||||
Period['Month%d' % i] = i, 'month'
|
|
||||||
OneDay = Day1
|
|
||||||
OneWeek = Week1
|
|
||||||
self.assertEqual(Period.Day7.value, timedelta(days=7))
|
|
||||||
self.assertEqual(Period.Day7.period, 'day')
|
|
||||||
|
|
||||||
def test_new_with_no_value_and_int_base_class(self):
|
|
||||||
class NoValue(int, Enum):
|
|
||||||
def __new__(cls, value):
|
|
||||||
obj = int.__new__(cls, value)
|
|
||||||
obj.index = len(cls.__members__)
|
|
||||||
return obj
|
|
||||||
this = 1
|
|
||||||
that = 2
|
|
||||||
self.assertEqual(list(NoValue), [NoValue.this, NoValue.that])
|
|
||||||
self.assertEqual(NoValue.this, 1)
|
|
||||||
self.assertEqual(NoValue.this.value, 1)
|
|
||||||
self.assertEqual(NoValue.this.index, 0)
|
|
||||||
self.assertEqual(NoValue.that, 2)
|
|
||||||
self.assertEqual(NoValue.that.value, 2)
|
|
||||||
self.assertEqual(NoValue.that.index, 1)
|
|
||||||
|
|
||||||
def test_new_with_no_value(self):
|
|
||||||
class NoValue(Enum):
|
|
||||||
def __new__(cls, value):
|
|
||||||
obj = object.__new__(cls)
|
|
||||||
obj.index = len(cls.__members__)
|
|
||||||
return obj
|
|
||||||
this = 1
|
|
||||||
that = 2
|
|
||||||
self.assertEqual(list(NoValue), [NoValue.this, NoValue.that])
|
|
||||||
self.assertEqual(NoValue.this.value, 1)
|
|
||||||
self.assertEqual(NoValue.this.index, 0)
|
|
||||||
self.assertEqual(NoValue.that.value, 2)
|
|
||||||
self.assertEqual(NoValue.that.index, 1)
|
|
||||||
|
|
||||||
|
|
||||||
class TestAutoNumber(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_autonumbering(self):
|
|
||||||
class Color(AutoEnum):
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue
|
|
||||||
self.assertEqual(list(Color), [Color.red, Color.green, Color.blue])
|
|
||||||
self.assertEqual(Color.red.value, 1)
|
|
||||||
self.assertEqual(Color.green.value, 2)
|
|
||||||
self.assertEqual(Color.blue.value, 3)
|
|
||||||
|
|
||||||
def test_autointnumbering(self):
|
|
||||||
class Color(int, AutoEnum):
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue
|
|
||||||
self.assertTrue(isinstance(Color.red, int))
|
|
||||||
self.assertEqual(Color.green, 2)
|
|
||||||
self.assertTrue(Color.blue > Color.red)
|
|
||||||
|
|
||||||
def test_autonumbering_with_start(self):
|
|
||||||
class Color(AutoEnum, start=7):
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue
|
|
||||||
self.assertEqual(list(Color), [Color.red, Color.green, Color.blue])
|
|
||||||
self.assertEqual(Color.red.value, 7)
|
|
||||||
self.assertEqual(Color.green.value, 8)
|
|
||||||
self.assertEqual(Color.blue.value, 9)
|
|
||||||
|
|
||||||
def test_autonumbering_with_start_and_skip(self):
|
|
||||||
class Color(AutoEnum, start=7):
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue = 11
|
|
||||||
brown
|
|
||||||
self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.brown])
|
|
||||||
self.assertEqual(Color.red.value, 7)
|
|
||||||
self.assertEqual(Color.green.value, 8)
|
|
||||||
self.assertEqual(Color.blue.value, 11)
|
|
||||||
self.assertEqual(Color.brown.value, 12)
|
|
||||||
|
|
||||||
|
|
||||||
def test_badly_overridden_ignore(self):
|
|
||||||
with self.assertRaisesRegex(TypeError, "'int' object is not callable"):
|
|
||||||
class Color(AutoEnum):
|
|
||||||
_ignore_ = ()
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue
|
|
||||||
@property
|
|
||||||
def whatever(self):
|
|
||||||
pass
|
|
||||||
with self.assertRaisesRegex(TypeError, "'int' object is not callable"):
|
|
||||||
class Color(AutoEnum, ignore=None):
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue
|
|
||||||
@property
|
|
||||||
def whatever(self):
|
|
||||||
pass
|
|
||||||
with self.assertRaisesRegex(TypeError, "'int' object is not callable"):
|
|
||||||
class Color(AutoEnum, ignore='classmethod staticmethod'):
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue
|
|
||||||
@property
|
|
||||||
def whatever(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_property(self):
|
|
||||||
class Color(AutoEnum):
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue
|
|
||||||
@property
|
|
||||||
def cap_name(self):
|
|
||||||
return self.name.title()
|
|
||||||
self.assertEqual(Color.blue.cap_name, 'Blue')
|
|
||||||
|
|
||||||
def test_magic_turns_off(self):
|
|
||||||
with self.assertRaisesRegex(NameError, "brown"):
|
|
||||||
class Color(AutoEnum):
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue
|
|
||||||
@property
|
|
||||||
def cap_name(self):
|
|
||||||
return self.name.title()
|
|
||||||
brown
|
|
||||||
|
|
||||||
with self.assertRaisesRegex(NameError, "rose"):
|
|
||||||
class Color(AutoEnum):
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue
|
|
||||||
def hello(self):
|
|
||||||
print('Hello! My serial is %s.' % self.value)
|
|
||||||
rose
|
|
||||||
|
|
||||||
with self.assertRaisesRegex(NameError, "cyan"):
|
|
||||||
class Color(AutoEnum):
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue
|
|
||||||
def __init__(self, *args):
|
|
||||||
pass
|
|
||||||
cyan
|
|
||||||
|
|
||||||
|
|
||||||
class TestGenerateMethod(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_autonaming(self):
|
|
||||||
class Color(Enum):
|
|
||||||
def _generate_next_value_(name, start, count, last_value):
|
|
||||||
return name
|
|
||||||
Red
|
|
||||||
Green
|
|
||||||
Blue
|
|
||||||
self.assertEqual(list(Color), [Color.Red, Color.Green, Color.Blue])
|
|
||||||
self.assertEqual(Color.Red.value, 'Red')
|
|
||||||
self.assertEqual(Color.Green.value, 'Green')
|
|
||||||
self.assertEqual(Color.Blue.value, 'Blue')
|
|
||||||
|
|
||||||
def test_autonamestr(self):
|
|
||||||
class Color(str, Enum):
|
|
||||||
def _generate_next_value_(name, start, count, last_value):
|
|
||||||
return name
|
|
||||||
Red
|
|
||||||
Green
|
|
||||||
Blue
|
|
||||||
self.assertTrue(isinstance(Color.Red, str))
|
|
||||||
self.assertEqual(Color.Green, 'Green')
|
|
||||||
self.assertTrue(Color.Blue < Color.Red)
|
|
||||||
|
|
||||||
def test_generate_as_staticmethod(self):
|
|
||||||
class Color(str, Enum):
|
|
||||||
@staticmethod
|
|
||||||
def _generate_next_value_(name, start, count, last_value):
|
|
||||||
return name.lower()
|
|
||||||
Red
|
|
||||||
Green
|
|
||||||
Blue
|
|
||||||
self.assertTrue(isinstance(Color.Red, str))
|
|
||||||
self.assertEqual(Color.Green, 'green')
|
|
||||||
self.assertTrue(Color.Blue < Color.Red)
|
|
||||||
|
|
||||||
|
|
||||||
def test_overridden_ignore(self):
|
|
||||||
with self.assertRaisesRegex(TypeError, "'str' object is not callable"):
|
|
||||||
class Color(Enum):
|
|
||||||
def _generate_next_value_(name, start, count, last_value):
|
|
||||||
return name
|
|
||||||
_ignore_ = ()
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue
|
|
||||||
@property
|
|
||||||
def whatever(self):
|
|
||||||
pass
|
|
||||||
with self.assertRaisesRegex(TypeError, "'str' object is not callable"):
|
|
||||||
class Color(Enum, ignore=None):
|
|
||||||
def _generate_next_value_(name, start, count, last_value):
|
|
||||||
return name
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue
|
|
||||||
@property
|
|
||||||
def whatever(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_property(self):
|
|
||||||
class Color(Enum):
|
|
||||||
def _generate_next_value_(name, start, count, last_value):
|
|
||||||
return name
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue
|
|
||||||
@property
|
|
||||||
def upper_name(self):
|
|
||||||
return self.name.upper()
|
|
||||||
self.assertEqual(Color.blue.upper_name, 'BLUE')
|
|
||||||
|
|
||||||
def test_magic_turns_off(self):
|
|
||||||
with self.assertRaisesRegex(NameError, "brown"):
|
|
||||||
class Color(Enum):
|
|
||||||
def _generate_next_value_(name, start, count, last_value):
|
|
||||||
return name
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue
|
|
||||||
@property
|
|
||||||
def cap_name(self):
|
|
||||||
return self.name.title()
|
|
||||||
brown
|
|
||||||
|
|
||||||
with self.assertRaisesRegex(NameError, "rose"):
|
|
||||||
class Color(Enum):
|
|
||||||
def _generate_next_value_(name, start, count, last_value):
|
|
||||||
return name
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue
|
|
||||||
def hello(self):
|
|
||||||
print('Hello! My value %s.' % self.value)
|
|
||||||
rose
|
|
||||||
|
|
||||||
with self.assertRaisesRegex(NameError, "cyan"):
|
|
||||||
class Color(Enum):
|
|
||||||
def _generate_next_value_(name, start, count, last_value):
|
|
||||||
return name
|
|
||||||
red
|
|
||||||
green
|
|
||||||
blue
|
|
||||||
def __init__(self, *args):
|
|
||||||
pass
|
|
||||||
cyan
|
|
||||||
|
|
||||||
def test_powers_of_two(self):
|
|
||||||
class Bits(Enum):
|
|
||||||
def _generate_next_value_(name, start, count, last_value):
|
|
||||||
return 2 ** count
|
|
||||||
one
|
|
||||||
two
|
|
||||||
four
|
|
||||||
eight
|
|
||||||
self.assertEqual(Bits.one.value, 1)
|
|
||||||
self.assertEqual(Bits.two.value, 2)
|
|
||||||
self.assertEqual(Bits.four.value, 4)
|
|
||||||
self.assertEqual(Bits.eight.value, 8)
|
|
||||||
|
|
||||||
def test_powers_of_two_as_int(self):
|
|
||||||
class Bits(int, Enum):
|
|
||||||
def _generate_next_value_(name, start, count, last_value):
|
|
||||||
return 2 ** count
|
|
||||||
one
|
|
||||||
two
|
|
||||||
four
|
|
||||||
eight
|
|
||||||
self.assertEqual(Bits.one, 1)
|
|
||||||
self.assertEqual(Bits.two, 2)
|
|
||||||
self.assertEqual(Bits.four, 4)
|
|
||||||
self.assertEqual(Bits.eight, 8)
|
|
||||||
|
|
||||||
|
|
||||||
class TestUnique(unittest.TestCase):
|
class TestUnique(unittest.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -179,8 +179,6 @@ Library
|
||||||
- Issue #27512: Fix a segfault when os.fspath() called a an __fspath__() method
|
- Issue #27512: Fix a segfault when os.fspath() called a an __fspath__() method
|
||||||
that raised an exception. Patch by Xiang Zhang.
|
that raised an exception. Patch by Xiang Zhang.
|
||||||
|
|
||||||
- Issue #26988: Add AutoEnum.
|
|
||||||
|
|
||||||
Tests
|
Tests
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue