gh-116021: Deprecate support for instantiating abstract AST nodes (#137865)

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
Brian Schubert 2026-05-02 12:50:06 -04:00 committed by GitHub
parent b1d6231736
commit bdedc4a20e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 173 additions and 14 deletions

61
Python/Python-ast.c generated
View file

@ -178,6 +178,7 @@ void _PyAST_Fini(PyInterpreterState *interp)
Py_CLEAR(state->__module__);
Py_CLEAR(state->_attributes);
Py_CLEAR(state->_fields);
Py_CLEAR(state->abstract_types);
Py_CLEAR(state->alias_type);
Py_CLEAR(state->annotation);
Py_CLEAR(state->arg);
@ -5269,6 +5270,19 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw)
return -1;
}
int contains = PySet_Contains(state->abstract_types, (PyObject *)Py_TYPE(self));
if (contains == -1) {
return -1;
}
else if (contains == 1) {
if (PyErr_WarnFormat(
PyExc_DeprecationWarning, 1,
"Instantiating abstract AST node class %T is deprecated. "
"This will become an error in Python 3.20", self) < 0) {
return -1;
}
}
Py_ssize_t i, numfields = 0;
int res = -1;
PyObject *key, *value, *fields, *attributes = NULL, *remaining_fields = NULL;
@ -6100,6 +6114,13 @@ init_types(void *arg)
if (!state->AST_type) {
return -1;
}
state->abstract_types = PySet_New(NULL);
if (!state->abstract_types) {
return -1;
}
if (PySet_Add(state->abstract_types, state->AST_type) < 0) {
return -1;
}
if (add_ast_fields(state) < 0) {
return -1;
}
@ -6110,6 +6131,7 @@ init_types(void *arg)
" | FunctionType(expr* argtypes, expr returns)");
if (!state->mod_type) return -1;
if (add_attributes(state, state->mod_type, NULL, 0) < 0) return -1;
if (PySet_Add(state->abstract_types, state->mod_type) < 0) return -1;
state->Module_type = make_type(state, "Module", state->mod_type,
Module_fields, 2,
"Module(stmt* body, type_ignore* type_ignores)");
@ -6159,6 +6181,7 @@ init_types(void *arg)
if (!state->stmt_type) return -1;
if (add_attributes(state, state->stmt_type, stmt_attributes, 4) < 0) return
-1;
if (PySet_Add(state->abstract_types, state->stmt_type) < 0) return -1;
if (PyObject_SetAttr(state->stmt_type, state->end_lineno, Py_None) == -1)
return -1;
if (PyObject_SetAttr(state->stmt_type, state->end_col_offset, Py_None) ==
@ -6348,6 +6371,7 @@ init_types(void *arg)
if (!state->expr_type) return -1;
if (add_attributes(state, state->expr_type, expr_attributes, 4) < 0) return
-1;
if (PySet_Add(state->abstract_types, state->expr_type) < 0) return -1;
if (PyObject_SetAttr(state->expr_type, state->end_lineno, Py_None) == -1)
return -1;
if (PyObject_SetAttr(state->expr_type, state->end_col_offset, Py_None) ==
@ -6494,6 +6518,8 @@ init_types(void *arg)
"expr_context = Load | Store | Del");
if (!state->expr_context_type) return -1;
if (add_attributes(state, state->expr_context_type, NULL, 0) < 0) return -1;
if (PySet_Add(state->abstract_types, state->expr_context_type) < 0) return
-1;
state->Load_type = make_type(state, "Load", state->expr_context_type, NULL,
0,
"Load");
@ -6518,6 +6544,7 @@ init_types(void *arg)
"boolop = And | Or");
if (!state->boolop_type) return -1;
if (add_attributes(state, state->boolop_type, NULL, 0) < 0) return -1;
if (PySet_Add(state->abstract_types, state->boolop_type) < 0) return -1;
state->And_type = make_type(state, "And", state->boolop_type, NULL, 0,
"And");
if (!state->And_type) return -1;
@ -6535,6 +6562,7 @@ init_types(void *arg)
"operator = Add | Sub | Mult | MatMult | Div | Mod | Pow | LShift | RShift | BitOr | BitXor | BitAnd | FloorDiv");
if (!state->operator_type) return -1;
if (add_attributes(state, state->operator_type, NULL, 0) < 0) return -1;
if (PySet_Add(state->abstract_types, state->operator_type) < 0) return -1;
state->Add_type = make_type(state, "Add", state->operator_type, NULL, 0,
"Add");
if (!state->Add_type) return -1;
@ -6629,6 +6657,7 @@ init_types(void *arg)
"unaryop = Invert | Not | UAdd | USub");
if (!state->unaryop_type) return -1;
if (add_attributes(state, state->unaryop_type, NULL, 0) < 0) return -1;
if (PySet_Add(state->abstract_types, state->unaryop_type) < 0) return -1;
state->Invert_type = make_type(state, "Invert", state->unaryop_type, NULL,
0,
"Invert");
@ -6659,6 +6688,7 @@ init_types(void *arg)
"cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn");
if (!state->cmpop_type) return -1;
if (add_attributes(state, state->cmpop_type, NULL, 0) < 0) return -1;
if (PySet_Add(state->abstract_types, state->cmpop_type) < 0) return -1;
state->Eq_type = make_type(state, "Eq", state->cmpop_type, NULL, 0,
"Eq");
if (!state->Eq_type) return -1;
@ -6732,6 +6762,8 @@ init_types(void *arg)
if (!state->excepthandler_type) return -1;
if (add_attributes(state, state->excepthandler_type,
excepthandler_attributes, 4) < 0) return -1;
if (PySet_Add(state->abstract_types, state->excepthandler_type) < 0) return
-1;
if (PyObject_SetAttr(state->excepthandler_type, state->end_lineno, Py_None)
== -1)
return -1;
@ -6822,6 +6854,7 @@ init_types(void *arg)
if (!state->pattern_type) return -1;
if (add_attributes(state, state->pattern_type, pattern_attributes, 4) < 0)
return -1;
if (PySet_Add(state->abstract_types, state->pattern_type) < 0) return -1;
state->MatchValue_type = make_type(state, "MatchValue",
state->pattern_type, MatchValue_fields,
1,
@ -6872,6 +6905,8 @@ init_types(void *arg)
"type_ignore = TypeIgnore(int lineno, string tag)");
if (!state->type_ignore_type) return -1;
if (add_attributes(state, state->type_ignore_type, NULL, 0) < 0) return -1;
if (PySet_Add(state->abstract_types, state->type_ignore_type) < 0) return
-1;
state->TypeIgnore_type = make_type(state, "TypeIgnore",
state->type_ignore_type,
TypeIgnore_fields, 2,
@ -6885,6 +6920,7 @@ init_types(void *arg)
if (!state->type_param_type) return -1;
if (add_attributes(state, state->type_param_type, type_param_attributes, 4)
< 0) return -1;
if (PySet_Add(state->abstract_types, state->type_param_type) < 0) return -1;
state->TypeVar_type = make_type(state, "TypeVar", state->type_param_type,
TypeVar_fields, 3,
"TypeVar(identifier name, expr? bound, expr? default_value)");
@ -17956,6 +17992,28 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out,
}
/* Helper for checking if a node class is abstract in the tests. */
static PyObject *
ast_is_abstract(PyObject *Py_UNUSED(module), PyObject *cls) {
struct ast_state *state = get_ast_state();
if (state == NULL) {
return NULL;
}
int contains = PySet_Contains(state->abstract_types, cls);
if (contains == -1) {
return NULL;
}
else if (contains == 1) {
Py_RETURN_TRUE;
}
Py_RETURN_FALSE;
}
static struct PyMethodDef astmodule_methods[] = {
{"_is_abstract", ast_is_abstract, METH_O, NULL},
{NULL} /* Sentinel */
};
static int
astmodule_exec(PyObject *m)
{
@ -18382,7 +18440,8 @@ static struct PyModuleDef _astmodule = {
.m_name = "_ast",
// The _ast module uses a per-interpreter state (PyInterpreterState.ast)
.m_size = 0,
.m_slots = astmodule_slots,
.m_methods = astmodule_methods,
.m_slots = astmodule_slots
};
PyMODINIT_FUNC