# =============================================================================
# >> IMPORTS
# =============================================================================
# Python
import itertools
import sys
# =============================================================================
# >> BACKWARDS COMPATIBILITY
# =============================================================================
# In Python 3 izip_longest has been renamed to zip_longest.
if sys.version_info >= (3,):
zip_longest = itertools.zip_longest
else:
zip_longest = itertools.izip_longest
# =============================================================================
# >> CONSTANTS
# =============================================================================
DEFAULT_ENCODING = 'utf-8'
# =============================================================================
# >> CLASSES
# =============================================================================
class Alignment(object):
'''
Contains constants to specify the aligment of table items.
'''
LEFT = unicode.ljust
RIGHT = unicode.rjust
CENTER = unicode.center
class Column(list):
'''
Represents a column of a table.
'''
def __init__(self, name, alignment=Alignment.CENTER, left_padding=2,
right_padding=2):
'''
Intializes the column.
@param <name>:
Name of the column.
@param <alignment>:
The alignment of the column name.
@param <left_padding>:
Number of spaces for left padding.
@param <right_padding>:
Number of spaces for right padding.
'''
self.name = Item(name, alignment)
self.left_padding = left_padding
self.right_padding = right_padding
def _get_max_padding(self):
'''
Returns the length of the longest item in the column (including the
name of the column).
'''
length = len(self.name)
if not self:
return length
return max(len(max(self, key=len)), length)
def _format(self, encoding=DEFAULT_ENCODING):
'''
Returns a two-tuple containing the formated name of the column and a
tuple containing all formatted items of this row.
'''
padding = self._get_max_padding()
return (
self.name._format(
padding, self.left_padding, self.right_padding, encoding),
tuple(item._format(padding, self.left_padding,
self.right_padding, encoding) for item in self)
)
class Item(object):
'''
Represents a value/item of a column.
'''
def __init__(self, value, alignment=Alignment.LEFT):
'''
Initializes the item.
@param <value>:
The value this item should have.
@param <aligment>:
The aligment of the item in the table.
'''
self.value = str(value)
self.alignment = alignment
def __len__(self):
'''
Returns the length of the value.
'''
return len(self.value)
def _format(self, padding, left_padding, right_padding,
encoding=DEFAULT_ENCODING):
'''
Formats the item.
@param <padding>:
The length of the longest item in the column.
@param <left_padding>:
A number that defines how many spaces should be added to the left of
the item.
@param <right_padding>:
A number that defines how many spaces should be added to the right of
the item.
@param <encoding>:
Specifies the encoding.
'''
return unicode(' ', encoding)*left_padding \
+ self.alignment(self.value.decode(encoding), padding) \
+ unicode(' ', encoding)*right_padding
class HorizontalSeparator(Item):
'''
Represents a horizontal separator.
'''
def __init__(self, separator=unicode('-', DEFAULT_ENCODING)):
'''
Initializes the separator.
@param <separator>:
The character that should be used to separate.
'''
if not isinstance(separator, unicode) \
or len(separator) != 1:
raise ValueError('Separator must be a single character and a unicode object.')
self.separator = separator
def __len__(self):
'''
Returns 0.
'''
return 0
def _format(self, padding, left_padding, right_padding,
encoding=DEFAULT_ENCODING):
'''
Formats the item.
@param <padding>:
The length of the longest item in the column.
@param <left_padding>:
A number that defines how many spaces should be added to the left of
the item.
@param <right_padding>:
A number that defines how many spaces should be added to the right of
the item.
@param <encoding>:
Specifies the encoding.
'''
return self.separator.decode(encoding) \
* (left_padding + padding + right_padding)
class AsciiTable(object):
'''
Represents a table.
'''
def __init__(self, *columns):
'''
Adds the given objects as columns to the table. If a given column is
not a Column object, it will be created.
'''
if len(columns) == 0:
raise ValueError('Table must have at least one column.')
self._columns = []
for column in columns:
if not isinstance(column, Column):
column = Column(column)
self._columns.append(column)
def __len__(self):
'''
Returns the number of columns.
'''
return len(self._columns)
def __iter__(self):
'''
Returns an iterator for all columns.
'''
return iter(self._columns)
def __getitem__(self, index):
'''
Returns the column at the given index.
'''
return self._columns[index]
def add_row(self, *items):
'''
Appends the given items to the table's columns.
'''
if len(items) != len(self):
raise ValueError('You must add exactly the same number of items' \
' like the number of columns.')
for index, item in enumerate(items):
if not isinstance(item, Item):
item = Item(item)
self[index].append(item)
def format(self, header_separator=unicode('=', DEFAULT_ENCODING),
column_separator=unicode('|', DEFAULT_ENCODING),
encoding=DEFAULT_ENCODING):
'''
Formats the table and returns an ASCII table string.
@param <header_separator>:
A single character that defines the character that should be used to
create the horizontal separator.
@param <column_separator>:
A single character that defines the character that should be used to
create the vertical separators.
@param <encoding>:
Specifies the encoding.
'''
if not isinstance(header_separator, unicode) \
or len(header_separator) != 1:
raise ValueError('Header separator must be a single character a' \
'nd a unicode object.')
columns = []
rows = []
for column in self:
column, row = column._format(encoding)
columns.append(column)
rows.append(row)
header = column_separator.join(columns)
return unicode('{0}\n{1}\n{2}', encoding).format(
header,
header_separator*len(header),
unicode('\n', encoding).join(column_separator.join(row) \
for row in zip_longest(*rows, fillvalue=''))
)