Skip to content

Commit

Permalink
Improve error formatting
Browse files Browse the repository at this point in the history
Refs   #812.
  • Loading branch information
evhub committed Nov 30, 2023
1 parent 7428bde commit f2eea1d
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 14 deletions.
3 changes: 2 additions & 1 deletion coconut/compiler/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
)

from coconut.constants import (
PY35,
specific_targets,
targets,
pseudo_targets,
Expand Down Expand Up @@ -1359,7 +1360,7 @@ def parse(
internal_assert(pre_procd is not None, "invalid deferred syntax error in pre-processing", err)
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:
except (RecursionError if PY35 else RuntimeError) as err:
raise CoconutException(
str(err), extra="try again with --recursion-limit greater than the current "
+ str(sys.getrecursionlimit()) + " (you may also need to increase --stack-size)",
Expand Down
2 changes: 1 addition & 1 deletion coconut/compiler/grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -2192,7 +2192,7 @@ class Grammar(object):
where_stmt_ref = where_item + where_suite

implicit_return = (
invalid_syntax(return_stmt, "expected expression but got return statement")
invalid_syntax(return_stmt, "assignment function expected expression as last statement but got return instead")
| attach(new_testlist_star_expr, implicit_return_handle)
)
implicit_return_where = Forward()
Expand Down
7 changes: 4 additions & 3 deletions coconut/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,12 +315,11 @@ def get_path_env_var(env_var, default):
)

tabideal = 4 # spaces to indent code for displaying

taberrfmt = 2 # spaces to indent exceptions

justify_len = 79 # ideal line length

taberrfmt = 2 # spaces to indent exceptions
min_squiggles_in_err_msg = 1
max_err_msg_lines = 10

# for pattern-matching
default_matcher_style = "python warn"
Expand Down Expand Up @@ -645,6 +644,8 @@ def get_path_env_var(env_var, default):
log_color_code = "93"

default_style = "default"
fake_styles = ("none", "list")

prompt_histfile = get_path_env_var(
"COCONUT_HISTORY_FILE",
os.path.join(coconut_home, ".coconut_history"),
Expand Down
22 changes: 19 additions & 3 deletions coconut/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
taberrfmt,
report_this_text,
min_squiggles_in_err_msg,
max_err_msg_lines,
)
from coconut.util import (
pickleable_obj,
Expand All @@ -38,6 +39,7 @@
clean,
get_displayable_target,
normalize_newlines,
highlight,
)

# -----------------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -153,8 +155,10 @@ def message(self, message, source, point, ln, extra=None, endpoint=None, filenam
point_ind = clip(point_ind, 0, len(part))
endpoint_ind = clip(endpoint_ind, point_ind, len(part))

message += "\n" + " " * taberrfmt + part
# add code to message
message += "\n" + " " * taberrfmt + highlight(part)

# add squiggles to message
if point_ind > 0 or endpoint_ind > 0:
err_len = endpoint_ind - point_ind
message += "\n" + " " * (taberrfmt + point_ind)
Expand Down Expand Up @@ -182,14 +186,26 @@ def message(self, message, source, point, ln, extra=None, endpoint=None, filenam

max_line_len = max(len(line) for line in lines)

# add top squiggles
message += "\n" + " " * (taberrfmt + point_ind)
if point_ind >= len(lines[0]):
message += "|"
else:
message += "/" + "~" * (len(lines[0]) - point_ind - 1)
message += "~" * (max_line_len - len(lines[0])) + "\n"
for line in lines:
message += "\n" + " " * taberrfmt + line

# add code
if len(lines) > max_err_msg_lines:
for i in range(max_err_msg_lines // 2):
message += "\n" + " " * taberrfmt + highlight(lines[i])
message += "\n" + " " * (taberrfmt // 2) + "..."
for i in range(len(lines) - max_err_msg_lines // 2, len(lines)):
message += "\n" + " " * taberrfmt + highlight(lines[i])
else:
for line in lines:
message += "\n" + " " * taberrfmt + highlight(line)

# add bottom squiggles
message += (
"\n\n" + " " * taberrfmt + "~" * endpoint_ind
+ ("^" if self.point_to_endpoint else "/" if 0 < endpoint_ind < len(lines[-1]) else "|")
Expand Down
16 changes: 16 additions & 0 deletions coconut/highlighter.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@

from coconut.root import * # NOQA

from pygments import highlight
from pygments.lexers import Python3Lexer, PythonConsoleLexer
from pygments.token import Text, Operator, Keyword, Name, Number
from pygments.lexer import words, bygroups
from pygments.util import shebang_matches
from pygments.formatters import Terminal256Formatter

from coconut.constants import (
highlight_builtins,
Expand All @@ -36,6 +38,9 @@
template_ext,
coconut_exceptions,
main_prompt,
style_env_var,
default_style,
fake_styles,
)

# -----------------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -113,3 +118,14 @@ def __init__(self, stripnl=False, stripall=False, ensurenl=True, tabsize=tabidea

def analyse_text(text):
return shebang_matches(text, shebang_regex)


def highlight_coconut_for_terminal(code):
"""Highlight Coconut code for the terminal."""
style = os.getenv(style_env_var, default_style)
if style not in fake_styles:
try:
return highlight(code, CoconutLexer(), Terminal256Formatter(style=style))
except Exception:
pass
return code
2 changes: 1 addition & 1 deletion coconut/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
VERSION = "3.0.4"
VERSION_NAME = None
# False for release, int >= 1 for develop
DEVELOP = 5
DEVELOP = 6
ALPHA = False # for pre releases rather than post releases

assert DEVELOP is False or DEVELOP >= 1, "DEVELOP must be False or an int >= 1"
Expand Down
13 changes: 8 additions & 5 deletions coconut/terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,15 +207,21 @@ def __init__(self, other=None):
self.patch_logging()

@classmethod
def enable_colors(cls):
def enable_colors(cls, file=None):
"""Attempt to enable CLI colors."""
if (
use_color is False
or use_color is None and file is not None and not isatty(file)
):
return False
if not cls.colors_enabled:
# necessary to resolve https://bugs.python.org/issue40134
try:
os.system("")
except BaseException:
logger.log_exc()
cls.colors_enabled = True
return True

def copy_from(self, other):
"""Copy other onto self."""
Expand Down Expand Up @@ -265,11 +271,8 @@ def display(
else:
raise CoconutInternalException("invalid logging level", level)

if use_color is False or (use_color is None and not isatty(file)):
color = None

if color:
self.enable_colors()
color = self.enable_colors(file) and color

raw_message = " ".join(str(msg) for msg in messages)
# if there's nothing to display but there is a sig, display the sig
Expand Down
16 changes: 16 additions & 0 deletions coconut/tests/src/extras.coco
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,22 @@ import abc
except CoconutStyleError as err:
assert str(err) == """found unused import 'abc' (add '# NOQA' to suppress) (remove --strict to downgrade to a warning) (line 1)
import abc"""
assert_raises(-> parse("""class A(object):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15"""), CoconutStyleError, err_has="\n ...\n")

setup(line_numbers=False, strict=True, target="sys")
assert_raises(-> parse("await f x"), CoconutParseError, err_has='invalid use of the keyword "await"')
Expand Down
13 changes: 13 additions & 0 deletions coconut/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,19 @@ def replace_all(inputstr, all_to_replace, replace_to):
return inputstr


def highlight(code):
"""Attempt to highlight Coconut code for the terminal."""
from coconut.terminal import logger # hide to remove circular deps
if logger.enable_colors(sys.stdout) and logger.enable_colors(sys.stderr):
try:
from coconut.highlighter import highlight_coconut_for_terminal
except ImportError:
pass
else:
return highlight_coconut_for_terminal(code)
return code


# -----------------------------------------------------------------------------------------------------------------------
# VERSIONING:
# -----------------------------------------------------------------------------------------------------------------------
Expand Down

0 comments on commit f2eea1d

Please sign in to comment.