summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/howto/enum.rst272
-rw-r--r--Doc/library/enum.rst265
-rw-r--r--Doc/library/ssl.rst4
-rw-r--r--Lib/enum.py603
-rw-r--r--Lib/inspect.py30
-rw-r--r--Lib/plistlib.py3
-rw-r--r--Lib/re.py2
-rw-r--r--Lib/ssl.py1
-rw-r--r--Lib/test/test_enum.py2864
-rw-r--r--Lib/test/test_signal.py2
-rwxr-xr-xLib/test/test_socket.py12
-rw-r--r--Lib/test/test_ssl.py8
-rw-r--r--Lib/test/test_unicode.py6
-rw-r--r--Misc/NEWS.d/next/Library/2022-01-13-11-41-24.bpo-40066.1QuVli.rst2
14 files changed, 2070 insertions, 2004 deletions
diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst
index 6c09b99..fa0e228 100644
--- a/Doc/howto/enum.rst
+++ b/Doc/howto/enum.rst
@@ -2,15 +2,10 @@
Enum HOWTO
==========
-:Author: Ethan Furman <ethan at stoneleaf dot us>
-
.. _enum-basic-tutorial:
.. currentmodule:: enum
-Basic Enum Tutorial
--------------------
-
An :class:`Enum` is a set of symbolic names bound to unique values. They are
similar to global variables, but they offer a more useful :func:`repr()`,
grouping, type-safety, and a few other features.
@@ -28,6 +23,14 @@ selection of values. For example, the days of the week::
... SATURDAY = 6
... SUNDAY = 7
+ Or perhaps the RGB primary colors::
+
+ >>> from enum import Enum
+ >>> class Color(Enum):
+ ... RED = 1
+ ... GREEN = 2
+ ... BLUE = 3
+
As you can see, creating an :class:`Enum` is as simple as writing a class that
inherits from :class:`Enum` itself.
@@ -41,13 +44,14 @@ important, but either way that value can be used to get the corresponding
member::
>>> Weekday(3)
- Weekday.WEDNESDAY
+ <Weekday.WEDNESDAY: 3>
-As you can see, the ``repr()`` of a member shows the enum name and the
-member name. The ``str()`` on a member shows only its name::
+As you can see, the ``repr()`` of a member shows the enum name, the member name,
+and the value. The ``str()`` of a member shows only the enum name and member
+name::
>>> print(Weekday.THURSDAY)
- THURSDAY
+ Weekday.THURSDAY
The *type* of an enumeration member is the enum it belongs to::
@@ -97,8 +101,8 @@ The complete :class:`Weekday` enum now looks like this::
Now we can find out what today is! Observe::
>>> from datetime import date
- >>> Weekday.from_date(date.today())
- Weekday.TUESDAY
+ >>> Weekday.from_date(date.today()) # doctest: +SKIP
+ <Weekday.TUESDAY: 2>
Of course, if you're reading this on some other day, you'll see that day instead.
@@ -124,21 +128,21 @@ Just like the original :class:`Weekday` enum above, we can have a single selecti
>>> first_week_day = Weekday.MONDAY
>>> first_week_day
- Weekday.MONDAY
+ <Weekday.MONDAY: 1>
But :class:`Flag` also allows us to combine several members into a single
variable::
>>> weekend = Weekday.SATURDAY | Weekday.SUNDAY
>>> weekend
- Weekday.SATURDAY|Weekday.SUNDAY
+ <Weekday.SATURDAY|SUNDAY: 96>
You can even iterate over a :class:`Flag` variable::
>>> for day in weekend:
... print(day)
- SATURDAY
- SUNDAY
+ Weekday.SATURDAY
+ Weekday.SUNDAY
Okay, let's get some chores set up::
@@ -173,6 +177,7 @@ yourself some work and use :func:`auto()` for the values::
.. _enum-advanced-tutorial:
+
Programmatic access to enumeration members and their attributes
---------------------------------------------------------------
@@ -181,16 +186,16 @@ situations where ``Color.RED`` won't do because the exact color is not known
at program-writing time). ``Enum`` allows such access::
>>> Color(1)
- Color.RED
+ <Color.RED: 1>
>>> Color(3)
- Color.BLUE
+ <Color.BLUE: 3>
If you want to access enum members by *name*, use item access::
>>> Color['RED']
- Color.RED
+ <Color.RED: 1>
>>> Color['GREEN']
- Color.GREEN
+ <Color.GREEN: 2>
If you have an enum member and need its :attr:`name` or :attr:`value`::
@@ -212,7 +217,7 @@ Having two enum members with the same name is invalid::
...
Traceback (most recent call last):
...
- TypeError: 'SQUARE' already defined as: 2
+ TypeError: 'SQUARE' already defined as 2
However, an enum member can have other names associated with it. Given two
entries ``A`` and ``B`` with the same value (and ``A`` defined first), ``B``
@@ -227,11 +232,11 @@ By-name lookup of ``B`` will also return the member ``A``::
... ALIAS_FOR_SQUARE = 2
...
>>> Shape.SQUARE
- Shape.SQUARE
+ <Shape.SQUARE: 2>
>>> Shape.ALIAS_FOR_SQUARE
- Shape.SQUARE
+ <Shape.SQUARE: 2>
>>> Shape(2)
- Shape.SQUARE
+ <Shape.SQUARE: 2>
.. note::
@@ -299,7 +304,7 @@ Iteration
Iterating over the members of an enum does not provide the aliases::
>>> list(Shape)
- [Shape.SQUARE, Shape.DIAMOND, Shape.CIRCLE]
+ [<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]
The special attribute ``__members__`` is a read-only ordered mapping of names
to members. It includes all names defined in the enumeration, including the
@@ -308,10 +313,10 @@ aliases::
>>> for name, member in Shape.__members__.items():
... name, member
...
- ('SQUARE', Shape.SQUARE)
- ('DIAMOND', Shape.DIAMOND)
- ('CIRCLE', Shape.CIRCLE)
- ('ALIAS_FOR_SQUARE', Shape.SQUARE)
+ ('SQUARE', <Shape.SQUARE: 2>)
+ ('DIAMOND', <Shape.DIAMOND: 1>)
+ ('CIRCLE', <Shape.CIRCLE: 3>)
+ ('ALIAS_FOR_SQUARE', <Shape.SQUARE: 2>)
The ``__members__`` attribute can be used for detailed programmatic access to
the enumeration members. For example, finding all the aliases::
@@ -360,8 +365,8 @@ below)::
Allowed members and attributes of enumerations
----------------------------------------------
-Most of the examples above use integers for enumeration values. Using integers is
-short and handy (and provided by default by the `Functional API`_), but not
+Most of the examples above use integers for enumeration values. Using integers
+is short and handy (and provided by default by the `Functional API`_), but not
strictly enforced. In the vast majority of use-cases, one doesn't care what
the actual value of an enumeration is. But if the value *is* important,
enumerations can have arbitrary values.
@@ -389,7 +394,7 @@ usual. If we have this enumeration::
Then::
>>> Mood.favorite_mood()
- Mood.HAPPY
+ <Mood.HAPPY: 3>
>>> Mood.HAPPY.describe()
('HAPPY', 3)
>>> str(Mood.FUNKY)
@@ -425,7 +430,7 @@ any members. So this is forbidden::
...
Traceback (most recent call last):
...
- TypeError: MoreColor: cannot extend enumeration 'Color'
+ TypeError: <enum 'MoreColor'> cannot extend <enum 'Color'>
But this is allowed::
@@ -476,11 +481,9 @@ The :class:`Enum` class is callable, providing the following functional API::
>>> Animal
<enum 'Animal'>
>>> Animal.ANT
- Animal.ANT
- >>> Animal.ANT.value
- 1
+ <Animal.ANT: 1>
>>> list(Animal)
- [Animal.ANT, Animal.BEE, Animal.CAT, Animal.DOG]
+ [<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>]
The semantics of this API resemble :class:`~collections.namedtuple`. The first
argument of the call to :class:`Enum` is the name of the enumeration.
@@ -625,16 +628,7 @@ StrEnum
The second variation of :class:`Enum` that is provided is also a subclass of
:class:`str`. Members of a :class:`StrEnum` can be compared to strings;
by extension, string enumerations of different types can also be compared
-to each other. :class:`StrEnum` exists to help avoid the problem of getting
-an incorrect member::
-
- >>> from enum import StrEnum
- >>> class Directions(StrEnum):
- ... NORTH = 'north', # notice the trailing comma
- ... SOUTH = 'south'
-
-Before :class:`StrEnum`, ``Directions.NORTH`` would have been the :class:`tuple`
-``('north',)``.
+to each other.
.. versionadded:: 3.11
@@ -645,9 +639,8 @@ IntFlag
The next variation of :class:`Enum` provided, :class:`IntFlag`, is also based
on :class:`int`. The difference being :class:`IntFlag` members can be combined
using the bitwise operators (&, \|, ^, ~) and the result is still an
-:class:`IntFlag` member, if possible. However, as the name implies, :class:`IntFlag`
-members also subclass :class:`int` and can be used wherever an :class:`int` is
-used.
+:class:`IntFlag` member, if possible. Like :class:`IntEnum`, :class:`IntFlag`
+members are also integers and can be used wherever an :class:`int` is used.
.. note::
@@ -670,7 +663,7 @@ Sample :class:`IntFlag` class::
... X = 1
...
>>> Perm.R | Perm.W
- Perm.R|Perm.W
+ <Perm.R|W: 6>
>>> Perm.R + Perm.W
6
>>> RW = Perm.R | Perm.W
@@ -685,11 +678,11 @@ It is also possible to name the combinations::
... X = 1
... RWX = 7
>>> Perm.RWX
- Perm.RWX
+ <Perm.RWX: 7>
>>> ~Perm.RWX
- Perm(0)
+ <Perm: 0>
>>> Perm(7)
- Perm.RWX
+ <Perm.RWX: 7>
.. note::
@@ -702,7 +695,7 @@ Another important difference between :class:`IntFlag` and :class:`Enum` is that
if no flags are set (the value is 0), its boolean evaluation is :data:`False`::
>>> Perm.R & Perm.X
- Perm(0)
+ <Perm: 0>
>>> bool(Perm.R & Perm.X)
False
@@ -710,7 +703,7 @@ Because :class:`IntFlag` members are also subclasses of :class:`int` they can
be combined with them (but may lose :class:`IntFlag` membership::
>>> Perm.X | 4
- Perm.R|Perm.X
+ <Perm.R|X: 5>
>>> Perm.X | 8
9
@@ -726,7 +719,7 @@ be combined with them (but may lose :class:`IntFlag` membership::
:class:`IntFlag` members can also be iterated over::
>>> list(RW)
- [Perm.R, Perm.W]
+ [<Perm.R: 4>, <Perm.W: 2>]
.. versionadded:: 3.11
@@ -753,7 +746,7 @@ flags being set, the boolean evaluation is :data:`False`::
... GREEN = auto()
...
>>> Color.RED & Color.GREEN
- Color(0)
+ <Color: 0>
>>> bool(Color.RED & Color.GREEN)
False
@@ -767,7 +760,7 @@ while combinations of flags won't::
... WHITE = RED | BLUE | GREEN
...
>>> Color.WHITE
- Color.WHITE
+ <Color.WHITE: 7>
Giving a name to the "no flags set" condition does not change its boolean
value::
@@ -779,7 +772,7 @@ value::
... GREEN = auto()
...
>>> Color.BLACK
- Color.BLACK
+ <Color.BLACK: 0>
>>> bool(Color.BLACK)
False
@@ -787,7 +780,7 @@ value::
>>> purple = Color.RED | Color.BLUE
>>> list(purple)
- [Color.RED, Color.BLUE]
+ [<Color.RED: 1>, <Color.BLUE: 2>]
.. versionadded:: 3.11
@@ -812,16 +805,16 @@ simple to implement independently::
pass
This demonstrates how similar derived enumerations can be defined; for example
-a :class:`StrEnum` that mixes in :class:`str` instead of :class:`int`.
+a :class:`FloatEnum` that mixes in :class:`float` instead of :class:`int`.
Some rules:
1. When subclassing :class:`Enum`, mix-in types must appear before
:class:`Enum` itself in the sequence of bases, as in the :class:`IntEnum`
example above.
-2. Mix-in types must be subclassable. For example,
- :class:`bool` and :class:`range` are not subclassable
- and will throw an error during Enum creation if used as the mix-in type.
+2. Mix-in types must be subclassable. For example, :class:`bool` and
+ :class:`range` are not subclassable and will throw an error during Enum
+ creation if used as the mix-in type.
3. While :class:`Enum` can have members of any type, once you mix in an
additional type, all the members must have values of that type, e.g.
:class:`int` above. This restriction does not apply to mix-ins which only
@@ -829,15 +822,18 @@ Some rules:
4. When another data type is mixed in, the :attr:`value` attribute is *not the
same* as the enum member itself, although it is equivalent and will compare
equal.
-5. %-style formatting: `%s` and `%r` call the :class:`Enum` class's
+5. %-style formatting: ``%s`` and ``%r`` call the :class:`Enum` class's
:meth:`__str__` and :meth:`__repr__` respectively; other codes (such as
- `%i` or `%h` for IntEnum) treat the enum member as its mixed-in type.
+ ``%i`` or ``%h`` for IntEnum) treat the enum member as its mixed-in type.
6. :ref:`Formatted string literals <f-strings>`, :meth:`str.format`,
- and :func:`format` will use the mixed-in type's :meth:`__format__`
- unless :meth:`__str__` or :meth:`__format__` is overridden in the subclass,
- in which case the overridden methods or :class:`Enum` methods will be used.
- Use the !s and !r format codes to force usage of the :class:`Enum` class's
- :meth:`__str__` and :meth:`__repr__` methods.
+ and :func:`format` will use the enum's :meth:`__str__` method.
+
+.. note::
+
+ Because :class:`IntEnum`, :class:`IntFlag`, and :class:`StrEnum` are
+ designed to be drop-in replacements for existing constants, their
+ :meth:`__str__` method has been reset to their data types
+ :meth:`__str__` method.
When to use :meth:`__new__` vs. :meth:`__init__`
------------------------------------------------
@@ -866,10 +862,10 @@ want one of them to be the value::
...
>>> print(Coordinate['PY'])
- PY
+ Coordinate.PY
>>> print(Coordinate(3))
- VY
+ Coordinate.VY
Finer Points
@@ -927,8 +923,8 @@ and raise an error if the two do not match::
Traceback (most recent call last):
...
TypeError: member order does not match _order_:
- ['RED', 'BLUE', 'GREEN']
- ['RED', 'GREEN', 'BLUE']
+ ['RED', 'BLUE', 'GREEN']
+ ['RED', 'GREEN', 'BLUE']
.. note::
@@ -949,35 +945,36 @@ but remain normal attributes.
""""""""""""""""""""
Enum members are instances of their enum class, and are normally accessed as
-``EnumClass.member``. In Python versions ``3.5`` to ``3.9`` you could access
-members from other members -- this practice was discouraged, and in ``3.12``
-:class:`Enum` will return to not allowing it, while in ``3.10`` and ``3.11``
-it will raise a :exc:`DeprecationWarning`::
+``EnumClass.member``. In Python versions ``3.5`` to ``3.10`` you could access
+members from other members -- this practice was discouraged, and in ``3.11``
+:class:`Enum` returns to not allowing it::
>>> class FieldTypes(Enum):
... name = 0
... value = 1
... size = 2
...
- >>> FieldTypes.value.size # doctest: +SKIP
- DeprecationWarning: accessing one member from another is not supported,
- and will be disabled in 3.12
- <FieldTypes.size: 2>
+ >>> FieldTypes.value.size
+ Traceback (most recent call last):
+ ...
+ AttributeError: <enum 'FieldTypes'> member has no attribute 'size'
+
.. versionchanged:: 3.5
+.. versionchanged:: 3.11
Creating members that are mixed with other data types
"""""""""""""""""""""""""""""""""""""""""""""""""""""
When subclassing other data types, such as :class:`int` or :class:`str`, with
-an :class:`Enum`, all values after the `=` are passed to that data type's
+an :class:`Enum`, all values after the ``=`` are passed to that data type's
constructor. For example::
- >>> class MyEnum(IntEnum):
- ... example = '11', 16 # '11' will be interpreted as a hexadecimal
- ... # number
- >>> MyEnum.example.value
+ >>> class MyEnum(IntEnum): # help(int) -> int(x, base=10) -> integer
+ ... example = '11', 16 # so x='11' and base=16
+ ...
+ >>> MyEnum.example.value # and hex(11) is...
17
@@ -1000,13 +997,12 @@ Plain :class:`Enum` classes always evaluate as :data:`True`.
"""""""""""""""""""""""""""""
If you give your enum subclass extra methods, like the `Planet`_
-class below, those methods will show up in a :func:`dir` of the member and the
-class. Attributes defined in an :func:`__init__` method will only show up in a
-:func:`dir` of the member::
+class below, those methods will show up in a :func:`dir` of the member,
+but not of the class::
- >>> dir(Planet)
- ['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__init__', '__members__', '__module__', 'surface_gravity']
- >>> dir(Planet.EARTH)
+ >>> dir(Planet) # doctest: +SKIP
+ ['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__']
+ >>> dir(Planet.EARTH) # doctest: +SKIP
['__class__', '__doc__', '__module__', 'mass', 'name', 'radius', 'surface_gravity', 'value']
@@ -1025,19 +1021,10 @@ are comprised of a single bit::
... CYAN = GREEN | BLUE
...
>>> Color(3) # named combination
- Color.YELLOW
+ <Color.YELLOW: 3>
>>> Color(7) # not named combination
- Color.RED|Color.GREEN|Color.BLUE
+ <Color.RED|GREEN|BLUE: 7>
-``StrEnum`` and :meth:`str.__str__`
-"""""""""""""""""""""""""""""""""""
-
-An important difference between :class:`StrEnum` and other Enums is the
-:meth:`__str__` method; because :class:`StrEnum` members are strings, some
-parts of Python will read the string data directly, while others will call
-:meth:`str()`. To make those two operations have the same result,
-:meth:`StrEnum.__str__` will be the same as :meth:`str.__str__` so that
-``str(StrEnum.member) == StrEnum.member`` is true.
``Flag`` and ``IntFlag`` minutia
""""""""""""""""""""""""""""""""
@@ -1060,16 +1047,16 @@ the following are true:
- only canonical flags are returned during iteration::
>>> list(Color.WHITE)
- [Color.RED, Color.GREEN, Color.BLUE]
+ [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]
- negating a flag or flag set returns a new flag/flag set with the
corresponding positive integer value::
>>> Color.BLUE
- Color.BLUE
+ <Color.BLUE: 4>
>>> ~Color.BLUE
- Color.RED|Color.GREEN
+ <Color.RED|GREEN: 3>
- names of pseudo-flags are constructed from their members' names::
@@ -1079,25 +1066,29 @@ the following are true:
- multi-bit flags, aka aliases, can be returned from operations::
>>> Color.RED | Color.BLUE
- Color.PURPLE
+ <Color.PURPLE: 5>
>>> Color(7) # or Color(-1)
- Color.WHITE
+ <Color.WHITE: 7>
>>> Color(0)
- Color.BLACK
+ <Color.BLACK: 0>
-- membership / containment checking has changed slightly -- zero-valued flags
- are never considered to be contained::
+- membership / containment checking: zero-valued flags are always considered
+ to be contained::
>>> Color.BLACK in Color.WHITE
- False
+ True
- otherwise, if all bits of one flag are in the other flag, True is returned::
+ otherwise, only if all bits of one flag are in the other flag will True
+ be returned::
>>> Color.PURPLE in Color.WHITE
True
+ >>> Color.GREEN in Color.PURPLE
+ False
+
There is a new boundary mechanism that controls how out-of-range / invalid
bits are handled: ``STRICT``, ``CONFORM``, ``EJECT``, and ``KEEP``:
@@ -1181,7 +1172,7 @@ Using :class:`auto` would look like::
... GREEN = auto()
...
>>> Color.GREEN
- <Color.GREEN>
+ <Color.GREEN: 3>
Using :class:`object`
@@ -1194,10 +1185,24 @@ Using :class:`object` would look like::
... GREEN = object()
... BLUE = object()
...
+ >>> Color.GREEN # doctest: +SKIP
+ <Color.GREEN: <object object at 0x...>>
+
+This is also a good example of why you might want to write your own
+:meth:`__repr__`::
+
+ >>> class Color(Enum):
+ ... RED = object()
+ ... GREEN = object()
+ ... BLUE = object()
+ ... def __repr__(self):
+ ... return "<%s.%s>" % (self.__class__.__name__, self._name_)
+ ...
>>> Color.GREEN
<Color.GREEN>
+
Using a descriptive string
""""""""""""""""""""""""""
@@ -1209,9 +1214,7 @@ Using a string as the value would look like::
... BLUE = 'too fast!'
...
>>> Color.GREEN
- <Color.GREEN>
- >>> Color.GREEN.value
- 'go'
+ <Color.GREEN: 'go'>
Using a custom :meth:`__new__`
@@ -1232,9 +1235,7 @@ Using an auto-numbering :meth:`__new__` would look like::
... BLUE = ()
...
>>> Color.GREEN
- <Color.GREEN>
- >>> Color.GREEN.value
- 2
+ <Color.GREEN: 2>
To make a more general purpose ``AutoNumber``, add ``*args`` to the signature::
@@ -1257,7 +1258,7 @@ to handle any extra arguments::
... BLEACHED_CORAL = () # New color, no Pantone code yet!
...
>>> Swatch.SEA_GREEN
- <Swatch.SEA_GREEN>
+ <Swatch.SEA_GREEN: 2>
>>> Swatch.SEA_GREEN.pantone
'1246'
>>> Swatch.BLEACHED_CORAL.pantone
@@ -1384,30 +1385,9 @@ An example to show the :attr:`_ignore_` attribute in use::
... Period['day_%d' % i] = i
...
>>> list(Period)[:2]
- [Period.day_0, Period.day_1]
+ [<Period.day_0: datetime.timedelta(0)>, <Period.day_1: datetime.timedelta(days=1)>]
>>> list(Period)[-2:]
- [Period.day_365, Period.day_366]
-
-
-Conforming input to Flag
-^^^^^^^^^^^^^^^^^^^^^^^^
-
-To create a :class:`Flag` enum that is more resilient to out-of-bounds results
-from mathematical operations, you can use the :attr:`FlagBoundary.CONFORM`
-setting::
-
- >>> from enum import Flag, CONFORM, auto
- >>> class Weekday(Flag, boundary=CONFORM):
- ... MONDAY = auto()
- ... TUESDAY = auto()
- ... WEDNESDAY = auto()
- ... THURSDAY = auto()
- ... FRIDAY = auto()
- ... SATURDAY = auto()
- ... SUNDAY = auto()
- >>> today = Weekday.TUESDAY
- >>> Weekday(today + 22) # what day is three weeks from tomorrow?
- >>> Weekday.WEDNESDAY
+ [<Period.day_365: datetime.timedelta(days=365)>, <Period.day_366: datetime.timedelta(days=366)>]
.. _enumtype-examples:
diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst
index 8bb19dc..906c60b 100644
--- a/Doc/library/enum.rst
+++ b/Doc/library/enum.rst
@@ -31,7 +31,7 @@ An enumeration:
* uses *call* syntax to return members by value
* uses *index* syntax to return members by name
-Enumerations are created either by using the :keyword:`class` syntax, or by
+Enumerations are created either by using :keyword:`class` syntax, or by
using function-call syntax::
>>> from enum import Enum
@@ -45,7 +45,7 @@ using function-call syntax::
>>> # functional syntax
>>> Color = Enum('Color', ['RED', 'GREEN', 'BLUE'])
-Even though we can use the :keyword:`class` syntax to create Enums, Enums
+Even though we can use :keyword:`class` syntax to create Enums, Enums
are not normal Python classes. See
:ref:`How are Enums different? <enum-class-differences>` for more details.
@@ -53,7 +53,7 @@ are not normal Python classes. See
- The class :class:`Color` is an *enumeration* (or *enum*)
- The attributes :attr:`Color.RED`, :attr:`Color.GREEN`, etc., are
- *enumeration members* (or *enum members*) and are functionally constants.
+ *enumeration members* (or *members*) and are functionally constants.
- The enum members have *names* and *values* (the name of
:attr:`Color.RED` is ``RED``, the value of :attr:`Color.BLUE` is
``3``, etc.)
@@ -110,15 +110,10 @@ Module Contents
:class:`StrEnum` defaults to the lower-cased version of the member name,
while other Enums default to 1 and increase from there.
- :func:`global_enum`
-
- :class:`Enum` class decorator to apply the appropriate global `__repr__`,
- and export its members into the global name space.
-
- :func:`.property`
+ :func:`property`
Allows :class:`Enum` members to have attributes without conflicting with
- other members' names.
+ member names.
:func:`unique`
@@ -131,7 +126,7 @@ Module Contents
.. versionadded:: 3.6 ``Flag``, ``IntFlag``, ``auto``
-.. versionadded:: 3.11 ``StrEnum``, ``EnumCheck``, ``FlagBoundary``
+.. versionadded:: 3.11 ``StrEnum``, ``EnumCheck``, ``FlagBoundary``, ``property``
---------------
@@ -145,6 +140,11 @@ Data Types
to subclass *EnumType* -- see :ref:`Subclassing EnumType <enumtype-examples>`
for details.
+ *EnumType* is responsible for setting the correct :meth:`__repr__`,
+ :meth:`__str__`, :meth:`__format__`, and :meth:`__reduce__` methods on the
+ final *enum*, as well as creating the enum members, properly handling
+ duplicates, providing iteration over the enum class, etc.
+
.. method:: EnumType.__contains__(cls, member)
Returns ``True`` if member belongs to the ``cls``::
@@ -162,32 +162,31 @@ Data Types
.. method:: EnumType.__dir__(cls)
Returns ``['__class__', '__doc__', '__members__', '__module__']`` and the
- names of the members in ``cls``. User-defined methods and methods from
- mixin classes will also be included::
+ names of the members in *cls*::
>>> dir(Color)
- ['BLUE', 'GREEN', 'RED', '__class__', '__doc__', '__members__', '__module__']
+ ['BLUE', 'GREEN', 'RED', '__class__', '__contains__', '__doc__', '__getitem__', '__init_subclass__', '__iter__', '__len__', '__members__', '__module__', '__name__', '__qualname__']
.. method:: EnumType.__getattr__(cls, name)
Returns the Enum member in *cls* matching *name*, or raises an :exc:`AttributeError`::
>>> Color.GREEN
- Color.GREEN
+ <Color.GREEN: 2>
.. method:: EnumType.__getitem__(cls, name)
- Returns the Enum member in *cls* matching *name*, or raises a :exc:`KeyError`::
+ Returns the Enum member in *cls* matching *name*, or raises an :exc:`KeyError`::
>>> Color['BLUE']
- Color.BLUE
+ <Color.BLUE: 3>
.. method:: EnumType.__iter__(cls)
Returns each member in *cls* in definition order::
>>> list(Color)
- [Color.RED, Color.GREEN, Color.BLUE]
+ [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 3>]
.. method:: EnumType.__len__(cls)
@@ -201,7 +200,7 @@ Data Types
Returns each member in *cls* in reverse definition order::
>>> list(reversed(Color))
- [Color.BLUE, Color.GREEN, Color.RED]
+ [<Color.BLUE: 3>, <Color.GREEN: 2>, <Color.RED: 1>]
.. class:: Enum
@@ -232,7 +231,7 @@ Data Types
.. attribute:: Enum._ignore_
``_ignore_`` is only used during creation and is removed from the
- enumeration once that is complete.
+ enumeration once creation is complete.
``_ignore_`` is a list of names that will not become members, and whose
names will also be removed from the completed enumeration. See
@@ -261,7 +260,7 @@ Data Types
.. method:: Enum.__dir__(self)
Returns ``['__class__', '__doc__', '__module__', 'name', 'value']`` and
- any public methods defined on ``self.__class__`` or a mixin class::
+ any public methods defined on *self.__class__*::
>>> from datetime import date
>>> class Weekday(Enum):
@@ -276,7 +275,7 @@ Data Types
... def today(cls):
... print('today is %s' % cls(date.today().isoweekday()).name)
>>> dir(Weekday.SATURDAY)
- ['__class__', '__doc__', '__module__', 'name', 'today', 'value']
+ ['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'today', 'value']
.. method:: Enum._generate_next_value_(name, start, count, last_values)
@@ -298,6 +297,11 @@ Data Types
>>> PowersOfThree.SECOND.value
6
+ .. method:: Enum.__init_subclass__(cls, \**kwds)
+
+ A *classmethod* that is used to further configure subsequent subclasses.
+ By default, does nothing.
+
.. method:: Enum._missing_(cls, value)
A *classmethod* for looking up values not found in *cls*. By default it
@@ -317,42 +321,55 @@ Data Types
>>> Build.DEBUG.value
'debug'
>>> Build('deBUG')
- Build.DEBUG
+ <Build.DEBUG: 'debug'>
.. method:: Enum.__repr__(self)
Returns the string used for *repr()* calls. By default, returns the
- *Enum* name and the member name, but can be overridden::
+ *Enum* name, member name, and value, but can be overridden::
- >>> class OldStyle(Enum):
- ... RETRO = auto()
- ... OLD_SCHOOl = auto()
- ... YESTERYEAR = auto()
+ >>> class OtherStyle(Enum):
+ ... ALTERNATE = auto()
+ ... OTHER = auto()
+ ... SOMETHING_ELSE = auto()
... def __repr__(self):
... cls_name = self.__class__.__name__
- ... return f'<{cls_name}.{self.name}: {self.value}>'
- >>> OldStyle.RETRO
- <OldStyle.RETRO: 1>
+ ... return f'{cls_name}.{self.name}'
+ >>> OtherStyle.ALTERNATE, str(OtherStyle.ALTERNATE), f"{OtherStyle.ALTERNATE}"
+ (OtherStyle.ALTERNATE, 'OtherStyle.ALTERNATE', 'OtherStyle.ALTERNATE')
.. method:: Enum.__str__(self)
Returns the string used for *str()* calls. By default, returns the
- member name, but can be overridden::
+ *Enum* name and member name, but can be overridden::
- >>> class OldStyle(Enum):
- ... RETRO = auto()
- ... OLD_SCHOOl = auto()
- ... YESTERYEAR = auto()
+ >>> class OtherStyle(Enum):
+ ... ALTERNATE = auto()
+ ... OTHER = auto()
+ ... SOMETHING_ELSE = auto()
... def __str__(self):
- ... cls_name = self.__class__.__name__
- ... return f'{cls_name}.{self.name}'
- >>> OldStyle.RETRO
- OldStyle.RETRO
+ ... return f'{self.name}'
+ >>> OtherStyle.ALTERNATE, str(OtherStyle.ALTERNATE), f"{OtherStyle.ALTERNATE}"
+ (<OtherStyle.ALTERNATE: 1>, 'ALTERNATE', 'ALTERNATE')
+
+ .. method:: Enum.__format__(self)
+
+ Returns the string used for *format()* and *f-string* calls. By default,
+ returns :meth:`__str__` returns, but can be overridden::
+
+ >>> class OtherStyle(Enum):
+ ... ALTERNATE = auto()
+ ... OTHER = auto()
+ ... SOMETHING_ELSE = auto()
+ ... def __format__(self, spec):
+ ... return f'{self.name}'
+ >>> OtherStyle.ALTERNATE, str(OtherStyle.ALTERNATE), f"{OtherStyle.ALTERNATE}"
+ (<OtherStyle.ALTERNATE: 1>, 'OtherStyle.ALTERNATE', 'ALTERNATE')
-.. note::
+ .. note::
- Using :class:`auto` with :class:`Enum` results in integers of increasing value,
- starting with ``1``.
+ Using :class:`auto` with :class:`Enum` results in integers of increasing value,
+ starting with ``1``.
.. class:: IntEnum
@@ -367,7 +384,7 @@ Data Types
... TWO = 2
... THREE = 3
>>> Numbers.THREE
- Numbers.THREE
+ <Numbers.THREE: 3>
>>> Numbers.ONE + Numbers.TWO
3
>>> Numbers.THREE + 5
@@ -375,10 +392,14 @@ Data Types
>>> Numbers.THREE == 3
True
-.. note::
+ .. note::
- Using :class:`auto` with :class:`IntEnum` results in integers of increasing value,
- starting with ``1``.
+ Using :class:`auto` with :class:`IntEnum` results in integers of increasing
+ value, starting with ``1``.
+
+ .. versionchanged:: 3.11 :meth:`__str__` is now :func:`int.__str__` to
+ better support the *replacement of existing constants* use-case.
+ :meth:`__format__` was already :func:`int.__format__` for that same reason.
.. class:: StrEnum
@@ -392,13 +413,16 @@ Data Types
instead of ``isinstance(str, unknown)``), and in those locations you
will need to use ``str(StrEnum.member)``.
+ .. note::
-.. note::
+ Using :class:`auto` with :class:`StrEnum` results in the lower-cased member
+ name as the value.
- Using :class:`auto` with :class:`StrEnum` results in values of the member name,
- lower-cased.
+ .. note:: :meth:`__str__` is :func:`str.__str__` to better support the
+ *replacement of existing constants* use-case. :meth:`__format__` is likewise
+ :func:`int.__format__` for that same reason.
-.. versionadded:: 3.11
+ .. versionadded:: 3.11
.. class:: Flag
@@ -431,9 +455,9 @@ Data Types
Returns all contained members::
>>> list(Color.RED)
- [Color.RED]
+ [<Color.RED: 1>]
>>> list(purple)
- [Color.RED, Color.BLUE]
+ [<Color.RED: 1>, <Color.BLUE: 4>]
.. method:: __len__(self):
@@ -461,42 +485,52 @@ Data Types
Returns current flag binary or'ed with other::
>>> Color.RED | Color.GREEN
- Color.RED|Color.GREEN
+ <Color.RED|GREEN: 3>
.. method:: __and__(self, other)
Returns current flag binary and'ed with other::
>>> purple & white
- Color.RED|Color.BLUE
+ <Color.RED|BLUE: 5>
>>> purple & Color.GREEN
- 0x0
+ <Color: 0>
.. method:: __xor__(self, other)
Returns current flag binary xor'ed with other::
>>> purple ^ white
- Color.GREEN
+ <Color.GREEN: 2>
>>> purple ^ Color.GREEN
- Color.RED|Color.GREEN|Color.BLUE
+ <Color.RED|GREEN|BLUE: 7>
.. method:: __invert__(self):
Returns all the flags in *type(self)* that are not in self::
>>> ~white
- 0x0
+ <Color: 0>
>>> ~purple
- Color.GREEN
+ <Color.GREEN: 2>
>>> ~Color.RED
- Color.GREEN|Color.BLUE
+ <Color.GREEN|BLUE: 6>
+
+ .. method:: _numeric_repr_
+
+ Function used to format any remaining unnamed numeric values. Default is
+ the value's repr; common choices are :func:`hex` and :func:`oct`.
+
+ .. note::
-.. note::
+ Using :class:`auto` with :class:`Flag` results in integers that are powers
+ of two, starting with ``1``.
- Using :class:`auto` with :class:`Flag` results in integers that are powers
- of two, starting with ``1``.
+ .. versionchanged:: 3.11 The *repr()* of zero-valued flags has changed. It
+ is now::
+ >>> Color(0)
+ <Color: 0>
.. class:: IntFlag
@@ -509,9 +543,9 @@ Data Types
... GREEN = auto()
... BLUE = auto()
>>> Color.RED & 2
- 0x0
+ <Color: 0>
>>> Color.RED | 2
- Color.RED|Color.GREEN
+ <Color.RED|GREEN: 3>
If any integer operation is performed with an *IntFlag* member, the result is
not an *IntFlag*::
@@ -524,15 +558,25 @@ Data Types
* the result is a valid *IntFlag*: an *IntFlag* is returned
* the result is not a valid *IntFlag*: the result depends on the *FlagBoundary* setting
-.. note::
+ The *repr()* of unnamed zero-valued flags has changed. It is now:
+
+ >>> Color(0)
+ <Color: 0>
+
+ .. note::
+
+ Using :class:`auto` with :class:`IntFlag` results in integers that are powers
+ of two, starting with ``1``.
+
+ .. versionchanged:: 3.11 :meth:`__str__` is now :func:`int.__str__` to
+ better support the *replacement of existing constants* use-case.
+ :meth:`__format__` was already :func:`int.__format__` for that same reason.
- Using :class:`auto` with :class:`IntFlag` results in integers that are powers
- of two, starting with ``1``.
.. class:: EnumCheck
*EnumCheck* contains the options used by the :func:`verify` decorator to ensure
- various constraints; failed constraints result in a :exc:`TypeError`.
+ various constraints; failed constraints result in a :exc:`ValueError`.
.. attribute:: UNIQUE
@@ -582,11 +626,11 @@ Data Types
...
ValueError: invalid Flag 'Color': aliases WHITE and NEON are missing combined values of 0x18 [use enum.show_flag_values(value) for details]
-.. note::
+ .. note::
- CONTINUOUS and NAMED_FLAGS are designed to work with integer-valued members.
+ CONTINUOUS and NAMED_FLAGS are designed to work with integer-valued members.
-.. versionadded:: 3.11
+ .. versionadded:: 3.11
.. class:: FlagBoundary
@@ -606,7 +650,7 @@ Data Types
>>> StrictFlag(2**2 + 2**4)
Traceback (most recent call last):
...
- ValueError: StrictFlag: invalid value: 20
+ ValueError: <flag 'StrictFlag'> invalid value 20
given 0b0 10100
allowed 0b0 00111
@@ -621,7 +665,7 @@ Data Types
... GREEN = auto()
... BLUE = auto()
>>> ConformFlag(2**2 + 2**4)
- ConformFlag.BLUE
+ <ConformFlag.BLUE: 4>
.. attribute:: EJECT
@@ -647,12 +691,52 @@ Data Types
... GREEN = auto()
... BLUE = auto()
>>> KeepFlag(2**2 + 2**4)
- KeepFlag.BLUE|0x10
+ <KeepFlag.BLUE|16: 20>
.. versionadded:: 3.11
---------------
+Supported ``__dunder__`` names
+""""""""""""""""""""""""""""""
+
+:attr:`__members__` is a read-only ordered mapping of ``member_name``:``member``
+items. It 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
+""""""""""""""""""""""""""""
+
+- ``_name_`` -- name of the member
+- ``_value_`` -- value of the member; can be set / modified in ``__new__``
+
+- ``_missing_`` -- a lookup function used when a value is not found; may be
+ overridden
+- ``_ignore_`` -- a list of names, either as a :class:`list` or a :class:`str`,
+ that will not be transformed into members, and will be removed from the final
+ class
+- ``_order_`` -- used in Python 2/3 code to ensure member order is consistent
+ (class attribute, removed during class creation)
+- ``_generate_next_value_`` -- used to get an appropriate value for an enum
+ member; may be overridden
+
+ .. note::
+
+ For standard :class:`Enum` classes the next value chosen is the last value seen
+ incremented by one.
+
+ For :class:`Flag` classes the next value chosen will be the next highest
+ power-of-two, regardless of the last value seen.
+
+.. versionadded:: 3.6 ``_missing_``, ``_order_``, ``_generate_next_value_``
+.. versionadded:: 3.7 ``_ignore_``
+
+---------------
+
Utilities and Decorators
------------------------
@@ -668,15 +752,6 @@ Utilities and Decorators
``_generate_next_value_`` can be overridden to customize the values used by
*auto*.
-.. decorator:: global_enum
-
- A :keyword:`class` decorator specifically for enumerations. It replaces the
- :meth:`__repr__` method with one that shows *module_name*.*member_name*. It
- also injects the members, and their aliases, into the global namespace they
- were defined in.
-
-.. versionadded:: 3.11
-
.. decorator:: property
A decorator similar to the built-in *property*, but specifically for
@@ -688,7 +763,7 @@ Utilities and Decorators
*Enum* class, and *Enum* subclasses can define members with the
names ``value`` and ``name``.
-.. versionadded:: 3.11
+ .. versionadded:: 3.11
.. decorator:: unique
@@ -714,7 +789,7 @@ Utilities and Decorators
:class:`EnumCheck` are used to specify which constraints should be checked
on the decorated enumeration.
-.. versionadded:: 3.11
+ .. versionadded:: 3.11
---------------
@@ -726,14 +801,20 @@ Notes
These three enum types are designed to be drop-in replacements for existing
integer- and string-based values; as such, they have extra limitations:
- - ``format()`` will use the value of the enum member, unless ``__str__``
- has been overridden
+ - ``__str__`` uses the value and not the name of the enum member
- - ``StrEnum.__str__`` uses the value and not the name of the enum member
+ - ``__format__``, because it uses ``__str__``, will also use the value of
+ the enum member instead of its name
- If you do not need/want those limitations, you can create your own base
- class by mixing in the ``int`` or ``str`` type yourself::
+ If you do not need/want those limitations, you can either create your own
+ base class by mixing in the ``int`` or ``str`` type yourself::
>>> from enum import Enum
>>> class MyIntEnum(int, Enum):
... pass
+
+ or you can reassign the appropriate :meth:`str`, etc., in your enum::
+
+ >>> from enum import IntEnum
+ >>> class MyIntEnum(IntEnum):
+ ... __str__ = IntEnum.__str__
diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
index eb33d7e..4d8488a 100644
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -2070,7 +2070,7 @@ to speed up repeated connections from the same clients.
:attr:`SSLContext.verify_flags` returns :class:`VerifyFlags` flags:
>>> ssl.create_default_context().verify_flags # doctest: +SKIP
- ssl.VERIFY_X509_TRUSTED_FIRST
+ <VerifyFlags.VERIFY_X509_TRUSTED_FIRST: 32768>
.. attribute:: SSLContext.verify_mode
@@ -2082,7 +2082,7 @@ to speed up repeated connections from the same clients.
:attr:`SSLContext.verify_mode` returns :class:`VerifyMode` enum:
>>> ssl.create_default_context().verify_mode
- ssl.CERT_REQUIRED
+ <VerifyMode.CERT_REQUIRED: 2>
.. index:: single: certificates
diff --git a/Lib/enum.py b/Lib/enum.py
index 93ea1be..772e1ea 100644
--- a/Lib/enum.py
+++ b/Lib/enum.py
@@ -1,16 +1,16 @@
import sys
+import builtins as bltns
from types import MappingProxyType, DynamicClassAttribute
from operator import or_ as _or_
from functools import reduce
-from builtins import property as _bltin_property, bin as _bltin_bin
__all__ = [
'EnumType', 'EnumMeta',
- 'Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag',
+ 'Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag', 'ReprEnum',
'auto', 'unique', 'property', 'verify',
'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP',
- 'global_flag_repr', 'global_enum_repr', 'global_enum',
+ 'global_flag_repr', 'global_enum_repr', 'global_str', 'global_enum',
'EnumCheck', 'CONTINUOUS', 'NAMED_FLAGS', 'UNIQUE',
]
@@ -18,7 +18,7 @@ __all__ = [
# Dummy value for Enum and Flag as there are explicit checks for them
# before they have been created.
# This is also why there are checks in EnumType like `if Enum is not None`
-Enum = Flag = EJECT = None
+Enum = Flag = EJECT = _stdlib_enums = ReprEnum = None
def _is_descriptor(obj):
"""
@@ -116,9 +116,9 @@ def bin(num, max_bits=None):
ceiling = 2 ** (num).bit_length()
if num >= 0:
- s = _bltin_bin(num + ceiling).replace('1', '0', 1)
+ s = bltns.bin(num + ceiling).replace('1', '0', 1)
else:
- s = _bltin_bin(~num ^ (ceiling - 1) + ceiling)
+ s = bltns.bin(~num ^ (ceiling - 1) + ceiling)
sign = s[:3]
digits = s[3:]
if max_bits is not None:
@@ -126,6 +126,19 @@ def bin(num, max_bits=None):
digits = (sign[-1] * max_bits + digits)[-max_bits:]
return "%s %s" % (sign, digits)
+def _dedent(text):
+ """
+ Like textwrap.dedent. Rewritten because we cannot import textwrap.
+ """
+ lines = text.split('\n')
+ blanks = 0
+ for i, ch in enumerate(lines[0]):
+ if ch != ' ':
+ break
+ for j, l in enumerate(lines):
+ lines[j] = l[i:]
+ return '\n'.join(lines)
+
_auto_null = object()
class auto:
@@ -149,22 +162,12 @@ class property(DynamicClassAttribute):
return ownerclass._member_map_[self.name]
except KeyError:
raise AttributeError(
- '%s: no class attribute %r' % (ownerclass.__name__, self.name)
+ '%r has no attribute %r' % (ownerclass, self.name)
)
else:
if self.fget is None:
- # check for member
- if self.name in ownerclass._member_map_:
- import warnings
- warnings.warn(
- "accessing one member from another is not supported, "
- " and will be disabled in 3.12",
- DeprecationWarning,
- stacklevel=2,
- )
- return ownerclass._member_map_[self.name]
raise AttributeError(
- '%s: no instance attribute %r' % (ownerclass.__name__, self.name)
+ '%r member has no attribute %r' % (ownerclass, self.name)
)
else:
return self.fget(instance)
@@ -172,7 +175,7 @@ class property(DynamicClassAttribute):
def __set__(self, instance, value):
if self.fset is None:
raise AttributeError(
- "%s: cannot set instance attribute %r" % (self.clsname, self.name)
+ "<enum %r> cannot set attribute %r" % (self.clsname, self.name)
)
else:
return self.fset(instance, value)
@@ -180,7 +183,7 @@ class property(DynamicClassAttribute):
def __delete__(self, instance):
if self.fdel is None:
raise AttributeError(
- "%s: cannot delete instance attribute %r" % (self.clsname, self.name)
+ "<enum %r> cannot delete attribute %r" % (self.clsname, self.name)
)
else:
return self.fdel(instance)
@@ -328,7 +331,7 @@ class _EnumDict(dict):
elif _is_sunder(key):
if key not in (
'_order_',
- '_generate_next_value_', '_missing_', '_ignore_',
+ '_generate_next_value_', '_numeric_repr_', '_missing_', '_ignore_',
'_iter_member_', '_iter_member_by_value_', '_iter_member_by_def_',
):
raise ValueError(
@@ -358,13 +361,13 @@ class _EnumDict(dict):
key = '_order_'
elif key in self._member_names:
# descriptor overwriting an enum?
- raise TypeError('%r already defined as: %r' % (key, self[key]))
+ raise TypeError('%r already defined as %r' % (key, self[key]))
elif key in self._ignore:
pass
elif not _is_descriptor(value):
if key in self:
# enum overwriting a descriptor?
- raise TypeError('%r already defined as: %r' % (key, self[key]))
+ raise TypeError('%r already defined as %r' % (key, self[key]))
if isinstance(value, auto):
if value.value == _auto_null:
value.value = self._generate_next_value(
@@ -395,7 +398,7 @@ class EnumType(type):
@classmethod
def __prepare__(metacls, cls, bases, **kwds):
# check that previous enum members do not exist
- metacls._check_for_existing_members(cls, bases)
+ metacls._check_for_existing_members_(cls, bases)
# create the namespace dict
enum_dict = _EnumDict()
enum_dict._cls_name = cls
@@ -413,9 +416,10 @@ class EnumType(type):
# inherited __new__ unless a new __new__ is defined (or the resulting
# class will fail).
#
- # remove any keys listed in _ignore_
if _simple:
return super().__new__(metacls, cls, bases, classdict, **kwds)
+ #
+ # remove any keys listed in _ignore_
classdict.setdefault('_ignore_', []).append('_ignore_')
ignore = classdict['_ignore_']
for key in ignore:
@@ -427,8 +431,8 @@ class EnumType(type):
# check for illegal enum names (any others?)
invalid_names = set(member_names) & {'mro', ''}
if invalid_names:
- raise ValueError('Invalid enum member name: {0}'.format(
- ','.join(invalid_names)))
+ raise ValueError('invalid enum member name(s) '.format(
+ ','.join(repr(n) for n in invalid_names)))
#
# adjust the sunders
_order_ = classdict.pop('_order_', None)
@@ -458,6 +462,8 @@ class EnumType(type):
classdict['_value2member_map_'] = {}
classdict['_unhashable_values_'] = []
classdict['_member_type_'] = member_type
+ # now set the __repr__ for the value
+ classdict['_value_repr_'] = metacls._find_data_repr_(cls, bases)
#
# Flag structures (will be removed if final class is not a Flag
classdict['_boundary_'] = (
@@ -467,10 +473,6 @@ class EnumType(type):
classdict['_flag_mask_'] = flag_mask
classdict['_all_bits_'] = 2 ** ((flag_mask).bit_length()) - 1
classdict['_inverted_'] = None
- #
- # create a default docstring if one has not been provided
- if '__doc__' not in classdict:
- classdict['__doc__'] = 'An enumeration.'
try:
exc = None
enum_class = super().__new__(metacls, cls, bases, classdict, **kwds)
@@ -481,18 +483,140 @@ class EnumType(type):
if exc is not None:
raise exc
#
+ # update classdict with any changes made by __init_subclass__
+ classdict.update(enum_class.__dict__)
+ #
+ # create a default docstring if one has not been provided
+ if enum_class.__doc__ is None:
+ if not member_names:
+ enum_class.__doc__ = classdict['__doc__'] = _dedent("""\
+ Create a collection of name/value pairs.
+
+ Example enumeration:
+
+ >>> class Color(Enum):
+ ... RED = 1
+ ... BLUE = 2
+ ... GREEN = 3
+
+ Access them by:
+
+ - attribute access::
+
+ >>> Color.RED
+ <Color.RED: 1>
+
+ - value lookup:
+
+ >>> Color(1)
+ <Color.RED: 1>
+
+ - name lookup:
+
+ >>> Color['RED']
+ <Color.RED: 1>
+
+ Enumerations can be iterated over, and know how many members they have:
+
+ >>> len(Color)
+ 3
+
+ >>> list(Color)
+ [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]
+
+ Methods can be added to enumerations, and members can have their own
+ attributes -- see the documentation for details.
+ """)
+ else:
+ member = list(enum_class)[0]
+ enum_length = len(enum_class)
+ cls_name = enum_class.__name__
+ if enum_length == 1:
+ list_line = 'list(%s)' % cls_name
+ list_repr = '[<%s.%s: %r>]' % (cls_name, member.name, member.value)
+ elif enum_length == 2:
+ member2 = list(enum_class)[1]
+ list_line = 'list(%s)' % cls_name
+ list_repr = '[<%s.%s: %r>, <%s.%s: %r>]' % (
+ cls_name, member.name, member.value,
+ cls_name, member2.name, member2.value,
+ )
+ else:
+ member2 = list(enum_class)[1]
+ member3 = list(enum_class)[2]
+ list_line = 'list(%s)%s' % (cls_name, ('','[:3]')[enum_length > 3])
+ list_repr = '[<%s.%s: %r>, <%s.%s: %r>, <%s.%s: %r>]' % (
+ cls_name, member.name, member.value,
+ cls_name, member2.name, member2.value,
+ cls_name, member3.name, member3.value,
+ )
+ enum_class.__doc__ = classdict['__doc__'] = _dedent("""\
+ A collection of name/value pairs.
+
+ Access them by:
+
+ - attribute access::
+
+ >>> %s.%s
+ <%s.%s: %r>
+
+ - value lookup:
+
+ >>> %s(%r)
+ <%s.%s: %r>
+
+ - name lookup:
+
+ >>> %s[%r]
+ <%s.%s: %r>
+
+ Enumerations can be iterated over, and know how many members they have:
+
+ >>> len(%s)
+ %r
+
+ >>> %s
+ %s
+
+ Methods can be added to enumerations, and members can have their own
+ attributes -- see the documentation for details.
+ """
+ % (cls_name, member.name,
+ cls_name, member.name, member.value,
+ cls_name, member.value,
+ cls_name, member.name, member.value,
+ cls_name, member.name,
+ cls_name, member.name, member.value,
+ cls_name, enum_length,
+ list_line, list_repr,
+ ))
+ #
# double check that repr and friends are not the mixin's or various
# things break (such as pickle)
# however, if the method is defined in the Enum itself, don't replace
# it
+ #
+ # Also, special handling for ReprEnum
+ if ReprEnum is not None and ReprEnum in bases:
+ if member_type is object:
+ raise TypeError(
+ 'ReprEnum subclasses must be mixed with a data type (i.e.'
+ ' int, str, float, etc.)'
+ )
+ if '__format__' not in classdict:
+ enum_class.__format__ = member_type.__format__
+ classdict['__format__'] = enum_class.__format__
+ if '__str__' not in classdict:
+ method = member_type.__str__
+ if method is object.__str__:
+ # if member_type does not define __str__, object.__str__ will use
+ # its __repr__ instead, so we'll also use its __repr__
+ method = member_type.__repr__
+ enum_class.__str__ = method
+ classdict['__str__'] = enum_class.__str__
for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'):
- if name in classdict:
- continue
- class_method = getattr(enum_class, name)
- obj_method = getattr(member_type, name, None)
- enum_method = getattr(first_enum, name, None)
- if obj_method is not None and obj_method is class_method:
- setattr(enum_class, name, enum_method)
+ if name not in classdict:
+ setattr(enum_class, name, getattr(first_enum, name))
#
# replace any other __new__ with our own (as long as Enum is not None,
# anyway) -- again, this is to support pickle
@@ -563,13 +687,13 @@ class EnumType(type):
# _order_ step 4: verify that _order_ and _member_names_ match
if _order_ != enum_class._member_names_:
raise TypeError(
- 'member order does not match _order_:\n%r\n%r'
+ 'member order does not match _order_:\n %r\n %r'
% (enum_class._member_names_, _order_)
)
#
return enum_class
- def __bool__(self):
+ def __bool__(cls):
"""
classes/types should always be True.
"""
@@ -614,6 +738,13 @@ class EnumType(type):
)
def __contains__(cls, member):
+ """
+ Return True if member is a member of this enum
+ raises TypeError if member is not an enum member
+
+ note: in 3.12 TypeError will no longer be raised, and True will also be
+ returned if member is the value of a member in this enum
+ """
if not isinstance(member, Enum):
import warnings
warnings.warn(
@@ -631,60 +762,33 @@ class EnumType(type):
# nicer error message when someone tries to delete an attribute
# (see issue19025).
if attr in cls._member_map_:
- raise AttributeError("%s: cannot delete Enum member %r." % (cls.__name__, attr))
+ raise AttributeError("%r cannot delete member %r." % (cls.__name__, attr))
super().__delattr__(attr)
- def __dir__(self):
- # Start off with the desired result for dir(Enum)
- cls_dir = {'__class__', '__doc__', '__members__', '__module__'}
- add_to_dir = cls_dir.add
- mro = self.__mro__
- this_module = globals().values()
- is_from_this_module = lambda cls: any(cls is thing for thing in this_module)
- first_enum_base = next(cls for cls in mro if is_from_this_module(cls))
- enum_dict = Enum.__dict__
- sentinel = object()
- # special-case __new__
- ignored = {'__new__', *filter(_is_sunder, enum_dict)}
- add_to_ignored = ignored.add
-
- # We want these added to __dir__
- # if and only if they have been user-overridden
- enum_dunders = set(filter(_is_dunder, enum_dict))
-
- for cls in mro:
- # Ignore any classes defined in this module
- if cls is object or is_from_this_module(cls):
- continue
-
- cls_lookup = cls.__dict__
-
- # If not an instance of EnumType,
- # ensure all attributes excluded from that class's `dir()` are ignored here.
- if not isinstance(cls, EnumType):
- cls_lookup = set(cls_lookup).intersection(dir(cls))
-
- for attr_name in cls_lookup:
- # Already seen it? Carry on
- if attr_name in cls_dir or attr_name in ignored:
- continue
- # Sunders defined in Enum.__dict__ are already in `ignored`,
- # But sunders defined in a subclass won't be (we want all sunders excluded).
- elif _is_sunder(attr_name):
- add_to_ignored(attr_name)
- # Not an "enum dunder"? Add it to dir() output.
- elif attr_name not in enum_dunders:
- add_to_dir(attr_name)
- # Is an "enum dunder", and is defined by a class from enum.py? Ignore it.
- elif getattr(self, attr_name) is getattr(first_enum_base, attr_name, sentinel):
- add_to_ignored(attr_name)
- # Is an "enum dunder", and is either user-defined or defined by a mixin class?
- # Add it to dir() output.
- else:
- add_to_dir(attr_name)
-
- # sort the output before returning it, so that the result is deterministic.
- return sorted(cls_dir)
+ def __dir__(cls):
+ # TODO: check for custom __init__, __new__, __format__, __repr__, __str__, __init_subclass__
+ # on object-based enums
+ if cls._member_type_ is object:
+ interesting = set(cls._member_names_)
+ if cls._new_member_ is not object.__new__:
+ interesting.add('__new__')
+ if cls.__init_subclass__ is not object.__init_subclass__:
+ interesting.add('__init_subclass__')
+ for method in ('__init__', '__format__', '__repr__', '__str__'):
+ if getattr(cls, method) not in (getattr(Enum, method), getattr(Flag, method)):
+ interesting.add(method)
+ return sorted(set([
+ '__class__', '__contains__', '__doc__', '__getitem__',
+ '__iter__', '__len__', '__members__', '__module__',
+ '__name__', '__qualname__',
+ ]) | interesting
+ )
+ else:
+ # return whatever mixed-in data type has
+ return sorted(set(
+ dir(cls._member_type_)
+ + cls._member_names_
+ ))
def __getattr__(cls, name):
"""
@@ -703,18 +807,24 @@ class EnumType(type):
raise AttributeError(name) from None
def __getitem__(cls, name):
+ """
+ Return the member matching `name`.
+ """
return cls._member_map_[name]
def __iter__(cls):
"""
- Returns members in definition order.
+ Return members in definition order.
"""
return (cls._member_map_[name] for name in cls._member_names_)
def __len__(cls):
+ """
+ Return the number of members (no aliases)
+ """
return len(cls._member_names_)
- @_bltin_property
+ @bltns.property
def __members__(cls):
"""
Returns a mapping of member name->value.
@@ -732,7 +842,7 @@ class EnumType(type):
def __reversed__(cls):
"""
- Returns members in reverse definition order.
+ Return members in reverse definition order.
"""
return (cls._member_map_[name] for name in reversed(cls._member_names_))
@@ -746,7 +856,7 @@ class EnumType(type):
"""
member_map = cls.__dict__.get('_member_map_', {})
if name in member_map:
- raise AttributeError('Cannot reassign member %r.' % (name, ))
+ raise AttributeError('cannot reassign member %r' % (name, ))
super().__setattr__(name, value)
def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, start=1, boundary=None):
@@ -801,8 +911,7 @@ class EnumType(type):
return metacls.__new__(metacls, class_name, bases, classdict, boundary=boundary)
- def _convert_(cls, name, module, filter, source=None, *, boundary=None):
-
+ def _convert_(cls, name, module, filter, source=None, *, boundary=None, as_global=False):
"""
Create a new Enum subclass that replaces a collection of global constants
"""
@@ -834,22 +943,25 @@ class EnumType(type):
tmp_cls = type(name, (object, ), body)
cls = _simple_enum(etype=cls, boundary=boundary or KEEP)(tmp_cls)
cls.__reduce_ex__ = _reduce_ex_by_global_name
- global_enum(cls)
+ if as_global:
+ global_enum(cls)
+ else:
+ sys.modules[cls.__module__].__dict__.update(cls.__members__)
module_globals[name] = cls
return cls
- @staticmethod
- def _check_for_existing_members(class_name, bases):
+ @classmethod
+ def _check_for_existing_members_(mcls, class_name, bases):
for chain in bases:
for base in chain.__mro__:
if issubclass(base, Enum) and base._member_names_:
raise TypeError(
- "%s: cannot extend enumeration %r"
- % (class_name, base.__name__)
+ "<enum %r> cannot extend %r"
+ % (class_name, base)
)
@classmethod
- def _get_mixins_(cls, class_name, bases):
+ def _get_mixins_(mcls, class_name, bases):
"""
Returns the type for creating enum members, and the first inherited
enum class.
@@ -859,30 +971,7 @@ class EnumType(type):
if not bases:
return object, Enum
- def _find_data_type(bases):
- data_types = set()
- for chain in bases:
- candidate = None
- for base in chain.__mro__:
- if base is object:
- continue
- elif issubclass(base, Enum):
- if base._member_type_ is not object:
- data_types.add(base._member_type_)
- break
- elif '__new__' in base.__dict__:
- if issubclass(base, Enum):
- continue
- data_types.add(candidate or base)
- break
- else:
- candidate = candidate or base
- if len(data_types) > 1:
- raise TypeError('%r: too many data types: %r' % (class_name, data_types))
- elif data_types:
- return data_types.pop()
- else:
- return None
+ mcls._check_for_existing_members_(class_name, bases)
# ensure final parent class is an Enum derivative, find any concrete
# data type, and check that Enum has no members
@@ -890,12 +979,51 @@ class EnumType(type):
if not issubclass(first_enum, Enum):
raise TypeError("new enumerations should be created as "
"`EnumName([mixin_type, ...] [data_type,] enum_type)`")
- cls._check_for_existing_members(class_name, bases)
- member_type = _find_data_type(bases) or object
+ member_type = mcls._find_data_type_(class_name, bases) or object
return member_type, first_enum
- @staticmethod
- def _find_new_(classdict, member_type, first_enum):
+ @classmethod
+ def _find_data_repr_(mcls, class_name, bases):
+ for chain in bases:
+ for base in chain.__mro__:
+ if base is object:
+ continue
+ elif issubclass(base, Enum):
+ # if we hit an Enum, use it's _value_repr_
+ return base._value_repr_
+ elif '__repr__' in base.__dict__:
+ # this is our data repr
+ return base.__dict__['__repr__']
+ return None
+
+ @classmethod
+ def _find_data_type_(mcls, class_name, bases):
+ data_types = set()
+ for chain in bases:
+ candidate = None
+ for base in chain.__mro__:
+ if base is object:
+ continue
+ elif issubclass(base, Enum):
+ if base._member_type_ is not object:
+ data_types.add(base._member_type_)
+ break
+ elif '__new__' in base.__dict__:
+ if issubclass(base, Enum):
+ continue
+ data_types.add(candidate or base)
+ break
+ else:
+ candidate = candidate or base
+ if len(data_types) > 1:
+ raise TypeError('too many data types for %r: %r' % (class_name, data_types))
+ elif data_types:
+ return data_types.pop()
+ else:
+ return None
+
+ @classmethod
+ def _find_new_(mcls, classdict, member_type, first_enum):
"""
Returns the __new__ to be used for creating the enum members.
@@ -943,9 +1071,42 @@ EnumMeta = EnumType
class Enum(metaclass=EnumType):
"""
- Generic enumeration.
+ Create a collection of name/value pairs.
+
+ Example enumeration:
+
+ >>> class Color(Enum):
+ ... RED = 1
+ ... BLUE = 2
+ ... GREEN = 3
+
+ Access them by:
+
+ - attribute access::
+
+ >>> Color.RED
+ <Color.RED: 1>
+
+ - value lookup:
+
+ >>> Color(1)
+ <Color.RED: 1>
- Derive from this class to define new enumerations.
+ - name lookup:
+
+ >>> Color['RED']
+ <Color.RED: 1>
+
+ Enumerations can be iterated over, and know how many members they have:
+
+ >>> len(Color)
+ 3
+
+ >>> list(Color)
+ [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]
+
+ Methods can be added to enumerations, and members can have their own
+ attributes -- see the documentation for details.
"""
def __new__(cls, value):
@@ -999,6 +1160,9 @@ class Enum(metaclass=EnumType):
exc = None
ve_exc = None
+ def __init__(self, *args, **kwds):
+ pass
+
def _generate_next_value_(name, start, count, last_values):
"""
Generate the next value when not given.
@@ -1021,47 +1185,44 @@ class Enum(metaclass=EnumType):
return None
def __repr__(self):
- return "%s.%s" % ( self.__class__.__name__, self._name_)
+ v_repr = self.__class__._value_repr_ or self._value_.__class__.__repr__
+ return "<%s.%s: %s>" % (self.__class__.__name__, self._name_, v_repr(self._value_))
def __str__(self):
- return "%s" % (self._name_, )
+ return "%s.%s" % (self.__class__.__name__, self._name_, )
def __dir__(self):
"""
Returns all members and all public methods
"""
- cls = type(self)
- to_exclude = {'__members__', '__init__', '__new__', *cls._member_names_}
- filtered_self_dict = (name for name in self.__dict__ if not name.startswith('_'))
- return sorted({'name', 'value', *dir(cls), *filtered_self_dict} - to_exclude)
+ if self.__class__._member_type_ is object:
+ interesting = set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value'])
+ else:
+ interesting = set(object.__dir__(self))
+ for name in getattr(self, '__dict__', []):
+ if name[0] != '_':
+ interesting.add(name)
+ for cls in self.__class__.mro():
+ for name, obj in cls.__dict__.items():
+ if name[0] == '_':
+ continue
+ if isinstance(obj, property):
+ # that's an enum.property
+ if obj.fget is not None or name not in self._member_map_:
+ interesting.add(name)
+ else:
+ # in case it was added by `dir(self)`
+ interesting.discard(name)
+ else:
+ interesting.add(name)
+ names = sorted(
+ set(['__class__', '__doc__', '__eq__', '__hash__', '__module__'])
+ | interesting
+ )
+ return names
def __format__(self, format_spec):
- """
- Returns format using actual value type unless __str__ has been overridden.
- """
- # mixed-in Enums should use the mixed-in type's __format__, otherwise
- # we can get strange results with the Enum name showing up instead of
- # the value
- #
- # pure Enum branch, or branch with __str__ explicitly overridden
- str_overridden = type(self).__str__ not in (Enum.__str__, IntEnum.__str__, Flag.__str__)
- if self._member_type_ is object or str_overridden:
- cls = str
- val = str(self)
- # mix-in branch
- else:
- if not format_spec or format_spec in ('{}','{:}'):
- import warnings
- warnings.warn(
- "in 3.12 format() will use the enum member, not the enum member's value;\n"
- "use a format specifier, such as :d for an integer-based Enum, to maintain "
- "the current display",
- DeprecationWarning,
- stacklevel=2,
- )
- cls = self._member_type_
- val = self._value_
- return cls.__format__(val, format_spec)
+ return str.__format__(str(self), format_spec)
def __hash__(self):
return hash(self._name_)
@@ -1088,34 +1249,25 @@ class Enum(metaclass=EnumType):
return self._value_
-class IntEnum(int, Enum):
+class ReprEnum(Enum):
"""
- Enum where members are also (and must be) ints
+ Only changes the repr(), leaving str() and format() to the mixed-in type.
"""
- def __str__(self):
- return "%s" % (self._name_, )
- def __format__(self, format_spec):
- """
- Returns format using actual value unless __str__ has been overridden.
- """
- str_overridden = type(self).__str__ != IntEnum.__str__
- if str_overridden:
- cls = str
- val = str(self)
- else:
- cls = self._member_type_
- val = self._value_
- return cls.__format__(val, format_spec)
+class IntEnum(int, ReprEnum):
+ """
+ Enum where members are also (and must be) ints
+ """
-class StrEnum(str, Enum):
+class StrEnum(str, ReprEnum):
"""
Enum where members are also (and must be) strings
"""
def __new__(cls, *values):
+ "values must already be of type `str`"
if len(values) > 3:
raise TypeError('too many arguments for str(): %r' % (values, ))
if len(values) == 1:
@@ -1135,10 +1287,6 @@ class StrEnum(str, Enum):
member._value_ = value
return member
- __str__ = str.__str__
-
- __format__ = str.__format__
-
def _generate_next_value_(name, start, count, last_values):
"""
Return the lower-cased version of the member name.
@@ -1169,6 +1317,8 @@ class Flag(Enum, boundary=STRICT):
Support for flags
"""
+ _numeric_repr_ = repr
+
def _generate_next_value_(name, start, count, last_values):
"""
Generate the next value when not given.
@@ -1184,7 +1334,7 @@ class Flag(Enum, boundary=STRICT):
try:
high_bit = _high_bit(last_value)
except Exception:
- raise TypeError('Invalid Flag value: %r' % last_value) from None
+ raise TypeError('invalid flag value %r' % last_value) from None
return 2 ** (high_bit+1)
@classmethod
@@ -1232,8 +1382,8 @@ class Flag(Enum, boundary=STRICT):
if cls._boundary_ is STRICT:
max_bits = max(value.bit_length(), flag_mask.bit_length())
raise ValueError(
- "%s: invalid value: %r\n given %s\n allowed %s" % (
- cls.__name__, value, bin(value, max_bits), bin(flag_mask, max_bits),
+ "%r invalid value %r\n given %s\n allowed %s" % (
+ cls, value, bin(value, max_bits), bin(flag_mask, max_bits),
))
elif cls._boundary_ is CONFORM:
value = value & flag_mask
@@ -1247,7 +1397,7 @@ class Flag(Enum, boundary=STRICT):
)
else:
raise ValueError(
- 'unknown flag boundary: %r' % (cls._boundary_, )
+ '%r unknown flag boundary %r' % (cls, cls._boundary_, )
)
if value < 0:
neg_value = value
@@ -1274,7 +1424,7 @@ class Flag(Enum, boundary=STRICT):
m._name_ for m in cls._iter_member_(member_value)
])
if unknown:
- pseudo_member._name_ += '|0x%x' % unknown
+ pseudo_member._name_ += '|%s' % cls._numeric_repr_(unknown)
else:
pseudo_member._name_ = None
# use setdefault in case another thread already created a composite
@@ -1292,10 +1442,8 @@ class Flag(Enum, boundary=STRICT):
"""
if not isinstance(other, self.__class__):
raise TypeError(
- "unsupported operand type(s) for 'in': '%s' and '%s'" % (
+ "unsupported operand type(s) for 'in': %r and %r" % (
type(other).__qualname__, self.__class__.__qualname__))
- if other._value_ == 0 or self._value_ == 0:
- return False
return other._value_ & self._value_ == other._value_
def __iter__(self):
@@ -1309,27 +1457,18 @@ class Flag(Enum, boundary=STRICT):
def __repr__(self):
cls_name = self.__class__.__name__
+ v_repr = self.__class__._value_repr_ or self._value_.__class__.__repr__
if self._name_ is None:
- return "0x%x" % (self._value_, )
- if _is_single_bit(self._value_):
- return '%s.%s' % (cls_name, self._name_)
- if self._boundary_ is not FlagBoundary.KEEP:
- return '%s.' % cls_name + ('|%s.' % cls_name).join(self.name.split('|'))
+ return "<%s: %s>" % (cls_name, v_repr(self._value_))
else:
- name = []
- for n in self._name_.split('|'):
- if n.startswith('0'):
- name.append(n)
- else:
- name.append('%s.%s' % (cls_name, n))
- return '|'.join(name)
+ return "<%s.%s: %s>" % (cls_name, self._name_, v_repr(self._value_))
def __str__(self):
- cls = self.__class__
+ cls_name = self.__class__.__name__
if self._name_ is None:
- return '%s(%x)' % (cls.__name__, self._value_)
+ return '%s(%r)' % (cls_name, self._value_)
else:
- return self._name_
+ return "%s.%s" % (cls_name, self._name_)
def __bool__(self):
return bool(self._value_)
@@ -1362,20 +1501,11 @@ class Flag(Enum, boundary=STRICT):
return self._inverted_
-class IntFlag(int, Flag, boundary=EJECT):
+class IntFlag(int, ReprEnum, Flag, boundary=EJECT):
"""
Support for integer-based Flags
"""
- def __format__(self, format_spec):
- """
- Returns format using actual value unless __str__ has been overridden.
- """
- str_overridden = type(self).__str__ != Flag.__str__
- value = self
- if not str_overridden:
- value = self._value_
- return int.__format__(value, format_spec)
def __or__(self, other):
if isinstance(other, self.__class__):
@@ -1412,6 +1542,7 @@ class IntFlag(int, Flag, boundary=EJECT):
__rxor__ = __xor__
__invert__ = Flag.__invert__
+
def _high_bit(value):
"""
returns index of highest bit, or -1 if value is zero or negative
@@ -1456,7 +1587,7 @@ def global_flag_repr(self):
module = self.__class__.__module__.split('.')[-1]
cls_name = self.__class__.__name__
if self._name_ is None:
- return "%s.%s(0x%x)" % (module, cls_name, self._value_)
+ return "%s.%s(%r)" % (module, cls_name, self._value_)
if _is_single_bit(self):
return '%s.%s' % (module, self._name_)
if self._boundary_ is not FlagBoundary.KEEP:
@@ -1464,14 +1595,22 @@ def global_flag_repr(self):
else:
name = []
for n in self._name_.split('|'):
- if n.startswith('0'):
+ if n[0].isdigit():
name.append(n)
else:
name.append('%s.%s' % (module, n))
return '|'.join(name)
+def global_str(self):
+ """
+ use enum_name instead of class.enum_name
+ """
+ if self._name_ is None:
+ return "%s(%r)" % (cls_name, self._value_)
+ else:
+ return self._name_
-def global_enum(cls):
+def global_enum(cls, update_str=False):
"""
decorator that makes the repr() of an enum member reference its module
instead of its class; also exports all members to the enum's module's
@@ -1481,6 +1620,8 @@ def global_enum(cls):
cls.__repr__ = global_flag_repr
else:
cls.__repr__ = global_enum_repr
+ if not issubclass(cls, ReprEnum) or update_str:
+ cls.__str__ = global_str
sys.modules[cls.__module__].__dict__.update(cls.__members__)
return cls
@@ -1522,6 +1663,7 @@ def _simple_enum(etype=Enum, *, boundary=None, use_args=None):
body['_value2member_map_'] = value2member_map = {}
body['_unhashable_values_'] = []
body['_member_type_'] = member_type = etype._member_type_
+ body['_value_repr_'] = etype._value_repr_
if issubclass(etype, Flag):
body['_boundary_'] = boundary or etype._boundary_
body['_flag_mask_'] = None
@@ -1543,13 +1685,8 @@ def _simple_enum(etype=Enum, *, boundary=None, use_args=None):
# it
enum_class = type(cls_name, (etype, ), body, boundary=boundary, _simple=True)
for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'):
- if name in body:
- continue
- class_method = getattr(enum_class, name)
- obj_method = getattr(member_type, name, None)
- enum_method = getattr(etype, name, None)
- if obj_method is not None and obj_method is class_method:
- setattr(enum_class, name, enum_method)
+ if name not in body:
+ setattr(enum_class, name, getattr(etype, name))
gnv_last_values = []
if issubclass(enum_class, Flag):
# Flag / IntFlag
@@ -1760,8 +1897,8 @@ def _test_simple_enum(checked_enum, simple_enum):
+ list(simple_enum._member_map_.keys())
)
for key in set(checked_keys + simple_keys):
- if key in ('__module__', '_member_map_', '_value2member_map_'):
- # keys known to be different
+ if key in ('__module__', '_member_map_', '_value2member_map_', '__doc__'):
+ # keys known to be different, or very long
continue
elif key in member_names:
# members are checked below
@@ -1882,3 +2019,5 @@ def _old_convert_(etype, name, module, filter, source=None, *, boundary=None):
cls.__reduce_ex__ = _reduce_ex_by_global_name
cls.__repr__ = global_enum_repr
return cls
+
+_stdlib_enums = IntEnum, StrEnum, IntFlag
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 5d33f0d..8236698 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -2567,15 +2567,21 @@ class _empty:
class _ParameterKind(enum.IntEnum):
- POSITIONAL_ONLY = 0
- POSITIONAL_OR_KEYWORD = 1
- VAR_POSITIONAL = 2
- KEYWORD_ONLY = 3
- VAR_KEYWORD = 4
+ POSITIONAL_ONLY = 'positional-only'
+ POSITIONAL_OR_KEYWORD = 'positional or keyword'
+ VAR_POSITIONAL = 'variadic positional'
+ KEYWORD_ONLY = 'keyword-only'
+ VAR_KEYWORD = 'variadic keyword'
+
+ def __new__(cls, description):
+ value = len(cls.__members__)
+ member = int.__new__(cls, value)
+ member._value_ = value
+ member.description = description
+ return member
- @property
- def description(self):
- return _PARAM_NAME_MAPPING[self]
+ def __str__(self):
+ return self.name
_POSITIONAL_ONLY = _ParameterKind.POSITIONAL_ONLY
_POSITIONAL_OR_KEYWORD = _ParameterKind.POSITIONAL_OR_KEYWORD
@@ -2583,14 +2589,6 @@ _VAR_POSITIONAL = _ParameterKind.VAR_POSITIONAL
_KEYWORD_ONLY = _ParameterKind.KEYWORD_ONLY
_VAR_KEYWORD = _ParameterKind.VAR_KEYWORD
-_PARAM_NAME_MAPPING = {
- _POSITIONAL_ONLY: 'positional-only',
- _POSITIONAL_OR_KEYWORD: 'positional or keyword',
- _VAR_POSITIONAL: 'variadic positional',
- _KEYWORD_ONLY: 'keyword-only',
- _VAR_KEYWORD: 'variadic keyword'
-}
-
class Parameter:
"""Represents a parameter in a function signature.
diff --git a/Lib/plistlib.py b/Lib/plistlib.py
index 3ab71ed..4862355 100644
--- a/Lib/plistlib.py
+++ b/Lib/plistlib.py
@@ -61,7 +61,8 @@ import struct
from xml.parsers.expat import ParserCreate
-PlistFormat = enum.global_enum(enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__))
+PlistFormat = enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__)
+globals().update(PlistFormat.__members__)
class UID:
diff --git a/Lib/re.py b/Lib/re.py
index ea41217..a7ab9b3 100644
--- a/Lib/re.py
+++ b/Lib/re.py
@@ -155,6 +155,8 @@ class RegexFlag:
# sre extensions (experimental, don't rely on these)
TEMPLATE = T = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking
DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation
+ __str__ = object.__str__
+ _numeric_repr_ = hex
# sre exception
error = sre_compile.error
diff --git a/Lib/ssl.py b/Lib/ssl.py
index 2079251..dafb70a 100644
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -119,7 +119,6 @@ from _ssl import (
)
from _ssl import _DEFAULT_CIPHERS, _OPENSSL_API_VERSION
-
_IntEnum._convert_(
'_SSLMethod', __name__,
lambda name: name.startswith('PROTOCOL_') and name != 'PROTOCOL_SSLv23',
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py
index 43f98c1..a0953fb 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -6,15 +6,18 @@ import pydoc
import sys
import unittest
import threading
+import builtins as bltns
from collections import OrderedDict
+from datetime import date
from enum import Enum, IntEnum, StrEnum, EnumType, Flag, IntFlag, unique, auto
from enum import STRICT, CONFORM, EJECT, KEEP, _simple_enum, _test_simple_enum
-from enum import verify, UNIQUE, CONTINUOUS, NAMED_FLAGS
+from enum import verify, UNIQUE, CONTINUOUS, NAMED_FLAGS, ReprEnum
from io import StringIO
from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL
from test import support
from test.support import ALWAYS_EQ
from test.support import threading_helper
+from textwrap import dedent
from datetime import timedelta
python_version = sys.version_info[:2]
@@ -107,6 +110,12 @@ def test_pickle_exception(assertion, exception, obj):
class TestHelpers(unittest.TestCase):
# _is_descriptor, _is_sunder, _is_dunder
+ sunder_names = '_bad_', '_good_', '_what_ho_'
+ dunder_names = '__mal__', '__bien__', '__que_que__'
+ private_names = '_MyEnum__private', '_MyEnum__still_private'
+ private_and_sunder_names = '_MyEnum__private_', '_MyEnum__also_private_'
+ random_names = 'okay', '_semi_private', '_weird__', '_MyEnum__'
+
def test_is_descriptor(self):
class foo:
pass
@@ -116,21 +125,36 @@ class TestHelpers(unittest.TestCase):
setattr(obj, attr, 1)
self.assertTrue(enum._is_descriptor(obj))
- def test_is_sunder(self):
+ def test_sunder(self):
+ for name in self.sunder_names + self.private_and_sunder_names:
+ self.assertTrue(enum._is_sunder(name), '%r is a not sunder name?' % name)
+ for name in self.dunder_names + self.private_names + self.random_names:
+ self.assertFalse(enum._is_sunder(name), '%r is a sunder name?' % name)
for s in ('_a_', '_aa_'):
self.assertTrue(enum._is_sunder(s))
-
for s in ('a', 'a_', '_a', '__a', 'a__', '__a__', '_a__', '__a_', '_',
'__', '___', '____', '_____',):
self.assertFalse(enum._is_sunder(s))
- def test_is_dunder(self):
+ def test_dunder(self):
+ for name in self.dunder_names:
+ self.assertTrue(enum._is_dunder(name), '%r is a not dunder name?' % name)
+ for name in self.sunder_names + self.private_names + self.private_and_sunder_names + self.random_names:
+ self.assertFalse(enum._is_dunder(name), '%r is a dunder name?' % name)
for s in ('__a__', '__aa__'):
self.assertTrue(enum._is_dunder(s))
for s in ('a', 'a_', '_a', '__a', 'a__', '_a_', '_a__', '__a_', '_',
'__', '___', '____', '_____',):
self.assertFalse(enum._is_dunder(s))
+
+ def test_is_private(self):
+ for name in self.private_names + self.private_and_sunder_names:
+ self.assertTrue(enum._is_private('MyEnum', name), '%r is a not private name?')
+ for name in self.sunder_names + self.dunder_names + self.random_names:
+ self.assertFalse(enum._is_private('MyEnum', name), '%r is a private name?')
+
+
# for subclassing tests
class classproperty:
@@ -166,473 +190,658 @@ class HeadlightsC(IntFlag, boundary=enum.CONFORM):
# tests
-class TestEnum(unittest.TestCase):
+class _EnumTests:
+ """
+ Test for behavior that is the same across the different types of enumerations.
+ """
+
+ values = None
def setUp(self):
- class Season(Enum):
- SPRING = 1
- SUMMER = 2
- AUTUMN = 3
- WINTER = 4
- self.Season = Season
+ class BaseEnum(self.enum_type):
+ @enum.property
+ def first(self):
+ return '%s is first!' % self.name
+ class MainEnum(BaseEnum):
+ first = auto()
+ second = auto()
+ third = auto()
+ if issubclass(self.enum_type, Flag):
+ dupe = 3
+ else:
+ dupe = third
+ self.MainEnum = MainEnum
+ #
+ class NewStrEnum(self.enum_type):
+ def __str__(self):
+ return self.name.upper()
+ first = auto()
+ self.NewStrEnum = NewStrEnum
+ #
+ class NewFormatEnum(self.enum_type):
+ def __format__(self, spec):
+ return self.name.upper()
+ first = auto()
+ self.NewFormatEnum = NewFormatEnum
+ #
+ class NewStrFormatEnum(self.enum_type):
+ def __str__(self):
+ return self.name.title()
+ def __format__(self, spec):
+ return ''.join(reversed(self.name))
+ first = auto()
+ self.NewStrFormatEnum = NewStrFormatEnum
+ #
+ class NewBaseEnum(self.enum_type):
+ def __str__(self):
+ return self.name.title()
+ def __format__(self, spec):
+ return ''.join(reversed(self.name))
+ class NewSubEnum(NewBaseEnum):
+ first = auto()
+ self.NewSubEnum = NewSubEnum
+ #
+ self.is_flag = False
+ self.names = ['first', 'second', 'third']
+ if issubclass(MainEnum, StrEnum):
+ self.values = self.names
+ elif MainEnum._member_type_ is str:
+ self.values = ['1', '2', '3']
+ elif issubclass(self.enum_type, Flag):
+ self.values = [1, 2, 4]
+ self.is_flag = True
+ self.dupe2 = MainEnum(5)
+ else:
+ self.values = self.values or [1, 2, 3]
+ #
+ if not getattr(self, 'source_values', False):
+ self.source_values = self.values
- class Konstants(float, Enum):
- E = 2.7182818
- PI = 3.1415926
- TAU = 2 * PI
- self.Konstants = Konstants
+ def assertFormatIsValue(self, spec, member):
+ self.assertEqual(spec.format(member), spec.format(member.value))
- class Grades(IntEnum):
- A = 5
- B = 4
- C = 3
- D = 2
- F = 0
- self.Grades = Grades
+ def assertFormatIsStr(self, spec, member):
+ self.assertEqual(spec.format(member), spec.format(str(member)))
- class Directional(str, Enum):
- EAST = 'east'
- WEST = 'west'
- NORTH = 'north'
- SOUTH = 'south'
- self.Directional = Directional
+ def test_attribute_deletion(self):
+ class Season(self.enum_type):
+ SPRING = auto()
+ SUMMER = auto()
+ AUTUMN = auto()
+ #
+ def spam(cls):
+ pass
+ #
+ self.assertTrue(hasattr(Season, 'spam'))
+ del Season.spam
+ self.assertFalse(hasattr(Season, 'spam'))
+ #
+ with self.assertRaises(AttributeError):
+ del Season.SPRING
+ with self.assertRaises(AttributeError):
+ del Season.DRY
+ with self.assertRaises(AttributeError):
+ del Season.SPRING.name
- from datetime import date
- class Holiday(date, Enum):
- NEW_YEAR = 2013, 1, 1
- IDES_OF_MARCH = 2013, 3, 15
- self.Holiday = Holiday
+ def test_basics(self):
+ TE = self.MainEnum
+ if self.is_flag:
+ self.assertEqual(repr(TE), "<flag 'MainEnum'>")
+ self.assertEqual(str(TE), "<flag 'MainEnum'>")
+ self.assertEqual(format(TE), "<flag 'MainEnum'>")
+ self.assertTrue(TE(5) is self.dupe2)
+ else:
+ self.assertEqual(repr(TE), "<enum 'MainEnum'>")
+ self.assertEqual(str(TE), "<enum 'MainEnum'>")
+ self.assertEqual(format(TE), "<enum 'MainEnum'>")
+ self.assertEqual(list(TE), [TE.first, TE.second, TE.third])
+ self.assertEqual(
+ [m.name for m in TE],
+ self.names,
+ )
+ self.assertEqual(
+ [m.value for m in TE],
+ self.values,
+ )
+ self.assertEqual(
+ [m.first for m in TE],
+ ['first is first!', 'second is first!', 'third is first!']
+ )
+ for member, name in zip(TE, self.names, strict=True):
+ self.assertIs(TE[name], member)
+ for member, value in zip(TE, self.values, strict=True):
+ self.assertIs(TE(value), member)
+ if issubclass(TE, StrEnum):
+ self.assertTrue(TE.dupe is TE('third') is TE['dupe'])
+ elif TE._member_type_ is str:
+ self.assertTrue(TE.dupe is TE('3') is TE['dupe'])
+ elif issubclass(TE, Flag):
+ self.assertTrue(TE.dupe is TE(3) is TE['dupe'])
+ else:
+ self.assertTrue(TE.dupe is TE(self.values[2]) is TE['dupe'])
- class DateEnum(date, Enum): pass
- self.DateEnum = DateEnum
+ def test_bool_is_true(self):
+ class Empty(self.enum_type):
+ pass
+ self.assertTrue(Empty)
+ #
+ self.assertTrue(self.MainEnum)
+ for member in self.MainEnum:
+ self.assertTrue(member)
- class FloatEnum(float, Enum): pass
- self.FloatEnum = FloatEnum
+ def test_changing_member_fails(self):
+ MainEnum = self.MainEnum
+ with self.assertRaises(AttributeError):
+ self.MainEnum.second = 'really first'
- class Wowser(Enum):
- this = 'that'
- these = 'those'
- def wowser(self):
- """Wowser docstring"""
- return ("Wowser! I'm %s!" % self.name)
- @classmethod
- def classmethod_wowser(cls): pass
- @staticmethod
- def staticmethod_wowser(): pass
- self.Wowser = Wowser
-
- class IntWowser(IntEnum):
- this = 1
- these = 2
- def wowser(self):
- """Wowser docstring"""
- return ("Wowser! I'm %s!" % self.name)
- @classmethod
- def classmethod_wowser(cls): pass
- @staticmethod
- def staticmethod_wowser(): pass
- self.IntWowser = IntWowser
-
- class FloatWowser(float, Enum):
- this = 3.14
- these = 4.2
- def wowser(self):
- """Wowser docstring"""
- return ("Wowser! I'm %s!" % self.name)
- @classmethod
- def classmethod_wowser(cls): pass
- @staticmethod
- def staticmethod_wowser(): pass
- self.FloatWowser = FloatWowser
+ @unittest.skipIf(
+ python_version >= (3, 12),
+ '__contains__ now returns True/False for all inputs',
+ )
+ def test_contains_er(self):
+ MainEnum = self.MainEnum
+ self.assertIn(MainEnum.third, MainEnum)
+ with self.assertRaises(TypeError):
+ with self.assertWarns(DeprecationWarning):
+ self.source_values[1] in MainEnum
+ with self.assertRaises(TypeError):
+ with self.assertWarns(DeprecationWarning):
+ 'first' in MainEnum
+ val = MainEnum.dupe
+ self.assertIn(val, MainEnum)
+ #
+ class OtherEnum(Enum):
+ one = auto()
+ two = auto()
+ self.assertNotIn(OtherEnum.two, MainEnum)
- class WowserNoMembers(Enum):
- def wowser(self): pass
- @classmethod
- def classmethod_wowser(cls): pass
- @staticmethod
- def staticmethod_wowser(): pass
- class SubclassOfWowserNoMembers(WowserNoMembers): pass
- self.WowserNoMembers = WowserNoMembers
- self.SubclassOfWowserNoMembers = SubclassOfWowserNoMembers
-
- class IntWowserNoMembers(IntEnum):
- def wowser(self): pass
- @classmethod
- def classmethod_wowser(cls): pass
- @staticmethod
- def staticmethod_wowser(): pass
- self.IntWowserNoMembers = IntWowserNoMembers
+ @unittest.skipIf(
+ python_version < (3, 12),
+ '__contains__ works only with enum memmbers before 3.12',
+ )
+ def test_contains_tf(self):
+ MainEnum = self.MainEnum
+ self.assertIn(MainEnum.first, MainEnum)
+ self.assertTrue(self.source_values[0] in MainEnum)
+ self.assertFalse('first' in MainEnum)
+ val = MainEnum.dupe
+ self.assertIn(val, MainEnum)
+ #
+ class OtherEnum(Enum):
+ one = auto()
+ two = auto()
+ self.assertNotIn(OtherEnum.two, MainEnum)
- class FloatWowserNoMembers(float, Enum):
- def wowser(self): pass
- @classmethod
- def classmethod_wowser(cls): pass
- @staticmethod
- def staticmethod_wowser(): pass
- self.FloatWowserNoMembers = FloatWowserNoMembers
-
- class EnumWithInit(Enum):
- def __init__(self, greeting, farewell):
- self.greeting = greeting
- self.farewell = farewell
- ENGLISH = 'hello', 'goodbye'
- GERMAN = 'Guten Morgen', 'Auf Wiedersehen'
- def some_method(self): pass
- self.EnumWithInit = EnumWithInit
+ def test_dir_on_class(self):
+ TE = self.MainEnum
+ self.assertEqual(set(dir(TE)), set(enum_dir(TE)))
+ def test_dir_on_item(self):
+ TE = self.MainEnum
+ self.assertEqual(set(dir(TE.first)), set(member_dir(TE.first)))
+
+ def test_dir_with_added_behavior(self):
+ class Test(self.enum_type):
+ this = auto()
+ these = auto()
+ def wowser(self):
+ return ("Wowser! I'm %s!" % self.name)
+ self.assertTrue('wowser' not in dir(Test))
+ self.assertTrue('wowser' in dir(Test.this))
+
+ def test_dir_on_sub_with_behavior_on_super(self):
# see issue22506
- class SuperEnum1(Enum):
+ class SuperEnum(self.enum_type):
def invisible(self):
return "did you see me?"
- class SubEnum1(SuperEnum1):
- sample = 5
- self.SubEnum1 = SubEnum1
+ class SubEnum(SuperEnum):
+ sample = auto()
+ self.assertTrue('invisible' not in dir(SubEnum))
+ self.assertTrue('invisible' in dir(SubEnum.sample))
- class SuperEnum2(IntEnum):
- def __new__(cls, value, description=""):
- obj = int.__new__(cls, value)
- obj._value_ = value
- obj.description = description
+ def test_dir_on_sub_with_behavior_including_instance_dict_on_super(self):
+ # see issue40084
+ class SuperEnum(self.enum_type):
+ def __new__(cls, *value, **kwds):
+ new = self.enum_type._member_type_.__new__
+ if self.enum_type._member_type_ is object:
+ obj = new(cls)
+ else:
+ if isinstance(value[0], tuple):
+ create_value ,= value[0]
+ else:
+ create_value = value
+ obj = new(cls, *create_value)
+ obj._value_ = value[0] if len(value) == 1 else value
+ obj.description = 'test description'
return obj
- class SubEnum2(SuperEnum2):
- sample = 5
- self.SubEnum2 = SubEnum2
-
- def test_dir_basics_for_all_enums(self):
- enums_for_tests = (
- # Generic enums in enum.py
- Enum,
- IntEnum,
- StrEnum,
- # Generic enums defined outside of enum.py
- self.DateEnum,
- self.FloatEnum,
- # Concrete enums derived from enum.py generics
- self.Grades,
- self.Season,
- # Concrete enums derived from generics defined outside of enum.py
- self.Konstants,
- self.Holiday,
- # Standard enum with added behaviour & members
- self.Wowser,
- # Mixin-enum-from-enum.py with added behaviour & members
- self.IntWowser,
- # Mixin-enum-from-oustide-enum.py with added behaviour & members
- self.FloatWowser,
- # Equivalents of the three immediately above, but with no members
- self.WowserNoMembers,
- self.IntWowserNoMembers,
- self.FloatWowserNoMembers,
- # Enum with members and an __init__ method
- self.EnumWithInit,
- # Special cases to test
- self.SubEnum1,
- self.SubEnum2
- )
-
- for cls in enums_for_tests:
- with self.subTest(cls=cls):
- cls_dir = dir(cls)
- # test that dir is deterministic
- self.assertEqual(cls_dir, dir(cls))
- # test that dir is sorted
- self.assertEqual(list(cls_dir), sorted(cls_dir))
- # test that there are no dupes in dir
- self.assertEqual(len(cls_dir), len(set(cls_dir)))
- # test that there are no sunders in dir
- self.assertFalse(any(enum._is_sunder(attr) for attr in cls_dir))
- self.assertNotIn('__new__', cls_dir)
-
- for attr in ('__class__', '__doc__', '__members__', '__module__'):
- with self.subTest(attr=attr):
- self.assertIn(attr, cls_dir)
-
- def test_dir_for_enum_with_members(self):
- enums_for_test = (
- # Enum with members
- self.Season,
- # IntEnum with members
- self.Grades,
- # Two custom-mixin enums with members
- self.Konstants,
- self.Holiday,
- # several enums-with-added-behaviour and members
- self.Wowser,
- self.IntWowser,
- self.FloatWowser,
- # An enum with an __init__ method and members
- self.EnumWithInit,
- # Special cases to test
- self.SubEnum1,
- self.SubEnum2
- )
-
- for cls in enums_for_test:
- cls_dir = dir(cls)
- member_names = cls._member_names_
- with self.subTest(cls=cls):
- self.assertTrue(all(member_name in cls_dir for member_name in member_names))
- for member in cls:
- member_dir = dir(member)
- # test that dir is deterministic
- self.assertEqual(member_dir, dir(member))
- # test that dir is sorted
- self.assertEqual(list(member_dir), sorted(member_dir))
- # test that there are no dupes in dir
- self.assertEqual(len(member_dir), len(set(member_dir)))
-
- for attr_name in cls_dir:
- with self.subTest(attr_name=attr_name):
- if attr_name in {'__members__', '__init__', '__new__', *member_names}:
- self.assertNotIn(attr_name, member_dir)
- else:
- self.assertIn(attr_name, member_dir)
-
- self.assertFalse(any(enum._is_sunder(attr) for attr in member_dir))
-
- def test_dir_for_enums_with_added_behaviour(self):
- enums_for_test = (
- self.Wowser,
- self.IntWowser,
- self.FloatWowser,
- self.WowserNoMembers,
- self.SubclassOfWowserNoMembers,
- self.IntWowserNoMembers,
- self.FloatWowserNoMembers
- )
-
- for cls in enums_for_test:
- with self.subTest(cls=cls):
- self.assertIn('wowser', dir(cls))
- self.assertIn('classmethod_wowser', dir(cls))
- self.assertIn('staticmethod_wowser', dir(cls))
- self.assertTrue(all(
- all(attr in dir(member) for attr in ('wowser', 'classmethod_wowser', 'staticmethod_wowser'))
- for member in cls
- ))
+ class SubEnum(SuperEnum):
+ sample = self.source_values[1]
+ self.assertTrue('description' not in dir(SubEnum))
+ self.assertTrue('description' in dir(SubEnum.sample), dir(SubEnum.sample))
- self.assertEqual(dir(self.WowserNoMembers), dir(self.SubclassOfWowserNoMembers))
- # Check classmethods are present
- self.assertIn('from_bytes', dir(self.IntWowser))
- self.assertIn('from_bytes', dir(self.IntWowserNoMembers))
-
- def test_help_output_on_enum_members(self):
- added_behaviour_enums = (
- self.Wowser,
- self.IntWowser,
- self.FloatWowser
- )
-
- for cls in added_behaviour_enums:
- with self.subTest(cls=cls):
- rendered_doc = pydoc.render_doc(cls.this)
- self.assertIn('Wowser docstring', rendered_doc)
- if cls in {self.IntWowser, self.FloatWowser}:
- self.assertIn('float(self)', rendered_doc)
-
- def test_dir_for_enum_with_init(self):
- EnumWithInit = self.EnumWithInit
-
- cls_dir = dir(EnumWithInit)
- self.assertIn('__init__', cls_dir)
- self.assertIn('some_method', cls_dir)
- self.assertNotIn('greeting', cls_dir)
- self.assertNotIn('farewell', cls_dir)
-
- member_dir = dir(EnumWithInit.ENGLISH)
- self.assertNotIn('__init__', member_dir)
- self.assertIn('some_method', member_dir)
- self.assertIn('greeting', member_dir)
- self.assertIn('farewell', member_dir)
-
- def test_mixin_dirs(self):
- from datetime import date
+ def test_enum_in_enum_out(self):
+ Main = self.MainEnum
+ self.assertIs(Main(Main.first), Main.first)
- enums_for_test = (
- # generic mixins from enum.py
- (IntEnum, int),
- (StrEnum, str),
- # generic mixins from outside enum.py
- (self.FloatEnum, float),
- (self.DateEnum, date),
- # concrete mixin from enum.py
- (self.Grades, int),
- # concrete mixin from outside enum.py
- (self.Holiday, date),
- # concrete mixin from enum.py with added behaviour
- (self.IntWowser, int),
- # concrete mixin from outside enum.py with added behaviour
- (self.FloatWowser, float)
- )
-
- enum_dict = Enum.__dict__
- enum_dir = dir(Enum)
- enum_module_names = enum.__all__
- is_from_enum_module = lambda cls: cls.__name__ in enum_module_names
- is_enum_dunder = lambda attr: enum._is_dunder(attr) and attr in enum_dict
-
- def attr_is_inherited_from_object(cls, attr_name):
- for base in cls.__mro__:
- if attr_name in base.__dict__:
- return base is object
- return False
-
- # General tests
- for enum_cls, mixin_cls in enums_for_test:
- with self.subTest(enum_cls=enum_cls):
- cls_dir = dir(enum_cls)
- cls_dict = enum_cls.__dict__
-
- mixin_attrs = [
- x for x in dir(mixin_cls)
- if not attr_is_inherited_from_object(cls=mixin_cls, attr_name=x)
- ]
+ def test_hash(self):
+ MainEnum = self.MainEnum
+ mapping = {}
+ mapping[MainEnum.first] = '1225'
+ mapping[MainEnum.second] = '0315'
+ mapping[MainEnum.third] = '0704'
+ self.assertEqual(mapping[MainEnum.second], '0315')
+
+ def test_invalid_names(self):
+ with self.assertRaises(ValueError):
+ class Wrong(self.enum_type):
+ mro = 9
+ with self.assertRaises(ValueError):
+ class Wrong(self.enum_type):
+ _create_= 11
+ with self.assertRaises(ValueError):
+ class Wrong(self.enum_type):
+ _get_mixins_ = 9
+ with self.assertRaises(ValueError):
+ class Wrong(self.enum_type):
+ _find_new_ = 1
+ with self.assertRaises(ValueError):
+ class Wrong(self.enum_type):
+ _any_name_ = 9
+
+ def test_object_str_override(self):
+ "check that setting __str__ to object's is not reset to Enum's"
+ class Generic(self.enum_type):
+ item = self.source_values[2]
+ def __repr__(self):
+ return "%s.test" % (self._name_, )
+ __str__ = object.__str__
+ self.assertEqual(str(Generic.item), 'item.test')
+
+ def test_overridden_str(self):
+ NS = self.NewStrEnum
+ self.assertEqual(str(NS.first), NS.first.name.upper())
+ self.assertEqual(format(NS.first), NS.first.name.upper())
+
+ def test_overridden_str_format(self):
+ NSF = self.NewStrFormatEnum
+ self.assertEqual(str(NSF.first), NSF.first.name.title())
+ self.assertEqual(format(NSF.first), ''.join(reversed(NSF.first.name)))
- first_enum_base = next(
- base for base in enum_cls.__mro__
- if is_from_enum_module(base)
+ def test_overridden_str_format_inherited(self):
+ NSE = self.NewSubEnum
+ self.assertEqual(str(NSE.first), NSE.first.name.title())
+ self.assertEqual(format(NSE.first), ''.join(reversed(NSE.first.name)))
+
+ def test_programmatic_function_string(self):
+ MinorEnum = self.enum_type('MinorEnum', 'june july august')
+ lst = list(MinorEnum)
+ self.assertEqual(len(lst), len(MinorEnum))
+ self.assertEqual(len(MinorEnum), 3, MinorEnum)
+ self.assertEqual(
+ [MinorEnum.june, MinorEnum.july, MinorEnum.august],
+ lst,
)
+ values = self.values
+ if self.enum_type is StrEnum:
+ values = ['june','july','august']
+ for month, av in zip('june july august'.split(), values):
+ e = MinorEnum[month]
+ self.assertEqual(e.value, av, list(MinorEnum))
+ self.assertEqual(e.name, month)
+ if MinorEnum._member_type_ is not object and issubclass(MinorEnum, MinorEnum._member_type_):
+ self.assertEqual(e, av)
+ else:
+ self.assertNotEqual(e, av)
+ self.assertIn(e, MinorEnum)
+ self.assertIs(type(e), MinorEnum)
+ self.assertIs(e, MinorEnum(av))
- for attr in mixin_attrs:
- with self.subTest(attr=attr):
- if enum._is_sunder(attr):
- # Unlikely, but no harm in testing
- self.assertNotIn(attr, cls_dir)
- elif attr in {'__class__', '__doc__', '__members__', '__module__'}:
- self.assertIn(attr, cls_dir)
- elif is_enum_dunder(attr):
- if is_from_enum_module(enum_cls):
- self.assertNotIn(attr, cls_dir)
- elif getattr(enum_cls, attr) is getattr(first_enum_base, attr):
- self.assertNotIn(attr, cls_dir)
- else:
- self.assertIn(attr, cls_dir)
- else:
- self.assertIn(attr, cls_dir)
-
- # Some specific examples
- int_enum_dir = dir(IntEnum)
- self.assertIn('imag', int_enum_dir)
- self.assertIn('__rfloordiv__', int_enum_dir)
- self.assertNotIn('__format__', int_enum_dir)
- self.assertNotIn('__hash__', int_enum_dir)
- self.assertNotIn('__init_subclass__', int_enum_dir)
- self.assertNotIn('__subclasshook__', int_enum_dir)
-
- class OverridesFormatOutsideEnumModule(Enum):
- def __format__(self, *args, **kwargs):
- return super().__format__(*args, **kwargs)
- SOME_MEMBER = 1
-
- self.assertIn('__format__', dir(OverridesFormatOutsideEnumModule))
- self.assertIn('__format__', dir(OverridesFormatOutsideEnumModule.SOME_MEMBER))
+ def test_programmatic_function_string_list(self):
+ MinorEnum = self.enum_type('MinorEnum', ['june', 'july', 'august'])
+ lst = list(MinorEnum)
+ self.assertEqual(len(lst), len(MinorEnum))
+ self.assertEqual(len(MinorEnum), 3, MinorEnum)
+ self.assertEqual(
+ [MinorEnum.june, MinorEnum.july, MinorEnum.august],
+ lst,
+ )
+ values = self.values
+ if self.enum_type is StrEnum:
+ values = ['june','july','august']
+ for month, av in zip('june july august'.split(), values):
+ e = MinorEnum[month]
+ self.assertEqual(e.value, av)
+ self.assertEqual(e.name, month)
+ if MinorEnum._member_type_ is not object and issubclass(MinorEnum, MinorEnum._member_type_):
+ self.assertEqual(e, av)
+ else:
+ self.assertNotEqual(e, av)
+ self.assertIn(e, MinorEnum)
+ self.assertIs(type(e), MinorEnum)
+ self.assertIs(e, MinorEnum(av))
- def test_dir_on_sub_with_behavior_on_super(self):
- # see issue22506
+ def test_programmatic_function_iterable(self):
+ MinorEnum = self.enum_type(
+ 'MinorEnum',
+ (('june', self.source_values[0]), ('july', self.source_values[1]), ('august', self.source_values[2]))
+ )
+ lst = list(MinorEnum)
+ self.assertEqual(len(lst), len(MinorEnum))
+ self.assertEqual(len(MinorEnum), 3, MinorEnum)
self.assertEqual(
- set(dir(self.SubEnum1.sample)),
- set(['__class__', '__doc__', '__module__', 'name', 'value', 'invisible']),
+ [MinorEnum.june, MinorEnum.july, MinorEnum.august],
+ lst,
)
+ for month, av in zip('june july august'.split(), self.values):
+ e = MinorEnum[month]
+ self.assertEqual(e.value, av)
+ self.assertEqual(e.name, month)
+ if MinorEnum._member_type_ is not object and issubclass(MinorEnum, MinorEnum._member_type_):
+ self.assertEqual(e, av)
+ else:
+ self.assertNotEqual(e, av)
+ self.assertIn(e, MinorEnum)
+ self.assertIs(type(e), MinorEnum)
+ self.assertIs(e, MinorEnum(av))
- def test_dir_on_sub_with_behavior_including_instance_dict_on_super(self):
- # see issue40084
- self.assertTrue({'description'} <= set(dir(self.SubEnum2.sample)))
+ def test_programmatic_function_from_dict(self):
+ MinorEnum = self.enum_type(
+ 'MinorEnum',
+ OrderedDict((('june', self.source_values[0]), ('july', self.source_values[1]), ('august', self.source_values[2])))
+ )
+ lst = list(MinorEnum)
+ self.assertEqual(len(lst), len(MinorEnum))
+ self.assertEqual(len(MinorEnum), 3, MinorEnum)
+ self.assertEqual(
+ [MinorEnum.june, MinorEnum.july, MinorEnum.august],
+ lst,
+ )
+ for month, av in zip('june july august'.split(), self.values):
+ e = MinorEnum[month]
+ if MinorEnum._member_type_ is not object and issubclass(MinorEnum, MinorEnum._member_type_):
+ self.assertEqual(e, av)
+ else:
+ self.assertNotEqual(e, av)
+ self.assertIn(e, MinorEnum)
+ self.assertIs(type(e), MinorEnum)
+ self.assertIs(e, MinorEnum(av))
- def test_enum_in_enum_out(self):
- Season = self.Season
- self.assertIs(Season(Season.WINTER), Season.WINTER)
+ def test_repr(self):
+ TE = self.MainEnum
+ if self.is_flag:
+ self.assertEqual(repr(TE(0)), "<MainEnum: 0>")
+ self.assertEqual(repr(TE.dupe), "<MainEnum.dupe: 3>")
+ self.assertEqual(repr(self.dupe2), "<MainEnum.first|third: 5>")
+ elif issubclass(TE, StrEnum):
+ self.assertEqual(repr(TE.dupe), "<MainEnum.third: 'third'>")
+ else:
+ self.assertEqual(repr(TE.dupe), "<MainEnum.third: %r>" % (self.values[2], ), TE._value_repr_)
+ for name, value, member in zip(self.names, self.values, TE, strict=True):
+ self.assertEqual(repr(member), "<MainEnum.%s: %r>" % (member.name, member.value))
- def test_enum_value(self):
- Season = self.Season
- self.assertEqual(Season.SPRING.value, 1)
+ def test_repr_override(self):
+ class Generic(self.enum_type):
+ first = auto()
+ second = auto()
+ third = auto()
+ def __repr__(self):
+ return "don't you just love shades of %s?" % self.name
+ self.assertEqual(
+ repr(Generic.third),
+ "don't you just love shades of third?",
+ )
- def test_intenum_value(self):
- self.assertEqual(IntStooges.CURLY.value, 2)
+ def test_inherited_repr(self):
+ class MyEnum(self.enum_type):
+ def __repr__(self):
+ return "My name is %s." % self.name
+ class MySubEnum(MyEnum):
+ this = auto()
+ that = auto()
+ theother = auto()
+ self.assertEqual(repr(MySubEnum.that), "My name is that.")
- def test_enum(self):
- Season = self.Season
- lst = list(Season)
- self.assertEqual(len(lst), len(Season))
- self.assertEqual(len(Season), 4, Season)
+ def test_reversed_iteration_order(self):
self.assertEqual(
- [Season.SPRING, Season.SUMMER, Season.AUTUMN, Season.WINTER], lst)
+ list(reversed(self.MainEnum)),
+ [self.MainEnum.third, self.MainEnum.second, self.MainEnum.first],
+ )
- for i, season in enumerate('SPRING SUMMER AUTUMN WINTER'.split(), 1):
- e = Season(i)
- self.assertEqual(e, getattr(Season, season))
- self.assertEqual(e.value, i)
- self.assertNotEqual(e, i)
- self.assertEqual(e.name, season)
- self.assertIn(e, Season)
- self.assertIs(type(e), Season)
- self.assertIsInstance(e, Season)
- self.assertEqual(str(e), season)
- self.assertEqual(repr(e), 'Season.{0}'.format(season))
-
- def test_value_name(self):
- Season = self.Season
- self.assertEqual(Season.SPRING.name, 'SPRING')
- self.assertEqual(Season.SPRING.value, 1)
- with self.assertRaises(AttributeError):
- Season.SPRING.name = 'invierno'
- with self.assertRaises(AttributeError):
- Season.SPRING.value = 2
+class _PlainOutputTests:
- def test_changing_member(self):
- Season = self.Season
- with self.assertRaises(AttributeError):
- Season.WINTER = 'really cold'
+ def test_str(self):
+ TE = self.MainEnum
+ if self.is_flag:
+ self.assertEqual(str(TE.dupe), "MainEnum.dupe")
+ self.assertEqual(str(self.dupe2), "MainEnum.first|third")
+ else:
+ self.assertEqual(str(TE.dupe), "MainEnum.third")
+ for name, value, member in zip(self.names, self.values, TE, strict=True):
+ self.assertEqual(str(member), "MainEnum.%s" % (member.name, ))
- def test_attribute_deletion(self):
- class Season(Enum):
- SPRING = 1
- SUMMER = 2
- AUTUMN = 3
- WINTER = 4
+ def test_format(self):
+ TE = self.MainEnum
+ if self.is_flag:
+ self.assertEqual(format(TE.dupe), "MainEnum.dupe")
+ self.assertEqual(format(self.dupe2), "MainEnum.first|third")
+ else:
+ self.assertEqual(format(TE.dupe), "MainEnum.third")
+ for name, value, member in zip(self.names, self.values, TE, strict=True):
+ self.assertEqual(format(member), "MainEnum.%s" % (member.name, ))
- def spam(cls):
- pass
+ def test_overridden_format(self):
+ NF = self.NewFormatEnum
+ self.assertEqual(str(NF.first), "NewFormatEnum.first", '%s %r' % (NF.__str__, NF.first))
+ self.assertEqual(format(NF.first), "FIRST")
- self.assertTrue(hasattr(Season, 'spam'))
- del Season.spam
- self.assertFalse(hasattr(Season, 'spam'))
+ def test_format_specs(self):
+ TE = self.MainEnum
+ self.assertFormatIsStr('{}', TE.second)
+ self.assertFormatIsStr('{:}', TE.second)
+ self.assertFormatIsStr('{:20}', TE.second)
+ self.assertFormatIsStr('{:^20}', TE.second)
+ self.assertFormatIsStr('{:>20}', TE.second)
+ self.assertFormatIsStr('{:<20}', TE.second)
+ self.assertFormatIsStr('{:5.2}', TE.second)
- with self.assertRaises(AttributeError):
- del Season.SPRING
- with self.assertRaises(AttributeError):
- del Season.DRY
- with self.assertRaises(AttributeError):
- del Season.SPRING.name
- def test_bool_of_class(self):
- class Empty(Enum):
- pass
- self.assertTrue(bool(Empty))
+class _MixedOutputTests:
- def test_bool_of_member(self):
- class Count(Enum):
- zero = 0
- one = 1
- two = 2
- for member in Count:
- self.assertTrue(bool(member))
+ def test_str(self):
+ TE = self.MainEnum
+ if self.is_flag:
+ self.assertEqual(str(TE.dupe), "MainEnum.dupe")
+ self.assertEqual(str(self.dupe2), "MainEnum.first|third")
+ else:
+ self.assertEqual(str(TE.dupe), "MainEnum.third")
+ for name, value, member in zip(self.names, self.values, TE, strict=True):
+ self.assertEqual(str(member), "MainEnum.%s" % (member.name, ))
+
+ def test_format(self):
+ TE = self.MainEnum
+ if self.is_flag:
+ self.assertEqual(format(TE.dupe), "MainEnum.dupe")
+ self.assertEqual(format(self.dupe2), "MainEnum.first|third")
+ else:
+ self.assertEqual(format(TE.dupe), "MainEnum.third")
+ for name, value, member in zip(self.names, self.values, TE, strict=True):
+ self.assertEqual(format(member), "MainEnum.%s" % (member.name, ))
+
+ def test_overridden_format(self):
+ NF = self.NewFormatEnum
+ self.assertEqual(str(NF.first), "NewFormatEnum.first")
+ self.assertEqual(format(NF.first), "FIRST")
+
+ def test_format_specs(self):
+ TE = self.MainEnum
+ self.assertFormatIsStr('{}', TE.first)
+ self.assertFormatIsStr('{:}', TE.first)
+ self.assertFormatIsStr('{:20}', TE.first)
+ self.assertFormatIsStr('{:^20}', TE.first)
+ self.assertFormatIsStr('{:>20}', TE.first)
+ self.assertFormatIsStr('{:<20}', TE.first)
+ self.assertFormatIsStr('{:5.2}', TE.first)
+
+
+class _MinimalOutputTests:
+
+ def test_str(self):
+ TE = self.MainEnum
+ if self.is_flag:
+ self.assertEqual(str(TE.dupe), "3")
+ self.assertEqual(str(self.dupe2), "5")
+ else:
+ self.assertEqual(str(TE.dupe), str(self.values[2]))
+ for name, value, member in zip(self.names, self.values, TE, strict=True):
+ self.assertEqual(str(member), str(value))
+
+ def test_format(self):
+ TE = self.MainEnum
+ if self.is_flag:
+ self.assertEqual(format(TE.dupe), "3")
+ self.assertEqual(format(self.dupe2), "5")
+ else:
+ self.assertEqual(format(TE.dupe), format(self.values[2]))
+ for name, value, member in zip(self.names, self.values, TE, strict=True):
+ self.assertEqual(format(member), format(value))
+
+ def test_overridden_format(self):
+ NF = self.NewFormatEnum
+ self.assertEqual(str(NF.first), str(self.values[0]))
+ self.assertEqual(format(NF.first), "FIRST")
+
+ def test_format_specs(self):
+ TE = self.MainEnum
+ self.assertFormatIsValue('{}', TE.third)
+ self.assertFormatIsValue('{:}', TE.third)
+ self.assertFormatIsValue('{:20}', TE.third)
+ self.assertFormatIsValue('{:^20}', TE.third)
+ self.assertFormatIsValue('{:>20}', TE.third)
+ self.assertFormatIsValue('{:<20}', TE.third)
+ if TE._member_type_ is float:
+ self.assertFormatIsValue('{:n}', TE.third)
+ self.assertFormatIsValue('{:5.2}', TE.third)
+ self.assertFormatIsValue('{:f}', TE.third)
+
+
+class _FlagTests:
+
+ def test_default_missing_with_wrong_type_value(self):
+ with self.assertRaisesRegex(
+ ValueError,
+ "'RED' is not a valid TestFlag.Color",
+ ) as ctx:
+ self.MainEnum('RED')
+ self.assertIs(ctx.exception.__context__, None)
+
+class TestPlainEnum(_EnumTests, _PlainOutputTests, unittest.TestCase):
+ enum_type = Enum
+
+
+class TestPlainFlag(_EnumTests, _PlainOutputTests, unittest.TestCase):
+ enum_type = Flag
+
+
+class TestIntEnum(_EnumTests, _MinimalOutputTests, unittest.TestCase):
+ enum_type = IntEnum
+
+
+class TestStrEnum(_EnumTests, _MinimalOutputTests, unittest.TestCase):
+ enum_type = StrEnum
+
+
+class TestIntFlag(_EnumTests, _MinimalOutputTests, unittest.TestCase):
+ enum_type = IntFlag
- def test_invalid_names(self):
- with self.assertRaises(ValueError):
- class Wrong(Enum):
- mro = 9
- with self.assertRaises(ValueError):
- class Wrong(Enum):
- _create_= 11
- with self.assertRaises(ValueError):
- class Wrong(Enum):
- _get_mixins_ = 9
- with self.assertRaises(ValueError):
- class Wrong(Enum):
- _find_new_ = 1
- with self.assertRaises(ValueError):
- class Wrong(Enum):
- _any_name_ = 9
+
+class TestMixedInt(_EnumTests, _MixedOutputTests, unittest.TestCase):
+ class enum_type(int, Enum): pass
+
+
+class TestMixedStr(_EnumTests, _MixedOutputTests, unittest.TestCase):
+ class enum_type(str, Enum): pass
+
+
+class TestMixedIntFlag(_EnumTests, _MixedOutputTests, unittest.TestCase):
+ class enum_type(int, Flag): pass
+
+
+class TestMixedDate(_EnumTests, _MixedOutputTests, unittest.TestCase):
+
+ values = [date(2021, 12, 25), date(2020, 3, 15), date(2019, 11, 27)]
+ source_values = [(2021, 12, 25), (2020, 3, 15), (2019, 11, 27)]
+
+ class enum_type(date, Enum):
+ def _generate_next_value_(name, start, count, last_values):
+ values = [(2021, 12, 25), (2020, 3, 15), (2019, 11, 27)]
+ return values[count]
+
+
+class TestMinimalDate(_EnumTests, _MinimalOutputTests, unittest.TestCase):
+
+ values = [date(2023, 12, 1), date(2016, 2, 29), date(2009, 1, 1)]
+ source_values = [(2023, 12, 1), (2016, 2, 29), (2009, 1, 1)]
+
+ class enum_type(date, ReprEnum):
+ def _generate_next_value_(name, start, count, last_values):
+ values = [(2023, 12, 1), (2016, 2, 29), (2009, 1, 1)]
+ return values[count]
+
+
+class TestMixedFloat(_EnumTests, _MixedOutputTests, unittest.TestCase):
+
+ values = [1.1, 2.2, 3.3]
+
+ class enum_type(float, Enum):
+ def _generate_next_value_(name, start, count, last_values):
+ values = [1.1, 2.2, 3.3]
+ return values[count]
+
+
+class TestMinimalFloat(_EnumTests, _MinimalOutputTests, unittest.TestCase):
+
+ values = [4.4, 5.5, 6.6]
+
+ class enum_type(float, ReprEnum):
+ def _generate_next_value_(name, start, count, last_values):
+ values = [4.4, 5.5, 6.6]
+ return values[count]
+
+
+class TestSpecial(unittest.TestCase):
+ """
+ various operations that are not attributable to every possible enum
+ """
+
+ def setUp(self):
+ class Season(Enum):
+ SPRING = 1
+ SUMMER = 2
+ AUTUMN = 3
+ WINTER = 4
+ self.Season = Season
+ #
+ class Grades(IntEnum):
+ A = 5
+ B = 4
+ C = 3
+ D = 2
+ F = 0
+ self.Grades = Grades
+ #
+ class Directional(str, Enum):
+ EAST = 'east'
+ WEST = 'west'
+ NORTH = 'north'
+ SOUTH = 'south'
+ self.Directional = Directional
+ #
+ from datetime import date
+ class Holiday(date, Enum):
+ NEW_YEAR = 2013, 1, 1
+ IDES_OF_MARCH = 2013, 3, 15
+ self.Holiday = Holiday
def test_bool(self):
# plain Enum members are always True
@@ -656,92 +865,56 @@ class TestEnum(unittest.TestCase):
self.assertTrue(IntLogic.true)
self.assertFalse(IntLogic.false)
- @unittest.skipIf(
- python_version >= (3, 12),
- '__contains__ now returns True/False for all inputs',
- )
- def test_contains_er(self):
- Season = self.Season
- self.assertIn(Season.AUTUMN, Season)
- with self.assertRaises(TypeError):
- with self.assertWarns(DeprecationWarning):
- 3 in Season
- with self.assertRaises(TypeError):
- with self.assertWarns(DeprecationWarning):
- 'AUTUMN' in Season
- val = Season(3)
- self.assertIn(val, Season)
- #
- class OtherEnum(Enum):
- one = 1; two = 2
- self.assertNotIn(OtherEnum.two, Season)
-
- @unittest.skipIf(
- python_version < (3, 12),
- '__contains__ only works with enum memmbers before 3.12',
- )
- def test_contains_tf(self):
- Season = self.Season
- self.assertIn(Season.AUTUMN, Season)
- self.assertTrue(3 in Season)
- self.assertFalse('AUTUMN' in Season)
- val = Season(3)
- self.assertIn(val, Season)
- #
- class OtherEnum(Enum):
- one = 1; two = 2
- self.assertNotIn(OtherEnum.two, Season)
-
def test_comparisons(self):
Season = self.Season
with self.assertRaises(TypeError):
Season.SPRING < Season.WINTER
with self.assertRaises(TypeError):
Season.SPRING > 4
-
+ #
self.assertNotEqual(Season.SPRING, 1)
-
+ #
class Part(Enum):
SPRING = 1
CLIP = 2
BARREL = 3
-
+ #
self.assertNotEqual(Season.SPRING, Part.SPRING)
with self.assertRaises(TypeError):
Season.SPRING < Part.CLIP
- def test_enum_duplicates(self):
- class Season(Enum):
- SPRING = 1
- SUMMER = 2
- AUTUMN = FALL = 3
- WINTER = 4
- ANOTHER_SPRING = 1
- lst = list(Season)
- self.assertEqual(
- lst,
- [Season.SPRING, Season.SUMMER,
- Season.AUTUMN, Season.WINTER,
- ])
- self.assertIs(Season.FALL, Season.AUTUMN)
- self.assertEqual(Season.FALL.value, 3)
- self.assertEqual(Season.AUTUMN.value, 3)
- self.assertIs(Season(3), Season.AUTUMN)
- self.assertIs(Season(1), Season.SPRING)
- self.assertEqual(Season.FALL.name, 'AUTUMN')
- self.assertEqual(
- [k for k,v in Season.__members__.items() if v.name != k],
- ['FALL', 'ANOTHER_SPRING'],
- )
+ def test_dir_with_custom_dunders(self):
+ class PlainEnum(Enum):
+ pass
+ cls_dir = dir(PlainEnum)
+ self.assertNotIn('__repr__', cls_dir)
+ self.assertNotIn('__str__', cls_dir)
+ self.assertNotIn('__repr__', cls_dir)
+ self.assertNotIn('__repr__', cls_dir)
+ #
+ class MyEnum(Enum):
+ def __repr__(self):
+ return object.__repr__(self)
+ def __str__(self):
+ return object.__repr__(self)
+ def __format__(self):
+ return object.__repr__(self)
+ def __init__(self):
+ pass
+ cls_dir = dir(MyEnum)
+ self.assertIn('__repr__', cls_dir)
+ self.assertIn('__str__', cls_dir)
+ self.assertIn('__repr__', cls_dir)
+ self.assertIn('__repr__', cls_dir)
- def test_duplicate_name(self):
+ def test_duplicate_name_error(self):
with self.assertRaises(TypeError):
class Color(Enum):
red = 1
green = 2
blue = 3
red = 4
-
+ #
with self.assertRaises(TypeError):
class Color(Enum):
red = 1
@@ -749,232 +922,45 @@ class TestEnum(unittest.TestCase):
blue = 3
def red(self):
return 'red'
-
+ #
with self.assertRaises(TypeError):
class Color(Enum):
- @property
+ @enum.property
def red(self):
return 'redder'
red = 1
green = 2
blue = 3
- def test_reserved__sunder_(self):
- with self.assertRaisesRegex(
- ValueError,
- '_sunder_ names, such as ._bad_., are reserved',
- ):
- class Bad(Enum):
- _bad_ = 1
+ def test_enum_function_with_qualname(self):
+ if isinstance(Theory, Exception):
+ raise Theory
+ self.assertEqual(Theory.__qualname__, 'spanish_inquisition')
def test_enum_with_value_name(self):
class Huh(Enum):
name = 1
value = 2
- self.assertEqual(
- list(Huh),
- [Huh.name, Huh.value],
- )
+ self.assertEqual(list(Huh), [Huh.name, Huh.value])
self.assertIs(type(Huh.name), Huh)
self.assertEqual(Huh.name.name, 'name')
self.assertEqual(Huh.name.value, 1)
- def test_format_enum(self):
- Season = self.Season
- self.assertEqual('{}'.format(Season.SPRING),
- '{}'.format(str(Season.SPRING)))
- self.assertEqual( '{:}'.format(Season.SPRING),
- '{:}'.format(str(Season.SPRING)))
- self.assertEqual('{:20}'.format(Season.SPRING),
- '{:20}'.format(str(Season.SPRING)))
- self.assertEqual('{:^20}'.format(Season.SPRING),
- '{:^20}'.format(str(Season.SPRING)))
- self.assertEqual('{:>20}'.format(Season.SPRING),
- '{:>20}'.format(str(Season.SPRING)))
- self.assertEqual('{:<20}'.format(Season.SPRING),
- '{:<20}'.format(str(Season.SPRING)))
-
- def test_str_override_enum(self):
- class EnumWithStrOverrides(Enum):
- one = auto()
- two = auto()
-
- def __str__(self):
- return 'Str!'
- self.assertEqual(str(EnumWithStrOverrides.one), 'Str!')
- self.assertEqual('{}'.format(EnumWithStrOverrides.one), 'Str!')
-
- def test_format_override_enum(self):
- class EnumWithFormatOverride(Enum):
- one = 1.0
- two = 2.0
- def __format__(self, spec):
- return 'Format!!'
- self.assertEqual(str(EnumWithFormatOverride.one), 'one')
- self.assertEqual('{}'.format(EnumWithFormatOverride.one), 'Format!!')
-
- def test_str_and_format_override_enum(self):
- class EnumWithStrFormatOverrides(Enum):
- one = auto()
- two = auto()
- def __str__(self):
- return 'Str!'
- def __format__(self, spec):
- return 'Format!'
- self.assertEqual(str(EnumWithStrFormatOverrides.one), 'Str!')
- self.assertEqual('{}'.format(EnumWithStrFormatOverrides.one), 'Format!')
-
- def test_str_override_mixin(self):
- class MixinEnumWithStrOverride(float, Enum):
- one = 1.0
- two = 2.0
- def __str__(self):
- return 'Overridden!'
- self.assertEqual(str(MixinEnumWithStrOverride.one), 'Overridden!')
- self.assertEqual('{}'.format(MixinEnumWithStrOverride.one), 'Overridden!')
-
- def test_str_and_format_override_mixin(self):
- class MixinWithStrFormatOverrides(float, Enum):
- one = 1.0
- two = 2.0
- def __str__(self):
- return 'Str!'
- def __format__(self, spec):
- return 'Format!'
- self.assertEqual(str(MixinWithStrFormatOverrides.one), 'Str!')
- self.assertEqual('{}'.format(MixinWithStrFormatOverrides.one), 'Format!')
-
- def test_format_override_mixin(self):
- class TestFloat(float, Enum):
- one = 1.0
- two = 2.0
- def __format__(self, spec):
- return 'TestFloat success!'
- self.assertEqual(str(TestFloat.one), 'one')
- self.assertEqual('{}'.format(TestFloat.one), 'TestFloat success!')
-
- @unittest.skipIf(
- python_version < (3, 12),
- 'mixin-format is still using member.value',
- )
- def test_mixin_format_warning(self):
- class Grades(int, Enum):
- A = 5
- B = 4
- C = 3
- D = 2
- F = 0
- self.assertEqual(f'{self.Grades.B}', 'B')
-
- @unittest.skipIf(
- python_version >= (3, 12),
- 'mixin-format now uses member instead of member.value',
- )
- def test_mixin_format_warning(self):
- class Grades(int, Enum):
- A = 5
- B = 4
- C = 3
- D = 2
- F = 0
- with self.assertWarns(DeprecationWarning):
- self.assertEqual(f'{Grades.B}', '4')
-
- def assertFormatIsValue(self, spec, member):
- if python_version < (3, 12) and (not spec or spec in ('{}','{:}')):
- with self.assertWarns(DeprecationWarning):
- self.assertEqual(spec.format(member), spec.format(member.value))
- else:
- self.assertEqual(spec.format(member), spec.format(member.value))
-
- def test_format_enum_date(self):
- Holiday = self.Holiday
- self.assertFormatIsValue('{}', Holiday.IDES_OF_MARCH)
- self.assertFormatIsValue('{:}', Holiday.IDES_OF_MARCH)
- self.assertFormatIsValue('{:20}', Holiday.IDES_OF_MARCH)
- self.assertFormatIsValue('{:^20}', Holiday.IDES_OF_MARCH)
- self.assertFormatIsValue('{:>20}', Holiday.IDES_OF_MARCH)
- self.assertFormatIsValue('{:<20}', Holiday.IDES_OF_MARCH)
- self.assertFormatIsValue('{:%Y %m}', Holiday.IDES_OF_MARCH)
- self.assertFormatIsValue('{:%Y %m %M:00}', Holiday.IDES_OF_MARCH)
-
- def test_format_enum_float(self):
- Konstants = self.Konstants
- self.assertFormatIsValue('{}', Konstants.TAU)
- self.assertFormatIsValue('{:}', Konstants.TAU)
- self.assertFormatIsValue('{:20}', Konstants.TAU)
- self.assertFormatIsValue('{:^20}', Konstants.TAU)
- self.assertFormatIsValue('{:>20}', Konstants.TAU)
- self.assertFormatIsValue('{:<20}', Konstants.TAU)
- self.assertFormatIsValue('{:n}', Konstants.TAU)
- self.assertFormatIsValue('{:5.2}', Konstants.TAU)
- self.assertFormatIsValue('{:f}', Konstants.TAU)
-
- def test_format_enum_int(self):
- class Grades(int, Enum):
- A = 5
- B = 4
- C = 3
- D = 2
- F = 0
- self.assertFormatIsValue('{}', Grades.C)
- self.assertFormatIsValue('{:}', Grades.C)
- self.assertFormatIsValue('{:20}', Grades.C)
- self.assertFormatIsValue('{:^20}', Grades.C)
- self.assertFormatIsValue('{:>20}', Grades.C)
- self.assertFormatIsValue('{:<20}', Grades.C)
- self.assertFormatIsValue('{:+}', Grades.C)
- self.assertFormatIsValue('{:08X}', Grades.C)
- self.assertFormatIsValue('{:b}', Grades.C)
-
- def test_format_enum_str(self):
- Directional = self.Directional
- self.assertFormatIsValue('{}', Directional.WEST)
- self.assertFormatIsValue('{:}', Directional.WEST)
- self.assertFormatIsValue('{:20}', Directional.WEST)
- self.assertFormatIsValue('{:^20}', Directional.WEST)
- self.assertFormatIsValue('{:>20}', Directional.WEST)
- self.assertFormatIsValue('{:<20}', Directional.WEST)
-
- def test_object_str_override(self):
- class Colors(Enum):
- RED, GREEN, BLUE = 1, 2, 3
- def __repr__(self):
- return "test.%s" % (self._name_, )
- __str__ = object.__str__
- self.assertEqual(str(Colors.RED), 'test.RED')
-
- def test_enum_str_override(self):
- class MyStrEnum(Enum):
- def __str__(self):
- return 'MyStr'
- class MyMethodEnum(Enum):
- def hello(self):
- return 'Hello! My name is %s' % self.name
- class Test1Enum(MyMethodEnum, int, MyStrEnum):
- One = 1
- Two = 2
- self.assertTrue(Test1Enum._member_type_ is int)
- self.assertEqual(str(Test1Enum.One), 'MyStr')
- self.assertEqual(format(Test1Enum.One, ''), 'MyStr')
- #
- class Test2Enum(MyStrEnum, MyMethodEnum):
- One = 1
- Two = 2
- self.assertEqual(str(Test2Enum.One), 'MyStr')
- self.assertEqual(format(Test1Enum.One, ''), 'MyStr')
-
def test_inherited_data_type(self):
class HexInt(int):
+ __qualname__ = 'HexInt'
def __repr__(self):
return hex(self)
class MyEnum(HexInt, enum.Enum):
+ __qualname__ = 'MyEnum'
A = 1
B = 2
C = 3
- def __repr__(self):
- return '<%s.%s: %r>' % (self.__class__.__name__, self._name_, self._value_)
self.assertEqual(repr(MyEnum.A), '<MyEnum.A: 0x1>')
+ globals()['HexInt'] = HexInt
+ globals()['MyEnum'] = MyEnum
+ test_pickle_dump_load(self.assertIs, MyEnum.A)
+ test_pickle_dump_load(self.assertIs, MyEnum)
#
class SillyInt(HexInt):
__qualname__ = 'SillyInt'
@@ -990,7 +976,7 @@ class TestEnum(unittest.TestCase):
test_pickle_dump_load(self.assertIs, MyOtherEnum.E)
test_pickle_dump_load(self.assertIs, MyOtherEnum)
#
- # This did not work in 3.9, but does now with pickling by name
+ # This did not work in 3.10, but does now with pickling by name
class UnBrokenInt(int):
__qualname__ = 'UnBrokenInt'
def __new__(cls, value):
@@ -1007,6 +993,124 @@ class TestEnum(unittest.TestCase):
test_pickle_dump_load(self.assertIs, MyUnBrokenEnum.I)
test_pickle_dump_load(self.assertIs, MyUnBrokenEnum)
+ def test_floatenum_fromhex(self):
+ h = float.hex(FloatStooges.MOE.value)
+ self.assertIs(FloatStooges.fromhex(h), FloatStooges.MOE)
+ h = float.hex(FloatStooges.MOE.value + 0.01)
+ with self.assertRaises(ValueError):
+ FloatStooges.fromhex(h)
+
+ def test_programmatic_function_type(self):
+ MinorEnum = Enum('MinorEnum', 'june july august', type=int)
+ lst = list(MinorEnum)
+ self.assertEqual(len(lst), len(MinorEnum))
+ self.assertEqual(len(MinorEnum), 3, MinorEnum)
+ self.assertEqual(
+ [MinorEnum.june, MinorEnum.july, MinorEnum.august],
+ lst,
+ )
+ for i, month in enumerate('june july august'.split(), 1):
+ e = MinorEnum(i)
+ self.assertEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertIn(e, MinorEnum)
+ self.assertIs(type(e), MinorEnum)
+
+ def test_programmatic_function_string_with_start(self):
+ MinorEnum = Enum('MinorEnum', 'june july august', start=10)
+ lst = list(MinorEnum)
+ self.assertEqual(len(lst), len(MinorEnum))
+ self.assertEqual(len(MinorEnum), 3, MinorEnum)
+ self.assertEqual(
+ [MinorEnum.june, MinorEnum.july, MinorEnum.august],
+ lst,
+ )
+ for i, month in enumerate('june july august'.split(), 10):
+ e = MinorEnum(i)
+ self.assertEqual(int(e.value), i)
+ self.assertNotEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertIn(e, MinorEnum)
+ self.assertIs(type(e), MinorEnum)
+
+ def test_programmatic_function_type_with_start(self):
+ MinorEnum = Enum('MinorEnum', 'june july august', type=int, start=30)
+ lst = list(MinorEnum)
+ self.assertEqual(len(lst), len(MinorEnum))
+ self.assertEqual(len(MinorEnum), 3, MinorEnum)
+ self.assertEqual(
+ [MinorEnum.june, MinorEnum.july, MinorEnum.august],
+ lst,
+ )
+ for i, month in enumerate('june july august'.split(), 30):
+ e = MinorEnum(i)
+ self.assertEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertIn(e, MinorEnum)
+ self.assertIs(type(e), MinorEnum)
+
+ def test_programmatic_function_string_list_with_start(self):
+ MinorEnum = Enum('MinorEnum', ['june', 'july', 'august'], start=20)
+ lst = list(MinorEnum)
+ self.assertEqual(len(lst), len(MinorEnum))
+ self.assertEqual(len(MinorEnum), 3, MinorEnum)
+ self.assertEqual(
+ [MinorEnum.june, MinorEnum.july, MinorEnum.august],
+ lst,
+ )
+ for i, month in enumerate('june july august'.split(), 20):
+ e = MinorEnum(i)
+ self.assertEqual(int(e.value), i)
+ self.assertNotEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertIn(e, MinorEnum)
+ self.assertIs(type(e), MinorEnum)
+
+ def test_programmatic_function_type_from_subclass(self):
+ MinorEnum = IntEnum('MinorEnum', 'june july august')
+ lst = list(MinorEnum)
+ self.assertEqual(len(lst), len(MinorEnum))
+ self.assertEqual(len(MinorEnum), 3, MinorEnum)
+ self.assertEqual(
+ [MinorEnum.june, MinorEnum.july, MinorEnum.august],
+ lst,
+ )
+ for i, month in enumerate('june july august'.split(), 1):
+ e = MinorEnum(i)
+ self.assertEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertIn(e, MinorEnum)
+ self.assertIs(type(e), MinorEnum)
+
+ def test_programmatic_function_type_from_subclass_with_start(self):
+ MinorEnum = IntEnum('MinorEnum', 'june july august', start=40)
+ lst = list(MinorEnum)
+ self.assertEqual(len(lst), len(MinorEnum))
+ self.assertEqual(len(MinorEnum), 3, MinorEnum)
+ self.assertEqual(
+ [MinorEnum.june, MinorEnum.july, MinorEnum.august],
+ lst,
+ )
+ for i, month in enumerate('june july august'.split(), 40):
+ e = MinorEnum(i)
+ self.assertEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertIn(e, MinorEnum)
+ self.assertIs(type(e), MinorEnum)
+
+ def test_intenum_from_bytes(self):
+ self.assertIs(IntStooges.from_bytes(b'\x00\x03', 'big'), IntStooges.MOE)
+ with self.assertRaises(ValueError):
+ IntStooges.from_bytes(b'\x00\x05', 'big')
+
+ def test_reserved_sunder_error(self):
+ with self.assertRaisesRegex(
+ ValueError,
+ '_sunder_ names, such as ._bad_., are reserved',
+ ):
+ class Bad(Enum):
+ _bad_ = 1
+
def test_too_many_data_types(self):
with self.assertRaisesRegex(TypeError, 'too many data types'):
class Huh(str, int, Enum):
@@ -1022,122 +1126,6 @@ class TestEnum(unittest.TestCase):
class Huh(MyStr, MyInt, Enum):
One = 1
- def test_value_auto_assign(self):
- class Some(Enum):
- def __new__(cls, val):
- return object.__new__(cls)
- x = 1
- y = 2
-
- self.assertEqual(Some.x.value, 1)
- self.assertEqual(Some.y.value, 2)
-
- def test_hash(self):
- Season = self.Season
- dates = {}
- dates[Season.WINTER] = '1225'
- dates[Season.SPRING] = '0315'
- dates[Season.SUMMER] = '0704'
- dates[Season.AUTUMN] = '1031'
- self.assertEqual(dates[Season.AUTUMN], '1031')
-
- def test_intenum_from_scratch(self):
- class phy(int, Enum):
- pi = 3
- tau = 2 * pi
- self.assertTrue(phy.pi < phy.tau)
-
- def test_intenum_inherited(self):
- class IntEnum(int, Enum):
- pass
- class phy(IntEnum):
- pi = 3
- tau = 2 * pi
- self.assertTrue(phy.pi < phy.tau)
-
- def test_floatenum_from_scratch(self):
- class phy(float, Enum):
- pi = 3.1415926
- tau = 2 * pi
- self.assertTrue(phy.pi < phy.tau)
-
- def test_floatenum_inherited(self):
- class FloatEnum(float, Enum):
- pass
- class phy(FloatEnum):
- pi = 3.1415926
- tau = 2 * pi
- self.assertTrue(phy.pi < phy.tau)
-
- def test_strenum_from_scratch(self):
- class phy(str, Enum):
- pi = 'Pi'
- tau = 'Tau'
- self.assertTrue(phy.pi < phy.tau)
-
- def test_strenum_inherited_methods(self):
- class phy(StrEnum):
- pi = 'Pi'
- tau = 'Tau'
- self.assertTrue(phy.pi < phy.tau)
- self.assertEqual(phy.pi.upper(), 'PI')
- self.assertEqual(phy.tau.count('a'), 1)
-
- def test_intenum(self):
- class WeekDay(IntEnum):
- SUNDAY = 1
- MONDAY = 2
- TUESDAY = 3
- WEDNESDAY = 4
- THURSDAY = 5
- FRIDAY = 6
- SATURDAY = 7
-
- self.assertEqual(['a', 'b', 'c'][WeekDay.MONDAY], 'c')
- self.assertEqual([i for i in range(WeekDay.TUESDAY)], [0, 1, 2])
-
- lst = list(WeekDay)
- self.assertEqual(len(lst), len(WeekDay))
- self.assertEqual(len(WeekDay), 7)
- target = 'SUNDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY'
- target = target.split()
- for i, weekday in enumerate(target, 1):
- e = WeekDay(i)
- self.assertEqual(e, i)
- self.assertEqual(int(e), i)
- self.assertEqual(e.name, weekday)
- self.assertIn(e, WeekDay)
- self.assertEqual(lst.index(e)+1, i)
- self.assertTrue(0 < e < 8)
- self.assertIs(type(e), WeekDay)
- self.assertIsInstance(e, int)
- self.assertIsInstance(e, Enum)
-
- def test_intenum_duplicates(self):
- class WeekDay(IntEnum):
- SUNDAY = 1
- MONDAY = 2
- TUESDAY = TEUSDAY = 3
- WEDNESDAY = 4
- THURSDAY = 5
- FRIDAY = 6
- SATURDAY = 7
- self.assertIs(WeekDay.TEUSDAY, WeekDay.TUESDAY)
- self.assertEqual(WeekDay(3).name, 'TUESDAY')
- self.assertEqual([k for k,v in WeekDay.__members__.items()
- if v.name != k], ['TEUSDAY', ])
-
- def test_intenum_from_bytes(self):
- self.assertIs(IntStooges.from_bytes(b'\x00\x03', 'big'), IntStooges.MOE)
- with self.assertRaises(ValueError):
- IntStooges.from_bytes(b'\x00\x05', 'big')
-
- def test_floatenum_fromhex(self):
- h = float.hex(FloatStooges.MOE.value)
- self.assertIs(FloatStooges.fromhex(h), FloatStooges.MOE)
- h = float.hex(FloatStooges.MOE.value + 0.01)
- with self.assertRaises(ValueError):
- FloatStooges.fromhex(h)
def test_pickle_enum(self):
if isinstance(Stooges, Exception):
@@ -1169,12 +1157,7 @@ class TestEnum(unittest.TestCase):
test_pickle_dump_load(self.assertIs, Question.who)
test_pickle_dump_load(self.assertIs, Question)
- def test_enum_function_with_qualname(self):
- if isinstance(Theory, Exception):
- raise Theory
- self.assertEqual(Theory.__qualname__, 'spanish_inquisition')
-
- def test_class_nested_enum_and_pickle_protocol_four(self):
+ def test_pickle_nested_class(self):
# would normally just have this directly in the class namespace
class NestedEnum(Enum):
twigs = 'common'
@@ -1192,7 +1175,7 @@ class TestEnum(unittest.TestCase):
for proto in range(HIGHEST_PROTOCOL):
self.assertEqual(ReplaceGlobalInt.TWO.__reduce_ex__(proto), 'TWO')
- def test_exploding_pickle(self):
+ def test_pickle_explodes(self):
BadPickle = Enum(
'BadPickle', 'dill sweet bread-n-butter', module=__name__)
globals()['BadPickle'] = BadPickle
@@ -1233,185 +1216,6 @@ class TestEnum(unittest.TestCase):
[Season.SUMMER, Season.WINTER, Season.AUTUMN, Season.SPRING],
)
- def test_reversed_iteration_order(self):
- self.assertEqual(
- list(reversed(self.Season)),
- [self.Season.WINTER, self.Season.AUTUMN, self.Season.SUMMER,
- self.Season.SPRING]
- )
-
- def test_programmatic_function_string(self):
- SummerMonth = Enum('SummerMonth', 'june july august')
- lst = list(SummerMonth)
- self.assertEqual(len(lst), len(SummerMonth))
- self.assertEqual(len(SummerMonth), 3, SummerMonth)
- self.assertEqual(
- [SummerMonth.june, SummerMonth.july, SummerMonth.august],
- lst,
- )
- for i, month in enumerate('june july august'.split(), 1):
- e = SummerMonth(i)
- self.assertEqual(int(e.value), i)
- self.assertNotEqual(e, i)
- self.assertEqual(e.name, month)
- self.assertIn(e, SummerMonth)
- self.assertIs(type(e), SummerMonth)
-
- def test_programmatic_function_string_with_start(self):
- SummerMonth = Enum('SummerMonth', 'june july august', start=10)
- lst = list(SummerMonth)
- self.assertEqual(len(lst), len(SummerMonth))
- self.assertEqual(len(SummerMonth), 3, SummerMonth)
- self.assertEqual(
- [SummerMonth.june, SummerMonth.july, SummerMonth.august],
- lst,
- )
- for i, month in enumerate('june july august'.split(), 10):
- e = SummerMonth(i)
- self.assertEqual(int(e.value), i)
- self.assertNotEqual(e, i)
- self.assertEqual(e.name, month)
- self.assertIn(e, SummerMonth)
- self.assertIs(type(e), SummerMonth)
-
- def test_programmatic_function_string_list(self):
- SummerMonth = Enum('SummerMonth', ['june', 'july', 'august'])
- lst = list(SummerMonth)
- self.assertEqual(len(lst), len(SummerMonth))
- self.assertEqual(len(SummerMonth), 3, SummerMonth)
- self.assertEqual(
- [SummerMonth.june, SummerMonth.july, SummerMonth.august],
- lst,
- )
- for i, month in enumerate('june july august'.split(), 1):
- e = SummerMonth(i)
- self.assertEqual(int(e.value), i)
- self.assertNotEqual(e, i)
- self.assertEqual(e.name, month)
- self.assertIn(e, SummerMonth)
- self.assertIs(type(e), SummerMonth)
-
- def test_programmatic_function_string_list_with_start(self):
- SummerMonth = Enum('SummerMonth', ['june', 'july', 'august'], start=20)
- lst = list(SummerMonth)
- self.assertEqual(len(lst), len(SummerMonth))
- self.assertEqual(len(SummerMonth), 3, SummerMonth)
- self.assertEqual(
- [SummerMonth.june, SummerMonth.july, SummerMonth.august],
- lst,
- )
- for i, month in enumerate('june july august'.split(), 20):
- e = SummerMonth(i)
- self.assertEqual(int(e.value), i)
- self.assertNotEqual(e, i)
- self.assertEqual(e.name, month)
- self.assertIn(e, SummerMonth)
- self.assertIs(type(e), SummerMonth)
-
- def test_programmatic_function_iterable(self):
- SummerMonth = Enum(
- 'SummerMonth',
- (('june', 1), ('july', 2), ('august', 3))
- )
- lst = list(SummerMonth)
- self.assertEqual(len(lst), len(SummerMonth))
- self.assertEqual(len(SummerMonth), 3, SummerMonth)
- self.assertEqual(
- [SummerMonth.june, SummerMonth.july, SummerMonth.august],
- lst,
- )
- for i, month in enumerate('june july august'.split(), 1):
- e = SummerMonth(i)
- self.assertEqual(int(e.value), i)
- self.assertNotEqual(e, i)
- self.assertEqual(e.name, month)
- self.assertIn(e, SummerMonth)
- self.assertIs(type(e), SummerMonth)
-
- def test_programmatic_function_from_dict(self):
- SummerMonth = Enum(
- 'SummerMonth',
- OrderedDict((('june', 1), ('july', 2), ('august', 3)))
- )
- lst = list(SummerMonth)
- self.assertEqual(len(lst), len(SummerMonth))
- self.assertEqual(len(SummerMonth), 3, SummerMonth)
- self.assertEqual(
- [SummerMonth.june, SummerMonth.july, SummerMonth.august],
- lst,
- )
- for i, month in enumerate('june july august'.split(), 1):
- e = SummerMonth(i)
- self.assertEqual(int(e.value), i)
- self.assertNotEqual(e, i)
- self.assertEqual(e.name, month)
- self.assertIn(e, SummerMonth)
- self.assertIs(type(e), SummerMonth)
-
- def test_programmatic_function_type(self):
- SummerMonth = Enum('SummerMonth', 'june july august', type=int)
- lst = list(SummerMonth)
- self.assertEqual(len(lst), len(SummerMonth))
- self.assertEqual(len(SummerMonth), 3, SummerMonth)
- self.assertEqual(
- [SummerMonth.june, SummerMonth.july, SummerMonth.august],
- lst,
- )
- for i, month in enumerate('june july august'.split(), 1):
- e = SummerMonth(i)
- self.assertEqual(e, i)
- self.assertEqual(e.name, month)
- self.assertIn(e, SummerMonth)
- self.assertIs(type(e), SummerMonth)
-
- def test_programmatic_function_type_with_start(self):
- SummerMonth = Enum('SummerMonth', 'june july august', type=int, start=30)
- lst = list(SummerMonth)
- self.assertEqual(len(lst), len(SummerMonth))
- self.assertEqual(len(SummerMonth), 3, SummerMonth)
- self.assertEqual(
- [SummerMonth.june, SummerMonth.july, SummerMonth.august],
- lst,
- )
- for i, month in enumerate('june july august'.split(), 30):
- e = SummerMonth(i)
- self.assertEqual(e, i)
- self.assertEqual(e.name, month)
- self.assertIn(e, SummerMonth)
- self.assertIs(type(e), SummerMonth)
-
- def test_programmatic_function_type_from_subclass(self):
- SummerMonth = IntEnum('SummerMonth', 'june july august')
- lst = list(SummerMonth)
- self.assertEqual(len(lst), len(SummerMonth))
- self.assertEqual(len(SummerMonth), 3, SummerMonth)
- self.assertEqual(
- [SummerMonth.june, SummerMonth.july, SummerMonth.august],
- lst,
- )
- for i, month in enumerate('june july august'.split(), 1):
- e = SummerMonth(i)
- self.assertEqual(e, i)
- self.assertEqual(e.name, month)
- self.assertIn(e, SummerMonth)
- self.assertIs(type(e), SummerMonth)
-
- def test_programmatic_function_type_from_subclass_with_start(self):
- SummerMonth = IntEnum('SummerMonth', 'june july august', start=40)
- lst = list(SummerMonth)
- self.assertEqual(len(lst), len(SummerMonth))
- self.assertEqual(len(SummerMonth), 3, SummerMonth)
- self.assertEqual(
- [SummerMonth.june, SummerMonth.july, SummerMonth.august],
- lst,
- )
- for i, month in enumerate('june july august'.split(), 40):
- e = SummerMonth(i)
- self.assertEqual(e, i)
- self.assertEqual(e.name, month)
- self.assertIn(e, SummerMonth)
- self.assertIs(type(e), SummerMonth)
-
def test_subclassing(self):
if isinstance(Name, Exception):
raise Name
@@ -1425,15 +1229,18 @@ class TestEnum(unittest.TestCase):
red = 1
green = 2
blue = 3
+ #
with self.assertRaises(TypeError):
class MoreColor(Color):
cyan = 4
magenta = 5
yellow = 6
- with self.assertRaisesRegex(TypeError, "EvenMoreColor: cannot extend enumeration 'Color'"):
+ #
+ with self.assertRaisesRegex(TypeError, "<enum .EvenMoreColor.> cannot extend <enum .Color.>"):
class EvenMoreColor(Color, IntEnum):
chartruese = 7
- with self.assertRaisesRegex(TypeError, "Foo: cannot extend enumeration 'Color'"):
+ #
+ with self.assertRaisesRegex(TypeError, "<enum .Foo.> cannot extend <enum .Color.>"):
Color('Foo', ('pink', 'black'))
def test_exclude_methods(self):
@@ -1537,27 +1344,7 @@ class TestEnum(unittest.TestCase):
with self.assertRaises(KeyError):
Color['chartreuse']
- def test_new_repr(self):
- class Color(Enum):
- red = 1
- green = 2
- blue = 3
- def __repr__(self):
- return "don't you just love shades of %s?" % self.name
- self.assertEqual(
- repr(Color.blue),
- "don't you just love shades of blue?",
- )
-
- def test_inherited_repr(self):
- class MyEnum(Enum):
- def __repr__(self):
- return "My name is %s." % self.name
- class MyIntEnum(int, MyEnum):
- this = 1
- that = 2
- theother = 3
- self.assertEqual(repr(MyIntEnum.that), "My name is that.")
+ # tests that need to be evalualted for moving
def test_multiple_mixin_mro(self):
class auto_enum(type(Enum)):
@@ -1610,7 +1397,7 @@ class TestEnum(unittest.TestCase):
return self
def __getnewargs__(self):
return self._args
- @property
+ @bltns.property
def __name__(self):
return self._intname
def __repr__(self):
@@ -1670,7 +1457,7 @@ class TestEnum(unittest.TestCase):
return self
def __getnewargs_ex__(self):
return self._args, {}
- @property
+ @bltns.property
def __name__(self):
return self._intname
def __repr__(self):
@@ -1730,7 +1517,7 @@ class TestEnum(unittest.TestCase):
return self
def __reduce__(self):
return self.__class__, self._args
- @property
+ @bltns.property
def __name__(self):
return self._intname
def __repr__(self):
@@ -1790,7 +1577,7 @@ class TestEnum(unittest.TestCase):
return self
def __reduce_ex__(self, proto):
return self.__class__, self._args
- @property
+ @bltns.property
def __name__(self):
return self._intname
def __repr__(self):
@@ -1847,7 +1634,7 @@ class TestEnum(unittest.TestCase):
self._intname = name
self._args = _args
return self
- @property
+ @bltns.property
def __name__(self):
return self._intname
def __repr__(self):
@@ -1902,7 +1689,7 @@ class TestEnum(unittest.TestCase):
self._intname = name
self._args = _args
return self
- @property
+ @bltns.property
def __name__(self):
return self._intname
def __repr__(self):
@@ -2091,6 +1878,7 @@ class TestEnum(unittest.TestCase):
class Test(Base):
test = 1
self.assertEqual(Test.test.test, 'dynamic')
+ self.assertEqual(Test.test.value, 1)
class Base2(Enum):
@enum.property
def flash(self):
@@ -2098,6 +1886,7 @@ class TestEnum(unittest.TestCase):
class Test(Base2):
flash = 1
self.assertEqual(Test.flash.flash, 'flashy dynamic')
+ self.assertEqual(Test.flash.value, 1)
def test_no_duplicates(self):
class UniqueEnum(Enum):
@@ -2134,7 +1923,7 @@ class TestEnum(unittest.TestCase):
def __init__(self, mass, radius):
self.mass = mass # in kilograms
self.radius = radius # in meters
- @property
+ @enum.property
def surface_gravity(self):
# universal gravitational constant (m3 kg-1 s-2)
G = 6.67300E-11
@@ -2204,90 +1993,7 @@ class TestEnum(unittest.TestCase):
self.assertEqual(LabelledList.unprocessed, 1)
self.assertEqual(LabelledList(1), LabelledList.unprocessed)
- def test_auto_number(self):
- class Color(Enum):
- red = auto()
- blue = auto()
- green = auto()
-
- self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
- self.assertEqual(Color.red.value, 1)
- self.assertEqual(Color.blue.value, 2)
- self.assertEqual(Color.green.value, 3)
-
- def test_auto_name(self):
- class Color(Enum):
- def _generate_next_value_(name, start, count, last):
- return name
- red = auto()
- blue = auto()
- green = auto()
-
- self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
- self.assertEqual(Color.red.value, 'red')
- self.assertEqual(Color.blue.value, 'blue')
- self.assertEqual(Color.green.value, 'green')
-
- def test_auto_name_inherit(self):
- class AutoNameEnum(Enum):
- def _generate_next_value_(name, start, count, last):
- return name
- class Color(AutoNameEnum):
- red = auto()
- blue = auto()
- green = auto()
-
- self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
- self.assertEqual(Color.red.value, 'red')
- self.assertEqual(Color.blue.value, 'blue')
- self.assertEqual(Color.green.value, 'green')
-
- def test_auto_garbage(self):
- class Color(Enum):
- red = 'red'
- blue = auto()
- self.assertEqual(Color.blue.value, 1)
-
- def test_auto_garbage_corrected(self):
- class Color(Enum):
- red = 'red'
- blue = 2
- green = auto()
-
- self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
- self.assertEqual(Color.red.value, 'red')
- self.assertEqual(Color.blue.value, 2)
- self.assertEqual(Color.green.value, 3)
-
- def test_auto_order(self):
- with self.assertRaises(TypeError):
- class Color(Enum):
- red = auto()
- green = auto()
- blue = auto()
- def _generate_next_value_(name, start, count, last):
- return name
-
- def test_auto_order_wierd(self):
- weird_auto = auto()
- weird_auto.value = 'pathological case'
- class Color(Enum):
- red = weird_auto
- def _generate_next_value_(name, start, count, last):
- return name
- blue = auto()
- self.assertEqual(list(Color), [Color.red, Color.blue])
- self.assertEqual(Color.red.value, 'pathological case')
- self.assertEqual(Color.blue.value, 'blue')
-
- def test_duplicate_auto(self):
- class Dupes(Enum):
- first = primero = auto()
- second = auto()
- third = auto()
- self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes))
-
- def test_default_missing(self):
+ def test_default_missing_no_chained_exception(self):
class Color(Enum):
RED = 1
GREEN = 2
@@ -2299,7 +2005,7 @@ class TestEnum(unittest.TestCase):
else:
raise Exception('Exception not raised.')
- def test_missing(self):
+ def test_missing_override(self):
class Color(Enum):
red = 1
green = 2
@@ -2363,9 +2069,9 @@ class TestEnum(unittest.TestCase):
class_1_ref = weakref.ref(Class1())
class_2_ref = weakref.ref(Class2())
#
- # The exception raised by Enum creates a reference loop and thus
- # Class2 instances will stick around until the next garbage collection
- # cycle, unlike Class1.
+ # The exception raised by Enum used to create a reference loop and thus
+ # Class2 instances would stick around until the next garbage collection
+ # cycle, unlike Class1. Verify Class2 no longer does this.
gc.collect() # For PyPy or other GCs.
self.assertIs(class_1_ref(), None)
self.assertIs(class_2_ref(), None)
@@ -2396,11 +2102,12 @@ class TestEnum(unittest.TestCase):
self.assertEqual(Color.GREEN.value, 2)
self.assertEqual(Color.BLUE.value, 3)
self.assertEqual(Color.MAX, 3)
- self.assertEqual(str(Color.BLUE), 'BLUE')
+ self.assertEqual(str(Color.BLUE), 'Color.BLUE')
class Color(MaxMixin, StrMixin, Enum):
RED = auto()
GREEN = auto()
BLUE = auto()
+ __str__ = StrMixin.__str__ # needed as of 3.11
self.assertEqual(Color.RED.value, 1)
self.assertEqual(Color.GREEN.value, 2)
self.assertEqual(Color.BLUE.value, 3)
@@ -2410,6 +2117,7 @@ class TestEnum(unittest.TestCase):
RED = auto()
GREEN = auto()
BLUE = auto()
+ __str__ = StrMixin.__str__ # needed as of 3.11
self.assertEqual(Color.RED.value, 1)
self.assertEqual(Color.GREEN.value, 2)
self.assertEqual(Color.BLUE.value, 3)
@@ -2419,6 +2127,7 @@ class TestEnum(unittest.TestCase):
RED = auto()
GREEN = auto()
BLUE = auto()
+ __str__ = StrMixin.__str__ # needed as of 3.11
self.assertEqual(CoolColor.RED.value, 1)
self.assertEqual(CoolColor.GREEN.value, 2)
self.assertEqual(CoolColor.BLUE.value, 3)
@@ -2428,6 +2137,7 @@ class TestEnum(unittest.TestCase):
RED = auto()
GREEN = auto()
BLUE = auto()
+ __str__ = StrMixin.__str__ # needed as of 3.11
self.assertEqual(CoolerColor.RED.value, 1)
self.assertEqual(CoolerColor.GREEN.value, 2)
self.assertEqual(CoolerColor.BLUE.value, 3)
@@ -2438,6 +2148,7 @@ class TestEnum(unittest.TestCase):
RED = auto()
GREEN = auto()
BLUE = auto()
+ __str__ = StrMixin.__str__ # needed as of 3.11
self.assertEqual(CoolestColor.RED.value, 1)
self.assertEqual(CoolestColor.GREEN.value, 2)
self.assertEqual(CoolestColor.BLUE.value, 3)
@@ -2448,6 +2159,7 @@ class TestEnum(unittest.TestCase):
RED = auto()
GREEN = auto()
BLUE = auto()
+ __str__ = StrMixin.__str__ # needed as of 3.11
self.assertEqual(ConfusedColor.RED.value, 1)
self.assertEqual(ConfusedColor.GREEN.value, 2)
self.assertEqual(ConfusedColor.BLUE.value, 3)
@@ -2458,6 +2170,7 @@ class TestEnum(unittest.TestCase):
RED = auto()
GREEN = auto()
BLUE = auto()
+ __str__ = StrMixin.__str__ # needed as of 3.11
self.assertEqual(ReformedColor.RED.value, 1)
self.assertEqual(ReformedColor.GREEN.value, 2)
self.assertEqual(ReformedColor.BLUE.value, 3)
@@ -2490,11 +2203,12 @@ class TestEnum(unittest.TestCase):
return hex(self)
class MyIntEnum(HexMixin, MyInt, enum.Enum):
- pass
+ __repr__ = HexMixin.__repr__
class Foo(MyIntEnum):
TEST = 1
self.assertTrue(isinstance(Foo.TEST, MyInt))
+ self.assertEqual(Foo._member_type_, MyInt)
self.assertEqual(repr(Foo.TEST), "0x1")
class Fee(MyIntEnum):
@@ -2506,7 +2220,7 @@ class TestEnum(unittest.TestCase):
return member
self.assertEqual(Fee.TEST, 2)
- def test_miltuple_mixin_with_common_data_type(self):
+ def test_multiple_mixin_with_common_data_type(self):
class CaseInsensitiveStrEnum(str, Enum):
@classmethod
def _missing_(cls, value):
@@ -2526,7 +2240,7 @@ class TestEnum(unittest.TestCase):
unknown._value_ = value
cls._member_map_[value] = unknown
return unknown
- @property
+ @enum.property
def valid(self):
return self._valid
#
@@ -2570,7 +2284,7 @@ class TestEnum(unittest.TestCase):
self.assertEqual('{}'.format(GoodStrEnum.one), '1')
self.assertEqual(GoodStrEnum.one, str(GoodStrEnum.one))
self.assertEqual(GoodStrEnum.one, '{}'.format(GoodStrEnum.one))
- self.assertEqual(repr(GoodStrEnum.one), 'GoodStrEnum.one')
+ self.assertEqual(repr(GoodStrEnum.one), "<GoodStrEnum.one: '1'>")
#
class DumbMixin:
def __str__(self):
@@ -2579,6 +2293,7 @@ class TestEnum(unittest.TestCase):
five = '5'
six = '6'
seven = '7'
+ __str__ = DumbMixin.__str__ # needed as of 3.11
self.assertEqual(DumbStrEnum.seven, '7')
self.assertEqual(str(DumbStrEnum.seven), "don't do this")
#
@@ -2620,74 +2335,6 @@ class TestEnum(unittest.TestCase):
one = '1'
two = b'2', 'ascii', 9
- @unittest.skipIf(
- python_version >= (3, 12),
- 'mixin-format now uses member instead of member.value',
- )
- def test_custom_strenum_with_warning(self):
- class CustomStrEnum(str, Enum):
- pass
- class OkayEnum(CustomStrEnum):
- one = '1'
- two = '2'
- three = b'3', 'ascii'
- four = b'4', 'latin1', 'strict'
- self.assertEqual(OkayEnum.one, '1')
- self.assertEqual(str(OkayEnum.one), 'one')
- with self.assertWarns(DeprecationWarning):
- self.assertEqual('{}'.format(OkayEnum.one), '1')
- self.assertEqual(OkayEnum.one, '{}'.format(OkayEnum.one))
- self.assertEqual(repr(OkayEnum.one), 'OkayEnum.one')
- #
- class DumbMixin:
- def __str__(self):
- return "don't do this"
- class DumbStrEnum(DumbMixin, CustomStrEnum):
- five = '5'
- six = '6'
- seven = '7'
- self.assertEqual(DumbStrEnum.seven, '7')
- self.assertEqual(str(DumbStrEnum.seven), "don't do this")
- #
- class EnumMixin(Enum):
- def hello(self):
- print('hello from %s' % (self, ))
- class HelloEnum(EnumMixin, CustomStrEnum):
- eight = '8'
- self.assertEqual(HelloEnum.eight, '8')
- self.assertEqual(str(HelloEnum.eight), 'eight')
- #
- class GoodbyeMixin:
- def goodbye(self):
- print('%s wishes you a fond farewell')
- class GoodbyeEnum(GoodbyeMixin, EnumMixin, CustomStrEnum):
- nine = '9'
- self.assertEqual(GoodbyeEnum.nine, '9')
- self.assertEqual(str(GoodbyeEnum.nine), 'nine')
- #
- class FirstFailedStrEnum(CustomStrEnum):
- one = 1 # this will become '1'
- two = '2'
- class SecondFailedStrEnum(CustomStrEnum):
- one = '1'
- two = 2, # this will become '2'
- three = '3'
- class ThirdFailedStrEnum(CustomStrEnum):
- one = '1'
- two = 2 # this will become '2'
- with self.assertRaisesRegex(TypeError, '.encoding. must be str, not '):
- class ThirdFailedStrEnum(CustomStrEnum):
- one = '1'
- two = b'2', sys.getdefaultencoding
- with self.assertRaisesRegex(TypeError, '.errors. must be str, not '):
- class ThirdFailedStrEnum(CustomStrEnum):
- one = '1'
- two = b'2', 'ascii', 9
-
- @unittest.skipIf(
- python_version < (3, 12),
- 'mixin-format currently uses member.value',
- )
def test_custom_strenum(self):
class CustomStrEnum(str, Enum):
pass
@@ -2697,9 +2344,9 @@ class TestEnum(unittest.TestCase):
three = b'3', 'ascii'
four = b'4', 'latin1', 'strict'
self.assertEqual(OkayEnum.one, '1')
- self.assertEqual(str(OkayEnum.one), 'one')
- self.assertEqual('{}'.format(OkayEnum.one), 'one')
- self.assertEqual(repr(OkayEnum.one), 'OkayEnum.one')
+ self.assertEqual(str(OkayEnum.one), 'OkayEnum.one')
+ self.assertEqual('{}'.format(OkayEnum.one), 'OkayEnum.one')
+ self.assertEqual(repr(OkayEnum.one), "<OkayEnum.one: '1'>")
#
class DumbMixin:
def __str__(self):
@@ -2708,6 +2355,7 @@ class TestEnum(unittest.TestCase):
five = '5'
six = '6'
seven = '7'
+ __str__ = DumbMixin.__str__ # needed as of 3.11
self.assertEqual(DumbStrEnum.seven, '7')
self.assertEqual(str(DumbStrEnum.seven), "don't do this")
#
@@ -2717,7 +2365,7 @@ class TestEnum(unittest.TestCase):
class HelloEnum(EnumMixin, CustomStrEnum):
eight = '8'
self.assertEqual(HelloEnum.eight, '8')
- self.assertEqual(str(HelloEnum.eight), 'eight')
+ self.assertEqual(str(HelloEnum.eight), 'HelloEnum.eight')
#
class GoodbyeMixin:
def goodbye(self):
@@ -2725,7 +2373,7 @@ class TestEnum(unittest.TestCase):
class GoodbyeEnum(GoodbyeMixin, EnumMixin, CustomStrEnum):
nine = '9'
self.assertEqual(GoodbyeEnum.nine, '9')
- self.assertEqual(str(GoodbyeEnum.nine), 'nine')
+ self.assertEqual(str(GoodbyeEnum.nine), 'GoodbyeEnum.nine')
#
class FirstFailedStrEnum(CustomStrEnum):
one = 1 # this will become '1'
@@ -2771,21 +2419,6 @@ class TestEnum(unittest.TestCase):
code = 'An$(5,1)', 2
description = 'Bn$', 3
- @unittest.skipUnless(
- python_version == (3, 9),
- 'private variables are now normal attributes',
- )
- def test_warning_for_private_variables(self):
- with self.assertWarns(DeprecationWarning):
- class Private(Enum):
- __corporal = 'Radar'
- self.assertEqual(Private._Private__corporal.value, 'Radar')
- try:
- with self.assertWarns(DeprecationWarning):
- class Private(Enum):
- __major_ = 'Hoolihan'
- except ValueError:
- pass
def test_private_variable_is_normal_attribute(self):
class Private(Enum):
@@ -2794,35 +2427,13 @@ class TestEnum(unittest.TestCase):
self.assertEqual(Private._Private__corporal, 'Radar')
self.assertEqual(Private._Private__major_, 'Hoolihan')
- @unittest.skipUnless(
- python_version < (3, 12),
- 'member-member access now raises an exception',
- )
- def test_warning_for_member_from_member_access(self):
- with self.assertWarns(DeprecationWarning):
- class Di(Enum):
- YES = 1
- NO = 0
- nope = Di.YES.NO
- self.assertIs(Di.NO, nope)
-
- @unittest.skipUnless(
- python_version >= (3, 12),
- 'member-member access currently issues a warning',
- )
def test_exception_for_member_from_member_access(self):
- with self.assertRaisesRegex(AttributeError, "Di: no instance attribute .NO."):
+ with self.assertRaisesRegex(AttributeError, "<enum .Di.> member has no attribute .NO."):
class Di(Enum):
YES = 1
NO = 0
nope = Di.YES.NO
- def test_strenum_auto(self):
- class Strings(StrEnum):
- ONE = auto()
- TWO = auto()
- self.assertEqual([Strings.ONE, Strings.TWO], ['one', 'two'])
-
def test_dynamic_members_with_static_methods(self):
#
@@ -2839,7 +2450,7 @@ class TestEnum(unittest.TestCase):
self.assertEqual(Foo.FOO_CAT.value, 'aloof')
self.assertEqual(Foo.FOO_HORSE.upper(), 'BIG')
#
- with self.assertRaisesRegex(TypeError, "'FOO_CAT' already defined as: 'aloof'"):
+ with self.assertRaisesRegex(TypeError, "'FOO_CAT' already defined as 'aloof'"):
class FooBar(Enum):
vars().update({
k: v
@@ -2851,8 +2462,42 @@ class TestEnum(unittest.TestCase):
def upper(self):
return self.value.upper()
+ def test_repr_with_dataclass(self):
+ "ensure dataclass-mixin has correct repr()"
+ from dataclasses import dataclass
+ @dataclass
+ class Foo:
+ __qualname__ = 'Foo'
+ a: int = 0
+ class Entries(Foo, Enum):
+ ENTRY1 = Foo(1)
+ self.assertEqual(repr(Entries.ENTRY1), '<Entries.ENTRY1: Foo(a=1)>')
+
+ def test_repr_with_non_data_type_mixin(self):
+ # non-data_type is a mixin that doesn't define __new__
+ class Foo:
+ def __init__(self, a):
+ self.a = a
+ def __repr__(self):
+ return f'Foo(a={self.a!r})'
+ class Entries(Foo, Enum):
+ ENTRY1 = Foo(1)
+
+ self.assertEqual(repr(Entries.ENTRY1), '<Entries.ENTRY1: Foo(a=1)>')
+
+ def test_value_backup_assign(self):
+ # check that enum will add missing values when custom __new__ does not
+ class Some(Enum):
+ def __new__(cls, val):
+ return object.__new__(cls)
+ x = 1
+ y = 2
+ self.assertEqual(Some.x.value, 1)
+ self.assertEqual(Some.y.value, 2)
+
class TestOrder(unittest.TestCase):
+ "test usage of the `_order_` attribute"
def test_same_members(self):
class Color(Enum):
@@ -2914,7 +2559,7 @@ class TestOrder(unittest.TestCase):
verde = green
-class TestFlag(unittest.TestCase):
+class OldTestFlag(unittest.TestCase):
"""Tests of the Flags."""
class Perm(Flag):
@@ -2937,65 +2582,6 @@ class TestFlag(unittest.TestCase):
WHITE = RED|GREEN|BLUE
BLANCO = RED|GREEN|BLUE
- def test_str(self):
- Perm = self.Perm
- self.assertEqual(str(Perm.R), 'R')
- self.assertEqual(str(Perm.W), 'W')
- self.assertEqual(str(Perm.X), 'X')
- self.assertEqual(str(Perm.R | Perm.W), 'R|W')
- self.assertEqual(str(Perm.R | Perm.W | Perm.X), 'R|W|X')
- self.assertEqual(str(Perm(0)), 'Perm(0)')
- self.assertEqual(str(~Perm.R), 'W|X')
- self.assertEqual(str(~Perm.W), 'R|X')
- self.assertEqual(str(~Perm.X), 'R|W')
- self.assertEqual(str(~(Perm.R | Perm.W)), 'X')
- self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), 'Perm(0)')
- self.assertEqual(str(Perm(~0)), 'R|W|X')
-
- Open = self.Open
- self.assertEqual(str(Open.RO), 'RO')
- self.assertEqual(str(Open.WO), 'WO')
- self.assertEqual(str(Open.AC), 'AC')
- self.assertEqual(str(Open.RO | Open.CE), 'CE')
- self.assertEqual(str(Open.WO | Open.CE), 'WO|CE')
- self.assertEqual(str(~Open.RO), 'WO|RW|CE')
- self.assertEqual(str(~Open.WO), 'RW|CE')
- self.assertEqual(str(~Open.AC), 'CE')
- self.assertEqual(str(~(Open.RO | Open.CE)), 'AC')
- self.assertEqual(str(~(Open.WO | Open.CE)), 'RW')
-
- def test_repr(self):
- Perm = self.Perm
- self.assertEqual(repr(Perm.R), 'Perm.R')
- self.assertEqual(repr(Perm.W), 'Perm.W')
- self.assertEqual(repr(Perm.X), 'Perm.X')
- self.assertEqual(repr(Perm.R | Perm.W), 'Perm.R|Perm.W')
- self.assertEqual(repr(Perm.R | Perm.W | Perm.X), 'Perm.R|Perm.W|Perm.X')
- self.assertEqual(repr(Perm(0)), '0x0')
- self.assertEqual(repr(~Perm.R), 'Perm.W|Perm.X')
- self.assertEqual(repr(~Perm.W), 'Perm.R|Perm.X')
- self.assertEqual(repr(~Perm.X), 'Perm.R|Perm.W')
- self.assertEqual(repr(~(Perm.R | Perm.W)), 'Perm.X')
- self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '0x0')
- self.assertEqual(repr(Perm(~0)), 'Perm.R|Perm.W|Perm.X')
-
- Open = self.Open
- self.assertEqual(repr(Open.RO), 'Open.RO')
- self.assertEqual(repr(Open.WO), 'Open.WO')
- self.assertEqual(repr(Open.AC), 'Open.AC')
- self.assertEqual(repr(Open.RO | Open.CE), 'Open.CE')
- self.assertEqual(repr(Open.WO | Open.CE), 'Open.WO|Open.CE')
- self.assertEqual(repr(~Open.RO), 'Open.WO|Open.RW|Open.CE')
- self.assertEqual(repr(~Open.WO), 'Open.RW|Open.CE')
- self.assertEqual(repr(~Open.AC), 'Open.CE')
- self.assertEqual(repr(~(Open.RO | Open.CE)), 'Open.AC')
- self.assertEqual(repr(~(Open.WO | Open.CE)), 'Open.RW')
-
- def test_format(self):
- Perm = self.Perm
- self.assertEqual(format(Perm.R, ''), 'R')
- self.assertEqual(format(Perm.R | Perm.X, ''), 'R|X')
-
def test_or(self):
Perm = self.Perm
for i in Perm:
@@ -3088,7 +2674,7 @@ class TestFlag(unittest.TestCase):
c = 4
d = 6
#
- self.assertRaisesRegex(ValueError, 'invalid value: 7', Iron, 7)
+ self.assertRaisesRegex(ValueError, 'invalid value 7', Iron, 7)
#
self.assertIs(Water(7), Water.ONE|Water.TWO)
self.assertIs(Water(~9), Water.TWO)
@@ -3297,7 +2883,7 @@ class TestFlag(unittest.TestCase):
self.assertEqual(Color.green.value, 4)
def test_auto_number_garbage(self):
- with self.assertRaisesRegex(TypeError, 'Invalid Flag value: .not an int.'):
+ with self.assertRaisesRegex(TypeError, 'invalid flag value .not an int.'):
class Color(Flag):
red = 'not an int'
blue = auto()
@@ -3332,11 +2918,12 @@ class TestFlag(unittest.TestCase):
self.assertEqual(Color.GREEN.value, 2)
self.assertEqual(Color.BLUE.value, 4)
self.assertEqual(Color.ALL.value, 7)
- self.assertEqual(str(Color.BLUE), 'BLUE')
+ self.assertEqual(str(Color.BLUE), 'Color.BLUE')
class Color(AllMixin, StrMixin, Flag):
RED = auto()
GREEN = auto()
BLUE = auto()
+ __str__ = StrMixin.__str__
self.assertEqual(Color.RED.value, 1)
self.assertEqual(Color.GREEN.value, 2)
self.assertEqual(Color.BLUE.value, 4)
@@ -3346,6 +2933,7 @@ class TestFlag(unittest.TestCase):
RED = auto()
GREEN = auto()
BLUE = auto()
+ __str__ = StrMixin.__str__
self.assertEqual(Color.RED.value, 1)
self.assertEqual(Color.GREEN.value, 2)
self.assertEqual(Color.BLUE.value, 4)
@@ -3426,21 +3014,8 @@ class TestFlag(unittest.TestCase):
self.assertFalse(NeverEnum.__dict__.get('_test1', False))
self.assertFalse(NeverEnum.__dict__.get('_test2', False))
- def test_default_missing(self):
- with self.assertRaisesRegex(
- ValueError,
- "'RED' is not a valid TestFlag.Color",
- ) as ctx:
- self.Color('RED')
- self.assertIs(ctx.exception.__context__, None)
-
- P = Flag('P', 'X Y')
- with self.assertRaisesRegex(ValueError, "'X' is not a valid P") as ctx:
- P('X')
- self.assertIs(ctx.exception.__context__, None)
-
-class TestIntFlag(unittest.TestCase):
+class OldTestIntFlag(unittest.TestCase):
"""Tests of the IntFlags."""
class Perm(IntFlag):
@@ -3485,73 +3060,6 @@ class TestIntFlag(unittest.TestCase):
self.assertTrue(isinstance(Open.WO | Open.RW, Open))
self.assertEqual(Open.WO | Open.RW, 3)
-
- def test_str(self):
- Perm = self.Perm
- self.assertEqual(str(Perm.R), 'R')
- self.assertEqual(str(Perm.W), 'W')
- self.assertEqual(str(Perm.X), 'X')
- self.assertEqual(str(Perm.R | Perm.W), 'R|W')
- self.assertEqual(str(Perm.R | Perm.W | Perm.X), 'R|W|X')
- self.assertEqual(str(Perm.R | 8), '12')
- self.assertEqual(str(Perm(0)), 'Perm(0)')
- self.assertEqual(str(Perm(8)), '8')
- self.assertEqual(str(~Perm.R), 'W|X')
- self.assertEqual(str(~Perm.W), 'R|X')
- self.assertEqual(str(~Perm.X), 'R|W')
- self.assertEqual(str(~(Perm.R | Perm.W)), 'X')
- self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), 'Perm(0)')
- self.assertEqual(str(~(Perm.R | 8)), '-13')
- self.assertEqual(str(Perm(~0)), 'R|W|X')
- self.assertEqual(str(Perm(~8)), '-9')
-
- Open = self.Open
- self.assertEqual(str(Open.RO), 'RO')
- self.assertEqual(str(Open.WO), 'WO')
- self.assertEqual(str(Open.AC), 'AC')
- self.assertEqual(str(Open.RO | Open.CE), 'CE')
- self.assertEqual(str(Open.WO | Open.CE), 'WO|CE')
- self.assertEqual(str(Open(4)), '4')
- self.assertEqual(str(~Open.RO), 'WO|RW|CE')
- self.assertEqual(str(~Open.WO), 'RW|CE')
- self.assertEqual(str(~Open.AC), 'CE')
- self.assertEqual(str(~(Open.RO | Open.CE)), 'AC')
- self.assertEqual(str(~(Open.WO | Open.CE)), 'RW')
- self.assertEqual(str(Open(~4)), '-5')
-
- def test_repr(self):
- Perm = self.Perm
- self.assertEqual(repr(Perm.R), 'Perm.R')
- self.assertEqual(repr(Perm.W), 'Perm.W')
- self.assertEqual(repr(Perm.X), 'Perm.X')
- self.assertEqual(repr(Perm.R | Perm.W), 'Perm.R|Perm.W')
- self.assertEqual(repr(Perm.R | Perm.W | Perm.X), 'Perm.R|Perm.W|Perm.X')
- self.assertEqual(repr(Perm.R | 8), '12')
- self.assertEqual(repr(Perm(0)), '0x0')
- self.assertEqual(repr(Perm(8)), '8')
- self.assertEqual(repr(~Perm.R), 'Perm.W|Perm.X')
- self.assertEqual(repr(~Perm.W), 'Perm.R|Perm.X')
- self.assertEqual(repr(~Perm.X), 'Perm.R|Perm.W')
- self.assertEqual(repr(~(Perm.R | Perm.W)), 'Perm.X')
- self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '0x0')
- self.assertEqual(repr(~(Perm.R | 8)), '-13')
- self.assertEqual(repr(Perm(~0)), 'Perm.R|Perm.W|Perm.X')
- self.assertEqual(repr(Perm(~8)), '-9')
-
- Open = self.Open
- self.assertEqual(repr(Open.RO), 'Open.RO')
- self.assertEqual(repr(Open.WO), 'Open.WO')
- self.assertEqual(repr(Open.AC), 'Open.AC')
- self.assertEqual(repr(Open.RO | Open.CE), 'Open.CE')
- self.assertEqual(repr(Open.WO | Open.CE), 'Open.WO|Open.CE')
- self.assertEqual(repr(Open(4)), '4')
- self.assertEqual(repr(~Open.RO), 'Open.WO|Open.RW|Open.CE')
- self.assertEqual(repr(~Open.WO), 'Open.RW|Open.CE')
- self.assertEqual(repr(~Open.AC), 'Open.CE')
- self.assertEqual(repr(~(Open.RO | Open.CE)), 'Open.AC')
- self.assertEqual(repr(~(Open.WO | Open.CE)), 'Open.RW')
- self.assertEqual(repr(Open(~4)), '-5')
-
def test_global_repr_keep(self):
self.assertEqual(
repr(HeadlightsK(0)),
@@ -3559,11 +3067,11 @@ class TestIntFlag(unittest.TestCase):
)
self.assertEqual(
repr(HeadlightsK(2**0 + 2**2 + 2**3)),
- '%(m)s.LOW_BEAM_K|%(m)s.FOG_K|0x8' % {'m': SHORT_MODULE},
+ '%(m)s.LOW_BEAM_K|%(m)s.FOG_K|8' % {'m': SHORT_MODULE},
)
self.assertEqual(
repr(HeadlightsK(2**3)),
- '%(m)s.HeadlightsK(0x8)' % {'m': SHORT_MODULE},
+ '%(m)s.HeadlightsK(8)' % {'m': SHORT_MODULE},
)
def test_global_repr_conform1(self):
@@ -3705,7 +3213,7 @@ class TestIntFlag(unittest.TestCase):
c = 4
d = 6
#
- self.assertRaisesRegex(ValueError, 'invalid value: 5', Iron, 5)
+ self.assertRaisesRegex(ValueError, 'invalid value 5', Iron, 5)
#
self.assertIs(Water(7), Water.ONE|Water.TWO)
self.assertIs(Water(~9), Water.TWO)
@@ -3942,11 +3450,12 @@ class TestIntFlag(unittest.TestCase):
self.assertEqual(Color.GREEN.value, 2)
self.assertEqual(Color.BLUE.value, 4)
self.assertEqual(Color.ALL.value, 7)
- self.assertEqual(str(Color.BLUE), 'BLUE')
+ self.assertEqual(str(Color.BLUE), '4')
class Color(AllMixin, StrMixin, IntFlag):
RED = auto()
GREEN = auto()
BLUE = auto()
+ __str__ = StrMixin.__str__
self.assertEqual(Color.RED.value, 1)
self.assertEqual(Color.GREEN.value, 2)
self.assertEqual(Color.BLUE.value, 4)
@@ -3956,6 +3465,7 @@ class TestIntFlag(unittest.TestCase):
RED = auto()
GREEN = auto()
BLUE = auto()
+ __str__ = StrMixin.__str__
self.assertEqual(Color.RED.value, 1)
self.assertEqual(Color.GREEN.value, 2)
self.assertEqual(Color.BLUE.value, 4)
@@ -4000,19 +3510,6 @@ class TestIntFlag(unittest.TestCase):
'at least one thread failed while creating composite members')
self.assertEqual(256, len(seen), 'too many composite members created')
- def test_default_missing(self):
- with self.assertRaisesRegex(
- ValueError,
- "'RED' is not a valid TestIntFlag.Color",
- ) as ctx:
- self.Color('RED')
- self.assertIs(ctx.exception.__context__, None)
-
- P = IntFlag('P', 'X Y')
- with self.assertRaisesRegex(ValueError, "'X' is not a valid P") as ctx:
- P('X')
- self.assertIs(ctx.exception.__context__, None)
-
class TestEmptyAndNonLatinStrings(unittest.TestCase):
@@ -4229,6 +3726,89 @@ class TestHelpers(unittest.TestCase):
for name in self.sunder_names + self.dunder_names + self.random_names:
self.assertFalse(enum._is_private('MyEnum', name), '%r is a private name?')
+ def test_auto_number(self):
+ class Color(Enum):
+ red = auto()
+ blue = auto()
+ green = auto()
+
+ self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
+ self.assertEqual(Color.red.value, 1)
+ self.assertEqual(Color.blue.value, 2)
+ self.assertEqual(Color.green.value, 3)
+
+ def test_auto_name(self):
+ class Color(Enum):
+ def _generate_next_value_(name, start, count, last):
+ return name
+ red = auto()
+ blue = auto()
+ green = auto()
+
+ self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
+ self.assertEqual(Color.red.value, 'red')
+ self.assertEqual(Color.blue.value, 'blue')
+ self.assertEqual(Color.green.value, 'green')
+
+ def test_auto_name_inherit(self):
+ class AutoNameEnum(Enum):
+ def _generate_next_value_(name, start, count, last):
+ return name
+ class Color(AutoNameEnum):
+ red = auto()
+ blue = auto()
+ green = auto()
+
+ self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
+ self.assertEqual(Color.red.value, 'red')
+ self.assertEqual(Color.blue.value, 'blue')
+ self.assertEqual(Color.green.value, 'green')
+
+ def test_auto_garbage(self):
+ class Color(Enum):
+ red = 'red'
+ blue = auto()
+ self.assertEqual(Color.blue.value, 1)
+
+ def test_auto_garbage_corrected(self):
+ class Color(Enum):
+ red = 'red'
+ blue = 2
+ green = auto()
+
+ self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
+ self.assertEqual(Color.red.value, 'red')
+ self.assertEqual(Color.blue.value, 2)
+ self.assertEqual(Color.green.value, 3)
+
+ def test_auto_order(self):
+ with self.assertRaises(TypeError):
+ class Color(Enum):
+ red = auto()
+ green = auto()
+ blue = auto()
+ def _generate_next_value_(name, start, count, last):
+ return name
+
+ def test_auto_order_wierd(self):
+ weird_auto = auto()
+ weird_auto.value = 'pathological case'
+ class Color(Enum):
+ red = weird_auto
+ def _generate_next_value_(name, start, count, last):
+ return name
+ blue = auto()
+ self.assertEqual(list(Color), [Color.red, Color.blue])
+ self.assertEqual(Color.red.value, 'pathological case')
+ self.assertEqual(Color.blue.value, 'blue')
+
+ def test_duplicate_auto(self):
+ class Dupes(Enum):
+ first = primero = auto()
+ second = auto()
+ third = auto()
+ self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes))
+
class TestEnumTypeSubclassing(unittest.TestCase):
pass
@@ -4238,7 +3818,35 @@ Help on class Color in module %s:
class Color(enum.Enum)
| Color(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)
|\x20\x20
- | An enumeration.
+ | A collection of name/value pairs.
+ |\x20\x20
+ | Access them by:
+ |\x20\x20
+ | - attribute access::
+ |\x20\x20
+ | >>> Color.CYAN
+ | <Color.CYAN: 1>
+ |\x20\x20
+ | - value lookup:
+ |\x20\x20
+ | >>> Color(1)
+ | <Color.CYAN: 1>
+ |\x20\x20
+ | - name lookup:
+ |\x20\x20
+ | >>> Color['CYAN']
+ | <Color.CYAN: 1>
+ |\x20\x20
+ | Enumerations can be iterated over, and know how many members they have:
+ |\x20\x20
+ | >>> len(Color)
+ | 3
+ |\x20\x20
+ | >>> list(Color)
+ | [<Color.CYAN: 1>, <Color.MAGENTA: 2>, <Color.YELLOW: 3>]
+ |\x20\x20
+ | Methods can be added to enumerations, and members can have their own
+ | attributes -- see the documentation for details.
|\x20\x20
| Method resolution order:
| Color
@@ -4247,11 +3855,11 @@ class Color(enum.Enum)
|\x20\x20
| Data and other attributes defined here:
|\x20\x20
- | blue = Color.blue
+ | CYAN = <Color.CYAN: 1>
|\x20\x20
- | green = Color.green
+ | MAGENTA = <Color.MAGENTA: 2>
|\x20\x20
- | red = Color.red
+ | YELLOW = <Color.YELLOW: 3>
|\x20\x20
| ----------------------------------------------------------------------
| Data descriptors inherited from enum.Enum:
@@ -4263,6 +3871,25 @@ class Color(enum.Enum)
| The value of the Enum member.
|\x20\x20
| ----------------------------------------------------------------------
+ | Methods inherited from enum.EnumType:
+ |\x20\x20
+ | __contains__(member) from enum.EnumType
+ | Return True if member is a member of this enum
+ | raises TypeError if member is not an enum member
+ |\x20\x20\x20\x20\x20\x20
+ | note: in 3.12 TypeError will no longer be raised, and True will also be
+ | returned if member is the value of a member in this enum
+ |\x20\x20
+ | __getitem__(name) from enum.EnumType
+ | Return the member matching `name`.
+ |\x20\x20
+ | __iter__() from enum.EnumType
+ | Return members in definition order.
+ |\x20\x20
+ | __len__() from enum.EnumType
+ | Return the number of members (no aliases)
+ |\x20\x20
+ | ----------------------------------------------------------------------
| Readonly properties inherited from enum.EnumType:
|\x20\x20
| __members__
@@ -4284,11 +3911,11 @@ class Color(enum.Enum)
|\x20\x20
| Data and other attributes defined here:
|\x20\x20
- | blue = Color.blue
+ | YELLOW = <Color.YELLOW: 3>
|\x20\x20
- | green = Color.green
+ | MAGENTA = <Color.MAGENTA: 2>
|\x20\x20
- | red = Color.red
+ | CYAN = <Color.CYAN: 1>
|\x20\x20
| ----------------------------------------------------------------------
| Data descriptors inherited from enum.Enum:
@@ -4307,9 +3934,9 @@ class TestStdLib(unittest.TestCase):
maxDiff = None
class Color(Enum):
- red = 1
- green = 2
- blue = 3
+ CYAN = 1
+ MAGENTA = 2
+ YELLOW = 3
def test_pydoc(self):
# indirectly test __objclass__
@@ -4321,24 +3948,34 @@ class TestStdLib(unittest.TestCase):
helper = pydoc.Helper(output=output)
helper(self.Color)
result = output.getvalue().strip()
- self.assertEqual(result, expected_text)
+ self.assertEqual(result, expected_text, result)
def test_inspect_getmembers(self):
values = dict((
('__class__', EnumType),
- ('__doc__', 'An enumeration.'),
+ ('__doc__', '...'),
('__members__', self.Color.__members__),
('__module__', __name__),
- ('blue', self.Color.blue),
- ('green', self.Color.green),
+ ('YELLOW', self.Color.YELLOW),
+ ('MAGENTA', self.Color.MAGENTA),
+ ('CYAN', self.Color.CYAN),
('name', Enum.__dict__['name']),
- ('red', self.Color.red),
('value', Enum.__dict__['value']),
+ ('__len__', self.Color.__len__),
+ ('__contains__', self.Color.__contains__),
+ ('__name__', 'Color'),
+ ('__getitem__', self.Color.__getitem__),
+ ('__qualname__', 'TestStdLib.Color'),
+ ('__init_subclass__', getattr(self.Color, '__init_subclass__')),
+ ('__iter__', self.Color.__iter__),
))
result = dict(inspect.getmembers(self.Color))
self.assertEqual(set(values.keys()), set(result.keys()))
failed = False
for k in values.keys():
+ if k == '__doc__':
+ # __doc__ is huge, not comparing
+ continue
if result[k] != values[k]:
print()
print('\n%s\n key: %s\n result: %s\nexpected: %s\n%s\n' %
@@ -4353,23 +3990,42 @@ class TestStdLib(unittest.TestCase):
values = [
Attribute(name='__class__', kind='data',
defining_class=object, object=EnumType),
+ Attribute(name='__contains__', kind='method',
+ defining_class=EnumType, object=self.Color.__contains__),
Attribute(name='__doc__', kind='data',
- defining_class=self.Color, object='An enumeration.'),
+ defining_class=self.Color, object='...'),
+ Attribute(name='__getitem__', kind='method',
+ defining_class=EnumType, object=self.Color.__getitem__),
+ Attribute(name='__iter__', kind='method',
+ defining_class=EnumType, object=self.Color.__iter__),
+ Attribute(name='__init_subclass__', kind='class method',
+ defining_class=object, object=getattr(self.Color, '__init_subclass__')),
+ Attribute(name='__len__', kind='method',
+ defining_class=EnumType, object=self.Color.__len__),
Attribute(name='__members__', kind='property',
defining_class=EnumType, object=EnumType.__members__),
Attribute(name='__module__', kind='data',
defining_class=self.Color, object=__name__),
- Attribute(name='blue', kind='data',
- defining_class=self.Color, object=self.Color.blue),
- Attribute(name='green', kind='data',
- defining_class=self.Color, object=self.Color.green),
- Attribute(name='red', kind='data',
- defining_class=self.Color, object=self.Color.red),
+ Attribute(name='__name__', kind='data',
+ defining_class=self.Color, object='Color'),
+ Attribute(name='__qualname__', kind='data',
+ defining_class=self.Color, object='TestStdLib.Color'),
+ Attribute(name='YELLOW', kind='data',
+ defining_class=self.Color, object=self.Color.YELLOW),
+ Attribute(name='MAGENTA', kind='data',
+ defining_class=self.Color, object=self.Color.MAGENTA),
+ Attribute(name='CYAN', kind='data',
+ defining_class=self.Color, object=self.Color.CYAN),
Attribute(name='name', kind='data',
defining_class=Enum, object=Enum.__dict__['name']),
Attribute(name='value', kind='data',
defining_class=Enum, object=Enum.__dict__['value']),
]
+ for v in values:
+ try:
+ v.name
+ except AttributeError:
+ print(v)
values.sort(key=lambda item: item.name)
result = list(inspect.classify_class_attrs(self.Color))
result.sort(key=lambda item: item.name)
@@ -4379,7 +4035,15 @@ class TestStdLib(unittest.TestCase):
)
failed = False
for v, r in zip(values, result):
- if r != v:
+ if r.name in ('__init_subclass__', '__doc__'):
+ # not sure how to make the __init_subclass_ Attributes match
+ # so as long as there is one, call it good
+ # __doc__ is too big to check exactly, so treat the same as __init_subclass__
+ for name in ('name','kind','defining_class'):
+ if getattr(v, name) != getattr(r, name):
+ print('\n%s\n%s\n%s\n%s\n' % ('=' * 75, r, v, '=' * 75), sep='')
+ failed = True
+ elif r != v:
print('\n%s\n%s\n%s\n%s\n' % ('=' * 75, r, v, '=' * 75), sep='')
failed = True
if failed:
@@ -4388,15 +4052,15 @@ class TestStdLib(unittest.TestCase):
def test_test_simple_enum(self):
@_simple_enum(Enum)
class SimpleColor:
- RED = 1
- GREEN = 2
- BLUE = 3
+ CYAN = 1
+ MAGENTA = 2
+ YELLOW = 3
class CheckedColor(Enum):
- RED = 1
- GREEN = 2
- BLUE = 3
+ CYAN = 1
+ MAGENTA = 2
+ YELLOW = 3
self.assertTrue(_test_simple_enum(CheckedColor, SimpleColor) is None)
- SimpleColor.GREEN._value_ = 9
+ SimpleColor.MAGENTA._value_ = 9
self.assertRaisesRegex(
TypeError, "enum mismatch",
_test_simple_enum, CheckedColor, SimpleColor,
@@ -4422,9 +4086,165 @@ class TestStdLib(unittest.TestCase):
class MiscTestCase(unittest.TestCase):
+
def test__all__(self):
support.check__all__(self, enum, not_exported={'bin', 'show_flag_values'})
+ def test_doc_1(self):
+ class Single(Enum):
+ ONE = 1
+ self.assertEqual(
+ Single.__doc__,
+ dedent("""\
+ A collection of name/value pairs.
+
+ Access them by:
+
+ - attribute access::
+
+ >>> Single.ONE
+ <Single.ONE: 1>
+
+ - value lookup:
+
+ >>> Single(1)
+ <Single.ONE: 1>
+
+ - name lookup:
+
+ >>> Single['ONE']
+ <Single.ONE: 1>
+
+ Enumerations can be iterated over, and know how many members they have:
+
+ >>> len(Single)
+ 1
+
+ >>> list(Single)
+ [<Single.ONE: 1>]
+
+ Methods can be added to enumerations, and members can have their own
+ attributes -- see the documentation for details.
+ """))
+
+ def test_doc_2(self):
+ class Double(Enum):
+ ONE = 1
+ TWO = 2
+ self.assertEqual(
+ Double.__doc__,
+ dedent("""\
+ A collection of name/value pairs.
+
+ Access them by:
+
+ - attribute access::
+
+ >>> Double.ONE
+ <Double.ONE: 1>
+
+ - value lookup:
+
+ >>> Double(1)
+ <Double.ONE: 1>
+
+ - name lookup:
+
+ >>> Double['ONE']
+ <Double.ONE: 1>
+
+ Enumerations can be iterated over, and know how many members they have:
+
+ >>> len(Double)
+ 2
+
+ >>> list(Double)
+ [<Double.ONE: 1>, <Double.TWO: 2>]
+
+ Methods can be added to enumerations, and members can have their own
+ attributes -- see the documentation for details.
+ """))
+
+
+ def test_doc_1(self):
+ class Triple(Enum):
+ ONE = 1
+ TWO = 2
+ THREE = 3
+ self.assertEqual(
+ Triple.__doc__,
+ dedent("""\
+ A collection of name/value pairs.
+
+ Access them by:
+
+ - attribute access::
+
+ >>> Triple.ONE
+ <Triple.ONE: 1>
+
+ - value lookup:
+
+ >>> Triple(1)
+ <Triple.ONE: 1>
+
+ - name lookup:
+
+ >>> Triple['ONE']
+ <Triple.ONE: 1>
+
+ Enumerations can be iterated over, and know how many members they have:
+
+ >>> len(Triple)
+ 3
+
+ >>> list(Triple)
+ [<Triple.ONE: 1>, <Triple.TWO: 2>, <Triple.THREE: 3>]
+
+ Methods can be added to enumerations, and members can have their own
+ attributes -- see the documentation for details.
+ """))
+
+ def test_doc_1(self):
+ class Quadruple(Enum):
+ ONE = 1
+ TWO = 2
+ THREE = 3
+ FOUR = 4
+ self.assertEqual(
+ Quadruple.__doc__,
+ dedent("""\
+ A collection of name/value pairs.
+
+ Access them by:
+
+ - attribute access::
+
+ >>> Quadruple.ONE
+ <Quadruple.ONE: 1>
+
+ - value lookup:
+
+ >>> Quadruple(1)
+ <Quadruple.ONE: 1>
+
+ - name lookup:
+
+ >>> Quadruple['ONE']
+ <Quadruple.ONE: 1>
+
+ Enumerations can be iterated over, and know how many members they have:
+
+ >>> len(Quadruple)
+ 4
+
+ >>> list(Quadruple)[:3]
+ [<Quadruple.ONE: 1>, <Quadruple.TWO: 2>, <Quadruple.THREE: 3>]
+
+ Methods can be added to enumerations, and members can have their own
+ attributes -- see the documentation for details.
+ """))
+
# These are unordered here on purpose to ensure that declaration order
# makes no difference.
@@ -4442,6 +4262,10 @@ CONVERT_STRING_TEST_NAME_A = 5 # This one should sort first.
CONVERT_STRING_TEST_NAME_E = 5
CONVERT_STRING_TEST_NAME_F = 5
+# global names for StrEnum._convert_ test
+CONVERT_STR_TEST_2 = 'goodbye'
+CONVERT_STR_TEST_1 = 'hello'
+
# We also need values that cannot be compared:
UNCOMPARABLE_A = 5
UNCOMPARABLE_C = (9, 1) # naming order is broken on purpose
@@ -4453,32 +4277,40 @@ COMPLEX_B = 3j
class _ModuleWrapper:
"""We use this class as a namespace for swapping modules."""
-
def __init__(self, module):
self.__dict__.update(module.__dict__)
-class TestIntEnumConvert(unittest.TestCase):
+class TestConvert(unittest.TestCase):
+ def tearDown(self):
+ # Reset the module-level test variables to their original integer
+ # values, otherwise the already created enum values get converted
+ # instead.
+ g = globals()
+ for suffix in ['A', 'B', 'C', 'D', 'E', 'F']:
+ g['CONVERT_TEST_NAME_%s' % suffix] = 5
+ g['CONVERT_STRING_TEST_NAME_%s' % suffix] = 5
+ for suffix, value in (('A', 5), ('B', (9, 1)), ('C', 'value')):
+ g['UNCOMPARABLE_%s' % suffix] = value
+ for suffix, value in (('A', 2j), ('B', 3j), ('C', 1j)):
+ g['COMPLEX_%s' % suffix] = value
+ for suffix, value in (('1', 'hello'), ('2', 'goodbye')):
+ g['CONVERT_STR_TEST_%s' % suffix] = value
+
def test_convert_value_lookup_priority(self):
- with support.swap_item(
- sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]),
- ):
- test_type = enum.IntEnum._convert_(
- 'UnittestConvert',
- MODULE,
- filter=lambda x: x.startswith('CONVERT_TEST_'))
+ test_type = enum.IntEnum._convert_(
+ 'UnittestConvert',
+ MODULE,
+ filter=lambda x: x.startswith('CONVERT_TEST_'))
# We don't want the reverse lookup value to vary when there are
# multiple possible names for a given value. It should always
# report the first lexigraphical name in that case.
self.assertEqual(test_type(5).name, 'CONVERT_TEST_NAME_A')
- def test_convert(self):
- with support.swap_item(
- sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]),
- ):
- test_type = enum.IntEnum._convert_(
- 'UnittestConvert',
- MODULE,
- filter=lambda x: x.startswith('CONVERT_TEST_'))
+ def test_convert_int(self):
+ test_type = enum.IntEnum._convert_(
+ 'UnittestConvert',
+ MODULE,
+ filter=lambda x: x.startswith('CONVERT_TEST_'))
# Ensure that test_type has all of the desired names and values.
self.assertEqual(test_type.CONVERT_TEST_NAME_F,
test_type.CONVERT_TEST_NAME_A)
@@ -4487,43 +4319,57 @@ class TestIntEnumConvert(unittest.TestCase):
self.assertEqual(test_type.CONVERT_TEST_NAME_D, 5)
self.assertEqual(test_type.CONVERT_TEST_NAME_E, 5)
# Ensure that test_type only picked up names matching the filter.
- self.assertEqual([name for name in dir(test_type)
- if name[0:2] not in ('CO', '__')
- and name not in dir(IntEnum)],
- [], msg='Names other than CONVERT_TEST_* found.')
+ int_dir = dir(int) + [
+ 'CONVERT_TEST_NAME_A', 'CONVERT_TEST_NAME_B', 'CONVERT_TEST_NAME_C',
+ 'CONVERT_TEST_NAME_D', 'CONVERT_TEST_NAME_E', 'CONVERT_TEST_NAME_F',
+ ]
+ self.assertEqual(
+ [name for name in dir(test_type) if name not in int_dir],
+ [],
+ msg='Names other than CONVERT_TEST_* found.',
+ )
def test_convert_uncomparable(self):
- # We swap a module to some other object with `__dict__`
- # because otherwise refleak is created.
- # `_convert_` uses a module side effect that does this. See 30472
- with support.swap_item(
- sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]),
- ):
- uncomp = enum.Enum._convert_(
- 'Uncomparable',
- MODULE,
- filter=lambda x: x.startswith('UNCOMPARABLE_'))
-
+ uncomp = enum.Enum._convert_(
+ 'Uncomparable',
+ MODULE,
+ filter=lambda x: x.startswith('UNCOMPARABLE_'))
# Should be ordered by `name` only:
self.assertEqual(
list(uncomp),
[uncomp.UNCOMPARABLE_A, uncomp.UNCOMPARABLE_B, uncomp.UNCOMPARABLE_C],
- )
+ )
def test_convert_complex(self):
- with support.swap_item(
- sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]),
- ):
- uncomp = enum.Enum._convert_(
- 'Uncomparable',
- MODULE,
- filter=lambda x: x.startswith('COMPLEX_'))
-
+ uncomp = enum.Enum._convert_(
+ 'Uncomparable',
+ MODULE,
+ filter=lambda x: x.startswith('COMPLEX_'))
# Should be ordered by `name` only:
self.assertEqual(
list(uncomp),
[uncomp.COMPLEX_A, uncomp.COMPLEX_B, uncomp.COMPLEX_C],
- )
+ )
+
+ def test_convert_str(self):
+ test_type = enum.StrEnum._convert_(
+ 'UnittestConvert',
+ MODULE,
+ filter=lambda x: x.startswith('CONVERT_STR_'),
+ as_global=True)
+ # Ensure that test_type has all of the desired names and values.
+ self.assertEqual(test_type.CONVERT_STR_TEST_1, 'hello')
+ self.assertEqual(test_type.CONVERT_STR_TEST_2, 'goodbye')
+ # Ensure that test_type only picked up names matching the filter.
+ str_dir = dir(str) + ['CONVERT_STR_TEST_1', 'CONVERT_STR_TEST_2']
+ self.assertEqual(
+ [name for name in dir(test_type) if name not in str_dir],
+ [],
+ msg='Names other than CONVERT_STR_* found.',
+ )
+ self.assertEqual(repr(test_type.CONVERT_STR_TEST_1), '%s.CONVERT_STR_TEST_1' % SHORT_MODULE)
+ self.assertEqual(str(test_type.CONVERT_STR_TEST_2), 'goodbye')
+ self.assertEqual(format(test_type.CONVERT_STR_TEST_1), 'hello')
def test_convert_raise(self):
with self.assertRaises(AttributeError):
@@ -4533,50 +4379,58 @@ class TestIntEnumConvert(unittest.TestCase):
filter=lambda x: x.startswith('CONVERT_TEST_'))
def test_convert_repr_and_str(self):
- with support.swap_item(
- sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]),
- ):
- test_type = enum.IntEnum._convert_(
- 'UnittestConvert',
- MODULE,
- filter=lambda x: x.startswith('CONVERT_STRING_TEST_'))
+ test_type = enum.IntEnum._convert_(
+ 'UnittestConvert',
+ MODULE,
+ filter=lambda x: x.startswith('CONVERT_STRING_TEST_'),
+ as_global=True)
self.assertEqual(repr(test_type.CONVERT_STRING_TEST_NAME_A), '%s.CONVERT_STRING_TEST_NAME_A' % SHORT_MODULE)
- self.assertEqual(str(test_type.CONVERT_STRING_TEST_NAME_A), 'CONVERT_STRING_TEST_NAME_A')
+ self.assertEqual(str(test_type.CONVERT_STRING_TEST_NAME_A), '5')
self.assertEqual(format(test_type.CONVERT_STRING_TEST_NAME_A), '5')
-# global names for StrEnum._convert_ test
-CONVERT_STR_TEST_2 = 'goodbye'
-CONVERT_STR_TEST_1 = 'hello'
-class TestStrEnumConvert(unittest.TestCase):
- def test_convert(self):
- with support.swap_item(
- sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]),
- ):
- test_type = enum.StrEnum._convert_(
- 'UnittestConvert',
- MODULE,
- filter=lambda x: x.startswith('CONVERT_STR_'))
- # Ensure that test_type has all of the desired names and values.
- self.assertEqual(test_type.CONVERT_STR_TEST_1, 'hello')
- self.assertEqual(test_type.CONVERT_STR_TEST_2, 'goodbye')
- # Ensure that test_type only picked up names matching the filter.
- self.assertEqual([name for name in dir(test_type)
- if name[0:2] not in ('CO', '__')
- and name not in dir(StrEnum)],
- [], msg='Names other than CONVERT_STR_* found.')
+# helpers
- def test_convert_repr_and_str(self):
- with support.swap_item(
- sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]),
- ):
- test_type = enum.StrEnum._convert_(
- 'UnittestConvert',
- MODULE,
- filter=lambda x: x.startswith('CONVERT_STR_'))
- self.assertEqual(repr(test_type.CONVERT_STR_TEST_1), '%s.CONVERT_STR_TEST_1' % SHORT_MODULE)
- self.assertEqual(str(test_type.CONVERT_STR_TEST_2), 'goodbye')
- self.assertEqual(format(test_type.CONVERT_STR_TEST_1), 'hello')
+def enum_dir(cls):
+ # TODO: check for custom __init__, __new__, __format__, __repr__, __str__, __init_subclass__
+ if cls._member_type_ is object:
+ interesting = set()
+ if cls.__init_subclass__ is not object.__init_subclass__:
+ interesting.add('__init_subclass__')
+ return sorted(set([
+ '__class__', '__contains__', '__doc__', '__getitem__',
+ '__iter__', '__len__', '__members__', '__module__',
+ '__name__', '__qualname__',
+ ]
+ + cls._member_names_
+ ) | interesting
+ )
+ else:
+ # return whatever mixed-in data type has
+ return sorted(set(
+ dir(cls._member_type_)
+ + cls._member_names_
+ ))
+
+def member_dir(member):
+ if member.__class__._member_type_ is object:
+ allowed = set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value'])
+ else:
+ allowed = set(dir(member))
+ for cls in member.__class__.mro():
+ for name, obj in cls.__dict__.items():
+ if name[0] == '_':
+ continue
+ if isinstance(obj, enum.property):
+ if obj.fget is not None or name not in member._member_map_:
+ allowed.add(name)
+ else:
+ allowed.discard(name)
+ else:
+ allowed.add(name)
+ return sorted(allowed)
+
+missing = object()
if __name__ == '__main__':
diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py
index 3f0e727..ac4626d 100644
--- a/Lib/test/test_signal.py
+++ b/Lib/test/test_signal.py
@@ -908,7 +908,7 @@ class PendingSignalsTests(unittest.TestCase):
%s
- blocked = %r
+ blocked = %s
signum = signal.SIGALRM
# child: block and wait the signal
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index 394d294..56cc23d 100755
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -1517,9 +1517,11 @@ class GeneralModuleTests(unittest.TestCase):
infos = socket.getaddrinfo(HOST, 80, socket.AF_INET, socket.SOCK_STREAM)
for family, type, _, _, _ in infos:
self.assertEqual(family, socket.AF_INET)
- self.assertEqual(str(family), 'AF_INET')
+ self.assertEqual(repr(family), '<AddressFamily.AF_INET: 2>')
+ self.assertEqual(str(family), '2')
self.assertEqual(type, socket.SOCK_STREAM)
- self.assertEqual(str(type), 'SOCK_STREAM')
+ self.assertEqual(repr(type), '<SocketKind.SOCK_STREAM: 1>')
+ self.assertEqual(str(type), '1')
infos = socket.getaddrinfo(HOST, None, 0, socket.SOCK_STREAM)
for _, socktype, _, _, _ in infos:
self.assertEqual(socktype, socket.SOCK_STREAM)
@@ -1793,8 +1795,10 @@ class GeneralModuleTests(unittest.TestCase):
# Make sure that the AF_* and SOCK_* constants have enum-like string
# reprs.
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
- self.assertEqual(str(s.family), 'AF_INET')
- self.assertEqual(str(s.type), 'SOCK_STREAM')
+ self.assertEqual(repr(s.family), '<AddressFamily.AF_INET: 2>')
+ self.assertEqual(repr(s.type), '<SocketKind.SOCK_STREAM: 1>')
+ self.assertEqual(str(s.family), '2')
+ self.assertEqual(str(s.type), '1')
def test_socket_consistent_sock_type(self):
SOCK_NONBLOCK = getattr(socket, 'SOCK_NONBLOCK', 0)
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index f99a3e8..64f4bce 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -373,7 +373,8 @@ class BasicSocketTests(unittest.TestCase):
# Make sure that the PROTOCOL_* constants have enum-like string
# reprs.
proto = ssl.PROTOCOL_TLS_CLIENT
- self.assertEqual(str(proto), 'PROTOCOL_TLS_CLIENT')
+ self.assertEqual(repr(proto), '<_SSLMethod.PROTOCOL_TLS_CLIENT: 16>')
+ self.assertEqual(str(proto), '16')
ctx = ssl.SSLContext(proto)
self.assertIs(ctx.protocol, proto)
@@ -622,7 +623,7 @@ class BasicSocketTests(unittest.TestCase):
with self.assertWarns(DeprecationWarning) as cm:
ssl.SSLContext(protocol)
self.assertEqual(
- f'{protocol!r} is deprecated',
+ f'ssl.{protocol.name} is deprecated',
str(cm.warning)
)
@@ -631,8 +632,9 @@ class BasicSocketTests(unittest.TestCase):
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
with self.assertWarns(DeprecationWarning) as cm:
ctx.minimum_version = version
+ version_text = '%s.%s' % (version.__class__.__name__, version.name)
self.assertEqual(
- f'ssl.{version!r} is deprecated',
+ f'ssl.{version_text} is deprecated',
str(cm.warning)
)
diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py
index d5e2c52..8e4e648 100644
--- a/Lib/test/test_unicode.py
+++ b/Lib/test/test_unicode.py
@@ -1490,8 +1490,10 @@ class UnicodeTest(string_tests.CommonTest,
# issue18780
import enum
class Float(float, enum.Enum):
+ # a mixed-in type will use the name for %s etc.
PI = 3.1415926
class Int(enum.IntEnum):
+ # IntEnum uses the value and not the name for %s etc.
IDES = 15
class Str(enum.StrEnum):
# StrEnum uses the value and not the name for %s etc.
@@ -1508,8 +1510,10 @@ class UnicodeTest(string_tests.CommonTest,
# formatting jobs delegated from the string implementation:
self.assertEqual('...%(foo)s...' % {'foo':Str.ABC},
'...abc...')
+ self.assertEqual('...%(foo)r...' % {'foo':Int.IDES},
+ '...<Int.IDES: 15>...')
self.assertEqual('...%(foo)s...' % {'foo':Int.IDES},
- '...IDES...')
+ '...15...')
self.assertEqual('...%(foo)i...' % {'foo':Int.IDES},
'...15...')
self.assertEqual('...%(foo)d...' % {'foo':Int.IDES},
diff --git a/Misc/NEWS.d/next/Library/2022-01-13-11-41-24.bpo-40066.1QuVli.rst b/Misc/NEWS.d/next/Library/2022-01-13-11-41-24.bpo-40066.1QuVli.rst
new file mode 100644
index 0000000..2df4878
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-01-13-11-41-24.bpo-40066.1QuVli.rst
@@ -0,0 +1,2 @@
+``IntEnum``, ``IntFlag``, and ``StrEnum`` use the mixed-in type for their
+``str()`` and ``format()`` output.