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