Mailing List Archive

Revert "bpo-40066: [Enum] update str() and format() output (GH-30582)" (GH-30632)
https://github.com/python/cpython/commit/42a64c03ec5c443f2a5c2ee4284622f5d1f5326c
commit: 42a64c03ec5c443f2a5c2ee4284622f5d1f5326c
branch: main
author: Victor Stinner <vstinner@python.org>
committer: vstinner <vstinner@python.org>
date: 2022-01-17T13:58:40+01:00
summary:

Revert "bpo-40066: [Enum] update str() and format() output (GH-30582)" (GH-30632)

This reverts commit acf7403f9baea3ae1119fc6b4a3298522188bf96.

files:
D Misc/NEWS.d/next/Library/2022-01-13-11-41-24.bpo-40066.1QuVli.rst
M Doc/howto/enum.rst
M Doc/library/enum.rst
M Doc/library/ssl.rst
M Lib/enum.py
M Lib/inspect.py
M Lib/plistlib.py
M Lib/re.py
M Lib/ssl.py
M Lib/test/test_enum.py
M Lib/test/test_signal.py
M Lib/test/test_socket.py
M Lib/test/test_ssl.py
M Lib/test/test_unicode.py

diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst
index fa0e2283ebc10..6c09b9925c1de 100644
--- a/Doc/howto/enum.rst
+++ b/Doc/howto/enum.rst
@@ -2,10 +2,15 @@
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.
@@ -23,14 +28,6 @@ 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.

@@ -44,14 +41,13 @@ important, but either way that value can be used to get the corresponding
member::

>>> Weekday(3)
- <Weekday.WEDNESDAY: 3>
+ Weekday.WEDNESDAY

-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::
+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::

>>> print(Weekday.THURSDAY)
- Weekday.THURSDAY
+ THURSDAY

The *type* of an enumeration member is the enum it belongs to::

@@ -101,8 +97,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()) # doctest: +SKIP
- <Weekday.TUESDAY: 2>
+ >>> Weekday.from_date(date.today())
+ Weekday.TUESDAY

Of course, if you're reading this on some other day, you'll see that day instead.

@@ -128,21 +124,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: 1>
+ Weekday.MONDAY

But :class:`Flag` also allows us to combine several members into a single
variable::

>>> weekend = Weekday.SATURDAY | Weekday.SUNDAY
>>> weekend
- <Weekday.SATURDAY|SUNDAY: 96>
+ Weekday.SATURDAY|Weekday.SUNDAY

You can even iterate over a :class:`Flag` variable::

>>> for day in weekend:
... print(day)
- Weekday.SATURDAY
- Weekday.SUNDAY
+ SATURDAY
+ SUNDAY

Okay, let's get some chores set up::

@@ -177,7 +173,6 @@ yourself some work and use :func:`auto()` for the values::

.. _enum-advanced-tutorial:

-
Programmatic access to enumeration members and their attributes
---------------------------------------------------------------

@@ -186,16 +181,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: 1>
+ Color.RED
>>> Color(3)
- <Color.BLUE: 3>
+ Color.BLUE

If you want to access enum members by *name*, use item access::

>>> Color['RED']
- <Color.RED: 1>
+ Color.RED
>>> Color['GREEN']
- <Color.GREEN: 2>
+ Color.GREEN

If you have an enum member and need its :attr:`name` or :attr:`value`::

@@ -217,7 +212,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``
@@ -232,11 +227,11 @@ By-name lookup of ``B`` will also return the member ``A``::
... ALIAS_FOR_SQUARE = 2
...
>>> Shape.SQUARE
- <Shape.SQUARE: 2>
+ Shape.SQUARE
>>> Shape.ALIAS_FOR_SQUARE
- <Shape.SQUARE: 2>
+ Shape.SQUARE
>>> Shape(2)
- <Shape.SQUARE: 2>
+ Shape.SQUARE

.. note::

@@ -304,7 +299,7 @@ Iteration
Iterating over the members of an enum does not provide the aliases::

>>> list(Shape)
- [<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]
+ [Shape.SQUARE, Shape.DIAMOND, Shape.CIRCLE]

The special attribute ``__members__`` is a read-only ordered mapping of names
to members. It includes all names defined in the enumeration, including the
@@ -313,10 +308,10 @@ aliases::
>>> for name, member in Shape.__members__.items():
... name, member
...
- ('SQUARE', <Shape.SQUARE: 2>)
- ('DIAMOND', <Shape.DIAMOND: 1>)
- ('CIRCLE', <Shape.CIRCLE: 3>)
- ('ALIAS_FOR_SQUARE', <Shape.SQUARE: 2>)
+ ('SQUARE', Shape.SQUARE)
+ ('DIAMOND', Shape.DIAMOND)
+ ('CIRCLE', Shape.CIRCLE)
+ ('ALIAS_FOR_SQUARE', Shape.SQUARE)

The ``__members__`` attribute can be used for detailed programmatic access to
the enumeration members. For example, finding all the aliases::
@@ -365,8 +360,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.
@@ -394,7 +389,7 @@ usual. If we have this enumeration::
Then::

>>> Mood.favorite_mood()
- <Mood.HAPPY: 3>
+ Mood.HAPPY
>>> Mood.HAPPY.describe()
('HAPPY', 3)
>>> str(Mood.FUNKY)
@@ -430,7 +425,7 @@ any members. So this is forbidden::
...
Traceback (most recent call last):
...
- TypeError: <enum 'MoreColor'> cannot extend <enum 'Color'>
+ TypeError: MoreColor: cannot extend enumeration 'Color'

But this is allowed::

@@ -481,9 +476,11 @@ The :class:`Enum` class is callable, providing the following functional API::
>>> Animal
<enum 'Animal'>
>>> Animal.ANT
- <Animal.ANT: 1>
+ Animal.ANT
+ >>> Animal.ANT.value
+ 1
>>> list(Animal)
- [<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>]
+ [Animal.ANT, Animal.BEE, Animal.CAT, Animal.DOG]

The semantics of this API resemble :class:`~collections.namedtuple`. The first
argument of the call to :class:`Enum` is the name of the enumeration.
@@ -628,7 +625,16 @@ 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.
+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',)``.

.. versionadded:: 3.11

@@ -639,8 +645,9 @@ 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. Like :class:`IntEnum`, :class:`IntFlag`
-members are also integers and can be used wherever an :class:`int` is used.
+: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.

.. note::

@@ -663,7 +670,7 @@ Sample :class:`IntFlag` class::
... X = 1
...
>>> Perm.R | Perm.W
- <Perm.R|W: 6>
+ Perm.R|Perm.W
>>> Perm.R + Perm.W
6
>>> RW = Perm.R | Perm.W
@@ -678,11 +685,11 @@ It is also possible to name the combinations::
... X = 1
... RWX = 7
>>> Perm.RWX
- <Perm.RWX: 7>
+ Perm.RWX
>>> ~Perm.RWX
- <Perm: 0>
+ Perm(0)
>>> Perm(7)
- <Perm.RWX: 7>
+ Perm.RWX

.. note::

@@ -695,7 +702,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

@@ -703,7 +710,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|X: 5>
+ Perm.R|Perm.X

