Skip to content

Commit b37d832

Browse files
kdartthomasballinger
authored andcommitted
Add feature to enable debugging on uncaught exceptions.
Also add pretty-printng option. The debugger is selected by an environment variable.
1 parent 92e9b0b commit b37d832

File tree

5 files changed

+105
-5
lines changed

5 files changed

+105
-5
lines changed

bpython/args.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ def parse(args, extras=None, ignore_stdin=False):
7474
help=_("Don't flush the output to stdout."))
7575
parser.add_option('--version', '-V', action='store_true',
7676
help=_('Print version and exit.'))
77+
parser.add_option('--pretty', '-p', action='store_true',
78+
help=_('Pretty print output.'))
79+
parser.add_option('--debugger', '-D', action='store_true',
80+
help=_('Enter a debugger on exceptions.'))
7781

7882
if extras is not None:
7983
extras_group = OptionGroup(parser, extras[0], extras[1])

bpython/cli.py

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import re
5050
import time
5151
import functools
52+
from pprint import pprint
5253

5354
import struct
5455
if platform.system() != 'Windows':
@@ -160,6 +161,7 @@ def __init__(self, interface):
160161
self.encoding = getpreferredencoding()
161162
self.interface = interface
162163
self.buffer = list()
164+
self.errors = "strict"
163165

164166
def __iter__(self):
165167
return iter(self.readlines())
@@ -957,6 +959,13 @@ def p_key(self, key):
957959
self.write2file()
958960
return ''
959961

962+
elif key in key_dispatch[config.debug_key]:
963+
if sys.excepthook is not debugger_hook:
964+
sys.excepthook = debugger_hook
965+
else:
966+
sys.excepthook = sys.__excepthook__
967+
return ''
968+
960969
elif key in key_dispatch[config.pastebin_key]:
961970
self.pastebin()
962971
return ''
@@ -1696,7 +1705,8 @@ def init_wins(scr, config):
16961705
(_('Save'), config.save_key),
16971706
(_('Pastebin'), config.pastebin_key),
16981707
(_('Pager'), config.last_output_key),
1699-
(_('Show Source'), config.show_source_key)
1708+
(_('Show Source'), config.show_source_key),
1709+
(_('Auto Debug'), config.debug_key),
17001710
)
17011711

17021712
message = ' '.join('<%s> %s' % (key, command) for command, key in commands
@@ -1944,17 +1954,59 @@ def main_curses(scr, args, config, interactive=True, locals_=None,
19441954
return (exit_value, clirepl.getstdout())
19451955

19461956

1957+
def prettydisplayhook(obj):
1958+
pprint(obj)
1959+
1960+
1961+
def debugger_hook(exc, value, tb):
1962+
1963+
if exc in (SyntaxError, IndentationError, KeyboardInterrupt):
1964+
sys.__excepthook__(exc, value, tb)
1965+
return
1966+
1967+
global stdscr
1968+
from bpython import debugger
1969+
1970+
orig_stdin = sys.stdin
1971+
orig_stdout = sys.stdout
1972+
orig_stderr = sys.stderr
1973+
1974+
sys.stdin = sys.__stdin__
1975+
sys.stderr = sys.__stderr__
1976+
sys.stdout = sys.__stdout__
1977+
1978+
stdscr.keypad(0)
1979+
curses.echo()
1980+
curses.nocbreak()
1981+
curses.endwin()
1982+
try:
1983+
debugger.post_mortem(tb, exc, value)
1984+
finally:
1985+
sys.stdin = orig_stdin
1986+
sys.stdout = orig_stdout
1987+
sys.stderr = orig_stderr
1988+
curses.cbreak()
1989+
curses.noecho()
1990+
stdscr.keypad(1)
1991+
if stdscr is None:
1992+
stdscr = curses.initscr()
1993+
1994+
19471995
def main(args=None, locals_=None, banner=None):
19481996
translations.init()
19491997

1950-
19511998
config, options, exec_args = bpython.args.parse(args)
19521999

19532000
# Save stdin, stdout and stderr for later restoration
19542001
orig_stdin = sys.stdin
19552002
orig_stdout = sys.stdout
19562003
orig_stderr = sys.stderr
19572004

2005+
if options.debugger:
2006+
sys.excepthook = debugger_hook
2007+
if options.pretty:
2008+
sys.displayhook = prettydisplayhook
2009+
19582010
try:
19592011
(exit_value, output) = curses_wrapper(
19602012
main_curses, exec_args, config, options.interactive, locals_,

bpython/config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ def loadini(struct, configfile):
122122
'transpose_chars': 'C-t',
123123
'undo': 'C-r',
124124
'up_one_line': 'C-p',
125-
'yank_from_buffer': 'C-y'
125+
'yank_from_buffer': 'C-y',
126+
'debug': 'F12',
126127
},
127128
'cli': {
128129
'suggestion_width': 0.8,
@@ -211,6 +212,7 @@ def get_key_no_doublebind(command):
211212
struct.edit_current_block_key = get_key_no_doublebind('edit_current_block')
212213
struct.external_editor_key = get_key_no_doublebind('external_editor')
213214
struct.help_key = get_key_no_doublebind('help')
215+
struct.debug_key = get_key_no_doublebind('debug')
214216

215217
struct.pastebin_confirm = config.getboolean('general', 'pastebin_confirm')
216218
struct.pastebin_url = config.get('general', 'pastebin_url')

bpython/debugger.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/usr/bin/python2.7
2+
# -*- coding: utf-8 -*-
3+
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
4+
5+
"""
6+
Debugger factory. Set PYTHON_DEBUGGER to a module path that has a post_mortem
7+
function in it. Defaults to pdb. This allows alternate debuggers to be used,
8+
such as pycopia.debugger. :)
9+
"""
10+
from __future__ import absolute_import
11+
from __future__ import print_function
12+
from __future__ import unicode_literals
13+
from __future__ import division
14+
15+
16+
import os
17+
import sys
18+
19+
20+
post_mortem = None
21+
22+
def _get_debugger():
23+
global post_mortem
24+
modname = os.environ.get("PYTHON_DEBUGGER", "pdb")
25+
__import__(modname)
26+
mod = sys.modules[modname]
27+
pm = getattr(mod, "post_mortem")
28+
if pm.__code__.co_argcount > 2:
29+
post_mortem = pm
30+
else:
31+
def _post_mortem(t, ex, exval):
32+
return pm(t)
33+
post_mortem = _post_mortem
34+
35+
_get_debugger()
36+

bpython/repl.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,15 @@ def showtraceback(self):
159159
l.insert(0, "Traceback (most recent call last):\n")
160160
l[len(l):] = traceback.format_exception_only(t, v)
161161
finally:
162-
tblist = tb = None
162+
tblist = None
163+
# If someone has set sys.excepthook, we let that take precedence
164+
# over self.write
165+
if sys.excepthook is sys.__excepthook__:
166+
tb = None
167+
self.writetb(l)
168+
else:
169+
sys.excepthook(t, v, tb)
163170

164-
self.writetb(l)
165171

166172
def writetb(self, lines):
167173
"""This outputs the traceback and should be overridden for anything

0 commit comments

Comments
 (0)