-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
gh-135700: Fix instructions in __annotate__ have incorrect code positions #135814
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
983ae79
dd3bea2
c2f0a4d
b79060e
2201864
aed06b7
d85247e
eca9248
e14cb29
f216c82
c620360
a84a62b
5b12e0b
0cc0aee
07bb428
f249c82
bf25413
7b36de1
a126c1a
aed1ed0
88222eb
c87b55b
62d59e3
f4bf6ab
e856bb9
e081415
9831101
576fe7a
1043b3e
1bf0089
c2e0003
d76582d
f49487f
772467a
412082d
cd18690
c5c3851
37b7efc
cc611f8
abb6398
2d75283
d8d537a
b7de590
e613aee
4f577c6
38ecbf9
cb689a3
6bdea9a
55e0336
5f3f5f9
8f8e6df
807d99d
11ce5c4
9987678
48e72c0
36de3ef
e51e785
d6f50a2
6d0ec7e
ce39ffc
b87d40c
1dc7ed8
91edb74
eba2d4c
551f927
5ee1779
a6a656f
9f48f74
9a3af56
22f7aed
9597a3c
dd67d75
d048a8c
ce55d2c
d06bbe0
f8c108c
820d7fc
b61ef62
a2e39c2
a353c49
3e3caf2
dfdae6e
c74b91b
f521cf1
c7b259d
d7c418b
b2d04ae
1dde428
1f39dc4
19227f3
9bfe4b5
e8d7647
5507f19
702a874
08702b9
1dc67db
764ecdc
88c9480
3e5e8c9
c73da53
4878d87
892677f
2ab5c67
ba6bfaf
3e29804
995f5ec
558779f
5038d9a
4034b20
32da324
7dcf3ae
811b391
d9021b2
7dec2ba
7dba304
804ee99
4eecde7
aac9be8
25a84a0
7ef2df6
de025c0
8685f8d
95d062f
531ed83
26b57a7
e977f81
993d5dc
4a7b37f
b3e1b1f
f0bc9e8
8c9b563
4a7eca3
55a5b18
612979d
b532bf7
7f1e57e
4d4bec5
94d304f
d998fd4
f54a660
90c565f
1cd93eb
7da498b
516d1ee
47ce0d8
3243ba1
d89e6f6
b1f5f61
68e3eac
9a21a39
25ca248
7736567
d2730af
a058841
cc60c3f
94ed50e
edc395a
6d8ec54
4905390
5c1b4be
984837d
93b9ae0
8f7d86d
e1ea753
4e04ff5
50baced
af9f0cd
569d5cd
a1b5eec
b96cb1d
1b348c2
7999793
c2a485f
272d235
ee3feab
e29e7c7
47d9ed6
bd3210d
f8f0602
780041b
d4199c7
35c3c2d
fc448ff
43275ad
795dd84
fb49280
978b416
6be101d
a317ea0
21b1d77
4e75f78
84ac4ca
18eb5ff
391d651
892634d
53fbe1b
fecd270
74291d2
559b6a2
77512cd
b0793d2
0f2138d
d684224
27b12f0
d58d9ae
4af89d2
40da9d3
87fdc0c
52ff16e
960a247
3dbba40
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1141,28 +1141,25 @@ def test_annotate_source_locations(self): | |
|
||
for case_name, annotate_code in test_cases: | ||
with self.subTest(case=case_name): | ||
instructions = list(dis.Bytecode(annotate_code)) | ||
resume_pos = next( | ||
( | ||
inst.positions | ||
for inst in instructions | ||
if inst.opname == "RESUME" | ||
), | ||
None, | ||
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 | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does this need to be so complicated? Don't we just need to check that all the instructions in this code object have the same line number (excluding Nones)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because the important thing is to make sure the instructions in no fix: Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=0) RESUME
Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=2) LOAD_FAST_BORROW # incorrect
Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=2) LOAD_SMALL_INT # incorrect
Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=2) COMPARE_OP # incorrect
Positions(lineno=1, end_lineno
1E11
span>=1, col_offset=0, end_col_offset=2) POP_JUMP_IF_FALSE # incorrect
Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=2) NOT_TAKEN # incorrect
Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=2) LOAD_COMMON_CONSTANT # incorrect
Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=2) RAISE_VARARGS # incorrect
Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=2) BUILD_MAP # incorrect fix: Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=0) RESUME
Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=0) LOAD_FAST_BORROW
Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=0) LOAD_SMALL_INT
Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=0) COMPARE_OP
Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=0) POP_JUMP_IF_FALSE
Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=0) NOT_TAKEN
Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=0) LOAD_COMMON_CONSTANT
Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=0) RAISE_VARARGS
Positions(lineno=1, end_lineno=1, col_offset=0, end_col_offset=0) BUILD_MAP And not all annotate lines are the same, because some opcodes need to indicate the "code" where the annotation occurs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, so we need to check they all have the same end_col_offset as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The test doesn't need to work for all inputs. Only the two inputs it runs on. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Just to be clear, instructions within code object can correspond to different line numbers. So I made an offset restriction here, limiting the check to between RESUME and BUILD_MAP (setup_annotations_scope) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't have much of an intuition on how line numbers should work for synthetic code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @15r10nk are you able to suggest a meaningful unit test for this? Alternatively, can you confirm that the PR resolved the issue? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can verify that this fixes the issue which I have reported 👍 . I also found out that the reported bug affected debugging with pdb. example.py import example_mod
print(example_mod.__annotations__) example_mod.py 22
class ClassVar():
pass
__dataclass_fields__: ClassVar
The breakpoint is hit a second time when the synthetic code is executed which uses the incorrect source positions. I remember that I found multiple different problems with similar synthetic codes like this in the past ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great, thank you! The pdb example can probably be used for a unit test (in test_pdb). I'd remove the test that is currently in this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hi @iritkatriel @AndPuQing , I wrote a test with test_pdb.py maybe that can be used for this pr. class PdbTestCase(unittest.TestCase):
...
def test_issue135700(self):
""" https://github.com/python/cpython/issues/135700 """
module_code = """\
22
class ClassVar:
pass
__dataclass_fields__: ClassVar
"""
with open('testmod.py', 'w') as f:
f.write(module_code)
self.addCleanup(os_helper.unlink, "testmod.py")
script = """
import testmod
print(testmod.__annotations__)
"""
commands = """
b testmod.py:1
c
c
"""
result = self.run_pdb_script(script, commands)
self.assertNotIn("(1)__annotate__()", result[0]) |
||
self.assertEqual( | ||
len(setup_annotations_scope_positions), | ||
1, | ||
f"{case_name}: Expected uniform positions, found {len(setup_annotations_scope_positions)}: {setup_annotations_scope_positions}", | ||
) | ||
for instruction in instructions: | ||
if instruction.opname == "BUILD_MAP": | ||
break | ||
if ( | ||
instruction.opname != "RESUME" | ||
and instruction.positions | ||
and instruction.positions.lineno | ||
): | ||
self.assertEqual( | ||
instruction.positions, | ||
resume_pos, | ||
f"{case_name}: Unexpected position {instruction.positions} in {instruction.opname}, expected {resume_pos}", | ||
) | ||
|
||
def test_kw_names(self): | ||
# Test that value is displayed for keyword argument names: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert here that
annotate_code
is a code object and thatannotate_code.co_name
is'__annotate__'
.