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:
Brandt Bucher
2021-02-26 14:51:55 -08:00
committed by GitHub
parent cc02b4f2e8
commit 145bf269df
43 changed files with 10867 additions and 2607 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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
View File

@@ -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);

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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
View File

@@ -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

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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}')

View File

@@ -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

View File

@@ -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__'

View File

@@ -54,7 +54,9 @@ kwlist = [
]
softkwlist = [
'_',
'case',
'match'
]
iskeyword = frozenset(kwlist).__contains__

View File

@@ -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)

View File

@@ -35,6 +35,7 @@ PGO_TESTS = [
'test_memoryview',
'test_operator',
'test_ordered_dict',
'test_patma',
'test_pickle',
'test_pprint',
'test_re',

View File

@@ -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()

View File

@@ -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

View File

@@ -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

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