PEP 653: Rephrase translations in terms of failure, rather than success, to make them easier to compose. (#1865)
This commit is contained in:
parent
c91e284b01
commit
2aec45c8b8
378
pep-0653.rst
378
pep-0653.rst
|
@ -160,7 +160,8 @@ Semantics of the matching process
|
|||
|
||||
In the following, all variables of the form ``$var`` are temporary variables and are not visible to the Python program.
|
||||
They may be visible via introspection, but that is an implementation detail and should not be relied on.
|
||||
The psuedo-statement ``DONE`` is used to signify that matching is complete and that following patterns should be ignored.
|
||||
The psuedo-statement ``FAIL`` is used to signify that matching failed for this pattern and that matching should move to the next pattern.
|
||||
If control reaches the end of the translation without reaching a ``FAIL``, then it has matched, and following patterns are ignored.
|
||||
All the translations below include guards. If no guard is present, simply substitute the guard ``if True`` when translating.
|
||||
|
||||
Variables of the form ``$ALL_CAPS`` are meta-variables holding a syntactic element, they are not normal variables.
|
||||
|
@ -197,57 +198,55 @@ In addition some helper variables are initialized::
|
|||
Capture patterns
|
||||
''''''''''''''''
|
||||
|
||||
Capture patterns always match, so::
|
||||
Capture patterns always match, so the irrefutable match::
|
||||
|
||||
case capture_var if guard:
|
||||
case capture_var:
|
||||
|
||||
translates to::
|
||||
|
||||
capture_var = $value
|
||||
if guard:
|
||||
DONE
|
||||
|
||||
Wildcard patterns
|
||||
'''''''''''''''''
|
||||
|
||||
Wildcard patterns always match, so::
|
||||
|
||||
case _ if guard:
|
||||
case _:
|
||||
|
||||
translates to::
|
||||
|
||||
if guard:
|
||||
DONE
|
||||
# No code -- Automatically matches
|
||||
|
||||
|
||||
Literal Patterns
|
||||
''''''''''''''''
|
||||
|
||||
The literal pattern::
|
||||
|
||||
case LITERAL if guard:
|
||||
case LITERAL:
|
||||
|
||||
translates to::
|
||||
|
||||
if $value == LITERAL and guard:
|
||||
DONE
|
||||
if $value != LITERAL:
|
||||
FAIL
|
||||
|
||||
except when the literal is one of ``None``, ``True`` or ``False`` ,
|
||||
when it translates to::
|
||||
|
||||
if $value is LITERAL and guard:
|
||||
DONE
|
||||
if $value is not LITERAL:
|
||||
FAIL
|
||||
|
||||
Value Patterns
|
||||
''''''''''''''
|
||||
|
||||
The value pattern::
|
||||
|
||||
case value.pattern if guard:
|
||||
case value.pattern:
|
||||
|
||||
translates to::
|
||||
|
||||
if $value == value.pattern and guard:
|
||||
DONE
|
||||
if $value != value.pattern:
|
||||
FAIL
|
||||
|
||||
Sequence Patterns
|
||||
'''''''''''''''''
|
||||
|
@ -257,33 +256,33 @@ Before matching the first sequence pattern, but after checking that ``$value`` i
|
|||
|
||||
A pattern not including a star pattern::
|
||||
|
||||
case [$VARS] if guard:
|
||||
case [$VARS]:
|
||||
|
||||
translates to::
|
||||
|
||||
if $kind & MATCH_SEQUENCE:
|
||||
if $list is None:
|
||||
$list = list($value)
|
||||
if len($list) == len($VARS):
|
||||
$VARS = $list
|
||||
if guard:
|
||||
DONE
|
||||
if $kind & MATCH_SEQUENCE == 0:
|
||||
FAIL
|
||||
if $list is None:
|
||||
$list = list($value)
|
||||
if len($list) != len($VARS):
|
||||
FAIL
|
||||
$VARS = $list
|
||||
|
||||
Example: [2]_
|
||||
|
||||
A pattern including a star pattern::
|
||||
|
||||
case [$VARS] if guard
|
||||
case [$VARS]
|
||||
|
||||
translates to::
|
||||
|
||||
if $kind & MATCH_SEQUENCE:
|
||||
if $list is None:
|
||||
$list = list($value)
|
||||
if len($list) >= len($VARS):
|
||||
$VARS = $list # Note that $VARS includes a star expression.
|
||||
if guard:
|
||||
DONE
|
||||
if $kind & MATCH_SEQUENCE == 0:
|
||||
FAIL
|
||||
if $list is None:
|
||||
$list = list($value)
|
||||
if len($list) < len($VARS):
|
||||
FAIL
|
||||
$VARS = $list # Note that $VARS includes a star expression.
|
||||
|
||||
Example: [3]_
|
||||
|
||||
|
@ -295,38 +294,39 @@ Before matching the first mapping pattern, but after checking that ``$value`` is
|
|||
|
||||
A pattern not including a double-star pattern::
|
||||
|
||||
case {$KEYWORD_PATTERNS} if guard:
|
||||
case {$KEYWORD_PATTERNS}:
|
||||
|
||||
translates to::
|
||||
|
||||
if $kind & MATCH_MAPPING:
|
||||
if $dict is None:
|
||||
$dict = dict($value)
|
||||
if $dict.keys() == $KEYWORD_PATTERNS.keys():
|
||||
# $KEYWORD_PATTERNS is a meta-variable mapping names to variables.
|
||||
for $KEYWORD in $KEYWORD_PATTERNS:
|
||||
$KEYWORD_PATTERNS[$KEYWORD] = $dict[QUOTE($KEYWORD)]
|
||||
if guard:
|
||||
DONE
|
||||
if $kind & MATCH_MAPPING == 0:
|
||||
FAIL
|
||||
if $dict is None:
|
||||
$dict = dict($value)
|
||||
if $dict.keys() != $KEYWORD_PATTERNS.keys():
|
||||
FAIL
|
||||
# $KEYWORD_PATTERNS is a meta-variable mapping names to variables.
|
||||
for $KEYWORD in $KEYWORD_PATTERNS:
|
||||
$KEYWORD_PATTERNS[$KEYWORD] = $dict[QUOTE($KEYWORD)]
|
||||
|
||||
Example: [4]_
|
||||
|
||||
A pattern including a double-star pattern::
|
||||
|
||||
case {$KEYWORD_PATTERNS, **$DOUBLE_STARRED_PATTERN} if guard::
|
||||
case {$KEYWORD_PATTERNS, **$DOUBLE_STARRED_PATTERN}:
|
||||
|
||||
translates to::
|
||||
|
||||
if $kind & MATCH_MAPPING:
|
||||
if $dict is None:
|
||||
$dict = dict($value)
|
||||
if $dict.keys() >= $KEYWORD_PATTERNS.keys():
|
||||
# $KEYWORD_PATTERNS is a meta-variable mapping names to variables.
|
||||
$tmp = dict($dict)
|
||||
for $KEYWORD in $KEYWORD_PATTERNS:
|
||||
$KEYWORD_PATTERNS[$KEYWORD] = $tmp.pop(QUOTE($KEYWORD))
|
||||
$DOUBLE_STARRED_PATTERN = $tmp
|
||||
DONE
|
||||
if $kind & MATCH_MAPPING == 0:
|
||||
FAIL
|
||||
if $dict is None:
|
||||
$dict = dict($value)
|
||||
if $dict.keys() not >= $KEYWORD_PATTERNS.keys():
|
||||
FAIL:
|
||||
# $KEYWORD_PATTERNS is a meta-variable mapping names to variables.
|
||||
$tmp = dict($dict)
|
||||
for $KEYWORD in $KEYWORD_PATTERNS:
|
||||
$KEYWORD_PATTERNS[$KEYWORD] = $tmp.pop(QUOTE($KEYWORD))
|
||||
$DOUBLE_STARRED_PATTERN = $tmp
|
||||
|
||||
Example: [5]_
|
||||
|
||||
|
@ -335,46 +335,45 @@ Class Patterns
|
|||
|
||||
Class pattern with no arguments::
|
||||
|
||||
match ClsName() if guard:
|
||||
case ClsName():
|
||||
|
||||
translates to::
|
||||
|
||||
if $kind & MATCH_CLASS:
|
||||
if isinstance($value, ClsName):
|
||||
if guard:
|
||||
DONE
|
||||
|
||||
if $kind & (MATCH_CLASS | MATCH_DEFAULT) == 0:
|
||||
FAIL
|
||||
if not isinstance($value, ClsName):
|
||||
FAIL
|
||||
|
||||
Class pattern with a single positional pattern::
|
||||
|
||||
match ClsName($PATTERN) if guard:
|
||||
case ClsName($VAR):
|
||||
|
||||
translates to::
|
||||
|
||||
if $kind & MATCH_SELF:
|
||||
if isinstance($value, ClsName):
|
||||
x = $value
|
||||
if guard:
|
||||
DONE
|
||||
if not isinstance($value, ClsName):
|
||||
FAIL
|
||||
$VAR = $value
|
||||
else:
|
||||
As other positional-only class pattern
|
||||
|
||||
|
||||
Positional-only class pattern::
|
||||
|
||||
match ClsName($VARS) if guard:
|
||||
case ClsName($VARS):
|
||||
|
||||
translates to::
|
||||
|
||||
if $kind & MATCH_CLASS:
|
||||
if isinstance($value, ClsName):
|
||||
if $items is None:
|
||||
$items = type($value).__deconstruct__($value)
|
||||
# $VARS is a meta-variable.
|
||||
if len($items) == len($VARS):
|
||||
$VARS = $items
|
||||
if guard:
|
||||
DONE
|
||||
if $kind & MATCH_CLASS == 0:
|
||||
FAIL
|
||||
if not isinstance($value, ClsName):
|
||||
FAIL
|
||||
if $items is None:
|
||||
$items = type($value).__deconstruct__($value)
|
||||
# $VARS is a meta-variable.
|
||||
if len($items) != len($VARS):
|
||||
FAIL
|
||||
$VARS = $items
|
||||
|
||||
|
||||
.. note::
|
||||
|
@ -384,45 +383,96 @@ translates to::
|
|||
|
||||
Class patterns with keyword patterns::
|
||||
|
||||
match ClsName($VARS, $KEYWORD_PATTERNS) if guard:
|
||||
case ClsName($VARS, $KEYWORD_PATTERNS):
|
||||
|
||||
translates to::
|
||||
|
||||
if $kind & MATCH_CLASS:
|
||||
if isinstance($value, ClsName):
|
||||
if $attrs is None:
|
||||
$attrs = type($value).__attributes__
|
||||
if $items is None:
|
||||
$items = type($value).__deconstruct__($value)
|
||||
$right_attrs = attrs[len($VARS):]
|
||||
if set($right_attrs) >= set($KEYWORD_PATTERNS):
|
||||
$VARS = items[:len($VARS)]
|
||||
for $KEYWORD in $KEYWORD_PATTERNS:
|
||||
$index = $attrs.index(QUOTE($KEYWORD))
|
||||
$KEYWORD_PATTERNS[$KEYWORD] = $items[$index]
|
||||
if guard:
|
||||
DONE
|
||||
if $kind & MATCH_CLASS == 0:
|
||||
FAIL
|
||||
if not isinstance($value, ClsName):
|
||||
FAIL
|
||||
if $attrs is None:
|
||||
$attrs = type($value).__attributes__
|
||||
if $items is None:
|
||||
$items = type($value).__deconstruct__($value)
|
||||
$right_attrs = attrs[len($VARS):]
|
||||
if not set($right_attrs) >= set($KEYWORD_PATTERNS):
|
||||
FAIL
|
||||
$VARS = items[:len($VARS)]
|
||||
for $KEYWORD in $KEYWORD_PATTERNS:
|
||||
$index = $attrs.index(QUOTE($KEYWORD))
|
||||
$KEYWORD_PATTERNS[$KEYWORD] = $items[$index]
|
||||
|
||||
Example: [6]_
|
||||
|
||||
Class patterns with all keyword patterns::
|
||||
|
||||
match ClsName($KEYWORD_PATTERNS) if guard:
|
||||
case ClsName($KEYWORD_PATTERNS):
|
||||
|
||||
translates to::
|
||||
|
||||
if $kind & MATCH_CLASS:
|
||||
As above with $VARS == ()
|
||||
elif $kind & MATCH_DEFAULT:
|
||||
if isinstance($value, ClsName) and hasattr($value, "__dict__"):
|
||||
if $value.__dict__.keys() >= set($KEYWORD_PATTERNS):
|
||||
for $KEYWORD in $KEYWORD_PATTERNS:
|
||||
$KEYWORD_PATTERNS[$KEYWORD] = $value.__dict__[QUOTE($KEYWORD)]
|
||||
if guard:
|
||||
DONE
|
||||
if not isinstance($value, ClsName):
|
||||
FAIL
|
||||
if not hasattr($value, "__dict__"):
|
||||
FAIL
|
||||
if not $value.__dict__.keys() >= set($KEYWORD_PATTERNS):
|
||||
FAIL
|
||||
for $KEYWORD in $KEYWORD_PATTERNS:
|
||||
$KEYWORD_PATTERNS[$KEYWORD] = $value.__dict__[QUOTE($KEYWORD)]
|
||||
else:
|
||||
FAIL
|
||||
|
||||
Example: [7]_
|
||||
|
||||
|
||||
Nested patterns
|
||||
'''''''''''''''
|
||||
|
||||
The above specification assumes that patterns are not nested. For nested patterns
|
||||
the above translations are applied recursively by introducing temporary capture patterns.
|
||||
|
||||
For example, the pattern::
|
||||
|
||||
case [int(), str()]:
|
||||
|
||||
translates to::
|
||||
|
||||
if $kind & MATCH_SEQUENCE == 0:
|
||||
FAIL
|
||||
if $list is None:
|
||||
$list = list($value)
|
||||
if len($list) != 2:
|
||||
FAIL
|
||||
$value_0, $value_1 = $list
|
||||
#Now match on temporary values
|
||||
$kind_0 = type($value_0).__match_kind__
|
||||
if $kind_0 & (MATCH_CLASS | MATCH_DEFAULT) == 0:
|
||||
FAIL
|
||||
if not isinstance($value_0, int):
|
||||
FAIL
|
||||
$kind_1 = type($value_1).__match_kind__
|
||||
if $kind_1 & (MATCH_CLASS | MATCH_DEFAULT) == 0:
|
||||
FAIL
|
||||
if not isinstance($value_1, str):
|
||||
FAIL
|
||||
|
||||
Guards
|
||||
''''''
|
||||
|
||||
Guards translate to a test following the rest of the translation::
|
||||
|
||||
case pattern if guard:
|
||||
|
||||
translates to::
|
||||
|
||||
[translation for pattern]
|
||||
if not guard:
|
||||
FAIL
|
||||
|
||||
|
||||
Non-conforming ``__match_kind__``
|
||||
'''''''''''''''''''''''''''''''''
|
||||
|
||||
|
@ -659,13 +709,15 @@ This::
|
|||
|
||||
translates to::
|
||||
|
||||
if $kind & MATCH_SEQUENCE:
|
||||
if $list is None:
|
||||
$list = list($value)
|
||||
if len($list) == 2:
|
||||
a, b = $list
|
||||
if a is b:
|
||||
DONE
|
||||
if $kind & MATCH_SEQUENCE == 0:
|
||||
FAIL
|
||||
if $list is None:
|
||||
$list = list($value)
|
||||
if len($list) != 2:
|
||||
FAIL
|
||||
a, b = $list
|
||||
if not a is b:
|
||||
FAIL
|
||||
|
||||
.. [3]
|
||||
|
||||
|
@ -675,12 +727,13 @@ This::
|
|||
|
||||
translates to::
|
||||
|
||||
if $kind & MATCH_SEQUENCE:
|
||||
if $list is None:
|
||||
$list = list($value)
|
||||
if len($list) >= 2:
|
||||
a, *b, c = $list
|
||||
DONE
|
||||
if $kind & MATCH_SEQUENCE == 0:
|
||||
FAIL
|
||||
if $list is None:
|
||||
$list = list($value)
|
||||
if len($list) < 2:
|
||||
FAIL
|
||||
a, *b, c = $list
|
||||
|
||||
.. [4]
|
||||
|
||||
|
@ -690,14 +743,16 @@ This::
|
|||
|
||||
translates to::
|
||||
|
||||
if $kind & MATCH_MAPPING:
|
||||
if $dict is None:
|
||||
$dict = dict($value)
|
||||
if $dict.keys() == {"x", "y"}:
|
||||
x = $dict["x"]
|
||||
y = $dict["y"]
|
||||
if x > 2:
|
||||
DONE
|
||||
if $kind & MATCH_MAPPING == 0:
|
||||
FAIL
|
||||
if $dict is None:
|
||||
$dict = dict($value)
|
||||
if $dict.keys() != {"x", "y"}:
|
||||
FAIL
|
||||
x = $dict["x"]
|
||||
y = $dict["y"]
|
||||
if not x > 2:
|
||||
FAIL
|
||||
|
||||
.. [5]
|
||||
|
||||
|
@ -707,15 +762,16 @@ This::
|
|||
|
||||
translates to::
|
||||
|
||||
if $kind & MATCH_MAPPING:
|
||||
if $dict is None:
|
||||
$dict = dict($value)
|
||||
if $dict.keys() >= {"x", "y"}:
|
||||
$tmp = dict($dict)
|
||||
x = $tmp.pop("x")
|
||||
y = $tmp.pop("y")
|
||||
z = $tmp
|
||||
DONE
|
||||
if $kind & MATCH_MAPPING == 0:
|
||||
FAIL
|
||||
if $dict is None:
|
||||
$dict = dict($value)
|
||||
if not $dict.keys() >= {"x", "y"}:
|
||||
FAIL
|
||||
$tmp = dict($dict)
|
||||
x = $tmp.pop("x")
|
||||
y = $tmp.pop("y")
|
||||
z = $tmp
|
||||
|
||||
.. [6]
|
||||
|
||||
|
@ -725,18 +781,20 @@ This::
|
|||
|
||||
translates to::
|
||||
|
||||
if $kind & MATCH_CLASS:
|
||||
if isinstance($value, ClsName):
|
||||
if $attrs is None:
|
||||
$attrs = type($value).__attributes__
|
||||
if $items is None:
|
||||
$items = type($value).__deconstruct__($value)
|
||||
$right_attrs = $attrs[1:]
|
||||
if "a" in $right_attrs:
|
||||
$y_index = $attrs.index("a")
|
||||
x = $items[0]
|
||||
y = $items[$y_index]
|
||||
DONE
|
||||
if $kind & MATCH_CLASS == 0:
|
||||
FAIL
|
||||
if not isinstance($value, ClsName):
|
||||
FAIL
|
||||
if $attrs is None:
|
||||
$attrs = type($value).__attributes__
|
||||
if $items is None:
|
||||
$items = type($value).__deconstruct__($value)
|
||||
$right_attrs = $attrs[1:]
|
||||
if not "a" in $right_attrs:
|
||||
FAIL
|
||||
$y_index = $attrs.index("a")
|
||||
x = $items[0]
|
||||
y = $items[$y_index]
|
||||
|
||||
.. [7]
|
||||
|
||||
|
@ -747,24 +805,34 @@ This::
|
|||
translates to::
|
||||
|
||||
if $kind & MATCH_CLASS:
|
||||
if isinstance($value, ClsName):
|
||||
if $attrs is None:
|
||||
$attrs = type($value).__attributes__
|
||||
if $items is None:
|
||||
$items = type($value).__deconstruct__($value)
|
||||
if "a" in $attrs and "b" in $attrs:
|
||||
$x_index = $attrs.index("a")
|
||||
x = $items[$x_index]
|
||||
$y_index = $attrs.index("b")
|
||||
y = $items[$y_index]
|
||||
DONE
|
||||
if not isinstance($value, ClsName):
|
||||
FAIL
|
||||
if $attrs is None:
|
||||
$attrs = type($value).__attributes__
|
||||
if $items is None:
|
||||
$items = type($value).__deconstruct__($value)
|
||||
if not "a" in $attrs:
|
||||
FAIL
|
||||
if not "b" in $attrs:
|
||||
FAIL
|
||||
$x_index = $attrs.index("a")
|
||||
x = $items[$x_index]
|
||||
$y_index = $attrs.index("b")
|
||||
y = $items[$y_index]
|
||||
elif $kind & MATCH_DEFAULT:
|
||||
if isinstance($value, ClsName) and hasattr($value, "__dict__"):
|
||||
$obj_dict = $value.__dict__
|
||||
if "a" in $obj_dict and "b" in $obj_dict:
|
||||
x = $obj_dict["a"]
|
||||
y = $obj_dict["b"]
|
||||
DONE
|
||||
if not isinstance($value, ClsName):
|
||||
FAIL
|
||||
if not hasattr($value, "__dict__"):
|
||||
FAIL
|
||||
$obj_dict = $value.__dict__
|
||||
if not "a" in $attrs:
|
||||
FAIL
|
||||
if not "b" in $attrs:
|
||||
FAIL
|
||||
x = $obj_dict["a"]
|
||||
y = $obj_dict["b"]
|
||||
else:
|
||||
FAIL
|
||||
|
||||
|
||||
Copyright
|
||||
|
|
Loading…
Reference in New Issue