Skip to content

Commit

Permalink
Retain compiled source and file 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 Dec 7, 2018
1 parent 9efd2a9 commit f9e9fd8
Show file tree
Hide file tree
Showing 17 changed files with 610 additions and 349 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
46 changes: 24 additions & 22 deletions hy/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@

import hy
from hy.lex import hy_parse, mangle
from hy.lex.exceptions import LexException, PrematureEndOfInput
from hy.lex.exceptions import PrematureEndOfInput
from hy.compiler import HyASTCompiler, hy_compile, hy_eval
from hy.errors import HyTypeError
from hy.errors import HyTypeError, HyLanguageError, HySyntaxError
from hy.importer import runhy
from hy.completer import completion, Completer
from hy.macros import macro, require
Expand Down Expand Up @@ -101,15 +101,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 @@ -121,17 +117,20 @@ def ast_callback(main_ast, expr_ast):
[ast.Expr(expr_ast.body)])
print(astor.to_source(new_ast))

value = hy_eval(do, self.locals,
value = hy_eval(do, self.locals, self.module,
ast_callback=ast_callback,
compiler=self.hy_compiler)
compiler=self.hy_compiler,
filename=filename,
source=source)

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
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 @@ -208,17 +207,19 @@ 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)
require("hy.cmdline", "__main__", assignments="ALL")
pretty_error(hy_eval, tree, None, importlib.import_module('__main__'))
def run_command(source, filename=None):
tree = hy_parse(source, filename=filename)
__main__ = importlib.import_module('__main__')
require("hy.cmdline", __main__, assignments="ALL")
pretty_error(hy_eval, tree, None, __main__, filename=filename,
source=source)
return 0


Expand Down Expand Up @@ -340,7 +341,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 @@ -356,7 +357,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 @@ -447,11 +448,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 f9e9fd8

Please sign in to comment.