diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index ef6c62dca1e124..1018e515724244 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -1079,6 +1079,11 @@ Imports alias(name='z')], level=0)]) + .. versionchanged:: next + + Previously, the ``level`` field was marked as optional in the interface + definition, although the CPython parser always sets this field to an integer. + .. class:: alias(name, asname) diff --git a/Misc/NEWS.d/next/Library/2025-06-25-18-58-12.gh-issue-135959.ZvUHYw.rst b/Misc/NEWS.d/next/Library/2025-06-25-18-58-12.gh-issue-135959.ZvUHYw.rst new file mode 100644 index 00000000000000..4a8524e772131d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-06-25-18-58-12.gh-issue-135959.ZvUHYw.rst @@ -0,0 +1,2 @@ +The ``level`` field on :class:`ast.ImportFrom` is now marked as required. +The parser always sets it to an integer. diff --git a/Parser/Python.asdl b/Parser/Python.asdl index 96f3914b029d4c..8c279882b68c5e 100644 --- a/Parser/Python.asdl +++ b/Parser/Python.asdl @@ -46,7 +46,7 @@ module Python | Assert(expr test, expr? msg) | Import(alias* names) - | ImportFrom(identifier? module, alias* names, int? level) + | ImportFrom(identifier? module, alias* names, int level) | Global(identifier* names) | Nonlocal(identifier* names) diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 660bc598a4862c..ae6fb4c7004cd3 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -2302,12 +2302,7 @@ add_ast_annotations(struct ast_state *state) } { PyObject *type = (PyObject *)&PyLong_Type; - type = _Py_union_type_or(type, Py_None); - cond = type != NULL; - if (!cond) { - Py_DECREF(ImportFrom_annotations); - return 0; - } + Py_INCREF(type); cond = PyDict_SetItemString(ImportFrom_annotations, "level", type) == 0; Py_DECREF(type); if (!cond) { @@ -6219,7 +6214,7 @@ init_types(void *arg) " | TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)\n" " | Assert(expr test, expr? msg)\n" " | Import(alias* names)\n" - " | ImportFrom(identifier? module, alias* names, int? level)\n" + " | ImportFrom(identifier? module, alias* names, int level)\n" " | Global(identifier* names)\n" " | Nonlocal(identifier* names)\n" " | Expr(expr value)\n" @@ -6353,12 +6348,10 @@ init_types(void *arg) if (!state->Import_type) return -1; state->ImportFrom_type = make_type(state, "ImportFrom", state->stmt_type, ImportFrom_fields, 3, - "ImportFrom(identifier? module, alias* names, int? level)"); + "ImportFrom(identifier? module, alias* names, int level)"); if (!state->ImportFrom_type) return -1; if (PyObject_SetAttr(state->ImportFrom_type, state->module, Py_None) == -1) return -1; - if (PyObject_SetAttr(state->ImportFrom_type, state->level, Py_None) == -1) - return -1; state->Global_type = make_type(state, "Global", state->stmt_type, Global_fields, 1, "Global(identifier* names)"); @@ -13593,9 +13586,9 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (PyObject_GetOptionalAttr(obj, state->level, &tmp) < 0) { return -1; } - if (tmp == NULL || tmp == Py_None) { - Py_CLEAR(tmp); - level = 0; + if (tmp == NULL) { + PyErr_SetString(PyExc_TypeError, "required field \"level\" missing from ImportFrom"); + return -1; } else { int res;