From 041ab30747bc337e67fa8d7c9769691f5e80d890 Mon Sep 17 00:00:00 2001 From: Hunter Hogan Date: Sun, 25 May 2025 21:10:57 -0500 Subject: [PATCH 1/3] gh-134674: Fix MatchStar name field handling and ast.dump display --- Lib/ast.py | 4 ++-- ...-05-25-20-30-00.gh-issue-134674.MatchS.rst | 4 ++++ Parser/Python.asdl | 2 +- Python/Python-ast.c | 24 +++++++++---------- 4 files changed, 18 insertions(+), 16 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-05-25-20-30-00.gh-issue-134674.MatchS.rst diff --git a/Lib/ast.py b/Lib/ast.py index b9791bf52d3e08..45783cbd04870a 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -151,8 +151,8 @@ def _format(node, level=0): not show_empty and (value is None or value == []) # Special cases: - # `Constant(value=None)` and `MatchSingleton(value=None)` - and not isinstance(node, (Constant, MatchSingleton)) + # `Constant(value=None)` and `MatchSingleton(value=None) and `MatchStar(name=None)` + and not isinstance(node, (Constant, MatchSingleton, MatchStar)) ): args_buffer.append(repr(value)) continue diff --git a/Misc/NEWS.d/next/Library/2025-05-25-20-30-00.gh-issue-134674.MatchS.rst b/Misc/NEWS.d/next/Library/2025-05-25-20-30-00.gh-issue-134674.MatchS.rst new file mode 100644 index 00000000000000..f9a9560f3c0d61 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-25-20-30-00.gh-issue-134674.MatchS.rst @@ -0,0 +1,4 @@ +Fixed :class:`ast.MatchStar` to remove incorrect class-level default value +for the ``name`` field. +Fixed :func:`ast.dump` to show the ``name`` field of +:class:`ast.MatchStar` when the value is ``None``. diff --git a/Parser/Python.asdl b/Parser/Python.asdl index 96f3914b029d4c..81fbbb471c32cb 100644 --- a/Parser/Python.asdl +++ b/Parser/Python.asdl @@ -137,7 +137,7 @@ module Python | MatchMapping(expr* keys, pattern* patterns, identifier? rest) | MatchClass(expr cls, pattern* patterns, identifier* kwd_attrs, pattern* kwd_patterns) - | MatchStar(identifier? name) + | MatchStar(identifier name) -- The optional "rest" MatchMapping parameter handles capturing extra mapping keys | MatchAs(pattern? pattern, identifier? name) diff --git a/Python/Python-ast.c b/Python/Python-ast.c index f7625ab1205bdc..935dc44ed231ef 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -4839,12 +4839,7 @@ add_ast_annotations(struct ast_state *state) if (!MatchStar_annotations) return 0; { PyObject *type = (PyObject *)&PyUnicode_Type; - type = _Py_union_type_or(type, Py_None); - cond = type != NULL; - if (!cond) { - Py_DECREF(MatchStar_annotations); - return 0; - } + Py_INCREF(type); cond = PyDict_SetItemString(MatchStar_annotations, "name", type) == 0; Py_DECREF(type); if (!cond) { @@ -6880,7 +6875,7 @@ init_types(void *arg) " | MatchSequence(pattern* patterns)\n" " | MatchMapping(expr* keys, pattern* patterns, identifier? rest)\n" " | MatchClass(expr cls, pattern* patterns, identifier* kwd_attrs, pattern* kwd_patterns)\n" - " | MatchStar(identifier? name)\n" + " | MatchStar(identifier name)\n" " | MatchAs(pattern? pattern, identifier? name)\n" " | MatchOr(pattern* patterns)"); if (!state->pattern_type) return -1; @@ -6915,10 +6910,8 @@ init_types(void *arg) if (!state->MatchClass_type) return -1; state->MatchStar_type = make_type(state, "MatchStar", state->pattern_type, MatchStar_fields, 1, - "MatchStar(identifier? name)"); + "MatchStar(identifier name)"); if (!state->MatchStar_type) return -1; - if (PyObject_SetAttr(state->MatchStar_type, state->name, Py_None) == -1) - return -1; state->MatchAs_type = make_type(state, "MatchAs", state->pattern_type, MatchAs_fields, 2, "MatchAs(pattern? pattern, identifier? name)"); @@ -8746,6 +8739,11 @@ _PyAST_MatchStar(identifier name, int lineno, int col_offset, int end_lineno, int end_col_offset, PyArena *arena) { pattern_ty p; + if (!name) { + PyErr_SetString(PyExc_ValueError, + "field 'name' is required for MatchStar"); + return NULL; + } p = (pattern_ty)_PyArena_Malloc(arena, sizeof(*p)); if (!p) return NULL; @@ -17530,9 +17528,9 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (PyObject_GetOptionalAttr(obj, state->name, &tmp) < 0) { return -1; } - if (tmp == NULL || tmp == Py_None) { - Py_CLEAR(tmp); - name = NULL; + if (tmp == NULL) { + PyErr_SetString(PyExc_TypeError, "required field \"name\" missing from MatchStar"); + return -1; } else { int res; From 5679ebdb32a99de4de9b096c7d85b5bf5d713e0e Mon Sep 17 00:00:00 2001 From: Hunter Hogan Date: Sun, 25 May 2025 23:03:48 -0500 Subject: [PATCH 2/3] Fix MatchStar instantiation to accept None in pattern matching tests --- Doc/library/ast.rst | 2 +- Lib/test/test_ast/test_ast.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index ca9a6b0712c9a2..6dada88218b777 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -1570,7 +1570,7 @@ Pattern matching match_case( pattern=MatchSequence( patterns=[ - MatchStar()]), + MatchStar(None)]), body=[ Expr( value=Constant(value=Ellipsis))])])]) diff --git a/Lib/test/test_ast/test_ast.py b/Lib/test/test_ast/test_ast.py index 46745cfa8f8325..26a09d451148ff 100644 --- a/Lib/test/test_ast/test_ast.py +++ b/Lib/test/test_ast/test_ast.py @@ -2398,7 +2398,7 @@ def test_stdlib_validates(self): ), ast.MatchClass( name_carter, - patterns=[ast.MatchStar()], + patterns=[ast.MatchStar(None)], kwd_attrs=[], kwd_patterns=[] ), @@ -2406,7 +2406,7 @@ def test_stdlib_validates(self): name_carter, patterns=[], kwd_attrs=[], - kwd_patterns=[ast.MatchStar()] + kwd_patterns=[ast.MatchStar(None)] ), ast.MatchClass( constant_true, # invalid name From a5253008ef489f462cd659037b2668c848e5b075 Mon Sep 17 00:00:00 2001 From: Hunter Hogan Date: Mon, 26 May 2025 01:05:04 -0500 Subject: [PATCH 3/3] fix: typo in comment --- Lib/ast.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/ast.py b/Lib/ast.py index 45783cbd04870a..00f6f326395efa 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -151,7 +151,7 @@ def _format(node, level=0): not show_empty and (value is None or value == []) # Special cases: - # `Constant(value=None)` and `MatchSingleton(value=None) and `MatchStar(name=None)` + # `Constant(value=None)` and `MatchSingleton(value=None)` and `MatchStar(name=None)` and not isinstance(node, (Constant, MatchSingleton, MatchStar)) ): args_buffer.append(repr(value))