Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/IPython/core/debugger.py: 15%

554 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:05 +0000

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 

72License 

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 inspect 

105import linecache 

106import sys 

107import re 

108import os 

109 

110from IPython import get_ipython 

111from contextlib import contextmanager 

112from IPython.utils import PyColorize 

113from IPython.utils import coloransi, py3compat 

114from IPython.core.excolors import exception_colors 

115 

116# skip module docstests 

117__skip_doctest__ = True 

118 

119prompt = 'ipdb> ' 

120 

121# We have to check this directly from sys.argv, config struct not yet available 

122from pdb import Pdb as OldPdb 

123 

124# Allow the set_trace code to operate outside of an ipython instance, even if 

125# it does so with some limitations. The rest of this support is implemented in 

126# the Tracer constructor. 

127 

128DEBUGGERSKIP = "__debuggerskip__" 

129 

130 

131# this has been implemented in Pdb in Python 3.13 (https://github.com/python/cpython/pull/106676 

132# on lower python versions, we backported the feature. 

133CHAIN_EXCEPTIONS = sys.version_info < (3, 13) 

134 

135 

136def make_arrow(pad): 

137 """generate the leading arrow in front of traceback or debugger""" 

138 if pad >= 2: 

139 return '-'*(pad-2) + '> ' 

140 elif pad == 1: 

141 return '>' 

142 return '' 

143 

144 

145def BdbQuit_excepthook(et, ev, tb, excepthook=None): 

146 """Exception hook which handles `BdbQuit` exceptions. 

147 

148 All other exceptions are processed using the `excepthook` 

149 parameter. 

150 """ 

151 raise ValueError( 

152 "`BdbQuit_excepthook` is deprecated since version 5.1", 

153 ) 

154 

155 

156def BdbQuit_IPython_excepthook(self, et, ev, tb, tb_offset=None): 

157 raise ValueError( 

158 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1", 

159 DeprecationWarning, stacklevel=2) 

160 

161 

162RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+') 

163 

164 

165def strip_indentation(multiline_string): 

166 return RGX_EXTRA_INDENT.sub('', multiline_string) 

167 

168 

169def decorate_fn_with_doc(new_fn, old_fn, additional_text=""): 

170 """Make new_fn have old_fn's doc string. This is particularly useful 

171 for the ``do_...`` commands that hook into the help system. 

172 Adapted from from a comp.lang.python posting 

173 by Duncan Booth.""" 

174 def wrapper(*args, **kw): 

175 return new_fn(*args, **kw) 

176 if old_fn.__doc__: 

177 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text 

178 return wrapper 

179 

180 

181class Pdb(OldPdb): 

182 """Modified Pdb class, does not load readline. 

183 

184 for a standalone version that uses prompt_toolkit, see 

185 `IPython.terminal.debugger.TerminalPdb` and 

186 `IPython.terminal.debugger.set_trace()` 

187 

188 

189 This debugger can hide and skip frames that are tagged according to some predicates. 

190 See the `skip_predicates` commands. 

191 

192 """ 

193 

194 if CHAIN_EXCEPTIONS: 

195 MAX_CHAINED_EXCEPTION_DEPTH = 999 

196 

197 default_predicates = { 

198 "tbhide": True, 

199 "readonly": False, 

200 "ipython_internal": True, 

201 "debuggerskip": True, 

202 } 

203 

204 def __init__(self, completekey=None, stdin=None, stdout=None, context=5, **kwargs): 

205 """Create a new IPython debugger. 

206 

207 Parameters 

208 ---------- 

209 completekey : default None 

210 Passed to pdb.Pdb. 

211 stdin : default None 

212 Passed to pdb.Pdb. 

213 stdout : default None 

214 Passed to pdb.Pdb. 

215 context : int 

216 Number of lines of source code context to show when 

217 displaying stacktrace information. 

218 **kwargs 

219 Passed to pdb.Pdb. 

220 

221 Notes 

222 ----- 

223 The possibilities are python version dependent, see the python 

224 docs for more info. 

225 """ 

226 

227 # Parent constructor: 

228 try: 

229 self.context = int(context) 

230 if self.context <= 0: 

231 raise ValueError("Context must be a positive integer") 

232 except (TypeError, ValueError) as e: 

