mirror of
https://github.com/python/cpython.git
synced 2025-09-10 10:47:34 +00:00
[3.14] gh-133783: Fix __replace__ on AST nodes for optional attributes (GH-133797) (#133842)
gh-133783: Fix __replace__ on AST nodes for optional attributes (GH-133797)
(cherry picked from commit 7dddb4e667
)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
parent
13c94d0401
commit
856e5903ba
4 changed files with 64 additions and 0 deletions
|
@ -1315,6 +1315,15 @@ class CopyTests(unittest.TestCase):
|
||||||
self.assertIs(repl.id, 'y')
|
self.assertIs(repl.id, 'y')
|
||||||
self.assertIs(repl.ctx, context)
|
self.assertIs(repl.ctx, context)
|
||||||
|
|
||||||
|
def test_replace_accept_missing_field_with_default(self):
|
||||||
|
node = ast.FunctionDef(name="foo", args=ast.arguments())
|
||||||
|
self.assertIs(node.returns, None)
|
||||||
|
self.assertEqual(node.decorator_list, [])
|
||||||
|
node2 = copy.replace(node, name="bar")
|
||||||
|
self.assertEqual(node2.name, "bar")
|
||||||
|
self.assertIs(node2.returns, None)
|
||||||
|
self.assertEqual(node2.decorator_list, [])
|
||||||
|
|
||||||
def test_replace_reject_known_custom_instance_fields_commits(self):
|
def test_replace_reject_known_custom_instance_fields_commits(self):
|
||||||
node = ast.parse('x').body[0].value
|
node = ast.parse('x').body[0].value
|
||||||
node.extra = extra = object() # add instance 'extra' field
|
node.extra = extra = object() # add instance 'extra' field
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Fix bug with applying :func:`copy.replace` to :mod:`ast` objects. Attributes
|
||||||
|
that default to ``None`` were incorrectly treated as required for manually
|
||||||
|
created AST nodes.
|
|
@ -1244,6 +1244,32 @@ ast_type_replace_check(PyObject *self,
|
||||||
Py_DECREF(unused);
|
Py_DECREF(unused);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Discard fields from 'expecting' that default to None
|
||||||
|
PyObject *field_types = NULL;
|
||||||
|
if (PyObject_GetOptionalAttr((PyObject*)Py_TYPE(self),
|
||||||
|
&_Py_ID(_field_types),
|
||||||
|
&field_types) < 0)
|
||||||
|
{
|
||||||
|
Py_DECREF(expecting);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (field_types != NULL) {
|
||||||
|
Py_ssize_t pos = 0;
|
||||||
|
PyObject *field_name, *field_type;
|
||||||
|
while (PyDict_Next(field_types, &pos, &field_name, &field_type)) {
|
||||||
|
if (_PyUnion_Check(field_type)) {
|
||||||
|
// optional field
|
||||||
|
if (PySet_Discard(expecting, field_name) < 0) {
|
||||||
|
Py_DECREF(expecting);
|
||||||
|
Py_DECREF(field_types);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Py_DECREF(field_types);
|
||||||
|
}
|
||||||
|
|
||||||
// Now 'expecting' contains the fields or attributes
|
// Now 'expecting' contains the fields or attributes
|
||||||
// that would not be filled inside ast_type_replace().
|
// that would not be filled inside ast_type_replace().
|
||||||
Py_ssize_t m = PySet_GET_SIZE(expecting);
|
Py_ssize_t m = PySet_GET_SIZE(expecting);
|
||||||
|
|
26
Python/Python-ast.c
generated
26
Python/Python-ast.c
generated
|
@ -5528,6 +5528,32 @@ ast_type_replace_check(PyObject *self,
|
||||||
Py_DECREF(unused);
|
Py_DECREF(unused);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Discard fields from 'expecting' that default to None
|
||||||
|
PyObject *field_types = NULL;
|
||||||
|
if (PyObject_GetOptionalAttr((PyObject*)Py_TYPE(self),
|
||||||
|
&_Py_ID(_field_types),
|
||||||
|
&field_types) < 0)
|
||||||
|
{
|
||||||
|
Py_DECREF(expecting);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (field_types != NULL) {
|
||||||
|
Py_ssize_t pos = 0;
|
||||||
|
PyObject *field_name, *field_type;
|
||||||
|
while (PyDict_Next(field_types, &pos, &field_name, &field_type)) {
|
||||||
|
if (_PyUnion_Check(field_type)) {
|
||||||
|
// optional field
|
||||||
|
if (PySet_Discard(expecting, field_name) < 0) {
|
||||||
|
Py_DECREF(expecting);
|
||||||
|
Py_DECREF(field_types);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Py_DECREF(field_types);
|
||||||
|
}
|
||||||
|
|
||||||
// Now 'expecting' contains the fields or attributes
|
// Now 'expecting' contains the fields or attributes
|
||||||
// that would not be filled inside ast_type_replace().
|
// that would not be filled inside ast_type_replace().
|
||||||
Py_ssize_t m = PySet_GET_SIZE(expecting);
|
Py_ssize_t m = PySet_GET_SIZE(expecting);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue