debugger.py 38 KB

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