https://github.com/python/cpython/commit/765b9ce9fb357bdb79a50ce51207c827fbd13dd8
commit: 765b9ce9fb357bdb79a50ce51207c827fbd13dd8
branch: main
author: Tian Gao <[email protected]>
committer: iritkatriel <[email protected]>
date: 2024-01-31T13:03:05Z
summary:
gh-59013: Set breakpoint on the first executable line of function when using
`break func` in pdb (#112470)
files:
A Misc/NEWS.d/next/Library/2023-11-27-19-54-43.gh-issue-59013.chpQ0e.rst
M Lib/pdb.py
M Lib/test/test_pdb.py
diff --git a/Lib/pdb.py b/Lib/pdb.py
index 6f7719eb9ba6c5..0754e8b628cf57 100755
--- a/Lib/pdb.py
+++ b/Lib/pdb.py
@@ -97,17 +97,47 @@ class Restart(Exception):
__all__ = ["run", "pm", "Pdb", "runeval", "runctx", "runcall", "set_trace",
"post_mortem", "help"]
+
+def find_first_executable_line(code):
+ """ Try to find the first executable line of the code object.
+
+ Equivalently, find the line number of the instruction that's
+ after RESUME
+
+ Return code.co_firstlineno if no executable line is found.
+ """
+ prev = None
+ for instr in dis.get_instructions(code):
+ if prev is not None and prev.opname == 'RESUME':
+ if instr.positions.lineno is not None:
+ return instr.positions.lineno
+ return code.co_firstlineno
+ prev = instr
+ return code.co_firstlineno
+
def find_function(funcname, filename):
cre = re.compile(r'def\s+%s\s*[(]' % re.escape(funcname))
try:
fp = tokenize.open(filename)
except OSError:
return None
+ funcdef = ""
+ funcstart = None
# consumer of this info expects the first line to be 1
with fp:
for lineno, line in enumerate(fp, start=1):
if cre.match(line):
- return funcname, filename, lineno
+ funcstart, funcdef = lineno, line
+ elif funcdef:
+ funcdef += line
+
+ if funcdef:
+ try:
+ funccode = compile(funcdef, filename, 'exec').co_consts[0]
+ except SyntaxError:
+ continue
+ lineno_offset = find_first_executable_line(funccode)
+ return funcname, filename, funcstart + lineno_offset - 1
return None
def lasti2lineno(code, lasti):
@@ -975,7 +1005,7 @@ def do_break(self, arg, temporary = 0):
#use co_name to identify the bkpt (function names
#could be aliased, but co_name is invariant)
funcname = code.co_name
- lineno = self._find_first_executable_line(code)
+ lineno = find_first_executable_line(code)
filename = code.co_filename
except:
# last thing to try
@@ -1078,23 +1108,6 @@ def checkline(self, filename, lineno):
return 0
return lineno
- def _find_first_executable_line(self, code):
- """ Try to find the first executable line of the code object.
-
- Equivalently, find the line number of the instruction that's
- after RESUME
-
- Return code.co_firstlineno if no executable line is found.
- """
- prev = None
- for instr in dis.get_instructions(code):
- if prev is not None and prev.opname == 'RESUME':
- if instr.positions.lineno is not None:
- return instr.positions.lineno
- return code.co_firstlineno
- prev = instr
- return code.co_firstlineno
-
def do_enable(self, arg):
"""enable bpnumber [bpnumber ...]
diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py
index c64df62c761471..b2283cff6cb462 100644
--- a/Lib/test/test_pdb.py
+++ b/Lib/test/test_pdb.py
@@ -2661,7 +2661,7 @@ def quux():
pass
""".encode(),
'bœr',
- ('bœr', 4),
+ ('bœr', 5),
)
def test_find_function_found_with_encoding_cookie(self):
@@ -2678,7 +2678,7 @@ def quux():
pass
""".encode('iso-8859-15'),
'bœr',
- ('bœr', 5),
+ ('bœr', 6),
)
def test_find_function_found_with_bom(self):
@@ -2688,9 +2688,34 @@ def bœr():
pass
""".encode(),
'bœr',
- ('bœr', 1),
+ ('bœr', 2),
)
+ def test_find_function_first_executable_line(self):
+ code = textwrap.dedent("""\
+ def foo(): pass
+
+ def bar():
+ pass # line 4
+
+ def baz():
+ # comment
+ pass # line 8
+
+ def mul():
+ # code on multiple lines
+ code = compile( # line 12
+ 'def f()',
+ '<string>',
+ 'exec',
+ )
+ """).encode()
+
+ self._assert_find_function(code, 'foo', ('foo', 1))
+ self._assert_find_function(code, 'bar', ('bar', 4))
+ self._assert_find_function(code, 'baz', ('baz', 8))
+ self._assert_find_function(code, 'mul', ('mul', 12))
+
def test_issue7964(self):
# open the file as binary so we can force \r\n newline
with open(os_helper.TESTFN, 'wb') as f:
diff --git
a/Misc/NEWS.d/next/Library/2023-11-27-19-54-43.gh-issue-59013.chpQ0e.rst
b/Misc/NEWS.d/next/Library/2023-11-27-19-54-43.gh-issue-59013.chpQ0e.rst
new file mode 100644
index 00000000000000..a2be2fb8eacf17
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-11-27-19-54-43.gh-issue-59013.chpQ0e.rst
@@ -0,0 +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`
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]