debugger.py 38 KB

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