Enhance error handling
We now have a full backtrace if the compiler bugs, and if it's a user problem we indicate him where he failed. Signed-off-by: Julien Danjou <julien@danjou.info>
This commit is contained in:
parent
fdb6bf944c
commit
6a09179809
@ -39,26 +39,36 @@ from collections import defaultdict
|
|||||||
import codecs
|
import codecs
|
||||||
import ast
|
import ast
|
||||||
import sys
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
|
||||||
class HyCompileError(HyError):
|
class HyCompileError(HyError):
|
||||||
def __init__(self, exception,
|
def __init__(self, exception, traceback=None):
|
||||||
start_line=0, start_column=0):
|
|
||||||
self.exception = exception
|
self.exception = exception
|
||||||
self.start_line = start_line
|
self.traceback = traceback
|
||||||
self.start_column = start_column
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.start_line == 0:
|
if isinstance(self.exception, HyTypeError):
|
||||||
return("Internal Compiler Bug\n⤷ %s: %s"
|
return str(self.exception)
|
||||||
% (self.exception.__class__.__name__,
|
if self.traceback:
|
||||||
self.exception))
|
tb = "".join(traceback.format_tb(self.traceback)).strip()
|
||||||
return ("Compilation error at line %d, column %d\n%s: %s"
|
else:
|
||||||
% (self.start_line, self.start_column,
|
tb = "No traceback available. 😟"
|
||||||
self.exception.__class__.__name__,
|
return("Internal Compiler Bug 😱\n⤷ %s: %s\nCompilation traceback:\n%s"
|
||||||
self.exception))
|
% (self.exception.__class__.__name__,
|
||||||
|
self.exception, tb))
|
||||||
|
|
||||||
|
|
||||||
|
class HyTypeError(TypeError):
|
||||||
|
def __init__(self, expression, message):
|
||||||
|
super(HyTypeError, self).__init__(message)
|
||||||
|
self.expression = expression
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return (self.message + " (line %s, column %d)"
|
||||||
|
% (self.expression.start_line,
|
||||||
|
self.expression.start_column))
|
||||||
|
|
||||||
_compile_table = {}
|
_compile_table = {}
|
||||||
|
|
||||||
|
|
||||||
@ -87,10 +97,9 @@ def builds(_type):
|
|||||||
|
|
||||||
|
|
||||||
def _raise_wrong_args_number(expression, error):
|
def _raise_wrong_args_number(expression, error):
|
||||||
err = TypeError(error % (expression.pop(0), len(expression)))
|
raise HyTypeError(expression,
|
||||||
err.start_line = expression.start_line
|
error % (expression.pop(0),
|
||||||
err.start_column = expression.start_column
|
len(expression)))
|
||||||
raise err
|
|
||||||
|
|
||||||
|
|
||||||
def checkargs(exact=None, min=None, max=None):
|
def checkargs(exact=None, min=None, max=None):
|
||||||
@ -134,15 +143,10 @@ class HyASTCompiler(object):
|
|||||||
# another HyCompileError!
|
# another HyCompileError!
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if isinstance(e, HyError):
|
raise HyCompileError(e, sys.exc_info()[2])
|
||||||
raise HyCompileError(
|
|
||||||
exception=e,
|
|
||||||
start_line=getattr(e, "start_line", 0),
|
|
||||||
start_column=getattr(e, "start_column", 0))
|
|
||||||
raise HyCompileError(exception=e)
|
|
||||||
|
|
||||||
raise HyCompileError(
|
raise HyCompileError(
|
||||||
"Unknown type - `%s' - %s" % (str(type(tree)), tree))
|
Exception("Unknown type: `%s'" % (str(type(tree)))))
|
||||||
|
|
||||||
def _mangle_branch(self, tree, start_line, start_column):
|
def _mangle_branch(self, tree, start_line, start_column):
|
||||||
# If tree is empty, just return a pass statement
|
# If tree is empty, just return a pass statement
|
||||||
@ -251,13 +255,14 @@ class HyASTCompiler(object):
|
|||||||
|
|
||||||
for e in expr:
|
for e in expr:
|
||||||
if not len(e):
|
if not len(e):
|
||||||
raise TypeError("Empty list not allowed in `try'")
|
raise HyTypeError(e, "Empty list not allowed in `try'")
|
||||||
|
|
||||||
if e[0] in (HySymbol("except"), HySymbol("catch")):
|
if e[0] in (HySymbol("except"), HySymbol("catch")):
|
||||||
handlers.append(self.compile(e))
|
handlers.append(self.compile(e))
|
||||||
elif e[0] == HySymbol("else"):
|
elif e[0] == HySymbol("else"):
|
||||||
if orelse:
|
if orelse:
|
||||||
raise TypeError(
|
raise HyTypeError(
|
||||||
|
e,
|
||||||
"`try' cannot have more than one `else'")
|
"`try' cannot have more than one `else'")
|
||||||
else:
|
else:
|
||||||
orelse = self._code_branch(self.compile(e[1:]),
|
orelse = self._code_branch(self.compile(e[1:]),
|
||||||
@ -265,18 +270,20 @@ class HyASTCompiler(object):
|
|||||||
e.start_column)
|
e.start_column)
|
||||||
elif e[0] == HySymbol("finally"):
|
elif e[0] == HySymbol("finally"):
|
||||||
if finalbody:
|
if finalbody:
|
||||||
raise TypeError(
|
raise HyTypeError(
|
||||||
|
e,
|
||||||
"`try' cannot have more than one `finally'")
|
"`try' cannot have more than one `finally'")
|
||||||
else:
|
else:
|
||||||
finalbody = self._code_branch(self.compile(e[1:]),
|
finalbody = self._code_branch(self.compile(e[1:]),
|
||||||
e.start_line,
|
e.start_line,
|
||||||
e.start_column)
|
e.start_column)
|
||||||
else:
|
else:
|
||||||
raise TypeError("Unknown expression in `try'")
|
raise HyTypeError(e, "Unknown expression in `try'")
|
||||||
|
|
||||||
# Using (else) without (except) is verboten!
|
# Using (else) without (except) is verboten!
|
||||||
if orelse and not handlers:
|
if orelse and not handlers:
|
||||||
raise TypeError(
|
raise HyTypeError(
|
||||||
|
e,
|
||||||
"`try' cannot have `else' without `except'")
|
"`try' cannot have `else' without `except'")
|
||||||
|
|
||||||
# (try) or (try BODY)
|
# (try) or (try BODY)
|
||||||
@ -346,9 +353,11 @@ class HyASTCompiler(object):
|
|||||||
# or
|
# or
|
||||||
# []
|
# []
|
||||||
if not isinstance(exceptions, HyList):
|
if not isinstance(exceptions, HyList):
|
||||||
raise TypeError("`%s' exceptions list is not a list" % catch)
|
raise HyTypeError(exceptions,
|
||||||
|
"`%s' exceptions list is not a list" % catch)
|
||||||
if len(exceptions) > 2:
|
if len(exceptions) > 2:
|
||||||
raise TypeError("`%s' exceptions list is too long" % catch)
|
raise HyTypeError(exceptions,
|
||||||
|
"`%s' exceptions list is too long" % catch)
|
||||||
|
|
||||||
# [variable [list of exceptions]]
|
# [variable [list of exceptions]]
|
||||||
# let's pop variable and use it as name
|
# let's pop variable and use it as name
|
||||||
@ -386,7 +395,8 @@ class HyASTCompiler(object):
|
|||||||
elif isinstance(exceptions_list, HySymbol):
|
elif isinstance(exceptions_list, HySymbol):
|
||||||
_type = self.compile(exceptions_list)
|
_type = self.compile(exceptions_list)
|
||||||
else:
|
else:
|
||||||
raise TypeError("`%s' needs a valid exception list" % catch)
|
raise HyTypeError(exceptions,
|
||||||
|
"`%s' needs a valid exception list" % catch)
|
||||||
|
|
||||||
body = self._code_branch([self.compile(x) for x in expr],
|
body = self._code_branch([self.compile(x) for x in expr],
|
||||||
expr.start_line,
|
expr.start_line,
|
||||||
@ -592,7 +602,7 @@ class HyASTCompiler(object):
|
|||||||
expr.pop(0) # decorate-with
|
expr.pop(0) # decorate-with
|
||||||
fn = self.compile(expr.pop(-1))
|
fn = self.compile(expr.pop(-1))
|
||||||
if type(fn) != ast.FunctionDef:
|
if type(fn) != ast.FunctionDef:
|
||||||
raise TypeError("Decorated a non-function")
|
raise HyTypeError(expr, "Decorated a non-function")
|
||||||
fn.decorator_list = [self.compile(x) for x in expr]
|
fn.decorator_list = [self.compile(x) for x in expr]
|
||||||
return fn
|
return fn
|
||||||
|
|
||||||
@ -603,7 +613,7 @@ class HyASTCompiler(object):
|
|||||||
|
|
||||||
args = expr.pop(0)
|
args = expr.pop(0)
|
||||||
if len(args) > 2 or len(args) < 1:
|
if len(args) > 2 or len(args) < 1:
|
||||||
raise TypeError("with needs [arg (expr)] or [(expr)]")
|
raise HyTypeError(expr, "with needs [arg (expr)] or [(expr)]")
|
||||||
|
|
||||||
args.reverse()
|
args.reverse()
|
||||||
ctx = self.compile(args.pop(0))
|
ctx = self.compile(args.pop(0))
|
||||||
@ -677,7 +687,7 @@ class HyASTCompiler(object):
|
|||||||
kwargs = expr.pop(0)
|
kwargs = expr.pop(0)
|
||||||
|
|
||||||
if type(call) != ast.Call:
|
if type(call) != ast.Call:
|
||||||
raise TypeError("kwapplying a non-call")
|
raise HyTypeError(expr, "kwapplying a non-call")
|
||||||
|
|
||||||
if type(kwargs) != HyDict:
|
if type(kwargs) != HyDict:
|
||||||
raise TypeError("kwapplying with a non-dict")
|
raise TypeError("kwapplying with a non-dict")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user