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