mirror of https://github.com/python/cpython
bpo-40066: [Enum] update str() and format() output (GH-30582)
Undo rejected PEP-663 changes: - restore `repr()` to its 3.10 status - restore `str()` to its 3.10 status New changes: - `IntEnum` and `IntFlag` now leave `__str__` as the original `int.__str__` so that str() and format() return the same result - zero-valued flags without a name have a slightly changed repr(), e.g. `repr(Color(0)) == '<Color: 0>'` - update `dir()` for mixed-in types to return all the methods and attributes of the mixed-in type - added `_numeric_repr_` to `Flag` to control display of unnamed values - enums without doc strings have a more comprehensive doc string added - `ReprEnum` added -- inheriting from this makes it so only `__repr__` is replaced, not `__str__` nor `__format__`; `IntEnum`, `IntFlag`, and `StrEnum` all inherit from `ReprEnum`
This commit is contained in:
parent
37eab55ac9
commit
acf7403f9b
|
@ -2,15 +2,10 @@
|
|||
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.
|
||||
|
@ -28,6 +23,14 @@ 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.
|
||||
|
||||
|
@ -41,13 +44,14 @@ important, but either way that value can be used to get the corresponding
|
|||
member::
|
||||
|
||||
>>> Weekday(3)
|
||||
Weekday.WEDNESDAY
|
||||
<Weekday.WEDNESDAY: 3>
|
||||
|
||||
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::
|
||||
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::
|
||||
|
||||
>>> print(Weekday.THURSDAY)
|
||||
THURSDAY
|
||||
Weekday.THURSDAY
|
||||
|
||||
The *type* of an enumeration member is the enum it belongs to::
|
||||
|
||||
|
@ -97,8 +101,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())
|
||||
Weekday.TUESDAY
|
||||
>>> Weekday.from_date(date.today()) # doctest: +SKIP
|
||||
<Weekday.TUESDAY: 2>
|
||||
|
||||
Of course, if you're reading this on some other day, you'll see that day instead.
|
||||
|
||||
|
@ -124,21 +128,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
|
||||
<Weekday.MONDAY: 1>
|
||||
|
||||
But :class:`Flag` also allows us to combine several members into a single
|
||||
variable::
|
||||
|
||||
>>> weekend = Weekday.SATURDAY | Weekday.SUNDAY
|
||||
>>> weekend
|
||||
Weekday.SATURDAY|Weekday.SUNDAY
|
||||
<Weekday.SATURDAY|SUNDAY: 96>
|
||||
|
||||
You can even iterate over a :class:`Flag` variable::
|
||||
|
||||
>>> for day in weekend:
|
||||
... print(day)
|
||||
SATURDAY
|
||||
SUNDAY
|
||||
Weekday.SATURDAY
|
||||
Weekday.SUNDAY
|
||||
|
||||
Okay, let's get some chores set up::
|
||||
|
||||
|
@ -173,6 +177,7 @@ yourself some work and use :func:`auto()` for the values::
|
|||
|
||||
.. _enum-advanced-tutorial:
|
||||
|
||||
|
||||
Programmatic access to enumeration members and their attributes
|
||||
---------------------------------------------------------------
|
||||
|
||||
|
@ -181,16 +186,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
|
||||
<Color.RED: 1>
|
||||
>>> Color(3)
|
||||
Color.BLUE
|
||||
<Color.BLUE: 3>
|
||||
|
||||
If you want to access enum members by *name*, use item access::
|
||||
|
||||
>>> Color['RED']
|
||||
Color.RED
|
||||
<Color.RED: 1>
|
||||
>>> Color['GREEN']
|
||||
Color.GREEN
|
||||
<Color.GREEN: 2>
|
||||
|
||||
If you have an enum member and need its :attr:`name` or :attr:`value`::
|
||||
|
||||
|
@ -212,7 +217,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``
|
||||
|
@ -227,11 +232,11 @@ By-name lookup of ``B`` will also return the member ``A``::
|
|||
... ALIAS_FOR_SQUARE = 2
|
||||
...
|
||||
>>> Shape.SQUARE
|
||||
Shape.SQUARE
|
||||
<Shape.SQUARE: 2>
|
||||
>>> Shape.ALIAS_FOR_SQUARE
|
||||
Shape.SQUARE
|
||||
<Shape.SQUARE: 2>
|
||||
>>> Shape(2)
|
||||
Shape.SQUARE
|
||||
<Shape.SQUARE: 2>
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -299,7 +304,7 @@ Iteration
|
|||
Iterating over the members of an enum does not provide the aliases::
|
||||
|
||||
>>> list(Shape)
|
||||
[Shape.SQUARE, Shape.DIAMOND, Shape.CIRCLE]
|
||||
[<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]
|
||||
|
||||
The special attribute ``__members__`` is a read-only ordered mapping of names
|
||||
to members. It includes all names defined in the enumeration, including the
|
||||
|
@ -308,10 +313,10 @@ aliases::
|
|||
>>> for name, member in Shape.__members__.items():
|
||||
... name, member
|
||||
...
|
||||
('SQUARE', Shape.SQUARE)
|
||||
('DIAMOND', Shape.DIAMOND)
|
||||
('CIRCLE', Shape.CIRCLE)
|
||||
('ALIAS_FOR_SQUARE', Shape.SQUARE)
|
||||
('SQUARE', <Shape.SQUARE: 2>)
|
||||
('DIAMOND', <Shape.DIAMOND: 1>)
|
||||
('CIRCLE', <Shape.CIRCLE: 3>)
|
||||
('ALIAS_FOR_SQUARE', <Shape.SQUARE: 2>)
|
||||
|
||||
The ``__members__`` attribute can be used for detailed programmatic access to
|
||||
the enumeration members. For example, finding all the aliases::
|
||||
|
@ -360,8 +365,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.
|
||||
|
@ -389,7 +394,7 @@ usual. If we have this enumeration::
|
|||
Then::
|
||||
|
||||
>>> Mood.favorite_mood()
|
||||
Mood.HAPPY
|
||||
<Mood.HAPPY: 3>
|
||||
>>> Mood.HAPPY.describe()
|
||||
('HAPPY', 3)
|
||||
>>> str(Mood.FUNKY)
|
||||
|
@ -425,7 +430,7 @@ any members. So this is forbidden::
|
|||
...
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: MoreColor: cannot extend enumeration 'Color'
|
||||
TypeError: <enum 'MoreColor'> cannot extend <enum 'Color'>
|
||||
|
||||
But this is allowed::
|
||||
|
||||
|
@ -476,11 +481,9 @@ The :class:`Enum` class is callable, providing the following functional API::
|
|||
>>> Animal
|
||||
<enum 'Animal'>
|
||||
>>> Animal.ANT
|
||||
Animal.ANT
|
||||
>>> Animal.ANT.value
|
||||
1
|
||||
<Animal.ANT: 1>
|
||||
>>> list(Animal)
|
||||
[Animal.ANT, Animal.BEE, Animal.CAT, Animal.DOG]
|
||||
[<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>]
|
||||
|
||||
The semantics of this API resemble :class:`~collections.namedtuple`. The first
|
||||
argument of the call to :class:`Enum` is the name of the enumeration.
|
||||
|
@ -625,16 +628,7 @@ 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. :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',)``.
|
||||
to each other.
|
||||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
|
@ -645,9 +639,8 @@ 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. However, as the name implies, :class:`IntFlag`
|
||||
members also subclass :class:`int` and can be used wherever an :class:`int` is
|
||||
used.
|
||||
:class:`IntFlag` member, if possible. Like :class:`IntEnum`, :class:`IntFlag`
|
||||
members are also integers and can be used wherever an :class:`int` is used.
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -670,7 +663,7 @@ Sample :class:`IntFlag` class::
|
|||
... X = 1
|
||||
...
|
||||
>>> Perm.R | Perm.W
|
||||
Perm.R|Perm.W
|
||||
<Perm.R|W: 6>
|
||||
>>> Perm.R + Perm.W
|
||||
6
|
||||
>>> RW = Perm.R | Perm.W
|
||||
|
@ -685,11 +678,11 @@ It is also possible to name the combinations::
|
|||
... X = 1
|
||||
... RWX = 7
|
||||
>>> Perm.RWX
|
||||
Perm.RWX
|
||||
<Perm.RWX: 7>
|
||||
>>> ~Perm.RWX
|
||||
Perm(0)
|
||||
<Perm: 0>
|
||||
>>> Perm(7)
|
||||
Perm.RWX
|
||||
<Perm.RWX: 7>
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -702,7 +695,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
|
||||
|
||||
|
@ -710,7 +703,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|Perm.X
|
||||
<Perm.R|X: 5>
|
||||
|
||||
>>> Perm.X | 8
|
||||
9
|
||||
|
@ -726,7 +719,7 @@ be combined with them (but may lose :class:`IntFlag` membership::
|
|||
:class:`IntFlag` members can also be iterated over::
|
||||
|
||||
>>> list(RW)
|
||||
[Perm.R, Perm.W]
|
||||
[<Perm.R: 4>, <Perm.W: 2>]
|
||||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
|
@ -753,7 +746,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
|
||||
|
||||
|
@ -767,7 +760,7 @@ while combinations of flags won't::
|
|||
... WHITE = RED | BLUE | GREEN
|
||||
...
|
||||
>>> Color.WHITE
|
||||
Color.WHITE
|
||||
<Color.WHITE: 7>
|
||||
|
||||
Giving a name to the "no flags set" condition does not change its boolean
|
||||
value::
|
||||
|
@ -779,7 +772,7 @@ value::
|
|||
... GREEN = auto()
|
||||
...
|
||||
>>> Color.BLACK
|
||||
Color.BLACK
|
||||
<Color.BLACK: 0>
|
||||
>>> bool(Color.BLACK)
|
||||
False
|
||||
|
||||
|
@ -787,7 +780,7 @@ value::
|
|||
|
||||
>>> purple = Color.RED | Color.BLUE
|
||||
>>> list(purple)
|
||||
[Color.RED, Color.BLUE]
|
||||
[<Color.RED: 1>, <Color.BLUE: 2>]
|
||||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
|
@ -812,16 +805,16 @@ simple to implement independently::
|
|||
pass
|
||||
|
||||
This demonstrates how similar derived enumerations can be defined; for example
|
||||
a :class:`StrEnum` that mixes in :class:`str` instead of :class:`int`.
|
||||
a :class:`FloatEnum` that mixes in :class:`float` 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
|
||||
|
@ -829,15 +822,18 @@ 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 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.
|
||||
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.
|
||||
|
||||
When to use :meth:`__new__` vs. :meth:`__init__`
|
||||
------------------------------------------------
|
||||
|
@ -866,10 +862,10 @@ want one of them to be the value::
|
|||
...
|
||||
|
||||
>>> print(Coordinate['PY'])
|
||||
PY
|
||||
Coordinate.PY
|
||||
|
||||
>>> print(Coordinate(3))
|
||||
VY
|
||||
Coordinate.VY
|
||||
|
||||
|
||||
Finer Points
|
||||
|
@ -949,35 +945,36 @@ 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.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`::
|
||||
``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::
|
||||
|
||||
>>> class FieldTypes(Enum):
|
||||
... name = 0
|
||||
... value = 1
|
||||
... size = 2
|
||||
...
|
||||
>>> FieldTypes.value.size # doctest: +SKIP
|
||||
DeprecationWarning: accessing one member from another is not supported,
|
||||
and will be disabled in 3.12
|
||||
<FieldTypes.size: 2>
|
||||
>>> FieldTypes.value.size
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AttributeError: <enum 'FieldTypes'> member has no attribute 'size'
|
||||
|
||||
|
||||
.. 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):
|
||||
... example = '11', 16 # '11' will be interpreted as a hexadecimal
|
||||
... # number
|
||||
>>> MyEnum.example.value
|
||||
>>> 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...
|
||||
17
|
||||
|
||||
|
||||
|
@ -1000,13 +997,12 @@ 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 and the
|
||||
class. Attributes defined in an :func:`__init__` method will only show up in a
|
||||
:func:`dir` of the member::
|
||||
class below, those methods will show up in a :func:`dir` of the member,
|
||||
but not of the class::
|
||||
|
||||
>>> dir(Planet)
|
||||
['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__init__', '__members__', '__module__', 'surface_gravity']
|
||||
>>> dir(Planet.EARTH)
|
||||
>>> dir(Planet) # doctest: +SKIP
|
||||
['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__']
|
||||
>>> dir(Planet.EARTH) # doctest: +SKIP
|
||||
['__class__', '__doc__', '__module__', 'mass', 'name', 'radius', 'surface_gravity', 'value']
|
||||
|
||||
|
||||
|
@ -1025,19 +1021,10 @@ are comprised of a single bit::
|
|||
... CYAN = GREEN | BLUE
|
||||
...
|
||||
>>> Color(3) # named combination
|
||||
Color.YELLOW
|
||||
<Color.YELLOW: 3>
|
||||
>>> Color(7) # not named combination
|
||||
Color.RED|Color.GREEN|Color.BLUE
|
||||
<Color.RED|GREEN|BLUE: 7>
|
||||
|
||||
``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
|
||||
""""""""""""""""""""""""""""""""
|
||||
|
@ -1060,16 +1047,16 @@ the following are true:
|
|||
- only canonical flags are returned during iteration::
|
||||
|
||||
>>> list(Color.WHITE)
|
||||
[Color.RED, Color.GREEN, Color.BLUE]
|
||||
[<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]
|
||||
|
||||
- negating a flag or flag set returns a new flag/flag set with the
|
||||
corresponding positive integer value::
|
||||
|
||||
>>> Color.BLUE
|
||||
Color.BLUE
|
||||
<Color.BLUE: 4>
|
||||
|
||||
>>> ~Color.BLUE
|
||||
Color.RED|Color.GREEN
|
||||
<Color.RED|GREEN: 3>
|
||||
|
||||
- names of pseudo-flags are constructed from their members' names::
|
||||
|
||||
|
@ -1079,25 +1066,29 @@ the following are true:
|
|||
- multi-bit flags, aka aliases, can be returned from operations::
|
||||
|
||||
>>> Color.RED | Color.BLUE
|
||||
Color.PURPLE
|
||||
<Color.PURPLE: 5>
|
||||
|
||||
>>> Color(7) # or Color(-1)
|
||||
Color.WHITE
|
||||
<Color.WHITE: 7>
|
||||
|
||||
>>> Color(0)
|
||||
Color.BLACK
|
||||
<Color.BLACK: 0>
|
||||
|
||||
- membership / containment checking has changed slightly -- zero-valued flags
|
||||
are never considered to be contained::
|
||||
- membership / containment checking: zero-valued flags are always considered
|
||||
to be contained::
|
||||
|
||||
>>> Color.BLACK in Color.WHITE
|
||||
False
|
||||
True
|
||||
|
||||
otherwise, if all bits of one flag are in the other flag, True is returned::
|
||||
otherwise, only if all bits of one flag are in the other flag will True
|
||||
be 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``:
|
||||
|
||||
|
@ -1181,7 +1172,7 @@ Using :class:`auto` would look like::
|
|||
... GREEN = auto()
|
||||
...
|
||||
>>> Color.GREEN
|
||||
<Color.GREEN>
|
||||
<Color.GREEN: 3>
|
||||
|
||||
|
||||
Using :class:`object`
|
||||
|
@ -1194,10 +1185,24 @@ 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
|
||||
""""""""""""""""""""""""""
|
||||
|
||||
|
@ -1209,9 +1214,7 @@ Using a string as the value would look like::
|
|||
... BLUE = 'too fast!'
|
||||
...
|
||||
>>> Color.GREEN
|
||||
<Color.GREEN>
|
||||
>>> Color.GREEN.value
|
||||
'go'
|
||||
<Color.GREEN: 'go'>
|
||||
|
||||
|
||||
Using a custom :meth:`__new__`
|
||||
|
@ -1232,9 +1235,7 @@ Using an auto-numbering :meth:`__new__` would look like::
|
|||
... BLUE = ()
|
||||
...
|
||||
>>> Color.GREEN
|
||||
<Color.GREEN>
|
||||
>>> Color.GREEN.value
|
||||
2
|
||||
<Color.GREEN: 2>
|
||||
|
||||
To make a more general purpose ``AutoNumber``, add ``*args`` to the signature::
|
||||
|
||||
|
@ -1257,7 +1258,7 @@ to handle any extra arguments::
|
|||
... BLEACHED_CORAL = () # New color, no Pantone code yet!
|
||||
...
|
||||
>>> Swatch.SEA_GREEN
|
||||
<Swatch.SEA_GREEN>
|
||||
<Swatch.SEA_GREEN: 2>
|
||||
>>> Swatch.SEA_GREEN.pantone
|
||||
'1246'
|
||||
>>> Swatch.BLEACHED_CORAL.pantone
|
||||
|
@ -1384,30 +1385,9 @@ An example to show the :attr:`_ignore_` attribute in use::
|
|||
... Period['day_%d' % i] = i
|
||||
...
|
||||
>>> list(Period)[:2]
|
||||
[Period.day_0, Period.day_1]
|
||||
[<Period.day_0: datetime.timedelta(0)>, <Period.day_1: datetime.timedelta(days=1)>]
|
||||
>>> list(Period)[-2:]
|
||||
[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
|
||||
[<Period.day_365: datetime.timedelta(days=365)>, <Period.day_366: datetime.timedelta(days=366)>]
|
||||
|
||||
|
||||
.. _enumtype-examples:
|
||||
|
|
|
@ -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 the :keyword:`class` syntax, or by
|
||||
Enumerations are created either by using :keyword:`class` syntax, or by
|
||||
using function-call syntax::
|
||||
|
||||
>>> from enum import Enum
|
||||
|
@ -45,7 +45,7 @@ using function-call syntax::
|
|||
>>> # functional syntax
|
||||
>>> Color = Enum('Color', ['RED', 'GREEN', 'BLUE'])
|
||||
|
||||
Even though we can use the :keyword:`class` syntax to create Enums, Enums
|
||||
Even though we can use :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 *enum members*) and are functionally constants.
|
||||
*enumeration members* (or *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,15 +110,10 @@ 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:`global_enum`
|
||||
|
||||
:class:`Enum` class decorator to apply the appropriate global `__repr__`,
|
||||
and export its members into the global name space.
|
||||
|
||||
:func:`.property`
|
||||
:func:`property`
|
||||
|
||||
Allows :class:`Enum` members to have attributes without conflicting with
|
||||
other members' names.
|
||||
member names.
|
||||
|
||||
:func:`unique`
|
||||
|
||||
|
@ -131,7 +126,7 @@ Module Contents
|
|||
|
||||
|
||||
.. versionadded:: 3.6 ``Flag``, ``IntFlag``, ``auto``
|
||||
.. versionadded:: 3.11 ``StrEnum``, ``EnumCheck``, ``FlagBoundary``
|
||||
.. versionadded:: 3.11 ``StrEnum``, ``EnumCheck``, ``FlagBoundary``, ``property``
|
||||
|
||||
---------------
|
||||
|
||||
|
@ -145,6 +140,11 @@ 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,32 +162,31 @@ Data Types
|
|||
.. method:: EnumType.__dir__(cls)
|
||||
|
||||
Returns ``['__class__', '__doc__', '__members__', '__module__']`` and the
|
||||
names of the members in ``cls``. User-defined methods and methods from
|
||||
mixin classes will also be included::
|
||||
names of the members in *cls*::
|
||||
|
||||
>>> dir(Color)
|
||||
['BLUE', 'GREEN', 'RED', '__class__', '__doc__', '__members__', '__module__']
|
||||
['BLUE', 'GREEN', 'RED', '__class__', '__contains__', '__doc__', '__getitem__', '__init_subclass__', '__iter__', '__len__', '__members__', '__module__', '__name__', '__qualname__']
|
||||
|
||||
.. method:: EnumType.__getattr__(cls, name)
|
||||
|
||||
Returns the Enum member in *cls* matching *name*, or raises an :exc:`AttributeError`::
|
||||
|
||||
>>> Color.GREEN
|
||||
Color.GREEN
|
||||
<Color.GREEN: 2>
|
||||
|
||||
.. method:: EnumType.__getitem__(cls, name)
|
||||
|
||||
Returns the Enum member in *cls* matching *name*, or raises a :exc:`KeyError`::
|
||||
Returns the Enum member in *cls* matching *name*, or raises an :exc:`KeyError`::
|
||||
|
||||
>>> Color['BLUE']
|
||||
Color.BLUE
|
||||
<Color.BLUE: 3>
|
||||
|
||||
.. method:: EnumType.__iter__(cls)
|
||||
|
||||
Returns each member in *cls* in definition order::
|
||||
|
||||
>>> list(Color)
|
||||
[Color.RED, Color.GREEN, Color.BLUE]
|
||||
[<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 3>]
|
||||
|
||||
.. method:: EnumType.__len__(cls)
|
||||
|
||||
|
@ -201,7 +200,7 @@ Data Types
|
|||
Returns each member in *cls* in reverse definition order::
|
||||
|
||||
>>> list(reversed(Color))
|
||||
[Color.BLUE, Color.GREEN, Color.RED]
|
||||
[<Color.BLUE: 3>, <Color.GREEN: 2>, <Color.RED: 1>]
|
||||
|
||||
|
||||
.. class:: Enum
|
||||
|
@ -232,7 +231,7 @@ Data Types
|
|||
.. attribute:: Enum._ignore_
|
||||
|
||||
``_ignore_`` is only used during creation and is removed from the
|
||||
enumeration once that is complete.
|
||||
enumeration once creation 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
|
||||
|
@ -261,7 +260,7 @@ Data Types
|
|||
.. method:: Enum.__dir__(self)
|
||||
|
||||
Returns ``['__class__', '__doc__', '__module__', 'name', 'value']`` and
|
||||
any public methods defined on ``self.__class__`` or a mixin class::
|
||||
any public methods defined on *self.__class__*::
|
||||
|
||||
>>> from datetime import date
|
||||
>>> class Weekday(Enum):
|
||||
|
@ -276,7 +275,7 @@ Data Types
|
|||
... def today(cls):
|
||||
... print('today is %s' % cls(date.today().isoweekday()).name)
|
||||
>>> dir(Weekday.SATURDAY)
|
||||
['__class__', '__doc__', '__module__', 'name', 'today', 'value']
|
||||
['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'today', 'value']
|
||||
|
||||
.. method:: Enum._generate_next_value_(name, start, count, last_values)
|
||||
|
||||
|
@ -298,6 +297,11 @@ 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
|
||||
|
@ -317,39 +321,52 @@ Data Types
|
|||
>>> Build.DEBUG.value
|
||||
'debug'
|
||||
>>> Build('deBUG')
|
||||
Build.DEBUG
|
||||
<Build.DEBUG: 'debug'>
|
||||
|
||||
.. method:: Enum.__repr__(self)
|
||||
|
||||
Returns the string used for *repr()* calls. By default, returns the
|
||||
*Enum* name and the member name, but can be overridden::
|
||||
*Enum* name, member name, and value, but can be overridden::
|
||||
|
||||
>>> class OldStyle(Enum):
|
||||
... RETRO = auto()
|
||||
... OLD_SCHOOl = auto()
|
||||
... YESTERYEAR = auto()
|
||||
>>> class OtherStyle(Enum):
|
||||
... ALTERNATE = auto()
|
||||
... OTHER = auto()
|
||||
... SOMETHING_ELSE = auto()
|
||||
... def __repr__(self):
|
||||
... cls_name = self.__class__.__name__
|
||||
... return f'<{cls_name}.{self.name}: {self.value}>'
|
||||
>>> OldStyle.RETRO
|
||||
<OldStyle.RETRO: 1>
|
||||
... return f'{cls_name}.{self.name}'
|
||||
>>> OtherStyle.ALTERNATE, str(OtherStyle.ALTERNATE), f"{OtherStyle.ALTERNATE}"
|
||||
(OtherStyle.ALTERNATE, 'OtherStyle.ALTERNATE', 'OtherStyle.ALTERNATE')
|
||||
|
||||
.. method:: Enum.__str__(self)
|
||||
|
||||
Returns the string used for *str()* calls. By default, returns the
|
||||
member name, but can be overridden::
|
||||
*Enum* name and member name, but can be overridden::
|
||||
|
||||
>>> class OldStyle(Enum):
|
||||
... RETRO = auto()
|
||||
... OLD_SCHOOl = auto()
|
||||
... YESTERYEAR = auto()
|
||||
>>> class OtherStyle(Enum):
|
||||
... ALTERNATE = auto()
|
||||
... OTHER = auto()
|
||||
... SOMETHING_ELSE = auto()
|
||||
... def __str__(self):
|
||||
... cls_name = self.__class__.__name__
|
||||
... return f'{cls_name}.{self.name}'
|
||||
>>> OldStyle.RETRO
|
||||
OldStyle.RETRO
|
||||
... return f'{self.name}'
|
||||
>>> OtherStyle.ALTERNATE, str(OtherStyle.ALTERNATE), f"{OtherStyle.ALTERNATE}"
|
||||
(<OtherStyle.ALTERNATE: 1>, 'ALTERNATE', 'ALTERNATE')
|
||||
|
||||
.. note::
|
||||
.. 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')
|
||||
|
||||
.. note::
|
||||
|
||||
Using :class:`auto` with :class:`Enum` results in integers of increasing value,
|
||||
starting with ``1``.
|
||||
|
@ -367,7 +384,7 @@ Data Types
|
|||
... TWO = 2
|
||||
... THREE = 3
|
||||
>>> Numbers.THREE
|
||||
Numbers.THREE
|
||||
<Numbers.THREE: 3>
|
||||
>>> Numbers.ONE + Numbers.TWO
|
||||
3
|
||||
>>> Numbers.THREE + 5
|
||||
|
@ -375,10 +392,14 @@ Data Types
|
|||
>>> Numbers.THREE == 3
|
||||
True
|
||||
|
||||
.. note::
|
||||
.. note::
|
||||
|
||||
Using :class:`auto` with :class:`IntEnum` results in integers of increasing value,
|
||||
starting with ``1``.
|
||||
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.
|
||||
|
||||
|
||||
.. class:: StrEnum
|
||||
|
@ -392,13 +413,16 @@ Data Types
|
|||
instead of ``isinstance(str, unknown)``), and in those locations you
|
||||
will need to use ``str(StrEnum.member)``.
|
||||
|
||||
.. note::
|
||||
|
||||
.. note::
|
||||
Using :class:`auto` with :class:`StrEnum` results in the lower-cased member
|
||||
name as the value.
|
||||
|
||||
Using :class:`auto` with :class:`StrEnum` results in values of the member name,
|
||||
lower-cased.
|
||||
.. 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.
|
||||
|
||||
.. versionadded:: 3.11
|
||||
.. versionadded:: 3.11
|
||||
|
||||
.. class:: Flag
|
||||
|
||||
|
@ -431,9 +455,9 @@ Data Types
|
|||
Returns all contained members::
|
||||
|
||||
>>> list(Color.RED)
|
||||
[Color.RED]
|
||||
[<Color.RED: 1>]
|
||||
>>> list(purple)
|
||||
[Color.RED, Color.BLUE]
|
||||
[<Color.RED: 1>, <Color.BLUE: 4>]
|
||||
|
||||
.. method:: __len__(self):
|
||||
|
||||
|
@ -461,42 +485,52 @@ Data Types
|
|||
Returns current flag binary or'ed with other::
|
||||
|
||||
>>> Color.RED | Color.GREEN
|
||||
Color.RED|Color.GREEN
|
||||
<Color.RED|GREEN: 3>
|
||||
|
||||
.. method:: __and__(self, other)
|
||||
|
||||
Returns current flag binary and'ed with other::
|
||||
|
||||
>>> purple & white
|
||||
Color.RED|Color.BLUE
|
||||
<Color.RED|BLUE: 5>
|
||||
>>> purple & Color.GREEN
|
||||
0x0
|
||||
<Color: 0>
|
||||
|
||||
.. method:: __xor__(self, other)
|
||||
|
||||
Returns current flag binary xor'ed with other::
|
||||
|
||||
>>> purple ^ white
|
||||
Color.GREEN
|
||||
<Color.GREEN: 2>
|
||||
>>> purple ^ Color.GREEN
|
||||
Color.RED|Color.GREEN|Color.BLUE
|
||||
<Color.RED|GREEN|BLUE: 7>
|
||||
|
||||
.. method:: __invert__(self):
|
||||
|
||||
Returns all the flags in *type(self)* that are not in self::
|
||||
|
||||
>>> ~white
|
||||
0x0
|
||||
<Color: 0>
|
||||
>>> ~purple
|
||||
Color.GREEN
|
||||
<Color.GREEN: 2>
|
||||
>>> ~Color.RED
|
||||
Color.GREEN|Color.BLUE
|
||||
<Color.GREEN|BLUE: 6>
|
||||
|
||||
.. note::
|
||||
.. 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::
|
||||
|
||||
Using :class:`auto` with :class:`Flag` results in integers that are powers
|
||||
of two, starting with ``1``.
|
||||
|
||||
.. versionchanged:: 3.11 The *repr()* of zero-valued flags has changed. It
|
||||
is now::
|
||||
|
||||
>>> Color(0)
|
||||
<Color: 0>
|
||||
|
||||
.. class:: IntFlag
|
||||
|
||||
|
@ -509,9 +543,9 @@ Data Types
|
|||
... GREEN = auto()
|
||||
... BLUE = auto()
|
||||
>>> Color.RED & 2
|
||||
0x0
|
||||
<Color: 0>
|
||||
>>> Color.RED | 2
|
||||
Color.RED|Color.GREEN
|
||||
<Color.RED|GREEN: 3>
|
||||
|
||||
If any integer operation is performed with an *IntFlag* member, the result is
|
||||
not an *IntFlag*::
|
||||
|
@ -524,15 +558,25 @@ 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
|
||||
|
||||
.. note::
|
||||
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.
|
||||
|
||||
|
||||
.. class:: EnumCheck
|
||||
|
||||
*EnumCheck* contains the options used by the :func:`verify` decorator to ensure
|
||||
various constraints; failed constraints result in a :exc:`TypeError`.
|
||||
various constraints; failed constraints result in a :exc:`ValueError`.
|
||||
|
||||
.. attribute:: UNIQUE
|
||||
|
||||
|
@ -582,11 +626,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.
|
||||
|
||||
.. versionadded:: 3.11
|
||||
.. versionadded:: 3.11
|
||||
|
||||
.. class:: FlagBoundary
|
||||
|
||||
|
@ -606,7 +650,7 @@ Data Types
|
|||
>>> StrictFlag(2**2 + 2**4)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: StrictFlag: invalid value: 20
|
||||
ValueError: <flag 'StrictFlag'> invalid value 20
|
||||
given 0b0 10100
|
||||
allowed 0b0 00111
|
||||
|
||||
|
@ -621,7 +665,7 @@ Data Types
|
|||
... GREEN = auto()
|
||||
... BLUE = auto()
|
||||
>>> ConformFlag(2**2 + 2**4)
|
||||
ConformFlag.BLUE
|
||||
<ConformFlag.BLUE: 4>
|
||||
|
||||
.. attribute:: EJECT
|
||||
|
||||
|
@ -647,12 +691,52 @@ Data Types
|
|||
... GREEN = auto()
|
||||
... BLUE = auto()
|
||||
>>> KeepFlag(2**2 + 2**4)
|
||||
KeepFlag.BLUE|0x10
|
||||
<KeepFlag.BLUE|16: 20>
|
||||
|
||||
.. 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
|
||||
------------------------
|
||||
|
||||
|
@ -668,15 +752,6 @@ 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
|
||||
|
@ -688,7 +763,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
|
||||
|
||||
|
@ -714,7 +789,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
|
||||
|
||||
---------------
|
||||
|
||||
|
@ -726,14 +801,20 @@ 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:
|
||||
|
||||
- ``format()`` will use the value of the enum member, unless ``__str__``
|
||||
has been overridden
|
||||
- ``__str__`` uses the value and not the name of the enum member
|
||||
|
||||
- ``StrEnum.__str__`` uses the value and not the name of the enum member
|
||||
- ``__format__``, because it uses ``__str__``, will also use the value of
|
||||
the enum member instead of its name
|
||||
|
||||
If you do not need/want those limitations, you can create your own base
|
||||
class by mixing in the ``int`` or ``str`` type yourself::
|
||||
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::
|
||||
|
||||
>>> 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__
|
||||
|
|
|
@ -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
|
||||
ssl.VERIFY_X509_TRUSTED_FIRST
|
||||
<VerifyFlags.VERIFY_X509_TRUSTED_FIRST: 32768>
|
||||
|
||||
.. 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
|
||||
ssl.CERT_REQUIRED
|
||||
<VerifyMode.CERT_REQUIRED: 2>
|
||||
|
||||
.. index:: single: certificates
|
||||
|
||||
|
|
573
Lib/enum.py
573
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',
|
||||
'Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag', 'ReprEnum',
|
||||
'auto', 'unique', 'property', 'verify',
|
||||
'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP',
|
||||
'global_flag_repr', 'global_enum_repr', 'global_enum',
|
||||
'global_flag_repr', 'global_enum_repr', 'global_str', 'global_enum',
|
||||
'EnumCheck', 'CONTINUOUS', 'NAMED_FLAGS', 'UNIQUE',
|
||||
]
|
||||
|
||||
|
@ -18,7 +18,7 @@ __all__ = [
|
|||
# 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 = None
|
||||
Enum = Flag = EJECT = _stdlib_enums = ReprEnum = None
|
||||
|
||||
def _is_descriptor(obj):
|
||||
"""
|
||||
|
@ -116,9 +116,9 @@ def bin(num, max_bits=None):
|
|||
|
||||
ceiling = 2 ** (num).bit_length()
|
||||
if num >= 0:
|
||||
s = _bltin_bin(num + ceiling).replace('1', '0', 1)
|
||||
s = bltns.bin(num + ceiling).replace('1', '0', 1)
|
||||
else:
|
||||
s = _bltin_bin(~num ^ (ceiling - 1) + ceiling)
|
||||
s = bltns.bin(~num ^ (ceiling - 1) + ceiling)
|
||||
sign = s[:3]
|
||||
digits = s[3:]
|
||||
if max_bits is not None:
|
||||
|
@ -126,6 +126,19 @@ 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:
|
||||
|
@ -149,22 +162,12 @@ class property(DynamicClassAttribute):
|
|||
return ownerclass._member_map_[self.name]
|
||||
except KeyError:
|
||||
raise AttributeError(
|
||||
'%s: no class attribute %r' % (ownerclass.__name__, self.name)
|
||||
'%r has no attribute %r' % (ownerclass, 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(
|
||||
'%s: no instance attribute %r' % (ownerclass.__name__, self.name)
|
||||
'%r member has no attribute %r' % (ownerclass, self.name)
|
||||
)
|
||||
else:
|
||||
return self.fget(instance)
|
||||
|
@ -172,7 +175,7 @@ class property(DynamicClassAttribute):
|
|||
def __set__(self, instance, value):
|
||||
if self.fset is None:
|
||||
raise AttributeError(
|
||||
"%s: cannot set instance attribute %r" % (self.clsname, self.name)
|
||||
"<enum %r> cannot set attribute %r" % (self.clsname, self.name)
|
||||
)
|
||||
else:
|
||||
return self.fset(instance, value)
|
||||
|
@ -180,7 +183,7 @@ class property(DynamicClassAttribute):
|
|||
def __delete__(self, instance):
|
||||
if self.fdel is None:
|
||||
raise AttributeError(
|
||||
"%s: cannot delete instance attribute %r" % (self.clsname, self.name)
|
||||
"<enum %r> cannot delete attribute %r" % (self.clsname, self.name)
|
||||
)
|
||||
else:
|
||||
return self.fdel(instance)
|
||||
|
@ -328,7 +331,7 @@ class _EnumDict(dict):
|
|||
elif _is_sunder(key):
|
||||
if key not in (
|
||||
'_order_',
|
||||
'_generate_next_value_', '_missing_', '_ignore_',
|
||||
'_generate_next_value_', '_numeric_repr_', '_missing_', '_ignore_',
|
||||
'_iter_member_', '_iter_member_by_value_', '_iter_member_by_def_',
|
||||
):
|
||||
raise ValueError(
|
||||
|
@ -358,13 +361,13 @@ class _EnumDict(dict):
|
|||
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(
|
||||
|
@ -395,7 +398,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
|
||||
|
@ -413,9 +416,10 @@ class EnumType(type):
|
|||
# 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:
|
||||
|
@ -427,8 +431,8 @@ class EnumType(type):
|
|||
# check for illegal enum names (any others?)
|
||||
invalid_names = set(member_names) & {'mro', ''}
|
||||
if invalid_names:
|
||||
raise ValueError('Invalid enum member name: {0}'.format(
|
||||
','.join(invalid_names)))
|
||||
raise ValueError('invalid enum member name(s) '.format(
|
||||
','.join(repr(n) for n in invalid_names)))
|
||||
#
|
||||
# adjust the sunders
|
||||
_order_ = classdict.pop('_order_', None)
|
||||
|
@ -458,6 +462,8 @@ class EnumType(type):
|
|||
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_'] = (
|
||||
|
@ -467,10 +473,6 @@ class EnumType(type):
|
|||
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)
|
||||
|
@ -481,18 +483,140 @@ class EnumType(type):
|
|||
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 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)
|
||||
if name not in classdict:
|
||||
setattr(enum_class, name, getattr(first_enum, name))
|
||||
#
|
||||
# replace any other __new__ with our own (as long as Enum is not None,
|
||||
# anyway) -- again, this is to support pickle
|
||||
|
@ -563,13 +687,13 @@ class EnumType(type):
|
|||
# _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__(self):
|
||||
def __bool__(cls):
|
||||
"""
|
||||
classes/types should always be True.
|
||||
"""
|
||||
|
@ -614,6 +738,13 @@ class EnumType(type):
|
|||
)
|
||||
|
||||
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(
|
||||
|
@ -631,60 +762,33 @@ class EnumType(type):
|
|||
# nicer error message when someone tries to delete an attribute
|
||||
# (see issue19025).
|
||||
if attr in cls._member_map_:
|
||||
raise AttributeError("%s: cannot delete Enum member %r." % (cls.__name__, attr))
|
||||
raise AttributeError("%r cannot delete member %r." % (cls.__name__, attr))
|
||||
super().__delattr__(attr)
|
||||
|
||||
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.
|
||||
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:
|
||||
add_to_dir(attr_name)
|
||||
|
||||
# sort the output before returning it, so that the result is deterministic.
|
||||
return sorted(cls_dir)
|
||||
# return whatever mixed-in data type has
|
||||
return sorted(set(
|
||||
dir(cls._member_type_)
|
||||
+ cls._member_names_
|
||||
))
|
||||
|
||||
def __getattr__(cls, name):
|
||||
"""
|
||||
|
@ -703,18 +807,24 @@ class EnumType(type):
|
|||
raise AttributeError(name) from None
|
||||
|
||||
def __getitem__(cls, name):
|
||||
"""
|
||||
Return the member matching `name`.
|
||||
"""
|
||||
return cls._member_map_[name]
|
||||
|
||||
def __iter__(cls):
|
||||
"""
|
||||
Returns members in definition order.
|
||||
Return 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_)
|
||||
|
||||
@_bltin_property
|
||||
@bltns.property
|
||||
def __members__(cls):
|
||||
"""
|
||||
Returns a mapping of member name->value.
|
||||
|
@ -732,7 +842,7 @@ class EnumType(type):
|
|||
|
||||
def __reversed__(cls):
|
||||
"""
|
||||
Returns members in reverse definition order.
|
||||
Return members in reverse definition order.
|
||||
"""
|
||||
return (cls._member_map_[name] for name in reversed(cls._member_names_))
|
||||
|
||||
|
@ -746,7 +856,7 @@ class EnumType(type):
|
|||
"""
|
||||
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):
|
||||
|
@ -801,8 +911,7 @@ class EnumType(type):
|
|||
|
||||
return metacls.__new__(metacls, class_name, bases, classdict, boundary=boundary)
|
||||
|
||||
def _convert_(cls, name, module, filter, source=None, *, boundary=None):
|
||||
|
||||
def _convert_(cls, name, module, filter, source=None, *, boundary=None, as_global=False):
|
||||
"""
|
||||
Create a new Enum subclass that replaces a collection of global constants
|
||||
"""
|
||||
|
@ -834,22 +943,25 @@ class EnumType(type):
|
|||
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__)
|
||||
module_globals[name] = cls
|
||||
return cls
|
||||
|
||||
@staticmethod
|
||||
def _check_for_existing_members(class_name, bases):
|
||||
@classmethod
|
||||
def _check_for_existing_members_(mcls, class_name, bases):
|
||||
for chain in bases:
|
||||
for base in chain.__mro__:
|
||||
if issubclass(base, Enum) and base._member_names_:
|
||||
raise TypeError(
|
||||
"%s: cannot extend enumeration %r"
|
||||
% (class_name, base.__name__)
|
||||
"<enum %r> cannot extend %r"
|
||||
% (class_name, base)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _get_mixins_(cls, class_name, bases):
|
||||
def _get_mixins_(mcls, class_name, bases):
|
||||
"""
|
||||
Returns the type for creating enum members, and the first inherited
|
||||
enum class.
|
||||
|
@ -859,7 +971,33 @@ class EnumType(type):
|
|||
if not bases:
|
||||
return object, Enum
|
||||
|
||||
def _find_data_type(bases):
|
||||
mcls._check_for_existing_members_(class_name, bases)
|
||||
|
||||
# ensure final parent class is an Enum derivative, find any concrete
|
||||
# data type, and check that Enum has no members
|
||||
first_enum = bases[-1]
|
||||
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
|
||||
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
|
||||
|
@ -878,24 +1016,14 @@ class EnumType(type):
|
|||
else:
|
||||
candidate = candidate or base
|
||||
if len(data_types) > 1:
|
||||
raise TypeError('%r: too many data types: %r' % (class_name, data_types))
|
||||
raise TypeError('too many data types for %r: %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
|
||||
first_enum = bases[-1]
|
||||
if not issubclass(first_enum, Enum):
|
||||
raise TypeError("new enumerations should be created as "
|
||||
"`EnumName([mixin_type, ...] [data_type,] enum_type)`")
|
||||
cls._check_for_existing_members(class_name, bases)
|
||||
member_type = _find_data_type(bases) or object
|
||||
return member_type, first_enum
|
||||
|
||||
@staticmethod
|
||||
def _find_new_(classdict, member_type, first_enum):
|
||||
@classmethod
|
||||
def _find_new_(mcls, classdict, member_type, first_enum):
|
||||
"""
|
||||
Returns the __new__ to be used for creating the enum members.
|
||||
|
||||
|
@ -943,9 +1071,42 @@ EnumMeta = EnumType
|
|||
|
||||
class Enum(metaclass=EnumType):
|
||||
"""
|
||||
Generic enumeration.
|
||||
Create a collection of name/value pairs.
|
||||
|
||||
Derive from this class to define new enumerations.
|
||||
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.
|
||||
"""
|
||||
|
||||
def __new__(cls, value):
|
||||
|
@ -999,6 +1160,9 @@ class Enum(metaclass=EnumType):
|
|||
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.
|
||||
|
@ -1021,47 +1185,44 @@ class Enum(metaclass=EnumType):
|
|||
return None
|
||||
|
||||
def __repr__(self):
|
||||
return "%s.%s" % ( self.__class__.__name__, self._name_)
|
||||
v_repr = self.__class__._value_repr_ or self._value_.__class__.__repr__
|
||||
return "<%s.%s: %s>" % (self.__class__.__name__, self._name_, v_repr(self._value_))
|
||||
|
||||
def __str__(self):
|
||||
return "%s" % (self._name_, )
|
||||
return "%s.%s" % (self.__class__.__name__, self._name_, )
|
||||
|
||||
def __dir__(self):
|
||||
"""
|
||||
Returns all members and all public methods
|
||||
"""
|
||||
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)
|
||||
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
|
||||
|
||||
def __format__(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)
|
||||
return str.__format__(str(self), format_spec)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self._name_)
|
||||
|
@ -1088,34 +1249,25 @@ class Enum(metaclass=EnumType):
|
|||
return self._value_
|
||||
|
||||
|
||||
class IntEnum(int, Enum):
|
||||
class ReprEnum(Enum):
|
||||
"""
|
||||
Only changes the repr(), leaving str() and format() to the mixed-in type.
|
||||
"""
|
||||
|
||||
|
||||
class IntEnum(int, ReprEnum):
|
||||
"""
|
||||
Enum where members are also (and must be) ints
|
||||
"""
|
||||
|
||||
def __str__(self):
|
||||
return "%s" % (self._name_, )
|
||||
|
||||
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, Enum):
|
||||
class StrEnum(str, ReprEnum):
|
||||
"""
|
||||
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:
|
||||
|
@ -1135,10 +1287,6 @@ class StrEnum(str, Enum):
|
|||
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.
|
||||
|
@ -1169,6 +1317,8 @@ 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.
|
||||
|
@ -1184,7 +1334,7 @@ class Flag(Enum, boundary=STRICT):
|
|||
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
|
||||
|
@ -1232,8 +1382,8 @@ class Flag(Enum, boundary=STRICT):
|
|||
if cls._boundary_ is STRICT:
|
||||
max_bits = max(value.bit_length(), flag_mask.bit_length())
|
||||
raise ValueError(
|
||||
"%s: invalid value: %r\n given %s\n allowed %s" % (
|
||||
cls.__name__, value, bin(value, max_bits), bin(flag_mask, max_bits),
|
||||
"%r invalid value %r\n given %s\n allowed %s" % (
|
||||
cls, value, bin(value, max_bits), bin(flag_mask, max_bits),
|
||||
))
|
||||
elif cls._boundary_ is CONFORM:
|
||||
value = value & flag_mask
|
||||
|
@ -1247,7 +1397,7 @@ class Flag(Enum, boundary=STRICT):
|
|||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
'unknown flag boundary: %r' % (cls._boundary_, )
|
||||
'%r unknown flag boundary %r' % (cls, cls._boundary_, )
|
||||
)
|
||||
if value < 0:
|
||||
neg_value = value
|
||||
|
@ -1274,7 +1424,7 @@ class Flag(Enum, boundary=STRICT):
|
|||
m._name_ for m in cls._iter_member_(member_value)
|
||||
])
|
||||
if unknown:
|
||||
pseudo_member._name_ += '|0x%x' % unknown
|
||||
pseudo_member._name_ += '|%s' % cls._numeric_repr_(unknown)
|
||||
else:
|
||||
pseudo_member._name_ = None
|
||||
# use setdefault in case another thread already created a composite
|
||||
|
@ -1292,10 +1442,8 @@ class Flag(Enum, boundary=STRICT):
|
|||
"""
|
||||
if not isinstance(other, self.__class__):
|
||||
raise TypeError(
|
||||
"unsupported operand type(s) for 'in': '%s' and '%s'" % (
|
||||
"unsupported operand type(s) for 'in': %r and %r" % (
|
||||
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):
|
||||
|
@ -1309,27 +1457,18 @@ class Flag(Enum, boundary=STRICT):
|
|||
|
||||
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 "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('|'))
|
||||
return "<%s: %s>" % (cls_name, v_repr(self._value_))
|
||||
else:
|
||||
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)
|
||||
return "<%s.%s: %s>" % (cls_name, self._name_, v_repr(self._value_))
|
||||
|
||||
def __str__(self):
|
||||
cls = self.__class__
|
||||
cls_name = self.__class__.__name__
|
||||
if self._name_ is None:
|
||||
return '%s(%x)' % (cls.__name__, self._value_)
|
||||
return '%s(%r)' % (cls_name, self._value_)
|
||||
else:
|
||||
return self._name_
|
||||
return "%s.%s" % (cls_name, self._name_)
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self._value_)
|
||||
|
@ -1362,20 +1501,11 @@ class Flag(Enum, boundary=STRICT):
|
|||
return self._inverted_
|
||||
|
||||
|
||||
class IntFlag(int, Flag, boundary=EJECT):
|
||||
class IntFlag(int, ReprEnum, 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__):
|
||||
|
@ -1412,6 +1542,7 @@ class IntFlag(int, Flag, boundary=EJECT):
|
|||
__rxor__ = __xor__
|
||||
__invert__ = Flag.__invert__
|
||||
|
||||
|
||||
def _high_bit(value):
|
||||
"""
|
||||
returns index of highest bit, or -1 if value is zero or negative
|
||||
|
@ -1456,7 +1587,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(0x%x)" % (module, cls_name, self._value_)
|
||||
return "%s.%s(%r)" % (module, cls_name, self._value_)
|
||||
if _is_single_bit(self):
|
||||
return '%s.%s' % (module, self._name_)
|
||||
if self._boundary_ is not FlagBoundary.KEEP:
|
||||
|
@ -1464,14 +1595,22 @@ def global_flag_repr(self):
|
|||
else:
|
||||
name = []
|
||||
for n in self._name_.split('|'):
|
||||
if n.startswith('0'):
|
||||
if n[0].isdigit():
|
||||
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):
|
||||
def global_enum(cls, update_str=False):
|
||||
"""
|
||||
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
|
||||
|
@ -1481,6 +1620,8 @@ def global_enum(cls):
|
|||
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
|
||||
|
||||
|
@ -1522,6 +1663,7 @@ def _simple_enum(etype=Enum, *, boundary=None, use_args=None):
|
|||
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
|
||||
|
@ -1543,13 +1685,8 @@ def _simple_enum(etype=Enum, *, boundary=None, use_args=None):
|
|||
# it
|
||||
enum_class = type(cls_name, (etype, ), body, boundary=boundary, _simple=True)
|
||||
for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'):
|
||||
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)
|
||||
if name not in body:
|
||||
setattr(enum_class, name, getattr(etype, name))
|
||||
gnv_last_values = []
|
||||
if issubclass(enum_class, Flag):
|
||||
# Flag / IntFlag
|
||||
|
@ -1760,8 +1897,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_'):
|
||||
# keys known to be different
|
||||
if key in ('__module__', '_member_map_', '_value2member_map_', '__doc__'):
|
||||
# keys known to be different, or very long
|
||||
continue
|
||||
elif key in member_names:
|
||||
# members are checked below
|
||||
|
@ -1882,3 +2019,5 @@ 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
|
||||
|
|
|
@ -2567,15 +2567,21 @@ class _empty:
|
|||
|
||||
|
||||
class _ParameterKind(enum.IntEnum):
|
||||
POSITIONAL_ONLY = 0
|
||||
POSITIONAL_OR_KEYWORD = 1
|
||||
VAR_POSITIONAL = 2
|
||||
KEYWORD_ONLY = 3
|
||||
VAR_KEYWORD = 4
|
||||
POSITIONAL_ONLY = 'positional-only'
|
||||
POSITIONAL_OR_KEYWORD = 'positional or keyword'
|
||||
VAR_POSITIONAL = 'variadic positional'
|
||||
KEYWORD_ONLY = 'keyword-only'
|
||||
VAR_KEYWORD = 'variadic keyword'
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
return _PARAM_NAME_MAPPING[self]
|
||||
def __new__(cls, description):
|
||||
value = len(cls.__members__)
|
||||
member = int.__new__(cls, value)
|
||||
member._value_ = value
|
||||
member.description = description
|
||||
return member
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
_POSITIONAL_ONLY = _ParameterKind.POSITIONAL_ONLY
|
||||
_POSITIONAL_OR_KEYWORD = _ParameterKind.POSITIONAL_OR_KEYWORD
|
||||
|
@ -2583,14 +2589,6 @@ _VAR_POSITIONAL = _ParameterKind.VAR_POSITIONAL
|
|||
_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.
|
||||
|
|
|
@ -61,7 +61,8 @@ import struct
|
|||
from xml.parsers.expat import ParserCreate
|
||||
|
||||
|
||||
PlistFormat = enum.global_enum(enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__))
|
||||
PlistFormat = enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__)
|
||||
globals().update(PlistFormat.__members__)
|
||||
|
||||
|
||||
class UID:
|
||||
|
|
|
@ -155,6 +155,8 @@ 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
|
||||
|
|
|
@ -119,7 +119,6 @@ from _ssl import (
|
|||
)
|
||||
from _ssl import _DEFAULT_CIPHERS, _OPENSSL_API_VERSION
|
||||
|
||||
|
||||
_IntEnum._convert_(
|
||||
'_SSLMethod', __name__,
|
||||
lambda name: name.startswith('PROTOCOL_') and name != 'PROTOCOL_SSLv23',
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -908,7 +908,7 @@ class PendingSignalsTests(unittest.TestCase):
|
|||
|
||||
%s
|
||||
|
||||
blocked = %r
|
||||
blocked = %s
|
||||
signum = signal.SIGALRM
|
||||
|
||||
# child: block and wait the signal
|
||||
|
|
|
@ -1517,9 +1517,11 @@ class GeneralModuleTests(unittest.TestCase):
|
|||
infos = socket.getaddrinfo(HOST, 80, socket.AF_INET, socket.SOCK_STREAM)
|
||||
for family, type, _, _, _ in infos:
|
||||
self.assertEqual(family, socket.AF_INET)
|
||||
self.assertEqual(str(family), 'AF_INET')
|
||||
self.assertEqual(repr(family), '<AddressFamily.AF_INET: 2>')
|
||||
self.assertEqual(str(family), '2')
|
||||
self.assertEqual(type, socket.SOCK_STREAM)
|
||||
self.assertEqual(str(type), 'SOCK_STREAM')
|
||||
self.assertEqual(repr(type), '<SocketKind.SOCK_STREAM: 1>')
|
||||
self.assertEqual(str(type), '1')
|
||||
infos = socket.getaddrinfo(HOST, None, 0, socket.SOCK_STREAM)
|
||||
for _, socktype, _, _, _ in infos:
|
||||
self.assertEqual(socktype, socket.SOCK_STREAM)
|
||||
|
@ -1793,8 +1795,10 @@ class GeneralModuleTests(unittest.TestCase):
|
|||
# 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(str(s.family), 'AF_INET')
|
||||
self.assertEqual(str(s.type), 'SOCK_STREAM')
|
||||
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')
|
||||
|
||||
def test_socket_consistent_sock_type(self):
|
||||
SOCK_NONBLOCK = getattr(socket, 'SOCK_NONBLOCK', 0)
|
||||
|
|
|
@ -373,7 +373,8 @@ class BasicSocketTests(unittest.TestCase):
|
|||
# Make sure that the PROTOCOL_* constants have enum-like string
|
||||
# reprs.
|
||||
proto = ssl.PROTOCOL_TLS_CLIENT
|
||||
self.assertEqual(str(proto), 'PROTOCOL_TLS_CLIENT')
|
||||
self.assertEqual(repr(proto), '<_SSLMethod.PROTOCOL_TLS_CLIENT: 16>')
|
||||
self.assertEqual(str(proto), '16')
|
||||
ctx = ssl.SSLContext(proto)
|
||||
self.assertIs(ctx.protocol, proto)
|
||||
|
||||
|
@ -622,7 +623,7 @@ class BasicSocketTests(unittest.TestCase):
|
|||
with self.assertWarns(DeprecationWarning) as cm:
|
||||
ssl.SSLContext(protocol)
|
||||
self.assertEqual(
|
||||
f'{protocol!r} is deprecated',
|
||||
f'ssl.{protocol.name} is deprecated',
|
||||
str(cm.warning)
|
||||
)
|
||||
|
||||
|
@ -631,8 +632,9 @@ class BasicSocketTests(unittest.TestCase):
|
|||
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!r} is deprecated',
|
||||
f'ssl.{version_text} is deprecated',
|
||||
str(cm.warning)
|
||||
)
|
||||
|
||||
|
|
|
@ -1490,8 +1490,10 @@ class UnicodeTest(string_tests.CommonTest,
|
|||
# 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.
|
||||
|
@ -1508,8 +1510,10 @@ class UnicodeTest(string_tests.CommonTest,
|
|||
# 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},
|
||||
'...IDES...')
|
||||
'...15...')
|
||||
self.assertEqual('...%(foo)i...' % {'foo':Int.IDES},
|
||||
'...15...')
|
||||
self.assertEqual('...%(foo)d...' % {'foo':Int.IDES},
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
``IntEnum``, ``IntFlag``, and ``StrEnum`` use the mixed-in type for their
|
||||
``str()`` and ``format()`` output.
|
Loading…
Reference in New Issue