Skip to content

Commit 0fa571d

Browse files
Refactor pdb executable targets (#112570)
Co-authored-by: Jason R. Coombs <[email protected]>
1 parent 54f7e14 commit 0fa571d

File tree

1 file changed

+36
-41
lines changed

1 file changed

+36
-41
lines changed

Lib/pdb.py

+36-41
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,12 @@
8282
import signal
8383
import inspect
8484
import tokenize
85-
import functools
8685
import traceback
8786
import linecache
8887

8988
from contextlib import contextmanager
9089
from rlcompleter import Completer
91-
from typing import Union
90+
from types import CodeType
9291

9392

9493
class Restart(Exception):
@@ -156,77 +155,75 @@ def __repr__(self):
156155
return self
157156

158157

159-
class _ScriptTarget(str):
160-
def __new__(cls, val):
161-
# Mutate self to be the "real path".
162-
res = super().__new__(cls, os.path.realpath(val))
158+
class _ExecutableTarget:
159+
filename: str
160+
code: CodeType | str
161+
namespace: dict
163162

164-
# Store the original path for error reporting.
165-
res.orig = val
166163

167-
return res
164+
class _ScriptTarget(_ExecutableTarget):
165+
def __init__(self, target):
166+
self._target = os.path.realpath(target)
168167

169-
def check(self):
170-
if not os.path.exists(self):
171-
print('Error:', self.orig, 'does not exist')
168+
if not os.path.exists(self._target):
169+
print(f'Error: {target} does not exist')
172170
sys.exit(1)
173-
if os.path.isdir(self):
174-
print('Error:', self.orig, 'is a directory')
171+
if os.path.isdir(self._target):
172+
print(f'Error: {target} is a directory')
175173
sys.exit(1)
176174

177175
# If safe_path(-P) is not set, sys.path[0] is the directory
178176
# of pdb, and we should replace it with the directory of the script
179177
if not sys.flags.safe_path:
180-
sys.path[0] = os.path.dirname(self)
178+
sys.path[0] = os.path.dirname(self._target)
179+
180+
def __repr__(self):
181+
return self._target
181182

182183
@property
183184
def filename(self):
184-
return self
185+
return self._target
186+
187+
@property
188+
def code(self):
189+
# Open the file each time because the file may be modified
190+
with io.open_code(self._target) as fp:
191+
return f"exec(compile({fp.read()!r}, {self._target!r}, 'exec'))"
185192

186193
@property
187194
def namespace(self):
188195
return dict(
189196
__name__='__main__',
190-
__file__=self,
197+
__file__=self._target,
191198
__builtins__=__builtins__,
192199
__spec__=None,
193200
)
194201

195-
@property
196-
def code(self):
197-
with io.open_code(self) as fp:
198-
return f"exec(compile({fp.read()!r}, {self!r}, 'exec'))"
199202

203+
class _ModuleTarget(_ExecutableTarget):
204+
def __init__(self, target):
205+
self._target = target
200206

201-
class _ModuleTarget(str):
202-
def check(self):
207+
import runpy
203208
try:
204-
self._details
209+
_, self._spec, self._code = runpy._get_module_details(self._target)
205210
except ImportError as e:
206211
print(f"ImportError: {e}")
207212
sys.exit(1)
208213
except Exception:
209214
traceback.print_exc()
210215
sys.exit(1)
211216

212-
@functools.cached_property
213-
def _details(self):
214-
import runpy
215-
return runpy._get_module_details(self)
217+
def __repr__(self):
218+
return self._target
216219

217220
@property
218221
def filename(self):
219-
return self.code.co_filename
222+
return self._code.co_filename
220223

221224
@property
222225
def code(self):
223-
name, spec, code = self._details
224-
return code
225-
226-
@property
227-
def _spec(self):
228-
name, spec, code = self._details
229-
return spec
226+
return self._code
230227

231228
@property
232229
def namespace(self):
@@ -2029,7 +2026,7 @@ def lookupmodule(self, filename):
20292026
return fullname
20302027
return None
20312028

2032-
def _run(self, target: Union[_ModuleTarget, _ScriptTarget]):
2029+
def _run(self, target: _ExecutableTarget):
20332030
# When bdb sets tracing, a number of call and line events happen
20342031
# BEFORE debugger even reaches user's code (and the exact sequence of
20352032
# events depends on python version). Take special measures to
@@ -2281,8 +2278,6 @@ def main():
22812278
file = opts.args.pop(0)
22822279
target = _ScriptTarget(file)
22832280

2284-
target.check()
2285-
22862281
sys.argv[:] = [file] + opts.args # Hide "pdb.py" and pdb options from argument list
22872282

22882283
# Note on saving/restoring sys.argv: it's a good idea when sys.argv was
@@ -2306,8 +2301,8 @@ def main():
23062301
print("Uncaught exception. Entering post mortem debugging")
23072302
print("Running 'cont' or 'step' will restart the program")
23082303
pdb.interaction(None, e)
2309-
print("Post mortem debugger finished. The " + target +
2310-
" will be restarted")
2304+
print(f"Post mortem debugger finished. The {target} will "
2305+
"be restarted")
23112306
if pdb._user_requested_quit:
23122307
break
23132308
print("The program finished and will be restarted")

0 commit comments

Comments
 (0)