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