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