python-peps/pep-0663.txt

198 lines
11 KiB
Plaintext
Raw Normal View History

PEP: 663
Title: Improving and Standardizing Enum str(), repr(), and format() behaviors
Version: $Revision$
Last-Modified: $Date$
Author: Ethan Furman <ethan@stoneleaf.us>
Discussions-To: python-dev@python.org
Status: Draft
Type: Informational
Content-Type: text/x-rst
Created: 23-Feb-2013
2021-07-03 22:32:17 -04:00
Python-Version: 3.11
Post-History: 20-Jul-2021
Resolution:
Abstract
========
Update the ``repr()``, ``str()``, and ``format()`` of the various Enum types
for consistency and to better match their intended purpose.
Motivation
==========
2021-07-21 00:10:30 -04:00
The addition of ``StrEnum`` with its requirement to have its ``str()`` be its
``value`` is inconsistent with other provided Enum's ``str``.
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.
Having the ``str()`` and ``format()`` of an enum member be different can be
confusing.
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.
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
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
str() output of each should be its inherent value; e.g. if ``Color`` is an
``IntEnum``::
>>> Color.RED
<Color.RED: 1>
>>> str(Color.RED)
'1'
2021-07-02 03:43:30 -04:00
>>> format(Color.RED)
'1'
Note that format() already produces the correct output in 3.10, only str() needs
updating.
2021-10-18 20:04:45 -04:00
As much as possible, the ``str()``, ``repr()``, and ``format()`` of enum members
should be standardized across the standard library.
2021-07-21 00:10:30 -04:00
The repr() of Flag currently includes aliases, which it should not; fixing that
will, of course, already change its ``repr()`` in certain cases.
Specification
=============
There a three broad categories of enum usage:
2021-09-13 16:18:38 -04:00
- simple: Enum or Flag
a new enum class is created with no data type mixins
- drop-in replacement: IntEnum, IntFlag, StrEnum
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__``
- 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
namespcae, and their module name shows in their ``repr()`` and ``str()``
(where appropriate)
2021-09-13 16:46:41 -04:00
Some sample enums::
# 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
Using the above enumerations, the following table shows the old and new
2021-09-13 16:46:41 -04:00
behavior (blank cells indicate no behavioral change):
2021-09-13 16:18:38 -04:00
2021-09-13 16:46:41 -04:00
+--------+------------------------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
2021-09-13 16:18:38 -04:00
| style | category | enum repr() | enum str() | enum format() | 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 |
2021-09-13 16:46:41 -04:00
| +-------------+----------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
2021-09-13 16:18:38 -04:00
| | user mixed | 3.10 | | | 1 | <Grey.WHITE: 1> | | 1 |
| | +----------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
| | | new | | | Grey.WHITE | <Grey(1): WHITE> | | Grey.WHITE |
2021-09-13 16:46:41 -04:00
| +-------------+----------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
2021-09-13 16:18:38 -04:00
| | int drop-in | 3.10 | | Hue.LIGHT | | <Color.RED|GREEN: 3> | Color.RED|GREEN | |
| | +----------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
| | | new | | -1 | | <Color(3): RED|GREEN> | 3 | |
2021-09-13 16:46:41 -04:00
+--------+-------------+----------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
2021-09-13 16:18:38 -04:00
| global | simple | 3.10 | <Hue.LIGHT: -1> | Hue.LIGHT | Hue.LIGHT | <Color.RED|GREEN: 3> | Color.RED|GREEN | Color.RED|GREEN |
| | +----------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
| | | new | tools.LIGHT | LIGHT | LIGHT | tools.RED|tools.GREEN | RED|GREEN | RED|GREEN |
2021-09-13 16:46:41 -04:00
| +-------------+----------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
2021-09-13 16:18:38 -04:00
| | user mixed | 3.10 | <Grey.WHITE: 1 | Grey.WHITE | Grey.WHITE | <Grey.WHITE: 1> | Grey.WHITE | 1 |
| | +----------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
| | | new | tools.WHITE | WHITE | WHITE | tools.WHITE | WHITE | WHITE |
2021-09-13 16:46:41 -04:00
| +-------------+----------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
2021-09-13 16:18:38 -04:00
| | int drop-in | 3.10 | <Hue.LIGHT: -1> | Hue.LIGHT | | <Color.RED|GREEN: 3> | Color.RED|GREEN | |
| | +----------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
| | | new | tools.LIGHT | -1 | | tools.RED|tools.GREEN | 3 | |
+--------+-------------+----------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
2021-09-13 16:46:41 -04:00
This table shows the final result:
2021-09-13 16:18:38 -04:00
+--------+-------------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
2021-09-13 16:46:41 -04:00
| style | category | enum repr() | enum str() | enum format() | flag repr() | flag str() | flag format() |
2021-09-13 16:18:38 -04:00
+--------+-------------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
| normal | simple | <Hue.LIGHT: -1> | Hue.LIGHT | Hue.LIGHT | <Color(3): RED|GREEN> | Color.RED|Color.GREEN | Color.RED|Color.GREEN |
2021-09-13 16:46:41 -04:00
| +-------------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
2021-09-13 16:18:38 -04:00
| | user mixed | <Grey.WHITE: 1> | Grey.WHITE | Grey.WHITE | <Grey(1): WHITE> | Grey.WHITE | Grey.WHITE |
2021-09-13 16:46:41 -04:00
| +-------------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
2021-09-13 16:18:38 -04:00
| | int drop-in | <Hue.LIGHT: -1> | -1 | -1 | <Color(3): RED|GREEN> | 3 | 3 |
+--------+-------------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
| global | simple | tools.LIGHT | LIGHT | LIGHT | tools.RED|tools.GREEN | RED|GREEN | RED|GREEN |
2021-09-13 16:46:41 -04:00
| +-------------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
2021-09-13 16:18:38 -04:00
| | user mixed | tools.WHITE | WHITE | WHITE | tools.WHITE | WHITE | WHITE |
2021-09-13 16:46:41 -04:00
| +-------------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
2021-09-13 16:18:38 -04:00
| | int drop-in | tools.LIGHT | -1 | -1 | tools.RED|tools.GREEN | 3 | 3 |
+--------+-------------+-----------------+------------+-----------------------+-----------------------+------------------------+-----------------------+
2021-07-13 15:19:58 -04:00
As can be seen, ``repr()`` is primarily affected by whether the members are
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
style was very clunky.
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
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.
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.