233 raise ValueError("Context must be a positive integer") from e 

234 

235 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`. 

236 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs) 

237 

238 # IPython changes... 

239 self.shell = get_ipython() 

240 

241 if self.shell is None: 

242 save_main = sys.modules['__main__'] 

243 # No IPython instance running, we must create one 

244 from IPython.terminal.interactiveshell import \ 

245 TerminalInteractiveShell 

246 self.shell = TerminalInteractiveShell.instance() 

247 # needed by any code which calls __import__("__main__") after 

248 # the debugger was entered. See also #9941. 

249 sys.modules["__main__"] = save_main 

250 

251 

252 color_scheme = self.shell.colors 

253 

254 self.aliases = {} 

255 

256 # Create color table: we copy the default one from the traceback 

257 # module and add a few attributes needed for debugging 

258 self.color_scheme_table = exception_colors() 

259 

260 # shorthands 

261 C = coloransi.TermColors 

262 cst = self.color_scheme_table 

263 

264 cst['NoColor'].colors.prompt = C.NoColor 

265 cst['NoColor'].colors.breakpoint_enabled = C.NoColor 

266 cst['NoColor'].colors.breakpoint_disabled = C.NoColor 

267 

268 cst['Linux'].colors.prompt = C.Green 

269 cst['Linux'].colors.breakpoint_enabled = C.LightRed 

270 cst['Linux'].colors.breakpoint_disabled = C.Red 

271 

272 cst['LightBG'].colors.prompt = C.Blue 

273 cst['LightBG'].colors.breakpoint_enabled = C.LightRed 

274 cst['LightBG'].colors.breakpoint_disabled = C.Red 

275 

276 cst['Neutral'].colors.prompt = C.Blue 

277 cst['Neutral'].colors.breakpoint_enabled = C.LightRed 

278 cst['Neutral'].colors.breakpoint_disabled = C.Red 

279 

280 # Add a python parser so we can syntax highlight source while 

281 # debugging. 

282 self.parser = PyColorize.Parser(style=color_scheme) 

283 self.set_colors(color_scheme) 

284 

285 # Set the prompt - the default prompt is '(Pdb)' 

286 self.prompt = prompt 

287 self.skip_hidden = True 

288 self.report_skipped = True 

289 

290 # list of predicates we use to skip frames 

291 self._predicates = self.default_predicates 

292 

293 if CHAIN_EXCEPTIONS: 

294 self._chained_exceptions = tuple() 

295 self._chained_exception_index = 0 

296 

297 # 

298 def set_colors(self, scheme): 

299 """Shorthand access to the color table scheme selector method.""" 

300 self.color_scheme_table.set_active_scheme(scheme) 

301 self.parser.style = scheme 

302 

303 def set_trace(self, frame=None): 

304 if frame is None: 

305 frame = sys._getframe().f_back 

306 self.initial_frame = frame 

307 return super().set_trace(frame) 

308 

309 def _hidden_predicate(self, frame): 

310 """ 

311 Given a frame return whether it it should be hidden or not by IPython. 

312 """ 

313 

314 if self._predicates["readonly"]: 

315 fname = frame.f_code.co_filename 

316 # we need to check for file existence and interactively define 

317 # function would otherwise appear as RO. 

318 if os.path.isfile(fname) and not os.access(fname, os.W_OK): 

319 return True 

320 

321 if self._predicates["tbhide"]: 

322 if frame in (self.curframe, getattr(self, "initial_frame", None)): 

323 return False 

324 frame_locals = self._get_frame_locals(frame) 

325 if "__tracebackhide__" not in frame_locals: 

326 return False 

327 return frame_locals["__tracebackhide__"] 

328 return False 

329 

330 def hidden_frames(self, stack): 

331 """ 

332 Given an index in the stack return whether it should be skipped. 

333 

334 This is used in up/down and where to skip frames. 

335 """ 

336 # The f_locals dictionary is updated from the actual frame 

337 # locals whenever the .f_locals accessor is called, so we 

338 # avoid calling it here to preserve self.curframe_locals. 

339 # Furthermore, there is no good reason to hide the current frame. 

340 ip_hide = [self._hidden_predicate(s[0]) for s in stack] 

341 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"] 

342 if ip_start and self._predicates["ipython_internal"]: 

343 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)] 

344 return ip_hide 

345 

346 if CHAIN_EXCEPTIONS: 

347 

348 def _get_tb_and_exceptions(self, tb_or_exc): 

349 """ 

350 Given a tracecack or an exception, return a tuple of chained exceptions 

351 and current traceback to inspect. 

352 This will deal with selecting the right ``__cause__`` or ``__context__`` 

353 as well as handling cycles, and return a flattened list of exceptions we 

354 can jump to with do_exceptions. 

355 """ 

356 _exceptions = [] 

357 if isinstance(tb_or_exc, BaseException): 

358 traceback, current = tb_or_exc.__traceback__, tb_or_exc 

359 

360 while current is not None: 

361 if current in _exceptions: 

362 break 

363 _exceptions.append(current) 

364 if current.__cause__ is not None: 

365 current = current.__cause__ 

366 elif ( 

367 current.__context__ is not None 

368 and not current.__suppress_context__ 

369 ): 

370 current = current.__context__ 

371 

372 if len(_exceptions) >= self.MAX_CHAINED_EXCEPTION_DEPTH: 

373 self.message( 

374 f"More than {self.MAX_CHAINED_EXCEPTION_DEPTH}" 

375 " chained exceptions found, not all exceptions" 

376 "will be browsable with `exceptions`." 

377 ) 

378 break 

379 else: 

380 traceback = tb_or_exc 

381 return tuple(reversed(_exceptions)), traceback 

382 

383 @contextmanager 

384 def _hold_exceptions(self, exceptions): 

385 """ 

386 Context manager to ensure proper cleaning of exceptions references 

387 When given a chained exception instead of a traceback, 

388 pdb may hold references to many objects which may leak memory. 

389 We use this context manager to make sure everything is properly cleaned 

390 """ 

391 try: 

392 self._chained_exceptions = exceptions 

393 self._chained_exception_index = len(exceptions) - 1 

394 yield 

395 finally: 

396 # we can't put those in forget as otherwise they would 

397 # be cleared on exception change 

398 self._chained_exceptions = tuple() 

399 self._chained_exception_index = 0 

400 

401 def do_exceptions(self, arg): 

402 """exceptions [number] 

403 List or change current exception in an exception chain. 

404 Without arguments, list all the current exception in the exception 

405 chain. Exceptions will be numbered, with the current exception indicated 

406 with an arrow. 

407 If given an integer as argument, switch to the exception at that index. 

408 """ 

409 if not self._chained_exceptions: 

410 self.message( 

411 "Did not find chained exceptions. To move between" 

412 " exceptions, pdb/post_mortem must be given an exception" 

413 " object rather than a traceback." 

414 ) 

415 return 

416 if not arg: 

417 for ix, exc in enumerate(self._chained_exceptions): 

418 prompt = ">" if ix == self._chained_exception_index else " " 

419 rep = repr(exc) 

420 if len(rep) > 80: 

421 rep = rep[:77] + "..." 

422 indicator = ( 

423 " -" 

424 if self._chained_exceptions[ix].__traceback__ is None 

425 else f"{ix:>3}" 

426 ) 

427 self.message(f"{prompt} {indicator} {rep}") 

428 else: 

429 try: 

430 number = int(arg) 

431 except ValueError: 

432 self.error("Argument must be an integer") 

433 return 

434 if 0 <= number < len(self._chained_exceptions): 

435 if self._chained_exceptions[number].__traceback__ is None: 

436 self.error( 

437 "This exception does not have a traceback, cannot jump to it" 

438 ) 

439 return 

440 

441 self._chained_exception_index = number 

442 self.setup(None, self._chained_exceptions[number].__traceback__) 

443 self.print_stack_entry(self.stack[self.curindex]) 

444 else: 

445 self.error("No exception with that number") 

446 

447 def interaction(self, frame, tb_or_exc): 

448 try: 

449 if CHAIN_EXCEPTIONS: 

450 # this context manager is part of interaction in 3.13 

451 _chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc) 

452 if isinstance(tb_or_exc, BaseException): 

453 assert tb is not None, "main exception must have a traceback" 

454 with self._hold_exceptions(_chained_exceptions): 

455 OldPdb.interaction(self, frame, tb) 

456 else: 

457 OldPdb.interaction(self, frame, traceback) 

458 

459 except KeyboardInterrupt: 

460 self.stdout.write("\n" + self.shell.get_exception_only()) 

461 

462 def precmd(self, line): 

463 """Perform useful escapes on the command before it is executed.""" 

464 

465 if line.endswith("??"): 

466 line = "pinfo2 " + line[:-2] 

467 elif line.endswith("?"): 

468 line = "pinfo " + line[:-1] 

469 

470 line = super().precmd(line) 

471 

472 return line 

473 

474 def new_do_frame(self, arg): 

475 OldPdb.do_frame(self, arg) 

476 

477 def new_do_quit(self, arg): 

478 

479 if hasattr(self, 'old_all_completions'): 

480 self.shell.Completer.all_completions = self.old_all_completions 

481 

482 return OldPdb.do_quit(self, arg) 

483 

484 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit) 

485 

486 def new_do_restart(self, arg): 

487 """Restart command. In the context of ipython this is exactly the same 

488 thing as 'quit'.""" 

489 self.msg("Restart doesn't make sense here. Using 'quit' instead.") 

490 return self.do_quit(arg) 

491 

492 def print_stack_trace(self, context=None): 

493 Colors = self.color_scheme_table.active_colors 

494 ColorsNormal = Colors.Normal 

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) as e: 

502 raise ValueError("Context must be a positive integer") from e 

503 try: 

504 skipped = 0 

505 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack): 

506 if hidden and self.skip_hidden: 

507 skipped += 1 

508 continue 

509 if skipped: 

510 print( 

511 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n" 

512 ) 

513 skipped = 0 

514 self.print_stack_entry(frame_lineno, context=context) 

515 if skipped: 

516 print( 

517 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n" 

518 ) 

519 except KeyboardInterrupt: 

520 pass 

521 

522 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ', 

523 context=None): 

524 if context is None: 

525 context = self.context 

526 try: 

527 context = int(context) 

528 if context <= 0: 

529 raise ValueError("Context must be a positive integer") 

530 except (TypeError, ValueError) as e: 

531 raise ValueError("Context must be a positive integer") from e 

532 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout) 

533 

534 # vds: >> 

535 frame, lineno = frame_lineno 

536 filename = frame.f_code.co_filename 

537 self.shell.hooks.synchronize_with_editor(filename, lineno, 0) 

538 # vds: << 

539 

540 def _get_frame_locals(self, frame): 

541 """ " 

542 Accessing f_local of current frame reset the namespace, so we want to avoid 

543 that or the following can happen 

544 

545 ipdb> foo 

546 "old" 

547 ipdb> foo = "new" 

548 ipdb> foo 

549 "new" 

550 ipdb> where 

551 ipdb> foo 

552 "old" 

553 

554 So if frame is self.current_frame we instead return self.curframe_locals 

555 

556 """ 

557 if frame is self.curframe: 

558 return self.curframe_locals 

559 else: 

560 return frame.f_locals 

561 

562 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None): 

563 if context is None: 

564 context = self.context 

565 try: 

566 context = int(context) 

567 if context <= 0: 

568 print("Context must be a positive integer", file=self.stdout) 

569 except (TypeError, ValueError): 

570 print("Context must be a positive integer", file=self.stdout) 

571 

572 import reprlib 

573 

574 ret = [] 

575 

576 Colors = self.color_scheme_table.active_colors 

577 ColorsNormal = Colors.Normal 

578 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal) 

579 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal) 

580 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal) 

581 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal) 

582 

583 frame, lineno = frame_lineno 

584 

585 return_value = '' 

586 loc_frame = self._get_frame_locals(frame) 

587 if "__return__" in loc_frame: 

588 rv = loc_frame["__return__"] 

589 # return_value += '->' 

590 return_value += reprlib.repr(rv) + "\n" 

591 ret.append(return_value) 

592 

593 #s = filename + '(' + `lineno` + ')' 

594 filename = self.canonic(frame.f_code.co_filename) 

595 link = tpl_link % py3compat.cast_unicode(filename) 

596 

597 if frame.f_code.co_name: 

598 func = frame.f_code.co_name 

599 else: 

600 func = "<lambda>" 

601 

602 call = "" 

603 if func != "?": 

604 if "__args__" in loc_frame: 

605 args = reprlib.repr(loc_frame["__args__"]) 

606 else: 

607 args = '()' 

608 call = tpl_call % (func, args) 

609 

610 # The level info should be generated in the same format pdb uses, to 

611 # avoid breaking the pdbtrack functionality of python-mode in *emacs. 

612 if frame is self.curframe: 

613 ret.append('> ') 

614 else: 

615 ret.append(" ") 

616 ret.append("%s(%s)%s\n" % (link, lineno, call)) 

617 

618 start = lineno - 1 - context//2 

619 lines = linecache.getlines(filename) 

620 start = min(start, len(lines) - context) 

621 start = max(start, 0) 

622 lines = lines[start : start + context] 

623 

624 for i, line in enumerate(lines): 

625 show_arrow = start + 1 + i == lineno 

626 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line 

627 ret.append( 

628 self.__format_line( 

629 linetpl, filename, start + 1 + i, line, arrow=show_arrow 

630 ) 

631 ) 

632 return "".join(ret) 

633 

634 def __format_line(self, tpl_line, filename, lineno, line, arrow=False): 

635 bp_mark = "" 

636 bp_mark_color = "" 

637 

638 new_line, err = self.parser.format2(line, 'str') 

639 if not err: 

640 line = new_line 

641 

642 bp = None 

643 if lineno in self.get_file_breaks(filename): 

644 bps = self.get_breaks(filename, lineno) 

645 bp = bps[-1] 

646 

647 if bp: 

648 Colors = self.color_scheme_table.active_colors 

649 bp_mark = str(bp.number) 

650 bp_mark_color = Colors.breakpoint_enabled 

651 if not bp.enabled: 

652 bp_mark_color = Colors.breakpoint_disabled 

653 

654 numbers_width = 7 

655 if arrow: 

656 # This is the line with the error 

657 pad = numbers_width - len(str(lineno)) - len(bp_mark) 

658 num = '%s%s' % (make_arrow(pad), str(lineno)) 

659 else: 

660 num = '%*s' % (numbers_width - len(bp_mark), str(lineno)) 

661 

662 return tpl_line % (bp_mark_color + bp_mark, num, line) 

663 

664 def print_list_lines(self, filename, first, last): 

665 """The printing (as opposed to the parsing part of a 'list' 

666 command.""" 

667 try: 

668 Colors = self.color_scheme_table.active_colors 

669 ColorsNormal = Colors.Normal 

670 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) 

671 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal) 

672 src = [] 

673 if filename == "<string>" and hasattr(self, "_exec_filename"): 

674 filename = self._exec_filename 

675 

676 for lineno in range(first, last+1): 

677 line = linecache.getline(filename, lineno) 

678 if not line: 

679 break 

680 

681 if lineno == self.curframe.f_lineno: 

682 line = self.__format_line( 

683 tpl_line_em, filename, lineno, line, arrow=True 

684 ) 

685 else: 

686 line = self.__format_line( 

687 tpl_line, filename, lineno, line, arrow=False 

688 ) 

689 

690 src.append(line) 

691 self.lineno = lineno 

692 

693 print(''.join(src), file=self.stdout) 

694 

695 except KeyboardInterrupt: 

696 pass 

697 

698 def do_skip_predicates(self, args): 

699 """ 

700 Turn on/off individual predicates as to whether a frame should be hidden/skip. 

701 

702 The global option to skip (or not) hidden frames is set with skip_hidden 

703 

704 To change the value of a predicate 

705 

706 skip_predicates key [true|false] 

707 

708 Call without arguments to see the current values. 

709 

710 To permanently change the value of an option add the corresponding 

711 command to your ``~/.pdbrc`` file. If you are programmatically using the 

712 Pdb instance you can also change the ``default_predicates`` class 

713 attribute. 

714 """ 

715 if not args.strip(): 

716 print("current predicates:") 

717 for p, v in self._predicates.items(): 

718 print(" ", p, ":", v) 

719 return 

720 type_value = args.strip().split(" ") 

721 if len(type_value) != 2: 

722 print( 

723 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}" 

724 ) 

725 return 

726 

727 type_, value = type_value 

728 if type_ not in self._predicates: 

729 print(f"{type_!r} not in {set(self._predicates.keys())}") 

730 return 

731 if value.lower() not in ("true", "yes", "1", "no", "false", "0"): 

732 print( 

733 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')" 

734 ) 

735 return 

736 

737 self._predicates[type_] = value.lower() in ("true", "yes", "1") 

738 if not any(self._predicates.values()): 

739 print( 

740 "Warning, all predicates set to False, skip_hidden may not have any effects." 

741 ) 

742 

743 def do_skip_hidden(self, arg): 

744 """ 

745 Change whether or not we should skip frames with the 

746 __tracebackhide__ attribute. 

747 """ 

748 if not arg.strip(): 

749 print( 

750 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change." 

751 ) 

752 elif arg.strip().lower() in ("true", "yes"): 

753 self.skip_hidden = True 

754 elif arg.strip().lower() in ("false", "no"): 

755 self.skip_hidden = False 

756 if not any(self._predicates.values()): 

757 print( 

758 "Warning, all predicates set to False, skip_hidden may not have any effects." 

759 ) 

760 

761 def do_list(self, arg): 

762 """Print lines of code from the current stack frame 

763 """ 

764 self.lastcmd = 'list' 

765 last = None 

766 if arg and arg != ".": 

767 try: 

768 x = eval(arg, {}, {}) 

769 if type(x) == type(()): 

770 first, last = x 

771 first = int(first) 

772 last = int(last) 

773 if last < first: 

774 # Assume it's a count 

775 last = first + last 

776 else: 

777 first = max(1, int(x) - 5) 

778 except: 

779 print('*** Error in argument:', repr(arg), file=self.stdout) 

780 return 

781 elif self.lineno is None or arg == ".": 

782 first = max(1, self.curframe.f_lineno - 5) 

783 else: 

784 first = self.lineno + 1 

785 if last is None: 

786 last = first + 10 

787 self.print_list_lines(self.curframe.f_code.co_filename, first, last) 

788 

789 # vds: >> 

790 lineno = first 

791 filename = self.curframe.f_code.co_filename 

792 self.shell.hooks.synchronize_with_editor(filename, lineno, 0) 

793 # vds: << 

794 

795 do_l = do_list 

796 

797 def getsourcelines(self, obj): 

798 lines, lineno = inspect.findsource(obj) 

799 if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj): 

800 # must be a module frame: do not try to cut a block out of it 

801 return lines, 1 

802 elif inspect.ismodule(obj): 

803 return lines, 1 

804 return inspect.getblock(lines[lineno:]), lineno+1 

805 

806 def do_longlist(self, arg): 

807 """Print lines of code from the current stack frame. 

808 

809 Shows more lines than 'list' does. 

810 """ 

811 self.lastcmd = 'longlist' 

812 try: 

813 lines, lineno = self.getsourcelines(self.curframe) 

814 except OSError as err: 

815 self.error(err) 

816 return 

817 last = lineno + len(lines) 

818 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last) 

819 do_ll = do_longlist 

820 

821 def do_debug(self, arg): 

822 """debug code 

823 Enter a recursive debugger that steps through the code 

824 argument (which is an arbitrary expression or statement to be 

825 executed in the current environment). 

826 """ 

827 trace_function = sys.gettrace() 

828 sys.settrace(None) 

829 globals = self.curframe.f_globals 

830 locals = self.curframe_locals 

831 p = self.__class__(completekey=self.completekey, 

832 stdin=self.stdin, stdout=self.stdout) 

833 p.use_rawinput = self.use_rawinput 

834 p.prompt = "(%s) " % self.prompt.strip() 

835 self.message("ENTERING RECURSIVE DEBUGGER") 

836 sys.call_tracing(p.run, (arg, globals, locals)) 

837 self.message("LEAVING RECURSIVE DEBUGGER") 

838 sys.settrace(trace_function) 

839 self.lastcmd = p.lastcmd 

840 

841 def do_pdef(self, arg): 

842 """Print the call signature for any callable object. 

843 

844 The debugger interface to %pdef""" 

845 namespaces = [ 

846 ("Locals", self.curframe_locals), 

847 ("Globals", self.curframe.f_globals), 

848 ] 

849 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces) 

850 

851 def do_pdoc(self, arg): 

852 """Print the docstring for an object. 

853 

854 The debugger interface to %pdoc.""" 

855 namespaces = [ 

856 ("Locals", self.curframe_locals), 

857 ("Globals", self.curframe.f_globals), 

858 ] 

859 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces) 

860 

861 def do_pfile(self, arg): 

862 """Print (or run through pager) the file where an object is defined. 

863 

864 The debugger interface to %pfile. 

865 """ 

866 namespaces = [ 

867 ("Locals", self.curframe_locals), 

868 ("Globals", self.curframe.f_globals), 

869 ] 

870 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces) 

871 

872 def do_pinfo(self, arg): 

873 """Provide detailed information about an object. 

874 

875 The debugger interface to %pinfo, i.e., obj?.""" 

876 namespaces = [ 

877 ("Locals", self.curframe_locals), 

878 ("Globals", self.curframe.f_globals), 

879 ] 

880 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces) 

881 

882 def do_pinfo2(self, arg): 

883 """Provide extra detailed information about an object. 

884 

885 The debugger interface to %pinfo2, i.e., obj??.""" 

886 namespaces = [ 

887 ("Locals", self.curframe_locals), 

888 ("Globals", self.curframe.f_globals), 

889 ] 

890 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces) 

891 

892 def do_psource(self, arg): 

893 """Print (or run through pager) the source code for an object.""" 

894 namespaces = [ 

895 ("Locals", self.curframe_locals), 

896 ("Globals", self.curframe.f_globals), 

897 ] 

898 self.shell.find_line_magic("psource")(arg, namespaces=namespaces) 

899 

900 def do_where(self, arg): 

901 """w(here) 

902 Print a stack trace, with the most recent frame at the bottom. 

903 An arrow indicates the "current frame", which determines the 

904 context of most commands. 'bt' is an alias for this command. 

905 

906 Take a number as argument as an (optional) number of context line to 

907 print""" 

908 if arg: 

909 try: 

910 context = int(arg) 

911 except ValueError as err: 

912 self.error(err) 

913 return 

914 self.print_stack_trace(context) 

915 else: 

916 self.print_stack_trace() 

917 

918 do_w = do_where 

919 

920 def break_anywhere(self, frame): 

921 """ 

922 _stop_in_decorator_internals is overly restrictive, as we may still want 

923 to trace function calls, so we need to also update break_anywhere so 

924 that is we don't `stop_here`, because of debugger skip, we may still 

925 stop at any point inside the function 

926 

927 """ 

928 

929 sup = super().break_anywhere(frame) 

930 if sup: 

931 return sup 

932 if self._predicates["debuggerskip"]: 

933 if DEBUGGERSKIP in frame.f_code.co_varnames: 

934 return True 

935 if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP): 

936 return True 

937 return False 

938 

939 def _is_in_decorator_internal_and_should_skip(self, frame): 

940 """ 

941 Utility to tell us whether we are in a decorator internal and should stop. 

942 

943 """ 

944 

945 # if we are disabled don't skip 

946 if not self._predicates["debuggerskip"]: 

947 return False 

948 

949 # if frame is tagged, skip by default. 

950 if DEBUGGERSKIP in frame.f_code.co_varnames: 

951 return True 

952 

953 # if one of the parent frame value set to True skip as well. 

954 

955 cframe = frame 

956 while getattr(cframe, "f_back", None): 

957 cframe = cframe.f_back 

958 if self._get_frame_locals(cframe).get(DEBUGGERSKIP): 

959 return True 

960 

961 return False 

962 

963 def stop_here(self, frame): 

964 if self._is_in_decorator_internal_and_should_skip(frame) is True: 

965 return False 

966 

967 hidden = False 

968 if self.skip_hidden: 

969 hidden = self._hidden_predicate(frame) 

970 if hidden: 

971 if self.report_skipped: 

972 Colors = self.color_scheme_table.active_colors 

973 ColorsNormal = Colors.Normal 

974 print( 

975 f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n" 

976 ) 

977 return super().stop_here(frame) 

978 

979 def do_up(self, arg): 

980 """u(p) [count] 

981 Move the current frame count (default one) levels up in the 

982 stack trace (to an older frame). 

983 

984 Will skip hidden frames. 

985 """ 

986 # modified version of upstream that skips 

987 # frames with __tracebackhide__ 

988 if self.curindex == 0: 

989 self.error("Oldest frame") 

990 return 

991 try: 

992 count = int(arg or 1) 

993 except ValueError: 

994 self.error("Invalid frame count (%s)" % arg) 

995 return 

996 skipped = 0 

997 if count < 0: 

998 _newframe = 0 

999 else: 

1000 counter = 0 

1001 hidden_frames = self.hidden_frames(self.stack) 

1002 for i in range(self.curindex - 1, -1, -1): 

1003 if hidden_frames[i] and self.skip_hidden: 

1004 skipped += 1 

1005 continue 

1006 counter += 1 

1007 if counter >= count: 

1008 break 

1009 else: 

1010 # if no break occurred. 

1011 self.error( 

1012 "all frames above hidden, use `skip_hidden False` to get get into those." 

1013 ) 

1014 return 

1015 

1016 Colors = self.color_scheme_table.active_colors 

1017 ColorsNormal = Colors.Normal 

1018 _newframe = i 

1019 self._select_frame(_newframe) 

1020 if skipped: 

1021 print( 

1022 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n" 

1023 ) 

1024 

1025 def do_down(self, arg): 

1026 """d(own) [count] 

1027 Move the current frame count (default one) levels down in the 

1028 stack trace (to a newer frame). 

1029 

1030 Will skip hidden frames. 

1031 """ 

1032 if self.curindex + 1 == len(self.stack): 

1033 self.error("Newest frame") 

1034 return 

1035 try: 

1036 count = int(arg or 1) 

1037 except ValueError: 

1038 self.error("Invalid frame count (%s)" % arg) 

1039 return 

1040 if count < 0: 

1041 _newframe = len(self.stack) - 1 

1042 else: 

1043 counter = 0 

1044 skipped = 0 

1045 hidden_frames = self.hidden_frames(self.stack) 

1046 for i in range(self.curindex + 1, len(self.stack)): 

1047 if hidden_frames[i] and self.skip_hidden: 

1048 skipped += 1 

1049 continue 

1050 counter += 1 

1051 if counter >= count: 

1052 break 

1053 else: 

1054 self.error( 

1055 "all frames below hidden, use `skip_hidden False` to get get into those." 

1056 ) 

1057 return 

1058 

1059 Colors = self.color_scheme_table.active_colors 

1060 ColorsNormal = Colors.Normal 

1061 if skipped: 

1062 print( 

1063 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n" 

1064 ) 

1065 _newframe = i 

1066 

1067 self._select_frame(_newframe) 

1068 

1069 do_d = do_down 

1070 do_u = do_up 

1071 

1072 def do_context(self, context): 

1073 """context number_of_lines 

1074 Set the number of lines of source code to show when displaying 

1075 stacktrace information. 

1076 """ 

1077 try: 

1078 new_context = int(context) 

1079 if new_context <= 0: 

1080 raise ValueError() 

1081 self.context = new_context 

1082 except ValueError: 

1083 self.error("The 'context' command requires a positive integer argument.") 

1084 

1085 

1086class InterruptiblePdb(Pdb): 

1087 """Version of debugger where KeyboardInterrupt exits the debugger altogether.""" 

1088 

1089 def cmdloop(self, intro=None): 

1090 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger.""" 

1091 try: 

1092 return OldPdb.cmdloop(self, intro=intro) 

1093 except KeyboardInterrupt: 

1094 self.stop_here = lambda frame: False 

1095 self.do_quit("") 

1096 sys.settrace(None) 

1097 self.quitting = False 

1098 raise 

1099 

1100 def _cmdloop(self): 

1101 while True: 

1102 try: 

1103 # keyboard interrupts allow for an easy way to cancel 

1104 # the current command, so allow them during interactive input 

1105 self.allow_kbdint = True 

1106 self.cmdloop() 

1107 self.allow_kbdint = False 

1108 break 

1109 except KeyboardInterrupt: 

1110 self.message('--KeyboardInterrupt--') 

1111 raise 

1112 

1113 

1114def set_trace(frame=None): 

1115 """ 

1116 Start debugging from `frame`. 

1117 

1118 If frame is not specified, debugging starts from caller's frame. 

1119 """ 

1120 Pdb().set_trace(frame or sys._getframe().f_back)