diff options
author | Jelle Zijlstra <jelle.zijlstra@gmail.com> | 2024-06-25 15:39:29 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-25 15:39:29 (GMT) |
commit | 899dfbaf0e62c71885eced4747d7d4c93b7bbd4f (patch) | |
tree | f8e401ba3ae816d4396cf12844a0b31faa3d6ce1 /Parser/asdl_c.py | |
parent | 1764a310e8bdd7dc0c59308bd7190a0f5aa8ff40 (diff) | |
download | cpython-899dfbaf0e62c71885eced4747d7d4c93b7bbd4f.zip cpython-899dfbaf0e62c71885eced4747d7d4c93b7bbd4f.tar.gz cpython-899dfbaf0e62c71885eced4747d7d4c93b7bbd4f.tar.bz2 |
[3.13] gh-120108: Fix deepcopying of AST trees with .parent attributes (GH-120114) (#121000)
(cherry picked from commit 42b2c9d78da7ebd6bd5925a4d4c78aec3c9e78e6)
Diffstat (limited to 'Parser/asdl_c.py')
-rwxr-xr-x | Parser/asdl_c.py | 31 |
1 files changed, 14 insertions, 17 deletions
diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index 9961d23..e338656 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -1064,17 +1064,22 @@ ast_type_reduce(PyObject *self, PyObject *unused) return NULL; } - PyObject *dict = NULL, *fields = NULL, *remaining_fields = NULL, - *remaining_dict = NULL, *positional_args = NULL; + PyObject *dict = NULL, *fields = NULL, *positional_args = NULL; if (PyObject_GetOptionalAttr(self, state->__dict__, &dict) < 0) { return NULL; } PyObject *result = NULL; if (dict) { - // Serialize the fields as positional args if possible, because if we - // serialize them as a dict, during unpickling they are set only *after* - // the object is constructed, which will now trigger a DeprecationWarning - // if the AST type has required fields. + // Unpickling (or copying) works as follows: + // - Construct the object with only positional arguments + // - Set the fields from the dict + // We have two constraints: + // - We must set all the required fields in the initial constructor call, + // or the unpickling or deepcopying of the object will trigger DeprecationWarnings. + // - We must not include child nodes in the positional args, because + // that may trigger runaway recursion during copying (gh-120108). + // To satisfy both constraints, we set all the fields to None in the + // initial list of positional args, and then set the fields from the dict. if (PyObject_GetOptionalAttr((PyObject*)Py_TYPE(self), state->_fields, &fields) < 0) { goto cleanup; } @@ -1084,11 +1089,6 @@ ast_type_reduce(PyObject *self, PyObject *unused) Py_DECREF(dict); goto cleanup; } - remaining_dict = PyDict_Copy(dict); - Py_DECREF(dict); - if (!remaining_dict) { - goto cleanup; - } positional_args = PyList_New(0); if (!positional_args) { goto cleanup; @@ -1099,7 +1099,7 @@ ast_type_reduce(PyObject *self, PyObject *unused) goto cleanup; } PyObject *value; - int rc = PyDict_Pop(remaining_dict, name, &value); + int rc = PyDict_GetItemRef(dict, name, &value); Py_DECREF(name); if (rc < 0) { goto cleanup; @@ -1107,7 +1107,7 @@ ast_type_reduce(PyObject *self, PyObject *unused) if (!value) { break; } - rc = PyList_Append(positional_args, value); + rc = PyList_Append(positional_args, Py_None); Py_DECREF(value); if (rc < 0) { goto cleanup; @@ -1117,8 +1117,7 @@ ast_type_reduce(PyObject *self, PyObject *unused) if (!args_tuple) { goto cleanup; } - result = Py_BuildValue("ONO", Py_TYPE(self), args_tuple, - remaining_dict); + result = Py_BuildValue("ONN", Py_TYPE(self), args_tuple, dict); } else { result = Py_BuildValue("O()N", Py_TYPE(self), dict); @@ -1129,8 +1128,6 @@ ast_type_reduce(PyObject *self, PyObject *unused) } cleanup: Py_XDECREF(fields); - Py_XDECREF(remaining_fields); - Py_XDECREF(remaining_dict); Py_XDECREF(positional_args); return result; } |