From 727f6c59e9210a4ac263a281b323f40915f2a520 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Mon, 27 Nov 2023 11:27:16 -0800 Subject: [PATCH 1/6] Make break function work consistently --- Lib/pdb.py | 51 +++++++++++++++++++++++++++----------------- Lib/test/test_pdb.py | 21 +++++++++++++++--- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/Lib/pdb.py b/Lib/pdb.py index ed78d749a47fa8..4bcafd1b070faa 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -96,17 +96,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): @@ -913,7 +943,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 @@ -1016,23 +1046,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 67a4053a2ac8bc..3f001088011084 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -2587,7 +2587,7 @@ def quux(): pass """.encode(), 'bœr', - ('bœr', 4), + ('bœr', 5), ) def test_find_function_found_with_encoding_cookie(self): @@ -2604,7 +2604,7 @@ def quux(): pass """.encode('iso-8859-15'), 'bœr', - ('bœr', 5), + ('bœr', 6), ) def test_find_function_found_with_bom(self): @@ -2614,9 +2614,24 @@ def bœr(): pass """.encode(), 'bœr', - ('bœr', 1), + ('bœr', 2), ) + def test_find_function_empty_file(self): + code = textwrap.dedent("""\ + def foo(): pass + + def bar(): + pass + + def baz(): + # comment + pass + """).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)) + def test_issue7964(self): # open the file as binary so we can force \r\n newline with open(os_helper.TESTFN, 'wb') as f: From 9c33910772f347b9c11902697f69a3f394eef51b Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Mon, 27 Nov 2023 11:41:48 -0800 Subject: [PATCH 2/6] Fix test name, add multiline test --- Lib/test/test_pdb.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 3f001088011084..502bb73f2e7e04 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -2617,7 +2617,7 @@ def bœr(): ('bœr', 2), ) - def test_find_function_empty_file(self): + def test_find_function_first_executable_line(self): code = textwrap.dedent("""\ def foo(): pass @@ -2627,10 +2627,20 @@ def bar(): def baz(): # comment pass + + def mul(): + # code on multiple lines + code = compile( + 'def f()', + '', + '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 From 8ffd98778efc0c2d51b9e08f3754157c68778246 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Mon, 27 Nov 2023 19:54:44 +0000 Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2023-11-27-19-54-43.gh-issue-59013.chpQ0e.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2023-11-27-19-54-43.gh-issue-59013.chpQ0e.rst 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..397513af93ffa7 --- /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 in :mod:`pdb` instead of function definition when using `break func` From eff667529b524a08ded77b6dd4dc23b275295f31 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Mon, 27 Nov 2023 12:42:07 -0800 Subject: [PATCH 4/6] Update 2023-11-27-19-54-43.gh-issue-59013.chpQ0e.rst --- .../next/Library/2023-11-27-19-54-43.gh-issue-59013.chpQ0e.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 397513af93ffa7..ee8caf1dbc10c7 100644 --- 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 @@ -1 +1 @@ -Set breakpoint on the first executable line in :mod:`pdb` instead of function definition when using `break func` +Set breakpoint on the first executable line in :mod:`pdb` instead of function definition when using ``break func`` From bb1f81ac501f47f4750496d29a33533ce20ddeec Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Tue, 30 Jan 2024 13:21:16 -0800 Subject: [PATCH 5/6] Add line number to the code Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> --- Lib/test/test_pdb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 502bb73f2e7e04..9628d2aceee5a1 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -2622,15 +2622,15 @@ def test_find_function_first_executable_line(self): def foo(): pass def bar(): - pass + pass # line 4 def baz(): # comment - pass + pass # line 8 def mul(): # code on multiple lines - code = compile( + code = compile( # line 12 'def f()', '', 'exec', From 3868f8862fa7fa10b79e0805208a4abdac73093e Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Tue, 30 Jan 2024 13:25:49 -0800 Subject: [PATCH 6/6] Update 2023-11-27-19-54-43.gh-issue-59013.chpQ0e.rst --- .../next/Library/2023-11-27-19-54-43.gh-issue-59013.chpQ0e.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index ee8caf1dbc10c7..a2be2fb8eacf17 100644 --- 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 @@ -1 +1 @@ -Set breakpoint on the first executable line in :mod:`pdb` instead of function definition when using ``break func`` +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`