From fb04ee69d72f8eeef5d5a7c15898b5c883bc0f0d Mon Sep 17 00:00:00 2001 From: Evan Hubinger Date: Fri, 3 Nov 2023 21:14:27 -0700 Subject: [PATCH] Improve exception formatting Resolves #794. --- coconut/compiler/compiler.py | 8 ++++---- coconut/exceptions.py | 7 +++++-- coconut/root.py | 2 +- .../tests/src/cocotest/agnostic/primary_2.coco | 5 ++--- coconut/tests/src/extras.coco | 15 +++++++++++++++ 5 files changed, 27 insertions(+), 10 deletions(-) diff --git a/coconut/compiler/compiler.py b/coconut/compiler/compiler.py index 74ba9feea..8ad08f02f 100644 --- a/coconut/compiler/compiler.py +++ b/coconut/compiler/compiler.py @@ -1218,10 +1218,10 @@ def make_err(self, errtype, message, original, loc=0, ln=None, extra=None, refor kwargs["extra"] = extra return errtype(message, snippet, loc_in_snip, ln, endpoint=endpt_in_snip, filename=self.filename, **kwargs) - def make_syntax_err(self, err, original): + def make_syntax_err(self, err, original, after_parsing=False): """Make a CoconutSyntaxError from a CoconutDeferredSyntaxError.""" msg, loc = err.args - return self.make_err(CoconutSyntaxError, msg, original, loc) + return self.make_err(CoconutSyntaxError, msg, original, loc, endpoint=not after_parsing) def make_parse_err(self, err, msg=None, include_ln=True, **kwargs): """Make a CoconutParseError from a ParseBaseException.""" @@ -1328,7 +1328,7 @@ def parse( filename=filename, incremental_cache_filename=incremental_cache_filename, )) - pre_procd = None + pre_procd = parsed = None try: with logger.gather_parsing_stats(): try: @@ -1339,7 +1339,7 @@ def parse( raise self.make_parse_err(err) except CoconutDeferredSyntaxError as err: internal_assert(pre_procd is not None, "invalid deferred syntax error in pre-processing", err) - raise self.make_syntax_err(err, pre_procd) + raise self.make_syntax_err(err, pre_procd, after_parsing=parsed is not None) # RuntimeError, not RecursionError, for Python < 3.5 except RuntimeError as err: raise CoconutException( diff --git a/coconut/exceptions.py b/coconut/exceptions.py index 61319e4ed..341ef3831 100644 --- a/coconut/exceptions.py +++ b/coconut/exceptions.py @@ -180,11 +180,14 @@ def message(self, message, source, point, ln, extra=None, endpoint=None, filenam point_ind = clip(point_ind, 0, len(lines[0])) endpoint_ind = clip(endpoint_ind, 0, len(lines[-1])) + max_line_len = max(len(line) for line in lines) + message += "\n" + " " * (taberrfmt + point_ind) if point_ind >= len(lines[0]): - message += "|\n" + message += "|" else: - message += "/" + "~" * (len(lines[0]) - point_ind - 1) + "\n" + message += "/" + "~" * (len(lines[0]) - point_ind - 1) + message += "~" * (max_line_len - len(lines[0])) + "\n" for line in lines: message += "\n" + " " * taberrfmt + line message += ( diff --git a/coconut/root.py b/coconut/root.py index b78fdbac9..26a67c81e 100644 --- a/coconut/root.py +++ b/coconut/root.py @@ -26,7 +26,7 @@ VERSION = "3.0.3" VERSION_NAME = None # False for release, int >= 1 for develop -DEVELOP = 16 +DEVELOP = 17 ALPHA = False # for pre releases rather than post releases assert DEVELOP is False or DEVELOP >= 1, "DEVELOP must be False or an int >= 1" diff --git a/coconut/tests/src/cocotest/agnostic/primary_2.coco b/coconut/tests/src/cocotest/agnostic/primary_2.coco index 9e27b0f6f..e49eae5c6 100644 --- a/coconut/tests/src/cocotest/agnostic/primary_2.coco +++ b/coconut/tests/src/cocotest/agnostic/primary_2.coco @@ -1,11 +1,10 @@ import collections import collections.abc import weakref +import sys -if TYPE_CHECKING: +if TYPE_CHECKING or sys.version_info >= (3, 5): from typing import Any, Iterable -else: - Any = Iterable = None from importlib import reload # NOQA from .util import assert_raises, typed_eq diff --git a/coconut/tests/src/extras.coco b/coconut/tests/src/extras.coco index 2e012d6c0..bf5ded5f1 100644 --- a/coconut/tests/src/extras.coco +++ b/coconut/tests/src/extras.coco @@ -277,6 +277,21 @@ def gam_eps_rate(bitarr) = ( else: assert False + try: + parse(""" +def f(x=1, y) = x, y + +class A + +def g(x) = x + """.strip()) + except CoconutSyntaxError as err: + err_str = str(err) + assert "non-default arguments must come first" in err_str, err_str + assert "class A" not in err_str, err_str + else: + assert False + assert parse("def f(x):\n ${var}", "xonsh") == "def f(x):\n ${var}\n" assert "data ABC" not in parse("data ABC:\n ${var}", "xonsh")