You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

118 lines
4.8 KiB

# coding: utf8
from __future__ import unicode_literals, print_function
import os
from .util import color, supports_ansi, NO_UTF8
LINE_EDGE = "└─" if not NO_UTF8 else "|_"
LINE_FORK = "├─" if not NO_UTF8 else "|__"
LINE_PATH = "──" if not NO_UTF8 else "__"
class TracebackPrinter(object):
def __init__(
self,
color_error="red",
color_tb="blue",
color_highlight="yellow",
indent=2,
tb_base=None,
tb_exclude=tuple(),
tb_range_start=-5,
tb_range_end=-2,
):
"""Initialize a traceback printer.
color_error (unicode / int): Color name or code for errors.
color_tb (unicode / int): Color name or code for traceback headline.
color_highlight (unicode / int): Color name or code for highlights.
indent (int): Indentation in spaces.
tb_base (unicode): Name of directory to use to show relative paths. For
example, "thinc" will look for the last occurence of "/thinc/" in
a path and only show path to the right of it.
tb_exclude (tuple): List of filenames to exclude from traceback.
tb_range_start (int): The starting index from a traceback to include.
tb_range_end (int): The final index from a traceback to include. If None
the traceback will continue until the last record.
RETURNS (TracebackPrinter): The traceback printer.
"""
self.color_error = color_error
self.color_tb = color_tb
self.color_highlight = color_highlight
self.indent = " " * indent
if tb_base == ".":
tb_base = "{}{}".format(os.getcwd(), os.path.sep)
elif tb_base is not None:
tb_base = "/{}/".format(tb_base)
self.tb_base = tb_base
self.tb_exclude = tuple(tb_exclude)
self.tb_range_start = tb_range_start
self.tb_range_end = tb_range_end
self.supports_ansi = supports_ansi()
def __call__(self, title, *texts, **settings):
"""Output custom formatted tracebacks and errors.
title (unicode): The message title.
*texts (unicode): The texts to print (one per line).
highlight (unicode): Optional sequence to highlight in the traceback,
e.g. the bad value that caused the error.
tb (iterable): The traceback, e.g. generated by traceback.extract_stack().
RETURNS (unicode): The formatted traceback. Can be printed or raised
by custom exception.
"""
highlight = settings.get("highlight", False)
tb = settings.get("tb", None)
if self.supports_ansi: # use first line as title
title = color(title, fg=self.color_error, bold=True)
info = "\n" + "\n".join([self.indent + text for text in texts]) if texts else ""
tb = self._get_traceback(tb, highlight) if tb else ""
msg = "\n\n{}{}{}{}\n".format(self.indent, title, info, tb)
return msg
def _get_traceback(self, tb, highlight):
# Exclude certain file names from traceback
tb = [record for record in tb if not record[0].endswith(self.tb_exclude)]
tb_range = (
tb[self.tb_range_start : self.tb_range_end]
if self.tb_range_end is not None
else tb[self.tb_range_start :]
)
tb_list = [
self._format_traceback(path, line, fn, text, i, len(tb_range), highlight)
for i, (path, line, fn, text) in enumerate(tb_range)
]
tb_data = "\n".join(tb_list).strip()
title = "Traceback:"
if self.supports_ansi:
title = color(title, fg=self.color_tb, bold=True)
return "\n\n{indent}{title}\n{indent}{tb}".format(
title=title, tb=tb_data, indent=self.indent
)
def _format_traceback(self, path, line, fn, text, i, count, highlight):
template = "{base_indent}{indent} {fn} in {path}:{line}{text}"
indent = (LINE_EDGE if i == count - 1 else LINE_FORK) + LINE_PATH * i
if self.tb_base and self.tb_base in path:
path = path.rsplit(self.tb_base, 1)[1]
text = self._format_user_error(text, i, highlight) if i == count - 1 else ""
if self.supports_ansi:
fn = color(fn, bold=True)
path = color(path, underline=True)
return template.format(
base_indent=self.indent,
line=line,
indent=indent,
text=text,
fn=fn,
path=path,
)
def _format_user_error(self, text, i, highlight):
spacing = " " * i + " >>>"
if self.supports_ansi:
spacing = color(spacing, fg=self.color_error)
if highlight and self.supports_ansi:
formatted_highlight = color(highlight, fg=self.color_highlight)
text = text.replace(highlight, formatted_highlight)
return "\n{} {} {}".format(self.indent, spacing, text)