summaryrefslogtreecommitdiffstats
path: root/cli_helpers/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'cli_helpers/utils.py')
-rw-r--r--cli_helpers/utils.py114
1 files changed, 114 insertions, 0 deletions
diff --git a/cli_helpers/utils.py b/cli_helpers/utils.py
new file mode 100644
index 0000000..053bdea
--- /dev/null
+++ b/cli_helpers/utils.py
@@ -0,0 +1,114 @@
+# -*- 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_string(value):
+ """Convert *value* to a string."""
+ if isinstance(value, binary_type):
+ return bytes_to_string(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}