From: Tian Gao Date: Thu, 20 Feb 2025 02:01:04 +0000 (-0500) Subject: gh-57537: Support breakpoints for zipimport modules on pdb (#130290) X-Git-Tag: v3.14.0a6~349 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ee337bea01beb3da6dccfe1b48db9a4c891b05a1;p=thirdparty%2FPython%2Fcpython.git gh-57537: Support breakpoints for zipimport modules on pdb (#130290) --- diff --git a/Lib/pdb.py b/Lib/pdb.py index cf0fa66e2f0c..08a941de79ec 100644 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -1134,6 +1134,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): filename = None lineno = None cond = None + module_globals = None comma = arg.find(',') if comma > 0: # parse stuff after comma: "condition" @@ -1179,6 +1180,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): funcname = code.co_name lineno = find_first_executable_line(code) filename = code.co_filename + module_globals = func.__globals__ except: # last thing to try (ok, filename, ln) = self.lineinfo(arg) @@ -1190,8 +1192,9 @@ class Pdb(bdb.Bdb, cmd.Cmd): lineno = int(ln) if not filename: filename = self.defaultFile() + filename = self.canonic(filename) # Check for reasonable breakpoint - line = self.checkline(filename, lineno) + line = self.checkline(filename, lineno, module_globals) if line: # now set the break point err = self.set_break(filename, line, temporary, cond, funcname) @@ -1258,7 +1261,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): answer = find_function(item, self.canonic(fname)) return answer or failed - def checkline(self, filename, lineno): + def checkline(self, filename, lineno, module_globals=None): """Check whether specified line seems to be executable. Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank @@ -1267,8 +1270,9 @@ class Pdb(bdb.Bdb, cmd.Cmd): # this method should be callable before starting debugging, so default # to "no globals" if there is no current frame frame = getattr(self, 'curframe', None) - globs = frame.f_globals if frame else None - line = linecache.getline(filename, lineno, globs) + if module_globals is None: + module_globals = frame.f_globals if frame else None + line = linecache.getline(filename, lineno, module_globals) if not line: self.message('End of file') return 0 diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index fa439c1fe891..83753279599f 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -11,6 +11,7 @@ import subprocess import textwrap import linecache import zipapp +import zipfile from contextlib import ExitStack, redirect_stdout from io import StringIO @@ -4207,6 +4208,38 @@ def bœr(): self.assertIn('42', stdout) self.assertIn('return x + 1', stdout) + def test_zipimport(self): + with os_helper.temp_dir() as temp_dir: + os.mkdir(os.path.join(temp_dir, 'source')) + zipmodule = textwrap.dedent( + """ + def bar(): + pass + """ + ) + script = textwrap.dedent( + f""" + import sys; sys.path.insert(0, {repr(os.path.join(temp_dir, 'zipmodule.zip'))}) + import foo + foo.bar() + """ + ) + + with zipfile.ZipFile(os.path.join(temp_dir, 'zipmodule.zip'), 'w') as zf: + zf.writestr('foo.py', zipmodule) + with open(os.path.join(temp_dir, 'script.py'), 'w') as f: + f.write(script) + + stdout, _ = self._run_pdb([os.path.join(temp_dir, 'script.py')], '\n'.join([ + 'n', + 'n', + 'b foo.bar', + 'c', + 'p f"break in {$_frame.f_code.co_name}"', + 'q' + ])) + self.assertIn('break in bar', stdout) + class ChecklineTests(unittest.TestCase): def setUp(self): diff --git a/Misc/NEWS.d/next/Library/2025-02-19-01-29-16.gh-issue-57537.4tdVuK.rst b/Misc/NEWS.d/next/Library/2025-02-19-01-29-16.gh-issue-57537.4tdVuK.rst new file mode 100644 index 000000000000..40e4094cc5fc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-02-19-01-29-16.gh-issue-57537.4tdVuK.rst @@ -0,0 +1 @@ +Support breakpoints for :mod:`zipimport` modules on :mod:`pdb`