10000 gh-59013: Set breakpoint on the first executable line of function whe… · python/cpython@765b9ce · GitHub
[go: up one dir, main page]

Skip to content

Commit 765b9ce

Browse files
gh-59013: Set breakpoint on the first executable line of function when using break func in pdb (#112470)
1 parent 1c2ea8b commit 765b9ce

File tree

3 files changed

+61
-22
lines changed

3 files changed

+61
-22
lines changed

Lib/pdb.py

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,47 @@ class Restart(Exception):
9797
__all__ = ["run", "pm", "Pdb", "runeval", "runctx", "runcall", "set_trace",
9898
"post_mortem", "help"]
9999

100+
101+
def find_first_executable_line(code):
102+
""" Try to find the first executable line of the code object.
103+
104+
Equivalently, find the line number of the instruction that's
105+
after RESUME
106+
107+
Return code.co_firstlineno if no executable line is found.
108+
"""
109+
prev = None
110+
for instr in dis.get_instructions(code):
111+
if prev is not None and prev.opname == 'RESUME':
112+
if instr.positions.lineno is not None:
113+
return instr.positions.lineno
114+
return code.co_firstlineno
115+
prev = instr
116+
return code.co_firstlineno
117+
100118
def find_function(funcname, filename):
101119
cre = re.compile(r'def\s+%s\s*[(]' % re.escape(funcname))
102120
try:
103121
fp = tokenize.open(filename)
104122
except OSError:
105123
return None
124+
funcdef = ""
125+
funcstart = None
106126
# consumer of this info expects the first line to be 1
107127
with fp:
108128
for lineno, line in enumerate(fp, start=1):
109129
if cre.match(line):
110-
return funcname, filename, lineno
130+
funcstart, funcdef = lineno, line
131+
elif funcdef:
132+
funcdef += line
133+
134+
if funcdef:
135+
try:
136+
funccode = compile(funcdef, filename, 'exec').co_consts[0]
137+
except SyntaxError:
138+
continue
139+
lineno_offset = find_first_executable_line(funccode)
140+
return funcname, filename, funcstart + lineno_offset - 1
111141
return None
112142

113143
def lasti2lineno(code, lasti):
@@ -975,7 +1005,7 @@ def do_break(self, arg, temporary = 0):
9751005
#use co_name to identify the bkpt (function names
9761006
#could be aliased, but co_name is invariant)
9771007
funcname = code.co_name
978-
lineno = self._find_first_executable_line(code)
1008+
lineno = find_first_executable_line(code)
9791009
filename = code.co_filename
9801010
except:
9811011
# last thing to try
@@ -1078,23 +1108,6 @@ def checkline(self, filename, lineno):
10781108
return 0
10791109
return lineno
10801110

1081-
def _find_first_executable_line(self, code):
1082-
""" Try to find the first executable line of the code object.
1083-
1084-
Equivalently, find the line number of the instruction that's
1085-
after RESUME
1086-
1087-
Return code.co_firstlineno if no executable line is found.
1088-
"""
1089-
prev = None
1090-
for instr in dis.get_instructions(code):
1091-
if prev is not None and prev.opname == 'RESUME':
1092-
if instr.positions.lineno is not None:
1093-
return instr.positions.lineno
1094-
return code.co_firstlineno
1095-
prev = instr
1096-
return code.co_firstlineno
1097-
10981111
def do_enable(self, arg):
10991112
"""enable bpnumber [bpnumber ...]
11001113

Lib/test/test_pdb.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2661,7 +2661,7 @@ def quux():
26612661
pass
26622662
""".encode(),
26632663
'bœr',
2664-
('bœr', 4),
2664+
('bœr', 5),
26652665
)
26662666

26672667
def test_find_function_found_with_encoding_cookie(self):
@@ -2678,7 +2678,7 @@ def quux():
26782678
pass
26792679
""".encode('iso-8859-15'),
26802680
'bœr',
2681-
('bœr', 5),
2681+
('bœr', 6),
26822682
)
26832683

26842684
def test_find_function_found_with_bom(self):
@@ -2688,9 +2688,34 @@ def bœr():
26882688
pass
26892689
""".encode(),
26902690
'bœr',
2691-
('bœr', 1),
2691+
('bœr', 2),
26922692
)
26932693

2694+
def test_find_function_first_executable_line(self):
2695+
code = textwrap.dedent("""\
2696+
def foo(): pass
2697+
2698+
def bar():
2699+
pass # line 4
2700+
2701+
def baz():
2702+
# comment
2703+
pass # line 8
2704+
2705+
def mul():
2706+
# code on multiple lines
2707+
code = compile( # line 12
2708+
'def f()',
2709+
'<string>',
2710+
'exec',
2711+
)
2712+
""").encode()
2713+
2714+
self._assert_find_function(code, 'foo', ('foo', 1))
2715+
self._assert_find_function(code, 'bar', ('bar', 4))
2716+
self._assert_find_function(code, 'baz', ('baz', 8))
2717+
self._assert_find_function(code, 'mul', ('mul', 12))
2718+
26942719
def test_issue7964(self):
26952720
# open the file as binary so we can force \r\n newline
26962721
with open(os_helper.TESTFN, 'wb') as f:
< 5CCA div class="d-flex flex-row">
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Set breakpoint on the first executable line of the function, instead of the line of function definition when the user do ``break func`` using :mod:`pdb`

0 commit comments

Comments
 (0)
0