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