#!/usr/bin/env python
# ###########################################################################
#
# This file is part of Taurus
#
# http://taurus-scada.org
#
# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
#
# Taurus is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Taurus is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Taurus. If not, see <http://www.gnu.org/licenses/>.
#
# ###########################################################################
"""Adapted from http://code.activestate.com/recipes/267662/
"""
from functools import reduce
import operator
import re
import math
__docformat__ = "restructuredtext"
__all__ = ["indent", "wrap_always", "wrap_onspace", "wrap_onspace_strict"]
[docs]
def indent(
rows,
hasHeader=False,
headerChar="-",
delim=" | ",
justify="left",
separateRows=False,
prefix="",
postfix="",
wrapfunc=lambda x: x,
):
"""Indents a table by column.
:param rows: A sequence of sequences of items, one sequence per row.
:param hasHeader: True if the first row consists of the columns' names.
:param headerChar: Character to be used for the row separator line (if
hasHeader==True or separateRows==True).
:param delim: The column delimiter.
:param justify: Determines how are data justified in their column. Valid
values are 'left','right' and 'center'.
:param separateRows: True if rows are to be separated by a line of
'headerChar's.
:param prefix: A string prepended to each printed row.
:param postfix: A string appended to each printed row.
:param wrapfunc: A function f(text) for wrapping text; each element in the
table is first wrapped by this function.
:return: a list of strings. One for each row of the table
"""
# closure for breaking logical rows to physical, using wrapfunc
def rowWrapper(row):
newRows = [wrapfunc(item).split("\n") for item in row]
return [[substr or "" for substr in item] for item in list(*newRows)]
# break each logical row into one or more physical ones
logicalRows = [rowWrapper(row) for row in rows]
# columns of physical rows
columns = list(*reduce(operator.add, logicalRows))
# get the maximum of each column by the string length of its items
maxWidths = [
max([len(str(item)) for item in column]) for column in columns
]
if separateRows or hasHeader:
rowSeparator = headerChar * (
len(prefix)
+ len(postfix)
+ sum(maxWidths)
+ len(delim) * (len(maxWidths) - 1)
)
else:
rowSeparator = "<ERR>"
# select the appropriate justify method
justify = {"center": str.center, "right": str.rjust, "left": str.ljust}[
justify.lower()
]
output = []
if separateRows:
output.append(rowSeparator)
for physicalRows in logicalRows:
for row in physicalRows:
line = prefix
line += delim.join(
[
justify(str(item), width)
for (item, width) in zip(row, maxWidths)
]
)
line += postfix
output.append(line)
if separateRows or hasHeader:
output.append(rowSeparator)
hasHeader = False
return output
# written by Mike Brown
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
[docs]
def wrap_onspace(text, width):
"""A word-wrap function that preserves existing line breaks
and most spaces in the text. Expects that existing line
breaks are posix newlines (\\\\n).
"""
return reduce(
lambda line, word, width=width: "%s%s%s"
% (
line,
" \n"[
(
len(line[line.rfind("\n") + 1 :])
+ len(word.split("\n", 1)[0])
>= width
)
],
word,
),
text.split(" "),
)
[docs]
def wrap_onspace_strict(text, width):
"""Similar to wrap_onspace, but enforces the width constraint:
words longer than width are split.
"""
wordRegex = re.compile(r"\S{" + str(width) + r",}")
return wrap_onspace(
wordRegex.sub(lambda m: wrap_always(m.group(), width), text), width
)
[docs]
def wrap_always(text, width):
"""A simple word-wrap function that wraps text on exactly width characters.
It doesn't split the text in words.
"""
return "\n".join(
[
text[width * i : width * (i + 1)]
for i in range(int(math.ceil(1.0 * len(text) / width)))
]
)
if __name__ == "__main__":
labels = ("First Name", "Last Name", "Age", "Position")
data = """John,Smith,24,Software Engineer
Mary,Brohowski,23,Sales Manager
Aristidis,Papageorgopoulos,28,Senior Reseacher"""
rows = [row.strip().split(",") for row in data.splitlines()]
print("Without wrapping function\n")
for label in indent([labels] + rows, hasHeader=True):
print(label)
# test indent with different wrapping functions
width = 10
for wrapper in (wrap_always, wrap_onspace, wrap_onspace_strict):
print(
"Wrapping function: %s(x,width=%d)\n" % (wrapper.__name__, width)
)
o = indent(
[labels] + rows,
headerChar="=",
hasHeader=True,
separateRows=False,
prefix="|",
postfix="|",
delim=" ",
wrapfunc=lambda x: wrapper(x, width),
)
for label in o:
print(label)
# output:
#
# Without wrapping function
#
# First Name | Last Name | Age | Position
# -------------------------------------------------------
# John | Smith | 24 | Software Engineer
# Mary | Brohowski | 23 | Sales Manager
# Aristidis | Papageorgopoulos | 28 | Senior Reseacher
#
# Wrapping function: wrap_always(x,width=10)
#
# ----------------------------------------------
# | First Name | Last Name | Age | Position |
# ----------------------------------------------
# | John | Smith | 24 | Software E |
# | | | | ngineer |
# ----------------------------------------------
# | Mary | Brohowski | 23 | Sales Mana |
# | | | | ger |
# ----------------------------------------------
# | Aristidis | Papageorgo | 28 | Senior Res |
# | | poulos | | eacher |
# ----------------------------------------------
#
# Wrapping function: wrap_onspace(x,width=10)
#
# ---------------------------------------------------
# | First Name | Last Name | Age | Position |
# ---------------------------------------------------
# | John | Smith | 24 | Software |
# | | | | Engineer |
# ---------------------------------------------------
# | Mary | Brohowski | 23 | Sales |
# | | | | Manager |
# ---------------------------------------------------
# | Aristidis | Papageorgopoulos | 28 | Senior |
# | | | | Reseacher |
# ---------------------------------------------------
#
# Wrapping function: wrap_onspace_strict(x,width=10)
#
# ---------------------------------------------
# | First Name | Last Name | Age | Position |
# ---------------------------------------------
# | John | Smith | 24 | Software |
# | | | | Engineer |
# ---------------------------------------------
# | Mary | Brohowski | 23 | Sales |
# | | | | Manager |
# ---------------------------------------------
# | Aristidis | Papageorgo | 28 | Senior |
# | | poulos | | Reseacher |
# ---------------------------------------------