Source code for pyyeti.pp

# -*- coding: utf-8 -*-
"""
A pretty printer.
"""
from collections.abc import Mapping
import numpy as np
import h5py
import pandas as pd


# FIXME: We need the str/repr formatting used in Numpy < 1.14.
try:
    np.set_printoptions(legacy="1.13")
except TypeError:
    pass


PP_None = object()


[docs] class PP: """ A simple class for pretty printing data structures. """
[docs] def __init__( self, var=PP_None, depth=1, tab=4, keylen=40, strlen=80, show_hidden=False ): """ Initializer for `PP`. Parameters ---------- var : any; optional If not ``PP_None``, the variable to pretty print. depth : integer; optional Maximum number of levels to print. tab : integer; optional Number of additional indent spaces for each level. keylen : integer; optional Maximum length for dictionary (and similar) keys. strlen : integer; optional Maximum length for dictionary (and similar) values. show_hidden : bool; optional Many objects (classes, class instances, namespaces, etc) are printed via looping over the ``.__dict__`` method. For those variables, if `show_hidden` is True, show members that start with '_'. Default is to not show them. Notes ----- A variable can be printed during instantiation or via :func:`PP.pp`. For example:: PP(var, depth=3, tab=8) gives the same output as:: p = PP(depth=3, tab=8) p.pp(var) Examples -------- >>> import numpy as np >>> import pandas as pd >>> from types import SimpleNamespace >>> from pyyeti.pp import PP >>> r = np.arange(4, dtype=np.int16) >>> s = np.zeros((4, 4, 4)) >>> t = np.array(9, dtype=np.uint8) >>> d = {'asdf': 4, ... '34': 'value', ... 'r': r, ... 'dataframe': pd.DataFrame(np.ones((3, 3))), ... 'series': pd.Series(np.ones(8)), ... 'longer name': {1: 2, ... 2: 3, ... 3: s, ... 4: SimpleNamespace(a=6, ... b=[1, 23], ... var='string', ... t=(s,)) ... } ... } >>> PP(d) # doctest: +ELLIPSIS <class 'dict'>[n=6] 'asdf' : 4 '34' : 'value' 'r' : int16 ndarray 4 elems: (4,) [0 1 2 3] 'dataframe' : pandas DataFrame: (3, 3) 'series' : pandas Series: (8,) 'longer name': <class 'dict'>[n=4] <BLANKLINE> <...> >>> PP(d, 5) # doctest: +ELLIPSIS <class 'dict'>[n=6] 'asdf' : 4 '34' : 'value' 'r' : int16 ndarray 4 elems: (4,) [0 1 2 3] 'dataframe' : pandas DataFrame: (3, 3) 'series' : pandas Series: (8,) 'longer name': <class 'dict'>[n=4] 1: 2 2: 3 3: float64 ndarray 64 elems: (4, 4, 4) 4: <class 'types.SimpleNamespace'>[n=4] .a : 6 .b : [n=2]: [1, 23] .var: 'string' .t : [n=1]: (float64 ndarray: (4, 4, 4),) <BLANKLINE> <...> To demonstrate the `show_hidden` option: >>> class A: ... a = 9 ... b = {'variable': [1, 2, 3]} >>> PP(A, 2) # doctest: +ELLIPSIS <class 'A'>[n=...] .a: 9 .b: <class 'dict'>[n=1] 'variable': [n=3]: [1, 2, 3] <BLANKLINE> <...> >>> PP(A, 2, show_hidden=1) # doctest: +SKIP <class 'A'>[n=6] .__module__ : 'pyyeti.pp' .a : 9 .b : <class 'dict'>[n=1] 'variable': [n=3]: [1, 2, 3] .__dict__ : <attribute '__dict__' of 'A' objects> .__weakref__: <attribute '__weakref__' of 'A' objects> .__doc__ : None <BLANKLINE> <...> """ self._tab = tab self._depth = depth self._keylen = keylen self._strlen = strlen self._show_hidden = show_hidden self._functions = { np.ndarray: self._array_string, h5py._hl.dataset.Dataset: self._h5data_string, } if var is not PP_None: self.pp(var)
def _lead_string(self, level): return " " * self._tab * level def _key_string(self, val, isns): if isinstance(val, str): if isns: s = f".{val}" else: s = f"'{val}'" else: s = str(val) if len(s) > self._keylen: s = f"{s[: self._keylen - 4]} ..." return s def _lst_tup_string(self, lst, list_level): s = [f"[n={len(lst)}]: "] be = "[]" if isinstance(lst, list) else "()" s.append(be[0]) if list_level > self._depth: s.append(f"...{be[1]}") return s list_level += 1 for i, item in enumerate(lst): if i > self._strlen // 3: # each entry is at least 3 chars: "1, " s.append(" ...") break if isinstance(item, np.ndarray): s.extend(self._shortarrhdr(item)) else: s.extend(self._value_string(item, list_level)) s.append(", ") if s[-1] == ", ": if len(lst) == 1 and isinstance(lst, tuple): s[-1] = "," else: s = s[:-1] s.append(be[1]) return s def _value_string(self, val, list_level): if isinstance(val, str): s = [f"'{val}'"] elif isinstance(val, (list, tuple)): s = self._lst_tup_string(val, list_level) else: s = [f"{val}".replace("\n", " ")] s = "".join(s) if len(s) > self._strlen: n = self._strlen // 2 - 3 s = f"{s[:n]} ... {s[-n:]}" return [s] def _shortarrhdr(self, arr): return [f"{arr.dtype} ndarray: {arr.shape}"] def _getarrhdr(self, arr): return [f"{arr.dtype} ndarray {arr.size} elems: {arr.shape}"] def _getarrstr(self, arr): s = f" {arr}".replace("\n", "") if len(s) > 4 * (self._strlen // 5): n = self._strlen // 3 s = f"{s[: n - 3]} <...> {s[-(n + 3) :]}" return [s] def _array_string(self, arr, level): s = self._getarrhdr(arr) if arr.size <= 10: s.extend(self._getarrstr(arr)) s.append("\n") return s def _h5data_string(self, arr, level): s = ["H5 "] s.extend(self._getarrhdr(arr)) if arr.size <= 10: s.extend(self._getarrstr(arr[...])) s.append("\n") return s def _get_keys(self, dct, showhidden): if not showhidden: keys = [k for k in dct if k[0] != "_"] else: keys = list(dct.keys()) return keys def _dict_string(self, dct, level, typename, isns=False, showhidden=True): s = [f"{typename}[n={len(dct)}]\n"] if level < self._depth: keys = self._get_keys(dct, showhidden) level += 1 # get max key length for pretty printing: n = 0 for k in keys: n = max(n, len(self._key_string(k, isns))) frm = f"{{:<{n}s}}: " for k in keys: s.append(self._lead_string(level)) s.append(frm.format(self._key_string(k, isns))) s.extend(self._print_var(dct[k], level)) return s def _pandas_string(self, var, level, typename): return [f"pandas {var.__class__.__name__}: {var.shape}\n"] def _print_var(self, var, level): try: s = self._functions[type(var)](var, level) except KeyError: cls = type(var) if cls is type: typename = f"<class '{var.__name__}'>" else: typename = str(cls) if isinstance(var, Mapping): s = self._dict_string(var, level, typename=typename) elif isinstance(var, pd.core.base.PandasObject): s = self._pandas_string(var, level, typename=typename) elif hasattr(var, "__dict__") and not hasattr(var, "keys"): s = self._dict_string( var.__dict__, level, typename=typename, isns=True, showhidden=self._show_hidden, ) else: s = self._value_string(var, 0) s.append("\n") return s
[docs] def pp(self, var): """ Pretty print variable `var`. See :class:`PP`. """ s = self._print_var(var, 0) self.s = s self.output = "".join(s) for line in self.output.split("\n"): print(line)
# print(repr(line)[1:-1])