PEP 622: Updates from Python-Dev (#1472)

In particular __match_args__ is simplified and the default processing only applies to certain built-in types.
This commit is contained in:
Brandt Bucher 2020-06-26 17:55:59 -07:00 committed by GitHub
parent c49bcbb1a0
commit a106177241
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 23 additions and 22 deletions

View File

@ -142,10 +142,9 @@ A simplified, approximate grammar for the proposed syntax is::
pattern: NAME ':=' or_pattern | or_pattern
or_pattern: closed_pattern ('|' closed_pattern)*
closed_pattern:
| name_pattern
| literal_pattern
| name_pattern
| constant_pattern
| group_pattern
| sequence_pattern
| mapping_pattern
| class_pattern
@ -496,7 +495,7 @@ guard succeeds. So this will work::
values = [0]
match value:
match values:
case [x] if x:
... # This is not executed
case _:
@ -571,26 +570,16 @@ The procedure is as following:
sub-patterns succeed, the overall class pattern match succeeds.
* If there are match-by-position items and the class has a
``__match_args__`` which is not ``None``, the item at position ``i``
``__match_args__``, the item at position ``i``
is matched against the value looked up by attribute
``__match_args__[i]``. For example, a pattern ``Point2D(5, 8)``,
where ``Point2D.__match_args__ == ["x", "y"]``, is translated
(approximately) into ``obj.x == 5 and obj.y == 8``.
* When ``__match_args__`` is missing (as is the default) or ``None``, a single
positional sub-pattern is allowed to be passed to the call. Rather than being
matched against any particular attribute on the proxy, it is instead matched
against the proxy itself. This creates default behavior that is useful and
intuitive for most objects:
* ``bool(False)`` matches ``False`` (but not ``0``).
* ``tuple((0, 1, 2))`` matches ``(0, 1, 2)`` (but not ``[0, 1, 2]``).
* ``int(i)`` matches any ``int`` and binds it to the name ``i``.
* If there are more positional items than the length of ``__match_args__``, an
``ImpossibleMatchError`` is raised.
* If the ``__match_args__`` attribute is absent on the matched class or ``None``,
* If the ``__match_args__`` attribute is absent on the matched class,
but more than one positional item appears in a match,
``ImpossibleMatchError`` is also raised. We don't fall back on
using ``__slots__`` or ``__annotations__`` -- "In the face of ambiguity,
@ -602,18 +591,30 @@ The procedure is as following:
two cases are distinguished:
* If an attribute is missing on the proxy and the class being matched
has no ``__match_args__`` attribute (or it is ``None``), the match
has no ``__match_args__`` attribute, the match
fails. This allows one to write ``case object(name=_)`` to
implement a check for the presence of a given attribute, or ``case
object(name=var)`` to check for its presence and extract its value.
* If an attribute is missing and the class has a ``__match_args__``
which is not ``None``, the match fails if the attribute name is in
* If an attribute is missing and the class has a ``__match_args__``,
the match fails if the attribute name is in
``__match_args__``, else the match raises ``ImpossibleMatchError``.
Such a protocol favors simplicity of implementation over flexibility and
performance. For other considered alternatives, see `rejected ideas`_.
For the most commonly-matched built-in types (specifically: ``bool``,
``bytearray``, ``bytes``, ``complex``, ``dict``, ``float``,
``frozenset``, ``int``, ``list``, ``set``, ``str``, ``tuple``, and
``type``), a single positional sub-pattern is allowed to be passed to
the call. Rather than being matched against any particular attribute
on the proxy, it is instead matched against the proxy itself. This
creates behavior that is useful and intuitive for these objects:
* ``bool(False)`` matches ``False`` (but not ``0``).
* ``tuple((0, 1, 2))`` matches ``(0, 1, 2)`` (but not ``[0, 1, 2]``).
* ``int(i)`` matches any ``int`` and binds it to the name ``i``.
Result value of ``__match__()``
-------------------------------
@ -660,7 +661,7 @@ Special attribute ``__match_args__``
The ``__match_args__`` attribute complements the ``__match__`` method and is
always looked up on the same class as the ``__match__`` method.
``__match_args__``, if it is present and not ``None``, must be a list or
``__match_args__``, if it is present, must be a list or
tuple of strings naming the allowed positional arguments.
@ -682,11 +683,11 @@ a class wants to disallow pattern matching against itself, it should define
``__match__ = None``. This will cause an exception when trying to match
against such a class.
The above implementation means that by default only match-by-name and a single
positional match by value against the proxy will work,
The above implementation means that by default only match-by-name will
work,
and classes should define ``__match_args__`` (e.g. as a class
attribute) if they would like to support match-by-position. Additionally,
dataclasses will support match-by-position out of the box. See below for more
dataclasses and named tuples will support match-by-position out of the box. See below for more
details.
Finally, all attributes are exposed for matching, if a class wants to hide