| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,155 @@ | |||
| 1 | + import code | ||
| 2 | + import traceback | ||
| 3 | + import sys | ||
| 4 | + from pygments.style import Style | ||
| 5 | + from pygments.token import * | ||
| 6 | + from pygments.formatter import Formatter | ||
| 7 | + from curtsies.bpythonparse import parse | ||
| 8 | + from codeop import CommandCompiler, compile_command | ||
| 9 | + from pygments.lexers import get_lexer_by_name | ||
| 10 | + from pygments.styles import get_style_by_name | ||
| 11 | + | ||
| 12 | + default_colors = { | ||
| 13 | + Generic.Error:'R', | ||
| 14 | + Keyword:'d', | ||
| 15 | + Name:'c', | ||
| 16 | + Name.Builtin:'g', | ||
| 17 | + Comment:'b', | ||
| 18 | + String:'m', | ||
| 19 | + Error:'r', | ||
| 20 | + Literal:'d', | ||
| 21 | + Number:'M', | ||
| 22 | + Number.Integer:'d', | ||
| 23 | + Operator:'d', | ||
| 24 | + Punctuation:'d', | ||
| 25 | + Token:'d', | ||
| 26 | + Whitespace:'d', | ||
| 27 | + Token.Punctuation.Parenthesis:'R', | ||
| 28 | + Name.Function:'d', | ||
| 29 | + Name.Class:'d', | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + class BPythonFormatter(Formatter): | ||
| 33 | + """This is subclassed from the custom formatter for bpython. | ||
| 34 | + Its format() method receives the tokensource | ||
| 35 | + and outfile params passed to it from the | ||
| 36 | + Pygments highlight() method and slops | ||
| 37 | + them into the appropriate format string | ||
| 38 | + as defined above, then writes to the outfile | ||
| 39 | + object the final formatted string. This does not write real strings. It writes format string (FmtStr) objects. | ||
| 40 | + | ||
| 41 | + See the Pygments source for more info; it's pretty | ||
| 42 | + straightforward.""" | ||
| 43 | + | ||
| 44 | + def __init__(self, color_scheme, **options): | ||
| 45 | + self.f_strings = {} | ||
| 46 | + for k, v in color_scheme.iteritems(): | ||
| 47 | + self.f_strings[k] = '\x01%s' % (v,) | ||
| 48 | + Formatter.__init__(self, **options) | ||
| 49 | + | ||
| 50 | + def format(self, tokensource, outfile): | ||
| 51 | + o = '' | ||
| 52 | + | ||
| 53 | + for token, text in tokensource: | ||
| 54 | + while token not in self.f_strings: | ||
| 55 | + token = token.parent | ||
| 56 | + o += "%s\x03%s\x04" % (self.f_strings[token], text) | ||
| 57 | + outfile.write(parse(o.rstrip())) | ||
| 58 | + | ||
| 59 | + class Interp(code.InteractiveInterpreter): | ||
| 60 | + def __init__(self, locals=None): | ||
| 61 | + """Constructor. | ||
| 62 | + | ||
| 63 | + The optional 'locals' argument specifies the dictionary in | ||
| 64 | + which code will be executed; it defaults to a newly created | ||
| 65 | + dictionary with key "__name__" set to "__console__" and key | ||
| 66 | + "__doc__" set to None. | ||
| 67 | + | ||
| 68 | + We include an argument for the outfile to pass to the formatter for it to write to. | ||
| 69 | + | ||
| 70 | + """ | ||
| 71 | + if locals is None: | ||
| 72 | + locals = {"__name__": "__console__", "__doc__": None} | ||
| 73 | + self.locals = locals | ||
| 74 | + self.compile = CommandCompiler() | ||
| 75 | + | ||
| 76 | + # typically changed after being instantiated | ||
| 77 | + self.write = lambda stuff: sys.stderr.write(stuff) | ||
| 78 | + self.outfile = self | ||
| 79 | + | ||
| 80 | + def showsyntaxerror(self, filename=None): | ||
| 81 | + """Display the syntax error that just occurred. | ||
| 82 | + | ||
| 83 | + This doesn't display a stack trace because there isn't one. | ||
| 84 | + | ||
| 85 | + If a filename is given, it is stuffed in the exception instead | ||
| 86 | + of what was there before (because Python's parser always uses | ||
| 87 | + "<string>" when reading from a string). | ||
| 88 | + | ||
| 89 | + The output is written by self.write(), below. | ||
| 90 | + | ||
| 91 | + """ | ||
| 92 | + type, value, sys.last_traceback = sys.exc_info() | ||
| 93 | + sys.last_type = type | ||
| 94 | + sys.last_value = value | ||
| 95 | + if filename and type is SyntaxError: | ||
| 96 | + # Work hard to stuff the correct filename in the exception | ||
| 97 | + try: | ||
| 98 | + msg, (dummy_filename, lineno, offset, line) = value | ||
| 99 | + except: | ||
| 100 | + # Not the format we expect; leave it alone | ||
| 101 | + pass | ||
| 102 | + else: | ||
| 103 | + # Stuff in the right filename | ||
| 104 | + value = SyntaxError(msg, (filename, lineno, offset, line)) | ||
| 105 | + sys.last_value = value | ||
| 106 | + l = traceback.format_exception_only(type, value) | ||
| 107 | + tbtext = ''.join(l) | ||
| 108 | + lexer = get_lexer_by_name("pytb") | ||
| 109 | + self.format(tbtext,lexer) | ||
| 110 | + | ||
| 111 | + def showtraceback(self): | ||
| 112 | + """Display the exception that just occurred. | ||
| 113 | + | ||
| 114 | + We remove the first stack item because it is our own code. | ||
| 115 | + | ||
| 116 | + | ||
| 117 | + """ | ||
| 118 | + type, value, tb = sys.exc_info() | ||
| 119 | + sys.last_type = type | ||
| 120 | + sys.last_value = value | ||
| 121 | + sys.last_traceback = tb | ||
| 122 | + tblist = traceback.extract_tb(tb) | ||
| 123 | + del tblist[:1] | ||
| 124 | + l = traceback.format_list(tblist) | ||
| 125 | + if l: | ||
| 126 | + l.insert(0, "Traceback (most recent call last):\n") | ||
| 127 | + l[len(l):] = traceback.format_exception_only(type, value) | ||
| 128 | + tbtext = ''.join(l) | ||
| 129 | + lexer = get_lexer_by_name("pytb", stripall=True) | ||
| 130 | + | ||
| 131 | + self.format(tbtext,lexer) | ||
| 132 | + | ||
| 133 | + | ||
| 134 | + def format(self, tbtext, lexer): | ||
| 135 | + traceback_informative_formatter = BPythonFormatter(default_colors) | ||
| 136 | + traceback_code_formatter = BPythonFormatter({Token: ('d')}) | ||
| 137 | + tokens= list(lexer.get_tokens(tbtext)) | ||
| 138 | + | ||
| 139 | + no_format_mode = False | ||
| 140 | + cur_line = [] | ||
| 141 | + for token, text in tokens: | ||
| 142 | + if text.endswith('\n'): | ||
| 143 | + cur_line.append((token,text)) | ||
| 144 | + if no_format_mode: | ||
| 145 | + traceback_code_formatter.format(cur_line, self.outfile) | ||
| 146 | + no_format_mode = False | ||
| 147 | + else: | ||
| 148 | + traceback_informative_formatter.format(cur_line, self.outfile) | ||
| 149 | + cur_line = [] | ||
| 150 | + elif text == ' ' and cur_line == []: | ||
| 151 | + no_format_mode = True | ||
| 152 | + cur_line.append((token,text)) | ||
| 153 | + else: | ||
| 154 | + cur_line.append((token,text)) | ||
| 155 | + assert cur_line == [], cur_line | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -17,6 +17,7 @@ | |||
| 17 | 17 | from pygments import format | |
| 18 | 18 | from pygments.lexers import PythonLexer | |
| 19 | 19 | from pygments.formatters import TerminalFormatter | |
| 20 | + from interpreter import Interp | ||
| 20 | 21 | ||
| 21 | 22 | import blessings | |
| 22 | 23 | ||
@@ -236,7 +237,8 @@ def __init__(self, | |||
| 236 | 237 | # would be unsafe because initial | |
| 237 | 238 | # state was passed in | |
| 238 | 239 | if interp is None: | |
| 239 | - interp = code.InteractiveInterpreter(locals=locals_) | ||
| 240 | + interp = Interp(locals=locals_) | ||
| 241 | + interp.writetb = self.send_to_stderr | ||
| 240 | 242 | if banner is None: | |
| 241 | 243 | if config.help_key: | |
| 242 | 244 | banner = _('Welcome to bpython!') + ' ' + (_('Press <%s> for help.') % config.help_key) | |
@@ -867,9 +869,7 @@ def send_to_stderr(self, error): | |||
| 867 | 869 | lines = error.split('\n') | |
| 868 | 870 | if lines[-1]: | |
| 869 | 871 | self.current_stdouterr_line += lines[-1] | |
| 870 | - self.display_lines.extend([func_for_letter(self.config.color_scheme['error'])(line) | ||
| 871 | - for line in sum([paint.display_linize(line, self.width, blank_line=True) | ||
| 872 | - for line in lines[:-1]], [])]) | ||
| 872 | + self.display_lines.extend(sum([paint.display_linize(line, self.width, blank_line=True) for line in lines[:-1]], [])) | ||
| 873 | 873 | ||
| 874 | 874 | def send_to_stdin(self, line): | |
| 875 | 875 | if line.endswith('\n'): | |
@@ -1202,6 +1202,7 @@ def reevaluate(self, insert_into_history=False): | |||
| 1202 | 1202 | ||
| 1203 | 1203 | if not self.weak_rewind: | |
| 1204 | 1204 | self.interp = self.interp.__class__() | |
| 1205 | + self.interp.writetb = self.send_to_stderr | ||
| 1205 | 1206 | self.coderunner.interp = self.interp | |
| 1206 | 1207 | ||
| 1207 | 1208 | self.buffer = [] | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,42 @@ | |||
| 1 | + import unittest | ||
| 2 | + | ||
| 3 | + from bpython.curtsiesfrontend import interpreter | ||
| 4 | + from curtsies.fmtfuncs import * | ||
| 5 | + | ||
| 6 | + class TestInterpreter(unittest.TestCase): | ||
| 7 | + def test_syntaxerror(self): | ||
| 8 | + i = interpreter.Interp() | ||
| 9 | + a = [] | ||
| 10 | + | ||
| 11 | + def append_to_a(message): | ||
| 12 | + a.append(message) | ||
| 13 | + | ||
| 14 | + i.write = append_to_a | ||
| 15 | + i.runsource('1.1.1.1') | ||
| 16 | + | ||
| 17 | + expected = ''+u''+u' File '+green(u'"<input>"')+u', line '+bold(magenta(u'1'))+u'\n'+u' '+u'1.1'+u'.'+u'1.1'+u'\n'+u' '+u' '+u'^'+u'\n'+bold(red(u'SyntaxError'))+u': '+cyan(u'invalid syntax')+u'\n' | ||
| 18 | + | ||
| 19 | + self.assertEquals(str(plain('').join(a)), str(expected)) | ||
| 20 | + self.assertEquals(plain('').join(a), expected) | ||
| 21 | + | ||
| 22 | + def test_traceback(self): | ||
| 23 | + i = interpreter.Interp() | ||
| 24 | + a = [] | ||
| 25 | + | ||
| 26 | + def append_to_a(message): | ||
| 27 | + a.append(message) | ||
| 28 | + | ||
| 29 | + i.write = append_to_a | ||
| 30 | + | ||
| 31 | + def f(): | ||
| 32 | + return 1/0 | ||
| 33 | + | ||
| 34 | + def g(): | ||
| 35 | + return f() | ||
| 36 | + | ||
| 37 | + i.runsource('g()') | ||
| 38 | + | ||
| 39 | + expected = u'Traceback (most recent call last):\n'+''+u' File '+green(u'"<input>"')+u', line '+bold (magenta(u'1'))+u', in '+cyan(u'<module>')+u'\n'+''+bold(red(u'NameError'))+u': '+cyan(u"name 'g' is not defined")+u'\n' | ||
| 40 | + | ||
| 41 | + self.assertEquals(str(plain('').join(a)), str(expected)) | ||
| 42 | + self.assertEquals(plain('').join(a), expected) | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments