Refactor pdb executable targets (#112570)

Co-authored-by: Jason R. Coombs <jaraco@jaraco.com>
This commit is contained in:
Tian Gao 2024-03-29 09:02:01 -07:00 committed by GitHub
parent 54f7e14500
commit 0fa571dbcd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -82,13 +82,12 @@
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 @@ def __repr__(self):
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 @@ def check(self):
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 @@ def lookupmodule(self, filename):
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")