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