Source code for fdi.dataset.ndprint

# -*- coding: utf-8 -*-

from ..utils.common import bstr, wls
from .metadata import tabulate

import logging
import sys
from itertools import zip_longest

if sys.version_info[0] + 0.1 * sys.version_info[1] >= 3.3:
    from collections.abc import ValuesView, KeysView, Sequence

    seqlist = (ValuesView, KeysView, Sequence)
else:
    from .collectionsMockUp import SequenceMockUp as Sequence
    import types
    seqlist = (tuple, list, Sequence, str)
    # ,types.XRangeType, types.BufferType)

# create logger
logger = logging.getLogger(__name__)
# logger.debug('level %d' %  (logger.getEffectiveLevel()))


[docs]def padstr(s, w, just='left', pad=' '): if just == 'center': return s.center(w, pad) elif just == 'right': return s.rjust(w, pad) else: return s.ljust(w, pad)
[docs]def ndprint(data, trans=True, mdim=None, maxElem=sys.maxsize, tablefmt3='plain', **kwds): """ makes a formated string of an N-dimensional array for printing. The fastest changing index is the innerest list. E.g. A 2 by 3 matrix is [[1,2],[3,4],[5,6]] written as:: 1 2 3 4 5 6 But if the matrix is a table, the cells in a column change the fastest, and the columns are written vertically. So to print a matrix as a table, whose columns are the innerest list, set trans = True (default) then the matrix needs to be printed transposed:: 1 3 5 2 4 6 Parameters ---------- :tablefmt3: control 2d array printing. Default 'plain'. :dim: Max dimension of the data. If given `None` guess will be made. This helps to disambiguit if there are iterables in the elements. DEfault ```None```. Returns ------- """ if data is None: return 'None' try: if len(data) == 0: return str(data) except TypeError: pass # dim, maxdim, and s are to be used as nonlocal variables in run() # to overcome python2's lack of nonlocal type this method is usded # https://stackoverflow.com/a/28433571 class context: # current dimension as we descend. # dim=1 is the slowest changing (outer-most) dimension. # dim=maxdim is the fastest changing (inner-most) dimension. dim = 0 # dimension of input data maxdim = 0 # output string s = '' # print("start " + str(data) + ' ' + str(trans)) if mdim is not None: context.maxdim = mdim else: t = data try: while not issubclass(t.__class__, (str, bytes, bytearray, memoryview)): tmp = list(t) # if we reach this line, tmp has a valid value if len(tmp) == 0: break t = tmp[0] context.maxdim += 1 except TypeError as e: # print(e) pass def loop(data, trans, **kwds): # nonlocal s # nonlocal maxdim # nonlocal dim dbg = False delta = '' padding = ' ' * context.dim * 4 if context.maxdim == 0: tf = tablefmt3 return tabulate.tabulate([[bstr(data)]], tablefmt=tf) elif context.maxdim == 1: tf = tablefmt3 d2 = [[bstr(x)] for x in data] if trans else [[bstr(x) for x in data]] return tabulate.tabulate(d2, tablefmt=tf) else: d = list(data) context.dim += 1 padding = ' ' * context.dim * 4 if dbg: print(padding + 'loop: d=%s dim=%d maxdim=%d %r' % (str(d), context.dim, context.maxdim, trans)) # if context.dim > context.maxdim: # context.maxdim = context.dim try: if trans: if context.maxdim - context.dim == 1: # transpose using list(zip) technique if maxdim > 1 d2 = list(zip_longest(*d, fillvalue='-')) else: d2 = d else: d2 = d except Exception as e: msg = 'bad tabledataset for printing. ' + str(e) logger.error(msg) raise if dbg: print(padding + 'd2 %s' % str(d2)) if context.dim + 1 == context.maxdim: tf = kwds.get('tablefmt2', 'simple') hd = kwds.get('headers', []) # d2 is a properly transposed 2D array dlimited = [x[:maxElem] for x in d2[:maxElem]] # this is where TableDataset prints its tables # if tf in ['simple', 'rst', 'grid'] and if len(hd) > 1 and issubclass(hd[0].__class__, tuple): _tab = tabulate.tabulate( dlimited, headers=list(h[1] for h in hd), tablefmt=tf) minp = 0 # tabulate.MIN_PADDING #print(tabulate.EVENTUAL_WIDTHS, tabulate.MIN_PADDING) # first rst cell cannot be blank if tf == 'rst': if hd[0][0] == '': hd[0] = ('..', hd[0][1]) last = hd[0][0] # width of each group w = tabulate.EVENTUAL_WIDTHS[0] # group strings and widths hd2, w2 = [], [] for i in range(1, len(hd)): this = hd[i][0] # column width wc = tabulate.EVENTUAL_WIDTHS[i] # skip thid for plain if this == last and tf != 'plain': # extra 2 for every internal gaps grouped w += wc + 2 + \ (1 if any(x in tf for x in ['grid', 'orgtbl', 'psql']) else 0) else: # padstr(last, w, just='center')) # if tf == 'simple' else last) hd2.append(wls(last, w)) w2.append(w) w = wc last = this hd2.append(padstr(last, w, just='center')) w2.append(w) saveb = tabulate.PRESERVE_WHITESPACE tabulate.PRESERVE_WHITESPACE = True dummy = [['X'*n for n in w2]] if tf in ['simple', 'plain', 'orgtbl']: _header = tabulate.tabulate(dummy, headers=hd2, stralign='center', tablefmt=tf) _header = _header.rsplit('\n', 1)[0] delta += _header + '\n' + _tab elif tf in ['rst']: _header = tabulate.tabulate(dummy, headers=hd2, stralign='center', tablefmt='simple') par = _tab.split('\n', 1) par.insert(1, _header.rsplit('\n', 1)[0]) _tab = '\n'.join(par) delta += '\n' + _tab elif any(x in tf for x in ['grid', 'orgtbl', 'psql']): _header = tabulate.tabulate(dummy, headers=hd2, stralign='center', tablefmt=tf) _header = _header.rsplit('\n', 3)[0] delta += _header + '\n' + _tab elif tf in ['x']: _header = tabulate.tabulate(dummy, headers=hd2, stralign='center', tablefmt=tf) _header = _header.rsplit('\n', 2)[0] delta += _header + '\n' + _tab else: _header = tabulate.tabulate(dummy, headers=hd2, stralign='center', tablefmt=tf) _header = _header # .rsplit('\n', 3)[0] delta += _header + _tab tabulate.PRESERVE_WHITESPACE = saveb else: if len(hd): _header = [h[1] for h in hd] if issubclass( hd[0].__class__, tuple) else hd else: _header = hd delta += tabulate.tabulate(dlimited, headers=_header, tablefmt=tf) # an extra blank line is added at the end of the 3rd dimension delta += '\n\n' else: nelem = 0 for x in d2: delta += loop(x, trans=trans, **kwds) # dimensions higher than 3 have these marks t = '#=== dimension ' + \ str(context.maxdim - context.dim + 1) + '\n' delta += t * (context.maxdim - context.dim + -2) + '\n' nelem += 1 if nelem >= maxElem: break context.s += delta context.dim -= 1 if dbg: print(padding + 'delta %s /delta dim=%d' % (delta, context.dim)) print(padding + 's ' + context.s + ' /s') return delta ret = loop(data, trans, **kwds) return ret