Skip to content

Commit

Permalink
Retain source and stack information for exceptions
Browse files Browse the repository at this point in the history
This commit refactors the exception/error classes and their handling.
It also retains Hy source strings and their originating file information, when
available, all throughout the core parser and compiler functions.

As well, with these changes, calling code is no longer responsible for providing
source and file details to exceptions,

Closes hylang#657.
  • Loading branch information
brandonwillard committed Oct 29, 2018
1 parent 4132adb commit b624ccc
Show file tree
Hide file tree
Showing 15 changed files with 510 additions and 327 deletions.
39 changes: 35 additions & 4 deletions hy/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
import __builtin__ as builtins
except ImportError:
import builtins # NOQA
import sys, keyword

import sys
import keyword
import textwrap

PY3 = sys.version_info[0] >= 3
PY35 = sys.version_info >= (3, 5)
Expand All @@ -22,11 +25,39 @@
long_type = int if PY3 else long # NOQA
string_types = str if PY3 else basestring # NOQA

#
# Inspired by the same-named `six` functions.
#
if PY3:
exec('def raise_empty(t, *args): raise t(*args) from None')
raise_src = textwrap.dedent('''
def raise_from(value, from_value):
try:
raise value from from_value
finally:
traceback = None
''')

def reraise(exc_type, value, traceback=None):
try:
raise value.with_traceback(traceback)
finally:
traceback = None

else:
def raise_empty(t, *args):
raise t(*args)
def raise_from(value, from_value=None):
raise value

raise_src = textwrap.dedent('''
def reraise(exc_type, value, traceback=None):
try:
raise exc_type, value, traceback
finally:
traceback = None
''')

raise_code = compile(raise_src, __file__, 'exec')
exec(raise_code)


def isidentifier(x):
if x in ('True', 'False', 'None', 'print'):
Expand Down
50 changes: 24 additions & 26 deletions hy/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
import astor.code_gen

import hy
from hy.lex import LexException, PrematureEndOfInput, mangle
from hy.compiler import HyTypeError, hy_compile

from hy.lex import mangle
from hy.lex.exceptions import PrematureEndOfInput
from hy.errors import HyLanguageError, HySyntaxError
from hy.compiler import hy_compile
from hy.importer import hy_eval, hy_parse, runhy
from hy.completer import completion, Completer
from hy.macros import macro, require
Expand Down Expand Up @@ -83,15 +86,11 @@ def error_handler(e, use_simple_traceback=False):
self.showtraceback()

try:
try:
do = hy_parse(source)
except PrematureEndOfInput:
return True
except LexException as e:
if e.source is None:
e.source = source
e.filename = filename
error_handler(e, use_simple_traceback=True)
do = hy_parse(source, filename=filename)
except PrematureEndOfInput:
return True
except HySyntaxError as e:
error_handler(e, use_simple_traceback=SIMPLE_TRACEBACKS)
return False

try:
Expand All @@ -102,16 +101,13 @@ def ast_callback(main_ast, expr_ast):
new_ast = ast.Module(main_ast.body +
[ast.Expr(expr_ast.body)])
print(astor.to_source(new_ast))

value = hy_eval(do, self.locals, "__console__",
ast_callback)
except HyTypeError as e:
if e.source is None:
e.source = source
e.filename = filename
error_handler(e, use_simple_traceback=SIMPLE_TRACEBACKS)
return False
ast_callback, filename=filename, source=source)
except SystemExit:
raise
except Exception as e:
error_handler(e)
error_handler(e, use_simple_traceback=SIMPLE_TRACEBACKS)
return False

if value is not None:
Expand Down Expand Up @@ -190,16 +186,17 @@ def ideas_macro(ETname):
def pretty_error(func, *args, **kw):
try:
return func(*args, **kw)
except (HyTypeError, LexException) as e:
except HyLanguageError as e:
if SIMPLE_TRACEBACKS:
print(e, file=sys.stderr)
sys.exit(1)
raise


def run_command(source):
tree = hy_parse(source)
pretty_error(hy_eval, tree, module_name="__main__")
def run_command(source, filename=None):
tree = hy_parse(source, filename=filename)
pretty_error(hy_eval, tree, module_name="__main__", filename=filename,
source=source)
return 0


Expand Down Expand Up @@ -321,7 +318,7 @@ def cmdline_handler(scriptname, argv):

if options.command:
# User did "hy -c ..."
return run_command(options.command)
return run_command(options.command, filename='<string>')

if options.mod:
# User did "hy -m ..."
Expand All @@ -337,7 +334,7 @@ def cmdline_handler(scriptname, argv):
if options.args:
if options.args[0] == "-":
# Read the program from stdin
return run_command(sys.stdin.read())
return run_command(sys.stdin.read(), filename='<stdin>')

else:
# User did "hy <filename>"
Expand Down Expand Up @@ -429,11 +426,12 @@ def hy2py_main():

if options.FILE is None or options.FILE == '-':
source = sys.stdin.read()
hst = pretty_error(hy_parse, source, filename='<stdin>')
else:
with io.open(options.FILE, 'r', encoding='utf-8') as source_file:
source = source_file.read()
hst = hy_parse(source, filename=options.FILE)

hst = pretty_error(hy_parse, source)
if options.with_source:
# need special printing on Windows in case the
# codepage doesn't support utf-8 characters
Expand Down
Loading

0 comments on commit b624ccc

Please sign in to comment.