diff --git a/Include/internal/pycore_ast_state.h b/Include/internal/pycore_ast_state.h index d4ac419f51d6b2..6ea74c6d2fbd4f 100644 --- a/Include/internal/pycore_ast_state.h +++ b/Include/internal/pycore_ast_state.h @@ -160,6 +160,7 @@ struct ast_state { PyObject *__match_args__; PyObject *__module__; PyObject *_attributes; + PyObject *_field_types; PyObject *_fields; PyObject *alias_type; PyObject *annotation; diff --git a/Misc/NEWS.d/next/Library/2025-06-21-16-28-06.gh-issue-135797.HXKtbB.rst b/Misc/NEWS.d/next/Library/2025-06-21-16-28-06.gh-issue-135797.HXKtbB.rst new file mode 100644 index 00000000000000..43067f79c49d02 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-06-21-16-28-06.gh-issue-135797.HXKtbB.rst @@ -0,0 +1,2 @@ +Add the _field_types attribute to the base AST class and its subclasses. The +default value is an empty dict. Patch by Hunter Hogan. diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index dba20226c3283a..fef45f00104bc0 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -1734,6 +1734,17 @@ def visitModule(self, mod): state->__module__, state->ast, state->__doc__, doc); + if (result) { + PyObject *empty_dict = PyDict_New(); + if (!empty_dict || + PyObject_SetAttr(result, state->_field_types, empty_dict) < 0) { + Py_XDECREF(empty_dict); + Py_DECREF(result); + Py_DECREF(fnames); + return NULL; + } + Py_DECREF(empty_dict); + } Py_DECREF(fnames); return result; } @@ -1860,16 +1871,20 @@ def visitModule(self, mod): static int add_ast_fields(struct ast_state *state) { - PyObject *empty_tuple; + PyObject *empty_tuple, *empty_dict; empty_tuple = PyTuple_New(0); - if (!empty_tuple || + empty_dict = PyDict_New(); + if (!empty_tuple || !empty_dict || PyObject_SetAttrString(state->AST_type, "_fields", empty_tuple) < 0 || + PyObject_SetAttrString(state->AST_type, "_field_types", empty_dict) < 0 || PyObject_SetAttrString(state->AST_type, "__match_args__", empty_tuple) < 0 || PyObject_SetAttrString(state->AST_type, "_attributes", empty_tuple) < 0) { Py_XDECREF(empty_tuple); + Py_XDECREF(empty_dict); return -1; } Py_DECREF(empty_tuple); + Py_DECREF(empty_dict); return 0; } @@ -2272,6 +2287,7 @@ def generate_module_def(mod, metadata, f, internal_h): state_strings = { "ast", "_fields", + "_field_types", "__match_args__", "__doc__", "__dict__", diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 660bc598a4862c..55b9378d8722e0 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -177,6 +177,7 @@ void _PyAST_Fini(PyInterpreterState *interp) Py_CLEAR(state->__match_args__); Py_CLEAR(state->__module__); Py_CLEAR(state->_attributes); + Py_CLEAR(state->_field_types); Py_CLEAR(state->_fields); Py_CLEAR(state->alias_type); Py_CLEAR(state->annotation); @@ -291,6 +292,7 @@ static int init_identifiers(struct ast_state *state) if ((state->__match_args__ = PyUnicode_InternFromString("__match_args__")) == NULL) return -1; if ((state->__module__ = PyUnicode_InternFromString("__module__")) == NULL) return -1; if ((state->_attributes = PyUnicode_InternFromString("_attributes")) == NULL) return -1; + if ((state->_field_types = PyUnicode_InternFromString("_field_types")) == NULL) return -1; if ((state->_fields = PyUnicode_InternFromString("_fields")) == NULL) return -1; if ((state->annotation = PyUnicode_InternFromString("annotation")) == NULL) return -1; if ((state->arg = PyUnicode_InternFromString("arg")) == NULL) return -1; @@ -6018,6 +6020,17 @@ make_type(struct ast_state *state, const char *type, PyObject* base, state->__module__, state->ast, state->__doc__, doc); + if (result) { + PyObject *empty_dict = PyDict_New(); + if (!empty_dict || + PyObject_SetAttr(result, state->_field_types, empty_dict) < 0) { + Py_XDECREF(empty_dict); + Py_DECREF(result); + Py_DECREF(fnames); + return NULL; + } + Py_DECREF(empty_dict); + } Py_DECREF(fnames); return result; } @@ -6144,16 +6157,20 @@ static int obj2ast_int(struct ast_state* Py_UNUSED(state), PyObject* obj, int* o static int add_ast_fields(struct ast_state *state) { - PyObject *empty_tuple; + PyObject *empty_tuple, *empty_dict; empty_tuple = PyTuple_New(0); - if (!empty_tuple || + empty_dict = PyDict_New(); + if (!empty_tuple || !empty_dict || PyObject_SetAttrString(state->AST_type, "_fields", empty_tuple) < 0 || + PyObject_SetAttrString(state->AST_type, "_field_types", empty_dict) < 0 || PyObject_SetAttrString(state->AST_type, "__match_args__", empty_tuple) < 0 || PyObject_SetAttrString(state->AST_type, "_attributes", empty_tuple) < 0) { Py_XDECREF(empty_tuple); + Py_XDECREF(empty_dict); return -1; } Py_DECREF(empty_tuple); + Py_DECREF(empty_dict); return 0; }