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/