Пример #1
0
 def __init__(self, level: int = logging.NOTSET) -> None:
     super().__init__(level=level)
     self.console = Console()
     self.highlighter = ReprHighlighter()
     self._log_render = LogRender(show_level=True)
Пример #2
0
            yield new_line
        if bottom_space > 0:
            yield from blank_lines(bottom_space)

    def __rich_measure__(self, console: "Console",
                         options: "ConsoleOptions") -> Measurement:
        measurement = Measurement.get(console, options, self.renderable)
        return measurement


if __name__ == "__main__":  # pragma: no cover
    from rich.console import Console, RenderGroup
    from rich.highlighter import ReprHighlighter
    from rich.panel import Panel

    highlighter = ReprHighlighter()
    console = Console()

    panel = Panel(
        RenderGroup(
            Align.left(highlighter("align='left'")),
            Align.center(highlighter("align='center'")),
            Align.right(highlighter("align='right'")),
        ),
        width=60,
        style="on dark_blue",
        title="Algin",
    )

    console.print(
        Align.center(panel,
Пример #3
0
def pretty_repr(
    _object: Any,
    *,
    max_width: Optional[int] = 80,
    indent_size: int = 4,
    highlighter: Highlighter = None,
    justify: "JustifyMethod" = None,
    overflow: "OverflowMethod" = None,
    no_wrap: bool = True,
) -> Text:
    """Return a 'pretty' repr.

    Args:
        _object (Any): Object to repr.
        max_width (int, optional): Maximum desired width. Defaults to 80.
        indent_size (int, optional): Number of spaces in an indent. Defaults to 4.
        highlighter (Highlighter, optional): A highlighter for repr strings. Defaults to ReprHighlighter.

    Returns:
        Text: A Text instance conaining a pretty repr.
    """
    class MaxLineReached(Exception):
        """Line is greater than maximum"""

    if highlighter is None:
        highlighter = ReprHighlighter()

    indent = " " * indent_size
    expand_level = 0
    lines: List[_Line] = [_Line()]

    visited_set: Set[int] = set()
    repr_cache: Dict[int, str] = {}
    repr_cache_get = repr_cache.get

    def to_repr_text(node: Any) -> str:
        """Convert object to repr."""
        node_id = id(node)
        cached = repr_cache_get(node_id)
        if cached is not None:
            return cached
        try:
            repr_text = repr(node)
        except Exception as error:
            repr_text = f"<error in repr: {error}>"
        repr_cache[node_id] = repr_text
        return repr_text

    line_break: Optional[int] = None

    def traverse(node: Any, level: int = 0) -> None:
        """Walk the data structure."""

        nonlocal line_break
        append_line = lines.append

        def append_text(text: str) -> None:
            nonlocal max_width
            nonlocal line_break
            line = lines[-1]
            line.append(text)
            if max_width is not None and line.cell_len > max_width:
                if line_break is not None and len(lines) <= line_break:
                    max_width = None
                else:
                    line_break = len(lines)
                    raise MaxLineReached(level)

        node_id = id(node)
        if node_id in visited_set:
            # Recursion detected
            append_text("...")
            return

        visited_set.add(node_id)
        if type(node) in _CONTAINERS:
            brace_open, brace_close, empty = _BRACES[type(node)]
            expanded = level < expand_level

            if not node:
                append_text(empty)
            else:
                append_text(brace_open)
                if isinstance(node, dict):
                    for last, (key, value) in loop_last(node.items()):
                        if expanded:
                            append_line(_Line())
                            append_text(indent * (level + 1))
                        append_text(f"{to_repr_text(key)}: ")
                        traverse(value, level + 1)
                        if not last:
                            append_text(", ")
                else:
                    for last, value in loop_last(node):
                        if expanded:
                            append_line(_Line())
                            append_text(indent * (level + 1))
                        traverse(value, level + 1)
                        if not last:
                            append_text(", ")
                if expanded:
                    append_line(_Line())
                    append_text(f"{indent * level}{brace_close}")
                else:
                    append_text(brace_close)
        else:
            append_text(to_repr_text(node))
        visited_set.remove(node_id)

    # Keep expanding levels until the text fits
    while True:
        try:
            traverse(_object)
        except MaxLineReached:
            del lines[:]
            visited_set.clear()
            lines.append(_Line())
            expand_level += 1
        else:
            break  # pragma: no cover

    text = Text(
        "\n".join(line.text for line in lines),
        justify=justify,
        overflow=overflow,
        no_wrap=no_wrap,
    )
    text = highlighter(text)
    return text
Пример #4
0
def test_highlight_regex(style_name: str, test_str: str):
    """Tests for the regular expressions used in ReprHighlighter."""
    text = Text(test_str)
    highlighter = ReprHighlighter()
    highlighter.highlight(text)
    assert style_name in repr(text)
Пример #5
0
def dump(entries, debug=False, eval_lazy=False, trace=False, title_only=False):
    """
    Dump *entries* to stdout

    :param list entries: Entries to be dumped.
    :param bool debug: Print non printable fields as well.
    :param bool eval_lazy: Evaluate lazy fields.
    :param bool trace: Display trace information.
    :param bool title_only: Display only title field
    """
    def sort_key(field):
        # Sort certain fields above the rest
        if field == 'title':
            return (0, )
        if field == 'url':
            return (1, )
        if field == 'original_url':
            return (2, )
        return 3, field

    highlighter = ReprHighlighter()

    for entry in entries:
        entry_table = TerminalTable(
            'field',
            ':',
            'value',
            show_header=False,
            show_edge=False,
            pad_edge=False,
            collapse_padding=True,
            box=None,
            padding=0,
        )
        for field in sorted(entry, key=sort_key):
            if field.startswith('_') and not debug:
                continue
            if title_only and field != 'title':
                continue
            if entry.is_lazy(field) and not eval_lazy:
                renderable = (
                    '[italic]<LazyField - value will be determined when it is accessed>[/italic]'
                )
            else:
                try:
                    value = entry[field]
                except KeyError:
                    renderable = '[italic]<LazyField - lazy lookup failed>[/italic]'
                else:
                    if field.rsplit('_', maxsplit=1)[-1] == 'url':
                        renderable = f'[link={value}][repr.url]{value}[/repr.url][/link]'
                    elif isinstance(value, str):
                        renderable = value.replace('\r', '').replace('\n', '')
                    elif is_expandable(value):
                        renderable = Pretty(value)
                    else:
                        try:
                            renderable = highlighter(str(value))
                        except Exception:
                            renderable = f'[[i]not printable[/i]] ({repr(value)})'
            entry_table.add_row(f'{field}', ': ', renderable)
        console(entry_table)
        if trace:
            console('── Processing trace:', style='italic')
            trace_table = TerminalTable(
                'Plugin',
                'Operation',
                'Message',
                show_edge=False,
                pad_edge=False,
            )
            for item in entry.traces:
                trace_table.add_row(item[0],
                                    '' if item[1] is None else item[1],
                                    item[2])
            console(trace_table)
        if not title_only:
            console('')
Пример #6
0
def listdir(path, extension=None, sortby=None):
    """
    Prints a nicely formatted table with an overview of files
    in a given directory

    :param path: str or Path object. Path to directory being listed
    :param extension: str. If passed files with that extension are highlighted
    :param sortby: str, default None. How to sort items. If None items
            are sorted alphabetically, if 'ext' or 'extension' items are
            sorted by extension, if 'size' items are sorted by size

    returns a list of files
    """
    def sort_ext(item):
        return item.suffix

    def sort_size(item):
        return item.stat().st_size

    # Check paths
    p = Path(path)
    if not p.is_dir():
        raise ValueError(f"The path passed is not a directory: {path}")

    # Create table
    tb = Table(
        box=None,
        show_lines=None,
        show_edge=None,
        expand=False,
        header_style=f"bold {mocassin}",
    )
    tb.add_column(header="Name")
    tb.add_column(header="Size")

    # Sort items
    if sortby == "extension" or sortby == "ext":
        std = sorted(dir_files(p), key=sort_ext)
    elif sortby == "size":
        std = sorted(dir_files(p), key=sort_size, reverse=True)
    else:
        std = sorted(dir_files(p))

    for fl in std:
        complete_path = str(fl)
        parent = fl.parent.name
        fl = fl.name
        _fl = fl

        # Format file name
        fl = f"[{mocassin}]{fl}"

        if extension is not None and fl.endswith(extension):
            fl = f"[{orange}]{_fl}"
            _col = orange
            _dimcol = orange
        else:
            _col = lightorange
            _dimcol = darkgray

        # Get file size
        size = format_size(os.path.getsize(complete_path)).split(" ")

        tb.add_row(
            f'[link=file://"{complete_path}"][dim]{parent}/[/]' + fl,
            f"[{_col}]" + size[0] + f"[/] [{_dimcol}]" + size[1],
        )

    console.print(f"Files in {path}\n",
                  tb,
                  sep="\n",
                  highlight=ReprHighlighter())

    return std
Пример #7
0
    def __rich_console__(self, console, options):
        # I basically just copied this from https://github.com/willmcgugan/rich/blob/master/rich/traceback.py
        # and removed calls to Panel
        theme = self.theme
        token_style = theme.get_style_for_token

        traceback_theme = Theme({
            "pretty":
            token_style(token.Text),
            "pygments.text":
            token_style(token.Token),
            "pygments.string":
            token_style(token.String),
            "pygments.function":
            token_style(token.Name.Function),
            "pygments.number":
            token_style(token.Number),
            "repr.indent":
            token_style(token.Comment) + Style(dim=True),
            "repr.str":
            token_style(token.String),
            "repr.brace":
            token_style(token.Text) + Style(bold=True),
            "repr.number":
            token_style(token.Number),
            "repr.bool_true":
            token_style(token.Keyword.Constant),
            "repr.bool_false":
            token_style(token.Keyword.Constant),
            "repr.none":
            token_style(token.Keyword.Constant),
            "scope.border":
            token_style(token.String.Delimiter),
            "scope.equals":
            token_style(token.Operator),
            "scope.key":
            token_style(token.Name),
            "scope.key.special":
            token_style(token.Name.Constant) + Style(dim=True),
        })

        highlighter = ReprHighlighter()
        for last, stack in loop_last(reversed(self.trace.stacks)):
            if stack.frames:
                stack_renderable = self._render_stack(stack)
                stack_renderable = Constrain(stack_renderable, self.width)
                with console.use_theme(traceback_theme):
                    yield stack_renderable
            if stack.syntax_error is not None:
                with console.use_theme(traceback_theme):
                    yield Constrain(
                        self._render_syntax_error(stack.syntax_error))
                yield Text.assemble(
                    (f"{stack.exc_type}: ", "traceback.exc_type"),
                    highlighter(stack.syntax_error.msg),
                )
            elif stack.exc_value:
                yield Text.assemble(
                    (f"{stack.exc_type}: ", "traceback.exc_type"),
                    highlighter(stack.exc_value),
                )
            else:
                yield Text.assemble(
                    (f"{stack.exc_type}", "traceback.exc_type"))

            if not last:
                if stack.is_cause:
                    yield Text.from_markup(
                        "\n[i]The above exception was the direct cause of the following exception:\n",
                    )
                else:
                    yield Text.from_markup(
                        "\n[i]During handling of the above exception, another exception occurred:\n",
                    )
Пример #8
0
def test_highlight_regex(style_name: str, test_str: str):
    """Tests for the regular expressions used in ReprHighlighter."""
    text = Text(test_str)
    highlighter = ReprHighlighter()
    highlighter.highlight(text)
    assert text._spans[-1] == Span(0, len(test_str), style_name)
Пример #9
0
class RichResults(object):
    width: int = 80
    lock: threading.Lock = threading.Lock()
    results: List[AggregatedResult] = field(default_factory=list)
    highlighter: Highlighter = ReprHighlighter()
    console: Console = Console(record=True)
    current_indent: int = 0

    def print(
        self,
        result: Result,
        vars: List[str] = None,
        failed: bool = False,
        severity_level: int = logging.INFO,
    ) -> None:
        """
        Prints an object of type `nornir.core.task.Result`
        Arguments:
            result: from a previous task
            vars: Which attributes you want to print
            failed: if ``True`` assume the task failed
            severity_level: Print only errors with this severity level or higher
        """
        LOCK.acquire()

        try:
            self.results.append(result)
            self._print_result(result, vars, failed, severity_level)
            self.console.line()
        finally:
            LOCK.release()

    def _print_result(
        self,
        result: Result,
        vars: List[str] = None,
        failed: bool = False,
        severity_level: int = logging.info,
    ) -> None:

        if isinstance(result, AggregatedResult):
            msg = f"{result.name} (hosts: {len(result)}"
            if result.failed:
                msg += ", failed: True"
            if result.failed_hosts:
                msg += f", failed_hosts: {list(result.failed_hosts.keys())})"
            else:
                msg += ")"

            self.console.print(f"{msg}",
                               style=Style(underline=True, color="black"))

            for host, host_data in sorted(result.items()):
                msg = f"{host} "

                result_details = []
                if host_data.changed:
                    result_details.append("changed = True")

                if host_data.failed:
                    result_details.append("failed = True")

                if result_details:
                    msg += "(" + ", ".join(result_details) + ")"

                self.console.print(
                    f"* {msg}",
                    style=Style(color="blue"),
                )
                self._print_result(host_data, vars, failed, severity_level)

        elif isinstance(result, MultiResult):
            self.current_indent += 1
            self._print_individual_result(result[0], group=True, vars=vars)

            for r in result[1:]:
                self._print_result(r, vars, failed, severity_level)

            self.current_indent -= 1

        elif isinstance(result, Result):
            self._print_individual_result(result, vars=vars)

    def _print_individual_result(
        self,
        result: Result,
        vars: List[str],
        group: bool = False,
    ) -> None:

        title_text = f"{result.name} "

        result_details = []
        if result.changed:
            result_details.append("changed = True")

        if result.severity_level != 20:
            result_details.append(
                f"logging_level = {logging.getLevelName(result.severity_level)}"
            )

        if result.failed:
            result_details.append("failed = True")

        if result_details:
            title_text += "(" + ", ".join(result_details) + ")"

        title = self.highlighter(title_text)
        if group:
            group_title = f"{' ' * self.current_indent}:heavy_check_mark: {title_text}"

        items_table = Table.grid(padding=(0, 1), expand=False)
        items_table.add_column(width=self.current_indent)
        items_table.add_column(justify="right")

        attrs = vars if vars else ["stdout", "result", "stderr", "diff"]

        for attr in attrs:
            x = getattr(result, attr, None)

            if x and attr == "tests":
                for i, test in enumerate(result.tests.tests):
                    status = ":green_circle:" if test.passed else ":red_circle:"
                    items_table.add_row(
                        None,
                        f"{attr} {status} " if i == 0 else f"{status}",
                        Pretty(test, highlighter=self.highlighter),
                    )
            elif x and attr == "exception":
                # TODO - figure out how to add traceback highlighting
                items_table.add_row(None, f"{attr} = ",
                                    Pretty(x, highlighter=self.highlighter))
            elif x:
                items_table.add_row(None, f"{attr} = ",
                                    Pretty(x, highlighter=self.highlighter))

        if items_table.row_count > 0:
            self.console.print(
                Padding(
                    Panel(items_table, title=title, title_align="left"),
                    (0, 0, 0, self.current_indent + 1),
                ))
        else:
            self.console.print(group_title)

    def summary(self) -> None:
        table = Table(
            expand=True,
            show_lines=False,
            box=ROUNDED,
            show_footer=True,
            width=self.width,
        )

        table.add_column("Task", ratio=5, no_wrap=True, footer="Total")
        table.add_column("Ok", ratio=1)
        table.add_column("Changed", ratio=1)
        table.add_column("Failed", ratio=1)

        totals = {"ok": 0, "failed": 0, "changed": 0}
        for result in self.results:
            failed = ok = changed = 0

            for host_data in result.values():
                failed += len(list(filter(lambda x: x.failed, host_data)))
                changed += len(list(filter(lambda x: x.changed, host_data)))
                ok += len(
                    list(
                        filter(lambda x: not x.changed and not x.failed,
                               host_data)))

            table.add_row(
                result.name,
                self._get_summary_count(ok, "ok"),
                self._get_summary_count(changed, "changed"),
                self._get_summary_count(failed, "failed"),
            )
            totals["ok"] += ok
            totals["changed"] += changed
            totals["failed"] += failed

        table.columns[1].footer = self._get_summary_count(totals["ok"], "ok")
        table.columns[2].footer = self._get_summary_count(
            totals["changed"], "changed")
        table.columns[3].footer = self._get_summary_count(
            totals["failed"], "failed")

        self.lock.acquire()
        self.console.print(table, width=self.width)
        self.lock.release()

    def _get_summary_count(self, count: int, style: str) -> Text:
        text = str(count) if count else "-"
        style = style if count else "no_style"
        return Text(text, style=style)

    def write(self,
              filename: str = "results.html",
              format: str = "html") -> None:
        self.lock.acquire()
        if format == "text":
            self.console.save_text(filename)
        else:
            self.console.save_html(filename)
        self.lock.release()

    def inventory(self, nr: Inventory, passwords: bool = False) -> None:
        table = Table(
            expand=True,
            box=ROUNDED,
            show_lines=True,
            width=self.width,
        )

        table.add_column("Host")
        table.add_column("Data")

        for host, host_data in nr.inventory.hosts.items():
            host_dict = host_data.dict()
            if not passwords:
                host_dict["password"] = "******"

            table.add_row(host, yaml.dump(host_dict))

        self.lock.acquire()
        self.console.print(table, width=self.width)
        self.lock.release()