pytermgui.prettifiers

This module provides some methods to prettify things.

The main export here is prettify. It uses pytermgui.parser.tim, and all of its markup magic to create prettier representations of whatever is given.

  1"""This module provides some methods to prettify things.
  2
  3The main export here is `prettify`. It uses `pytermgui.parser.tim`, and all of its
  4markup magic to create prettier representations of whatever is given.
  5"""
  6
  7from __future__ import annotations
  8
  9from collections import UserDict, UserList
 10from typing import Any
 11
 12from .parser import RE_MARKUP, tim
 13from .highlighters import highlight_python
 14from .fancy_repr import supports_fancy_repr, build_fancy_repr
 15
 16
 17__all__ = ["prettify"]
 18
 19CONTAINER_TYPES = (list, dict, set, tuple, UserDict, UserList)
 20
 21
 22# Note: This function can be optimized in a lot of ways, primarily the way containers
 23#       are treated.
 24def prettify(  # pylint: disable=too-many-branches
 25    target: Any,
 26    indent: int = 2,
 27    force_markup: bool = False,
 28    expand_all: bool = False,
 29    parse: bool = True,
 30) -> str:
 31    """Prettifies any Python object.
 32
 33    This uses a set of pre-defined aliases for the styling, and as such is fully
 34    customizable.
 35
 36    The aliases are:
 37    - `str`: Applied to all strings, so long as they do not contain TIM code.
 38    - `int`: Applied to all integers and booleans. The latter are included as they
 39        subclass int.
 40    - `type`: Applied to all types.
 41    - `none`: Applied to NoneType. Note that when using `pytermgui.pretty`, a
 42        single `None` return value will not be printed, only when part of a more
 43        complex structure.
 44
 45    Args:
 46        target: The object to prettify. Can be any type.
 47        indent: The indentation used for multi-line objects, like containers. When
 48            set to 0, these will be collapsed. By default, container types with
 49            `len() == 1` are always collapsed, regardless of this value. See
 50            `expand_all` to overwrite that behaviour.
 51        force_markup: When this is set every ANSI-sequence string will be turned
 52            into markup and syntax highlighted.
 53        expand_all: When set, objects that would normally be force-collapsed are
 54            also going to be expanded.
 55        parse: If not set, the return value will be a plain markup string, not yet
 56            parsed.
 57
 58    Returns:
 59        A pretty string of the given target.
 60    """
 61
 62    if isinstance(target, str):
 63        if RE_MARKUP.match(target) is not None:
 64            if parse:
 65                return f'"{tim.prettify_markup(target)}"'
 66
 67            return target + "[/]"
 68
 69        target = repr(target)
 70
 71    if isinstance(target, CONTAINER_TYPES):
 72        if len(target) < 2 and not expand_all:
 73            indent = 0
 74
 75        indent_str = ("\n" if indent > 0 else "") + indent * " "
 76
 77        chars = str(target)[0], str(target)[-1]
 78        buff = chars[0]
 79
 80        if isinstance(target, (dict, UserDict)):
 81            for i, (key, value) in enumerate(target.items()):
 82                if i > 0:
 83                    buff += ", "
 84
 85                buff += indent_str + highlight_python(f"{key!r}: ")
 86
 87                pretty = prettify(
 88                    value,
 89                    indent=indent,
 90                    expand_all=expand_all,
 91                    force_markup=force_markup,
 92                    parse=False,
 93                )
 94
 95                lines = pretty.splitlines()
 96                buff += lines[0]
 97
 98                for line in lines[1:]:
 99                    buff += indent_str + line
