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

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.