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