debugger.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. # -*- coding: utf-8 -*-
  2. """
  3. Pdb debugger class.
  4. Modified from the standard pdb.Pdb class to avoid including readline, so that
  5. the command line completion of other programs which include this isn't
  6. damaged.
  7. In the future, this class will be expanded with improvements over the standard
  8. pdb.
  9. The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
  10. changes. Licensing should therefore be under the standard Python terms. For
  11. details on the PSF (Python Software Foundation) standard license, see:
  12. http://www.python.org/2.2.3/license.html"""
  13. #*****************************************************************************
  14. #
  15. # This file is licensed under the PSF license.
  16. #
  17. # Copyright (C) 2001 Python Software Foundation, www.python.org
  18. # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
  19. #
  20. #
  21. #*****************************************************************************
  22. from __future__ import print_function
  23. import bdb
  24. import functools
  25. import inspect
  26. import sys
  27. import warnings
  28. from IPython import get_ipython
  29. from IPython.utils import PyColorize, ulinecache
  30. from IPython.utils import coloransi, py3compat
  31. from IPython.core.excolors import exception_colors
  32. from IPython.testing.skipdoctest import skip_doctest
  33. prompt = 'ipdb> '
  34. #We have to check this directly from sys.argv, config struct not yet available
  35. from pdb import Pdb as OldPdb
  36. # Allow the set_trace code to operate outside of an ipython instance, even if
  37. # it does so with some limitations. The rest of this support is implemented in
  38. # the Tracer constructor.
  39. def make_arrow(pad):
  40. """generate the leading arrow in front of traceback or debugger"""
  41. if pad >= 2:
  42. return '-'*(pad-2) + '> '
  43. elif pad == 1:
  44. return '>'
  45. return ''
  46. def BdbQuit_excepthook(et, ev, tb, excepthook=None):
  47. """Exception hook which handles `BdbQuit` exceptions.
  48. All other exceptions are processed using the `excepthook`
  49. parameter.
  50. """
  51. warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
  52. DeprecationWarning)
  53. if et==bdb.BdbQuit:
  54. print('Exiting Debugger.')
  55. elif excepthook is not None:
  56. excepthook(et, ev, tb)
  57. else:
  58. # Backwards compatibility. Raise deprecation warning?
  59. BdbQuit_excepthook.excepthook_ori(et,ev,tb)
  60. def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
  61. warnings.warn(
  62. "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
  63. DeprecationWarning)
  64. print('Exiting Debugger.')
  65. class Tracer(object):
  66. """
  67. DEPRECATED
  68. Class for local debugging, similar to pdb.set_trace.
  69. Instances of this class, when called, behave like pdb.set_trace, but
  70. providing IPython's enhanced capabilities.
  71. This is implemented as a class which must be initialized in your own code
  72. and not as a standalone function because we need to detect at runtime
  73. whether IPython is already active or not. That detection is done in the
  74. constructor, ensuring that this code plays nicely with a running IPython,
  75. while functioning acceptably (though with limitations) if outside of it.
  76. """
  77. @skip_doctest
  78. def __init__(self, colors=None):
  79. """
  80. DEPRECATED
  81. Create a local debugger instance.
  82. Parameters
  83. ----------
  84. colors : str, optional
  85. The name of the color scheme to use, it must be one of IPython's
  86. valid color schemes. If not given, the function will default to
  87. the current IPython scheme when running inside IPython, and to
  88. 'NoColor' otherwise.
  89. Examples
  90. --------
  91. ::
  92. from IPython.core.debugger import Tracer; debug_here = Tracer()
  93. Later in your code::
  94. debug_here() # -> will open up the debugger at that point.
  95. Once the debugger activates, you can use all of its regular commands to
  96. step through code, set breakpoints, etc. See the pdb documentation
  97. from the Python standard library for usage details.
  98. """
  99. warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
  100. "`IPython.core.debugger.Pdb.set_trace()`",
  101. DeprecationWarning)
  102. ip = get_ipython()
  103. if ip is None:
  104. # Outside of ipython, we set our own exception hook manually
  105. sys.excepthook = functools.partial(BdbQuit_excepthook,
  106. excepthook=sys.excepthook)
  107. def_colors = 'NoColor'
  108. else:
  109. # In ipython, we use its custom exception handler mechanism
  110. def_colors = ip.colors
  111. ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
  112. if colors is None:
  113. colors = def_colors
  114. # The stdlib debugger internally uses a modified repr from the `repr`
  115. # module, that limits the length of printed strings to a hardcoded
  116. # limit of 30 characters. That much trimming is too aggressive, let's
  117. # at least raise that limit to 80 chars, which should be enough for
  118. # most interactive uses.
  119. try:
  120. try:
  121. from reprlib import aRepr # Py 3
  122. except ImportError:
  123. from repr import aRepr # Py 2
  124. aRepr.maxstring = 80
  125. except:
  126. # This is only a user-facing convenience, so any error we encounter
  127. # here can be warned about but can be otherwise ignored. These
  128. # printouts will tell us about problems if this API changes
  129. import traceback
  130. traceback.print_exc()
  131. self.debugger = Pdb(colors)
  132. def __call__(self):
  133. """Starts an interactive debugger at the point where called.
  134. This is similar to the pdb.set_trace() function from the std lib, but
  135. using IPython's enhanced debugger."""
  136. self.debugger.set_trace(sys._getframe().f_back)
  137. def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
  138. """Make new_fn have old_fn's doc string. This is particularly useful
  139. for the ``do_...`` commands that hook into the help system.
  140. Adapted from from a comp.lang.python posting
  141. by Duncan Booth."""
  142. def wrapper(*args, **kw):
  143. return new_fn(*args, **kw)
  144. if old_fn.__doc__:
  145. wrapper.__doc__ = old_fn.__doc__ + additional_text
  146. return wrapper
  147. def _file_lines(fname):
  148. """Return the contents of a named file as a list of lines.
  149. This function never raises an IOError exception: if the file can't be
  150. read, it simply returns an empty list."""
  151. try:
  152. outfile = open(fname)
  153. except IOError:
  154. return []
  155. else:
  156. out = outfile.readlines()
  157. outfile.close()
  158. return out
  159. class Pdb(OldPdb, object):
  160. """Modified Pdb class, does not load readline.
  161. for a standalone version that uses prompt_toolkit, see
  162. `IPython.terminal.debugger.TerminalPdb` and
  163. `IPython.terminal.debugger.set_trace()`
  164. """
  165. def __init__(self, color_scheme=None, completekey=None,
  166. stdin=None, stdout=None, context=5):
  167. # Parent constructor:
  168. try:
  169. self.context = int(context)
  170. if self.context <= 0:
  171. raise ValueError("Context must be a positive integer")
  172. except (TypeError, ValueError):
  173. raise ValueError("Context must be a positive integer")
  174. OldPdb.__init__(self, completekey, stdin, stdout)
  175. # IPython changes...
  176. self.shell = get_ipython()
  177. if self.shell is None:
  178. # No IPython instance running, we must create one
  179. from IPython.terminal.interactiveshell import \
  180. TerminalInteractiveShell
  181. self.shell = TerminalInteractiveShell.instance()
  182. if color_scheme is not None:
  183. warnings.warn(
  184. "The `color_scheme` argument is deprecated since version 5.1",
  185. DeprecationWarning)
  186. else:
  187. color_scheme = self.shell.colors
  188. self.aliases = {}
  189. # Create color table: we copy the default one from the traceback
  190. # module and add a few attributes needed for debugging
  191. self.color_scheme_table = exception_colors()
  192. # shorthands
  193. C = coloransi.TermColors
  194. cst = self.color_scheme_table
  195. cst['NoColor'].colors.prompt = C.NoColor
  196. cst['NoColor'].colors.breakpoint_enabled = C.NoColor
  197. cst['NoColor'].colors.breakpoint_disabled = C.NoColor
  198. cst['Linux'].colors.prompt = C.Green
  199. cst['Linux'].colors.breakpoint_enabled = C.LightRed
  200. cst['Linux'].colors.breakpoint_disabled = C.Red
  201. cst['LightBG'].colors.prompt = C.Blue
  202. cst['LightBG'].colors.breakpoint_enabled = C.LightRed
  203. cst['LightBG'].colors.breakpoint_disabled = C.Red
  204. cst['Neutral'].colors.prompt = C.Blue
  205. cst['Neutral'].colors.breakpoint_enabled = C.LightRed
  206. cst['Neutral'].colors.breakpoint_disabled = C.Red
  207. self.set_colors(color_scheme)
  208. # Add a python parser so we can syntax highlight source while
  209. # debugging.
  210. self.parser = PyColorize.Parser()
  211. # Set the prompt - the default prompt is '(Pdb)'
  212. self.prompt = prompt
  213. def set_colors(self, scheme):
  214. """Shorthand access to the color table scheme selector method."""
  215. self.color_scheme_table.set_active_scheme(scheme)
  216. def trace_dispatch(self, frame, event, arg):
  217. try:
  218. return super(Pdb, self).trace_dispatch(frame, event, arg)
  219. except bdb.BdbQuit:
  220. pass
  221. def interaction(self, frame, traceback):
  222. try:
  223. OldPdb.interaction(self, frame, traceback)
  224. except KeyboardInterrupt:
  225. sys.stdout.write('\n' + self.shell.get_exception_only())
  226. def parseline(self, line):
  227. if line.startswith("!!"):
  228. # Force standard behavior.
  229. return super(Pdb, self).parseline(line[2:])
  230. # "Smart command mode" from pdb++: don't execute commands if a variable
  231. # with the same name exists.
  232. cmd, arg, newline = super(Pdb, self).parseline(line)
  233. # Fix for #9611: Do not trigger smart command if the command is `exit`
  234. # or `quit` and it would resolve to their *global* value (the
  235. # `ExitAutocall` object). Just checking that it is not present in the
  236. # locals dict is not enough as locals and globals match at the
  237. # toplevel.
  238. if ((cmd in self.curframe.f_locals or cmd in self.curframe.f_globals)
  239. and not (cmd in ["exit", "quit"]
  240. and (self.curframe.f_locals is self.curframe.f_globals
  241. or cmd not in self.curframe.f_locals))):
  242. return super(Pdb, self).parseline("!" + line)
  243. return super(Pdb, self).parseline(line)
  244. def new_do_up(self, arg):
  245. OldPdb.do_up(self, arg)
  246. do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
  247. def new_do_down(self, arg):
  248. OldPdb.do_down(self, arg)
  249. do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
  250. def new_do_frame(self, arg):
  251. OldPdb.do_frame(self, arg)
  252. def new_do_quit(self, arg):
  253. if hasattr(self, 'old_all_completions'):
  254. self.shell.Completer.all_completions=self.old_all_completions
  255. return OldPdb.do_quit(self, arg)
  256. do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
  257. def new_do_restart(self, arg):
  258. """Restart command. In the context of ipython this is exactly the same
  259. thing as 'quit'."""
  260. self.msg("Restart doesn't make sense here. Using 'quit' instead.")
  261. return self.do_quit(arg)
  262. def print_stack_trace(self, context=None):
  263. if context is None:
  264. context = self.context
  265. try:
  266. context=int(context)
  267. if context <= 0:
  268. raise ValueError("Context must be a positive integer")
  269. except (TypeError, ValueError):
  270. raise ValueError("Context must be a positive integer")
  271. try:
  272. for frame_lineno in self.stack:
  273. self.print_stack_entry(frame_lineno, context=context)
  274. except KeyboardInterrupt:
  275. pass
  276. def print_stack_entry(self,frame_lineno, prompt_prefix='\n-> ',
  277. context=None):
  278. if context is None:
  279. context = self.context
  280. try:
  281. context=int(context)
  282. if context <= 0:
  283. raise ValueError("Context must be a positive integer")
  284. except (TypeError, ValueError):
  285. raise ValueError("Context must be a positive integer")
  286. print(self.format_stack_entry(frame_lineno, '', context))
  287. # vds: >>
  288. frame, lineno = frame_lineno
  289. filename = frame.f_code.co_filename
  290. self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
  291. # vds: <<
  292. def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
  293. if context is None:
  294. context = self.context
  295. try:
  296. context=int(context)
  297. if context <= 0:
  298. print("Context must be a positive integer")
  299. except (TypeError, ValueError):
  300. print("Context must be a positive integer")
  301. try:
  302. import reprlib # Py 3
  303. except ImportError:
  304. import repr as reprlib # Py 2
  305. ret = []
  306. Colors = self.color_scheme_table.active_colors
  307. ColorsNormal = Colors.Normal
  308. tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
  309. tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
  310. tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
  311. tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
  312. ColorsNormal)
  313. frame, lineno = frame_lineno
  314. return_value = ''
  315. if '__return__' in frame.f_locals:
  316. rv = frame.f_locals['__return__']
  317. #return_value += '->'
  318. return_value += reprlib.repr(rv) + '\n'
  319. ret.append(return_value)
  320. #s = filename + '(' + `lineno` + ')'
  321. filename = self.canonic(frame.f_code.co_filename)
  322. link = tpl_link % py3compat.cast_unicode(filename)
  323. if frame.f_code.co_name:
  324. func = frame.f_code.co_name
  325. else:
  326. func = "<lambda>"
  327. call = ''
  328. if func != '?':
  329. if '__args__' in frame.f_locals:
  330. args = reprlib.repr(frame.f_locals['__args__'])
  331. else:
  332. args = '()'
  333. call = tpl_call % (func, args)
  334. # The level info should be generated in the same format pdb uses, to
  335. # avoid breaking the pdbtrack functionality of python-mode in *emacs.
  336. if frame is self.curframe:
  337. ret.append('> ')
  338. else:
  339. ret.append(' ')
  340. ret.append(u'%s(%s)%s\n' % (link,lineno,call))
  341. start = lineno - 1 - context//2
  342. lines = ulinecache.getlines(filename)
  343. start = min(start, len(lines) - context)
  344. start = max(start, 0)
  345. lines = lines[start : start + context]
  346. for i,line in enumerate(lines):
  347. show_arrow = (start + 1 + i == lineno)
  348. linetpl = (frame is self.curframe or show_arrow) \
  349. and tpl_line_em \
  350. or tpl_line
  351. ret.append(self.__format_line(linetpl, filename,
  352. start + 1 + i, line,
  353. arrow = show_arrow) )
  354. return ''.join(ret)
  355. def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
  356. bp_mark = ""
  357. bp_mark_color = ""
  358. scheme = self.color_scheme_table.active_scheme_name
  359. new_line, err = self.parser.format2(line, 'str', scheme)
  360. if not err: line = new_line
  361. bp = None
  362. if lineno in self.get_file_breaks(filename):
  363. bps = self.get_breaks(filename, lineno)
  364. bp = bps[-1]
  365. if bp:
  366. Colors = self.color_scheme_table.active_colors
  367. bp_mark = str(bp.number)
  368. bp_mark_color = Colors.breakpoint_enabled
  369. if not bp.enabled:
  370. bp_mark_color = Colors.breakpoint_disabled
  371. numbers_width = 7
  372. if arrow:
  373. # This is the line with the error
  374. pad = numbers_width - len(str(lineno)) - len(bp_mark)
  375. num = '%s%s' % (make_arrow(pad), str(lineno))
  376. else:
  377. num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
  378. return tpl_line % (bp_mark_color + bp_mark, num, line)
  379. def print_list_lines(self, filename, first, last):
  380. """The printing (as opposed to the parsing part of a 'list'
  381. command."""
  382. try:
  383. Colors = self.color_scheme_table.active_colors
  384. ColorsNormal = Colors.Normal
  385. tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
  386. tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
  387. src = []
  388. if filename == "<string>" and hasattr(self, "_exec_filename"):
  389. filename = self._exec_filename
  390. for lineno in range(first, last+1):
  391. line = ulinecache.getline(filename, lineno)
  392. if not line:
  393. break
  394. if lineno == self.curframe.f_lineno:
  395. line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
  396. else:
  397. line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
  398. src.append(line)
  399. self.lineno = lineno
  400. print(''.join(src))
  401. except KeyboardInterrupt:
  402. pass
  403. def do_list(self, arg):
  404. self.lastcmd = 'list'
  405. last = None
  406. if arg:
  407. try:
  408. x = eval(arg, {}, {})
  409. if type(x) == type(()):
  410. first, last = x
  411. first = int(first)
  412. last = int(last)
  413. if last < first:
  414. # Assume it's a count
  415. last = first + last
  416. else:
  417. first = max(1, int(x) - 5)
  418. except:
  419. print('*** Error in argument:', repr(arg))
  420. return
  421. elif self.lineno is None:
  422. first = max(1, self.curframe.f_lineno - 5)
  423. else:
  424. first = self.lineno + 1
  425. if last is None:
  426. last = first + 10
  427. self.print_list_lines(self.curframe.f_code.co_filename, first, last)
  428. # vds: >>
  429. lineno = first
  430. filename = self.curframe.f_code.co_filename
  431. self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
  432. # vds: <<
  433. do_l = do_list
  434. def getsourcelines(self, obj):
  435. lines, lineno = inspect.findsource(obj)
  436. if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
  437. # must be a module frame: do not try to cut a block out of it
  438. return lines, 1
  439. elif inspect.ismodule(obj):
  440. return lines, 1
  441. return inspect.getblock(lines[lineno:]), lineno+1
  442. def do_longlist(self, arg):
  443. self.lastcmd = 'longlist'
  444. try:
  445. lines, lineno = self.getsourcelines(self.curframe)
  446. except OSError as err:
  447. self.error(err)
  448. return
  449. last = lineno + len(lines)
  450. self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
  451. do_ll = do_longlist
  452. def do_pdef(self, arg):
  453. """Print the call signature for any callable object.
  454. The debugger interface to %pdef"""
  455. namespaces = [('Locals', self.curframe.f_locals),
  456. ('Globals', self.curframe.f_globals)]
  457. self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
  458. def do_pdoc(self, arg):
  459. """Print the docstring for an object.
  460. The debugger interface to %pdoc."""
  461. namespaces = [('Locals', self.curframe.f_locals),
  462. ('Globals', self.curframe.f_globals)]
  463. self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
  464. def do_pfile(self, arg):
  465. """Print (or run through pager) the file where an object is defined.
  466. The debugger interface to %pfile.
  467. """
  468. namespaces = [('Locals', self.curframe.f_locals),
  469. ('Globals', self.curframe.f_globals)]
  470. self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
  471. def do_pinfo(self, arg):
  472. """Provide detailed information about an object.
  473. The debugger interface to %pinfo, i.e., obj?."""
  474. namespaces = [('Locals', self.curframe.f_locals),
  475. ('Globals', self.curframe.f_globals)]
  476. self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
  477. def do_pinfo2(self, arg):
  478. """Provide extra detailed information about an object.
  479. The debugger interface to %pinfo2, i.e., obj??."""
  480. namespaces = [('Locals', self.curframe.f_locals),
  481. ('Globals', self.curframe.f_globals)]
  482. self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
  483. def do_psource(self, arg):
  484. """Print (or run through pager) the source code for an object."""
  485. namespaces = [('Locals', self.curframe.f_locals),
  486. ('Globals', self.curframe.f_globals)]
  487. self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
  488. if sys.version_info > (3, ):
  489. def do_where(self, arg):
  490. """w(here)
  491. Print a stack trace, with the most recent frame at the bottom.
  492. An arrow indicates the "current frame", which determines the
  493. context of most commands. 'bt' is an alias for this command.
  494. Take a number as argument as an (optional) number of context line to
  495. print"""
  496. if arg:
  497. context = int(arg)
  498. self.print_stack_trace(context)
  499. else:
  500. self.print_stack_trace()
  501. do_w = do_where