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