diff --git a/hy/compiler.py b/hy/compiler.py index 00931db..e01f078 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -97,7 +97,7 @@ def builds(_type): unpythonic_chars = ["-"] really_ok = ["-"] - if True in (x in str_type(_type) for x in unpythonic_chars): + if any(x in unpythonic_chars for x in str_type(_type)): if _type not in really_ok: raise TypeError("`build' needs to be *post* translated strings, " "Mr. / Mrs. Hypser. -- `%s' sucks." % (_type)) @@ -419,22 +419,24 @@ class HyASTCompiler(object): if isinstance(expr, HyLambdaListKeyword): if expr not in expr._valid_types: - raise HyCompileError("{0} is not a valid " - "lambda-keyword.".format(repr(expr))) + raise HyTypeError(expr, "{0} is not a valid " + "lambda-keyword.".format(repr(expr))) if expr == "&rest" and lambda_keyword is None: lambda_keyword = expr elif expr == "&optional": if len(defaults) > 0: - raise HyCompileError("There can only be &optional " - "arguments or one &key argument") + raise HyTypeError(expr, + "There can only be &optional " + "arguments or one &key argument") lambda_keyword = expr elif expr == "&key": lambda_keyword = expr elif expr == "&kwargs": lambda_keyword = expr else: - raise HyCompileError("{0} is in an invalid " - "position.".format(repr(expr))) + raise HyTypeError(expr, + "{0} is in an invalid " + "position.".format(repr(expr))) # we don't actually care about this token, so we set # our state and continue to the next token... continue @@ -443,17 +445,20 @@ class HyASTCompiler(object): args.append(expr) elif lambda_keyword == "&rest": if varargs: - raise HyCompileError("There can only be one " - "&rest argument") + raise HyTypeError(expr, + "There can only be one " + "&rest argument") varargs = str(expr) elif lambda_keyword == "&key": if type(expr) != HyDict: - raise TypeError("There can only be one &key " - "argument") + raise HyTypeError(expr, + "There can only be one &key " + "argument") else: if len(defaults) > 0: - raise HyCompileError("There can only be &optional " - "arguments or one &key argument") + raise HyTypeError(expr, + "There can only be &optional " + "arguments or one &key argument") # As you can see, Python has a funny way of # defining keyword arguments. it = iter(expr) @@ -464,8 +469,9 @@ class HyASTCompiler(object): elif lambda_keyword == "&optional": if isinstance(expr, HyList): if not len(expr) == 2: - raise TypeError("optional args should be bare names " - "or 2-item lists") + raise HyTypeError(expr, + "optional args should be bare names " + "or 2-item lists") k, v = expr else: k = expr @@ -475,8 +481,9 @@ class HyASTCompiler(object): defaults.append(ret.force_expr) elif lambda_keyword == "&kwargs": if kwargs: - raise HyCompileError("There can only be one " - "&kwargs argument") + raise HyTypeError(expr, + "There can only be one " + "&kwargs argument") kwargs = str(expr) return ret, args, defaults, varargs, kwargs @@ -761,7 +768,8 @@ class HyASTCompiler(object): @builds("except") @builds("catch") def magic_internal_form(self, expr): - raise TypeError("Error: `%s' can't be used like that." % (expr[0])) + raise HyTypeError(expr, + "Error: `%s' can't be used like that." % (expr[0])) def _compile_catch_expression(self, expr, var): catch = expr.pop(0) # catch @@ -793,6 +801,11 @@ class HyASTCompiler(object): # let's pop variable and use it as name if len(exceptions) == 2: name = exceptions.pop(0) + if not isinstance(name, HySymbol): + raise HyTypeError( + exceptions, + "Exception storage target name must be a symbol.") + if sys.version_info[0] >= 3: # Python3 features a change where the Exception handler # moved the name from a Name() to a pure Python String type. diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index fc17892..694e10d 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -22,7 +22,7 @@ from __future__ import unicode_literals from hy import HyString -from hy.compiler import hy_compile, HyCompileError +from hy.compiler import hy_compile, HyCompileError, HyTypeError from hy.lex import tokenize import ast @@ -43,8 +43,11 @@ def cant_compile(expr): try: hy_compile(expr) assert False - except HyCompileError: - pass + except HyCompileError as e: + # Anything that can't be compiled should raise a user friendly + # error, otherwise it's a compiler bug. + assert isinstance(e.exception, HyTypeError) + assert e.traceback def test_ast_bad_type(): diff --git a/tests/compilers/test_compiler.py b/tests/compilers/test_compiler.py index 5f93144..246534d 100644 --- a/tests/compilers/test_compiler.py +++ b/tests/compilers/test_compiler.py @@ -27,11 +27,21 @@ if sys.version_info[0] <= 2 and sys.version_info[1] <= 6: else: import unittest - +from hy import compiler from hy.models.expression import HyExpression from hy.models.list import HyList from hy.models.symbol import HySymbol -from hy.compiler import HyASTCompiler + + +class CompilerTest(unittest.TestCase): + + def test_builds_with_dash(self): + self.assert_(callable(compiler.builds("foobar"))) + self.assert_(callable(compiler.builds("foo_bar"))) + self.assert_(callable(compiler.builds("-"))) + self.assertRaisesRegexp(TypeError, + "\*post\* translated strings", + compiler.builds, "foobar-with-dash-") class HyASTCompilerTest(unittest.TestCase): @@ -46,7 +56,7 @@ class HyASTCompilerTest(unittest.TestCase): return h def setUp(self): - self.c = HyASTCompiler() + self.c = compiler.HyASTCompiler() def test_fn_compiler_empty_function(self): ret = self.c.compile_function_def(