]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-118500: Add pdb support for zipapp (#118501)
authorTian Gao <gaogaotiantian@hotmail.com>
Thu, 2 May 2024 20:53:27 +0000 (13:53 -0700)
committerGitHub <noreply@github.com>
Thu, 2 May 2024 20:53:27 +0000 (21:53 +0100)
Doc/whatsnew/3.13.rst
Lib/pdb.py
Lib/test/test_pdb.py
Lib/test/test_pyclbr.py
Misc/NEWS.d/next/Library/2024-05-02-04-27-12.gh-issue-118500.pBGGtQ.rst [new file with mode: 0644]

index fbf2f4c447468dba466d0c75e49767a549149e6e..d59c4ee22e67d3e19351a8ea38700f459c7e9582 100644 (file)
@@ -705,6 +705,9 @@ pdb
   command line option or :envvar:`PYTHONSAFEPATH` environment variable).
   (Contributed by Tian Gao and Christian Walther in :gh:`111762`.)
 
+* :mod:`zipapp` is supported as a debugging target.
+  (Contributed by Tian Gao in :gh:`118501`.)
+
 queue
 -----
 
index fa2e1ec94be2358cafb6167ecc92f4493929a137..bb669a0d2c1ce563d7085e58ddf146bf49ce7acf 100755 (executable)
@@ -120,7 +120,10 @@ def find_function(funcname, filename):
     try:
         fp = tokenize.open(filename)
     except OSError:
-        return None
+        lines = linecache.getlines(filename)
+        if not lines:
+            return None
+        fp = io.StringIO(''.join(lines))
     funcdef = ""
     funcstart = None
     # consumer of this info expects the first line to be 1
@@ -237,6 +240,44 @@ class _ModuleTarget(_ExecutableTarget):
         )
 
 
+class _ZipTarget(_ExecutableTarget):
+    def __init__(self, target):
+        import runpy
+
+        self._target = os.path.realpath(target)
+        sys.path.insert(0, self._target)
+        try:
+            _, self._spec, self._code = runpy._get_main_module_details()
+        except ImportError as e:
+            print(f"ImportError: {e}")
+            sys.exit(1)
+        except Exception:
+            traceback.print_exc()
+            sys.exit(1)
+
+    def __repr__(self):
+        return self._target
+
+    @property
+    def filename(self):
+        return self._code.co_filename
+
+    @property
+    def code(self):
+        return self._code
+
+    @property
+    def namespace(self):
+        return dict(
+            __name__='__main__',
+            __file__=os.path.normcase(os.path.abspath(self.filename)),
+            __package__=self._spec.parent,
+            __loader__=self._spec.loader,
+            __spec__=self._spec,
+            __builtins__=__builtins__,
+        )
+
+
 class _PdbInteractiveConsole(code.InteractiveConsole):
     def __init__(self, ns, message):
         self._message = message
@@ -1076,7 +1117,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
             if f:
                 fname = f
             item = parts[1]
-        answer = find_function(item, fname)
+        answer = find_function(item, self.canonic(fname))
         return answer or failed
 
     def checkline(self, filename, lineno):
@@ -2282,7 +2323,10 @@ def main():
         if not opts.args:
             parser.error("no module or script to run")
         file = opts.args.pop(0)
-        target = _ScriptTarget(file)
+        if file.endswith('.pyz'):
+            target = _ZipTarget(file)
+        else:
+            target = _ScriptTarget(file)
 
     sys.argv[:] = [file] + opts.args  # Hide "pdb.py" and pdb options from argument list
 
index 5635d17c99d2a329069e38ea1cf525387ca4378e..a3d2dda43b086d9400b715b0554a993e5609494a 100644 (file)
@@ -10,6 +10,7 @@ import unittest
 import subprocess
 import textwrap
 import linecache
+import zipapp
 
 from contextlib import ExitStack, redirect_stdout
 from io import StringIO
@@ -3532,6 +3533,30 @@ def bœr():
             if filename.endswith(".py"):
                 self._run_pdb([os.path.join(script_dir, filename)], 'q')
 
+    def test_zipapp(self):
+        with os_helper.temp_dir() as temp_dir:
+            os.mkdir(os.path.join(temp_dir, 'source'))
+            script = textwrap.dedent(
+                """
+                def f(x):
+                    return x + 1
+                f(21 + 21)
+                """
+            )
+            with open(os.path.join(temp_dir, 'source', '__main__.py'), 'w') as f:
+                f.write(script)
+            zipapp.create_archive(os.path.join(temp_dir, 'source'),
+                                  os.path.join(temp_dir, 'zipapp.pyz'))
+            stdout, _ = self._run_pdb([os.path.join(temp_dir, 'zipapp.pyz')], '\n'.join([
+                'b f',
+                'c',
+                'p x',
+                'q'
+            ]))
+            self.assertIn('42', stdout)
+            self.assertIn('return x + 1', stdout)
+
+
 class ChecklineTests(unittest.TestCase):
     def setUp(self):
         linecache.clearcache()  # Pdb.checkline() uses linecache.getline()
index c7c5419ffe3e37926f3f86518dd821df4b82bc88..46206accbafc36625ba3c5a5ff5435cb775de82a 100644 (file)
@@ -226,7 +226,7 @@ class PyclbrTest(TestCase):
         cm(
             'pdb',
             # pyclbr does not handle elegantly `typing` or properties
-            ignore=('Union', '_ModuleTarget', '_ScriptTarget'),
+            ignore=('Union', '_ModuleTarget', '_ScriptTarget', '_ZipTarget'),
         )
         cm('pydoc', ignore=('input', 'output',)) # properties
 
diff --git a/Misc/NEWS.d/next/Library/2024-05-02-04-27-12.gh-issue-118500.pBGGtQ.rst b/Misc/NEWS.d/next/Library/2024-05-02-04-27-12.gh-issue-118500.pBGGtQ.rst
new file mode 100644 (file)
index 0000000..62c7b5f
--- /dev/null
@@ -0,0 +1 @@
+Add :mod:`pdb` support for zipapps