From 0fa571dbcdf19b541276cb00bb929381930467b2 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Fri, 29 Mar 2024 09:02:01 -0700 Subject: Refactor pdb executable targets (#112570) Co-authored-by: Jason R. Coombs --- Lib/pdb.py | 77 +++++++++++++++++++++++++++++--------------------------------- 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/Lib/pdb.py b/Lib/pdb.py index f80171d..d4138b9 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -82,13 +82,12 @@ import pprint import signal import inspect import tokenize -import functools import traceback import linecache from contextlib import contextmanager from rlcompleter import Completer -from typing import Union +from types import CodeType class Restart(Exception): @@ -156,52 +155,58 @@ class _rstr(str): return self -class _ScriptTarget(str): - def __new__(cls, val): - # Mutate self to be the "real path". - res = super().__new__(cls, os.path.realpath(val)) +class _ExecutableTarget: + filename: str + code: CodeType | str + namespace: dict - # Store the original path for error reporting. - res.orig = val - return res +class _ScriptTarget(_ExecutableTarget): + def __init__(self, target): + self._target = os.path.realpath(target) - def check(self): - if not os.path.exists(self): - print('Error:', self.orig, 'does not exist') + if not os.path.exists(self._target): + print(f'Error: {target} does not exist') sys.exit(1) - if os.path.isdir(self): - print('Error:', self.orig, 'is a directory') + if os.path.isdir(self._target): + print(f'Error: {target} is a directory') sys.exit(1) # If safe_path(-P) is not set, sys.path[0] is the directory # of pdb, and we should replace it with the directory of the script if not sys.flags.safe_path: - sys.path[0] = os.path.dirname(self) + sys.path[0] = os.path.dirname(self._target) + + def __repr__(self): + return self._target @property def filename(self): - return self + return self._target + + @property + def code(self): + # Open the file each time because the file may be modified + with io.open_code(self._target) as fp: + return f"exec(compile({fp.read()!r}, {self._target!r}, 'exec'))" @property def namespace(self): return dict( __name__='__main__', - __file__=self, + __file__=self._target, __builtins__=__builtins__, __spec__=None, ) - @property - def code(self): - with io.open_code(self) as fp: - return f"exec(compile({fp.read()!r}, {self!r}, 'exec'))" +class _ModuleTarget(_ExecutableTarget): + def __init__(self, target): + self._target = target -class _ModuleTarget(str): - def check(self): + import runpy try: - self._details + _, self._spec, self._code = runpy._get_module_details(self._target) except ImportError as e: print(f"ImportError: {e}") sys.exit(1) @@ -209,24 +214,16 @@ class _ModuleTarget(str): traceback.print_exc() sys.exit(1) - @functools.cached_property - def _details(self): - import runpy - return runpy._get_module_details(self) + def __repr__(self): + return self._target @property def filename(self): - return self.code.co_filename + return self._code.co_filename @property def code(self): - name, spec, code = self._details - return code - - @property - def _spec(self): - name, spec, code = self._details - return spec + return self._code @property def namespace(self): @@ -2029,7 +2026,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): return fullname return None - def _run(self, target: Union[_ModuleTarget, _ScriptTarget]): + def _run(self, target: _ExecutableTarget): # When bdb sets tracing, a number of call and line events happen # BEFORE debugger even reaches user's code (and the exact sequence of # events depends on python version). Take special measures to @@ -2281,8 +2278,6 @@ def main(): file = opts.args.pop(0) target = _ScriptTarget(file) - target.check() - sys.argv[:] = [file] + opts.args # Hide "pdb.py" and pdb options from argument list # Note on saving/restoring sys.argv: it's a good idea when sys.argv was @@ -2306,8 +2301,8 @@ def main(): print("Uncaught exception. Entering post mortem debugging") print("Running 'cont' or 'step' will restart the program") pdb.interaction(None, e) - print("Post mortem debugger finished. The " + target + - " will be restarted") + print(f"Post mortem debugger finished. The {target} will " + "be restarted") if pdb._user_requested_quit: break print("The program finished and will be restarted") -- cgit v0.12