diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 355990ed58ee09..02c4ca8b1ba5f6 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -1124,6 +1124,43 @@ def test_bug_46724(self): # Test that negative operargs are handled properly self.do_disassembly_test(bug46724, dis_bug46724) + def test_annotate_source_locations(self): + # See gh-135700 + issue_135700 = "1\nx: int" + issue_135700_class = "class A:\n 1\n x: int" + + test_cases = [ + ("module", compile(issue_135700, "", "exec").co_consts[1]), + ( + "class", + compile(ast.parse(issue_135700_class), "?", "exec") + .co_consts[0] + .co_consts[1], + ), + ] + + for case_name, annotate_code in test_cases: + with self.subTest(case=case_name): + line_starts_iterator = dis.findlinestarts(annotate_code) + valid_line_starts = [ + item[0] + for item in line_starts_iterator + if item[1] is not None + ] # The first item is not RESUME in class case + setup_scope_begin = valid_line_starts[0] + setup_scope_end = valid_line_starts[1] + setup_annotations_scope_positions = { + instr.positions + for instr in dis.get_instructions(annotate_code) + if setup_scope_begin <= instr.offset < setup_scope_end + and instr.positions + } + self.assertEqual( + len(setup_annotations_scope_positions), + 1, + f"{case_name}: Expected uniform positions, found {len(setup_annotations_scope_positions)}: {setup_annotations_scope_positions}", + ) + def test_kw_names(self): # Test that value is displayed for keyword argument names: self.do_disassembly_test(wrap_func_w_kwargs, dis_kw_names) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-22-10-47-27.gh-issue-135700.0qdtCl.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-22-10-47-27.gh-issue-135700.0qdtCl.rst new file mode 100644 index 00000000000000..dec0f3291c5125 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-22-10-47-27.gh-issue-135700.0qdtCl.rst @@ -0,0 +1 @@ +Fix instructions positions in :attr:`~object.__annotate__`. diff --git a/Python/codegen.c b/Python/codegen.c index 0023d72cd5e91d..a8744a533e63c7 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -685,13 +685,14 @@ codegen_setup_annotations_scope(compiler *c, location loc, PyObject *value_with_fake_globals = PyLong_FromLong(_Py_ANNOTATE_FORMAT_VALUE_WITH_FAKE_GLOBALS); assert(!SYMTABLE_ENTRY(c)->ste_has_docstring); _Py_DECLARE_STR(format, ".format"); - ADDOP_I(c, loc, LOAD_FAST, 0); - ADDOP_LOAD_CONST(c, loc, value_with_fake_globals); - ADDOP_I(c, loc, COMPARE_OP, (Py_GT << 5) | compare_masks[Py_GT]); + + ADDOP_I(c, NO_LOCATION, LOAD_FAST, 0); + ADDOP_LOAD_CONST(c, NO_LOCATION, value_with_fake_globals); + ADDOP_I(c, NO_LOCATION, COMPARE_OP, (Py_GT << 5) | compare_masks[Py_GT]); NEW_JUMP_TARGET_LABEL(c, body); - ADDOP_JUMP(c, loc, POP_JUMP_IF_FALSE, body); - ADDOP_I(c, loc, LOAD_COMMON_CONSTANT, CONSTANT_NOTIMPLEMENTEDERROR); - ADDOP_I(c, loc, RAISE_VARARGS, 1); + ADDOP_JUMP(c, NO_LOCATION, POP_JUMP_IF_FALSE, body); + ADDOP_I(c, NO_LOCATION, LOAD_COMMON_CONSTANT, CONSTANT_NOTIMPLEMENTEDERROR); + ADDOP_I(c, NO_LOCATION, RAISE_VARARGS, 1); USE_LABEL(c, body); return SUCCESS; } @@ -750,7 +751,7 @@ codegen_deferred_annotations_body(compiler *c, location loc, assert(PyList_CheckExact(conditional_annotation_indices)); assert(annotations_len == PyList_Size(conditional_annotation_indices)); - ADDOP_I(c, loc, BUILD_MAP, 0); // stack now contains + ADDOP_I(c, NO_LOCATION, BUILD_MAP, 0); // stack now contains for (Py_ssize_t i = 0; i < annotations_len; i++) { PyObject *ptr = PyList_GET_ITEM(deferred_anno, i);