>>> Perm.X | 8
9
@@ -719,7 +726,7 @@ be combined with them (but may lose :class:`IntFlag` membership::
:class:`IntFlag` members can also be iterated over::

>>> list(RW)
- [<Perm.R: 4>, <Perm.W: 2>]
+ [Perm.R, Perm.W]

.. versionadded:: 3.11

@@ -746,7 +753,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

@@ -760,7 +767,7 @@ while combinations of flags won't::
... WHITE = RED | BLUE | GREEN
...
>>> Color.WHITE
- <Color.WHITE: 7>
+ Color.WHITE

Giving a name to the "no flags set" condition does not change its boolean
value::
@@ -772,7 +779,7 @@ value::
... GREEN = auto()
...
>>> Color.BLACK
- <Color.BLACK: 0>
+ Color.BLACK
>>> bool(Color.BLACK)
False

@@ -780,7 +787,7 @@ value::

>>> purple = Color.RED | Color.BLUE
>>> list(purple)
- [<Color.RED: 1>, <Color.BLUE: 2>]
+ [Color.RED, Color.BLUE]

.. versionadded:: 3.11

@@ -805,16 +812,16 @@ simple to implement independently::
pass

This demonstrates how similar derived enumerations can be defined; for example
-a :class:`FloatEnum` that mixes in :class:`float` instead of :class:`int`.
+a :class:`StrEnum` that mixes in :class:`str` 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
@@ -822,18 +829,15 @@ 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 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.
+ 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.

When to use :meth:`__new__` vs. :meth:`__init__`
------------------------------------------------
@@ -862,10 +866,10 @@ want one of them to be the value::
...

>>> print(Coordinate['PY'])
- Coordinate.PY
+ PY

>>> print(Coordinate(3))
- Coordinate.VY
+ VY


Finer Points
@@ -923,8 +927,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::

@@ -945,36 +949,35 @@ 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.10`` you could access
-members from other members -- this practice was discouraged, and in ``3.11``
-:class:`Enum` returns to not allowing it::
+``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`::

>>> class FieldTypes(Enum):
... name = 0
... value = 1
... size = 2
...
- >>> FieldTypes.value.size
- Traceback (most recent call last):
- ...
- AttributeError: <enum 'FieldTypes'> member has no attribute 'size'
-
+ >>> FieldTypes.value.size # doctest: +SKIP
+ DeprecationWarning: accessing one member from another is not supported,
+ and will be disabled in 3.12
+ <FieldTypes.size: 2>

.. 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): # help(int) -> int(x, base=10) -> integer
- ... example = '11', 16 # so x='11' and base=16
- ...
- >>> MyEnum.example.value # and hex(11) is...
+ >>> class MyEnum(IntEnum):
+ ... example = '11', 16 # '11' will be interpreted as a hexadecimal
+ ... # number
+ >>> MyEnum.example.value
17


@@ -997,12 +1000,13 @@ 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,
-but not of the class::
+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::

- >>> dir(Planet) # doctest: +SKIP
- [.'EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__']
- >>> dir(Planet.EARTH) # doctest: +SKIP
+ >>> dir(Planet)
+ [.'EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__init__', '__members__', '__module__', 'surface_gravity']
+ >>> dir(Planet.EARTH)
['__class__', '__doc__', '__module__', 'mass', 'name', 'radius', 'surface_gravity', 'value']


@@ -1021,10 +1025,19 @@ are comprised of a single bit::
... CYAN = GREEN | BLUE
...
>>> Color(3) # named combination
- <Color.YELLOW: 3>
+ Color.YELLOW
>>> Color(7) # not named combination
- <Color.RED|GREEN|BLUE: 7>
+ Color.RED|Color.GREEN|Color.BLUE

+``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
""""""""""""""""""""""""""""""""
@@ -1047,16 +1060,16 @@ the following are true:
- only canonical flags are returned during iteration::

>>> list(Color.WHITE)
- [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]
+ [Color.RED, Color.GREEN, Color.BLUE]

- negating a flag or flag set returns a new flag/flag set with the
corresponding positive integer value::

>>> Color.BLUE
- <Color.BLUE: 4>
+ Color.BLUE

>>> ~Color.BLUE
- <Color.RED|GREEN: 3>
+ Color.RED|Color.GREEN

- names of pseudo-flags are constructed from their members' names::

@@ -1066,29 +1079,25 @@ the following are true:
- multi-bit flags, aka aliases, can be returned from operations::

>>> Color.RED | Color.BLUE
- <Color.PURPLE: 5>
+ Color.PURPLE

>>> Color(7) # or Color(-1)
- <Color.WHITE: 7>
+ Color.WHITE

>>> Color(0)
- <Color.BLACK: 0>
+ Color.BLACK

-- membership / containment checking: zero-valued flags are always considered
- to be contained::
+- membership / containment checking has changed slightly -- zero-valued flags
+ are never considered to be contained::

>>> Color.BLACK in Color.WHITE
- True
+ False

- otherwise, only if all bits of one flag are in the other flag will True
- be returned::
+ otherwise, if all bits of one flag are in the other flag, True is 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``:

@@ -1172,7 +1181,7 @@ Using :class:`auto` would look like::
... GREEN = auto()
...
>>> Color.GREEN
- <Color.GREEN: 3>
+ <Color.GREEN>


Using :class:`object`
@@ -1185,24 +1194,10 @@ 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
""""""""""""""""""""""""""

@@ -1214,7 +1209,9 @@ Using a string as the value would look like::
... BLUE = 'too fast!'
...
>>> Color.GREEN
- <Color.GREEN: 'go'>
+ <Color.GREEN>
+ >>> Color.GREEN.value
+ 'go'


Using a custom :meth:`__new__`
@@ -1235,7 +1232,9 @@ Using an auto-numbering :meth:`__new__` would look like::
... BLUE = ()
...
>>> Color.GREEN
- <Color.GREEN: 2>
+ <Color.GREEN>
+ >>> Color.GREEN.value
+ 2

To make a more general purpose ``AutoNumber``, add ``*args`` to the signature::

@@ -1258,7 +1257,7 @@ to handle any extra arguments::
... BLEACHED_CORAL = () # New color, no Pantone code yet!
...
>>> Swatch.SEA_GREEN
- <Swatch.SEA_GREEN: 2>
+ <Swatch.SEA_GREEN>
>>> Swatch.SEA_GREEN.pantone
'1246'
>>> Swatch.BLEACHED_CORAL.pantone
@@ -1385,9 +1384,30 @@ An example to show the :attr:`_ignore_` attribute in use::
... Period['day_%d' % i] = i
...
>>> list(Period)[:2]
- [<Period.day_0: datetime.timedelta(0)>, <Period.day_1: datetime.timedelta(days=1)>]
+ [Period.day_0, Period.day_1]
>>> list(Period)[-2:]
- [<Period.day_365: datetime.timedelta(days=365)>, <Period.day_366: datetime.timedelta(days=366)>]
+ [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


.. _enumtype-examples:
diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst
index 906c60bc3efe3..8bb19dcdf2b61 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 :keyword:`class` syntax, or by
+Enumerations are created either by using the :keyword:`class` syntax, or by
using function-call syntax::

>>> from enum import Enum
@@ -45,7 +45,7 @@ using function-call syntax::
>>> Color = Enum('Color', ['RED', 'GREEN', 'BLUE'])

-Even though we can use :keyword:`class` syntax to create Enums, Enums
+Even though we can use the :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 *members*) and are functionally constants.
+ *enumeration members* (or *enum 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,10 +110,15 @@ 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:`property`
+ :func:`global_enum`
+
+ :class:`Enum` class decorator to apply the appropriate global `__repr__`,
+ and export its members into the global name space.
+
+ :func:`.property`

Allows :class:`Enum` members to have attributes without conflicting with
- member names.
+ other members' names.

:func:`unique`

@@ -126,7 +131,7 @@ Module Contents


.. versionadded:: 3.6 ``Flag``, ``IntFlag``, ``auto``
-.. versionadded:: 3.11 ``StrEnum``, ``EnumCheck``, ``FlagBoundary``, ``property``
+.. versionadded:: 3.11 ``StrEnum``, ``EnumCheck``, ``FlagBoundary``

---------------

@@ -140,11 +145,6 @@ 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,31 +162,32 @@ Data Types
.. method:: EnumType.__dir__(cls)

Returns ``['__class__', '__doc__', '__members__', '__module__']`` and the
- names of the members in *cls*::
+ names of the members in ``cls``. User-defined methods and methods from
+ mixin classes will also be included::

>>> dir(Color)
- [.'BLUE', 'GREEN', 'RED', '__class__', '__contains__', '__doc__', '__getitem__', '__init_subclass__', '__iter__', '__len__', '__members__', '__module__', '__name__', '__qualname__']
+ ['BLUE', 'GREEN', 'RED', '__class__', '__doc__', '__members__', '__module__']

.. method:: EnumType.__getattr__(cls, name)

Returns the Enum member in *cls* matching *name*, or raises an :exc:`AttributeError`::

>>> Color.GREEN
- <Color.GREEN: 2>
+ Color.GREEN

.. method:: EnumType.__getitem__(cls, name)

- Returns the Enum member in *cls* matching *name*, or raises an :exc:`KeyError`::
+ Returns the Enum member in *cls* matching *name*, or raises a :exc:`KeyError`::

>>> Color['BLUE']
- <Color.BLUE: 3>
+ Color.BLUE

.. method:: EnumType.__iter__(cls)

Returns each member in *cls* in definition order::

>>> list(Color)
- [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 3>]
+ [Color.RED, Color.GREEN, Color.BLUE]

.. method:: EnumType.__len__(cls)

@@ -200,7 +201,7 @@ Data Types
Returns each member in *cls* in reverse definition order::

>>> list(reversed(Color))
- [<Color.BLUE: 3>, <Color.GREEN: 2>, <Color.RED: 1>]
+ [Color.BLUE, Color.GREEN, Color.RED]


.. class:: Enum
@@ -231,7 +232,7 @@ Data Types
.. attribute:: Enum._ignore_

``_ignore_`` is only used during creation and is removed from the
- enumeration once creation is complete.
+ enumeration once that 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
@@ -260,7 +261,7 @@ Data Types
.. method:: Enum.__dir__(self)

Returns ``['__class__', '__doc__', '__module__', 'name', 'value']`` and
- any public methods defined on *self.__class__*::
+ any public methods defined on ``self.__class__`` or a mixin class::

>>> from datetime import date
>>> class Weekday(Enum):
@@ -275,7 +276,7 @@ Data Types
... def today(cls):
... print('today is %s' % cls(date.today().isoweekday()).name)
>>> dir(Weekday.SATURDAY)
- ['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'today', 'value']
+ ['__class__', '__doc__', '__module__', 'name', 'today', 'value']

.. method:: Enum._generate_next_value_(name, start, count, last_values)

@@ -297,11 +298,6 @@ 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
@@ -321,55 +317,42 @@ Data Types
>>> Build.DEBUG.value
'debug'
>>> Build('deBUG')
- <Build.DEBUG: 'debug'>
+ Build.DEBUG

.. method:: Enum.__repr__(self)

Returns the string used for *repr()* calls. By default, returns the
- *Enum* name, member name, and value, but can be overridden::
+ *Enum* name and the member name, but can be overridden::

- >>> class OtherStyle(Enum):
- ... ALTERNATE = auto()
- ... OTHER = auto()
- ... SOMETHING_ELSE = auto()
+ >>> class OldStyle(Enum):
+ ... RETRO = auto()
+ ... OLD_SCHOOl = auto()
+ ... YESTERYEAR = auto()
... def __repr__(self):
... cls_name = self.__class__.__name__
- ... return f'{cls_name}.{self.name}'
- >>> OtherStyle.ALTERNATE, str(OtherStyle.ALTERNATE), f"{OtherStyle.ALTERNATE}"
- (OtherStyle.ALTERNATE, 'OtherStyle.ALTERNATE', 'OtherStyle.ALTERNATE')
+ ... return f'<{cls_name}.{self.name}: {self.value}>'
+ >>> OldStyle.RETRO
+ <OldStyle.RETRO: 1>

.. method:: Enum.__str__(self)

Returns the string used for *str()* calls. By default, returns the
- *Enum* name and member name, but can be overridden::
+ member name, but can be overridden::

- >>> class OtherStyle(Enum):
- ... ALTERNATE = auto()
- ... OTHER = auto()
- ... SOMETHING_ELSE = auto()
+ >>> class OldStyle(Enum):
+ ... RETRO = auto()
+ ... OLD_SCHOOl = auto()
+ ... YESTERYEAR = auto()
... def __str__(self):
- ... 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')
+ ... cls_name = self.__class__.__name__
+ ... return f'{cls_name}.{self.name}'
+ >>> OldStyle.RETRO
+ OldStyle.RETRO

- .. 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
@@ -384,7 +367,7 @@ Data Types
... TWO = 2
... THREE = 3
>>> Numbers.THREE
- <Numbers.THREE: 3>
+ Numbers.THREE
>>> Numbers.ONE + Numbers.TWO
3
>>> Numbers.THREE + 5
@@ -392,14 +375,10 @@ Data Types
>>> Numbers.THREE == 3
True

- .. note::
+.. note::

- 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.
+ Using :class:`auto` with :class:`IntEnum` results in integers of increasing value,
+ starting with ``1``.


.. class:: StrEnum
@@ -413,16 +392,13 @@ Data Types
instead of ``isinstance(str, unknown)``), and in those locations you
will need to use ``str(StrEnum.member)``.

- .. note::

- Using :class:`auto` with :class:`StrEnum` results in the lower-cased member
- name as the value.
+.. note::

- .. 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.
+ Using :class:`auto` with :class:`StrEnum` results in values of the member name,
+ lower-cased.

- .. versionadded:: 3.11
+.. versionadded:: 3.11

.. class:: Flag

@@ -455,9 +431,9 @@ Data Types
Returns all contained members::

>>> list(Color.RED)
- [<Color.RED: 1>]
+ [Color.RED]
>>> list(purple)
- [<Color.RED: 1>, <Color.BLUE: 4>]
+ [Color.RED, Color.BLUE]

.. method:: __len__(self):

@@ -485,52 +461,42 @@ Data Types
Returns current flag binary or'ed with other::

>>> Color.RED | Color.GREEN
- <Color.RED|GREEN: 3>
+ Color.RED|Color.GREEN

.. method:: __and__(self, other)

Returns current flag binary and'ed with other::

>>> purple & white
- <Color.RED|BLUE: 5>
+ Color.RED|Color.BLUE
>>> purple & Color.GREEN
- <Color: 0>
+ 0x0

.. method:: __xor__(self, other)

Returns current flag binary xor'ed with other::

>>> purple ^ white
- <Color.GREEN: 2>
+ Color.GREEN
>>> purple ^ Color.GREEN
- <Color.RED|GREEN|BLUE: 7>
+ Color.RED|Color.GREEN|Color.BLUE

.. method:: __invert__(self):

Returns all the flags in *type(self)* that are not in self::

>>> ~white
- <Color: 0>
+ 0x0
>>> ~purple
- <Color.GREEN: 2>
+ Color.GREEN
>>> ~Color.RED
- <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::
+ Color.GREEN|Color.BLUE

- Using :class:`auto` with :class:`Flag` results in integers that are powers
- of two, starting with ``1``.
+.. note::

- .. versionchanged:: 3.11 The *repr()* of zero-valued flags has changed. It
- is now::
+ Using :class:`auto` with :class:`Flag` results in integers that are powers
+ of two, starting with ``1``.

- >>> Color(0)
- <Color: 0>

.. class:: IntFlag

@@ -543,9 +509,9 @@ Data Types
... GREEN = auto()
... BLUE = auto()
>>> Color.RED & 2
- <Color: 0>
+ 0x0
>>> Color.RED | 2
- <Color.RED|GREEN: 3>
+ Color.RED|Color.GREEN

If any integer operation is performed with an *IntFlag* member, the result is
not an *IntFlag*::
@@ -558,25 +524,15 @@ 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

- 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.
+.. note::

+ 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:`ValueError`.
+ various constraints; failed constraints result in a :exc:`TypeError`.

.. attribute:: UNIQUE

@@ -626,11 +582,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

@@ -650,7 +606,7 @@ Data Types
>>> StrictFlag(2**2 + 2**4)
Traceback (most recent call last):
...
- ValueError: <flag 'StrictFlag'> invalid value 20
+ ValueError: StrictFlag: invalid value: 20
given 0b0 10100
allowed 0b0 00111

@@ -665,7 +621,7 @@ Data Types
... GREEN = auto()
... BLUE = auto()
>>> ConformFlag(2**2 + 2**4)
- <ConformFlag.BLUE: 4>
+ ConformFlag.BLUE

.. attribute:: EJECT

@@ -691,52 +647,12 @@ Data Types
... GREEN = auto()
... BLUE = auto()
>>> KeepFlag(2**2 + 2**4)
- <KeepFlag.BLUE|16: 20>
+ KeepFlag.BLUE|0x10

.. 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
------------------------

@@ -752,6 +668,15 @@ 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
@@ -763,7 +688,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

@@ -789,7 +714,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

---------------

@@ -801,20 +726,14 @@ 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:

- - ``__str__`` uses the value and not the name of the enum member
+ - ``format()`` will use the value of the enum member, unless ``__str__``
+ has been overridden

- - ``__format__``, because it uses ``__str__``, will also use the value of
- the enum member instead of its name
+ - ``StrEnum.__str__`` uses the value and not the name of the enum member

- 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::
+ If you do not need/want those limitations, you can 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 4d8488a4a28de..eb33d7e1778a7 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
- <VerifyFlags.VERIFY_X509_TRUSTED_FIRST: 32768>
+ ssl.VERIFY_X509_TRUSTED_FIRST

.. 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
- <VerifyMode.CERT_REQUIRED: 2>
+ ssl.CERT_REQUIRED

.. index:: single: certificates

diff --git a/Lib/enum.py b/Lib/enum.py
index 772e1eac0e1e6..93ea1bea36db7 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', 'ReprEnum',
+ 'Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag',
'auto', 'unique', 'property', 'verify',
'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP',
- 'global_flag_repr', 'global_enum_repr', 'global_str', 'global_enum',
+ 'global_flag_repr', 'global_enum_repr', 'global_enum',
'EnumCheck', 'CONTINUOUS', 'NAMED_FLAGS', 'UNIQUE',
]

@@ -18,7 +18,7 @@
# 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 = _stdlib_enums = ReprEnum = None
+Enum = Flag = EJECT = None

def _is_descriptor(obj):
"""
@@ -116,9 +116,9 @@ def bin(num, max_bits=None):

ceiling = 2 ** (num).bit_length()
if num >= 0:
- s = bltns.bin(num + ceiling).replace('1', '0', 1)
+ s = _bltin_bin(num + ceiling).replace('1', '0', 1)
else:
- s = bltns.bin(~num ^ (ceiling - 1) + ceiling)
+ s = _bltin_bin(~num ^ (ceiling - 1) + ceiling)
sign = s[:3]
digits = s[3:]
if max_bits is not None:
@@ -126,19 +126,6 @@ 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:
@@ -162,12 +149,22 @@ def __get__(self, instance, ownerclass=None):
return ownerclass._member_map_[self.name]
except KeyError:
raise AttributeError(
- '%r has no attribute %r' % (ownerclass, self.name)
+ '%s: no class attribute %r' % (ownerclass.__name__, 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(
- '%r member has no attribute %r' % (ownerclass, self.name)
+ '%s: no instance attribute %r' % (ownerclass.__name__, self.name)
)
else:
return self.fget(instance)
@@ -175,7 +172,7 @@ def __get__(self, instance, ownerclass=None):
def __set__(self, instance, value):
if self.fset is None:
raise AttributeError(
- "<enum %r> cannot set attribute %r" % (self.clsname, self.name)
+ "%s: cannot set instance attribute %r" % (self.clsname, self.name)
)
else:
return self.fset(instance, value)
@@ -183,7 +180,7 @@ def __set__(self, instance, value):
def __delete__(self, instance):
if self.fdel is None:
raise AttributeError(
- "<enum %r> cannot delete attribute %r" % (self.clsname, self.name)
+ "%s: cannot delete instance attribute %r" % (self.clsname, self.name)
)
else:
return self.fdel(instance)
@@ -331,7 +328,7 @@ def __setitem__(self, key, value):
elif _is_sunder(key):
if key not in (
'_order_',
- '_generate_next_value_', '_numeric_repr_', '_missing_', '_ignore_',
+ '_generate_next_value_', '_missing_', '_ignore_',
'_iter_member_', '_iter_member_by_value_', '_iter_member_by_def_',
):
raise ValueError(
@@ -361,13 +358,13 @@ def __setitem__(self, key, value):
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(
@@ -398,7 +395,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
@@ -416,10 +413,9 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k
# 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:
@@ -431,8 +427,8 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k
# check for illegal enum names (any others?)
invalid_names = set(member_names) & {'mro', ''}
if invalid_names:
- raise ValueError('invalid enum member name(s) '.format(
- ','.join(repr(n) for n in invalid_names)))
+ raise ValueError('Invalid enum member name: {0}'.format(
+ ','.join(invalid_names)))
#
# adjust the sunders
_order_ = classdict.pop('_order_', None)
@@ -462,8 +458,6 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k
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_'] = (
@@ -473,6 +467,10 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k
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)
@@ -483,140 +481,18 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k
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 not in classdict:
- setattr(enum_class, name, getattr(first_enum, name))
+ 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)
#
# replace any other __new__ with our own (as long as Enum is not None,
# anyway) -- again, this is to support pickle
@@ -687,13 +563,13 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k
# _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__(cls):
+ def __bool__(self):
"""
classes/types should always be True.
"""
@@ -738,13 +614,6 @@ def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, s
)

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(
@@ -762,33 +631,60 @@ def __delattr__(cls, attr):
# nicer error message when someone tries to delete an attribute
# (see issue19025).
if attr in cls._member_map_:
- raise AttributeError("%r cannot delete member %r." % (cls.__name__, attr))
+ raise AttributeError("%s: cannot delete Enum member %r." % (cls.__name__, attr))
super().__delattr__(attr)

- 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 __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 __getattr__(cls, name):
"""
@@ -807,24 +703,18 @@ def __getattr__(cls, name):
raise AttributeError(name) from None

def __getitem__(cls, name):
- """
- Return the member matching `name`.
- """
return cls._member_map_[name]

def __iter__(cls):
"""
- Return members in definition order.
+ Returns 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_)

- @bltns.property
+ @_bltin_property
def __members__(cls):
"""
Returns a mapping of member name->value.
@@ -842,7 +732,7 @@ def __repr__(cls):

def __reversed__(cls):
"""
- Return members in reverse definition order.
+ Returns members in reverse definition order.
"""
return (cls._member_map_[name] for name in reversed(cls._member_names_))

@@ -856,7 +746,7 @@ def __setattr__(cls, name, value):
"""
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):
@@ -911,7 +801,8 @@ def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, s

return metacls.__new__(metacls, class_name, bases, classdict, boundary=boundary)

- def _convert_(cls, name, module, filter, source=None, *, boundary=None, as_global=False):
+ def _convert_(cls, name, module, filter, source=None, *, boundary=None):
+
"""
Create a new Enum subclass that replaces a collection of global constants
"""
@@ -943,25 +834,22 @@ def _convert_(cls, name, module, filter, source=None, *, boundary=None, as_globa
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
- if as_global:
- global_enum(cls)
- else:
- sys.modules[cls.__module__].__dict__.update(cls.__members__)
+ global_enum(cls)
module_globals[name] = cls
return cls

- @classmethod
- def _check_for_existing_members_(mcls, class_name, bases):
+ @staticmethod
+ def _check_for_existing_members(class_name, bases):
for chain in bases:
for base in chain.__mro__:
if issubclass(base, Enum) and base._member_names_:
raise TypeError(
- "<enum %r> cannot extend %r"
- % (class_name, base)
+ "%s: cannot extend enumeration %r"
+ % (class_name, base.__name__)
)

@classmethod
- def _get_mixins_(mcls, class_name, bases):
+ def _get_mixins_(cls, class_name, bases):
"""
Returns the type for creating enum members, and the first inherited
enum class.
@@ -971,7 +859,30 @@ def _get_mixins_(mcls, class_name, bases):
if not bases:
return object, Enum

- mcls._check_for_existing_members_(class_name, bases)
+ 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

# ensure final parent class is an Enum derivative, find any concrete
# data type, and check that Enum has no members
@@ -979,51 +890,12 @@ def _get_mixins_(mcls, class_name, bases):
if not issubclass(first_enum, Enum):
raise TypeError("new enumerations should be created as "
"`EnumName([mixin_type, ...] [data_type,] enum_type)`")
- member_type = mcls._find_data_type_(class_name, bases) or object
+ cls._check_for_existing_members(class_name, bases)
+ member_type = _find_data_type(bases) or object
return 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):
+ @staticmethod
+ def _find_new_(classdict, member_type, first_enum):
"""
Returns the __new__ to be used for creating the enum members.

@@ -1071,42 +943,9 @@ def _find_new_(mcls, classdict, member_type, first_enum):

class Enum(metaclass=EnumType):
"""
- 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>
+ Generic enumeration.

- - 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.
+ Derive from this class to define new enumerations.
"""

def __new__(cls, value):
@@ -1160,9 +999,6 @@ def __new__(cls, value):
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.
@@ -1185,44 +1021,47 @@ def _missing_(cls, value):
return None

def __repr__(self):
- v_repr = self.__class__._value_repr_ or self._value_.__class__.__repr__
- return "<%s.%s: %s>" % (self.__class__.__name__, self._name_, v_repr(self._value_))
+ return "%s.%s" % ( self.__class__.__name__, self._name_)

def __str__(self):
- return "%s.%s" % (self.__class__.__name__, self._name_, )
+ return "%s" % (self._name_, )

def __dir__(self):
"""
Returns all members and all public methods
"""
- 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
+ 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)

def __format__(self, format_spec):
- return str.__format__(str(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)

def __hash__(self):
return hash(self._name_)
@@ -1249,25 +1088,34 @@ def value(self):
return self._value_


-class ReprEnum(Enum):
+class IntEnum(int, Enum):
"""
- Only changes the repr(), leaving str() and format() to the mixed-in type.
+ Enum where members are also (and must be) ints
"""

+ def __str__(self):
+ return "%s" % (self._name_, )

-class IntEnum(int, ReprEnum):
- """
- Enum where members are also (and must be) ints
- """
+ 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 StrEnum(str, ReprEnum):
+class StrEnum(str, Enum):
"""
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:
@@ -1287,6 +1135,10 @@ def __new__(cls, *values):
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.
@@ -1317,8 +1169,6 @@ 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.
@@ -1334,7 +1184,7 @@ def _generate_next_value_(name, start, count, last_values):
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
@@ -1382,8 +1232,8 @@ def _missing_(cls, value):
if cls._boundary_ is STRICT:
max_bits = max(value.bit_length(), flag_mask.bit_length())
raise ValueError(
- "%r invalid value %r\n given %s\n allowed %s" % (
- cls, value, bin(value, max_bits), bin(flag_mask, max_bits),
+ "%s: invalid value: %r\n given %s\n allowed %s" % (
+ cls.__name__, value, bin(value, max_bits), bin(flag_mask, max_bits),
))
elif cls._boundary_ is CONFORM:
value = value & flag_mask
@@ -1397,7 +1247,7 @@ def _missing_(cls, value):
)
else:
raise ValueError(
- '%r unknown flag boundary %r' % (cls, cls._boundary_, )
+ 'unknown flag boundary: %r' % (cls._boundary_, )
)
if value < 0:
neg_value = value
@@ -1424,7 +1274,7 @@ def _missing_(cls, value):
m._name_ for m in cls._iter_member_(member_value)
])
if unknown:
- pseudo_member._name_ += '|%s' % cls._numeric_repr_(unknown)
+ pseudo_member._name_ += '|0x%x' % unknown
else:
pseudo_member._name_ = None
# use setdefault in case another thread already created a composite
@@ -1442,8 +1292,10 @@ def __contains__(self, other):
"""
if not isinstance(other, self.__class__):
raise TypeError(
- "unsupported operand type(s) for 'in': %r and %r" % (
+ "unsupported operand type(s) for 'in': '%s' and '%s'" % (
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):
@@ -1457,18 +1309,27 @@ def __len__(self):

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 "<%s: %s>" % (cls_name, v_repr(self._value_))
+ 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('|'))
else:
- return "<%s.%s: %s>" % (cls_name, self._name_, v_repr(self._value_))
+ 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)

def __str__(self):
- cls_name = self.__class__.__name__
+ cls = self.__class__
if self._name_ is None:
- return '%s(%r)' % (cls_name, self._value_)
+ return '%s(%x)' % (cls.__name__, self._value_)
else:
- return "%s.%s" % (cls_name, self._name_)
+ return self._name_

def __bool__(self):
return bool(self._value_)
@@ -1501,11 +1362,20 @@ def __invert__(self):
return self._inverted_


-class IntFlag(int, ReprEnum, Flag, boundary=EJECT):
+class IntFlag(int, 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__):
@@ -1542,7 +1412,6 @@ def __xor__(self, other):
__rxor__ = __xor__
__invert__ = Flag.__invert__

-
def _high_bit(value):
"""
returns index of highest bit, or -1 if value is zero or negative
@@ -1587,7 +1456,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(%r)" % (module, cls_name, self._value_)
+ return "%s.%s(0x%x)" % (module, cls_name, self._value_)
if _is_single_bit(self):
return '%s.%s' % (module, self._name_)
if self._boundary_ is not FlagBoundary.KEEP:
@@ -1595,22 +1464,14 @@ def global_flag_repr(self):
else:
name = []
for n in self._name_.split('|'):
- if n[0].isdigit():
+ if n.startswith('0'):
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, update_str=False):
+def global_enum(cls):
"""
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
@@ -1620,8 +1481,6 @@ def global_enum(cls, update_str=False):
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

@@ -1663,7 +1522,6 @@ def convert_class(cls):
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
@@ -1685,8 +1543,13 @@ def convert_class(cls):
# it
enum_class = type(cls_name, (etype, ), body, boundary=boundary, _simple=True)
for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'):
- if name not in body:
- setattr(enum_class, name, getattr(etype, name))
+ 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)
gnv_last_values = []
if issubclass(enum_class, Flag):
# Flag / IntFlag
@@ -1897,8 +1760,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_', '__doc__'):
- # keys known to be different, or very long
+ if key in ('__module__', '_member_map_', '_value2member_map_'):
+ # keys known to be different
continue
elif key in member_names:
# members are checked below
@@ -2019,5 +1882,3 @@ 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 8236698b8de0f..5d33f0d445fb9 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -2567,21 +2567,15 @@ class _empty:


class _ParameterKind(enum.IntEnum):
- 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
+ POSITIONAL_ONLY = 0
+ POSITIONAL_OR_KEYWORD = 1
+ VAR_POSITIONAL = 2
+ KEYWORD_ONLY = 3
+ VAR_KEYWORD = 4

- def __str__(self):
- return self.name
+ @property
+ def description(self):
+ return _PARAM_NAME_MAPPING[self]

_POSITIONAL_ONLY = _ParameterKind.POSITIONAL_ONLY
_POSITIONAL_OR_KEYWORD = _ParameterKind.POSITIONAL_OR_KEYWORD
@@ -2589,6 +2583,14 @@ def __str__(self):
_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 4862355b2252c..3ab71edc320af 100644
--- a/Lib/plistlib.py
+++ b/Lib/plistlib.py
@@ -61,8 +61,7 @@
from xml.parsers.expat import ParserCreate


-PlistFormat = enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__)
-globals().update(PlistFormat.__members__)
+PlistFormat = enum.global_enum(enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__))


class UID:
diff --git a/Lib/re.py b/Lib/re.py
index a7ab9b3706748..ea41217ce08c2 100644
--- a/Lib/re.py
+++ b/Lib/re.py
@@ -155,8 +155,6 @@ 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 dafb70a67864c..207925166efa3 100644
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -119,6 +119,7 @@
)
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 a0953fb960f33..43f98c1c1efb6 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -6,18 +6,15 @@
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, ReprEnum
+from enum import verify, UNIQUE, CONTINUOUS, NAMED_FLAGS
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]
@@ -110,12 +107,6 @@ 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
@@ -125,36 +116,21 @@ class foo:
setattr(obj, attr, 1)
self.assertTrue(enum._is_descriptor(obj))

- 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)
+ def test_is_sunder(self):
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_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)
+ def test_is_dunder(self):
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:
@@ -190,658 +166,473 @@ class HeadlightsC(IntFlag, boundary=enum.CONFORM):

# tests

-class _EnumTests:
- """
- Test for behavior that is the same across the different types of enumerations.
- """
-
- values = None
+class TestEnum(unittest.TestCase):

def setUp(self):
- 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
-
- def assertFormatIsValue(self, spec, member):
- self.assertEqual(spec.format(member), spec.format(member.value))
-
- def assertFormatIsStr(self, spec, member):
- self.assertEqual(spec.format(member), spec.format(str(member)))
-
- 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
-
- 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 Season(Enum):
+ SPRING = 1
+ SUMMER = 2
+ AUTUMN = 3
+ WINTER = 4
+ self.Season = Season

- 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 Konstants(float, Enum):
+ E = 2.7182818
+ PI = 3.1415926
+ TAU = 2 * PI
+ self.Konstants = Konstants

- def test_changing_member_fails(self):
- MainEnum = self.MainEnum
- with self.assertRaises(AttributeError):
- self.MainEnum.second = 'really first'
+ class Grades(IntEnum):
+ A = 5
+ B = 4
+ C = 3
+ D = 2
+ F = 0
+ self.Grades = Grades

- @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 Directional(str, Enum):
+ EAST = 'east'
+ WEST = 'west'
+ NORTH = 'north'
+ SOUTH = 'south'
+ self.Directional = Directional

- @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)
+ from datetime import date
+ class Holiday(date, Enum):
+ NEW_YEAR = 2013, 1, 1
+ IDES_OF_MARCH = 2013, 3, 15
+ self.Holiday = Holiday

- def test_dir_on_class(self):
- TE = self.MainEnum
- self.assertEqual(set(dir(TE)), set(enum_dir(TE)))
+ class DateEnum(date, Enum): pass
+ self.DateEnum = DateEnum

- def test_dir_on_item(self):
- TE = self.MainEnum
- self.assertEqual(set(dir(TE.first)), set(member_dir(TE.first)))
+ class FloatEnum(float, Enum): pass
+ self.FloatEnum = FloatEnum

- def test_dir_with_added_behavior(self):
- class Test(self.enum_type):
- this = auto()
- these = auto()
+ 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)
- self.assertTrue('wowser' not in dir(Test))
- self.assertTrue('wowser' in dir(Test.this))
+ @classmethod
+ def classmethod_wowser(cls): pass
+ @staticmethod
+ def staticmethod_wowser(): pass
+ self.FloatWowser = FloatWowser
+
+ 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
+
+ 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_sub_with_behavior_on_super(self):
# see issue22506
- class SuperEnum(self.enum_type):
+ class SuperEnum1(Enum):
def invisible(self):
return "did you see me?"
- class SubEnum(SuperEnum):
- sample = auto()
- self.assertTrue('invisible' not in dir(SubEnum))
- self.assertTrue('invisible' in dir(SubEnum.sample))
+ class SubEnum1(SuperEnum1):
+ sample = 5
+ self.SubEnum1 = SubEnum1

- 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'
+ class SuperEnum2(IntEnum):
+ def __new__(cls, value, description=""):
+ obj = int.__new__(cls, value)
+ obj._value_ = value
+ obj.description = description
return obj
- 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))
-
- def test_enum_in_enum_out(self):
- Main = self.MainEnum
- self.assertIs(Main(Main.first), Main.first)
-
- 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())
+ 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
+ ))

- 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)))
+ 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_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)))
+ 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_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,
+ first_enum_base = next(
+ base for base in enum_cls.__mro__
+ if is_from_enum_module(base)
)
- 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))

- 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))
+ 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_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)
+ def test_dir_on_sub_with_behavior_on_super(self):
+ # see issue22506
self.assertEqual(
- [MinorEnum.june, MinorEnum.july, MinorEnum.august],
- lst,
+ set(dir(self.SubEnum1.sample)),
+ set(['__class__', '__doc__', '__module__', 'name', 'value', 'invisible']),
)
- 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_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_dir_on_sub_with_behavior_including_instance_dict_on_super(self):
+ # see issue40084
+ self.assertTrue({'description'} <= set(dir(self.SubEnum2.sample)))

- 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_in_enum_out(self):
+ Season = self.Season
+ self.assertIs(Season(Season.WINTER), Season.WINTER)

- 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_enum_value(self):
+ Season = self.Season
+ self.assertEqual(Season.SPRING.value, 1)

- 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_intenum_value(self):
+ self.assertEqual(IntStooges.CURLY.value, 2)

- def test_reversed_iteration_order(self):
+ def test_enum(self):
+ Season = self.Season
+ lst = list(Season)
+ self.assertEqual(len(lst), len(Season))
+ self.assertEqual(len(Season), 4, Season)
self.assertEqual(
- list(reversed(self.MainEnum)),
- [self.MainEnum.third, self.MainEnum.second, self.MainEnum.first],
- )
-
-class _PlainOutputTests:
-
- 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", '%s %r' % (NF.__str__, NF.first))
- self.assertEqual(format(NF.first), "FIRST")
-
- 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)
+ [Season.SPRING, Season.SUMMER, Season.AUTUMN, Season.WINTER], lst)

+ 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 _MixedOutputTests:
-
- 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
-
-
-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]
+ def test_changing_member(self):
+ Season = self.Season
+ with self.assertRaises(AttributeError):
+ Season.WINTER = 'really cold'

+ def test_attribute_deletion(self):
+ class Season(Enum):
+ SPRING = 1
+ SUMMER = 2
+ AUTUMN = 3
+ WINTER = 4

-class TestMinimalFloat(_EnumTests, _MinimalOutputTests, unittest.TestCase):
+ def spam(cls):
+ pass

- values = [4.4, 5.5, 6.6]
+ self.assertTrue(hasattr(Season, 'spam'))
+ del Season.spam
+ self.assertFalse(hasattr(Season, 'spam'))

- class enum_type(float, ReprEnum):
- def _generate_next_value_(name, start, count, last_values):
- values = [4.4, 5.5, 6.6]
- return values[count]
+ 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 TestSpecial(unittest.TestCase):
- """
- various operations that are not attributable to every possible enum
- """
+ def test_bool_of_member(self):
+ class Count(Enum):
+ zero = 0
+ one = 1
+ two = 2
+ for member in Count:
+ self.assertTrue(bool(member))

- 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_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

def test_bool(self):
# plain Enum members are always True
@@ -865,56 +656,92 @@ class IntLogic(int, Enum):
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_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_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_duplicate_name_error(self):
+ def test_duplicate_name(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
@@ -922,45 +749,232 @@ class Color(Enum):
blue = 3
def red(self):
return 'red'
- #
+
with self.assertRaises(TypeError):
class Color(Enum):
- @enum.property
+ @property
def red(self):
return 'redder'
red = 1
green = 2
blue = 3

- def test_enum_function_with_qualname(self):
- if isinstance(Theory, Exception):
- raise Theory
- self.assertEqual(Theory.__qualname__, 'spanish_inquisition')
+ def test_reserved__sunder_(self):
+ with self.assertRaisesRegex(
+ ValueError,
+ '_sunder_ names, such as ._bad_., are reserved',
+ ):
+ class Bad(Enum):
+ _bad_ = 1

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'
@@ -976,7 +990,7 @@ class MyOtherEnum(SillyInt, enum.Enum):
test_pickle_dump_load(self.assertIs, MyOtherEnum.E)
test_pickle_dump_load(self.assertIs, MyOtherEnum)
#
- # This did not work in 3.10, but does now with pickling by name
+ # This did not work in 3.9, but does now with pickling by name
class UnBrokenInt(int):
__qualname__ = 'UnBrokenInt'
def __new__(cls, value):
@@ -993,124 +1007,6 @@ class MyUnBrokenEnum(UnBrokenInt, Enum):
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):
@@ -1126,6 +1022,122 @@ def repr(self):
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):
@@ -1157,7 +1169,12 @@ def test_pickle_enum_function_with_module(self):
test_pickle_dump_load(self.assertIs, Question.who)
test_pickle_dump_load(self.assertIs, Question)

- def test_pickle_nested_class(self):
+ 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):
# would normally just have this directly in the class namespace
class NestedEnum(Enum):
twigs = 'common'
@@ -1175,7 +1192,7 @@ class ReplaceGlobalInt(IntEnum):
for proto in range(HIGHEST_PROTOCOL):
self.assertEqual(ReplaceGlobalInt.TWO.__reduce_ex__(proto), 'TWO')

- def test_pickle_explodes(self):
+ def test_exploding_pickle(self):
BadPickle = Enum(
'BadPickle', 'dill sweet bread-n-butter', module=__name__)
globals()['BadPickle'] = BadPickle
@@ -1184,37 +1201,216 @@ def test_pickle_explodes(self):
test_pickle_exception(self.assertRaises, TypeError, BadPickle.dill)
test_pickle_exception(self.assertRaises, PicklingError, BadPickle)

- def test_string_enum(self):
- class SkillLevel(str, Enum):
- master = 'what is the sound of one hand clapping?'
- journeyman = 'why did the chicken cross the road?'
- apprentice = 'knock, knock!'
- self.assertEqual(SkillLevel.apprentice, 'knock, knock!')
+ def test_string_enum(self):
+ class SkillLevel(str, Enum):
+ master = 'what is the sound of one hand clapping?'
+ journeyman = 'why did the chicken cross the road?'
+ apprentice = 'knock, knock!'
+ self.assertEqual(SkillLevel.apprentice, 'knock, knock!')
+
+ def test_getattr_getitem(self):
+ class Period(Enum):
+ morning = 1
+ noon = 2
+ evening = 3
+ night = 4
+ self.assertIs(Period(2), Period.noon)
+ self.assertIs(getattr(Period, 'night'), Period.night)
+ self.assertIs(Period['morning'], Period.morning)
+
+ def test_getattr_dunder(self):
+ Season = self.Season
+ self.assertTrue(getattr(Season, '__eq__'))
+
+ def test_iteration_order(self):
+ class Season(Enum):
+ SUMMER = 2
+ WINTER = 4
+ AUTUMN = 3
+ SPRING = 1
+ self.assertEqual(
+ list(Season),
+ [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_getattr_getitem(self):
- class Period(Enum):
- morning = 1
- noon = 2
- evening = 3
- night = 4
- self.assertIs(Period(2), Period.noon)
- self.assertIs(getattr(Period, 'night'), Period.night)
- self.assertIs(Period['morning'], Period.morning)
+ 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_getattr_dunder(self):
- Season = self.Season
- self.assertTrue(getattr(Season, '__eq__'))
+ 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_iteration_order(self):
- class Season(Enum):
- SUMMER = 2
- WINTER = 4
- AUTUMN = 3
- SPRING = 1
+ 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(
- list(Season),
- [Season.SUMMER, Season.WINTER, Season.AUTUMN, Season.SPRING],
+ [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):
@@ -1229,18 +1425,15 @@ class Color(Enum):
red = 1
green = 2
blue = 3
- #
with self.assertRaises(TypeError):
class MoreColor(Color):
cyan = 4
magenta = 5
yellow = 6
- #
- with self.assertRaisesRegex(TypeError, "<enum .EvenMoreColor.> cannot extend <enum .Color.>"):
+ with self.assertRaisesRegex(TypeError, "EvenMoreColor: cannot extend enumeration 'Color'"):
class EvenMoreColor(Color, IntEnum):
chartruese = 7
- #
- with self.assertRaisesRegex(TypeError, "<enum .Foo.> cannot extend <enum .Color.>"):
+ with self.assertRaisesRegex(TypeError, "Foo: cannot extend enumeration 'Color'"):
Color('Foo', ('pink', 'black'))

def test_exclude_methods(self):
@@ -1344,7 +1537,27 @@ class Color(Enum):
with self.assertRaises(KeyError):
Color['chartreuse']

- # tests that need to be evalualted for moving
+ 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.")

def test_multiple_mixin_mro(self):
class auto_enum(type(Enum)):
@@ -1397,7 +1610,7 @@ def __new__(cls, *args):
return self
def __getnewargs__(self):
return self._args
- @bltns.property
+ @property
def __name__(self):
return self._intname
def __repr__(self):
@@ -1457,7 +1670,7 @@ def __new__(cls, *args):
return self
def __getnewargs_ex__(self):
return self._args, {}
- @bltns.property
+ @property
def __name__(self):
return self._intname
def __repr__(self):
@@ -1517,7 +1730,7 @@ def __new__(cls, *args):
return self
def __reduce__(self):
return self.__class__, self._args
- @bltns.property
+ @property
def __name__(self):
return self._intname
def __repr__(self):
@@ -1577,7 +1790,7 @@ def __new__(cls, *args):
return self
def __reduce_ex__(self, proto):
return self.__class__, self._args
- @bltns.property
+ @property
def __name__(self):
return self._intname
def __repr__(self):
@@ -1634,7 +1847,7 @@ def __new__(cls, *args):
self._intname = name
self._args = _args
return self
- @bltns.property
+ @property
def __name__(self):
return self._intname
def __repr__(self):
@@ -1689,7 +1902,7 @@ def __new__(cls, *args):
self._intname = name
self._args = _args
return self
- @bltns.property
+ @property
def __name__(self):
return self._intname
def __repr__(self):
@@ -1878,7 +2091,6 @@ def test(self):
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):
@@ -1886,7 +2098,6 @@ def flash(self):
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):
@@ -1923,7 +2134,7 @@ class Planet(Enum):
def __init__(self, mass, radius):
self.mass = mass # in kilograms
self.radius = radius # in meters
- @enum.property
+ @property
def surface_gravity(self):
# universal gravitational constant (m3 kg-1 s-2)
G = 6.67300E-11
@@ -1993,7 +2204,90 @@ class LabelledList(LabelledIntEnum):
self.assertEqual(LabelledList.unprocessed, 1)
self.assertEqual(LabelledList(1), LabelledList.unprocessed)

- def test_default_missing_no_chained_exception(self):
+ 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):
class Color(Enum):
RED = 1
GREEN = 2
@@ -2005,7 +2299,7 @@ class Color(Enum):
else:
raise Exception('Exception not raised.')

- def test_missing_override(self):
+ def test_missing(self):
class Color(Enum):
red = 1
green = 2
@@ -2069,9 +2363,9 @@ def __init__(self):
class_1_ref = weakref.ref(Class1())
class_2_ref = weakref.ref(Class2())
#
- # 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.
+ # The exception raised by Enum creates a reference loop and thus
+ # Class2 instances will stick around until the next garbage collection
+ # cycle, unlike Class1.
gc.collect() # For PyPy or other GCs.
self.assertIs(class_1_ref(), None)
self.assertIs(class_2_ref(), None)
@@ -2102,12 +2396,11 @@ class Color(MaxMixin, Enum):
self.assertEqual(Color.GREEN.value, 2)
self.assertEqual(Color.BLUE.value, 3)
self.assertEqual(Color.MAX, 3)
- self.assertEqual(str(Color.BLUE), 'Color.BLUE')
+ self.assertEqual(str(Color.BLUE), '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)
@@ -2117,7 +2410,6 @@ class Color(StrMixin, MaxMixin, 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)
@@ -2127,7 +2419,6 @@ class CoolColor(StrMixin, SomeEnum, Enum):
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)
@@ -2137,7 +2428,6 @@ class CoolerColor(StrMixin, AnotherEnum, Enum):
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)
@@ -2148,7 +2438,6 @@ class CoolestColor(StrMixin, SomeEnum, AnotherEnum):
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)
@@ -2159,7 +2448,6 @@ class ConfusedColor(StrMixin, AnotherEnum, SomeEnum):
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)
@@ -2170,7 +2458,6 @@ class ReformedColor(StrMixin, IntEnum, SomeEnum, AnotherEnum):
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)
@@ -2203,12 +2490,11 @@ def __repr__(self):
return hex(self)

class MyIntEnum(HexMixin, MyInt, enum.Enum):
- __repr__ = HexMixin.__repr__
+ pass

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):
@@ -2220,7 +2506,7 @@ def __new__(cls, value):
return member
self.assertEqual(Fee.TEST, 2)

- def test_multiple_mixin_with_common_data_type(self):
+ def test_miltuple_mixin_with_common_data_type(self):
class CaseInsensitiveStrEnum(str, Enum):
@classmethod
def _missing_(cls, value):
@@ -2240,7 +2526,7 @@ def _missing_(cls, value):
unknown._value_ = value
cls._member_map_[value] = unknown
return unknown
- @enum.property
+ @property
def valid(self):
return self._valid
#
@@ -2284,7 +2570,7 @@ class GoodStrEnum(StrEnum):
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: '1'>")
+ self.assertEqual(repr(GoodStrEnum.one), 'GoodStrEnum.one')
#
class DumbMixin:
def __str__(self):
@@ -2293,7 +2579,6 @@ class DumbStrEnum(DumbMixin, StrEnum):
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")
#
@@ -2335,6 +2620,74 @@ class ThirdFailedStrEnum(StrEnum):
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
@@ -2344,9 +2697,9 @@ class OkayEnum(CustomStrEnum):
three = b'3', 'ascii'
four = b'4', 'latin1', 'strict'
self.assertEqual(OkayEnum.one, '1')
- self.assertEqual(str(OkayEnum.one), 'OkayEnum.one')
- self.assertEqual('{}'.format(OkayEnum.one), 'OkayEnum.one')
- self.assertEqual(repr(OkayEnum.one), "<OkayEnum.one: '1'>")
+ self.assertEqual(str(OkayEnum.one), 'one')
+ self.assertEqual('{}'.format(OkayEnum.one), 'one')
+ self.assertEqual(repr(OkayEnum.one), 'OkayEnum.one')
#
class DumbMixin:
def __str__(self):
@@ -2355,7 +2708,6 @@ class DumbStrEnum(DumbMixin, CustomStrEnum):
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")
#
@@ -2365,7 +2717,7 @@ def hello(self):
class HelloEnum(EnumMixin, CustomStrEnum):
eight = '8'
self.assertEqual(HelloEnum.eight, '8')
- self.assertEqual(str(HelloEnum.eight), 'HelloEnum.eight')
+ self.assertEqual(str(HelloEnum.eight), 'eight')
#
class GoodbyeMixin:
def goodbye(self):
@@ -2373,7 +2725,7 @@ def goodbye(self):
class GoodbyeEnum(GoodbyeMixin, EnumMixin, CustomStrEnum):
nine = '9'
self.assertEqual(GoodbyeEnum.nine, '9')
- self.assertEqual(str(GoodbyeEnum.nine), 'GoodbyeEnum.nine')
+ self.assertEqual(str(GoodbyeEnum.nine), 'nine')
#
class FirstFailedStrEnum(CustomStrEnum):
one = 1 # this will become '1'
@@ -2419,6 +2771,21 @@ def __repr__(self):
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):
@@ -2427,13 +2794,35 @@ class Private(Enum):
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, "<enum .Di.> member has no attribute .NO."):
+ with self.assertRaisesRegex(AttributeError, "Di: no instance 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):
#
@@ -2450,7 +2839,7 @@ def upper(self):
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
@@ -2462,42 +2851,8 @@ class FooBar(Enum):
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):
@@ -2559,7 +2914,7 @@ class Color(Enum):
verde = green


-class OldTestFlag(unittest.TestCase):
+class TestFlag(unittest.TestCase):
"""Tests of the Flags."""

class Perm(Flag):
@@ -2582,6 +2937,65 @@ class Color(Flag):
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:
@@ -2674,7 +3088,7 @@ class Bizarre(Flag, boundary=KEEP):
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)
@@ -2883,7 +3297,7 @@ class Color(Flag):
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()
@@ -2918,12 +3332,11 @@ class Color(AllMixin, Flag):
self.assertEqual(Color.GREEN.value, 2)
self.assertEqual(Color.BLUE.value, 4)
self.assertEqual(Color.ALL.value, 7)
- self.assertEqual(str(Color.BLUE), 'Color.BLUE')
+ self.assertEqual(str(Color.BLUE), '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)
@@ -2933,7 +3346,6 @@ class Color(StrMixin, AllMixin, 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)
@@ -3014,8 +3426,21 @@ class NeverEnum(WhereEnum):
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 OldTestIntFlag(unittest.TestCase):
+class TestIntFlag(unittest.TestCase):
"""Tests of the IntFlags."""

class Perm(IntFlag):
@@ -3060,6 +3485,73 @@ def test_type(self):
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)),
@@ -3067,11 +3559,11 @@ def test_global_repr_keep(self):
)
self.assertEqual(
repr(HeadlightsK(2**0 + 2**2 + 2**3)),
- '%(m)s.LOW_BEAM_K|%(m)s.FOG_K|8' % {'m': SHORT_MODULE},
+ '%(m)s.LOW_BEAM_K|%(m)s.FOG_K|0x8' % {'m': SHORT_MODULE},
)
self.assertEqual(
repr(HeadlightsK(2**3)),
- '%(m)s.HeadlightsK(8)' % {'m': SHORT_MODULE},
+ '%(m)s.HeadlightsK(0x8)' % {'m': SHORT_MODULE},
)

def test_global_repr_conform1(self):
@@ -3213,7 +3705,7 @@ class Bizarre(IntFlag, boundary=KEEP):
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)
@@ -3450,12 +3942,11 @@ class Color(AllMixin, IntFlag):
self.assertEqual(Color.GREEN.value, 2)
self.assertEqual(Color.BLUE.value, 4)
self.assertEqual(Color.ALL.value, 7)
- self.assertEqual(str(Color.BLUE), '4')
+ self.assertEqual(str(Color.BLUE), 'BLUE')
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)
@@ -3465,7 +3956,6 @@ class Color(StrMixin, AllMixin, 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)
@@ -3510,6 +4000,19 @@ def cycle_enum():
'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):

@@ -3726,89 +4229,6 @@ def test_is_private(self):
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

@@ -3818,35 +4238,7 @@ class TestEnumTypeSubclassing(unittest.TestCase):
class Color(enum.Enum)
| Color(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)
|\x20\x20
- | 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.
+ | An enumeration.
|\x20\x20
| Method resolution order:
| Color
@@ -3855,11 +4247,11 @@ class Color(enum.Enum)
|\x20\x20
| Data and other attributes defined here:
|\x20\x20
- | CYAN = <Color.CYAN: 1>
+ | blue = Color.blue
|\x20\x20
- | MAGENTA = <Color.MAGENTA: 2>
+ | green = Color.green
|\x20\x20
- | YELLOW = <Color.YELLOW: 3>
+ | red = Color.red
|\x20\x20
| ----------------------------------------------------------------------
| Data descriptors inherited from enum.Enum:
@@ -3871,25 +4263,6 @@ 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__
@@ -3911,11 +4284,11 @@ class Color(enum.Enum)
|\x20\x20
| Data and other attributes defined here:
|\x20\x20
- | YELLOW = <Color.YELLOW: 3>
+ | blue = Color.blue
|\x20\x20
- | MAGENTA = <Color.MAGENTA: 2>
+ | green = Color.green
|\x20\x20
- | CYAN = <Color.CYAN: 1>
+ | red = Color.red
|\x20\x20
| ----------------------------------------------------------------------
| Data descriptors inherited from enum.Enum:
@@ -3934,9 +4307,9 @@ class TestStdLib(unittest.TestCase):
maxDiff = None

class Color(Enum):
- CYAN = 1
- MAGENTA = 2
- YELLOW = 3
+ red = 1
+ green = 2
+ blue = 3

def test_pydoc(self):
# indirectly test __objclass__
@@ -3948,34 +4321,24 @@ def test_pydoc(self):
helper = pydoc.Helper(output=output)
helper(self.Color)
result = output.getvalue().strip()
- self.assertEqual(result, expected_text, result)
+ self.assertEqual(result, expected_text)

def test_inspect_getmembers(self):
values = dict((
('__class__', EnumType),
- ('__doc__', '...'),
+ ('__doc__', 'An enumeration.'),
('__members__', self.Color.__members__),
('__module__', __name__),
- ('YELLOW', self.Color.YELLOW),
- ('MAGENTA', self.Color.MAGENTA),
- ('CYAN', self.Color.CYAN),
+ ('blue', self.Color.blue),
+ ('green', self.Color.green),
('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' %
@@ -3990,42 +4353,23 @@ def test_inspect_classify_class_attrs(self):
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='...'),
- 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__),
+ defining_class=self.Color, object='An enumeration.'),
Attribute(name='__members__', kind='property',
defining_class=EnumType, object=EnumType.__members__),
Attribute(name='__module__', kind='data',
defining_class=self.Color, object=__name__),
- 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='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=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)
@@ -4035,15 +4379,7 @@ def test_inspect_classify_class_attrs(self):
)
failed = False
for v, r in zip(values, result):
- 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:
+ if r != v:
print('\n%s\n%s\n%s\n%s\n' % ('=' * 75, r, v, '=' * 75), sep='')
failed = True
if failed:
@@ -4052,15 +4388,15 @@ def test_inspect_classify_class_attrs(self):
def test_test_simple_enum(self):
@_simple_enum(Enum)
class SimpleColor:
- CYAN = 1
- MAGENTA = 2
- YELLOW = 3
+ RED = 1
+ GREEN = 2
+ BLUE = 3
class CheckedColor(Enum):
- CYAN = 1
- MAGENTA = 2
- YELLOW = 3
+ RED = 1
+ GREEN = 2
+ BLUE = 3
self.assertTrue(_test_simple_enum(CheckedColor, SimpleColor) is None)
- SimpleColor.MAGENTA._value_ = 9
+ SimpleColor.GREEN._value_ = 9
self.assertRaisesRegex(
TypeError, "enum mismatch",
_test_simple_enum, CheckedColor, SimpleColor,
@@ -4086,165 +4422,9 @@ class Missing:


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.
@@ -4262,10 +4442,6 @@ class Quadruple(Enum):
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
@@ -4277,40 +4453,32 @@ class Quadruple(Enum):

class _ModuleWrapper:
"""We use this class as a namespace for swapping modules."""
+
def __init__(self, module):
self.__dict__.update(module.__dict__)

-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
-
+class TestIntEnumConvert(unittest.TestCase):
def test_convert_value_lookup_priority(self):
- test_type = enum.IntEnum._convert_(
- 'UnittestConvert',
- MODULE,
- filter=lambda x: x.startswith('CONVERT_TEST_'))
+ 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_'))
# 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_int(self):
- test_type = enum.IntEnum._convert_(
- 'UnittestConvert',
- MODULE,
- filter=lambda x: x.startswith('CONVERT_TEST_'))
+ 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_'))
# 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)
@@ -4319,57 +4487,43 @@ def test_convert_int(self):
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.
- 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.',
- )
+ 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.')

def test_convert_uncomparable(self):
- uncomp = enum.Enum._convert_(
- 'Uncomparable',
- MODULE,
- filter=lambda x: x.startswith('UNCOMPARABLE_'))
+ # 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_'))
+
# Should be ordered by `name` only:
self.assertEqual(
list(uncomp),
[uncomp.UNCOMPARABLE_A, uncomp.UNCOMPARABLE_B, uncomp.UNCOMPARABLE_C],
- )
+ )

def test_convert_complex(self):
- uncomp = enum.Enum._convert_(
- 'Uncomparable',
- MODULE,
- filter=lambda x: x.startswith('COMPLEX_'))
+ with support.swap_item(
+ sys.modules, MODULE, _ModuleWrapper(sys.modules[MODULE]),
+ ):
+ 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):
@@ -4379,58 +4533,50 @@ def test_convert_raise(self):
filter=lambda x: x.startswith('CONVERT_TEST_'))

def test_convert_repr_and_str(self):
- test_type = enum.IntEnum._convert_(
- 'UnittestConvert',
- MODULE,
- filter=lambda x: x.startswith('CONVERT_STRING_TEST_'),
- as_global=True)
+ 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_'))
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), '5')
+ self.assertEqual(str(test_type.CONVERT_STRING_TEST_NAME_A), 'CONVERT_STRING_TEST_NAME_A')
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'

-# helpers
-
-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)
+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.')

-missing = object()
+ 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')


if __name__ == '__main__':
diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py
index ac4626d0c456e..3f0e7270eb26f 100644
--- a/Lib/test/test_signal.py
+++ b/Lib/test/test_signal.py
@@ -908,7 +908,7 @@ def handler(signum, frame):

%s

- blocked = %s
+ blocked = %r
signum = signal.SIGALRM

# child: block and wait the signal
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index 56cc23dbbbf4e..394d2942483fb 100755
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -1517,11 +1517,9 @@ def testGetaddrinfo(self):
infos = socket.getaddrinfo(HOST, 80, socket.AF_INET, socket.SOCK_STREAM)
for family, type, _, _, _ in infos:
self.assertEqual(family, socket.AF_INET)
- self.assertEqual(repr(family), '<AddressFamily.AF_INET: 2>')
- self.assertEqual(str(family), '2')
+ self.assertEqual(str(family), 'AF_INET')
self.assertEqual(type, socket.SOCK_STREAM)
- self.assertEqual(repr(type), '<SocketKind.SOCK_STREAM: 1>')
- self.assertEqual(str(type), '1')
+ self.assertEqual(str(type), 'SOCK_STREAM')
infos = socket.getaddrinfo(HOST, None, 0, socket.SOCK_STREAM)
for _, socktype, _, _, _ in infos:
self.assertEqual(socktype, socket.SOCK_STREAM)
@@ -1795,10 +1793,8 @@ def test_str_for_enums(self):
# 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(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')
+ self.assertEqual(str(s.family), 'AF_INET')
+ self.assertEqual(str(s.type), 'SOCK_STREAM')

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 64f4bce7f7781..f99a3e8da95f8 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -373,8 +373,7 @@ def test_str_for_enums(self):
# Make sure that the PROTOCOL_* constants have enum-like string
# reprs.
proto = ssl.PROTOCOL_TLS_CLIENT
- self.assertEqual(repr(proto), '<_SSLMethod.PROTOCOL_TLS_CLIENT: 16>')
- self.assertEqual(str(proto), '16')
+ self.assertEqual(str(proto), 'PROTOCOL_TLS_CLIENT')
ctx = ssl.SSLContext(proto)
self.assertIs(ctx.protocol, proto)

@@ -623,7 +622,7 @@ def test_openssl111_deprecations(self):
with self.assertWarns(DeprecationWarning) as cm:
ssl.SSLContext(protocol)
self.assertEqual(
- f'ssl.{protocol.name} is deprecated',
+ f'{protocol!r} is deprecated',
str(cm.warning)
)

@@ -632,9 +631,8 @@ def test_openssl111_deprecations(self):
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_text} is deprecated',
+ f'ssl.{version!r} is deprecated',
str(cm.warning)
)

diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py
index 8e4e64808b688..d5e2c5266aae7 100644
--- a/Lib/test/test_unicode.py
+++ b/Lib/test/test_unicode.py
@@ -1490,10 +1490,8 @@ def test_formatting_with_enum(self):
# 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.
@@ -1510,10 +1508,8 @@ class Str(enum.StrEnum):
# 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},
- '...15...')
+ '...IDES...')
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
deleted file mode 100644
index 2df487855785e..0000000000000
--- a/Misc/NEWS.d/next/Library/2022-01-13-11-41-24.bpo-40066.1QuVli.rst
+++ /dev/null
@@ -1,2 +0,0 @@
-``IntEnum``, ``IntFlag``, and ``StrEnum`` use the mixed-in type for their
-``str()`` and ``format()`` output.

_______________________________________________
Python-checkins mailing list
Python-checkins@python.org
https://mail.python.org/mailman/listinfo/python-checkins