##// END OF EJS Templates
Merge branch 'main' into cdeb
M Bussonnier -
r28713:8225d787 merge
parent child Browse files
Show More
@@ -1,1100 +1,1090 b''
1 """
1 """
2 Pdb debugger class.
2 Pdb debugger class.
3
3
4
4
5 This is an extension to PDB which adds a number of new features.
5 This is an extension to PDB which adds a number of new features.
6 Note that there is also the `IPython.terminal.debugger` class which provides UI
6 Note that there is also the `IPython.terminal.debugger` class which provides UI
7 improvements.
7 improvements.
8
8
9 We also strongly recommend to use this via the `ipdb` package, which provides
9 We also strongly recommend to use this via the `ipdb` package, which provides
10 extra configuration options.
10 extra configuration options.
11
11
12 Among other things, this subclass of PDB:
12 Among other things, this subclass of PDB:
13 - supports many IPython magics like pdef/psource
13 - supports many IPython magics like pdef/psource
14 - hide frames in tracebacks based on `__tracebackhide__`
14 - hide frames in tracebacks based on `__tracebackhide__`
15 - allows to skip frames based on `__debuggerskip__`
15 - allows to skip frames based on `__debuggerskip__`
16
16
17 The skipping and hiding frames are configurable via the `skip_predicates`
17 The skipping and hiding frames are configurable via the `skip_predicates`
18 command.
18 command.
19
19
20 By default, frames from readonly files will be hidden, frames containing
20 By default, frames from readonly files will be hidden, frames containing
21 ``__tracebackhide__=True`` will be hidden.
21 ``__tracebackhide__=True`` will be hidden.
22
22
23 Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent
23 Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent
24 frames value of ``__debuggerskip__`` is ``True`` will be skipped.
24 frames value of ``__debuggerskip__`` is ``True`` will be skipped.
25
25
26 >>> def helpers_helper():
26 >>> def helpers_helper():
27 ... pass
27 ... pass
28 ...
28 ...
29 ... def helper_1():
29 ... def helper_1():
30 ... print("don't step in me")
30 ... print("don't step in me")
31 ... helpers_helpers() # will be stepped over unless breakpoint set.
31 ... helpers_helpers() # will be stepped over unless breakpoint set.
32 ...
32 ...
33 ...
33 ...
34 ... def helper_2():
34 ... def helper_2():
35 ... print("in me neither")
35 ... print("in me neither")
36 ...
36 ...
37
37
38 One can define a decorator that wraps a function between the two helpers:
38 One can define a decorator that wraps a function between the two helpers:
39
39
40 >>> def pdb_skipped_decorator(function):
40 >>> def pdb_skipped_decorator(function):
41 ...
41 ...
42 ...
42 ...
43 ... def wrapped_fn(*args, **kwargs):
43 ... def wrapped_fn(*args, **kwargs):
44 ... __debuggerskip__ = True
44 ... __debuggerskip__ = True
45 ... helper_1()
45 ... helper_1()
46 ... __debuggerskip__ = False
46 ... __debuggerskip__ = False
47 ... result = function(*args, **kwargs)
47 ... result = function(*args, **kwargs)
48 ... __debuggerskip__ = True
48 ... __debuggerskip__ = True
49 ... helper_2()
49 ... helper_2()
50 ... # setting __debuggerskip__ to False again is not necessary
50 ... # setting __debuggerskip__ to False again is not necessary
51 ... return result
51 ... return result
52 ...
52 ...
53 ... return wrapped_fn
53 ... return wrapped_fn
54
54
55 When decorating a function, ipdb will directly step into ``bar()`` by
55 When decorating a function, ipdb will directly step into ``bar()`` by
56 default:
56 default:
57
57
58 >>> @foo_decorator
58 >>> @foo_decorator
59 ... def bar(x, y):
59 ... def bar(x, y):
60 ... return x * y
60 ... return x * y
61
61
62
62
63 You can toggle the behavior with
63 You can toggle the behavior with
64
64
65 ipdb> skip_predicates debuggerskip false
65 ipdb> skip_predicates debuggerskip false
66
66
67 or configure it in your ``.pdbrc``
67 or configure it in your ``.pdbrc``
68
68
69
69
70
70
71 License
71 License
72 -------
72 -------
73
73
74 Modified from the standard pdb.Pdb class to avoid including readline, so that
74 Modified from the standard pdb.Pdb class to avoid including readline, so that
75 the command line completion of other programs which include this isn't
75 the command line completion of other programs which include this isn't
76 damaged.
76 damaged.
77
77
78 In the future, this class will be expanded with improvements over the standard
78 In the future, this class will be expanded with improvements over the standard
79 pdb.
79 pdb.
80
80
81 The original code in this file is mainly lifted out of cmd.py in Python 2.2,
81 The original code in this file is mainly lifted out of cmd.py in Python 2.2,
82 with minor changes. Licensing should therefore be under the standard Python
82 with minor changes. Licensing should therefore be under the standard Python
83 terms. For details on the PSF (Python Software Foundation) standard license,
83 terms. For details on the PSF (Python Software Foundation) standard license,
84 see:
84 see:
85
85
86 https://docs.python.org/2/license.html
86 https://docs.python.org/2/license.html
87
87
88
88
89 All the changes since then are under the same license as IPython.
89 All the changes since then are under the same license as IPython.
90
90
91 """
91 """
92
92
93 #*****************************************************************************
93 #*****************************************************************************
94 #
94 #
95 # This file is licensed under the PSF license.
95 # This file is licensed under the PSF license.
96 #
96 #
97 # Copyright (C) 2001 Python Software Foundation, www.python.org
97 # Copyright (C) 2001 Python Software Foundation, www.python.org
98 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
98 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
99 #
99 #
100 #
100 #
101 #*****************************************************************************
101 #*****************************************************************************
102
102
103 from __future__ import annotations
103 from __future__ import annotations
104
104
105 import inspect
105 import inspect
106 import linecache
106 import linecache
107 import os
107 import os
108 import re
108 import re
109 import sys
109 import sys
110 from contextlib import contextmanager
110 from contextlib import contextmanager
111 from functools import lru_cache
111 from functools import lru_cache
112
112
113 from IPython import get_ipython
113 from IPython import get_ipython
114 from IPython.core.excolors import exception_colors
114 from IPython.core.excolors import exception_colors
115 from IPython.utils import PyColorize, coloransi, py3compat
115 from IPython.utils import PyColorize, coloransi, py3compat
116
116
117 from typing import TYPE_CHECKING
117 from typing import TYPE_CHECKING
118
118
119 if TYPE_CHECKING:
119 if TYPE_CHECKING:
120 # otherwise circular import
120 # otherwise circular import
121 from IPython.core.interactiveshell import InteractiveShell
121 from IPython.core.interactiveshell import InteractiveShell
122
122
123 # skip module docstests
123 # skip module docstests
124 __skip_doctest__ = True
124 __skip_doctest__ = True
125
125
126 prompt = 'ipdb> '
126 prompt = 'ipdb> '
127
127
128 # We have to check this directly from sys.argv, config struct not yet available
128 # We have to check this directly from sys.argv, config struct not yet available
129 from pdb import Pdb as OldPdb
129 from pdb import Pdb as OldPdb
130
130
131 # Allow the set_trace code to operate outside of an ipython instance, even if
131 # Allow the set_trace code to operate outside of an ipython instance, even if
132 # it does so with some limitations. The rest of this support is implemented in
132 # it does so with some limitations. The rest of this support is implemented in
133 # the Tracer constructor.
133 # the Tracer constructor.
134
134
135 DEBUGGERSKIP = "__debuggerskip__"
135 DEBUGGERSKIP = "__debuggerskip__"
136
136
137
137
138 # this has been implemented in Pdb in Python 3.13 (https://github.com/python/cpython/pull/106676
138 # this has been implemented in Pdb in Python 3.13 (https://github.com/python/cpython/pull/106676
139 # on lower python versions, we backported the feature.
139 # on lower python versions, we backported the feature.
140 CHAIN_EXCEPTIONS = sys.version_info < (3, 13)
140 CHAIN_EXCEPTIONS = sys.version_info < (3, 13)
141
141
142
142
143 def make_arrow(pad):
143 def make_arrow(pad):
144 """generate the leading arrow in front of traceback or debugger"""
144 """generate the leading arrow in front of traceback or debugger"""
145 if pad >= 2:
145 if pad >= 2:
146 return '-'*(pad-2) + '> '
146 return '-'*(pad-2) + '> '
147 elif pad == 1:
147 elif pad == 1:
148 return '>'
148 return '>'
149 return ''
149 return ''
150
150
151
151
152 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
153 """Exception hook which handles `BdbQuit` exceptions.
154
155 All other exceptions are processed using the `excepthook`
156 parameter.
157 """
158 raise ValueError(
159 "`BdbQuit_excepthook` is deprecated since version 5.1",
160 )
161
162 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
152 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
163
153
164
154
165 def strip_indentation(multiline_string):
155 def strip_indentation(multiline_string):
166 return RGX_EXTRA_INDENT.sub('', multiline_string)
156 return RGX_EXTRA_INDENT.sub('', multiline_string)
167
157
168
158
169 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
159 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
170 """Make new_fn have old_fn's doc string. This is particularly useful
160 """Make new_fn have old_fn's doc string. This is particularly useful
171 for the ``do_...`` commands that hook into the help system.
161 for the ``do_...`` commands that hook into the help system.
172 Adapted from from a comp.lang.python posting
162 Adapted from from a comp.lang.python posting
173 by Duncan Booth."""
163 by Duncan Booth."""
174 def wrapper(*args, **kw):
164 def wrapper(*args, **kw):
175 return new_fn(*args, **kw)
165 return new_fn(*args, **kw)
176 if old_fn.__doc__:
166 if old_fn.__doc__:
177 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
167 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
178 return wrapper
168 return wrapper
179
169
180
170
181 class Pdb(OldPdb):
171 class Pdb(OldPdb):
182 """Modified Pdb class, does not load readline.
172 """Modified Pdb class, does not load readline.
183
173
184 for a standalone version that uses prompt_toolkit, see
174 for a standalone version that uses prompt_toolkit, see
185 `IPython.terminal.debugger.TerminalPdb` and
175 `IPython.terminal.debugger.TerminalPdb` and
186 `IPython.terminal.debugger.set_trace()`
176 `IPython.terminal.debugger.set_trace()`
187
177
188
178
189 This debugger can hide and skip frames that are tagged according to some predicates.
179 This debugger can hide and skip frames that are tagged according to some predicates.
190 See the `skip_predicates` commands.
180 See the `skip_predicates` commands.
191
181
192 """
182 """
193
183
194 shell: InteractiveShell
184 shell: InteractiveShell
195
185
196 if CHAIN_EXCEPTIONS:
186 if CHAIN_EXCEPTIONS:
197 MAX_CHAINED_EXCEPTION_DEPTH = 999
187 MAX_CHAINED_EXCEPTION_DEPTH = 999
198
188
199 default_predicates = {
189 default_predicates = {
200 "tbhide": True,
190 "tbhide": True,
201 "readonly": False,
191 "readonly": False,
202 "ipython_internal": True,
192 "ipython_internal": True,
203 "debuggerskip": True,
193 "debuggerskip": True,
204 }
194 }
205
195
206 def __init__(self, completekey=None, stdin=None, stdout=None, context=5, **kwargs):
196 def __init__(self, completekey=None, stdin=None, stdout=None, context=5, **kwargs):
207 """Create a new IPython debugger.
197 """Create a new IPython debugger.
208
198
209 Parameters
199 Parameters
210 ----------
200 ----------
211 completekey : default None
201 completekey : default None
212 Passed to pdb.Pdb.
202 Passed to pdb.Pdb.
213 stdin : default None
203 stdin : default None
214 Passed to pdb.Pdb.
204 Passed to pdb.Pdb.
215 stdout : default None
205 stdout : default None
216 Passed to pdb.Pdb.
206 Passed to pdb.Pdb.
217 context : int
207 context : int
218 Number of lines of source code context to show when
208 Number of lines of source code context to show when
219 displaying stacktrace information.
209 displaying stacktrace information.
220 **kwargs
210 **kwargs
221 Passed to pdb.Pdb.
211 Passed to pdb.Pdb.
222
212
223 Notes
213 Notes
224 -----
214 -----
225 The possibilities are python version dependent, see the python
215 The possibilities are python version dependent, see the python
226 docs for more info.
216 docs for more info.
227 """
217 """
228
218
229 # Parent constructor:
219 # Parent constructor:
230 try:
220 try:
231 self.context = int(context)
221 self.context = int(context)
232 if self.context <= 0:
222 if self.context <= 0:
233 raise ValueError("Context must be a positive integer")
223 raise ValueError("Context must be a positive integer")
234 except (TypeError, ValueError) as e:
224 except (TypeError, ValueError) as e:
235 raise ValueError("Context must be a positive integer") from e
225 raise ValueError("Context must be a positive integer") from e
236
226
237 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
227 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
238 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
228 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
239
229
240 # IPython changes...
230 # IPython changes...
241 self.shell = get_ipython()
231 self.shell = get_ipython()
242
232
243 if self.shell is None:
233 if self.shell is None:
244 save_main = sys.modules['__main__']
234 save_main = sys.modules['__main__']
245 # No IPython instance running, we must create one
235 # No IPython instance running, we must create one
246 from IPython.terminal.interactiveshell import \
236 from IPython.terminal.interactiveshell import \
247 TerminalInteractiveShell
237 TerminalInteractiveShell
248 self.shell = TerminalInteractiveShell.instance()
238 self.shell = TerminalInteractiveShell.instance()
249 # needed by any code which calls __import__("__main__") after
239 # needed by any code which calls __import__("__main__") after
250 # the debugger was entered. See also #9941.
240 # the debugger was entered. See also #9941.
251 sys.modules["__main__"] = save_main
241 sys.modules["__main__"] = save_main
252
242
253
243
254 color_scheme = self.shell.colors
244 color_scheme = self.shell.colors
255
245
256 self.aliases = {}
246 self.aliases = {}
257
247
258 # Create color table: we copy the default one from the traceback
248 # Create color table: we copy the default one from the traceback
259 # module and add a few attributes needed for debugging
249 # module and add a few attributes needed for debugging
260 self.color_scheme_table = exception_colors()
250 self.color_scheme_table = exception_colors()
261
251
262 # shorthands
252 # shorthands
263 C = coloransi.TermColors
253 C = coloransi.TermColors
264 cst = self.color_scheme_table
254 cst = self.color_scheme_table
265
255
266
256
267 # Add a python parser so we can syntax highlight source while
257 # Add a python parser so we can syntax highlight source while
268 # debugging.
258 # debugging.
269 self.parser = PyColorize.Parser(style=color_scheme)
259 self.parser = PyColorize.Parser(style=color_scheme)
270 self.set_colors(color_scheme)
260 self.set_colors(color_scheme)
271
261
272 # Set the prompt - the default prompt is '(Pdb)'
262 # Set the prompt - the default prompt is '(Pdb)'
273 self.prompt = prompt
263 self.prompt = prompt
274 self.skip_hidden = True
264 self.skip_hidden = True
275 self.report_skipped = True
265 self.report_skipped = True
276
266
277 # list of predicates we use to skip frames
267 # list of predicates we use to skip frames
278 self._predicates = self.default_predicates
268 self._predicates = self.default_predicates
279
269
280 if CHAIN_EXCEPTIONS:
270 if CHAIN_EXCEPTIONS:
281 self._chained_exceptions = tuple()
271 self._chained_exceptions = tuple()
282 self._chained_exception_index = 0
272 self._chained_exception_index = 0
283
273
284 #
274 #
285 def set_colors(self, scheme):
275 def set_colors(self, scheme):
286 """Shorthand access to the color table scheme selector method."""
276 """Shorthand access to the color table scheme selector method."""
287 self.color_scheme_table.set_active_scheme(scheme)
277 self.color_scheme_table.set_active_scheme(scheme)
288 self.parser.style = scheme
278 self.parser.style = scheme
289
279
290 def set_trace(self, frame=None):
280 def set_trace(self, frame=None):
291 if frame is None:
281 if frame is None:
292 frame = sys._getframe().f_back
282 frame = sys._getframe().f_back
293 self.initial_frame = frame
283 self.initial_frame = frame
294 return super().set_trace(frame)
284 return super().set_trace(frame)
295
285
296 def _hidden_predicate(self, frame):
286 def _hidden_predicate(self, frame):
297 """
287 """
298 Given a frame return whether it it should be hidden or not by IPython.
288 Given a frame return whether it it should be hidden or not by IPython.
299 """
289 """
300
290
301 if self._predicates["readonly"]:
291 if self._predicates["readonly"]:
302 fname = frame.f_code.co_filename
292 fname = frame.f_code.co_filename
303 # we need to check for file existence and interactively define
293 # we need to check for file existence and interactively define
304 # function would otherwise appear as RO.
294 # function would otherwise appear as RO.
305 if os.path.isfile(fname) and not os.access(fname, os.W_OK):
295 if os.path.isfile(fname) and not os.access(fname, os.W_OK):
306 return True
296 return True
307
297
308 if self._predicates["tbhide"]:
298 if self._predicates["tbhide"]:
309 if frame in (self.curframe, getattr(self, "initial_frame", None)):
299 if frame in (self.curframe, getattr(self, "initial_frame", None)):
310 return False
300 return False
311 frame_locals = self._get_frame_locals(frame)
301 frame_locals = self._get_frame_locals(frame)
312 if "__tracebackhide__" not in frame_locals:
302 if "__tracebackhide__" not in frame_locals:
313 return False
303 return False
314 return frame_locals["__tracebackhide__"]
304 return frame_locals["__tracebackhide__"]
315 return False
305 return False
316
306
317 def hidden_frames(self, stack):
307 def hidden_frames(self, stack):
318 """
308 """
319 Given an index in the stack return whether it should be skipped.
309 Given an index in the stack return whether it should be skipped.
320
310
321 This is used in up/down and where to skip frames.
311 This is used in up/down and where to skip frames.
322 """
312 """
323 # The f_locals dictionary is updated from the actual frame
313 # The f_locals dictionary is updated from the actual frame
324 # locals whenever the .f_locals accessor is called, so we
314 # locals whenever the .f_locals accessor is called, so we
325 # avoid calling it here to preserve self.curframe_locals.
315 # avoid calling it here to preserve self.curframe_locals.
326 # Furthermore, there is no good reason to hide the current frame.
316 # Furthermore, there is no good reason to hide the current frame.
327 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
317 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
328 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
318 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
329 if ip_start and self._predicates["ipython_internal"]:
319 if ip_start and self._predicates["ipython_internal"]:
330 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
320 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
331 return ip_hide
321 return ip_hide
332
322
333 if CHAIN_EXCEPTIONS:
323 if CHAIN_EXCEPTIONS:
334
324
335 def _get_tb_and_exceptions(self, tb_or_exc):
325 def _get_tb_and_exceptions(self, tb_or_exc):
336 """
326 """
337 Given a tracecack or an exception, return a tuple of chained exceptions
327 Given a tracecack or an exception, return a tuple of chained exceptions
338 and current traceback to inspect.
328 and current traceback to inspect.
339 This will deal with selecting the right ``__cause__`` or ``__context__``
329 This will deal with selecting the right ``__cause__`` or ``__context__``
340 as well as handling cycles, and return a flattened list of exceptions we
330 as well as handling cycles, and return a flattened list of exceptions we
341 can jump to with do_exceptions.
331 can jump to with do_exceptions.
342 """
332 """
343 _exceptions = []
333 _exceptions = []
344 if isinstance(tb_or_exc, BaseException):
334 if isinstance(tb_or_exc, BaseException):
345 traceback, current = tb_or_exc.__traceback__, tb_or_exc
335 traceback, current = tb_or_exc.__traceback__, tb_or_exc
346
336
347 while current is not None:
337 while current is not None:
348 if current in _exceptions:
338 if current in _exceptions:
349 break
339 break
350 _exceptions.append(current)
340 _exceptions.append(current)
351 if current.__cause__ is not None:
341 if current.__cause__ is not None:
352 current = current.__cause__
342 current = current.__cause__
353 elif (
343 elif (
354 current.__context__ is not None
344 current.__context__ is not None
355 and not current.__suppress_context__
345 and not current.__suppress_context__
356 ):
346 ):
357 current = current.__context__
347 current = current.__context__
358
348
359 if len(_exceptions) >= self.MAX_CHAINED_EXCEPTION_DEPTH:
349 if len(_exceptions) >= self.MAX_CHAINED_EXCEPTION_DEPTH:
360 self.message(
350 self.message(
361 f"More than {self.MAX_CHAINED_EXCEPTION_DEPTH}"
351 f"More than {self.MAX_CHAINED_EXCEPTION_DEPTH}"
362 " chained exceptions found, not all exceptions"
352 " chained exceptions found, not all exceptions"
363 "will be browsable with `exceptions`."
353 "will be browsable with `exceptions`."
364 )
354 )
365 break
355 break
366 else:
356 else:
367 traceback = tb_or_exc
357 traceback = tb_or_exc
368 return tuple(reversed(_exceptions)), traceback
358 return tuple(reversed(_exceptions)), traceback
369
359
370 @contextmanager
360 @contextmanager
371 def _hold_exceptions(self, exceptions):
361 def _hold_exceptions(self, exceptions):
372 """
362 """
373 Context manager to ensure proper cleaning of exceptions references
363 Context manager to ensure proper cleaning of exceptions references
374 When given a chained exception instead of a traceback,
364 When given a chained exception instead of a traceback,
375 pdb may hold references to many objects which may leak memory.
365 pdb may hold references to many objects which may leak memory.
376 We use this context manager to make sure everything is properly cleaned
366 We use this context manager to make sure everything is properly cleaned
377 """
367 """
378 try:
368 try:
379 self._chained_exceptions = exceptions
369 self._chained_exceptions = exceptions
380 self._chained_exception_index = len(exceptions) - 1
370 self._chained_exception_index = len(exceptions) - 1
381 yield
371 yield
382 finally:
372 finally:
383 # we can't put those in forget as otherwise they would
373 # we can't put those in forget as otherwise they would
384 # be cleared on exception change
374 # be cleared on exception change
385 self._chained_exceptions = tuple()
375 self._chained_exceptions = tuple()
386 self._chained_exception_index = 0
376 self._chained_exception_index = 0
387
377
388 def do_exceptions(self, arg):
378 def do_exceptions(self, arg):
389 """exceptions [number]
379 """exceptions [number]
390 List or change current exception in an exception chain.
380 List or change current exception in an exception chain.
391 Without arguments, list all the current exception in the exception
381 Without arguments, list all the current exception in the exception
392 chain. Exceptions will be numbered, with the current exception indicated
382 chain. Exceptions will be numbered, with the current exception indicated
393 with an arrow.
383 with an arrow.
394 If given an integer as argument, switch to the exception at that index.
384 If given an integer as argument, switch to the exception at that index.
395 """
385 """
396 if not self._chained_exceptions:
386 if not self._chained_exceptions:
397 self.message(
387 self.message(
398 "Did not find chained exceptions. To move between"
388 "Did not find chained exceptions. To move between"
399 " exceptions, pdb/post_mortem must be given an exception"
389 " exceptions, pdb/post_mortem must be given an exception"
400 " object rather than a traceback."
390 " object rather than a traceback."
401 )
391 )
402 return
392 return
403 if not arg:
393 if not arg:
404 for ix, exc in enumerate(self._chained_exceptions):
394 for ix, exc in enumerate(self._chained_exceptions):
405 prompt = ">" if ix == self._chained_exception_index else " "
395 prompt = ">" if ix == self._chained_exception_index else " "
406 rep = repr(exc)
396 rep = repr(exc)
407 if len(rep) > 80:
397 if len(rep) > 80:
408 rep = rep[:77] + "..."
398 rep = rep[:77] + "..."
409 indicator = (
399 indicator = (
410 " -"
400 " -"
411 if self._chained_exceptions[ix].__traceback__ is None
401 if self._chained_exceptions[ix].__traceback__ is None
412 else f"{ix:>3}"
402 else f"{ix:>3}"
413 )
403 )
414 self.message(f"{prompt} {indicator} {rep}")
404 self.message(f"{prompt} {indicator} {rep}")
415 else:
405 else:
416 try:
406 try:
417 number = int(arg)
407 number = int(arg)
418 except ValueError:
408 except ValueError:
419 self.error("Argument must be an integer")
409 self.error("Argument must be an integer")
420 return
410 return
421 if 0 <= number < len(self._chained_exceptions):
411 if 0 <= number < len(self._chained_exceptions):
422 if self._chained_exceptions[number].__traceback__ is None:
412 if self._chained_exceptions[number].__traceback__ is None:
423 self.error(
413 self.error(
424 "This exception does not have a traceback, cannot jump to it"
414 "This exception does not have a traceback, cannot jump to it"
425 )
415 )
426 return
416 return
427
417
428 self._chained_exception_index = number
418 self._chained_exception_index = number
429 self.setup(None, self._chained_exceptions[number].__traceback__)
419 self.setup(None, self._chained_exceptions[number].__traceback__)
430 self.print_stack_entry(self.stack[self.curindex])
420 self.print_stack_entry(self.stack[self.curindex])
431 else:
421 else:
432 self.error("No exception with that number")
422 self.error("No exception with that number")
433
423
434 def interaction(self, frame, tb_or_exc):
424 def interaction(self, frame, tb_or_exc):
435 try:
425 try:
436 if CHAIN_EXCEPTIONS:
426 if CHAIN_EXCEPTIONS:
437 # this context manager is part of interaction in 3.13
427 # this context manager is part of interaction in 3.13
438 _chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc)
428 _chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc)
439 if isinstance(tb_or_exc, BaseException):
429 if isinstance(tb_or_exc, BaseException):
440 assert tb is not None, "main exception must have a traceback"
430 assert tb is not None, "main exception must have a traceback"
441 with self._hold_exceptions(_chained_exceptions):
431 with self._hold_exceptions(_chained_exceptions):
442 OldPdb.interaction(self, frame, tb)
432 OldPdb.interaction(self, frame, tb)
443 else:
433 else:
444 OldPdb.interaction(self, frame, tb_or_exc)
434 OldPdb.interaction(self, frame, tb_or_exc)
445
435
446 except KeyboardInterrupt:
436 except KeyboardInterrupt:
447 self.stdout.write("\n" + self.shell.get_exception_only())
437 self.stdout.write("\n" + self.shell.get_exception_only())
448
438
449 def precmd(self, line):
439 def precmd(self, line):
450 """Perform useful escapes on the command before it is executed."""
440 """Perform useful escapes on the command before it is executed."""
451
441
452 if line.endswith("??"):
442 if line.endswith("??"):
453 line = "pinfo2 " + line[:-2]
443 line = "pinfo2 " + line[:-2]
454 elif line.endswith("?"):
444 elif line.endswith("?"):
455 line = "pinfo " + line[:-1]
445 line = "pinfo " + line[:-1]
456
446
457 line = super().precmd(line)
447 line = super().precmd(line)
458
448
459 return line
449 return line
460
450
461 def new_do_quit(self, arg):
451 def new_do_quit(self, arg):
462 return OldPdb.do_quit(self, arg)
452 return OldPdb.do_quit(self, arg)
463
453
464 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
454 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
465
455
466 def print_stack_trace(self, context=None):
456 def print_stack_trace(self, context=None):
467 Colors = self.color_scheme_table.active_colors
457 Colors = self.color_scheme_table.active_colors
468 ColorsNormal = Colors.Normal
458 ColorsNormal = Colors.Normal
469 if context is None:
459 if context is None:
470 context = self.context
460 context = self.context
471 try:
461 try:
472 context = int(context)
462 context = int(context)
473 if context <= 0:
463 if context <= 0:
474 raise ValueError("Context must be a positive integer")
464 raise ValueError("Context must be a positive integer")
475 except (TypeError, ValueError) as e:
465 except (TypeError, ValueError) as e:
476 raise ValueError("Context must be a positive integer") from e
466 raise ValueError("Context must be a positive integer") from e
477 try:
467 try:
478 skipped = 0
468 skipped = 0
479 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
469 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
480 if hidden and self.skip_hidden:
470 if hidden and self.skip_hidden:
481 skipped += 1
471 skipped += 1
482 continue
472 continue
483 if skipped:
473 if skipped:
484 print(
474 print(
485 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
475 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
486 )
476 )
487 skipped = 0
477 skipped = 0
488 self.print_stack_entry(frame_lineno, context=context)
478 self.print_stack_entry(frame_lineno, context=context)
489 if skipped:
479 if skipped:
490 print(
480 print(
491 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
481 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
492 )
482 )
493 except KeyboardInterrupt:
483 except KeyboardInterrupt:
494 pass
484 pass
495
485
496 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
486 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
497 context=None):
487 context=None):
498 if context is None:
488 if context is None:
499 context = self.context
489 context = self.context
500 try:
490 try:
501 context = int(context)
491 context = int(context)
502 if context <= 0:
492 if context <= 0:
503 raise ValueError("Context must be a positive integer")
493 raise ValueError("Context must be a positive integer")
504 except (TypeError, ValueError) as e:
494 except (TypeError, ValueError) as e:
505 raise ValueError("Context must be a positive integer") from e
495 raise ValueError("Context must be a positive integer") from e
506 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
496 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
507
497
508 # vds: >>
498 # vds: >>
509 frame, lineno = frame_lineno
499 frame, lineno = frame_lineno
510 filename = frame.f_code.co_filename
500 filename = frame.f_code.co_filename
511 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
501 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
512 # vds: <<
502 # vds: <<
513
503
514 def _get_frame_locals(self, frame):
504 def _get_frame_locals(self, frame):
515 """ "
505 """ "
516 Accessing f_local of current frame reset the namespace, so we want to avoid
506 Accessing f_local of current frame reset the namespace, so we want to avoid
517 that or the following can happen
507 that or the following can happen
518
508
519 ipdb> foo
509 ipdb> foo
520 "old"
510 "old"
521 ipdb> foo = "new"
511 ipdb> foo = "new"
522 ipdb> foo
512 ipdb> foo
523 "new"
513 "new"
524 ipdb> where
514 ipdb> where
525 ipdb> foo
515 ipdb> foo
526 "old"
516 "old"
527
517
528 So if frame is self.current_frame we instead return self.curframe_locals
518 So if frame is self.current_frame we instead return self.curframe_locals
529
519
530 """
520 """
531 if frame is self.curframe:
521 if frame is self.curframe:
532 return self.curframe_locals
522 return self.curframe_locals
533 else:
523 else:
534 return frame.f_locals
524 return frame.f_locals
535
525
536 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
526 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
537 if context is None:
527 if context is None:
538 context = self.context
528 context = self.context
539 try:
529 try:
540 context = int(context)
530 context = int(context)
541 if context <= 0:
531 if context <= 0:
542 print("Context must be a positive integer", file=self.stdout)
532 print("Context must be a positive integer", file=self.stdout)
543 except (TypeError, ValueError):
533 except (TypeError, ValueError):
544 print("Context must be a positive integer", file=self.stdout)
534 print("Context must be a positive integer", file=self.stdout)
545
535
546 import reprlib
536 import reprlib
547
537
548 ret = []
538 ret = []
549
539
550 Colors = self.color_scheme_table.active_colors
540 Colors = self.color_scheme_table.active_colors
551 ColorsNormal = Colors.Normal
541 ColorsNormal = Colors.Normal
552 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
542 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
553 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
543 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
554 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
544 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
555 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
545 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
556
546
557 frame, lineno = frame_lineno
547 frame, lineno = frame_lineno
558
548
559 return_value = ''
549 return_value = ''
560 loc_frame = self._get_frame_locals(frame)
550 loc_frame = self._get_frame_locals(frame)
561 if "__return__" in loc_frame:
551 if "__return__" in loc_frame:
562 rv = loc_frame["__return__"]
552 rv = loc_frame["__return__"]
563 # return_value += '->'
553 # return_value += '->'
564 return_value += reprlib.repr(rv) + "\n"
554 return_value += reprlib.repr(rv) + "\n"
565 ret.append(return_value)
555 ret.append(return_value)
566
556
567 #s = filename + '(' + `lineno` + ')'
557 #s = filename + '(' + `lineno` + ')'
568 filename = self.canonic(frame.f_code.co_filename)
558 filename = self.canonic(frame.f_code.co_filename)
569 link = tpl_link % py3compat.cast_unicode(filename)
559 link = tpl_link % py3compat.cast_unicode(filename)
570
560
571 if frame.f_code.co_name:
561 if frame.f_code.co_name:
572 func = frame.f_code.co_name
562 func = frame.f_code.co_name
573 else:
563 else:
574 func = "<lambda>"
564 func = "<lambda>"
575
565
576 call = ""
566 call = ""
577 if func != "?":
567 if func != "?":
578 if "__args__" in loc_frame:
568 if "__args__" in loc_frame:
579 args = reprlib.repr(loc_frame["__args__"])
569 args = reprlib.repr(loc_frame["__args__"])
580 else:
570 else:
581 args = '()'
571 args = '()'
582 call = tpl_call % (func, args)
572 call = tpl_call % (func, args)
583
573
584 # The level info should be generated in the same format pdb uses, to
574 # The level info should be generated in the same format pdb uses, to
585 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
575 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
586 if frame is self.curframe:
576 if frame is self.curframe:
587 ret.append('> ')
577 ret.append('> ')
588 else:
578 else:
589 ret.append(" ")
579 ret.append(" ")
590 ret.append("%s(%s)%s\n" % (link, lineno, call))
580 ret.append("%s(%s)%s\n" % (link, lineno, call))
591
581
592 start = lineno - 1 - context//2
582 start = lineno - 1 - context//2
593 lines = linecache.getlines(filename)
583 lines = linecache.getlines(filename)
594 start = min(start, len(lines) - context)
584 start = min(start, len(lines) - context)
595 start = max(start, 0)
585 start = max(start, 0)
596 lines = lines[start : start + context]
586 lines = lines[start : start + context]
597
587
598 for i, line in enumerate(lines):
588 for i, line in enumerate(lines):
599 show_arrow = start + 1 + i == lineno
589 show_arrow = start + 1 + i == lineno
600 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
590 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
601 ret.append(
591 ret.append(
602 self.__format_line(
592 self.__format_line(
603 linetpl, filename, start + 1 + i, line, arrow=show_arrow
593 linetpl, filename, start + 1 + i, line, arrow=show_arrow
604 )
594 )
605 )
595 )
606 return "".join(ret)
596 return "".join(ret)
607
597
608 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
598 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
609 bp_mark = ""
599 bp_mark = ""
610 bp_mark_color = ""
600 bp_mark_color = ""
611
601
612 new_line, err = self.parser.format2(line, 'str')
602 new_line, err = self.parser.format2(line, 'str')
613 if not err:
603 if not err:
614 line = new_line
604 line = new_line
615
605
616 bp = None
606 bp = None
617 if lineno in self.get_file_breaks(filename):
607 if lineno in self.get_file_breaks(filename):
618 bps = self.get_breaks(filename, lineno)
608 bps = self.get_breaks(filename, lineno)
619 bp = bps[-1]
609 bp = bps[-1]
620
610
621 if bp:
611 if bp:
622 Colors = self.color_scheme_table.active_colors
612 Colors = self.color_scheme_table.active_colors
623 bp_mark = str(bp.number)
613 bp_mark = str(bp.number)
624 bp_mark_color = Colors.breakpoint_enabled
614 bp_mark_color = Colors.breakpoint_enabled
625 if not bp.enabled:
615 if not bp.enabled:
626 bp_mark_color = Colors.breakpoint_disabled
616 bp_mark_color = Colors.breakpoint_disabled
627
617
628 numbers_width = 7
618 numbers_width = 7
629 if arrow:
619 if arrow:
630 # This is the line with the error
620 # This is the line with the error
631 pad = numbers_width - len(str(lineno)) - len(bp_mark)
621 pad = numbers_width - len(str(lineno)) - len(bp_mark)
632 num = '%s%s' % (make_arrow(pad), str(lineno))
622 num = '%s%s' % (make_arrow(pad), str(lineno))
633 else:
623 else:
634 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
624 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
635
625
636 return tpl_line % (bp_mark_color + bp_mark, num, line)
626 return tpl_line % (bp_mark_color + bp_mark, num, line)
637
627
638 def print_list_lines(self, filename, first, last):
628 def print_list_lines(self, filename, first, last):
639 """The printing (as opposed to the parsing part of a 'list'
629 """The printing (as opposed to the parsing part of a 'list'
640 command."""
630 command."""
641 try:
631 try:
642 Colors = self.color_scheme_table.active_colors
632 Colors = self.color_scheme_table.active_colors
643 ColorsNormal = Colors.Normal
633 ColorsNormal = Colors.Normal
644 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
634 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
645 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
635 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
646 src = []
636 src = []
647 if filename == "<string>" and hasattr(self, "_exec_filename"):
637 if filename == "<string>" and hasattr(self, "_exec_filename"):
648 filename = self._exec_filename
638 filename = self._exec_filename
649
639
650 for lineno in range(first, last+1):
640 for lineno in range(first, last+1):
651 line = linecache.getline(filename, lineno)
641 line = linecache.getline(filename, lineno)
652 if not line:
642 if not line:
653 break
643 break
654
644
655 if lineno == self.curframe.f_lineno:
645 if lineno == self.curframe.f_lineno:
656 line = self.__format_line(
646 line = self.__format_line(
657 tpl_line_em, filename, lineno, line, arrow=True
647 tpl_line_em, filename, lineno, line, arrow=True
658 )
648 )
659 else:
649 else:
660 line = self.__format_line(
650 line = self.__format_line(
661 tpl_line, filename, lineno, line, arrow=False
651 tpl_line, filename, lineno, line, arrow=False
662 )
652 )
663
653
664 src.append(line)
654 src.append(line)
665 self.lineno = lineno
655 self.lineno = lineno
666
656
667 print(''.join(src), file=self.stdout)
657 print(''.join(src), file=self.stdout)
668
658
669 except KeyboardInterrupt:
659 except KeyboardInterrupt:
670 pass
660 pass
671
661
672 def do_skip_predicates(self, args):
662 def do_skip_predicates(self, args):
673 """
663 """
674 Turn on/off individual predicates as to whether a frame should be hidden/skip.
664 Turn on/off individual predicates as to whether a frame should be hidden/skip.
675
665
676 The global option to skip (or not) hidden frames is set with skip_hidden
666 The global option to skip (or not) hidden frames is set with skip_hidden
677
667
678 To change the value of a predicate
668 To change the value of a predicate
679
669
680 skip_predicates key [true|false]
670 skip_predicates key [true|false]
681
671
682 Call without arguments to see the current values.
672 Call without arguments to see the current values.
683
673
684 To permanently change the value of an option add the corresponding
674 To permanently change the value of an option add the corresponding
685 command to your ``~/.pdbrc`` file. If you are programmatically using the
675 command to your ``~/.pdbrc`` file. If you are programmatically using the
686 Pdb instance you can also change the ``default_predicates`` class
676 Pdb instance you can also change the ``default_predicates`` class
687 attribute.
677 attribute.
688 """
678 """
689 if not args.strip():
679 if not args.strip():
690 print("current predicates:")
680 print("current predicates:")
691 for p, v in self._predicates.items():
681 for p, v in self._predicates.items():
692 print(" ", p, ":", v)
682 print(" ", p, ":", v)
693 return
683 return
694 type_value = args.strip().split(" ")
684 type_value = args.strip().split(" ")
695 if len(type_value) != 2:
685 if len(type_value) != 2:
696 print(
686 print(
697 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
687 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
698 )
688 )
699 return
689 return
700
690
701 type_, value = type_value
691 type_, value = type_value
702 if type_ not in self._predicates:
692 if type_ not in self._predicates:
703 print(f"{type_!r} not in {set(self._predicates.keys())}")
693 print(f"{type_!r} not in {set(self._predicates.keys())}")
704 return
694 return
705 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
695 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
706 print(
696 print(
707 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
697 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
708 )
698 )
709 return
699 return
710
700
711 self._predicates[type_] = value.lower() in ("true", "yes", "1")
701 self._predicates[type_] = value.lower() in ("true", "yes", "1")
712 if not any(self._predicates.values()):
702 if not any(self._predicates.values()):
713 print(
703 print(
714 "Warning, all predicates set to False, skip_hidden may not have any effects."
704 "Warning, all predicates set to False, skip_hidden may not have any effects."
715 )
705 )
716
706
717 def do_skip_hidden(self, arg):
707 def do_skip_hidden(self, arg):
718 """
708 """
719 Change whether or not we should skip frames with the
709 Change whether or not we should skip frames with the
720 __tracebackhide__ attribute.
710 __tracebackhide__ attribute.
721 """
711 """
722 if not arg.strip():
712 if not arg.strip():
723 print(
713 print(
724 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
714 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
725 )
715 )
726 elif arg.strip().lower() in ("true", "yes"):
716 elif arg.strip().lower() in ("true", "yes"):
727 self.skip_hidden = True
717 self.skip_hidden = True
728 elif arg.strip().lower() in ("false", "no"):
718 elif arg.strip().lower() in ("false", "no"):
729 self.skip_hidden = False
719 self.skip_hidden = False
730 if not any(self._predicates.values()):
720 if not any(self._predicates.values()):
731 print(
721 print(
732 "Warning, all predicates set to False, skip_hidden may not have any effects."
722 "Warning, all predicates set to False, skip_hidden may not have any effects."
733 )
723 )
734
724
735 def do_list(self, arg):
725 def do_list(self, arg):
736 """Print lines of code from the current stack frame
726 """Print lines of code from the current stack frame
737 """
727 """
738 self.lastcmd = 'list'
728 self.lastcmd = 'list'
739 last = None
729 last = None
740 if arg and arg != ".":
730 if arg and arg != ".":
741 try:
731 try:
742 x = eval(arg, {}, {})
732 x = eval(arg, {}, {})
743 if type(x) == type(()):
733 if type(x) == type(()):
744 first, last = x
734 first, last = x
745 first = int(first)
735 first = int(first)
746 last = int(last)
736 last = int(last)
747 if last < first:
737 if last < first:
748 # Assume it's a count
738 # Assume it's a count
749 last = first + last
739 last = first + last
750 else:
740 else:
751 first = max(1, int(x) - 5)
741 first = max(1, int(x) - 5)
752 except:
742 except:
753 print('*** Error in argument:', repr(arg), file=self.stdout)
743 print('*** Error in argument:', repr(arg), file=self.stdout)
754 return
744 return
755 elif self.lineno is None or arg == ".":
745 elif self.lineno is None or arg == ".":
756 first = max(1, self.curframe.f_lineno - 5)
746 first = max(1, self.curframe.f_lineno - 5)
757 else:
747 else:
758 first = self.lineno + 1
748 first = self.lineno + 1
759 if last is None:
749 if last is None:
760 last = first + 10
750 last = first + 10
761 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
751 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
762
752
763 # vds: >>
753 # vds: >>
764 lineno = first
754 lineno = first
765 filename = self.curframe.f_code.co_filename
755 filename = self.curframe.f_code.co_filename
766 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
756 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
767 # vds: <<
757 # vds: <<
768
758
769 do_l = do_list
759 do_l = do_list
770
760
771 def getsourcelines(self, obj):
761 def getsourcelines(self, obj):
772 lines, lineno = inspect.findsource(obj)
762 lines, lineno = inspect.findsource(obj)
773 if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj):
763 if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj):
774 # must be a module frame: do not try to cut a block out of it
764 # must be a module frame: do not try to cut a block out of it
775 return lines, 1
765 return lines, 1
776 elif inspect.ismodule(obj):
766 elif inspect.ismodule(obj):
777 return lines, 1
767 return lines, 1
778 return inspect.getblock(lines[lineno:]), lineno+1
768 return inspect.getblock(lines[lineno:]), lineno+1
779
769
780 def do_longlist(self, arg):
770 def do_longlist(self, arg):
781 """Print lines of code from the current stack frame.
771 """Print lines of code from the current stack frame.
782
772
783 Shows more lines than 'list' does.
773 Shows more lines than 'list' does.
784 """
774 """
785 self.lastcmd = 'longlist'
775 self.lastcmd = 'longlist'
786 try:
776 try:
787 lines, lineno = self.getsourcelines(self.curframe)
777 lines, lineno = self.getsourcelines(self.curframe)
788 except OSError as err:
778 except OSError as err:
789 self.error(err)
779 self.error(err)
790 return
780 return
791 last = lineno + len(lines)
781 last = lineno + len(lines)
792 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
782 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
793 do_ll = do_longlist
783 do_ll = do_longlist
794
784
795 def do_debug(self, arg):
785 def do_debug(self, arg):
796 """debug code
786 """debug code
797 Enter a recursive debugger that steps through the code
787 Enter a recursive debugger that steps through the code
798 argument (which is an arbitrary expression or statement to be
788 argument (which is an arbitrary expression or statement to be
799 executed in the current environment).
789 executed in the current environment).
800 """
790 """
801 trace_function = sys.gettrace()
791 trace_function = sys.gettrace()
802 sys.settrace(None)
792 sys.settrace(None)
803 globals = self.curframe.f_globals
793 globals = self.curframe.f_globals
804 locals = self.curframe_locals
794 locals = self.curframe_locals
805 p = self.__class__(completekey=self.completekey,
795 p = self.__class__(completekey=self.completekey,
806 stdin=self.stdin, stdout=self.stdout)
796 stdin=self.stdin, stdout=self.stdout)
807 p.use_rawinput = self.use_rawinput
797 p.use_rawinput = self.use_rawinput
808 p.prompt = "(%s) " % self.prompt.strip()
798 p.prompt = "(%s) " % self.prompt.strip()
809 self.message("ENTERING RECURSIVE DEBUGGER")
799 self.message("ENTERING RECURSIVE DEBUGGER")
810 sys.call_tracing(p.run, (arg, globals, locals))
800 sys.call_tracing(p.run, (arg, globals, locals))
811 self.message("LEAVING RECURSIVE DEBUGGER")
801 self.message("LEAVING RECURSIVE DEBUGGER")
812 sys.settrace(trace_function)
802 sys.settrace(trace_function)
813 self.lastcmd = p.lastcmd
803 self.lastcmd = p.lastcmd
814
804
815 def do_pdef(self, arg):
805 def do_pdef(self, arg):
816 """Print the call signature for any callable object.
806 """Print the call signature for any callable object.
817
807
818 The debugger interface to %pdef"""
808 The debugger interface to %pdef"""
819 namespaces = [
809 namespaces = [
820 ("Locals", self.curframe_locals),
810 ("Locals", self.curframe_locals),
821 ("Globals", self.curframe.f_globals),
811 ("Globals", self.curframe.f_globals),
822 ]
812 ]
823 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
813 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
824
814
825 def do_pdoc(self, arg):
815 def do_pdoc(self, arg):
826 """Print the docstring for an object.
816 """Print the docstring for an object.
827
817
828 The debugger interface to %pdoc."""
818 The debugger interface to %pdoc."""
829 namespaces = [
819 namespaces = [
830 ("Locals", self.curframe_locals),
820 ("Locals", self.curframe_locals),
831 ("Globals", self.curframe.f_globals),
821 ("Globals", self.curframe.f_globals),
832 ]
822 ]
833 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
823 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
834
824
835 def do_pfile(self, arg):
825 def do_pfile(self, arg):
836 """Print (or run through pager) the file where an object is defined.
826 """Print (or run through pager) the file where an object is defined.
837
827
838 The debugger interface to %pfile.
828 The debugger interface to %pfile.
839 """
829 """
840 namespaces = [
830 namespaces = [
841 ("Locals", self.curframe_locals),
831 ("Locals", self.curframe_locals),
842 ("Globals", self.curframe.f_globals),
832 ("Globals", self.curframe.f_globals),
843 ]
833 ]
844 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
834 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
845
835
846 def do_pinfo(self, arg):
836 def do_pinfo(self, arg):
847 """Provide detailed information about an object.
837 """Provide detailed information about an object.
848
838
849 The debugger interface to %pinfo, i.e., obj?."""
839 The debugger interface to %pinfo, i.e., obj?."""
850 namespaces = [
840 namespaces = [
851 ("Locals", self.curframe_locals),
841 ("Locals", self.curframe_locals),
852 ("Globals", self.curframe.f_globals),
842 ("Globals", self.curframe.f_globals),
853 ]
843 ]
854 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
844 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
855
845
856 def do_pinfo2(self, arg):
846 def do_pinfo2(self, arg):
857 """Provide extra detailed information about an object.
847 """Provide extra detailed information about an object.
858
848
859 The debugger interface to %pinfo2, i.e., obj??."""
849 The debugger interface to %pinfo2, i.e., obj??."""
860 namespaces = [
850 namespaces = [
861 ("Locals", self.curframe_locals),
851 ("Locals", self.curframe_locals),
862 ("Globals", self.curframe.f_globals),
852 ("Globals", self.curframe.f_globals),
863 ]
853 ]
864 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
854 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
865
855
866 def do_psource(self, arg):
856 def do_psource(self, arg):
867 """Print (or run through pager) the source code for an object."""
857 """Print (or run through pager) the source code for an object."""
868 namespaces = [
858 namespaces = [
869 ("Locals", self.curframe_locals),
859 ("Locals", self.curframe_locals),
870 ("Globals", self.curframe.f_globals),
860 ("Globals", self.curframe.f_globals),
871 ]
861 ]
872 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
862 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
873
863
874 def do_where(self, arg):
864 def do_where(self, arg):
875 """w(here)
865 """w(here)
876 Print a stack trace, with the most recent frame at the bottom.
866 Print a stack trace, with the most recent frame at the bottom.
877 An arrow indicates the "current frame", which determines the
867 An arrow indicates the "current frame", which determines the
878 context of most commands. 'bt' is an alias for this command.
868 context of most commands. 'bt' is an alias for this command.
879
869
880 Take a number as argument as an (optional) number of context line to
870 Take a number as argument as an (optional) number of context line to
881 print"""
871 print"""
882 if arg:
872 if arg:
883 try:
873 try:
884 context = int(arg)
874 context = int(arg)
885 except ValueError as err:
875 except ValueError as err:
886 self.error(err)
876 self.error(err)
887 return
877 return
888 self.print_stack_trace(context)
878 self.print_stack_trace(context)
889 else:
879 else:
890 self.print_stack_trace()
880 self.print_stack_trace()
891
881
892 do_w = do_where
882 do_w = do_where
893
883
894 def break_anywhere(self, frame):
884 def break_anywhere(self, frame):
895 """
885 """
896 _stop_in_decorator_internals is overly restrictive, as we may still want
886 _stop_in_decorator_internals is overly restrictive, as we may still want
897 to trace function calls, so we need to also update break_anywhere so
887 to trace function calls, so we need to also update break_anywhere so
898 that is we don't `stop_here`, because of debugger skip, we may still
888 that is we don't `stop_here`, because of debugger skip, we may still
899 stop at any point inside the function
889 stop at any point inside the function
900
890
901 """
891 """
902
892
903 sup = super().break_anywhere(frame)
893 sup = super().break_anywhere(frame)
904 if sup:
894 if sup:
905 return sup
895 return sup
906 if self._predicates["debuggerskip"]:
896 if self._predicates["debuggerskip"]:
907 if DEBUGGERSKIP in frame.f_code.co_varnames:
897 if DEBUGGERSKIP in frame.f_code.co_varnames:
908 return True
898 return True
909 if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
899 if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
910 return True
900 return True
911 return False
901 return False
912
902
913 def _is_in_decorator_internal_and_should_skip(self, frame):
903 def _is_in_decorator_internal_and_should_skip(self, frame):
914 """
904 """
915 Utility to tell us whether we are in a decorator internal and should stop.
905 Utility to tell us whether we are in a decorator internal and should stop.
916
906
917 """
907 """
918 # if we are disabled don't skip
908 # if we are disabled don't skip
919 if not self._predicates["debuggerskip"]:
909 if not self._predicates["debuggerskip"]:
920 return False
910 return False
921
911
922 return self._cachable_skip(frame)
912 return self._cachable_skip(frame)
923
913
924 @lru_cache
914 @lru_cache
925 def _cachable_skip(self, frame):
915 def _cachable_skip(self, frame):
926 # if frame is tagged, skip by default.
916 # if frame is tagged, skip by default.
927 if DEBUGGERSKIP in frame.f_code.co_varnames:
917 if DEBUGGERSKIP in frame.f_code.co_varnames:
928 return True
918 return True
929
919
930 # if one of the parent frame value set to True skip as well.
920 # if one of the parent frame value set to True skip as well.
931
921
932 cframe = frame
922 cframe = frame
933 while getattr(cframe, "f_back", None):
923 while getattr(cframe, "f_back", None):
934 cframe = cframe.f_back
924 cframe = cframe.f_back
935 if self._get_frame_locals(cframe).get(DEBUGGERSKIP):
925 if self._get_frame_locals(cframe).get(DEBUGGERSKIP):
936 return True
926 return True
937
927
938 return False
928 return False
939
929
940 def stop_here(self, frame):
930 def stop_here(self, frame):
941 if self._is_in_decorator_internal_and_should_skip(frame) is True:
931 if self._is_in_decorator_internal_and_should_skip(frame) is True:
942 return False
932 return False
943
933
944 hidden = False
934 hidden = False
945 if self.skip_hidden:
935 if self.skip_hidden:
946 hidden = self._hidden_predicate(frame)
936 hidden = self._hidden_predicate(frame)
947 if hidden:
937 if hidden:
948 if self.report_skipped:
938 if self.report_skipped:
949 Colors = self.color_scheme_table.active_colors
939 Colors = self.color_scheme_table.active_colors
950 ColorsNormal = Colors.Normal
940 ColorsNormal = Colors.Normal
951 print(
941 print(
952 f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n"
942 f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n"
953 )
943 )
954 return super().stop_here(frame)
944 return super().stop_here(frame)
955
945
956 def do_up(self, arg):
946 def do_up(self, arg):
957 """u(p) [count]
947 """u(p) [count]
958 Move the current frame count (default one) levels up in the
948 Move the current frame count (default one) levels up in the
959 stack trace (to an older frame).
949 stack trace (to an older frame).
960
950
961 Will skip hidden frames.
951 Will skip hidden frames.
962 """
952 """
963 # modified version of upstream that skips
953 # modified version of upstream that skips
964 # frames with __tracebackhide__
954 # frames with __tracebackhide__
965 if self.curindex == 0:
955 if self.curindex == 0:
966 self.error("Oldest frame")
956 self.error("Oldest frame")
967 return
957 return
968 try:
958 try:
969 count = int(arg or 1)
959 count = int(arg or 1)
970 except ValueError:
960 except ValueError:
971 self.error("Invalid frame count (%s)" % arg)
961 self.error("Invalid frame count (%s)" % arg)
972 return
962 return
973 skipped = 0
963 skipped = 0
974 if count < 0:
964 if count < 0:
975 _newframe = 0
965 _newframe = 0
976 else:
966 else:
977 counter = 0
967 counter = 0
978 hidden_frames = self.hidden_frames(self.stack)
968 hidden_frames = self.hidden_frames(self.stack)
979 for i in range(self.curindex - 1, -1, -1):
969 for i in range(self.curindex - 1, -1, -1):
980 if hidden_frames[i] and self.skip_hidden:
970 if hidden_frames[i] and self.skip_hidden:
981 skipped += 1
971 skipped += 1
982 continue
972 continue
983 counter += 1
973 counter += 1
984 if counter >= count:
974 if counter >= count:
985 break
975 break
986 else:
976 else:
987 # if no break occurred.
977 # if no break occurred.
988 self.error(
978 self.error(
989 "all frames above hidden, use `skip_hidden False` to get get into those."
979 "all frames above hidden, use `skip_hidden False` to get get into those."
990 )
980 )
991 return
981 return
992
982
993 Colors = self.color_scheme_table.active_colors
983 Colors = self.color_scheme_table.active_colors
994 ColorsNormal = Colors.Normal
984 ColorsNormal = Colors.Normal
995 _newframe = i
985 _newframe = i
996 self._select_frame(_newframe)
986 self._select_frame(_newframe)
997 if skipped:
987 if skipped:
998 print(
988 print(
999 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
989 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
1000 )
990 )
1001
991
1002 def do_down(self, arg):
992 def do_down(self, arg):
1003 """d(own) [count]
993 """d(own) [count]
1004 Move the current frame count (default one) levels down in the
994 Move the current frame count (default one) levels down in the
1005 stack trace (to a newer frame).
995 stack trace (to a newer frame).
1006
996
1007 Will skip hidden frames.
997 Will skip hidden frames.
1008 """
998 """
1009 if self.curindex + 1 == len(self.stack):
999 if self.curindex + 1 == len(self.stack):
1010 self.error("Newest frame")
1000 self.error("Newest frame")
1011 return
1001 return
1012 try:
1002 try:
1013 count = int(arg or 1)
1003 count = int(arg or 1)
1014 except ValueError:
1004 except ValueError:
1015 self.error("Invalid frame count (%s)" % arg)
1005 self.error("Invalid frame count (%s)" % arg)
1016 return
1006 return
1017 if count < 0:
1007 if count < 0:
1018 _newframe = len(self.stack) - 1
1008 _newframe = len(self.stack) - 1
1019 else:
1009 else:
1020 counter = 0
1010 counter = 0
1021 skipped = 0
1011 skipped = 0
1022 hidden_frames = self.hidden_frames(self.stack)
1012 hidden_frames = self.hidden_frames(self.stack)
1023 for i in range(self.curindex + 1, len(self.stack)):
1013 for i in range(self.curindex + 1, len(self.stack)):
1024 if hidden_frames[i] and self.skip_hidden:
1014 if hidden_frames[i] and self.skip_hidden:
1025 skipped += 1
1015 skipped += 1
1026 continue
1016 continue
1027 counter += 1
1017 counter += 1
1028 if counter >= count:
1018 if counter >= count:
1029 break
1019 break
1030 else:
1020 else:
1031 self.error(
1021 self.error(
1032 "all frames below hidden, use `skip_hidden False` to get get into those."
1022 "all frames below hidden, use `skip_hidden False` to get get into those."
1033 )
1023 )
1034 return
1024 return
1035
1025
1036 Colors = self.color_scheme_table.active_colors
1026 Colors = self.color_scheme_table.active_colors
1037 ColorsNormal = Colors.Normal
1027 ColorsNormal = Colors.Normal
1038 if skipped:
1028 if skipped:
1039 print(
1029 print(
1040 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
1030 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
1041 )
1031 )
1042 _newframe = i
1032 _newframe = i
1043
1033
1044 self._select_frame(_newframe)
1034 self._select_frame(_newframe)
1045
1035
1046 do_d = do_down
1036 do_d = do_down
1047 do_u = do_up
1037 do_u = do_up
1048
1038
1049 def do_context(self, context):
1039 def do_context(self, context):
1050 """context number_of_lines
1040 """context number_of_lines
1051 Set the number of lines of source code to show when displaying
1041 Set the number of lines of source code to show when displaying
1052 stacktrace information.
1042 stacktrace information.
1053 """
1043 """
1054 try:
1044 try:
1055 new_context = int(context)
1045 new_context = int(context)
1056 if new_context <= 0:
1046 if new_context <= 0:
1057 raise ValueError()
1047 raise ValueError()
1058 self.context = new_context
1048 self.context = new_context
1059 except ValueError:
1049 except ValueError:
1060 self.error("The 'context' command requires a positive integer argument.")
1050 self.error("The 'context' command requires a positive integer argument.")
1061
1051
1062
1052
1063 class InterruptiblePdb(Pdb):
1053 class InterruptiblePdb(Pdb):
1064 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
1054 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
1065
1055
1066 def cmdloop(self, intro=None):
1056 def cmdloop(self, intro=None):
1067 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
1057 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
1068 try:
1058 try:
1069 return OldPdb.cmdloop(self, intro=intro)
1059 return OldPdb.cmdloop(self, intro=intro)
1070 except KeyboardInterrupt:
1060 except KeyboardInterrupt:
1071 self.stop_here = lambda frame: False
1061 self.stop_here = lambda frame: False
1072 self.do_quit("")
1062 self.do_quit("")
1073 sys.settrace(None)
1063 sys.settrace(None)
1074 self.quitting = False
1064 self.quitting = False
1075 raise
1065 raise
1076
1066
1077 def _cmdloop(self):
1067 def _cmdloop(self):
1078 while True:
1068 while True:
1079 try:
1069 try:
1080 # keyboard interrupts allow for an easy way to cancel
1070 # keyboard interrupts allow for an easy way to cancel
1081 # the current command, so allow them during interactive input
1071 # the current command, so allow them during interactive input
1082 self.allow_kbdint = True
1072 self.allow_kbdint = True
1083 self.cmdloop()
1073 self.cmdloop()
1084 self.allow_kbdint = False
1074 self.allow_kbdint = False
1085 break
1075 break
1086 except KeyboardInterrupt:
1076 except KeyboardInterrupt:
1087 self.message('--KeyboardInterrupt--')
1077 self.message('--KeyboardInterrupt--')
1088 raise
1078 raise
1089
1079
1090
1080
1091 def set_trace(frame=None, header=None):
1081 def set_trace(frame=None, header=None):
1092 """
1082 """
1093 Start debugging from `frame`.
1083 Start debugging from `frame`.
1094
1084
1095 If frame is not specified, debugging starts from caller's frame.
1085 If frame is not specified, debugging starts from caller's frame.
1096 """
1086 """
1097 pdb = Pdb()
1087 pdb = Pdb()
1098 if header is not None:
1088 if header is not None:
1099 pdb.message(header)
1089 pdb.message(header)
1100 pdb.set_trace(frame or sys._getframe().f_back)
1090 pdb.set_trace(frame or sys._getframe().f_back)
General Comments 0
You need to be logged in to leave comments. Login now