mirror of
https://github.com/AdaCore/cpython.git
synced 2026-02-12 12:57:15 -08:00
bpo-42128: Structural Pattern Matching (PEP 634) (GH-22917)
Co-authored-by: Guido van Rossum <guido@python.org> Co-authored-by: Talin <viridia@gmail.com> Co-authored-by: Pablo Galindo <pablogsal@gmail.com>
This commit is contained in:
@@ -752,6 +752,49 @@ iterations of the loop.
|
||||
.. versionadded:: 3.2
|
||||
|
||||
|
||||
.. opcode:: COPY_DICT_WITHOUT_KEYS
|
||||
|
||||
TOS is a tuple of mapping keys, and TOS1 is the match subject. Replace TOS
|
||||
with a :class:`dict` formed from the items of TOS1, but without any of the
|
||||
keys in TOS.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
|
||||
|
||||
.. opcode:: GET_LEN
|
||||
|
||||
Push ``len(TOS)`` onto the stack.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
|
||||
|
||||
.. opcode:: MATCH_MAPPING
|
||||
|
||||
If TOS is an instance of :class:`collections.abc.Mapping`, push ``True`` onto
|
||||
the stack. Otherwise, push ``False``.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
|
||||
|
||||
.. opcode:: MATCH_SEQUENCE
|
||||
|
||||
If TOS is an instance of :class:`collections.abc.Sequence` and is *not* an
|
||||
instance of :class:`str`/:class:`bytes`/:class:`bytearray`, push ``True``
|
||||
onto the stack. Otherwise, push ``False``.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
|
||||
|
||||
.. opcode:: MATCH_KEYS
|
||||
|
||||
TOS is a tuple of mapping keys, and TOS1 is the match subject. If TOS1
|
||||
contains all of the keys in TOS, push a :class:`tuple` containing the
|
||||
corresponding values, followed by ``True``. Otherwise, push ``None``,
|
||||
followed by ``False``.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
|
||||
|
||||
All of the following opcodes use their arguments.
|
||||
|
||||
.. opcode:: STORE_NAME (namei)
|
||||
@@ -1192,6 +1235,19 @@ All of the following opcodes use their arguments.
|
||||
.. versionadded:: 3.6
|
||||
|
||||
|
||||
.. opcode:: MATCH_CLASS (count)
|
||||
|
||||
TOS is a tuple of keyword attribute names, TOS1 is the class being matched
|
||||
against, and TOS2 is the match subject. *count* is the number of positional
|
||||
sub-patterns.
|
||||
|
||||
Pop TOS. If TOS2 is an instance of TOS1 and has the positional and keyword
|
||||
attributes required by *count* and TOS, set TOS to ``True`` and TOS1 to a
|
||||
tuple of extracted attributes. Otherwise, set TOS to ``False``.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
|
||||
|
||||
.. opcode:: HAVE_ARGUMENT
|
||||
|
||||
This is not really an opcode. It identifies the dividing line between
|
||||
|
||||
@@ -365,3 +365,4 @@ whatsnew/changelog,,::,default::DeprecationWarning
|
||||
library/importlib.metadata,,:main,"EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts')"
|
||||
library/importlib.metadata,,`,loading the metadata for packages for the indicated ``context``.
|
||||
library/re,,`,"`"
|
||||
library/dis,,:TOS1,TOS[x:TOS1 - 1 - y]
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 344 and column 55.
|
@@ -85,6 +85,7 @@ compound_stmt[stmt_ty]:
|
||||
| &('for' | ASYNC) for_stmt
|
||||
| &'try' try_stmt
|
||||
| &'while' while_stmt
|
||||
| match_stmt
|
||||
|
||||
# NOTE: annotated_rhs may start with 'yield'; yield_expr must start with 'yield'
|
||||
assignment[stmt_ty]:
|
||||
@@ -207,6 +208,114 @@ except_block[excepthandler_ty]:
|
||||
| invalid_except_block
|
||||
finally_block[asdl_stmt_seq*]: 'finally' ':' a=block { a }
|
||||
|
||||
match_stmt[stmt_ty]:
|
||||
| "match" subject=subject_expr ':' NEWLINE INDENT cases[asdl_match_case_seq*]=case_block+ DEDENT {
|
||||
CHECK_VERSION(stmt_ty, 10, "Pattern matching is", _Py_Match(subject, cases, EXTRA)) }
|
||||
subject_expr[expr_ty]:
|
||||
| value=star_named_expression ',' values=star_named_expressions? {
|
||||
_Py_Tuple(CHECK(asdl_expr_seq*, _PyPegen_seq_insert_in_front(p, value, values)), Load, EXTRA) }
|
||||
| named_expression
|
||||
case_block[match_case_ty]:
|
||||
| "case" pattern=patterns guard=guard? ':' body=block {
|
||||
_Py_match_case(pattern, guard, body, p->arena) }
|
||||
guard[expr_ty]: 'if' guard=named_expression { guard }
|
||||
|
||||
patterns[expr_ty]:
|
||||
| values[asdl_expr_seq*]=open_sequence_pattern {
|
||||
_Py_Tuple(values, Load, EXTRA) }
|
||||
| pattern
|
||||
pattern[expr_ty]:
|
||||
| as_pattern
|
||||
| or_pattern
|
||||
as_pattern[expr_ty]:
|
||||
| pattern=or_pattern 'as' target=capture_pattern {
|
||||
_Py_MatchAs(pattern, target->v.Name.id, EXTRA) }
|
||||
or_pattern[expr_ty]:
|
||||
| patterns[asdl_expr_seq*]='|'.closed_pattern+ {
|
||||
asdl_seq_LEN(patterns) == 1 ? asdl_seq_GET(patterns, 0) : _Py_MatchOr(patterns, EXTRA) }
|
||||
closed_pattern[expr_ty]:
|
||||
| literal_pattern
|
||||
| capture_pattern
|
||||
| wildcard_pattern
|
||||
| value_pattern
|
||||
| group_pattern
|
||||
| sequence_pattern
|
||||
| mapping_pattern
|
||||
| class_pattern
|
||||
|
||||
literal_pattern[expr_ty]:
|
||||
| signed_number !('+' | '-')
|
||||
| real=signed_number '+' imag=NUMBER { _Py_BinOp(real, Add, imag, EXTRA) }
|
||||
| real=signed_number '-' imag=NUMBER { _Py_BinOp(real, Sub, imag, EXTRA) }
|
||||
| strings
|
||||
| 'None' { _Py_Constant(Py_None, NULL, EXTRA) }
|
||||
| 'True' { _Py_Constant(Py_True, NULL, EXTRA) }
|
||||
| 'False' { _Py_Constant(Py_False, NULL, EXTRA) }
|
||||
signed_number[expr_ty]:
|
||||
| NUMBER
|
||||
| '-' number=NUMBER { _Py_UnaryOp(USub, number, EXTRA) }
|
||||
|
||||
capture_pattern[expr_ty]:
|
||||
| !"_" name=NAME !('.' | '(' | '=') {
|
||||
_PyPegen_set_expr_context(p, name, Store) }
|
||||
|
||||
wildcard_pattern[expr_ty]:
|
||||
| "_" { _Py_Name(CHECK(PyObject*, _PyPegen_new_identifier(p, "_")), Store, EXTRA) }
|
||||
|
||||
value_pattern[expr_ty]:
|
||||
| attr=attr !('.' | '(' | '=') { attr }
|
||||
attr[expr_ty]:
|
||||
| value=name_or_attr '.' attr=NAME {
|
||||
_Py_Attribute(value, attr->v.Name.id, Load, EXTRA) }
|
||||
name_or_attr[expr_ty]:
|
||||
| attr
|
||||
| NAME
|
||||
|
||||
group_pattern[expr_ty]:
|
||||
| '(' pattern=pattern ')' { pattern }
|
||||
|
||||
sequence_pattern[expr_ty]:
|
||||
| '[' values=maybe_sequence_pattern? ']' { _Py_List(values, Load, EXTRA) }
|
||||
| '(' values=open_sequence_pattern? ')' { _Py_Tuple(values, Load, EXTRA) }
|
||||
open_sequence_pattern[asdl_seq*]:
|
||||
| value=maybe_star_pattern ',' values=maybe_sequence_pattern? {
|
||||
_PyPegen_seq_insert_in_front(p, value, values) }
|
||||
maybe_sequence_pattern[asdl_seq*]:
|
||||
| values=','.maybe_star_pattern+ ','? { values }
|
||||
maybe_star_pattern[expr_ty]:
|
||||
| star_pattern
|
||||
| pattern
|
||||
star_pattern[expr_ty]:
|
||||
| '*' value=(capture_pattern | wildcard_pattern) {
|
||||
_Py_Starred(value, Store, EXTRA) }
|
||||
|
||||
mapping_pattern[expr_ty]:
|
||||
| '{' items=items_pattern? '}' {
|
||||
_Py_Dict(CHECK(asdl_expr_seq*, _PyPegen_get_keys(p, items)), CHECK(asdl_expr_seq*, _PyPegen_get_values(p, items)), EXTRA) }
|
||||
items_pattern[asdl_seq*]:
|
||||
| items=','.key_value_pattern+ ','? { items }
|
||||
key_value_pattern[KeyValuePair*]:
|
||||
| key=(literal_pattern | value_pattern) ':' value=pattern {
|
||||
_PyPegen_key_value_pair(p, key, value) }
|
||||
| double_star_pattern
|
||||
double_star_pattern[KeyValuePair*]:
|
||||
| '**' value=capture_pattern { _PyPegen_key_value_pair(p, NULL, value) }
|
||||
|
||||
class_pattern[expr_ty]:
|
||||
| func=name_or_attr '(' ')' { _Py_Call(func, NULL, NULL, EXTRA) }
|
||||
| func=name_or_attr '(' args=positional_patterns ','? ')' {
|
||||
_Py_Call(func, args, NULL, EXTRA) }
|
||||
| func=name_or_attr '(' keywords=keyword_patterns ','? ')' {
|
||||
_Py_Call(func, NULL, keywords, EXTRA) }
|
||||
| func=name_or_attr '(' args=positional_patterns ',' keywords=keyword_patterns ','? ')' {
|
||||
_Py_Call(func, args, keywords, EXTRA) }
|
||||
positional_patterns[asdl_expr_seq*]:
|
||||
| args[asdl_expr_seq*]=','.pattern+ { args }
|
||||
keyword_patterns[asdl_keyword_seq*]:
|
||||
| keywords[asdl_keyword_seq*]=','.keyword_pattern+ { keywords }
|
||||
keyword_pattern[keyword_ty]:
|
||||
| arg=NAME '=' value=pattern { _Py_keyword(arg->v.Name.id, value, EXTRA) }
|
||||
|
||||
return_stmt[stmt_ty]:
|
||||
| 'return' a=[star_expressions] { _Py_Return(a, EXTRA) }
|
||||
|
||||
@@ -676,7 +785,7 @@ invalid_assignment:
|
||||
RAISE_SYNTAX_ERROR_INVALID_TARGET(STAR_TARGETS, a) }
|
||||
| (star_targets '=')* a=yield_expr '=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "assignment to yield expression not possible") }
|
||||
| a=star_expressions augassign (yield_expr | star_expressions) {
|
||||
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
|
||||
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
|
||||
a,
|
||||
"'%s' is an illegal expression for augmented assignment",
|
||||
_PyPegen_get_expr_name(a)
|
||||
|
||||
55
Include/Python-ast.h
generated
55
Include/Python-ast.h
generated
@@ -44,6 +44,8 @@ typedef struct _alias *alias_ty;
|
||||
|
||||
typedef struct _withitem *withitem_ty;
|
||||
|
||||
typedef struct _match_case *match_case_ty;
|
||||
|
||||
typedef struct _type_ignore *type_ignore_ty;
|
||||
|
||||
|
||||
@@ -119,6 +121,14 @@ typedef struct {
|
||||
|
||||
asdl_withitem_seq *_Py_asdl_withitem_seq_new(Py_ssize_t size, PyArena *arena);
|
||||
|
||||
typedef struct {
|
||||
_ASDL_SEQ_HEAD
|
||||
match_case_ty typed_elements[1];
|
||||
} asdl_match_case_seq;
|
||||
|
||||
asdl_match_case_seq *_Py_asdl_match_case_seq_new(Py_ssize_t size, PyArena
|
||||
*arena);
|
||||
|
||||
typedef struct {
|
||||
_ASDL_SEQ_HEAD
|
||||
type_ignore_ty typed_elements[1];
|
||||
@@ -158,10 +168,10 @@ enum _stmt_kind {FunctionDef_kind=1, AsyncFunctionDef_kind=2, ClassDef_kind=3,
|
||||
Return_kind=4, Delete_kind=5, Assign_kind=6,
|
||||
AugAssign_kind=7, AnnAssign_kind=8, For_kind=9,
|
||||
AsyncFor_kind=10, While_kind=11, If_kind=12, With_kind=13,
|
||||
AsyncWith_kind=14, Raise_kind=15, Try_kind=16,
|
||||
Assert_kind=17, Import_kind=18, ImportFrom_kind=19,
|
||||
Global_kind=20, Nonlocal_kind=21, Expr_kind=22, Pass_kind=23,
|
||||
Break_kind=24, Continue_kind=25};
|
||||
AsyncWith_kind=14, Match_kind=15, Raise_kind=16, Try_kind=17,
|
||||
Assert_kind=18, Import_kind=19, ImportFrom_kind=20,
|
||||
Global_kind=21, Nonlocal_kind=22, Expr_kind=23, Pass_kind=24,
|
||||
Break_kind=25, Continue_kind=26};
|
||||
struct _stmt {
|
||||
enum _stmt_kind kind;
|
||||
union {
|
||||
@@ -258,6 +268,11 @@ struct _stmt {
|
||||
string type_comment;
|
||||
} AsyncWith;
|
||||
|
||||
struct {
|
||||
expr_ty subject;
|
||||
asdl_match_case_seq *cases;
|
||||
} Match;
|
||||
|
||||
struct {
|
||||
expr_ty exc;
|
||||
expr_ty cause;
|
||||
@@ -311,7 +326,8 @@ enum _expr_kind {BoolOp_kind=1, NamedExpr_kind=2, BinOp_kind=3, UnaryOp_kind=4,
|
||||
YieldFrom_kind=15, Compare_kind=16, Call_kind=17,
|
||||
FormattedValue_kind=18, JoinedStr_kind=19, Constant_kind=20,
|
||||
Attribute_kind=21, Subscript_kind=22, Starred_kind=23,
|
||||
Name_kind=24, List_kind=25, Tuple_kind=26, Slice_kind=27};
|
||||
Name_kind=24, List_kind=25, Tuple_kind=26, Slice_kind=27,
|
||||
MatchAs_kind=28, MatchOr_kind=29};
|
||||
struct _expr {
|
||||
enum _expr_kind kind;
|
||||
union {
|
||||
@@ -454,6 +470,15 @@ struct _expr {
|
||||
expr_ty step;
|
||||
} Slice;
|
||||
|
||||
struct {
|
||||
expr_ty pattern;
|
||||
identifier name;
|
||||
} MatchAs;
|
||||
|
||||
struct {
|
||||
asdl_expr_seq *patterns;
|
||||
} MatchOr;
|
||||
|
||||
} v;
|
||||
int lineno;
|
||||
int col_offset;
|
||||
@@ -524,6 +549,12 @@ struct _withitem {
|
||||
expr_ty optional_vars;
|
||||
};
|
||||
|
||||
struct _match_case {
|
||||
expr_ty pattern;
|
||||
expr_ty guard;
|
||||
asdl_stmt_seq *body;
|
||||
};
|
||||
|
||||
enum _type_ignore_kind {TypeIgnore_kind=1};
|
||||
struct _type_ignore {
|
||||
enum _type_ignore_kind kind;
|
||||
@@ -607,6 +638,10 @@ stmt_ty _Py_With(asdl_withitem_seq * items, asdl_stmt_seq * body, string
|
||||
stmt_ty _Py_AsyncWith(asdl_withitem_seq * items, asdl_stmt_seq * body, string
|
||||
type_comment, int lineno, int col_offset, int end_lineno,
|
||||
int end_col_offset, PyArena *arena);
|
||||
#define Match(a0, a1, a2, a3, a4, a5, a6) _Py_Match(a0, a1, a2, a3, a4, a5, a6)
|
||||
stmt_ty _Py_Match(expr_ty subject, asdl_match_case_seq * cases, int lineno, int
|
||||
col_offset, int end_lineno, int end_col_offset, PyArena
|
||||
*arena);
|
||||
#define Raise(a0, a1, a2, a3, a4, a5, a6) _Py_Raise(a0, a1, a2, a3, a4, a5, a6)
|
||||
stmt_ty _Py_Raise(expr_ty exc, expr_ty cause, int lineno, int col_offset, int
|
||||
end_lineno, int end_col_offset, PyArena *arena);
|
||||
@@ -743,6 +778,13 @@ expr_ty _Py_Tuple(asdl_expr_seq * elts, expr_context_ty ctx, int lineno, int
|
||||
expr_ty _Py_Slice(expr_ty lower, expr_ty upper, expr_ty step, int lineno, int
|
||||
col_offset, int end_lineno, int end_col_offset, PyArena
|
||||
*arena);
|
||||
#define MatchAs(a0, a1, a2, a3, a4, a5, a6) _Py_MatchAs(a0, a1, a2, a3, a4, a5, a6)
|
||||
expr_ty _Py_MatchAs(expr_ty pattern, identifier name, int lineno, int
|
||||
col_offset, int end_lineno, int end_col_offset, PyArena
|
||||
*arena);
|
||||
#define MatchOr(a0, a1, a2, a3, a4, a5) _Py_MatchOr(a0, a1, a2, a3, a4, a5)
|
||||
expr_ty _Py_MatchOr(asdl_expr_seq * patterns, int lineno, int col_offset, int
|
||||
end_lineno, int end_col_offset, PyArena *arena);
|
||||
#define comprehension(a0, a1, a2, a3, a4) _Py_comprehension(a0, a1, a2, a3, a4)
|
||||
comprehension_ty _Py_comprehension(expr_ty target, expr_ty iter, asdl_expr_seq
|
||||
* ifs, int is_async, PyArena *arena);
|
||||
@@ -769,6 +811,9 @@ alias_ty _Py_alias(identifier name, identifier asname, PyArena *arena);
|
||||
#define withitem(a0, a1, a2) _Py_withitem(a0, a1, a2)
|
||||
withitem_ty _Py_withitem(expr_ty context_expr, expr_ty optional_vars, PyArena
|
||||
*arena);
|
||||
#define match_case(a0, a1, a2, a3) _Py_match_case(a0, a1, a2, a3)
|
||||
match_case_ty _Py_match_case(expr_ty pattern, expr_ty guard, asdl_stmt_seq *
|
||||
body, PyArena *arena);
|
||||
#define TypeIgnore(a0, a1, a2) _Py_TypeIgnore(a0, a1, a2)
|
||||
type_ignore_ty _Py_TypeIgnore(int lineno, string tag, PyArena *arena);
|
||||
|
||||
|
||||
@@ -91,6 +91,9 @@ struct ast_state {
|
||||
PyObject *Lt_type;
|
||||
PyObject *MatMult_singleton;
|
||||
PyObject *MatMult_type;
|
||||
PyObject *MatchAs_type;
|
||||
PyObject *MatchOr_type;
|
||||
PyObject *Match_type;
|
||||
PyObject *Mod_singleton;
|
||||
PyObject *Mod_type;
|
||||
PyObject *Module_type;
|
||||
@@ -137,6 +140,7 @@ struct ast_state {
|
||||
PyObject *Yield_type;
|
||||
PyObject *__dict__;
|
||||
PyObject *__doc__;
|
||||
PyObject *__match_args__;
|
||||
PyObject *__module__;
|
||||
PyObject *_attributes;
|
||||
PyObject *_fields;
|
||||
@@ -153,6 +157,7 @@ struct ast_state {
|
||||
PyObject *bases;
|
||||
PyObject *body;
|
||||
PyObject *boolop_type;
|
||||
PyObject *cases;
|
||||
PyObject *cause;
|
||||
PyObject *cmpop_type;
|
||||
PyObject *col_offset;
|
||||
@@ -175,6 +180,7 @@ struct ast_state {
|
||||
PyObject *format_spec;
|
||||
PyObject *func;
|
||||
PyObject *generators;
|
||||
PyObject *guard;
|
||||
PyObject *handlers;
|
||||
PyObject *id;
|
||||
PyObject *ifs;
|
||||
@@ -193,6 +199,7 @@ struct ast_state {
|
||||
PyObject *level;
|
||||
PyObject *lineno;
|
||||
PyObject *lower;
|
||||
PyObject *match_case_type;
|
||||
PyObject *mod_type;
|
||||
PyObject *module;
|
||||
PyObject *msg;
|
||||
@@ -204,6 +211,8 @@ struct ast_state {
|
||||
PyObject *ops;
|
||||
PyObject *optional_vars;
|
||||
PyObject *orelse;
|
||||
PyObject *pattern;
|
||||
PyObject *patterns;
|
||||
PyObject *posonlyargs;
|
||||
PyObject *returns;
|
||||
PyObject *right;
|
||||
@@ -211,6 +220,7 @@ struct ast_state {
|
||||
PyObject *slice;
|
||||
PyObject *step;
|
||||
PyObject *stmt_type;
|
||||
PyObject *subject;
|
||||
PyObject *tag;
|
||||
PyObject *target;
|
||||
PyObject *targets;
|
||||
|
||||
@@ -253,6 +253,10 @@ struct _is {
|
||||
// importlib module
|
||||
PyObject *importlib;
|
||||
|
||||
// Kept handy for pattern matching:
|
||||
PyObject *map_abc; // _collections_abc.Mapping
|
||||
PyObject *seq_abc; // _collections_abc.Sequence
|
||||
|
||||
/* Used in Modules/_threadmodule.c. */
|
||||
long num_threads;
|
||||
/* Support for runtime thread stack size tuning.
|
||||
@@ -347,4 +351,3 @@ PyAPI_FUNC(void) _PyInterpreterState_IDDecref(struct _is *);
|
||||
}
|
||||
#endif
|
||||
#endif /* !Py_INTERNAL_INTERP_H */
|
||||
|
||||
|
||||
@@ -359,6 +359,11 @@ given type object has a specified feature.
|
||||
#define Py_TPFLAGS_HAVE_AM_SEND (1UL << 21)
|
||||
#endif
|
||||
|
||||
// This undocumented flag gives certain built-ins their unique pattern-matching
|
||||
// behavior, which allows a single positional subpattern to match against the
|
||||
// subject itself (rather than a mapped attribute on it):
|
||||
#define _Py_TPFLAGS_MATCH_SELF (1UL << 22)
|
||||
|
||||
/* These flags are used to determine if a type is a subclass. */
|
||||
#define Py_TPFLAGS_LONG_SUBCLASS (1UL << 24)
|
||||
#define Py_TPFLAGS_LIST_SUBCLASS (1UL << 25)
|
||||
|
||||
6
Include/opcode.h
generated
6
Include/opcode.h
generated
@@ -30,6 +30,11 @@ extern "C" {
|
||||
#define BINARY_TRUE_DIVIDE 27
|
||||
#define INPLACE_FLOOR_DIVIDE 28
|
||||
#define INPLACE_TRUE_DIVIDE 29
|
||||
#define GET_LEN 30
|
||||
#define MATCH_MAPPING 31
|
||||
#define MATCH_SEQUENCE 32
|
||||
#define MATCH_KEYS 33
|
||||
#define COPY_DICT_WITHOUT_KEYS 34
|
||||
#define WITH_EXCEPT_START 49
|
||||
#define GET_AITER 50
|
||||
#define GET_ANEXT 51
|
||||
@@ -117,6 +122,7 @@ extern "C" {
|
||||
#define SET_ADD 146
|
||||
#define MAP_ADD 147
|
||||
#define LOAD_CLASSDEREF 148
|
||||
#define MATCH_CLASS 152
|
||||
#define SETUP_ASYNC_WITH 154
|
||||
#define FORMAT_VALUE 155
|
||||
#define BUILD_CONST_KEY_MAP 156
|
||||
|
||||
@@ -33,6 +33,7 @@ struct symtable {
|
||||
the symbol table */
|
||||
int recursion_depth; /* current recursion depth */
|
||||
int recursion_limit; /* recursion limit */
|
||||
int in_pattern; /* whether we are currently in a pattern */
|
||||
};
|
||||
|
||||
typedef struct _symtable_entry {
|
||||
|
||||
27
Lib/ast.py
27
Lib/ast.py
@@ -1478,6 +1478,13 @@ class _Unparser(NodeVisitor):
|
||||
self.write(":")
|
||||
self.traverse(node.step)
|
||||
|
||||
def visit_Match(self, node):
|
||||
self.fill("match ")
|
||||
self.traverse(node.subject)
|
||||
with self.block():
|
||||
for case in node.cases:
|
||||
self.traverse(case)
|
||||
|
||||
def visit_arg(self, node):
|
||||
self.write(node.arg)
|
||||
if node.annotation:
|
||||
@@ -1562,6 +1569,26 @@ class _Unparser(NodeVisitor):
|
||||
self.write(" as ")
|
||||
self.traverse(node.optional_vars)
|
||||
|
||||
def visit_match_case(self, node):
|
||||
self.fill("case ")
|
||||
self.traverse(node.pattern)
|
||||
if node.guard:
|
||||
self.write(" if ")
|
||||
self.traverse(node.guard)
|
||||
with self.block():
|
||||
self.traverse(node.body)
|
||||
|
||||
def visit_MatchAs(self, node):
|
||||
with self.require_parens(_Precedence.TEST, node):
|
||||
self.set_precedence(_Precedence.BOR, node.pattern)
|
||||
self.traverse(node.pattern)
|
||||
self.write(f" as {node.name}")
|
||||
|
||||
def visit_MatchOr(self, node):
|
||||
with self.require_parens(_Precedence.BOR, node):
|
||||
self.set_precedence(_Precedence.BOR.next(), *node.patterns)
|
||||
self.interleave(lambda: self.write(" | "), self.traverse, node.patterns)
|
||||
|
||||
def unparse(ast_obj):
|
||||
unparser = _Unparser()
|
||||
return unparser.visit(ast_obj)
|
||||
|
||||
@@ -472,6 +472,7 @@ def namedtuple(typename, field_names, *, rename=False, defaults=None, module=Non
|
||||
'__repr__': __repr__,
|
||||
'_asdict': _asdict,
|
||||
'__getnewargs__': __getnewargs__,
|
||||
'__match_args__': field_names,
|
||||
}
|
||||
for index, name in enumerate(field_names):
|
||||
doc = _sys.intern(f'Alias for field number {index}')
|
||||
|
||||
@@ -152,6 +152,15 @@ __all__ = ['dataclass',
|
||||
#
|
||||
# See _hash_action (below) for a coded version of this table.
|
||||
|
||||
# __match_args__
|
||||
#
|
||||
# | no | yes | <--- class has __match_args__ in __dict__?
|
||||
# +=======+=======+
|
||||
# | add | | <- the default
|
||||
# +=======+=======+
|
||||
# __match_args__ is always added unless the class already defines it. It is a
|
||||
# tuple of __init__ parameter names; non-init fields must be matched by keyword.
|
||||
|
||||
|
||||
# Raised when an attempt is made to modify a frozen class.
|
||||
class FrozenInstanceError(AttributeError): pass
|
||||
@@ -1007,6 +1016,9 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):
|
||||
cls.__doc__ = (cls.__name__ +
|
||||
str(inspect.signature(cls)).replace(' -> NoneType', ''))
|
||||
|
||||
if '__match_args__' not in cls.__dict__:
|
||||
cls.__match_args__ = tuple(f.name for f in flds if f.init)
|
||||
|
||||
abc.update_abstractmethods(cls)
|
||||
|
||||
return cls
|
||||
|
||||
@@ -313,6 +313,7 @@ _code_type = type(_write_atomic.__code__)
|
||||
# Python 3.10a1 3431 (New line number table format -- PEP 626)
|
||||
# Python 3.10a2 3432 (Function annotation for MAKE_FUNCTION is changed from dict to tuple bpo-42202)
|
||||
# Python 3.10a2 3433 (RERAISE restores f_lasti if oparg != 0)
|
||||
# Python 3.10a6 3434 (PEP 634: Structural Pattern Matching)
|
||||
|
||||
#
|
||||
# MAGIC must change whenever the bytecode emitted by the compiler may no
|
||||
@@ -322,7 +323,7 @@ _code_type = type(_write_atomic.__code__)
|
||||
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
|
||||
# in PC/launcher.c must also be updated.
|
||||
|
||||
MAGIC_NUMBER = (3433).to_bytes(2, 'little') + b'\r\n'
|
||||
MAGIC_NUMBER = (3434).to_bytes(2, 'little') + b'\r\n'
|
||||
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
|
||||
|
||||
_PYCACHE = '__pycache__'
|
||||
|
||||
@@ -54,7 +54,9 @@ kwlist = [
|
||||
]
|
||||
|
||||
softkwlist = [
|
||||
|
||||
'_',
|
||||
'case',
|
||||
'match'
|
||||
]
|
||||
|
||||
iskeyword = frozenset(kwlist).__contains__
|
||||
|
||||
@@ -67,7 +67,6 @@ def_op('UNARY_NEGATIVE', 11)
|
||||
def_op('UNARY_NOT', 12)
|
||||
|
||||
def_op('UNARY_INVERT', 15)
|
||||
|
||||
def_op('BINARY_MATRIX_MULTIPLY', 16)
|
||||
def_op('INPLACE_MATRIX_MULTIPLY', 17)
|
||||
|
||||
@@ -82,6 +81,11 @@ def_op('BINARY_FLOOR_DIVIDE', 26)
|
||||
def_op('BINARY_TRUE_DIVIDE', 27)
|
||||
def_op('INPLACE_FLOOR_DIVIDE', 28)
|
||||
def_op('INPLACE_TRUE_DIVIDE', 29)
|
||||
def_op('GET_LEN', 30)
|
||||
def_op('MATCH_MAPPING', 31)
|
||||
def_op('MATCH_SEQUENCE', 32)
|
||||
def_op('MATCH_KEYS', 33)
|
||||
def_op('COPY_DICT_WITHOUT_KEYS', 34)
|
||||
|
||||
def_op('WITH_EXCEPT_START', 49)
|
||||
def_op('GET_AITER', 50)
|
||||
@@ -104,7 +108,6 @@ def_op('BINARY_OR', 66)
|
||||
def_op('INPLACE_POWER', 67)
|
||||
def_op('GET_ITER', 68)
|
||||
def_op('GET_YIELD_FROM_ITER', 69)
|
||||
|
||||
def_op('PRINT_EXPR', 70)
|
||||
def_op('LOAD_BUILD_CLASS', 71)
|
||||
def_op('YIELD_FROM', 72)
|
||||
@@ -136,6 +139,7 @@ name_op('STORE_ATTR', 95) # Index in name list
|
||||
name_op('DELETE_ATTR', 96) # ""
|
||||
name_op('STORE_GLOBAL', 97) # ""
|
||||
name_op('DELETE_GLOBAL', 98) # ""
|
||||
|
||||
def_op('LOAD_CONST', 100) # Index in const list
|
||||
hasconst.append(100)
|
||||
name_op('LOAD_NAME', 101) # Index in name list
|
||||
@@ -148,16 +152,13 @@ def_op('COMPARE_OP', 107) # Comparison operator
|
||||
hascompare.append(107)
|
||||
name_op('IMPORT_NAME', 108) # Index in name list
|
||||
name_op('IMPORT_FROM', 109) # Index in name list
|
||||
|
||||
jrel_op('JUMP_FORWARD', 110) # Number of bytes to skip
|
||||
jabs_op('JUMP_IF_FALSE_OR_POP', 111) # Target byte offset from beginning of code
|
||||
jabs_op('JUMP_IF_TRUE_OR_POP', 112) # ""
|
||||
jabs_op('JUMP_ABSOLUTE', 113) # ""
|
||||
jabs_op('POP_JUMP_IF_FALSE', 114) # ""
|
||||
jabs_op('POP_JUMP_IF_TRUE', 115) # ""
|
||||
|
||||
name_op('LOAD_GLOBAL', 116) # Index in name list
|
||||
|
||||
def_op('IS_OP', 117)
|
||||
def_op('CONTAINS_OP', 118)
|
||||
def_op('RERAISE', 119)
|
||||
@@ -176,6 +177,7 @@ def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
|
||||
def_op('CALL_FUNCTION', 131) # #args
|
||||
def_op('MAKE_FUNCTION', 132) # Flags
|
||||
def_op('BUILD_SLICE', 133) # Number of items
|
||||
|
||||
def_op('LOAD_CLOSURE', 135)
|
||||
hasfree.append(135)
|
||||
def_op('LOAD_DEREF', 136)
|
||||
@@ -187,28 +189,24 @@ hasfree.append(138)
|
||||
|
||||
def_op('CALL_FUNCTION_KW', 141) # #args + #kwargs
|
||||
def_op('CALL_FUNCTION_EX', 142) # Flags
|
||||
|
||||
jrel_op('SETUP_WITH', 143)
|
||||
|
||||
def_op('EXTENDED_ARG', 144)
|
||||
EXTENDED_ARG = 144
|
||||
def_op('LIST_APPEND', 145)
|
||||
def_op('SET_ADD', 146)
|
||||
def_op('MAP_ADD', 147)
|
||||
|
||||
def_op('LOAD_CLASSDEREF', 148)
|
||||
hasfree.append(148)
|
||||
|
||||
def_op('EXTENDED_ARG', 144)
|
||||
EXTENDED_ARG = 144
|
||||
def_op('MATCH_CLASS', 152)
|
||||
|
||||
jrel_op('SETUP_ASYNC_WITH', 154)
|
||||
|
||||
def_op('FORMAT_VALUE', 155)
|
||||
def_op('BUILD_CONST_KEY_MAP', 156)
|
||||
def_op('BUILD_STRING', 157)
|
||||
|
||||
name_op('LOAD_METHOD', 160)
|
||||
def_op('CALL_METHOD', 161)
|
||||
|
||||
def_op('LIST_EXTEND', 162)
|
||||
def_op('SET_UPDATE', 163)
|
||||
def_op('DICT_MERGE', 164)
|
||||
|
||||
@@ -35,6 +35,7 @@ PGO_TESTS = [
|
||||
'test_memoryview',
|
||||
'test_operator',
|
||||
'test_ordered_dict',
|
||||
'test_patma',
|
||||
'test_pickle',
|
||||
'test_pprint',
|
||||
'test_re',
|
||||
|
||||
@@ -273,6 +273,7 @@ class AST_Tests(unittest.TestCase):
|
||||
self._assertTrueorder(child, first_pos)
|
||||
elif value is not None:
|
||||
self._assertTrueorder(value, parent_pos)
|
||||
self.assertEqual(ast_node._fields, ast_node.__match_args__)
|
||||
|
||||
def test_AST_objects(self):
|
||||
x = ast.AST()
|
||||
|
||||
@@ -687,6 +687,10 @@ class TestNamedTuple(unittest.TestCase):
|
||||
self.assertEqual(new_func.__globals__['__builtins__'], {})
|
||||
self.assertEqual(new_func.__builtins__, {})
|
||||
|
||||
def test_match_args(self):
|
||||
Point = namedtuple('Point', 'x y')
|
||||
self.assertEqual(Point.__match_args__, ('x', 'y'))
|
||||
|
||||
|
||||
################################################################################
|
||||
### Abstract Base Classes
|
||||
|
||||
@@ -3375,5 +3375,21 @@ class TestAbstract(unittest.TestCase):
|
||||
self.assertRaisesRegex(TypeError, msg, Date)
|
||||
|
||||
|
||||
class TestMatchArgs(unittest.TestCase):
|
||||
def test_match_args(self):
|
||||
@dataclass
|
||||
class C:
|
||||
a: int
|
||||
self.assertEqual(C(42).__match_args__, ('a',))
|
||||
|
||||
def test_explicit_match_args(self):
|
||||
ma = []
|
||||
@dataclass
|
||||
class C:
|
||||
a: int
|
||||
__match_args__ = ma
|
||||
self.assertIs(C(42).__match_args__, ma)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
2878
Lib/test/test_patma.py
Normal file
2878
Lib/test/test_patma.py
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user