Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Lib/test/test_ast/test_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -1315,6 +1315,15 @@ def test_replace_reject_missing_field(self):
self.assertIs(repl.id, 'y')
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):
node = ast.parse('x').body[0].value
node.extra = extra = object() # add instance 'extra' field
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
24 changes: 24 additions & 0 deletions Parser/asdl_c.py
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,30 @@ def visitModule(self, mod):
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
// that would not be filled inside ast_type_replace().
Py_ssize_t m = PySet_GET_SIZE(expecting);
Expand Down
24 changes: 24 additions & 0 deletions Python/Python-ast.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading