Make colored output configurable

Colored exception output is now disabled by default and configurable through
`hy.errors._hy_colored_errors` and the environment variable
`HY_COLORED_ERRORS`.

Likewise, Hy model/AST color printing is now configurable and disabled by
default.  The corresponding variables are `hy.models._hy_colored_ast_objects`
and `HY_COLORED_AST_OBJECTS`.

Closes hylang/hy#1429, closes hylang/hy#1510.
This commit is contained in:
Brandon T. Willard 2018-10-28 22:02:08 -05:00 committed by Kodi Arfer
parent e468d5f081
commit cadfa4152b
2 changed files with 38 additions and 23 deletions

View File

@ -15,6 +15,7 @@ from clint.textui import colored
_hy_filter_internal_errors = _initialize_env_var('HY_FILTER_INTERNAL_ERRORS', _hy_filter_internal_errors = _initialize_env_var('HY_FILTER_INTERNAL_ERRORS',
True) True)
_hy_colored_errors = _initialize_env_var('HY_COLORED_ERRORS', False)
class HyError(Exception): class HyError(Exception):
@ -73,9 +74,16 @@ class HyTypeError(HyLanguageError, TypeError):
source) source)
def __str__(self): def __str__(self):
global _hy_colored_errors
result = "" result = ""
if _hy_colored_errors:
from clint.textui import colored
red, green, yellow = colored.red, colored.green, colored.yellow
else:
red = green = yellow = lambda x: x
if all(getattr(self.expression, x, None) is not None if all(getattr(self.expression, x, None) is not None
for x in ("start_line", "start_column", "end_column")): for x in ("start_line", "start_column", "end_column")):
@ -97,28 +105,28 @@ class HyTypeError(HyLanguageError, TypeError):
start) start)
if len(source) == 1: if len(source) == 1:
result += ' %s\n' % colored.red(source[0]) result += ' %s\n' % red(source[0])
result += ' %s%s\n' % (' '*(start-1), result += ' %s%s\n' % (' '*(start-1),
colored.green('^' + '-'*(length-1) + '^')) green('^' + '-'*(length-1) + '^'))
if len(source) > 1: if len(source) > 1:
result += ' %s\n' % colored.red(source[0]) result += ' %s\n' % red(source[0])
result += ' %s%s\n' % (' '*(start-1), result += ' %s%s\n' % (' '*(start-1),
colored.green('^' + '-'*length)) green('^' + '-'*length))
if len(source) > 2: # write the middle lines if len(source) > 2: # write the middle lines
for line in source[1:-1]: for line in source[1:-1]:
result += ' %s\n' % colored.red("".join(line)) result += ' %s\n' % red("".join(line))
result += ' %s\n' % colored.green("-"*len(line)) result += ' %s\n' % green("-"*len(line))
# write the last line # write the last line
result += ' %s\n' % colored.red("".join(source[-1])) result += ' %s\n' % red("".join(source[-1]))
result += ' %s\n' % colored.green('-'*(end-1) + '^') result += ' %s\n' % green('-'*(end-1) + '^')
else: else:
result += ' File "%s", unknown location\n' % self.filename result += ' File "%s", unknown location\n' % self.filename
result += colored.yellow("%s: %s\n\n" % result += yellow("%s: %s\n\n" %
(self.__class__.__name__, (self.__class__.__name__,
self.message)) self.message))
return result return result
@ -199,18 +207,21 @@ class HySyntaxError(HyLanguageError, SyntaxError):
return HySyntaxError(message, filename, lineno, colno, source) return HySyntaxError(message, filename, lineno, colno, source)
def __str__(self): def __str__(self):
global _hy_colored_errors
output = traceback.format_exception_only(SyntaxError, output = traceback.format_exception_only(SyntaxError,
SyntaxError(*self.args)) SyntaxError(*self.args))
output[-1] = colored.yellow(output[-1]) if _hy_colored_errors:
if len(self.source) > 0: from hy.errors import colored
output[-2] = colored.green(output[-2]) output[-1] = colored.yellow(output[-1])
for line in output[::-2]: if len(self.source) > 0:
if line.strip().startswith( output[-2] = colored.green(output[-2])
'File "{}", line'.format(self.filename)): for line in output[::-2]:
break if line.strip().startswith(
output[-3] = colored.red(output[-3]) 'File "{}", line'.format(self.filename)):
break
output[-3] = colored.red(output[-3])
# Avoid "...expected str instance, ColoredString found" # Avoid "...expected str instance, ColoredString found"
return reduce(lambda x, y: x + y, output) return reduce(lambda x, y: x + y, output)

View File

@ -1,16 +1,17 @@
# Copyright 2019 the authors. # Copyright 2019 the authors.
# This file is part of Hy, which is free software licensed under the Expat # This file is part of Hy, which is free software licensed under the Expat
# license. See the LICENSE. # license. See the LICENSE.
from __future__ import unicode_literals from __future__ import unicode_literals
from contextlib import contextmanager from contextlib import contextmanager
from math import isnan, isinf from math import isnan, isinf
from hy import _initialize_env_var
from hy._compat import PY3, str_type, bytes_type, long_type, string_types from hy._compat import PY3, str_type, bytes_type, long_type, string_types
from fractions import Fraction from fractions import Fraction
from clint.textui import colored from clint.textui import colored
PRETTY = True PRETTY = True
_hy_colored_ast_objects = _initialize_env_var('HY_COLORED_AST_OBJECTS', False)
@contextmanager @contextmanager
@ -271,8 +272,9 @@ class HySequence(HyObject, list):
return str(self) if PRETTY else super(HySequence, self).__repr__() return str(self) if PRETTY else super(HySequence, self).__repr__()
def __str__(self): def __str__(self):
global _hy_colored_ast_objects
with pretty(): with pretty():
c = self.color c = self.color if _hy_colored_ast_objects else str
if self: if self:
return ("{}{}\n {}{}").format( return ("{}{}\n {}{}").format(
c(self.__class__.__name__), c(self.__class__.__name__),
@ -298,10 +300,12 @@ class HyDict(HySequence):
""" """
HyDict (just a representation of a dict) HyDict (just a representation of a dict)
""" """
color = staticmethod(colored.green)
def __str__(self): def __str__(self):
global _hy_colored_ast_objects
with pretty(): with pretty():
g = colored.green g = self.color if _hy_colored_ast_objects else str
if self: if self:
pairs = [] pairs = []
for k, v in zip(self[::2],self[1::2]): for k, v in zip(self[::2],self[1::2]):