gh-118851: Default ctx arguments to AST constructors to Load() (#118854)

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
Jelle Zijlstra 2024-05-09 15:30:14 -07:00 committed by GitHub
parent 98ff3f65c0
commit 68fbc00dc8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 41 additions and 6 deletions

View File

@ -120,7 +120,8 @@ Node classes
If a field that is optional in the grammar is omitted from the constructor, 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 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() <ast.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 and the AST node will not have this field. In Python 3.15, this condition will
raise an error. raise an error.
@ -596,8 +597,7 @@ Expressions
* ``keywords`` holds a list of :class:`.keyword` objects representing * ``keywords`` holds a list of :class:`.keyword` objects representing
arguments passed by keyword. arguments passed by keyword.
When creating a ``Call`` node, ``args`` and ``keywords`` are required, but The ``args`` and ``keywords`` arguments are optional and default to empty lists.
they can be empty lists.
.. doctest:: .. doctest::

View File

@ -521,8 +521,10 @@ ast
If an optional field on an AST node is not included as an argument when 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, 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. 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 and if a :class:`!ast.expr_context` field is omitted, it defaults to
:class:`Load() <ast.Load>`.
(Previously, in all cases, the attribute would be missing on the newly
constructed AST node instance.) constructed AST node instance.)
If other arguments are omitted, a :exc:`DeprecationWarning` is emitted. 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 unless the class opts in to the new behavior by setting the attribute
:attr:`ast.AST._field_types`. :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* * :func:`ast.parse` now accepts an optional argument *optimize*
which is passed on to the :func:`compile` built-in. This makes it which is passed on to the :func:`compile` built-in. This makes it

View File

@ -3036,6 +3036,23 @@ class ASTConstructorTests(unittest.TestCase):
self.assertEqual(node.name, 'foo') self.assertEqual(node.name, 'foo')
self.assertEqual(node.decorator_list, []) self.assertEqual(node.decorator_list, [])
def test_expr_context(self):
name = ast.Name("x")
self.assertEqual(name.id, "x")
self.assertIsInstance(name.ctx, ast.Load)
name2 = ast.Name("x", ast.Store())
self.assertEqual(name2.id, "x")
self.assertIsInstance(name2.ctx, ast.Store)
name3 = ast.Name("x", ctx=ast.Del())
self.assertEqual(name3.id, "x")
self.assertIsInstance(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): def test_custom_subclass_with_no_fields(self):
class NoInit(ast.AST): class NoInit(ast.AST):
pass pass

View File

@ -0,0 +1,2 @@
``ctx`` arguments to the constructors of :mod:`ast` node classes now default
to :class:`ast.Load() <ast.Load>`. Patch by Jelle Zijlstra.

View File

@ -1022,6 +1022,13 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw)
goto set_remaining_cleanup; 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 { else {
// simple field (e.g., identifier) // simple field (e.g., identifier)
if (PyErr_WarnFormat( if (PyErr_WarnFormat(

7
Python/Python-ast.c generated
View File

@ -5221,6 +5221,13 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw)
goto set_remaining_cleanup; 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 { else {
// simple field (e.g., identifier) // simple field (e.g., identifier)
if (PyErr_WarnFormat( if (PyErr_WarnFormat(