Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4e2caf2

Browse filesBrowse files
gh-118500: Add pdb support for zipapp (#118501)
1 parent e54b0c8 commit 4e2caf2
Copy full SHA for 4e2caf2

File tree

Expand file treeCollapse file tree

5 files changed

+77
-4
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+77
-4
lines changed

‎Doc/whatsnew/3.13.rst

Copy file name to clipboardExpand all lines: Doc/whatsnew/3.13.rst
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,9 @@ pdb
705705
command line option or :envvar:`PYTHONSAFEPATH` environment variable).
706706
(Contributed by Tian Gao and Christian Walther in :gh:`111762`.)
707707

708+
* :mod:`zipapp` is supported as a debugging target.
709+
(Contributed by Tian Gao in :gh:`118501`.)
710+
708711
queue
709712
-----
710713

‎Lib/pdb.py

Copy file name to clipboardExpand all lines: Lib/pdb.py
+47-3Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,10 @@ def find_function(funcname, filename):
120120
try:
121121
fp = tokenize.open(filename)
122122
except OSError:
123-
return None
123+
lines = linecache.getlines(filename)
124+
if not lines:
125+
return None
126+
fp = io.StringIO(''.join(lines))
124127
funcdef = ""
125128
funcstart = None
126129
# consumer of this info expects the first line to be 1
@@ -237,6 +240,44 @@ def namespace(self):
237240
)
238241

239242

243+
class _ZipTarget(_ExecutableTarget):
244+
def __init__(self, target):
245+
import runpy
246+
247+
self._target = os.path.realpath(target)
248+
sys.path.insert(0, self._target)
249+
try:
250+
_, self._spec, self._code = runpy._get_main_module_details()
251+
except ImportError as e:
252+
print(f"ImportError: {e}")
253+
sys.exit(1)
254+
except Exception:
255+
traceback.print_exc()
256+
sys.exit(1)
257+
258+
def __repr__(self):
259+
return self._target
260+
261+
@property
262+
def filename(self):
263+
return self._code.co_filename
264+
265+
@property
266+
def code(self):
267+
return self._code
268+
269+
@property
270+
def namespace(self):
271+
return dict(
272+
__name__='__main__',
273+
__file__=os.path.normcase(os.path.abspath(self.filename)),
274+
__package__=self._spec.parent,
275+
__loader__=self._spec.loader,
276+
__spec__=self._spec,
277+
__builtins__=__builtins__,
278+
)
279+
280+
240281
class _PdbInteractiveConsole(code.InteractiveConsole):
241282
def __init__(self, ns, message):
242283
self._message = message
@@ -1076,7 +1117,7 @@ def lineinfo(self, identifier):
10761117
if f:
10771118
fname = f
10781119
item = parts[1]
1079-
answer = find_function(item, fname)
1120+
answer = find_function(item, self.canonic(fname))
10801121
return answer or failed
10811122

10821123
def checkline(self, filename, lineno):
@@ -2282,7 +2323,10 @@ def main():
22822323
if not opts.args:
22832324
parser.error("no module or script to run")
22842325
file = opts.args.pop(0)
2285-
target = _ScriptTarget(file)
2326+
if file.endswith('.pyz'):
2327+
target = _ZipTarget(file)
2328+
else:
2329+
target = _ScriptTarget(file)
22862330

22872331
sys.argv[:] = [file] + opts.args # Hide "pdb.py" and pdb options from argument list
22882332

‎Lib/test/test_pdb.py

Copy file name to clipboardExpand all lines: Lib/test/test_pdb.py
+25Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import subprocess
1111
import textwrap
1212
import linecache
13+
import zipapp
1314

1415
from contextlib import ExitStack, redirect_stdout
1516
from io import StringIO
@@ -3532,6 +3533,30 @@ def test_non_utf8_encoding(self):
35323533
if filename.endswith(".py"):
35333534
self._run_pdb([os.path.join(script_dir, filename)], 'q')
35343535

3536+
def test_zipapp(self):
3537+
with os_helper.temp_dir() as temp_dir:
3538+
os.mkdir(os.path.join(temp_dir, 'source'))
3539+
script = textwrap.dedent(
3540+
"""
3541+
def f(x):
3542+
return x + 1
3543+
f(21 + 21)
3544+
"""
3545+
)
3546+
with open(os.path.join(temp_dir, 'source', '__main__.py'), 'w') as f:
3547+
f.write(script)
3548+
zipapp.create_archive(os.path.join(temp_dir, 'source'),
3549+
os.path.join(temp_dir, 'zipapp.pyz'))
3550+
stdout, _ = self._run_pdb([os.path.join(temp_dir, 'zipapp.pyz')], '\n'.join([
3551+
'b f',
3552+
'c',
3553+
'p x',
3554+
'q'
3555+
]))
3556+
self.assertIn('42', stdout)
3557+
self.assertIn('return x + 1', stdout)
3558+
3559+
35353560
class ChecklineTests(unittest.TestCase):
35363561
def setUp(self):
35373562
linecache.clearcache() # Pdb.checkline() uses linecache.getline()

‎Lib/test/test_pyclbr.py

Copy file name to clipboardExpand all lines: Lib/test/test_pyclbr.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ def test_others(self):
226226
cm(
227227
'pdb',
228228
# pyclbr does not handle elegantly `typing` or properties
229-
ignore=('Union', '_ModuleTarget', '_ScriptTarget'),
229+
ignore=('Union', '_ModuleTarget', '_ScriptTarget', '_ZipTarget'),
230230
)
231231
cm('pydoc', ignore=('input', 'output',)) # properties
232232

+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add :mod:`pdb` support for zipapps

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.