From f8ef09530e79b9118a836f64ec1755887ea6901c Mon Sep 17 00:00:00 2001 From: Daniel F Moisset Date: Tue, 23 Feb 2021 22:30:23 +0000 Subject: [PATCH 01/34] Add section on soft keywords "match" and "case" --- Doc/reference/lexical_analysis.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst index 77e0578f5d89b6..80c33598ad375a 100644 --- a/Doc/reference/lexical_analysis.rst +++ b/Doc/reference/lexical_analysis.rst @@ -356,6 +356,19 @@ exactly as written here: single: __, identifiers .. _id-classes: +Soft Keywords +------------- + +.. versionadded:: 3.10 + +The identifiers ``match`` and ``case`` can syntactically act as keywords in some +specific contexts, but this distinction is done at the parser level, not when +tokenizing. + +This is done to allow their use in the pattern matching statement, while still +preserving compatibility with existing code that uses "match" and "case" as +identifier names. + Reserved classes of identifiers ------------------------------- From 2a1b2e19e7c715f255e673590ec089bf3181d461 Mon Sep 17 00:00:00 2001 From: Daniel F Moisset Date: Tue, 23 Feb 2021 23:25:12 +0000 Subject: [PATCH 02/34] Add tutorial on pattern matching The tutorial is a slightly modified version of PEP-636 Appendix A --- Doc/tutorial/controlflow.rst | 163 +++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst index 9ee18f75847e7b..44e4d61a41bd5f 100644 --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -246,6 +246,169 @@ at a more abstract level. The :keyword:`!pass` is silently ignored:: ... pass # Remember to implement this! ... + +.. _tut-match: + +:keyword:`!match` Statements +============================ + +A match statement takes an expression and compares its value to successive +patterns given as one or more case blocks. This is superficially +similar to a switch statement in C, Java or JavaScript (and many +other languages), but it can also extract components (sequence elements or +object attributes) from the value into variables. + +The simplest form compares a subject value against one or more literals:: + + def http_error(status): + match status: + case 400: + return "Bad request" + case 404: + return "Not found" + case 418: + return "I'm a teapot" + case _: + return "Something's wrong with the Internet" + +Note the last block: the "variable name" ``_`` acts as a *wildcard* and +never fails to match. If no case matches, none of the branches is executed. + +You can combine several literals in a single pattern using ``|`` ("or"):: + + case 401 | 403 | 404: + return "Not allowed" + +Patterns can look like unpacking assignments, and can be used to bind +variables:: + + # point is an (x, y) tuple + match point: + case (0, 0): + print("Origin") + case (0, y): + print(f"Y={y}") + case (x, 0): + print(f"X={x}") + case (x, y): + print(f"X={x}, Y={y}") + case _: + raise ValueError("Not a point") + +Study that one carefully! The first pattern has two literals, and can +be thought of as an extension of the literal pattern shown above. But +the next two patterns combine a literal and a variable, and the +variable *binds* a value from the subject (``point``). The fourth +pattern captures two values, which makes it conceptually similar to +the unpacking assignment ``(x, y) = point``. + +If you are using classes to structure your data +you can use the class name followed by an argument list resembling a +constructor, but with the ability to capture attributes into variables:: + + class Point: + x: int + y: int + + def where_is(point): + match point: + case Point(x=0, y=0): + print("Origin") + case Point(x=0, y=y): + print(f"Y={y}") + case Point(x=x, y=0): + print(f"X={x}") + case Point(): + print("Somewhere else") + case _: + print("Not a point") + +You can use positional parameters with some builtin classes that provide an +ordering for their attributes (e.g. dataclasses). You can also define a specific +position for attributes in patterns by setting the ``__match_args__`` special +attribute in your classes. If it's set to ("x", "y"), the following patterns are all +equivalent (and all bind the ``y`` attribute to the ``var`` variable):: + + Point(1, var) + Point(1, y=var) + Point(x=1, y=var) + Point(y=var, x=1) + +A recommended way to read patterns is to look at them as an extended form of what you +would put on the left of an assignment, to understand which variables would be set to +what. +Only the standalone names (like ``var`` above) are assigned to by a match statement. +Dotted names (like ``foo.bar``), attribute names (the ``x=`` and ``y=`` above) or class names +(recognized by the "(...)" next to them like ``Point`` above) are never assigned to. + +Patterns can be arbitrarily nested. For example, if we have a short +list of points, we could match it like this:: + + match points: + case []: + print("No points") + case [Point(0, 0)]: + print("The origin") + case [Point(x, y)]: + print(f"Single point {x}, {y}") + case [Point(0, y1), Point(0, y2)]: + print(f"Two on the Y axis at {y1}, {y2}") + case _: + print("Something else") + +We can add an ``if`` clause to a pattern, known as a "guard". If the +guard is false, ``match`` goes on to try the next case block. Note +that value capture happens before the guard is evaluated:: + + match point: + case Point(x, y) if x == y: + print(f"Y=X at {x}") + case Point(x, y): + print(f"Not on the diagonal") + +Several other key features of this statement: + +- Like unpacking assignments, tuple and list patterns have exactly the + same meaning and actually match arbitrary sequences. An important + exception is that they don't match iterators or strings. + +- Sequence patterns support extended unpacking: ``[x, y, *rest]`` and ``(x, y, + *rest)`` work similar to unpacking assignments. The + name after ``*`` may also be ``_``, so ``(x, y, *_)`` matches a sequence + of at least two items without binding the remaining items. + +- Mapping patterns: ``{"bandwidth": b, "latency": l}`` captures the + ``"bandwidth"`` and ``"latency"`` values from a dictionary. Unlike sequence + patterns, extra keys are ignored. An unpacking like ``**rest`` is also + supported. (But ``**_`` would be redundant, so it not allowed.) + +- Subpatterns may be captured using the ``as`` keyword:: + + case (Point(x1, y1), Point(x2, y2) as p2): ... + + will capture the second element of the input as ``p2`` (as long as the input is + a sequence of two points) + +- Most literals are compared by equality, however the singletons ``True``, + ``False`` and ``None`` are compared by identity. + +- Patterns may use named constants. These must be dotted names + to prevent them from being interpreted as capture variable:: + + from enum import Enum + class Color(Enum): + RED = 0 + GREEN = 1 + BLUE = 2 + + match color: + case Color.RED: + print("I see red!") + case Color.GREEN: + print("Grass is green") + case Color.BLUE: + print("I'm feeling the blues :(") + .. _tut-functions: Defining Functions From 83495eaae63dbd39616b181309b41b7ec4ea0f70 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 24 Feb 2021 16:11:04 +0800 Subject: [PATCH 03/34] move index and link down to correct (original) location, add index for soft keywords --- Doc/reference/lexical_analysis.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst index 80c33598ad375a..17918a7cac8396 100644 --- a/Doc/reference/lexical_analysis.rst +++ b/Doc/reference/lexical_analysis.rst @@ -351,14 +351,12 @@ exactly as written here: assert del global not with async elif if or yield -.. index:: - single: _, identifiers - single: __, identifiers -.. _id-classes: Soft Keywords ------------- +.. index:: soft keyword, keyword + .. versionadded:: 3.10 The identifiers ``match`` and ``case`` can syntactically act as keywords in some @@ -369,6 +367,12 @@ This is done to allow their use in the pattern matching statement, while still preserving compatibility with existing code that uses "match" and "case" as identifier names. + +.. index:: + single: _, identifiers + single: __, identifiers +.. _id-classes: + Reserved classes of identifiers ------------------------------- From 4fda91c5268cfc17e23baa80aa91b77e396ee5d6 Mon Sep 17 00:00:00 2001 From: Daniel F Moisset Date: Fri, 26 Feb 2021 00:54:13 +0000 Subject: [PATCH 04/34] Add "_" as a soft keyword --- Doc/reference/lexical_analysis.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst index 17918a7cac8396..96cba9b021c41f 100644 --- a/Doc/reference/lexical_analysis.rst +++ b/Doc/reference/lexical_analysis.rst @@ -359,12 +359,12 @@ Soft Keywords .. versionadded:: 3.10 -The identifiers ``match`` and ``case`` can syntactically act as keywords in some -specific contexts, but this distinction is done at the parser level, not when -tokenizing. +The identifiers ``match``, ``case`` and ``_`` can syntactically act as keywords in some +specific contexts related to the pattern matching statement, but this distinction is done +at the parser level, not when tokenizing. -This is done to allow their use in the pattern matching statement, while still -preserving compatibility with existing code that uses "match" and "case" as +This is done to allow their use while still +preserving compatibility with existing code that uses ``match``, ``case`` and ``_`` as identifier names. From f07fe3e348a5d0f57d6c56c7dca8157aeebc0012 Mon Sep 17 00:00:00 2001 From: Daniel F Moisset Date: Fri, 26 Feb 2021 01:28:29 +0000 Subject: [PATCH 05/34] Add __match_args__ to the data model --- Doc/reference/datamodel.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 62f852953623c1..b3bf5a95abbdf5 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2553,6 +2553,36 @@ For more information on context managers, see :ref:`typecontextmanager`. statement. +.. _pattern-matching: + +Customizing positional arguments in class pattern matching +---------------------------------------------------------- + +When using a class name in a pattern, positional arguments in the pattern are not +allowed by default, i.e. ``case MyClass(x, y)`` is typically invalid without special +support in ``MyClass``. To be able to use that kind of patterns, the class needs to +define a *__match_args__* attribute. + +.. data:: object.__match_args__ + + This class variable can be assigned a tuple or list of strings. When this class is + used in a class pattern with positional arguments, each positional argument will + be converted into a keyword argument, using the corresponding value in + *__match_args__* as the keyword. The absence of this attribute is equivalent to + setting it to ``()``. + +For example if ``MyClass.__match_args__`` is ``("left", "center", "right")`` that means +that ``case MyClass(x, y)`` is equivalent to ``case MyClass(left=x, center=y)``. Note +that the number of arguments in the pattern must be smaller than or equal to the number +of elements in *__match_args__*; if it is larger, the pattern match attempt will raise +a *TypeError*. + +.. seealso:: + + :pep:`634` - Structural Pattern Matching + The specification for the Python ``match`` statement. + + .. _special-lookup: Special method lookup From 72ce7ccf506b62c0046a17808442970732873462 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 27 Feb 2021 16:59:38 +0800 Subject: [PATCH 06/34] Add versionadded to datamodel --- Doc/reference/datamodel.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index b3bf5a95abbdf5..13c4463aa5880a 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2577,6 +2577,8 @@ that the number of arguments in the pattern must be smaller than or equal to the of elements in *__match_args__*; if it is larger, the pattern match attempt will raise a *TypeError*. +.. versionadded:: 3.10 + .. seealso:: :pep:`634` - Structural Pattern Matching From faa04502e8693a9e85fbe69f3bb179032053e570 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 27 Feb 2021 17:05:12 +0800 Subject: [PATCH 07/34] Rename __match_args__ section link --- Doc/reference/datamodel.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 13c4463aa5880a..04437bd2faf33b 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2553,7 +2553,7 @@ For more information on context managers, see :ref:`typecontextmanager`. statement. -.. _pattern-matching: +.. _class-pattern-matching: Customizing positional arguments in class pattern matching ---------------------------------------------------------- From 0392e9cbaf84a471e423f9cc485ec4af4768507b Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 27 Feb 2021 17:09:11 +0800 Subject: [PATCH 08/34] object.__match_args__ -> class.__match_args__ --- Doc/reference/datamodel.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 04437bd2faf33b..2621cc49306182 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2563,7 +2563,7 @@ allowed by default, i.e. ``case MyClass(x, y)`` is typically invalid without spe support in ``MyClass``. To be able to use that kind of patterns, the class needs to define a *__match_args__* attribute. -.. data:: object.__match_args__ +.. data:: class.__match_args__ This class variable can be assigned a tuple or list of strings. When this class is used in a class pattern with positional arguments, each positional argument will From e9a33ed834723a68498c3ce3affa224c4ec407a6 Mon Sep 17 00:00:00 2001 From: Daniel F Moisset Date: Sat, 27 Feb 2021 18:39:20 +0000 Subject: [PATCH 09/34] Refer to match wherever the C switch statement was compared with Python. Brought up in https://bugs.python.org/msg387769 --- Doc/faq/design.rst | 5 ++--- Doc/tutorial/controlflow.rst | 3 +++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Doc/faq/design.rst b/Doc/faq/design.rst index 8cf271c3024084..9bece6dc9c8260 100644 --- a/Doc/faq/design.rst +++ b/Doc/faq/design.rst @@ -259,9 +259,8 @@ Why isn't there a switch or case statement in Python? ----------------------------------------------------- You can do this easily enough with a sequence of ``if... elif... elif... else``. -There have been some proposals for switch statement syntax, but there is no -consensus (yet) on whether and how to do range tests. See :pep:`275` for -complete details and the current status. +For literal values, or constants within a namespace, you can also use a +``match ... case`` statement. For cases where you need to choose from a very large number of possibilities, you can create a dictionary mapping case values to functions to call. For diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst index 44e4d61a41bd5f..3088af1e1e7708 100644 --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -36,6 +36,9 @@ to avoid excessive indentation. An :keyword:`!if` ... :keyword:`!elif` ... :keyword:`!elif` ... sequence is a substitute for the ``switch`` or ``case`` statements found in other languages. +If you're making comparison with constants, or checking for specific types or +attributes, you may also find the :keyword:`!match` statement useful. For more +details see :ref:`tut-match`. .. _tut-for: From 734dfea6e6847128e02e1fad57899f1e9cfd3a6c Mon Sep 17 00:00:00 2001 From: Daniel F Moisset Date: Sat, 27 Feb 2021 18:46:39 +0000 Subject: [PATCH 10/34] Revert "object.__match_args__ -> class.__match_args__" This reverts commit 6320747216b01cf78959d9209eb0ee1d89acc23d. --- Doc/reference/datamodel.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 2621cc49306182..04437bd2faf33b 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2563,7 +2563,7 @@ allowed by default, i.e. ``case MyClass(x, y)`` is typically invalid without spe support in ``MyClass``. To be able to use that kind of patterns, the class needs to define a *__match_args__* attribute. -.. data:: class.__match_args__ +.. data:: object.__match_args__ This class variable can be assigned a tuple or list of strings. When this class is used in a class pattern with positional arguments, each positional argument will From cd736ddeacc9ba28a8c9b3bfdc9047bc6ace0b37 Mon Sep 17 00:00:00 2001 From: Daniel F Moisset Date: Sat, 27 Feb 2021 19:11:51 +0000 Subject: [PATCH 11/34] Refer to the PEP tutorial from the tutorial --- Doc/tutorial/controlflow.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst index 3088af1e1e7708..8727bf50e552da 100644 --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -412,6 +412,9 @@ Several other key features of this statement: case Color.BLUE: print("I'm feeling the blues :(") +For a more detailed explanation and additional examples, you can look into +:pep:`636` which is written in a tutorial format. + .. _tut-functions: Defining Functions From bdcf259045a0a912dc5a8a4e0e4e37078be9001c Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Mon, 22 Feb 2021 00:33:30 +0800 Subject: [PATCH 12/34] Add half of PEP 634 (lots of TODOs left) Co-Authored-By: Daniel F Moisset Co-Authored-By: Guido van Rossum <2894642+gvanrossum@users.noreply.github.com> Co-Authored-By: Brandt Bucher --- Doc/reference/compound_stmts.rst | 290 +++++++++++++++++++++++++++++ Doc/reference/lexical_analysis.rst | 5 +- 2 files changed, 294 insertions(+), 1 deletion(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index f22af8b44a1127..89bac08f0be9c0 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -51,6 +51,7 @@ Summarizing: : | `for_stmt` : | `try_stmt` : | `with_stmt` + : | `match_stmt` : | `funcdef` : | `classdef` : | `async_with_stmt` @@ -510,6 +511,295 @@ the items are surrounded by parentheses. For example:: The specification, background, and examples for the Python :keyword:`with` statement. +.. _match: + +The match statement +=================== + +.. index:: + ! statement: match + ! keyword: case + keyword: if + keyword: as + pair: match; case + single: : (colon); compound statement + +.. versionadded:: 3.10 + +The match statement is used for pattern matching. Pattern matching takes a +pattern as input (following ``case``) and a subject value (following ``match``). +The pattern (which may contain subpatterns) is matched against the subject value. +The outcome of which is a successful match or a failure, and matched values are +bound to a name. + +.. productionlist:: python-grammar + match_stmt: "match" `subject_expr` ':' NEWLINE INDENT `case_block`+ DEDENT + subject_expr: `star_named_expression` ',' `star_named_expressions`? + : | `named_expression` + case_block: "case" `patterns` [`guard`] ':' `block` + guard: 'if' `named_expression` + +The match and case keywords are :ref:`soft keywords `, i.e. they +are not reserved words in other grammatical contexts (including at the start of +a line if there is no colon where expected). This implies that they are +recognized as keywords when part of a match statement or case block only, and +are allowed to be used in all other contexts as variable or argument names. + +The rules ``star_named_expression``, ``star_named_expressions``, +``named_expression`` and ``block`` are part of the +:doc:`standard Python grammar <./grammar>`. While ``patterns`` is specified +further below. + +Here's an overview of the logical flow: + +#. The subject expression ``subject_expr`` is evaluated and a resulting subject + value obtained. + +#. The first ``case_block`` whose patterns succeed in matching the subject + value and whose ``guard`` condition (if present) is "truthy" is selected. + + - If no case blocks qualify, the match statement is completed. + + .. note:: + + During failed pattern matches, some subpatterns may succeed. User code + including a match statement shouldn't rely on the bindings being made + for a failed match, but also shouldn't assume that variables are unchanged + by a failed match. The exact behavior is dependent on implementation and + may vary. This is an intentional decision made to allow different + implementations to add optimizations. + + - Otherwise, name bindings occur and the ``block`` inside ``case_block`` is + executed. The precise pattern binding rules vary per pattern type and are + specified below. **Name bindings made during a successful pattern match + outlive the executed block and can be used after the match statement**. + +.. note:: + + Users should generally never rely on the number of times a pattern is written + in a match statement as a measurement of the number of times a pattern's + expression is executed. Depending on implementation, the interpreter may + cache values or use other optimizations which reduce evaluations. + + +Guards +------ + +If a ``guard`` is present in a case block, it is evaluated if and only if the +pattern(s) in a case block succeed. If this raises an exception, the exception +bubbles up. Otherwise, if the guard condition is "truthy", the case block is +selected; if it is "falsy" the case block is not selected. + +Since guards are expressions they are allowed to have side effects. Guard +evaluation must proceed from the first to the last case block, one at a time, +skipping case blocks whose pattern(s) don't all succeed. (I.e., +guard evaluation must happen in order.) Guard evaluation must stop once a case +block is selected. + +Irrefutable case blocks +----------------------- + +A pattern is considered irrefutable if we can prove from its syntax alone that +it will always succeed. In particular, capture patterns and wildcard patterns +are irrefutable, and so are :ref:`AS patterns ` whose left-hand +side is irrefutable, :ref:`OR patterns ` containing at least one +irrefutable pattern, and parenthesized irrefutable patterns. + +A case block is considered irrefutable if it has no guard and its pattern is +irrefutable. + +A match statement may have at most one irrefutable case block, and it must be +last. + +Patterns +-------- + +The top-level syntax for patterns is as follows. Note that the notation +``SEP.RULE+`` is shorthand for ``RULE (SEP RULE)*``. Grammar further below +may also have the notation ``!RULE`` which is shorthand for a negative lookahead +assertion: + +.. productionlist:: python-grammar + patterns: `open_sequence_pattern` | `pattern` + pattern: `as_pattern` | `or_pattern` + as_pattern: `or_pattern` 'as' `capture_pattern` + or_pattern: '|'.`closed_pattern`+ + closed_pattern: | `literal_pattern` + : | `capture_pattern` + : | `wildcard_pattern` + : | `value_pattern` + : | `group_pattern` + : | `sequence_pattern` + : | `mapping_pattern` + : | `class_pattern` + +.. _as_pattern: + +* ``as_pattern``: An AS pattern matches the OR pattern on the left of the as + keyword against the subject. If this fails, the AS pattern fails. + Otherwise, the AS pattern binds the subject to the name on the right of the + as keyword and succeeds. Note that ``capture_pattern`` cannot be a a ``_``. + +.. _or_pattern: + +* ``or_pattern``: An OR pattern is two or more patterns separated by vertical + bars ``|``. Only the final subpattern may be irrefutable, and each subpattern + must bind the same set of names to avoid ambiguity. An OR pattern matches + each of its subpatterns in turn to the subject value, until one succeeds. The + OR pattern is then considered successful. Otherwise, if none of the + subpatterns succeed, the OR pattern fails. + +The following belong to ``closed_pattern``: + +* ``literal_pattern``: A literal pattern corresponds to most + :ref:`literals ` in Python. Syntax: + + .. productionlist:: python-grammar + literal_pattern: signed_number + : | signed_number '+' NUMBER + : | signed_number '-' NUMBER + : | strings + : | 'None' + : | 'True' + : | 'False' + : | signed_number: NUMBER | '-' NUMBER + + The rule ``strings`` and the token ``NUMBER`` are defined in the + :doc:`standard Python grammar <./grammar>`. Triple-quoted strings are + supported. Raw strings and byte strings are supported. :ref:`f-strings` are + not supported. + + The forms ``signed_number '+' NUMBER`` and ``signed_number '-' NUMBER`` are + for expressing :ref:`complex numbers `; they require a real number + on the left and an imaginary number on the right. E.g. ``3 + 4j``. + +* ``capture_pattern``: A capture pattern binds the subject value to the name. + Syntax: + + .. productionlist:: python-grammar + capture_pattern: !"_" NAME + + A single underscore ``_`` is not a capture pattern (this is what ``!"_"`` + expresses). And is instead treated as a :token:`wildcard_pattern`. + + In a given pattern, a given name can only be bound once. E.g. + ``case x, x: ...`` is invalid while ``case [x] | x: ...`` is allowed. + + Capture patterns always succeed. The binding follows scoping rules + established by the assignment expression operator in :pep`572`, where the + name becomes a local variable in the closest containing function scope unless + there's an applicable :keyword:`global` or :keyword:`nonlocal` statement. + +* ``wildcard_pattern``: A wildcard pattern always succeeds (matches anything) + and binds no name. Syntax: + + .. productionlist:: python-grammar + wildcard_pattern: "_" + +* ``value_pattern``: A value pattern corresponds to a named value in Python. + Syntax: + + .. productionlist:: python-grammar + value_pattern: `attr` + attr: `name_or_attr` '.' NAME + name_or_attr: `attr` | NAME + + The dotted name in the pattern is looked up using standard Python + :ref:`name resolution rules `. The pattern succeeds if the + value found compares equal to the subject value (using the ``==`` equality + operator). + + .. note:: + + If the same value occurs multiple times in the same match statement, the + interpreter may cache the first value found and reuse it rather than repeat + the same lookup. This cache is strictly tied to a given execution of a + given match statement. + +* ``group_pattern``: TODO + + .. productionlist:: python-grammar + group_pattern: '(' `pattern` ')' + + +* ``sequence_pattern``: TODO + + .. productionlist:: python-grammar + sequence_pattern: '[' [`maybe_sequence_pattern`] ']' + : | '(' [`open_sequence_pattern`] ')' + open_sequence_pattern: `maybe_star_pattern` ',' [`maybe_sequence_pattern`] + maybe_sequence_pattern: ','.`maybe_star_pattern`+ ','? + maybe_star_pattern: `star_pattern` | `pattern` + star_pattern: '*' (`capture_pattern` | `wildcard_pattern`) + + +* ``mapping_pattern``: TODO + + .. productionlist:: python-grammar + mapping_pattern: '{' [`items_pattern`] '}' + items_pattern: ','.`key_value_pattern`+ ','? + key_value_pattern: (`literal_pattern` | `value_pattern`) ':' `pattern` + : | `double_star_pattern` + double_star_pattern: '**' `capture_pattern` + +* ``class_pattern``: TODO + + .. productionlist:: python-grammar + class_pattern: `name_or_attr` '(' [`pattern_arguments` ','?] ')' + pattern_arguments: `positional_patterns` [',' `keyword_patterns`] + : | `keyword_patterns` + positional_patterns: ','.`pattern`+ + keyword_patterns: ','.`keyword_pattern`+ + keyword_pattern: NAME '=' `pattern` + + +Full Grammar +------------ + +.. productionlist:: python-grammar + patterns: open_sequence_pattern | pattern + pattern: as_pattern | or_pattern + as_pattern: or_pattern 'as' capture_pattern + or_pattern: '|'.closed_pattern+ + closed_pattern: literal_pattern + : | capture_pattern + : | wildcard_pattern + : | value_pattern + : | group_pattern + : | sequence_pattern + : | mapping_pattern + : | class_pattern + literal_pattern: signed_number !('+' | '-') + : | signed_number '+' NUMBER + : | signed_number '-' NUMBER + : | strings + : | 'None' + : | 'True' + : | 'False' + signed_number: NUMBER | '-' NUMBER + capture_pattern: !"_" NAME !('.' | '(' | '=') + wildcard_pattern: "_" + value_pattern: attr !('.' | '(' | '=') + attr: name_or_attr '.' NAME + name_or_attr: attr | NAME + group_pattern: '(' pattern ')' + sequence_pattern: '[' [maybe_sequence_pattern] ']' + : | '(' [open_sequence_pattern] ')' + open_sequence_pattern: maybe_star_pattern ',' [maybe_sequence_pattern] + maybe_sequence_pattern: ','.maybe_star_pattern+ ','? + maybe_star_pattern: star_pattern | pattern + star_pattern: '*' (capture_pattern | wildcard_pattern) + mapping_pattern: '{' [items_pattern] '}' + items_pattern: ','.key_value_pattern+ ','? + key_value_pattern: | (literal_pattern | value_pattern) ':' pattern + : | double_star_pattern + double_star_pattern: '**' capture_pattern + class_pattern: name_or_attr '(' [pattern_arguments ','?] ')' + pattern_arguments: positional_patterns [',' keyword_patterns] + : | keyword_patterns + positional_patterns: ','.pattern+ + keyword_patterns: ','.keyword_pattern+ + keyword_pattern: NAME '=' pattern .. index:: single: parameter; function definition diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst index 96cba9b021c41f..048b2d0cc1aba1 100644 --- a/Doc/reference/lexical_analysis.rst +++ b/Doc/reference/lexical_analysis.rst @@ -351,11 +351,14 @@ exactly as written here: assert del global not with async elif if or yield +.. _soft_keywords: Soft Keywords ------------- -.. index:: soft keyword, keyword +.. index:: + single: soft keyword; keyword + pair: soft; keyword .. versionadded:: 3.10 From 0e2d7689496e1157af9470f43015c259a23976c4 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 24 Feb 2021 01:25:15 +0800 Subject: [PATCH 13/34] use better wording and add lots of lists and tables (still lots of TODOs left) one table idea was inspired by Raymond's awesome tweet here https://twitter.com/raymondh/status/1361780586570948609 Co-Authored-By: Raymond Hettinger <1623689+rhettinger@users.noreply.github.com> --- Doc/reference/compound_stmts.rst | 467 +++++++++++++++++++++-------- Doc/reference/lexical_analysis.rst | 5 +- 2 files changed, 338 insertions(+), 134 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 89bac08f0be9c0..70b77e6bfb6cc8 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -513,12 +513,13 @@ the items are surrounded by parentheses. For example:: .. _match: -The match statement -=================== +The :keyword:`!match` statement +=============================== .. index:: ! statement: match ! keyword: case + ! single: pattern matching keyword: if keyword: as pair: match; case @@ -526,98 +527,180 @@ The match statement .. versionadded:: 3.10 -The match statement is used for pattern matching. Pattern matching takes a -pattern as input (following ``case``) and a subject value (following ``match``). -The pattern (which may contain subpatterns) is matched against the subject value. -The outcome of which is a successful match or a failure, and matched values are -bound to a name. +The match statement is used for pattern matching. Syntax: .. productionlist:: python-grammar match_stmt: "match" `subject_expr` ':' NEWLINE INDENT `case_block`+ DEDENT subject_expr: `star_named_expression` ',' `star_named_expressions`? : | `named_expression` case_block: "case" `patterns` [`guard`] ':' `block` - guard: 'if' `named_expression` -The match and case keywords are :ref:`soft keywords `, i.e. they -are not reserved words in other grammatical contexts (including at the start of -a line if there is no colon where expected). This implies that they are -recognized as keywords when part of a match statement or case block only, and -are allowed to be used in all other contexts as variable or argument names. +Pattern matching takes a pattern as input (following ``case``) and a subject +value (following ``match``). The pattern (which may contain subpatterns) is +matched against the subject value. The outcomes are: + +* A match success or failure (also termed a pattern success or failure). + +* Possible binding of matched values to a name. The prerequisites for this are + further discussed below. + +The ``match`` and ``case`` keywords are :ref:`soft keywords `. +This means ``match`` and ``case`` have the following characteristics: + +* They are not reserved words in other grammatical contexts, including at the + start of a line if there is no colon where expected. + +* They are recognized as keywords when part of a match statement or case block + only. + +* They can be used in all other contexts as variable or argument names + The rules ``star_named_expression``, ``star_named_expressions``, ``named_expression`` and ``block`` are part of the :doc:`standard Python grammar <./grammar>`. While ``patterns`` is specified further below. +Overview +-------- + Here's an overview of the logical flow: #. The subject expression ``subject_expr`` is evaluated and a resulting subject - value obtained. + value obtained. If the subject expression contains a comma, a tuple is + constructed using :ref:`the standard rules `. #. The first ``case_block`` whose patterns succeed in matching the subject value and whose ``guard`` condition (if present) is "truthy" is selected. - - If no case blocks qualify, the match statement is completed. + * If no case blocks qualify, the match statement is completed. .. note:: - During failed pattern matches, some subpatterns may succeed. User code - including a match statement shouldn't rely on the bindings being made - for a failed match, but also shouldn't assume that variables are unchanged - by a failed match. The exact behavior is dependent on implementation and - may vary. This is an intentional decision made to allow different - implementations to add optimizations. + During failed pattern matches, some subpatterns may succeed. Do not + rely on bindings being made for a failed match. Conversely, do not + rely on variables remaining unchanged after a failed match. The exact + behavior is dependent on implementation and may vary. This is an + intentional decision made to allow different implementations to add + optimizations. - - Otherwise, name bindings occur and the ``block`` inside ``case_block`` is + * Otherwise, name bindings occur and the ``block`` inside ``case_block`` is executed. The precise pattern binding rules vary per pattern type and are specified below. **Name bindings made during a successful pattern match outlive the executed block and can be used after the match statement**. .. note:: - Users should generally never rely on the number of times a pattern is written - in a match statement as a measurement of the number of times a pattern's - expression is executed. Depending on implementation, the interpreter may - cache values or use other optimizations which reduce evaluations. + Users should generally never rely on a pattern being evaluated. Depending on + implementation, the interpreter may cache values or use other optimizations + which skip repeated evaluations. + +A sample match statement:: + + >>> flag = False + >>> match (100, 200): + ... case (100, 300): + ... print('Case 1') + ... case (100, 200) if flag: + ... print('Case 2') + ... case (100, y): + ... print(f'Case 3, y: {y}') + ... case _: + ... print('Case 4, I match anything!') + ... + Case 3, y: 200 + +In this case, ``if flag`` is a guard. Read more about that in the next section. Guards ------ -If a ``guard`` is present in a case block, it is evaluated if and only if the -pattern(s) in a case block succeed. If this raises an exception, the exception -bubbles up. Otherwise, if the guard condition is "truthy", the case block is -selected; if it is "falsy" the case block is not selected. +.. index:: ! guard + +.. productionlist:: python-grammar + guard: 'if' `named_expression` + +A ``guard`` (which is part of the ``case``) must succeed for code inside +the ``case`` block to execute. It takes the form: :keyword:`if` followed by an +expression. Conceptually it's similar to a +:ref:`conditional expression `. An example guard:: + + >>> match 100: + ... case x if x < 0: + ... print(f'{x} is negative!') + ... case x: + ... print(f'{x} is non-negative!') + ... + 100 is non-negative! + -Since guards are expressions they are allowed to have side effects. Guard +The logical flow of a ``case`` block with a ``guard`` follows: + +#. Check that the pattern in the ``case`` block succeeded. If the pattern + failed, the ``guard`` is not evaluated and the next ``case`` block is + checked. + +#. If the pattern succeeded, evaluate the ``guard``. + + * If the ``guard`` condition evaluates to "truthy", the case block is + selected. + + * If the ``guard`` condition evaluates to "falsy", the case block is not + selected. + + * If the ``guard`` raises an exception during evaluation, the exception + bubbles up. + +Guards are allowed to have side effects as they are expressions. Guard evaluation must proceed from the first to the last case block, one at a time, skipping case blocks whose pattern(s) don't all succeed. (I.e., guard evaluation must happen in order.) Guard evaluation must stop once a case block is selected. -Irrefutable case blocks + +.. _irrefutable_case: + +Irrefutable Case Blocks ----------------------- -A pattern is considered irrefutable if we can prove from its syntax alone that -it will always succeed. In particular, capture patterns and wildcard patterns -are irrefutable, and so are :ref:`AS patterns ` whose left-hand -side is irrefutable, :ref:`OR patterns ` containing at least one -irrefutable pattern, and parenthesized irrefutable patterns. +.. index:: irrefutable case block, case block + +An irrefutable case block is a catch-all case block. A match statement may have +at most one irrefutable case block, and it must be last. A case block is considered irrefutable if it has no guard and its pattern is -irrefutable. +irrefutable. A pattern is considered irrefutable if we can prove from its +syntax alone that it will always succeed. The following patterns are +irrefutable: + +* :ref:`as_patterns` whose left-hand side is irrefutable + +* :ref:`or_patterns` containing at least one irrefutable pattern + +* :ref:`capture_patterns` + +* :ref:`wildcard_patterns` + +* parenthesized irrefutable patterns -A match statement may have at most one irrefutable case block, and it must be -last. Patterns -------- -The top-level syntax for patterns is as follows. Note that the notation -``SEP.RULE+`` is shorthand for ``RULE (SEP RULE)*``. Grammar further below -may also have the notation ``!RULE`` which is shorthand for a negative lookahead -assertion: +.. index:: + single: ! patterns + single: AS pattern, OR pattern, capture pattern, wildcard pattern + +.. note:: + This section uses grammar notations beyond standard EBNF: + + * the notation ``SEP.RULE+`` is shorthand for ``RULE (SEP RULE)*`` + + * the notation ``!RULE`` is shorthand for a negative lookahead assertion + + +The top-level syntax for ``patterns`` is: .. productionlist:: python-grammar patterns: `open_sequence_pattern` | `pattern` @@ -633,124 +716,246 @@ assertion: : | `mapping_pattern` : | `class_pattern` -.. _as_pattern: +The following is a very brief overview of the different patterns and an +approximation of their behavior (credits to Raymond Hettinger for the idea): + ++--------------------------+-------------------+----------------------------------------------------------+ +| Pattern Type | Pattern Form | Logical behavior | ++==========================+===================+==========================================================+ +| :ref:`or_patterns` | ``p1 | p2 | ...`` | 1. test ``p1`` | +| | | | +| | | 2. if failure, test ``p2`` | +| | | | +| | | 3. if failure test ... | ++--------------------------+-------------------+----------------------------------------------------------+ +| :ref:`as_patterns` | ``or_pattern`` | 1. test ``or_pattern`` | +| | :keyword:`as` | | +| | ``capture`` | 2. if success, bind names in | +| | | ``capture`` | ++--------------------------+-------------------+----------------------------------------------------------+ +| :ref:`literal_patterns` | ``"literal"`` | ``subject == "literal"`` | ++--------------------------+-------------------+----------------------------------------------------------+ +| :ref:`capture_patterns` | ``name`` | ``name = subject`` | ++--------------------------+-------------------+----------------------------------------------------------+ +| :ref:`wildcard_patterns` | ``_`` | ``pass`` | ++--------------------------+-------------------+----------------------------------------------------------+ +| :ref:`value_patterns` | ``x.y`` | ``subject == x.y`` | ++--------------------------+-------------------+----------------------------------------------------------+ +| :ref:`group_patterns` | ``( pattern )`` | test ``pattern`` | ++--------------------------+-------------------+----------------------------------------------------------+ +| :ref:`sequence_patterns` | ``[p1, p2, ...]`` | 1. check ``isinstance(subject, collections.abc.Sequence``| +| | | | +| | | 2. check ``len(subject) == len(sequence_pattern)`` | +| | | | +| | | 3. test ``p1`` against ``subject_value[0]`` | +| | | | +| | | 4. test ``p2`` against ``subject_value[1]`` | +| | | | +| | | 5. repeat step 4. for subsequent patterns and indexes | ++--------------------------+-------------------+----------------------------------------------------------+ +| :ref:`mapping_patterns` | ``{p1: p2, ...}`` | 1. check ``isinstance(subject, collections.abc.Mapping)``| +| | | | +| | | 2. test ``p1 in subject`` | +| | | | +| | | 3. test ``p2`` matches ``subject[p1]`` | +| | | | +| | | 4. repeat steps 2. and 3. for subsequent key and value | +| | | patterns. | ++--------------------------+-------------------+----------------------------------------------------------+ +| :ref:`class_patterns` | ``K(p1, name=p2)``| 1. check ``isinstance(K, type)`` | +| | | | +| | | 2. check ``isinstance(subject, K)`` | +| | | | +| | | 3. check ``hasattr(subject, name)`` | +| | | | +| | | 4. test ``p2`` matches ``subject.name`` | +| | | | +| | | 5. convert ``p1`` to a keyword pattern using | +| | | ``K.__match_args__`` and repeat steps 2. and 3. | ++--------------------------+-------------------+----------------------------------------------------------+ + +Note that this table purely for illustration purposes and **may not** reflect +the underlying implementation. Furthermore, they do not cover all valid forms. +Read the pattern's respective section to learn more. + + +.. _or_patterns: + +OR Patterns +^^^^^^^^^^^ + +.. productionlist:: python-grammar + or_pattern: '|'.`closed_pattern`+ + +``or_pattern``: An OR pattern is two or more patterns separated by vertical +bars ``|``. Only the final subpattern may be +:ref:`irrefutable `, and each subpattern +must bind the same set of names to avoid ambiguity. + +An OR pattern matches each of its subpatterns in turn to the subject value, +until one succeeds. The OR pattern is then considered successful. Otherwise, +if none of the subpatterns succeed, the OR pattern fails. + -* ``as_pattern``: An AS pattern matches the OR pattern on the left of the as - keyword against the subject. If this fails, the AS pattern fails. - Otherwise, the AS pattern binds the subject to the name on the right of the - as keyword and succeeds. Note that ``capture_pattern`` cannot be a a ``_``. +.. + @TODO: NOTE TO SELF: EVERYTHING ONWARDS FROM HERE IS A TODO. + + +.. _as_patterns: + +AS Patterns +^^^^^^^^^^^ + +.. productionlist:: python-grammar + as_pattern: `or_pattern` 'as' `capture_pattern` + +``as_pattern``: An AS pattern matches the OR pattern on the left of the as +keyword against the subject. If this fails, the AS pattern fails. +Otherwise, the AS pattern binds the subject to the name on the right of the +as keyword and succeeds. Note that ``capture_pattern`` cannot be a a ``_``. -.. _or_pattern: -* ``or_pattern``: An OR pattern is two or more patterns separated by vertical - bars ``|``. Only the final subpattern may be irrefutable, and each subpattern - must bind the same set of names to avoid ambiguity. An OR pattern matches - each of its subpatterns in turn to the subject value, until one succeeds. The - OR pattern is then considered successful. Otherwise, if none of the - subpatterns succeed, the OR pattern fails. The following belong to ``closed_pattern``: -* ``literal_pattern``: A literal pattern corresponds to most - :ref:`literals ` in Python. Syntax: +.. _literal_patterns: + +Literal Patterns +^^^^^^^^^^^^^^^^ + +``literal_pattern``: A literal pattern corresponds to most +:ref:`literals ` in Python. Syntax: + +.. productionlist:: python-grammar + literal_pattern: signed_number + : | signed_number '+' NUMBER + : | signed_number '-' NUMBER + : | strings + : | 'None' + : | 'True' + : | 'False' + : | signed_number: NUMBER | '-' NUMBER + +The rule ``strings`` and the token ``NUMBER`` are defined in the +:doc:`standard Python grammar <./grammar>`. Triple-quoted strings are +supported. Raw strings and byte strings are supported. :ref:`f-strings` are +not supported. + +The forms ``signed_number '+' NUMBER`` and ``signed_number '-' NUMBER`` are +for expressing :ref:`complex numbers `; they require a real number +on the left and an imaginary number on the right. E.g. ``3 + 4j``. + +.. _capture_patterns: - .. productionlist:: python-grammar - literal_pattern: signed_number - : | signed_number '+' NUMBER - : | signed_number '-' NUMBER - : | strings - : | 'None' - : | 'True' - : | 'False' - : | signed_number: NUMBER | '-' NUMBER +Capture Patterns +^^^^^^^^^^^^^^^^ - The rule ``strings`` and the token ``NUMBER`` are defined in the - :doc:`standard Python grammar <./grammar>`. Triple-quoted strings are - supported. Raw strings and byte strings are supported. :ref:`f-strings` are - not supported. +``capture_pattern``: A capture pattern binds the subject value to the name. +Syntax: + +.. productionlist:: python-grammar + capture_pattern: !"_" NAME - The forms ``signed_number '+' NUMBER`` and ``signed_number '-' NUMBER`` are - for expressing :ref:`complex numbers `; they require a real number - on the left and an imaginary number on the right. E.g. ``3 + 4j``. +A single underscore ``_`` is not a capture pattern (this is what ``!"_"`` +expresses). And is instead treated as a :token:`wildcard_pattern`. -* ``capture_pattern``: A capture pattern binds the subject value to the name. - Syntax: +In a given pattern, a given name can only be bound once. E.g. +``case x, x: ...`` is invalid while ``case [x] | x: ...`` is allowed. - .. productionlist:: python-grammar - capture_pattern: !"_" NAME +Capture patterns always succeed. The binding follows scoping rules +established by the assignment expression operator in :pep`572`, where the +name becomes a local variable in the closest containing function scope unless +there's an applicable :keyword:`global` or :keyword:`nonlocal` statement. - A single underscore ``_`` is not a capture pattern (this is what ``!"_"`` - expresses). And is instead treated as a :token:`wildcard_pattern`. +.. _wildcard_patterns: - In a given pattern, a given name can only be bound once. E.g. - ``case x, x: ...`` is invalid while ``case [x] | x: ...`` is allowed. +Wildcard Patterns +^^^^^^^^^^^^^^^^^ - Capture patterns always succeed. The binding follows scoping rules - established by the assignment expression operator in :pep`572`, where the - name becomes a local variable in the closest containing function scope unless - there's an applicable :keyword:`global` or :keyword:`nonlocal` statement. +``wildcard_pattern``: A wildcard pattern always succeeds (matches anything) +and binds no name. Syntax: -* ``wildcard_pattern``: A wildcard pattern always succeeds (matches anything) - and binds no name. Syntax: +.. productionlist:: python-grammar + wildcard_pattern: "_" - .. productionlist:: python-grammar - wildcard_pattern: "_" +.. _value_patterns: -* ``value_pattern``: A value pattern corresponds to a named value in Python. - Syntax: +Value Patterns +^^^^^^^^^^^^^^ - .. productionlist:: python-grammar - value_pattern: `attr` - attr: `name_or_attr` '.' NAME - name_or_attr: `attr` | NAME +``value_pattern``: A value pattern corresponds to a named value in Python. +Syntax: - The dotted name in the pattern is looked up using standard Python - :ref:`name resolution rules `. The pattern succeeds if the - value found compares equal to the subject value (using the ``==`` equality - operator). +.. productionlist:: python-grammar + value_pattern: `attr` + attr: `name_or_attr` '.' NAME + name_or_attr: `attr` | NAME - .. note:: +The dotted name in the pattern is looked up using standard Python +:ref:`name resolution rules `. The pattern succeeds if the +value found compares equal to the subject value (using the ``==`` equality +operator). - If the same value occurs multiple times in the same match statement, the - interpreter may cache the first value found and reuse it rather than repeat - the same lookup. This cache is strictly tied to a given execution of a - given match statement. +.. note:: -* ``group_pattern``: TODO + If the same value occurs multiple times in the same match statement, the + interpreter may cache the first value found and reuse it rather than repeat + the same lookup. This cache is strictly tied to a given execution of a + given match statement. - .. productionlist:: python-grammar - group_pattern: '(' `pattern` ')' +.. _group_patterns: +Group Patterns +^^^^^^^^^^^^^^ -* ``sequence_pattern``: TODO +``group_pattern``: TODO - .. productionlist:: python-grammar - sequence_pattern: '[' [`maybe_sequence_pattern`] ']' - : | '(' [`open_sequence_pattern`] ')' - open_sequence_pattern: `maybe_star_pattern` ',' [`maybe_sequence_pattern`] - maybe_sequence_pattern: ','.`maybe_star_pattern`+ ','? - maybe_star_pattern: `star_pattern` | `pattern` - star_pattern: '*' (`capture_pattern` | `wildcard_pattern`) +.. productionlist:: python-grammar + group_pattern: '(' `pattern` ')' +.. _sequence_patterns: -* ``mapping_pattern``: TODO +Sequence Patterns +^^^^^^^^^^^^^^^^^ - .. productionlist:: python-grammar - mapping_pattern: '{' [`items_pattern`] '}' - items_pattern: ','.`key_value_pattern`+ ','? - key_value_pattern: (`literal_pattern` | `value_pattern`) ':' `pattern` - : | `double_star_pattern` - double_star_pattern: '**' `capture_pattern` +``sequence_pattern``: TODO -* ``class_pattern``: TODO +.. productionlist:: python-grammar + sequence_pattern: '[' [`maybe_sequence_pattern`] ']' + : | '(' [`open_sequence_pattern`] ')' + open_sequence_pattern: `maybe_star_pattern` ',' [`maybe_sequence_pattern`] + maybe_sequence_pattern: ','.`maybe_star_pattern`+ ','? + maybe_star_pattern: `star_pattern` | `pattern` + star_pattern: '*' (`capture_pattern` | `wildcard_pattern`) - .. productionlist:: python-grammar - class_pattern: `name_or_attr` '(' [`pattern_arguments` ','?] ')' - pattern_arguments: `positional_patterns` [',' `keyword_patterns`] - : | `keyword_patterns` - positional_patterns: ','.`pattern`+ - keyword_patterns: ','.`keyword_pattern`+ - keyword_pattern: NAME '=' `pattern` +.. _mapping_patterns: + +Mapping Patterns +^^^^^^^^^^^^^^^^ + +``mapping_pattern``: TODO + +.. productionlist:: python-grammar + mapping_pattern: '{' [`items_pattern`] '}' + items_pattern: ','.`key_value_pattern`+ ','? + key_value_pattern: (`literal_pattern` | `value_pattern`) ':' `pattern` + : | `double_star_pattern` + double_star_pattern: '**' `capture_pattern` + +.. _class_patterns: + +Class Patterns +^^^^^^^^^^^^^^ + +``class_pattern``: TODO + +.. productionlist:: python-grammar + class_pattern: `name_or_attr` '(' [`pattern_arguments` ','?] ')' + pattern_arguments: `positional_patterns` [',' `keyword_patterns`] + : | `keyword_patterns` + positional_patterns: ','.`pattern`+ + keyword_patterns: ','.`keyword_pattern`+ + keyword_pattern: NAME '=' `pattern` Full Grammar diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst index 048b2d0cc1aba1..74609e01b13434 100644 --- a/Doc/reference/lexical_analysis.rst +++ b/Doc/reference/lexical_analysis.rst @@ -351,14 +351,13 @@ exactly as written here: assert del global not with async elif if or yield + .. _soft_keywords: Soft Keywords ------------- -.. index:: - single: soft keyword; keyword - pair: soft; keyword +.. index:: soft keyword, keyword .. versionadded:: 3.10 From 2790c6efe48ab90d36d808a2957e8c4a1112caea Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 24 Feb 2021 16:05:12 +0800 Subject: [PATCH 14/34] done! --- Doc/reference/compound_stmts.rst | 332 +++++++++++++++++++++---------- 1 file changed, 226 insertions(+), 106 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 70b77e6bfb6cc8..4b5c34002c98de 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -535,6 +535,10 @@ The match statement is used for pattern matching. Syntax: : | `named_expression` case_block: "case" `patterns` [`guard`] ':' `block` +The rules ``star_named_expression``, ``star_named_expressions``, +``named_expression`` and ``block`` are part of the +:doc:`standard Python grammar <./grammar>`. + Pattern matching takes a pattern as input (following ``case``) and a subject value (following ``match``). The pattern (which may contain subpatterns) is matched against the subject value. The outcomes are: @@ -553,18 +557,18 @@ This means ``match`` and ``case`` have the following characteristics: * They are recognized as keywords when part of a match statement or case block only. -* They can be used in all other contexts as variable or argument names +* They can be used in all other contexts as variable or argument names. +.. seealso:: + + * :pep:`634` -- Structural Pattern Matching: Specification + * :pep:`636` -- Structural Pattern Matching: Tutorial -The rules ``star_named_expression``, ``star_named_expressions``, -``named_expression`` and ``block`` are part of the -:doc:`standard Python grammar <./grammar>`. While ``patterns`` is specified -further below. Overview -------- -Here's an overview of the logical flow: +Here's an overview of the logical flow of a match statement: #. The subject expression ``subject_expr`` is evaluated and a resulting subject value obtained. If the subject expression contains a comma, a tuple is @@ -666,7 +670,7 @@ Irrefutable Case Blocks .. index:: irrefutable case block, case block -An irrefutable case block is a catch-all case block. A match statement may have +An irrefutable case block is a match-all case block. A match statement may have at most one irrefutable case block, and it must be last. A case block is considered irrefutable if it has no guard and its pattern is @@ -733,13 +737,13 @@ approximation of their behavior (credits to Raymond Hettinger for the idea): | | ``capture`` | 2. if success, bind names in | | | | ``capture`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`literal_patterns` | ``"literal"`` | ``subject == "literal"`` | +| :ref:`literal_patterns` | ``"literal"`` | test ``subject == "literal"`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`capture_patterns` | ``name`` | ``name = subject`` | +| :ref:`capture_patterns` | ``name`` | test ``name = subject`` | +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`wildcard_patterns` | ``_`` | ``pass`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`value_patterns` | ``x.y`` | ``subject == x.y`` | +| :ref:`value_patterns` | ``x.y`` | test ``subject == x.y`` | +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`group_patterns` | ``( pattern )`` | test ``pattern`` | +--------------------------+-------------------+----------------------------------------------------------+ @@ -776,7 +780,7 @@ approximation of their behavior (credits to Raymond Hettinger for the idea): Note that this table purely for illustration purposes and **may not** reflect the underlying implementation. Furthermore, they do not cover all valid forms. -Read the pattern's respective section to learn more. +Read the pattern's respective sections to learn more. .. _or_patterns: @@ -784,57 +788,51 @@ Read the pattern's respective section to learn more. OR Patterns ^^^^^^^^^^^ +An OR pattern is two or more patterns separated by vertical +bars ``|``. Syntax: + .. productionlist:: python-grammar or_pattern: '|'.`closed_pattern`+ -``or_pattern``: An OR pattern is two or more patterns separated by vertical -bars ``|``. Only the final subpattern may be -:ref:`irrefutable `, and each subpattern -must bind the same set of names to avoid ambiguity. +Only the final subpattern may be :ref:`irrefutable `, and each +subpattern must bind the same set of names to avoid ambiguity. An OR pattern matches each of its subpatterns in turn to the subject value, until one succeeds. The OR pattern is then considered successful. Otherwise, if none of the subpatterns succeed, the OR pattern fails. - -.. - @TODO: NOTE TO SELF: EVERYTHING ONWARDS FROM HERE IS A TODO. - - .. _as_patterns: AS Patterns ^^^^^^^^^^^ +An AS pattern matches an OR pattern on the left of the :keyword:`as` +keyword against a subject. Syntax: + .. productionlist:: python-grammar as_pattern: `or_pattern` 'as' `capture_pattern` -``as_pattern``: An AS pattern matches the OR pattern on the left of the as -keyword against the subject. If this fails, the AS pattern fails. -Otherwise, the AS pattern binds the subject to the name on the right of the -as keyword and succeeds. Note that ``capture_pattern`` cannot be a a ``_``. - - - -The following belong to ``closed_pattern``: +If the OR pattern fails, the AS pattern fails. Otherwise, the AS pattern binds +the subject to the name on the right of the as keyword and succeeds. +``capture_pattern`` cannot be a a ``_``. .. _literal_patterns: Literal Patterns ^^^^^^^^^^^^^^^^ -``literal_pattern``: A literal pattern corresponds to most +A literal pattern corresponds to most :ref:`literals ` in Python. Syntax: .. productionlist:: python-grammar - literal_pattern: signed_number - : | signed_number '+' NUMBER - : | signed_number '-' NUMBER - : | strings - : | 'None' - : | 'True' - : | 'False' - : | signed_number: NUMBER | '-' NUMBER + literal_pattern: `signed_number` + : | `signed_number` '+' NUMBER + : | `signed_number` '-' NUMBER + : | `strings` + : | 'None' + : | 'True' + : | 'False' + : | `signed_number`: NUMBER | '-' NUMBER The rule ``strings`` and the token ``NUMBER`` are defined in the :doc:`standard Python grammar <./grammar>`. Triple-quoted strings are @@ -850,11 +848,11 @@ on the left and an imaginary number on the right. E.g. ``3 + 4j``. Capture Patterns ^^^^^^^^^^^^^^^^ -``capture_pattern``: A capture pattern binds the subject value to the name. +A capture pattern binds the subject value to a name. Syntax: .. productionlist:: python-grammar - capture_pattern: !"_" NAME + capture_pattern: !"_" NAME A single underscore ``_`` is not a capture pattern (this is what ``!"_"`` expresses). And is instead treated as a :token:`wildcard_pattern`. @@ -863,7 +861,7 @@ In a given pattern, a given name can only be bound once. E.g. ``case x, x: ...`` is invalid while ``case [x] | x: ...`` is allowed. Capture patterns always succeed. The binding follows scoping rules -established by the assignment expression operator in :pep`572`, where the +established by the assignment expression operator in :pep`572`; the name becomes a local variable in the closest containing function scope unless there's an applicable :keyword:`global` or :keyword:`nonlocal` statement. @@ -872,24 +870,24 @@ there's an applicable :keyword:`global` or :keyword:`nonlocal` statement. Wildcard Patterns ^^^^^^^^^^^^^^^^^ -``wildcard_pattern``: A wildcard pattern always succeeds (matches anything) +A wildcard pattern always succeeds (matches anything) and binds no name. Syntax: .. productionlist:: python-grammar - wildcard_pattern: "_" + wildcard_pattern: "_" .. _value_patterns: Value Patterns ^^^^^^^^^^^^^^ -``value_pattern``: A value pattern corresponds to a named value in Python. +A value pattern represents a named value in Python. Syntax: .. productionlist:: python-grammar - value_pattern: `attr` - attr: `name_or_attr` '.' NAME - name_or_attr: `attr` | NAME + value_pattern: `attr` + attr: `name_or_attr` '.' NAME + name_or_attr: `attr` | NAME The dotted name in the pattern is looked up using standard Python :ref:`name resolution rules `. The pattern succeeds if the @@ -898,27 +896,30 @@ operator). .. note:: - If the same value occurs multiple times in the same match statement, the - interpreter may cache the first value found and reuse it rather than repeat - the same lookup. This cache is strictly tied to a given execution of a - given match statement. + If the same value occurs multiple times in the same match statement, the + interpreter may cache the first value found and reuse it rather than repeat + the same lookup. This cache is strictly tied to a given execution of a + given match statement. .. _group_patterns: Group Patterns ^^^^^^^^^^^^^^ -``group_pattern``: TODO +A group pattern allows users to add parentheses around patterns to +emphasize the intended grouping. Otherwise, it has no additional syntax. +Syntax: .. productionlist:: python-grammar - group_pattern: '(' `pattern` ')' + group_pattern: '(' `pattern` ')' .. _sequence_patterns: Sequence Patterns ^^^^^^^^^^^^^^^^^ -``sequence_pattern``: TODO +A sequence pattern contains a sequence of subpatterns. The syntax is +similar to the construction of a list or tuple. .. productionlist:: python-grammar sequence_pattern: '[' [`maybe_sequence_pattern`] ']' @@ -928,26 +929,113 @@ Sequence Patterns maybe_star_pattern: `star_pattern` | `pattern` star_pattern: '*' (`capture_pattern` | `wildcard_pattern`) + +There is no difference if parentheses (``(...)``) or square brackets (``[...]``) +are used for sequence patterns. + +.. note:: + A single pattern enclosed in parentheses without a trailing comma + (e.g. ``(3 | 4)``) is a :ref:`group pattern `. + While a single pattern enclosed in square brackets (e.g. ``[3 | 4]``) is + still a sequence pattern. + +At most one star subpattern may be in a sequence pattern. The star subpattern +may occur in any position. If no star subpattern is present, the sequence +pattern is a fixed-length sequence pattern; otherwise it is a variable-length +sequence pattern. + +The following is the logical flow for matching a sequence pattern against a +subject value: + +#. If the subject value is not an instance of a + :class:`collections.abc.Sequence` the sequence pattern fails. + +#. If the subject value is an instance of ``str``, ``bytes`` or ``bytearray`` + the sequence pattern fails. + +#. The subsequent steps depend on whether the sequence pattern is fixed or + variable-length. + + If the sequence pattern is fixed-length: + + #. If the length of the subject sequence is not equal to the number of + subpatterns, the sequence pattern fails + + #. Subpatterns in the sequence pattern are matched to their corresponding + items in the subject sequence from left to right. Matching stops as soon + as a subpattern fails. If all subpatterns succeed in matching their + corresponding item, the sequence pattern succeeds. + + Otherwise, if the sequence pattern is variable-length: + + #. If the length of the subject sequence is less than the number of non-star + subpatterns, the sequence pattern fails. + + #. The leading non-star subpatterns are matched to their corresponding items + as for fixed-length sequences. + + #. If the previous step succeeds, the star subpattern matches a list formed + of the remaining subject items, excluding the remaining items + corresponding to non-star subpatterns following the star subpattern. + + #. Remaining non-star subpatterns are matched to their corresponding subject + items, as for a fixed-length sequence. + + .. note:: The length of the subject sequence is obtained via + :func:`len` (i.e. via the :meth:`__len__` protocol). This length may be + cached by the interpreter in a similar manner as + :ref:`value patterns `. + + .. _mapping_patterns: Mapping Patterns ^^^^^^^^^^^^^^^^ -``mapping_pattern``: TODO +A mapping pattern contains one or more key-value patterns. The syntax is +similar to the construction of a dictionary. +Syntax: .. productionlist:: python-grammar - mapping_pattern: '{' [`items_pattern`] '}' - items_pattern: ','.`key_value_pattern`+ ','? - key_value_pattern: (`literal_pattern` | `value_pattern`) ':' `pattern` - : | `double_star_pattern` - double_star_pattern: '**' `capture_pattern` + mapping_pattern: '{' [`items_pattern`] '}' + items_pattern: ','.`key_value_pattern`+ ','? + key_value_pattern: (`literal_pattern` | `value_pattern`) ':' `pattern` + : | `double_star_pattern` + double_star_pattern: '**' `capture_pattern` + +At most one double star pattern may be in a mapping pattern. The double star +pattern must be the last subpattern in the mapping pattern. + +Duplicate key values in mapping patterns are disallowed. (If all key patterns +are literal patterns this is considered a syntax error; otherwise this is a +runtime error and will raise :exc:`ValueError`.) + +The following is the logical flow for matching a mapping pattern against a +subject value: + +#. If the subject value is not an instance of :class:`collections.abc.Mapping`, + the mapping pattern fails. + +#. If every key given in the mapping pattern is present in the subject mapping, + and the pattern for each key matches the corresponding item of the subject + mapping, the mapping pattern succeeds. + +#. If duplicate keys are detected in the mapping pattern, the pattern is + considered invalid and :exc:`ValueError` is raised. + +.. note:: Key-value pairs are matched using the two-argument form of the mapping + subject's ``get()`` method. Matched key-value pairs must already be present + in the mapping, and not created on-the-fly via :meth:`__missing__` or + :meth:`__getitem__`. + .. _class_patterns: Class Patterns ^^^^^^^^^^^^^^ -``class_pattern``: TODO +A class pattern represents a class and its positional and keyword arguments +(if any). Syntax: .. productionlist:: python-grammar class_pattern: `name_or_attr` '(' [`pattern_arguments` ','?] ')' @@ -957,54 +1045,86 @@ Class Patterns keyword_patterns: ','.`keyword_pattern`+ keyword_pattern: NAME '=' `pattern` +The same keyword should not be repeated in class patterns. -Full Grammar ------------- +The following is the logical flow for matching a mapping pattern against a +subject value: + +#. If ``name_or_attr`` is not an instance of the builtin :class:`type` , raise + :exc:`TypeError`. + +#. If the subject value is not an instance of ``name_or_attr`` (tested via + :func:`isinstance`), the class pattern fails. + +#. If no pattern arguments are present, the pattern succeeds. Otherwise, + the subsequent steps depend on whether keyword or positional argument patterns + are present. + + For a number of built-in types (specified below), a single positional + subpattern is accepted which will match the entire subject; for these types + no keyword patterns are accepted. + + If only keyword patterns are present, they are processed as follows, + one by one: + + I. The keyword is looked up as an attribute on the subject. + + * If this raises an exception other than :exc:`AttributeError`, the + exception bubbles up. + + * If this raises :exc:`AttributeError`, the class pattern has failed. + + * Else, the subpattern associated with the keyword pattern is matched + against the subject's attribute value. If this fails, the class + pattern fails; if this succeeds, the match proceeds to the next keyword. + + + II. If all keyword patterns succeed, the class pattern succeeds. + + If any positional patterns are present, they are converted to keyword + patterns using the ``__match_args__`` attribute on the class + ``name_or_attr`` before matching: + + I. The equivalent of getattr(cls, "__match_args__", ())) is called. + + * If this raises an exception, the exception bubbles up. + + * If the returned value is not a list or tuple, the conversion fails and + :exc:`TypeError` is raised. + + * If there are more positional patterns than ``len(cls.__match_args__)``, + :exc:`TypeError` is raised. + + * Otherwise, positional pattern ``i`` is converted to a keyword pattern + using ``__match_args__[i]`` as the keyword. ``__match_args__[i]`` must + be a string; if not :exc:`TypeError` is raised. + + * If there are duplicate keywords, :exc:`TypeError` is raised. + + II. Once all positional patterns have been converted to keyword patterns, + the match proceeds as if there were only keyword patterns. + + For the following built-in types the handling of positional subpatterns is + different: + + * :class:`bool` + * :class:`bytearray` + * :class:`bytes` + * :class:`dict` + * :class:`float` + * :class:`frozenset` + * :class:`int` + * :class:`list` + * :class:`set` + * :class:`str` + * :class:`tuple` + + +.. seealso:: + + * :pep:`634` -- Structural Pattern Matching: Specification + * :pep:`636` -- Structural Pattern Matching: Tutorial -.. productionlist:: python-grammar - patterns: open_sequence_pattern | pattern - pattern: as_pattern | or_pattern - as_pattern: or_pattern 'as' capture_pattern - or_pattern: '|'.closed_pattern+ - closed_pattern: literal_pattern - : | capture_pattern - : | wildcard_pattern - : | value_pattern - : | group_pattern - : | sequence_pattern - : | mapping_pattern - : | class_pattern - literal_pattern: signed_number !('+' | '-') - : | signed_number '+' NUMBER - : | signed_number '-' NUMBER - : | strings - : | 'None' - : | 'True' - : | 'False' - signed_number: NUMBER | '-' NUMBER - capture_pattern: !"_" NAME !('.' | '(' | '=') - wildcard_pattern: "_" - value_pattern: attr !('.' | '(' | '=') - attr: name_or_attr '.' NAME - name_or_attr: attr | NAME - group_pattern: '(' pattern ')' - sequence_pattern: '[' [maybe_sequence_pattern] ']' - : | '(' [open_sequence_pattern] ')' - open_sequence_pattern: maybe_star_pattern ',' [maybe_sequence_pattern] - maybe_sequence_pattern: ','.maybe_star_pattern+ ','? - maybe_star_pattern: star_pattern | pattern - star_pattern: '*' (capture_pattern | wildcard_pattern) - mapping_pattern: '{' [items_pattern] '}' - items_pattern: ','.key_value_pattern+ ','? - key_value_pattern: | (literal_pattern | value_pattern) ':' pattern - : | double_star_pattern - double_star_pattern: '**' capture_pattern - class_pattern: name_or_attr '(' [pattern_arguments ','?] ')' - pattern_arguments: positional_patterns [',' keyword_patterns] - : | keyword_patterns - positional_patterns: ','.pattern+ - keyword_patterns: ','.keyword_pattern+ - keyword_pattern: NAME '=' pattern .. index:: single: parameter; function definition From 82aa7dc8f0d8d9b76f682493d7a766e6ee6b3a2c Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 27 Feb 2021 15:16:55 +0800 Subject: [PATCH 15/34] Apply suggested changes from code review by Daniel (part 1) Co-authored-by: Daniel F Moisset --- Doc/reference/compound_stmts.rst | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 4b5c34002c98de..337f10f458693c 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -535,9 +535,6 @@ The match statement is used for pattern matching. Syntax: : | `named_expression` case_block: "case" `patterns` [`guard`] ':' `block` -The rules ``star_named_expression``, ``star_named_expressions``, -``named_expression`` and ``block`` are part of the -:doc:`standard Python grammar <./grammar>`. Pattern matching takes a pattern as input (following ``case``) and a subject value (following ``match``). The pattern (which may contain subpatterns) is @@ -549,15 +546,6 @@ matched against the subject value. The outcomes are: further discussed below. The ``match`` and ``case`` keywords are :ref:`soft keywords `. -This means ``match`` and ``case`` have the following characteristics: - -* They are not reserved words in other grammatical contexts, including at the - start of a line if there is no colon where expected. - -* They are recognized as keywords when part of a match statement or case block - only. - -* They can be used in all other contexts as variable or argument names. .. seealso:: @@ -627,16 +615,7 @@ Guards A ``guard`` (which is part of the ``case``) must succeed for code inside the ``case`` block to execute. It takes the form: :keyword:`if` followed by an -expression. Conceptually it's similar to a -:ref:`conditional expression `. An example guard:: - - >>> match 100: - ... case x if x < 0: - ... print(f'{x} is negative!') - ... case x: - ... print(f'{x} is non-negative!') - ... - 100 is non-negative! +expression. The logical flow of a ``case`` block with a ``guard`` follows: @@ -734,12 +713,12 @@ approximation of their behavior (credits to Raymond Hettinger for the idea): +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`as_patterns` | ``or_pattern`` | 1. test ``or_pattern`` | | | :keyword:`as` | | -| | ``capture`` | 2. if success, bind names in | +| | ``capture`` | 2. if success, bind name into | | | | ``capture`` | +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`literal_patterns` | ``"literal"`` | test ``subject == "literal"`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`capture_patterns` | ``name`` | test ``name = subject`` | +| :ref:`capture_patterns` | ``name`` | bind ``name = subject`` | +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`wildcard_patterns` | ``_`` | ``pass`` | +--------------------------+-------------------+----------------------------------------------------------+ From ab3ca5a8e1e796febb869189856d3aea0f8fb509 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 27 Feb 2021 15:30:25 +0800 Subject: [PATCH 16/34] Apply final suggestions --- Doc/reference/compound_stmts.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 337f10f458693c..74d10bb1f1d4c6 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -711,9 +711,9 @@ approximation of their behavior (credits to Raymond Hettinger for the idea): | | | | | | | 3. if failure test ... | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`as_patterns` | ``or_pattern`` | 1. test ``or_pattern`` | +| :ref:`as_patterns` | ``pattern`` | 1. test ``patttern`` | | | :keyword:`as` | | -| | ``capture`` | 2. if success, bind name into | +| | ``capture`` | 2. if success, bind name into | | | | ``capture`` | +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`literal_patterns` | ``"literal"`` | test ``subject == "literal"`` | @@ -722,7 +722,9 @@ approximation of their behavior (credits to Raymond Hettinger for the idea): +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`wildcard_patterns` | ``_`` | ``pass`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`value_patterns` | ``x.y`` | test ``subject == x.y`` | +| :ref:`value_patterns` | ``x.y`` | For strings and numbers, test ``subject == x.y`` | +| | | Otherwise, for singletons like ``None`` and :func:`bool`,| +| | | test ``subject is x.y`` | +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`group_patterns` | ``( pattern )`` | test ``pattern`` | +--------------------------+-------------------+----------------------------------------------------------+ @@ -855,6 +857,8 @@ and binds no name. Syntax: .. productionlist:: python-grammar wildcard_pattern: "_" +``_`` is a :ref:`soft keyword `. + .. _value_patterns: Value Patterns From 26ef46d10087af59b2e242fa7c105d1353e8baad Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 27 Feb 2021 15:52:14 +0800 Subject: [PATCH 17/34] Add link, make grammar and links consistent with rest of docs --- Doc/reference/compound_stmts.rst | 123 +++++++++++++++-------------- Doc/reference/lexical_analysis.rst | 2 +- 2 files changed, 64 insertions(+), 61 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 74d10bb1f1d4c6..c9e1ed5d6c85eb 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -530,11 +530,14 @@ The :keyword:`!match` statement The match statement is used for pattern matching. Syntax: .. productionlist:: python-grammar - match_stmt: "match" `subject_expr` ':' NEWLINE INDENT `case_block`+ DEDENT - subject_expr: `star_named_expression` ',' `star_named_expressions`? + match_stmt: 'match' `subject_expr` ":" NEWLINE INDENT `case_block`+ DEDENT + subject_expr: `star_named_expression` "," `star_named_expressions`? : | `named_expression` - case_block: "case" `patterns` [`guard`] ':' `block` + case_block: 'case' `patterns` [`guard`] ':' `block` +.. note:: + This section uses single quotes to denote + :ref:`soft keywords `. Pattern matching takes a pattern as input (following ``case``) and a subject value (following ``match``). The pattern (which may contain subpatterns) is @@ -545,7 +548,7 @@ matched against the subject value. The outcomes are: * Possible binding of matched values to a name. The prerequisites for this are further discussed below. -The ``match`` and ``case`` keywords are :ref:`soft keywords `. +The ``match`` and ``case`` keywords are :ref:`soft keywords `. .. seealso:: @@ -611,7 +614,7 @@ Guards .. index:: ! guard .. productionlist:: python-grammar - guard: 'if' `named_expression` + guard: "if" `named_expression` A ``guard`` (which is part of the ``case``) must succeed for code inside the ``case`` block to execute. It takes the form: :keyword:`if` followed by an @@ -657,13 +660,13 @@ irrefutable. A pattern is considered irrefutable if we can prove from its syntax alone that it will always succeed. The following patterns are irrefutable: -* :ref:`as_patterns` whose left-hand side is irrefutable +* :ref:`as-patterns` whose left-hand side is irrefutable -* :ref:`or_patterns` containing at least one irrefutable pattern +* :ref:`or-patterns` containing at least one irrefutable pattern -* :ref:`capture_patterns` +* :ref:`capture-patterns` -* :ref:`wildcard_patterns` +* :ref:`wildcard-patterns` * parenthesized irrefutable patterns @@ -688,8 +691,8 @@ The top-level syntax for ``patterns`` is: .. productionlist:: python-grammar patterns: `open_sequence_pattern` | `pattern` pattern: `as_pattern` | `or_pattern` - as_pattern: `or_pattern` 'as' `capture_pattern` - or_pattern: '|'.`closed_pattern`+ + as_pattern: `or_pattern` "as" `capture_pattern` + or_pattern: "|".`closed_pattern`+ closed_pattern: | `literal_pattern` : | `capture_pattern` : | `wildcard_pattern` @@ -705,30 +708,30 @@ approximation of their behavior (credits to Raymond Hettinger for the idea): +--------------------------+-------------------+----------------------------------------------------------+ | Pattern Type | Pattern Form | Logical behavior | +==========================+===================+==========================================================+ -| :ref:`or_patterns` | ``p1 | p2 | ...`` | 1. test ``p1`` | +| :ref:`or-patterns` | ``p1 | p2 | ...`` | 1. test ``p1`` | | | | | | | | 2. if failure, test ``p2`` | | | | | | | | 3. if failure test ... | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`as_patterns` | ``pattern`` | 1. test ``patttern`` | +| :ref:`as-patterns` | ``pattern`` | 1. test ``patttern`` | | | :keyword:`as` | | | | ``capture`` | 2. if success, bind name into | | | | ``capture`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`literal_patterns` | ``"literal"`` | test ``subject == "literal"`` | +| :ref:`literal-patterns` | ``"literal"`` | test ``subject == "literal"`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`capture_patterns` | ``name`` | bind ``name = subject`` | +| :ref:`capture-patterns` | ``name`` | bind ``name = subject`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`wildcard_patterns` | ``_`` | ``pass`` | +| :ref:`wildcard-patterns` | ``_`` | ``pass`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`value_patterns` | ``x.y`` | For strings and numbers, test ``subject == x.y`` | +| :ref:`value-patterns` | ``x.y`` | For strings and numbers, test ``subject == x.y`` | | | | Otherwise, for singletons like ``None`` and :func:`bool`,| | | | test ``subject is x.y`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`group_patterns` | ``( pattern )`` | test ``pattern`` | +| :ref:`group-patterns` | ``( pattern )`` | test ``pattern`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`sequence_patterns` | ``[p1, p2, ...]`` | 1. check ``isinstance(subject, collections.abc.Sequence``| +| :ref:`sequence-patterns` | ``[p1, p2, ...]`` | 1. check ``isinstance(subject, collections.abc.Sequence``| | | | | | | | 2. check ``len(subject) == len(sequence_pattern)`` | | | | | @@ -738,7 +741,7 @@ approximation of their behavior (credits to Raymond Hettinger for the idea): | | | | | | | 5. repeat step 4. for subsequent patterns and indexes | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`mapping_patterns` | ``{p1: p2, ...}`` | 1. check ``isinstance(subject, collections.abc.Mapping)``| +| :ref:`mapping-patterns` | ``{p1: p2, ...}`` | 1. check ``isinstance(subject, collections.abc.Mapping)``| | | | | | | | 2. test ``p1 in subject`` | | | | | @@ -747,7 +750,7 @@ approximation of their behavior (credits to Raymond Hettinger for the idea): | | | 4. repeat steps 2. and 3. for subsequent key and value | | | | patterns. | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`class_patterns` | ``K(p1, name=p2)``| 1. check ``isinstance(K, type)`` | +| :ref:`class-patterns` | ``K(p1, name=p2)``| 1. check ``isinstance(K, type)`` | | | | | | | | 2. check ``isinstance(subject, K)`` | | | | | @@ -764,7 +767,7 @@ the underlying implementation. Furthermore, they do not cover all valid forms. Read the pattern's respective sections to learn more. -.. _or_patterns: +.. _or-patterns: OR Patterns ^^^^^^^^^^^ @@ -773,7 +776,7 @@ An OR pattern is two or more patterns separated by vertical bars ``|``. Syntax: .. productionlist:: python-grammar - or_pattern: '|'.`closed_pattern`+ + or_pattern: "|".`closed_pattern`+ Only the final subpattern may be :ref:`irrefutable `, and each subpattern must bind the same set of names to avoid ambiguity. @@ -782,7 +785,7 @@ An OR pattern matches each of its subpatterns in turn to the subject value, until one succeeds. The OR pattern is then considered successful. Otherwise, if none of the subpatterns succeed, the OR pattern fails. -.. _as_patterns: +.. _as-patterns: AS Patterns ^^^^^^^^^^^ @@ -791,13 +794,13 @@ An AS pattern matches an OR pattern on the left of the :keyword:`as` keyword against a subject. Syntax: .. productionlist:: python-grammar - as_pattern: `or_pattern` 'as' `capture_pattern` + as_pattern: `or_pattern` "as" `capture_pattern` If the OR pattern fails, the AS pattern fails. Otherwise, the AS pattern binds the subject to the name on the right of the as keyword and succeeds. ``capture_pattern`` cannot be a a ``_``. -.. _literal_patterns: +.. _literal-patterns: Literal Patterns ^^^^^^^^^^^^^^^^ @@ -807,13 +810,13 @@ A literal pattern corresponds to most .. productionlist:: python-grammar literal_pattern: `signed_number` - : | `signed_number` '+' NUMBER - : | `signed_number` '-' NUMBER + : | `signed_number` "+" NUMBER + : | `signed_number` "-" NUMBER : | `strings` - : | 'None' - : | 'True' - : | 'False' - : | `signed_number`: NUMBER | '-' NUMBER + : | "None" + : | "True" + : | "False" + : | `signed_number`: NUMBER | "-" NUMBER The rule ``strings`` and the token ``NUMBER`` are defined in the :doc:`standard Python grammar <./grammar>`. Triple-quoted strings are @@ -824,7 +827,7 @@ The forms ``signed_number '+' NUMBER`` and ``signed_number '-' NUMBER`` are for expressing :ref:`complex numbers `; they require a real number on the left and an imaginary number on the right. E.g. ``3 + 4j``. -.. _capture_patterns: +.. _capture-patterns: Capture Patterns ^^^^^^^^^^^^^^^^ @@ -833,9 +836,9 @@ A capture pattern binds the subject value to a name. Syntax: .. productionlist:: python-grammar - capture_pattern: !"_" NAME + capture_pattern: !'_' NAME -A single underscore ``_`` is not a capture pattern (this is what ``!"_"`` +A single underscore ``_`` is not a capture pattern (this is what ``!'_'`` expresses). And is instead treated as a :token:`wildcard_pattern`. In a given pattern, a given name can only be bound once. E.g. @@ -846,7 +849,7 @@ established by the assignment expression operator in :pep`572`; the name becomes a local variable in the closest containing function scope unless there's an applicable :keyword:`global` or :keyword:`nonlocal` statement. -.. _wildcard_patterns: +.. _wildcard-patterns: Wildcard Patterns ^^^^^^^^^^^^^^^^^ @@ -855,11 +858,11 @@ A wildcard pattern always succeeds (matches anything) and binds no name. Syntax: .. productionlist:: python-grammar - wildcard_pattern: "_" + wildcard_pattern: '_' -``_`` is a :ref:`soft keyword `. +``_`` is a :ref:`soft keyword `. -.. _value_patterns: +.. _value-patterns: Value Patterns ^^^^^^^^^^^^^^ @@ -869,7 +872,7 @@ Syntax: .. productionlist:: python-grammar value_pattern: `attr` - attr: `name_or_attr` '.' NAME + attr: `name_or_attr` "." NAME name_or_attr: `attr` | NAME The dotted name in the pattern is looked up using standard Python @@ -884,7 +887,7 @@ operator). the same lookup. This cache is strictly tied to a given execution of a given match statement. -.. _group_patterns: +.. _group-patterns: Group Patterns ^^^^^^^^^^^^^^ @@ -896,7 +899,7 @@ Syntax: .. productionlist:: python-grammar group_pattern: '(' `pattern` ')' -.. _sequence_patterns: +.. _sequence-patterns: Sequence Patterns ^^^^^^^^^^^^^^^^^ @@ -905,12 +908,12 @@ A sequence pattern contains a sequence of subpatterns. The syntax is similar to the construction of a list or tuple. .. productionlist:: python-grammar - sequence_pattern: '[' [`maybe_sequence_pattern`] ']' - : | '(' [`open_sequence_pattern`] ')' - open_sequence_pattern: `maybe_star_pattern` ',' [`maybe_sequence_pattern`] - maybe_sequence_pattern: ','.`maybe_star_pattern`+ ','? + sequence_pattern: "[" [`maybe_sequence_pattern`] "]" + : | "(" [`open_sequence_pattern`] ")" + open_sequence_pattern: `maybe_star_pattern` "," [`maybe_sequence_pattern`] + maybe_sequence_pattern: ",".`maybe_star_pattern`+ ","? maybe_star_pattern: `star_pattern` | `pattern` - star_pattern: '*' (`capture_pattern` | `wildcard_pattern`) + star_pattern: "*" (`capture_pattern` | `wildcard_pattern`) There is no difference if parentheses (``(...)``) or square brackets (``[...]``) @@ -918,7 +921,7 @@ are used for sequence patterns. .. note:: A single pattern enclosed in parentheses without a trailing comma - (e.g. ``(3 | 4)``) is a :ref:`group pattern `. + (e.g. ``(3 | 4)``) is a :ref:`group pattern `. While a single pattern enclosed in square brackets (e.g. ``[3 | 4]``) is still a sequence pattern. @@ -967,10 +970,10 @@ subject value: .. note:: The length of the subject sequence is obtained via :func:`len` (i.e. via the :meth:`__len__` protocol). This length may be cached by the interpreter in a similar manner as - :ref:`value patterns `. + :ref:`value patterns `. -.. _mapping_patterns: +.. _mapping-patterns: Mapping Patterns ^^^^^^^^^^^^^^^^ @@ -980,11 +983,11 @@ similar to the construction of a dictionary. Syntax: .. productionlist:: python-grammar - mapping_pattern: '{' [`items_pattern`] '}' - items_pattern: ','.`key_value_pattern`+ ','? - key_value_pattern: (`literal_pattern` | `value_pattern`) ':' `pattern` + mapping_pattern: "{" [`items_pattern`] "}" + items_pattern: ",".`key_value_pattern`+ ","? + key_value_pattern: (`literal_pattern` | `value_pattern`) ":" `pattern` : | `double_star_pattern` - double_star_pattern: '**' `capture_pattern` + double_star_pattern: "**" `capture_pattern` At most one double star pattern may be in a mapping pattern. The double star pattern must be the last subpattern in the mapping pattern. @@ -1012,7 +1015,7 @@ subject value: :meth:`__getitem__`. -.. _class_patterns: +.. _class-patterns: Class Patterns ^^^^^^^^^^^^^^ @@ -1021,12 +1024,12 @@ A class pattern represents a class and its positional and keyword arguments (if any). Syntax: .. productionlist:: python-grammar - class_pattern: `name_or_attr` '(' [`pattern_arguments` ','?] ')' - pattern_arguments: `positional_patterns` [',' `keyword_patterns`] + class_pattern: `name_or_attr` "(" [`pattern_arguments` ","?] ")" + pattern_arguments: `positional_patterns` ["," `keyword_patterns`] : | `keyword_patterns` - positional_patterns: ','.`pattern`+ - keyword_patterns: ','.`keyword_pattern`+ - keyword_pattern: NAME '=' `pattern` + positional_patterns: ",".`pattern`+ + keyword_patterns: ",".`keyword_pattern`+ + keyword_pattern: NAME "=" `pattern` The same keyword should not be repeated in class patterns. diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst index 74609e01b13434..4d5d9a6717be43 100644 --- a/Doc/reference/lexical_analysis.rst +++ b/Doc/reference/lexical_analysis.rst @@ -352,7 +352,7 @@ exactly as written here: async elif if or yield -.. _soft_keywords: +.. _soft-keywords: Soft Keywords ------------- From f13eb7fe04a08bf5a263ea91151de5df40158093 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 27 Feb 2021 16:11:38 +0800 Subject: [PATCH 18/34] touch up table --- Doc/reference/compound_stmts.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index c9e1ed5d6c85eb..83cc7ff67c8880 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -719,15 +719,16 @@ approximation of their behavior (credits to Raymond Hettinger for the idea): | | ``capture`` | 2. if success, bind name into | | | | ``capture`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`literal-patterns` | ``"literal"`` | test ``subject == "literal"`` | +| :ref:`literal-patterns` | ``"literal"`` | for strings and numbers, test ``subject == x.y`` | +| | | | +| | | otherwise, for singletons like ``None`` and :func:`bool`,| +| | | test ``subject is x.y`` | +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`capture-patterns` | ``name`` | bind ``name = subject`` | +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`wildcard-patterns` | ``_`` | ``pass`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`value-patterns` | ``x.y`` | For strings and numbers, test ``subject == x.y`` | -| | | Otherwise, for singletons like ``None`` and :func:`bool`,| -| | | test ``subject is x.y`` | +| :ref:`value-patterns` | ``x.y`` | test ``subject == "literal"`` | +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`group-patterns` | ``( pattern )`` | test ``pattern`` | +--------------------------+-------------------+----------------------------------------------------------+ @@ -759,7 +760,8 @@ approximation of their behavior (credits to Raymond Hettinger for the idea): | | | 4. test ``p2`` matches ``subject.name`` | | | | | | | | 5. convert ``p1`` to a keyword pattern using | -| | | ``K.__match_args__`` and repeat steps 2. and 3. | +| | | ``K.__match_args__`` and repeat steps 2. to 4. | +| | | using ``p1`` | +--------------------------+-------------------+----------------------------------------------------------+ Note that this table purely for illustration purposes and **may not** reflect From 43e94192c54a65e4b440134dd1beb3e88241feb0 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 27 Feb 2021 17:12:54 +0800 Subject: [PATCH 19/34] Add the datamodel __match_args__ links in --- Doc/reference/compound_stmts.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 83cc7ff67c8880..93096bf8cc7a90 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -1070,10 +1070,10 @@ subject value: II. If all keyword patterns succeed, the class pattern succeeds. If any positional patterns are present, they are converted to keyword - patterns using the ``__match_args__`` attribute on the class + patterns using the :data:`~class.__match_args__` attribute on the class ``name_or_attr`` before matching: - I. The equivalent of getattr(cls, "__match_args__", ())) is called. + I. The equivalent of ``getattr(cls, "__match_args__", ()))`` is called. * If this raises an exception, the exception bubbles up. @@ -1089,6 +1089,8 @@ subject value: * If there are duplicate keywords, :exc:`TypeError` is raised. + .. seealso:: :ref:`class-pattern-matching` + II. Once all positional patterns have been converted to keyword patterns, the match proceeds as if there were only keyword patterns. From 3bb4f3f4e6f62d00305ceb980b71eb2de77d0da4 Mon Sep 17 00:00:00 2001 From: Daniel F Moisset Date: Sat, 27 Feb 2021 19:09:53 +0000 Subject: [PATCH 20/34] Apply suggestions from code review Some minor typos, and copy/paste fixes --- Doc/reference/compound_stmts.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 93096bf8cc7a90..6463b015ead0ba 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -714,21 +714,21 @@ approximation of their behavior (credits to Raymond Hettinger for the idea): | | | | | | | 3. if failure test ... | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`as-patterns` | ``pattern`` | 1. test ``patttern`` | +| :ref:`as-patterns` | ``pattern`` | 1. test ``pattern`` | | | :keyword:`as` | | | | ``capture`` | 2. if success, bind name into | | | | ``capture`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`literal-patterns` | ``"literal"`` | for strings and numbers, test ``subject == x.y`` | +| :ref:`literal-patterns` | ``"literal"`` | for strings and numbers, test ``subject == "literal"`` | | | | | | | | otherwise, for singletons like ``None`` and :func:`bool`,| -| | | test ``subject is x.y`` | +| | | test ``subject is literal`` | +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`capture-patterns` | ``name`` | bind ``name = subject`` | +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`wildcard-patterns` | ``_`` | ``pass`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`value-patterns` | ``x.y`` | test ``subject == "literal"`` | +| :ref:`value-patterns` | ``x.y`` | test ``subject == x.y`` | +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`group-patterns` | ``( pattern )`` | test ``pattern`` | +--------------------------+-------------------+----------------------------------------------------------+ From 0b3d46a9bef7f56ec31544cadade09108b36a42e Mon Sep 17 00:00:00 2001 From: Daniel F Moisset Date: Sat, 27 Feb 2021 23:43:49 +0000 Subject: [PATCH 21/34] Fix documentation build errors --- Doc/reference/compound_stmts.rst | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 6463b015ead0ba..d4a3f962c33735 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -691,8 +691,6 @@ The top-level syntax for ``patterns`` is: .. productionlist:: python-grammar patterns: `open_sequence_pattern` | `pattern` pattern: `as_pattern` | `or_pattern` - as_pattern: `or_pattern` "as" `capture_pattern` - or_pattern: "|".`closed_pattern`+ closed_pattern: | `literal_pattern` : | `capture_pattern` : | `wildcard_pattern` @@ -714,21 +712,21 @@ approximation of their behavior (credits to Raymond Hettinger for the idea): | | | | | | | 3. if failure test ... | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`as-patterns` | ``pattern`` | 1. test ``pattern`` | +| :ref:`as-patterns` | ``pattern`` | 1. test ``pattern`` | | | :keyword:`as` | | | | ``capture`` | 2. if success, bind name into | | | | ``capture`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`literal-patterns` | ``"literal"`` | for strings and numbers, test ``subject == "literal"`` | +| :ref:`literal-patterns` | ``"literal"`` | for strings and numbers, test ``subject == "literal"`` | | | | | | | | otherwise, for singletons like ``None`` and :func:`bool`,| -| | | test ``subject is literal`` | +| | | test ``subject is literal`` | +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`capture-patterns` | ``name`` | bind ``name = subject`` | +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`wildcard-patterns` | ``_`` | ``pass`` | +--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`value-patterns` | ``x.y`` | test ``subject == x.y`` | +| :ref:`value-patterns` | ``x.y`` | test ``subject == x.y`` | +--------------------------+-------------------+----------------------------------------------------------+ | :ref:`group-patterns` | ``( pattern )`` | test ``pattern`` | +--------------------------+-------------------+----------------------------------------------------------+ From d0ce4d8cb2534e92e3ab038fdc80aed57817bced Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sat, 27 Feb 2021 23:56:34 +0000 Subject: [PATCH 22/34] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Documentation/2021-02-27-23-56-34.bpo-42128.8AGXVv.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Documentation/2021-02-27-23-56-34.bpo-42128.8AGXVv.rst diff --git a/Misc/NEWS.d/next/Documentation/2021-02-27-23-56-34.bpo-42128.8AGXVv.rst b/Misc/NEWS.d/next/Documentation/2021-02-27-23-56-34.bpo-42128.8AGXVv.rst new file mode 100644 index 00000000000000..6950fe49bf4d16 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2021-02-27-23-56-34.bpo-42128.8AGXVv.rst @@ -0,0 +1 @@ +skip news \ No newline at end of file From 3d8cbd6a1182f4c36f905d595a0acc0de393b8f5 Mon Sep 17 00:00:00 2001 From: Daniel F Moisset Date: Sun, 28 Feb 2021 00:01:12 +0000 Subject: [PATCH 23/34] Clean linter issues --- Doc/faq/design.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/faq/design.rst b/Doc/faq/design.rst index 9bece6dc9c8260..7fe1c6d58f58a1 100644 --- a/Doc/faq/design.rst +++ b/Doc/faq/design.rst @@ -259,7 +259,7 @@ Why isn't there a switch or case statement in Python? ----------------------------------------------------- You can do this easily enough with a sequence of ``if... elif... elif... else``. -For literal values, or constants within a namespace, you can also use a +For literal values, or constants within a namespace, you can also use a ``match ... case`` statement. For cases where you need to choose from a very large number of possibilities, From afa3ca0c7076b9a73adf4adfaf14f87e7b7868d2 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 28 Feb 2021 10:02:40 +0800 Subject: [PATCH 24/34] *TypeError* -> :exc:`TypeError` in Doc/reference/datamodel.rst --- Doc/reference/datamodel.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 04437bd2faf33b..b0c7c22c57e6df 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2575,7 +2575,7 @@ For example if ``MyClass.__match_args__`` is ``("left", "center", "right")`` tha that ``case MyClass(x, y)`` is equivalent to ``case MyClass(left=x, center=y)``. Note that the number of arguments in the pattern must be smaller than or equal to the number of elements in *__match_args__*; if it is larger, the pattern match attempt will raise -a *TypeError*. +a :exc:`TypeError`. .. versionadded:: 3.10 From b9d1321305c75d258efe94d5599951e8ab898942 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 28 Feb 2021 10:07:29 +0800 Subject: [PATCH 25/34] Delete news file: shares same news as Brandt's PR --- .../next/Documentation/2021-02-27-23-56-34.bpo-42128.8AGXVv.rst | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Misc/NEWS.d/next/Documentation/2021-02-27-23-56-34.bpo-42128.8AGXVv.rst diff --git a/Misc/NEWS.d/next/Documentation/2021-02-27-23-56-34.bpo-42128.8AGXVv.rst b/Misc/NEWS.d/next/Documentation/2021-02-27-23-56-34.bpo-42128.8AGXVv.rst deleted file mode 100644 index 6950fe49bf4d16..00000000000000 --- a/Misc/NEWS.d/next/Documentation/2021-02-27-23-56-34.bpo-42128.8AGXVv.rst +++ /dev/null @@ -1 +0,0 @@ -skip news \ No newline at end of file From 1f4d697c9ed04e0f133cc012154ab60d6392a62a Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sun, 28 Feb 2021 15:15:41 +0800 Subject: [PATCH 26/34] update link in compound statements --- Doc/reference/compound_stmts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index d4a3f962c33735..b11f6d693866a5 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -1068,7 +1068,7 @@ subject value: II. If all keyword patterns succeed, the class pattern succeeds. If any positional patterns are present, they are converted to keyword - patterns using the :data:`~class.__match_args__` attribute on the class + patterns using the :data:`~object.__match_args__` attribute on the class ``name_or_attr`` before matching: I. The equivalent of ``getattr(cls, "__match_args__", ()))`` is called. From a55a396d4b7c9c985f8a4709874d9b15b0340bed Mon Sep 17 00:00:00 2001 From: Daniel F Moisset Date: Sun, 28 Feb 2021 12:06:21 +0000 Subject: [PATCH 27/34] Better detail on what a soft keyword actually is Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> --- Doc/reference/lexical_analysis.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst index 4d5d9a6717be43..c3e454909d3eef 100644 --- a/Doc/reference/lexical_analysis.rst +++ b/Doc/reference/lexical_analysis.rst @@ -361,9 +361,11 @@ Soft Keywords .. versionadded:: 3.10 -The identifiers ``match``, ``case`` and ``_`` can syntactically act as keywords in some -specific contexts related to the pattern matching statement, but this distinction is done -at the parser level, not when tokenizing. +Some identifiers are only reserved under specific contexts. These are known as +*soft keywords*. The identifiers ``match``, ``case`` and ``_`` can +syntactically act as keywords in contexts related to the pattern matching +statement, but this distinction is done at the parser level, not when +tokenizing. This is done to allow their use while still preserving compatibility with existing code that uses ``match``, ``case`` and ``_`` as From 68e02513860f921c0fb17d1e8bff845aa14ac21d Mon Sep 17 00:00:00 2001 From: Daniel F Moisset Date: Sun, 28 Feb 2021 22:33:57 +0000 Subject: [PATCH 28/34] Rework the sequence of steps in the overview This avoid some possible confusion present in the previous language, where it wasn't explicit that bindings happened before the guard was valuated --- Doc/reference/compound_stmts.rst | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index b11f6d693866a5..d04ddc9ad1e158 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -561,14 +561,17 @@ Overview Here's an overview of the logical flow of a match statement: + #. The subject expression ``subject_expr`` is evaluated and a resulting subject - value obtained. If the subject expression contains a comma, a tuple is + value obtained. If the subject expression contains a comma, a tuple is constructed using :ref:`the standard rules `. -#. The first ``case_block`` whose patterns succeed in matching the subject - value and whose ``guard`` condition (if present) is "truthy" is selected. - - * If no case blocks qualify, the match statement is completed. +#. Each pattern in a ``case_block`` is attempted to match with the subject value. The + specific rules for success or failure are described below. The match attempt can also + bind some or all of the standalone names within the pattern. The precise + pattern binding rules vary per pattern type and are + specified below. **Name bindings made during a successful pattern match + outlive the executed block and can be used after the match statement**. .. note:: @@ -579,10 +582,15 @@ Here's an overview of the logical flow of a match statement: intentional decision made to allow different implementations to add optimizations. - * Otherwise, name bindings occur and the ``block`` inside ``case_block`` is - executed. The precise pattern binding rules vary per pattern type and are - specified below. **Name bindings made during a successful pattern match - outlive the executed block and can be used after the match statement**. +#. If the pattern succeeds, the corresponding guard (if present) is evaluated. In + this case all name bindings are guaranteed to have happened. + + * If the guard evaluates as truthy or missing, the ``block`` inside ``case_block`` is + executed. + + * Otherwise, the next ``case_block`` is attempted as described above. + + * If there are no further case blocks, the match statement is completed. .. note:: From 7a2cb660e67a1f8d0766b0d5a824d69b862a9d1a Mon Sep 17 00:00:00 2001 From: Daniel F Moisset Date: Sun, 28 Feb 2021 22:42:13 +0000 Subject: [PATCH 29/34] Add some comments to the example --- Doc/reference/compound_stmts.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index d04ddc9ad1e158..41ae67ca589de4 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -602,13 +602,13 @@ A sample match statement:: >>> flag = False >>> match (100, 200): - ... case (100, 300): + ... case (100, 300): # Mismatch: 200 != 300 ... print('Case 1') - ... case (100, 200) if flag: + ... case (100, 200) if flag: # Successful match, but guard fails ... print('Case 2') - ... case (100, y): + ... case (100, y): # Matches and binds y to 200 ... print(f'Case 3, y: {y}') - ... case _: + ... case _: # Pattern not attempted ... print('Case 4, I match anything!') ... Case 3, y: 200 From 66f343564820ad3189011076a3a925d0e5440943 Mon Sep 17 00:00:00 2001 From: Daniel F Moisset Date: Sun, 28 Feb 2021 22:44:41 +0000 Subject: [PATCH 30/34] Made the irrefutable definition slightly more stricter --- Doc/reference/compound_stmts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 41ae67ca589de4..4e687a6cc28f32 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -665,7 +665,7 @@ at most one irrefutable case block, and it must be last. A case block is considered irrefutable if it has no guard and its pattern is irrefutable. A pattern is considered irrefutable if we can prove from its -syntax alone that it will always succeed. The following patterns are +syntax alone that it will always succeed. Only the following patterns are irrefutable: * :ref:`as-patterns` whose left-hand side is irrefutable From e783adb1b5c841d909a05ebf7af0838fd0b9d65a Mon Sep 17 00:00:00 2001 From: Daniel F Moisset Date: Sun, 28 Feb 2021 23:37:02 +0000 Subject: [PATCH 31/34] Move table rows as apragraphs into the description of each pattern --- Doc/reference/compound_stmts.rst | 132 ++++++++++++++----------------- 1 file changed, 61 insertions(+), 71 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 4e687a6cc28f32..f0a7274255e2a8 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -568,7 +568,7 @@ Here's an overview of the logical flow of a match statement: #. Each pattern in a ``case_block`` is attempted to match with the subject value. The specific rules for success or failure are described below. The match attempt can also - bind some or all of the standalone names within the pattern. The precise + bind some or all of the standalone names within the pattern. The precise pattern binding rules vary per pattern type and are specified below. **Name bindings made during a successful pattern match outlive the executed block and can be used after the match statement**. @@ -708,71 +708,15 @@ The top-level syntax for ``patterns`` is: : | `mapping_pattern` : | `class_pattern` -The following is a very brief overview of the different patterns and an -approximation of their behavior (credits to Raymond Hettinger for the idea): - -+--------------------------+-------------------+----------------------------------------------------------+ -| Pattern Type | Pattern Form | Logical behavior | -+==========================+===================+==========================================================+ -| :ref:`or-patterns` | ``p1 | p2 | ...`` | 1. test ``p1`` | -| | | | -| | | 2. if failure, test ``p2`` | -| | | | -| | | 3. if failure test ... | -+--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`as-patterns` | ``pattern`` | 1. test ``pattern`` | -| | :keyword:`as` | | -| | ``capture`` | 2. if success, bind name into | -| | | ``capture`` | -+--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`literal-patterns` | ``"literal"`` | for strings and numbers, test ``subject == "literal"`` | -| | | | -| | | otherwise, for singletons like ``None`` and :func:`bool`,| -| | | test ``subject is literal`` | -+--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`capture-patterns` | ``name`` | bind ``name = subject`` | -+--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`wildcard-patterns` | ``_`` | ``pass`` | -+--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`value-patterns` | ``x.y`` | test ``subject == x.y`` | -+--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`group-patterns` | ``( pattern )`` | test ``pattern`` | -+--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`sequence-patterns` | ``[p1, p2, ...]`` | 1. check ``isinstance(subject, collections.abc.Sequence``| -| | | | -| | | 2. check ``len(subject) == len(sequence_pattern)`` | -| | | | -| | | 3. test ``p1`` against ``subject_value[0]`` | -| | | | -| | | 4. test ``p2`` against ``subject_value[1]`` | -| | | | -| | | 5. repeat step 4. for subsequent patterns and indexes | -+--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`mapping-patterns` | ``{p1: p2, ...}`` | 1. check ``isinstance(subject, collections.abc.Mapping)``| -| | | | -| | | 2. test ``p1 in subject`` | -| | | | -| | | 3. test ``p2`` matches ``subject[p1]`` | -| | | | -| | | 4. repeat steps 2. and 3. for subsequent key and value | -| | | patterns. | -+--------------------------+-------------------+----------------------------------------------------------+ -| :ref:`class-patterns` | ``K(p1, name=p2)``| 1. check ``isinstance(K, type)`` | -| | | | -| | | 2. check ``isinstance(subject, K)`` | -| | | | -| | | 3. check ``hasattr(subject, name)`` | -| | | | -| | | 4. test ``p2`` matches ``subject.name`` | -| | | | -| | | 5. convert ``p1`` to a keyword pattern using | -| | | ``K.__match_args__`` and repeat steps 2. to 4. | -| | | using ``p1`` | -+--------------------------+-------------------+----------------------------------------------------------+ - -Note that this table purely for illustration purposes and **may not** reflect +The descriptions below will include a description "in simple terms" of what a pattern +does for illustration purposes (credits to Raymond Hettinger for a document that +inspired most of the descriptions). Note that these descriptions are purely for +illustration purposes and **may not** reflect the underlying implementation. Furthermore, they do not cover all valid forms. -Read the pattern's respective sections to learn more. + +The following is a very brief overview of the different patterns and an +approximation of their behavior: + .. _or-patterns: @@ -793,6 +737,9 @@ An OR pattern matches each of its subpatterns in turn to the subject value, until one succeeds. The OR pattern is then considered successful. Otherwise, if none of the subpatterns succeed, the OR pattern fails. +In simple terms, ``P1 | P2 | ...`` will try to match ``P1``, if it fails it will try to +match ``P2``, succeeding immediately if any succeeds, failing otherwise. + .. _as-patterns: AS Patterns @@ -808,6 +755,10 @@ If the OR pattern fails, the AS pattern fails. Otherwise, the AS pattern binds the subject to the name on the right of the as keyword and succeeds. ``capture_pattern`` cannot be a a ``_``. +In simple terms ``P as NAME`` will match with ``P``, and on success it will +set ``NAME = ``. + + .. _literal-patterns: Literal Patterns @@ -835,6 +786,9 @@ The forms ``signed_number '+' NUMBER`` and ``signed_number '-' NUMBER`` are for expressing :ref:`complex numbers `; they require a real number on the left and an imaginary number on the right. E.g. ``3 + 4j``. +In simple terms, ``LITERAL`` will succeed only if `` == LITERAL``. For +the singletons ``None``, ``True`` and ``False``, the :keyword:`is` operator is used. + .. _capture-patterns: Capture Patterns @@ -853,10 +807,12 @@ In a given pattern, a given name can only be bound once. E.g. ``case x, x: ...`` is invalid while ``case [x] | x: ...`` is allowed. Capture patterns always succeed. The binding follows scoping rules -established by the assignment expression operator in :pep`572`; the +established by the assignment expression operator in :pep:`572`; the name becomes a local variable in the closest containing function scope unless there's an applicable :keyword:`global` or :keyword:`nonlocal` statement. +In simple terms ``NAME`` will always succeed and it will set ``NAME = ``. + .. _wildcard-patterns: Wildcard Patterns @@ -870,6 +826,8 @@ and binds no name. Syntax: ``_`` is a :ref:`soft keyword `. +In simple terms, ``_`` will always succeed. + .. _value-patterns: Value Patterns @@ -888,6 +846,8 @@ The dotted name in the pattern is looked up using standard Python value found compares equal to the subject value (using the ``==`` equality operator). +In simple terms ``NAME1.NAME2`` will succeed only if `` == NAME1.NAME2`` + .. note:: If the same value occurs multiple times in the same match statement, the @@ -907,13 +867,15 @@ Syntax: .. productionlist:: python-grammar group_pattern: '(' `pattern` ')' +In simple terms ``(P)`` has the same effect as ``P``. + .. _sequence-patterns: Sequence Patterns ^^^^^^^^^^^^^^^^^ -A sequence pattern contains a sequence of subpatterns. The syntax is -similar to the construction of a list or tuple. +A sequence pattern contains several subpatterns to be matched against sequence elements. +The syntax is similar to the unpacking of a list or tuple. .. productionlist:: python-grammar sequence_pattern: "[" [`maybe_sequence_pattern`] "]" @@ -923,9 +885,8 @@ similar to the construction of a list or tuple. maybe_star_pattern: `star_pattern` | `pattern` star_pattern: "*" (`capture_pattern` | `wildcard_pattern`) - -There is no difference if parentheses (``(...)``) or square brackets (``[...]``) -are used for sequence patterns. +There is no difference if parentheses or square brackets +are used for sequence patterns (i.e. ``(...)`` vs ``[...]`` ). .. note:: A single pattern enclosed in parentheses without a trailing comma @@ -981,6 +942,15 @@ subject value: :ref:`value patterns `. +In simple terms ``[P1, P2, P3,`` ... ``, P]`` matches only if all the following +happens: + +* ``isinstance(, collections.abc.Sequence)`` +* ``len(subject) == `` +* ``P1`` matches ``[0]`` (note that this match can also bind names) +* ``P2`` matches ``[1]`` (note that this match can also bind names) +* ... and so on for the corresponding pattern/element. + .. _mapping-patterns: Mapping Patterns @@ -1022,6 +992,14 @@ subject value: in the mapping, and not created on-the-fly via :meth:`__missing__` or :meth:`__getitem__`. +In simple terms ``{KEY1: P1, KEY2: P2, ... }`` matches only if all the following +happens: + +* ``isinstance(, collections.abc.Mapping)`` +* ``KEY1 in `` +* ``P1`` matches ``[KEY1]`` +* ... and so on for the corresponding KEY/pattern pair. + .. _class-patterns: @@ -1115,6 +1093,18 @@ subject value: * :class:`str` * :class:`tuple` + These classes accept a single positional argument, and the pattern there is matched + against the whole object rather than an attribute. For example ``int(0|1)`` matches + the value ``0``, but not the values ``0.0`` or ``False``. + +In simple terms ``CLS(P1, attr=P2)`` matches only if the following happens: + +* ``isinstance(, CLS)`` +* convert ``P1`` to a keyword pattern using ``CLS.__match_args__`` +* For each keyword argument ``attr=P2``: + * ``hasattr(, "attr")`` + * ``P2`` matches ``.attr`` +* ... and so on for the corresponding keyword argument/pattern pair. .. seealso:: From f9e1193136504477ab035da2849821effe76340f Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sun, 28 Feb 2021 16:55:34 -0800 Subject: [PATCH 32/34] Apply my two specific suggestions from code review --- Doc/reference/datamodel.rst | 2 +- Doc/tutorial/controlflow.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index b0c7c22c57e6df..dfe5eb6857c0f5 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2571,7 +2571,7 @@ define a *__match_args__* attribute. *__match_args__* as the keyword. The absence of this attribute is equivalent to setting it to ``()``. -For example if ``MyClass.__match_args__`` is ``("left", "center", "right")`` that means +For example, if ``MyClass.__match_args__`` is ``("left", "center", "right")`` that means that ``case MyClass(x, y)`` is equivalent to ``case MyClass(left=x, center=y)``. Note that the number of arguments in the pattern must be smaller than or equal to the number of elements in *__match_args__*; if it is larger, the pattern match attempt will raise diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst index 8727bf50e552da..277e5c18562d47 100644 --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -36,7 +36,7 @@ to avoid excessive indentation. An :keyword:`!if` ... :keyword:`!elif` ... :keyword:`!elif` ... sequence is a substitute for the ``switch`` or ``case`` statements found in other languages. -If you're making comparison with constants, or checking for specific types or +If you're comparing the same value to several constants, or checking for specific types or attributes, you may also find the :keyword:`!match` statement useful. For more details see :ref:`tut-match`. From 6fafe4b2f0bd93405259141a2d81ce3c3a7124a1 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sun, 28 Feb 2021 19:30:30 -0800 Subject: [PATCH 33/34] Final suggestion by Carol Co-authored-by: Carol Willing --- Doc/reference/lexical_analysis.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst index c3e454909d3eef..4ad8f8be1e7ddf 100644 --- a/Doc/reference/lexical_analysis.rst +++ b/Doc/reference/lexical_analysis.rst @@ -367,7 +367,7 @@ syntactically act as keywords in contexts related to the pattern matching statement, but this distinction is done at the parser level, not when tokenizing. -This is done to allow their use while still +As soft keywords, their use with pattern matching is possible while still preserving compatibility with existing code that uses ``match``, ``case`` and ``_`` as identifier names. From 365912ccc71713363b1a85e8e9130c84c19fb297 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Mon, 1 Mar 2021 11:39:24 +0800 Subject: [PATCH 34/34] delete redundant paragraph since table is gone --- Doc/reference/compound_stmts.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index f0a7274255e2a8..e13d6dd956209f 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -714,10 +714,6 @@ inspired most of the descriptions). Note that these descriptions are purely for illustration purposes and **may not** reflect the underlying implementation. Furthermore, they do not cover all valid forms. -The following is a very brief overview of the different patterns and an -approximation of their behavior: - - .. _or-patterns: