File: utils.py

package info (click to toggle)
cli-helpers 2.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 360 kB
  • sloc: python: 2,248; makefile: 16
file content (138 lines) | stat: -rw-r--r-- 3,659 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# -*- coding: utf-8 -*-
"""Various utility functions and helpers."""

import binascii
import re
from functools import lru_cache
from typing import Dict

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from pygments.style import StyleMeta

from cli_helpers.compat import binary_type, text_type, Terminal256Formatter, StringIO


def bytes_to_string(b):
    """Convert bytes *b* to a string.

    Hexlify bytes that can't be decoded.

    """
    if isinstance(b, binary_type):
        needs_hex = False
        try:
            result = b.decode("utf8")
            needs_hex = not result.isprintable()
        except UnicodeDecodeError:
            needs_hex = True
        if needs_hex:
            return "0x" + binascii.hexlify(b).decode("ascii")
        else:
            return result
    return b


def to_hex_if_bin(b):
    """Convert bytes *b* to a string.

    Pass b through if not bytes.

    """
    if isinstance(b, binary_type):
        return "0x" + binascii.hexlify(b).decode("ascii")
    return b


def to_string(value):
    """Convert *value* to a string."""
    if isinstance(value, binary_type):
        return bytes_to_string(value)
    else:
        return text_type(value)


def to_undecoded_string(value):
    """Convert *value* to an undecoded string, respecting Nones."""
    # preserve Nones so that
    # * this can run before override_missing_value when stringifying
    # * Nones are preserved in formats such as CSV
    if value is None:
        return None
    elif isinstance(value, binary_type):
        return to_hex_if_bin(value)
    else:
        return text_type(value)


def truncate_string(value, max_width=None, skip_multiline_string=True):
    """Truncate string values."""
    if skip_multiline_string and isinstance(value, text_type) and "\n" in value:
        return value
    elif (
        isinstance(value, text_type)
        and max_width is not None
        and len(value) > max_width
    ):
        return value[: max_width - 3] + "..."
    return value


def intlen(n):
    """Find the length of the integer part of a number *n*."""
    pos = n.find(".")
    return len(n) if pos < 0 else pos


def filter_dict_by_key(d, keys):
    """Filter the dict *d* to remove keys not in *keys*."""
    return {k: v for k, v in d.items() if k in keys}


def unique_items(seq):
    """Return the unique items from iterable *seq* (in order)."""
    seen = set()
    return [x for x in seq if not (x in seen or seen.add(x))]


_ansi_re = re.compile("\033\\[((?:\\d|;)*)([a-zA-Z])")


def strip_ansi(value):
    """Strip the ANSI escape sequences from a string."""
    return _ansi_re.sub("", value)


def replace(s, replace):
    """Replace multiple values in a string"""
    for r in replace:
        s = s.replace(*r)
    return s


@lru_cache()
def _get_formatter(style) -> Terminal256Formatter:
    return Terminal256Formatter(style=style)


def style_field(token, field, style):
    """Get the styled text for a *field* using *token* type."""
    formatter = _get_formatter(style)
    s = StringIO()
    formatter.format(((token, field),), s)
    return s.getvalue()


def filter_style_table(style: "StyleMeta", *relevant_styles: str) -> Dict:
    """
    get a dictionary of styles for given tokens. Typical usage:

    filter_style_table(style, Token.Output.EvenRow, Token.Output.OddRow) == {
        Token.Output.EvenRow: "",
        Token.Output.OddRow: "",
    }
    """
    _styles_iter = ((key, val) for key, val in getattr(style, "styles", {}).items())
    _relevant_styles_iter = filter(lambda tpl: tpl[0] in relevant_styles, _styles_iter)
    return {key: val for key, val in _relevant_styles_iter}