100
101        else:
102            for i, value in enumerate(target):
103                if i > 0:
104                    buff += ", "
105
106                pretty = prettify(
107                    value,
108                    indent=indent,
109                    expand_all=expand_all,
110                    force_markup=force_markup,
111                    parse=False,
112                )
113
114                lines = pretty.splitlines()
115
116                for line in lines:
117                    buff += indent_str + line
118
119        if indent > 0:
120            buff += "\n"
121
122        buff += chars[1]
123
124        if force_markup:
125            return buff
126
127        return tim.parse(buff)
128
129    if supports_fancy_repr(target):
130        buff = build_fancy_repr(target)
131
132    else:
133        buff = highlight_python(str(target))
134
135    return tim.parse(buff) if parse else buff
def prettify( target: Any, indent: int = 2, force_markup: bool = False, expand_all: bool = False, parse: bool = True) -> str:
 25def prettify(  # pylint: disable=too-many-branches
 26    target: Any,
 27    indent: int = 2,
 28    force_markup: bool = False,
 29    expand_all: bool = False,
 30    parse: bool = True,
 31) -> str:
 32    """Prettifies any Python object.
 33
 34    This uses a set of pre-defined aliases for the styling, and as such is fully
 35    customizable.
 36
 37    The aliases are:
 38    - `str`: Applied to all strings, so long as they do not contain TIM code.
 39    - `int`: Applied to all integers and booleans. The latter are included as they
 40        subclass int.
 41    - `type`: Applied to all types.
 42    - `none`: Applied to NoneType. Note that when using `pytermgui.pretty`, a
 43        single `None` return value will not be printed, only when part of a more
 44        complex structure.
 45
 46    Args:
 47        target: The object to prettify. Can be any type.
 48        indent: The indentation used for multi-line objects, like containers. When
 49            set to 0, these will be collapsed. By default, container types with
 50            `len() == 1` are always collapsed, regardless of this value. See
 51            `expand_all` to overwrite that behaviour.
 52        force_markup: When this is set every ANSI-sequence string will be turned
 53            into markup and syntax highlighted.
 54        expand_all: When set, objects that would normally be force-collapsed are
 55            also going to be expanded.
 56        parse: If not set, the return value will be a plain markup string, not yet
 57            parsed.
 58
 59    Returns:
 60        A pretty string of the given target.
 61    """
 62
 63    if isinstance(target, str):
 64        if RE_MARKUP.match(target) is not None:
 65            if parse:
 66                return f'"{tim.prettify_markup(target)}"'
 67
 68            return target + "[/]"
 69
 70        target = repr(target)
 71
 72    if isinstance(target, CONTAINER_TYPES):
 73        if len(target) < 2 and not expand_all:
 74            indent = 0
 75
 76        indent_str = ("\n" if indent > 0 else "") + indent * " "
 77
 78        chars = str(target)[0], str(target)[-1]
 79        buff = chars[0]
 80
 81        if isinstance(target, (dict, UserDict)):
 82            for i, (key, value) in enumerate(target.items()):
 83                if i > 0:
 84                    buff += ", "
 85
 86                buff += indent_str + highlight_python(f"{key!r}: ")
 87
 88                pretty = prettify(
 89                    value,
 90                    indent=indent,
 91                    expand_all=expand_all,
 92                    force_markup=force_markup,
 93                    parse=False,
 94                )
 95
 96                lines = pretty.splitlines()
 97                buff += lines[0]
 98
 99                for line in lines[1:]:
100                    buff += indent_str + line
101
102        else:
103            for i, value in enumerate(target):
104                if i > 0:
105                    buff += ", "
106
107                pretty = prettify(
108                    value,
109                    indent=indent,
110                    expand_all=expand_all,
111                    force_markup=force_markup,
112                    parse=False,
113                )
114
115                lines = pretty.splitlines()
116
117                for line in lines:
118                    buff += indent_str + line
119
120        if indent > 0:
121            buff += "\n"
122
123        buff += chars[1]
124
125        if force_markup:
126            return buff
127
128        return tim.parse(buff)
129
130    if supports_fancy_repr(target):
131        buff = build_fancy_repr(target)
132
133    else:
134        buff = highlight_python(str(target))
135
136    return tim.parse(buff) if parse else buff

Prettifies any Python object.

This uses a set of pre-defined aliases for the styling, and as such is fully customizable.

The aliases are:

  • str: Applied to all strings, so long as they do not contain TIM code.
  • int: Applied to all integers and booleans. The latter are included as they subclass int.
  • type: Applied to all types.
  • none: Applied to NoneType. Note that when using pytermgui.pretty, a single None return value will not be printed, only when part of a more complex structure.
Args
  • target: The object to prettify. Can be any type.
  • indent: The indentation used for multi-line objects, like containers. When set to 0, these will be collapsed. By default, container types with len() == 1 are always collapsed, regardless of this value. See expand_all to overwrite that behaviour.
  • force_markup: When this is set every ANSI-sequence string will be turned into markup and syntax highlighted.
  • expand_all: When set, objects that would normally be force-collapsed are also going to be expanded.
  • parse: If not set, the return value will be a plain markup string, not yet parsed.
Returns

A pretty string of the given target.