From 39785b46574a2d3e7e72484abe44b072d8494dd4 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Wed, 20 Sep 2017 10:40:52 -0700 Subject: [PATCH] Fix a crash in HyTypeError.__str__ --- NEWS | 2 ++ hy/errors.py | 68 ++++++++++++++++++++++++++--------------------- tests/test_bin.py | 10 +++++++ 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/NEWS b/NEWS index cc19a6b..8fe2294 100644 --- a/NEWS +++ b/NEWS @@ -39,6 +39,8 @@ Changes from 0.13.0 * Fixed a crash when `with` suppresses an exception. `with` now returns `None` in this case. * Fixed a crash when --repl-output-fn raises an exception + * Fixed a crash when HyTypeError was raised with objects that had no + source position * `assoc` now evaluates its arguments only once each * `break` and `continue` now raise an error when given arguments instead of silently ignoring them diff --git a/hy/errors.py b/hy/errors.py index 6fb00fd..a1e04c5 100644 --- a/hy/errors.py +++ b/hy/errors.py @@ -43,41 +43,47 @@ class HyTypeError(TypeError): def __str__(self): - line = self.expression.start_line - start = self.expression.start_column - end = self.expression.end_column - - source = [] - if self.source is not None: - source = self.source.split("\n")[line-1:self.expression.end_line] - - if line == self.expression.end_line: - length = end - start - else: - length = len(source[0]) - start - result = "" - result += ' File "%s", line %d, column %d\n\n' % (self.filename, - line, - start) + if all(getattr(self.expression, x, None) is not None + for x in ("start_line", "start_column", "end_column")): - if len(source) == 1: - result += ' %s\n' % colored.red(source[0]) - result += ' %s%s\n' % (' '*(start-1), - colored.green('^' + '-'*(length-1) + '^')) - if len(source) > 1: - result += ' %s\n' % colored.red(source[0]) - result += ' %s%s\n' % (' '*(start-1), - colored.green('^' + '-'*length)) - if len(source) > 2: # write the middle lines - for line in source[1:-1]: - result += ' %s\n' % colored.red("".join(line)) - result += ' %s\n' % colored.green("-"*len(line)) + line = self.expression.start_line + start = self.expression.start_column + end = self.expression.end_column - # write the last line - result += ' %s\n' % colored.red("".join(source[-1])) - result += ' %s\n' % colored.green('-'*(end-1) + '^') + source = [] + if self.source is not None: + source = self.source.split("\n")[line-1:self.expression.end_line] + + if line == self.expression.end_line: + length = end - start + else: + length = len(source[0]) - start + + result += ' File "%s", line %d, column %d\n\n' % (self.filename, + line, + start) + + if len(source) == 1: + result += ' %s\n' % colored.red(source[0]) + result += ' %s%s\n' % (' '*(start-1), + colored.green('^' + '-'*(length-1) + '^')) + if len(source) > 1: + result += ' %s\n' % colored.red(source[0]) + result += ' %s%s\n' % (' '*(start-1), + colored.green('^' + '-'*length)) + if len(source) > 2: # write the middle lines + for line in source[1:-1]: + result += ' %s\n' % colored.red("".join(line)) + result += ' %s\n' % colored.green("-"*len(line)) + + # write the last line + result += ' %s\n' % colored.red("".join(source[-1])) + result += ' %s\n' % colored.green('-'*(end-1) + '^') + + else: + result += ' File "%s", unknown location\n' % self.filename result += colored.yellow("%s: %s\n\n" % (self.__class__.__name__, diff --git a/tests/test_bin.py b/tests/test_bin.py index ad2ea13..0b2a0ad 100644 --- a/tests/test_bin.py +++ b/tests/test_bin.py @@ -138,6 +138,16 @@ def test_bin_hy_stdin_except_do(): assert "zzz" in output +def test_bin_hy_stdin_unlocatable_hytypeerror(): + # https://github.com/hylang/hy/issues/1412 + # The chief test of interest here is the returncode assertion + # inside run_cmd. + _, err = run_cmd("hy", """ + (import hy.errors) + (raise (hy.errors.HyTypeError '[] (+ "A" "Z")))""") + assert "AZ" in err + + def test_bin_hy_stdin_bad_repr(): # https://github.com/hylang/hy/issues/1389 output, err = run_cmd("hy", """