Rendered versions can be found at the following links:

https://www.python.org/dev/peps/pep-0663/
https://python.github.io/peps/pep-0663/

On Tue, Nov 2, 2021 at 8:41 PM Ethan Furman <et...@stoneleaf.us> wrote:

> See the latest changes, which are mostly a (hopefully) improved abstract,
> better tables, and some slight rewordings.
>
> Feedback welcome!
>
> -----------------------------------
>
> PEP: 663
> Title: Standardizing Enum str(), repr(), and format() behaviors
> Version: $Revision$
> Last-Modified: $Date$
> Author: Ethan Furman <et...@stoneleaf.us>
> Discussions-To: python-dev@python.org
> Status: Draft
> Type: Informational
> Content-Type: text/x-rst
> Created: 23-Feb-2013
> Python-Version: 3.11
> Post-History: 20-Jul-2021, 02-Nov-2021
> Resolution:
>
>
> Abstract
> ========
>
> Update the ``repr()``, ``str()``, and ``format()`` of the various Enum
> types
> 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
> ``format()``) of the decorated enum to be a valid global reference: i.e.
> ``re.IGNORECASE`` instead of ``<RegexFlag.IGNORECASE: 2>``.
>
>
> Motivation
> ==========
>
> Having the ``str()`` of ``IntEnum`` and ``IntFlag`` not be the value causes
> bugs and extra work when replacing existing constants.
>
> Having the ``str()`` and ``format()`` of an enum member be different can be
> confusing.
>
> The addition of ``StrEnum`` with its requirement to have its ``str()`` be
> its
> ``value`` is inconsistent with other provided Enum's ``str``.
>
> 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.
>
> 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'
>      >>> format(Color.RED)
>      '1'
>
> Note that ``format()`` already produces the correct output, only ``str()``
> needs
> updating.
>
> As much as possible, the ``str()``, ``repr()``, and ``format()`` of enum
> members
> 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()``.
>
> 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:
>
> - 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
>    ``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.
>
> 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
>    namespace, and their module name shows in their ``repr()`` and ``str()``
>    (where appropriate)
>
> 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 two tables show the old and new
> output (blank cells indicate no change):
>
>
> +--------+------------------------+-----------------+------------+-----------------------+
> | 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                     |
>
> +--------+-------------+-----------------------+------------------------+-----------------------+
>
> 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 flawed.
>
>
> Backwards Compatibility
> =======================
>
> Backwards compatibility of stringified objects is not guaranteed across
> major
> 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
> as ``re.ASCII`` and will still compare equal to ``256``.
>
> If the previous output needs to be maintained, for example to ensure
> compatibility between different Python versions, software projects will
> need to
> 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.
> _______________________________________________
> Python-Dev mailing list -- python-dev@python.org
> To unsubscribe send an email to python-dev-le...@python.org
> https://mail.python.org/mailman3/lists/python-dev.python.org/
> Message archived at
> https://mail.python.org/archives/list/python-dev@python.org/message/EICAVXCJAYUMV3KU3Y5S533QW5R35BB5/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/O4JEOSM7OKQ7L6Q4ECN5DX5XWLGBGDDS/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to