From 83d76cd74a410824220adcf56f4b559b84570445 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 9 May 2024 08:46:35 -0700 Subject: [PATCH 1/3] gh-118851: Default ctx arguments to AST constructors to Load() --- Doc/library/ast.rst | 6 +++--- Doc/whatsnew/3.13.rst | 8 +++++--- Lib/test/test_ast.py | 17 +++++++++++++++++ ...24-05-09-08-46-12.gh-issue-118851.aPAoJw.rst | 2 ++ Parser/asdl_c.py | 11 +++++++++-- Python/Python-ast.c | 11 +++++++++-- 6 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-05-09-08-46-12.gh-issue-118851.aPAoJw.rst diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index 24c56f17ebb002..594e79091694e3 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -120,7 +120,8 @@ Node classes If a field that is optional in the grammar is omitted from the constructor, it defaults to ``None``. If a list field is omitted, it defaults to the empty - list. If any other field is omitted, a :exc:`DeprecationWarning` is raised + list. If a field of type :class:`!ast.expr_context` is omitted, it defaults to + :class:`Load() `. If any other field is omitted, a :exc:`DeprecationWarning` is raised and the AST node will not have this field. In Python 3.15, this condition will raise an error. @@ -596,8 +597,7 @@ Expressions * ``keywords`` holds a list of :class:`.keyword` objects representing arguments passed by keyword. - When creating a ``Call`` node, ``args`` and ``keywords`` are required, but - they can be empty lists. + The ``args`` and ``keywords`` arguments are optional and default to empty lists. .. doctest:: diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 44555718184e19..9dab458b210093 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -521,8 +521,10 @@ ast If an optional field on an AST node is not included as an argument when constructing an instance, the field will now be set to ``None``. Similarly, - if a list field is omitted, that field will now be set to an empty list. - (Previously, in both cases, the attribute would be missing on the newly + if a list field is omitted, that field will now be set to an empty list, + and if a :class:`!ast.expr_context` field is omitted, it defaults to + :class:`Load() `. + (Previously, in all cases, the attribute would be missing on the newly constructed AST node instance.) If other arguments are omitted, a :exc:`DeprecationWarning` is emitted. @@ -534,7 +536,7 @@ ast unless the class opts in to the new behavior by setting the attribute :attr:`ast.AST._field_types`. - (Contributed by Jelle Zijlstra in :gh:`105858` and :gh:`117486`.) + (Contributed by Jelle Zijlstra in :gh:`105858`, :gh:`117486`, and :gh:`118851`.) * :func:`ast.parse` now accepts an optional argument *optimize* which is passed on to the :func:`compile` built-in. This makes it diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index f6e22d44406d9e..e665abad9b9e85 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -3036,6 +3036,23 @@ def test_FunctionDef(self): self.assertEqual(node.name, 'foo') self.assertEqual(node.decorator_list, []) + def test_expr_context(self): + name = ast.Name("x") + self.assertEqual(name.id, "x") + self.assertEqual(name.ctx, ast.Load()) + + name2 = ast.Name("x", ast.Store()) + self.assertEqual(name2.id, "x") + self.assertEqual(name2.ctx, ast.Store()) + + name3 = ast.Name("x", ctx=ast.Del()) + self.assertEqual(name3.id, "x") + self.assertEqual(name3.ctx, ast.Del()) + + with self.assertWarnsRegex(DeprecationWarning, + r"Name\.__init__ missing 1 required positional argument: 'id'"): + name3 = ast.Name() + def test_custom_subclass_with_no_fields(self): class NoInit(ast.AST): pass diff --git a/Misc/NEWS.d/next/Library/2024-05-09-08-46-12.gh-issue-118851.aPAoJw.rst b/Misc/NEWS.d/next/Library/2024-05-09-08-46-12.gh-issue-118851.aPAoJw.rst new file mode 100644 index 00000000000000..d036d0cda617ef --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-05-09-08-46-12.gh-issue-118851.aPAoJw.rst @@ -0,0 +1,2 @@ +``ctx`` arguments to the constructors of :mod:`ast` node classes now default +to :class:`ast.Load() `. Patch by Jelle Zijlstra. diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index 11d59faeb0d42c..cd70b8784ebb5c 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -1022,13 +1022,20 @@ def visitModule(self, mod): goto set_remaining_cleanup; } } + else if (type == state->expr_context_type) { + // special case for expr_context: default to Load() + res = PyObject_SetAttr(self, name, state->Load_singleton); + if (res < 0) { + goto set_remaining_cleanup; + } + } else { // simple field (e.g., identifier) if (PyErr_WarnFormat( PyExc_DeprecationWarning, 1, "%.400s.__init__ missing 1 required positional argument: '%U'. " - "This will become an error in Python 3.15.", - Py_TYPE(self)->tp_name, name + "This will become an error in Python 3.15. %R", + Py_TYPE(self)->tp_name, name, type ) < 0) { goto set_remaining_cleanup; } diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 4956d04f719de9..d2e5fcc20e65d6 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -5221,13 +5221,20 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) goto set_remaining_cleanup; } } + else if (type == state->expr_context_type) { + // special case for expr_context: default to Load() + res = PyObject_SetAttr(self, name, state->Load_singleton); + if (res < 0) { + goto set_remaining_cleanup; + } + } else { // simple field (e.g., identifier) if (PyErr_WarnFormat( PyExc_DeprecationWarning, 1, "%.400s.__init__ missing 1 required positional argument: '%U'. " - "This will become an error in Python 3.15.", - Py_TYPE(self)->tp_name, name + "This will become an error in Python 3.15. %R", + Py_TYPE(self)->tp_name, name, type ) < 0) { goto set_remaining_cleanup; } From 03898833c502e1ff2b8db20ae8c9c326e062c396 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 9 May 2024 08:48:43 -0700 Subject: [PATCH 2/3] tweaks --- Lib/test/test_ast.py | 6 +++--- Parser/asdl_c.py | 4 ++-- Python/Python-ast.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index e665abad9b9e85..5422c861ffb5c0 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -3039,15 +3039,15 @@ def test_FunctionDef(self): def test_expr_context(self): name = ast.Name("x") self.assertEqual(name.id, "x") - self.assertEqual(name.ctx, ast.Load()) + self.assertIsInstance(name.ctx, ast.Load) name2 = ast.Name("x", ast.Store()) self.assertEqual(name2.id, "x") - self.assertEqual(name2.ctx, ast.Store()) + self.assertIsInstance(name2.ctx, ast.Store) name3 = ast.Name("x", ctx=ast.Del()) self.assertEqual(name3.id, "x") - self.assertEqual(name3.ctx, ast.Del()) + self.assertIsInstance(name3.ctx, ast.Del) with self.assertWarnsRegex(DeprecationWarning, r"Name\.__init__ missing 1 required positional argument: 'id'"): diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index cd70b8784ebb5c..9961d23629abc5 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -1034,8 +1034,8 @@ def visitModule(self, mod): if (PyErr_WarnFormat( PyExc_DeprecationWarning, 1, "%.400s.__init__ missing 1 required positional argument: '%U'. " - "This will become an error in Python 3.15. %R", - Py_TYPE(self)->tp_name, name, type + "This will become an error in Python 3.15.", + Py_TYPE(self)->tp_name, name ) < 0) { goto set_remaining_cleanup; } diff --git a/Python/Python-ast.c b/Python/Python-ast.c index d2e5fcc20e65d6..7aa1c5119d8f28 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -5233,8 +5233,8 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) if (PyErr_WarnFormat( PyExc_DeprecationWarning, 1, "%.400s.__init__ missing 1 required positional argument: '%U'. " - "This will become an error in Python 3.15. %R", - Py_TYPE(self)->tp_name, name, type + "This will become an error in Python 3.15.", + Py_TYPE(self)->tp_name, name ) < 0) { goto set_remaining_cleanup; } From b4d31e6d9969eecb04c3bdbdbc00440964aac0dd Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 9 May 2024 09:04:27 -0700 Subject: [PATCH 3/3] Update Doc/library/ast.rst Co-authored-by: Alex Waygood --- Doc/library/ast.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index 594e79091694e3..d4ccf282a5d00a 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -121,7 +121,7 @@ Node classes If a field that is optional in the grammar is omitted from the constructor, it defaults to ``None``. If a list field is omitted, it defaults to the empty list. If a field of type :class:`!ast.expr_context` is omitted, it defaults to - :class:`Load() `. If any other field is omitted, a :exc:`DeprecationWarning` is raised + :class:`Load() `. If any other field is omitted, a :exc:`DeprecationWarning` is raised and the AST node will not have this field. In Python 3.15, this condition will raise an error.