2021-06-30 23:16:24 -04:00
|
|
|
PEP: 663
|
2021-11-02 23:34:53 -04:00
|
|
|
Title: Standardizing Enum str(), repr(), and format() behaviors
|
2021-06-30 23:16:24 -04:00
|
|
|
Version: $Revision$
|
|
|
|
Last-Modified: $Date$
|
|
|
|
Author: Ethan Furman <ethan@stoneleaf.us>
|
|
|
|
Discussions-To: python-dev@python.org
|
2021-11-18 01:02:37 -05:00
|
|
|
Status: Rejected
|
2021-06-30 23:16:24 -04:00
|
|
|
Type: Informational
|
|
|
|
Content-Type: text/x-rst
|
2021-11-05 23:01:01 -04:00
|
|
|
Created: 30-Jun-2021
|
2021-07-03 22:32:17 -04:00
|
|
|
Python-Version: 3.11
|
2021-11-02 23:40:52 -04:00
|
|
|
Post-History: 20-Jul-2021, 02-Nov-2021
|
2021-11-18 01:02:37 -05:00
|
|
|
Resolution: https://mail.python.org/archives/list/python-dev@python.org/message/RN3WCRZSTQR55DOHJTZ2KIO6CZPJPCU7/
|
2021-06-30 23:16:24 -04:00
|
|
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
========
|
|
|
|
|
2021-09-13 15:19:29 -04:00
|
|
|
Update the ``repr()``, ``str()``, and ``format()`` of the various Enum types
|
2021-11-02 23:34:53 -04:00
|
|
|
to better match their intended purpose. For example, ``IntEnum`` will have
|
|
|
|
its ``str()`` change to match its ``format()``, while a user-mixed int-enum
|
|
|
|
will have its ``format()`` match its ``str()``. In all cases, an enum's
|
|
|
|
``str()`` and ``format()`` will be the same (unless the user overrides
|
|
|
|
``format()``).
|
|
|
|
|
|
|
|
Add a global enum decorator which changes the ``str()`` and ``repr()`` (and
|
2021-11-02 23:40:52 -04:00
|
|
|
``format()``) of the decorated enum to be a valid global reference: i.e.
|
2021-11-02 23:34:53 -04:00
|
|
|
``re.IGNORECASE`` instead of ``<RegexFlag.IGNORECASE: 2>``.
|
2021-06-30 23:16:24 -04:00
|
|
|
|
|
|
|
|
|
|
|
Motivation
|
|
|
|
==========
|
|
|
|
|
|
|
|
Having the ``str()`` of ``IntEnum`` and ``IntFlag`` not be the value causes
|
2021-07-21 00:10:30 -04:00
|
|
|
bugs and extra work when replacing existing constants.
|
2021-06-30 23:16:24 -04:00
|
|
|
|
|
|
|
Having the ``str()`` and ``format()`` of an enum member be different can be
|
|
|
|
confusing.
|
|
|
|
|
2021-11-02 23:34:53 -04:00
|
|
|
The addition of ``StrEnum`` with its requirement to have its ``str()`` be its
|
|
|
|
``value`` is inconsistent with other provided Enum's ``str``.
|
|
|
|
|
2021-07-03 22:32:17 -04:00
|
|
|
The iteration of ``Flag`` members, which directly affects their ``repr()``, is
|
|
|
|
inelegant at best, and buggy at worst.
|
2021-06-30 23:16:24 -04:00
|
|
|
|
|
|
|
|
|
|
|
Rationale
|
|
|
|
=========
|
|
|
|
|
|
|
|
Enums are becoming more common in the standard library; being able to recognize
|
|
|
|
enum members by their ``repr()``, and having that ``repr()`` be easy to parse, is
|
|
|
|
useful and can save time and effort in understanding and debugging code.
|
|
|
|
|
2021-07-13 15:19:58 -04:00
|
|
|
However, the enums with mixed-in data types (``IntEnum``, ``IntFlag``, and the new
|
|
|
|
``StrEnum``) need to be more backwards compatible with the constants they are
|
2021-06-30 23:16:24 -04:00
|
|
|
replacing -- specifically, ``str(replacement_enum_member) == str(original_constant)``
|
|
|
|
should be true (and the same for ``format()``).
|
|
|
|
|
|
|
|
IntEnum, IntFlag, and StrEnum should be as close to a drop-in replacement of
|
|
|
|
existing integer and string constants as is possible. Towards that goal, the
|
2021-11-02 23:34:53 -04:00
|
|
|
``str()`` output of each should be its inherent value; e.g. if ``Color`` is an
|
2021-07-21 14:19:43 -04:00
|
|
|
``IntEnum``::
|
2021-06-30 23:16:24 -04:00
|
|
|
|
|
|
|
>>> Color.RED
|
|
|
|
<Color.RED: 1>
|
|
|
|
>>> str(Color.RED)
|
2021-07-21 14:19:43 -04:00
|
|
|
'1'
|
2021-07-02 03:43:30 -04:00
|
|
|
>>> format(Color.RED)
|
2021-06-30 23:16:24 -04:00
|
|
|
'1'
|
|
|
|
|
2021-11-02 23:34:53 -04:00
|
|
|
Note that ``format()`` already produces the correct output, only ``str()`` needs
|
2021-06-30 23:16:24 -04:00
|
|
|
updating.
|
|
|
|
|
2021-10-18 20:04:45 -04:00
|
|
|
As much as possible, the ``str()``, ``repr()``, and ``format()`` of enum members
|
2021-11-02 23:34:53 -04:00
|
|
|
should be standardized across the standard library. However, up to Python 3.10
|
|
|
|
several enums in the standard library have a custom ``str()`` and/or ``repr()``.
|
2021-06-30 23:16:24 -04:00
|
|
|
|
2021-11-02 23:34:53 -04:00
|
|
|
The ``repr()`` of Flag currently includes aliases, which it should not; fixing that
|
2021-07-21 00:10:30 -04:00
|
|
|
will, of course, already change its ``repr()`` in certain cases.
|
2021-06-30 23:16:24 -04:00
|
|
|
|
|
|
|
|
|
|
|
Specification
|
|
|
|
=============
|
|
|
|
|
2021-11-05 23:01:01 -04:00
|
|
|
There are three broad categories of enum usage:
|
2021-06-30 23:16:24 -04:00
|
|
|
|
2021-11-02 23:34:53 -04:00
|
|
|
- simple: ``Enum`` or ``Flag``
|
2021-09-13 16:18:38 -04:00
|
|
|
a new enum class is created with no data type mixins
|
2021-06-30 23:16:24 -04:00
|
|
|
|
2021-11-02 23:34:53 -04:00
|
|
|
- drop-in replacement: ``IntEnum``, ``IntFlag``, ``StrEnum``
|
2021-06-30 23:16:24 -04:00
|
|
|
a new enum class is created which also subclasses ``int`` or ``str`` and uses
|
2021-09-13 16:18:38 -04:00
|
|
|
``int.__str__`` or ``str.__str__``
|
2021-06-30 23:16:24 -04:00
|
|
|
|
|
|
|
- user-mixed enums and flags
|
|
|
|
the user creates their own integer-, float-, str-, whatever-enums instead of
|
|
|
|
using enum.IntEnum, etc.
|
|
|
|
|
2021-09-13 16:18:38 -04:00
|
|
|
There are also two styles:
|
|
|
|
|
|
|
|
- normal: the enumeration members remain in their classes and are accessed as
|
|
|
|
``classname.membername``, and the class name shows in their ``repr()`` and
|
|
|
|
``str()`` (where appropriate)
|
|
|
|
|
|
|
|
- global: the enumeration members are copied into their module's global
|
2021-11-02 23:34:53 -04:00
|
|
|
namespace, and their module name shows in their ``repr()`` and ``str()``
|
2021-09-13 16:18:38 -04:00
|
|
|
(where appropriate)
|
|
|
|
|
2021-09-13 16:46:41 -04:00
|
|
|
Some sample enums::
|
2021-06-30 23:16:24 -04:00
|
|
|
|
|
|
|
# module: tools.py
|
|
|
|
|
|
|
|
class Hue(Enum): # or IntEnum
|
|
|
|
LIGHT = -1
|
|
|
|
NORMAL = 0
|
|
|
|
DARK = +1
|
|
|
|
|
|
|
|
class Color(Flag): # or IntFlag
|
|
|
|
RED = 1
|
|
|
|
GREEN = 2
|
|
|
|
BLUE = 4
|
|
|
|
|
|
|
|
class Grey(int, Enum): # or (int, Flag)
|
|
|
|
BLACK = 0
|
|
|
|
WHITE = 1
|
|
|
|
|
2021-10-22 08:04:44 -04:00
|
|
|
Using the above enumerations, the following two tables show the old and new
|
2021-11-02 23:34:53 -04:00
|
|
|
output (blank cells indicate no change):
|
2021-09-13 16:18:38 -04:00
|
|
|
|
2021-10-22 08:04:44 -04:00
|
|
|
+--------+------------------------+-----------------+------------+-----------------------+
|
|
|
|
| style | category | enum repr() | enum str() | enum format() |
|
|
|
|
+--------+-------------+----------+-----------------+------------+-----------------------+
|
|
|
|
| normal | simple | 3.10 | | | |
|
|
|
|
| | +----------+-----------------+------------+-----------------------+
|
|
|
|
| | | new | | | |
|
|
|
|
| +-------------+----------+-----------------+------------+-----------------------+
|
|
|
|
| | user mixed | 3.10 | | | 1 |
|
|
|
|
| | +----------+-----------------+------------+-----------------------+
|
|
|
|
| | | new | | | Grey.WHITE |
|
|
|
|
| +-------------+----------+-----------------+------------+-----------------------+
|
|
|
|
| | int drop-in | 3.10 | | Hue.LIGHT | |
|
|
|
|
| | +----------+-----------------+------------+-----------------------+
|
|
|
|
| | | new | | -1 | |
|
|
|
|
+--------+-------------+----------+-----------------+------------+-----------------------+
|
|
|
|
| global | simple | 3.10 | <Hue.LIGHT: -1> | Hue.LIGHT | Hue.LIGHT |
|
|
|
|
| | +----------+-----------------+------------+-----------------------+
|
|
|
|
| | | new | tools.LIGHT | LIGHT | LIGHT |
|
|
|
|
| +-------------+----------+-----------------+------------+-----------------------+
|
|
|
|
| | user mixed | 3.10 | <Grey.WHITE: 1 | Grey.WHITE | Grey.WHITE |
|
|
|
|
| | +----------+-----------------+------------+-----------------------+
|
|
|
|
| | | new | tools.WHITE | WHITE | WHITE |
|
|
|
|
| +-------------+----------+-----------------+------------+-----------------------+
|
|
|
|
| | int drop-in | 3.10 | <Hue.LIGHT: -1> | Hue.LIGHT | |
|
|
|
|
| | +----------+-----------------+------------+-----------------------+
|
|
|
|
| | | new | tools.LIGHT | -1 | |
|
|
|
|
+--------+-------------+----------+-----------------+------------+-----------------------+
|
|
|
|
|
|
|
|
+--------+------------------------+-----------------------+------------------------+-----------------------+
|
|
|
|
| style | category | flag repr() | flag str() | flag format() |
|
|
|
|
+--------+-------------+----------+-----------------------+------------------------+-----------------------+
|
|
|
|
| normal | simple | 3.10 | <Color.RED|GREEN: 3> | Color.RED|GREEN | Color.RED|GREEN |
|
|
|
|
| | +----------+-----------------------+------------------------+-----------------------+
|
|
|
|
| | | new | <Color(3): RED|GREEN> | Color.RED|Color.GREEN | Color.RED|Color.GREEN |
|
|
|
|
| +-------------+----------+-----------------------+------------------------+-----------------------+
|
|
|
|
| | user mixed | 3.10 | <Grey.WHITE: 1> | | 1 |
|
|
|
|
| | +----------+-----------------------+------------------------+-----------------------+
|
|
|
|
| | | new | <Grey(1): WHITE> | | Grey.WHITE |
|
|
|
|
| +-------------+----------+-----------------------+------------------------+-----------------------+
|
|
|
|
| | int drop-in | 3.10 | <Color.RED|GREEN: 3> | Color.RED|GREEN | |
|
|
|
|
| | +----------+-----------------------+------------------------+-----------------------+
|
|
|
|
| | | new | <Color(3): RED|GREEN> | 3 | |
|
|
|
|
+--------+-------------+----------+-----------------------+------------------------+-----------------------+
|
|
|
|
| global | simple | 3.10 | <Color.RED|GREEN: 3> | Color.RED|GREEN | Color.RED|GREEN |
|
|
|
|
| | +----------+-----------------------+------------------------+-----------------------+
|
|
|
|
| | | new | tools.RED|tools.GREEN | RED|GREEN | RED|GREEN |
|
|
|
|
| +-------------+----------+-----------------------+------------------------+-----------------------+
|
|
|
|
| | user mixed | 3.10 | <Grey.WHITE: 1> | Grey.WHITE | 1 |
|
|
|
|
| | +----------+-----------------------+------------------------+-----------------------+
|
|
|
|
| | | new | tools.WHITE | WHITE | WHITE |
|
|
|
|
| +-------------+----------+-----------------------+------------------------+-----------------------+
|
|
|
|
| | int drop-in | 3.10 | <Color.RED|GREEN: 3> | Color.RED|GREEN | |
|
|
|
|
| | +----------+-----------------------+------------------------+-----------------------+
|
|
|
|
| | | new | tools.RED|tools.GREEN | 3 | |
|
|
|
|
+--------+-------------+----------+-----------------------+------------------------+-----------------------+
|
|
|
|
|
|
|
|
These two tables show the final result:
|
|
|
|
|
|
|
|
+--------+-------------+-----------------+------------+-----------------------+
|
|
|
|
| style | category | enum repr() | enum str() | enum format() |
|
|
|
|
+--------+-------------+-----------------+------------+-----------------------+
|
|
|
|
| normal | simple | <Hue.LIGHT: -1> | Hue.LIGHT | Hue.LIGHT |
|
|
|
|
| +-------------+-----------------+------------+-----------------------+
|
|
|
|
| | user mixed | <Grey.WHITE: 1> | Grey.WHITE | Grey.WHITE |
|
|
|
|
| +-------------+-----------------+------------+-----------------------+
|
|
|
|
| | int drop-in | <Hue.LIGHT: -1> | -1 | -1 |
|
|
|
|
+--------+-------------+-----------------+------------+-----------------------+
|
|
|
|
| global | simple | tools.LIGHT | LIGHT | LIGHT |
|
|
|
|
| +-------------+-----------------+------------+-----------------------+
|
|
|
|
| | user mixed | tools.WHITE | WHITE | WHITE |
|
|
|
|
| +-------------+-----------------+------------+-----------------------+
|
|
|
|
| | int drop-in | tools.LIGHT | -1 | -1 |
|
|
|
|
+--------+-------------+-----------------+------------+-----------------------+
|
|
|
|
|
|
|
|
+--------+-------------+-----------------------+------------------------+-----------------------+
|
|
|
|
| style | category | flag repr() | flag str() | flag format() |
|
|
|
|
+--------+-------------+-----------------------+------------------------+-----------------------+
|
|
|
|
| normal | simple | <Color(3): RED|GREEN> | Color.RED|Color.GREEN | Color.RED|Color.GREEN |
|
|
|
|
| +-------------+-----------------------+------------------------+-----------------------+
|
|
|
|
| | user mixed | <Grey(1): WHITE> | Grey.WHITE | Grey.WHITE |
|
|
|
|
| +-------------+-----------------------+------------------------+-----------------------+
|
|
|
|
| | int drop-in | <Color(3): RED|GREEN> | 3 | 3 |
|
|
|
|
+--------+-------------+-----------------------+------------------------+-----------------------+
|
|
|
|
| global | simple | tools.RED|tools.GREEN | RED|GREEN | RED|GREEN |
|
|
|
|
| +-------------+-----------------------+------------------------+-----------------------+
|
|
|
|
| | user mixed | tools.WHITE | WHITE | WHITE |
|
|
|
|
| +-------------+-----------------------+------------------------+-----------------------+
|
|
|
|
| | int drop-in | tools.RED|tools.GREEN | 3 | 3 |
|
|
|
|
+--------+-------------+-----------------------+------------------------+-----------------------+
|
2021-06-30 23:16:24 -04:00
|
|
|
|
2021-07-13 15:19:58 -04:00
|
|
|
As can be seen, ``repr()`` is primarily affected by whether the members are
|
2021-06-30 23:16:24 -04:00
|
|
|
global, while ``str()`` is affected by being global or by being a drop-in
|
|
|
|
replacement, with the drop-in replacement status having a higher priority.
|
|
|
|
Also, the basic ``repr()`` and ``str()`` have changed for flags as the old
|
2021-11-02 23:34:53 -04:00
|
|
|
style was flawed.
|
2021-06-30 23:16:24 -04:00
|
|
|
|
|
|
|
|
|
|
|
Backwards Compatibility
|
|
|
|
=======================
|
|
|
|
|
2021-09-11 02:01:12 -04:00
|
|
|
Backwards compatibility of stringified objects is not guaranteed across major
|
2021-09-11 01:43:42 -04:00
|
|
|
Python versions, and there will be backwards compatibility breaks where
|
|
|
|
software uses the ``repr()``, ``str()``, and ``format()`` output of enums in
|
|
|
|
tests, documentation, data structures, and/or code generation.
|
|
|
|
|
|
|
|
Normal usage of enum members will not change: ``re.ASCII`` can still be used
|
2021-09-13 16:46:41 -04:00
|
|
|
as ``re.ASCII`` and will still compare equal to ``256``.
|
2021-09-11 01:43:42 -04:00
|
|
|
|
|
|
|
If the previous output needs to be maintained, for example to ensure
|
2021-09-17 14:18:24 -04:00
|
|
|
compatibility between different Python versions, software projects will need to
|
2021-09-11 01:43:42 -04:00
|
|
|
create their own enum base class with the appropriate methods overridden.
|
2021-06-30 23:16:24 -04:00
|
|
|
|
|
|
|
Note that by changing the ``str()`` of the drop-in category, we will actually
|
|
|
|
prevent future breakage when ``IntEnum``, et al, are used to replace existing
|
|
|
|
constants.
|
|
|
|
|
|
|
|
|
|
|
|
Copyright
|
|
|
|
=========
|
|
|
|
|
|
|
|
This document is placed in the public domain or under the
|
|
|
|
CC0-1.0-Universal license, whichever is more permissive.
|