#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © 2017 Taylor C. Richberger <taywee@gmx.com>
# This code is released under the license described in the LICENSE file
from __future__ import division, absolute_import, print_function, unicode_literals
from six.moves import zip
from texttables.dialect import Dialect
[docs]class writer(object):
"""Fixed-table document writer, writing tables with predefined column-sizes.
The :class:`texttables.Dialect` class is used to configure how this writes
tables. This works as a context manager, in which case :meth:`writetop` and
:meth:`writebottom` will be called automatically."""
def __init__(self, file, widths, dialect=None, **fmtparams):
"""
:param file: A writable file object with a ``write`` method
:param widths: An iterable of widths, containing the field sizes of the table.
Each width may be prefixed with <, >, =, or ^, for alignment through
the Python format specification.
:param dialect: A dialect class or object used to define aspects of the
table. The stored dialect is always an instance of
:class:`texttables.Dialect`, not necessarily the passed-in object.
All the attributes of Dialect are grabbed from this object using
getattr.
:param fmtparams: parameters to override the parameters in
:obj:`dialect`.
"""
self._file = file
self._widths = tuple(widths)
self.dialect = dialect
for attribute in dir(self.dialect):
if '__' not in attribute:
if attribute in fmtparams:
setattr(self._dialect, attribute, fmtparams[attribute])
self.__wroterow = False
self.__wroteheader = False
def __enter__(self):
if self.dialect.top_border:
self.writetop()
return self
def __exit__(self, type, value, traceback):
if self.dialect.bottom_border:
self.writebottom()
@property
def file(self):
'''The file object that was passed in to the constructor. It is not
safe to change this object until you are finished using the class'''
return self._file
@property
def widths(self):
'''The widths that were passed into the constructor, as a tuple.'''
return self._widths
@property
def dialect(self):
'''The :class:`texttables.Dialect` constructed from the passed-in
dialect. This is always unique, and is not the same object that is
passed in. Assigning to this will also likewise construct a new
:class:`texttables.Dialect`, not simply assign the attribute.'''
return self._dialect
@dialect.setter
def dialect(self, value):
self._dialect = Dialect()
if value:
for attribute in dir(self._dialect):
if '__' not in attribute:
setattr(self._dialect, attribute, getattr(value, attribute))
def _row(self, row):
dialect = self.dialect
contents = list()
for cell, rawwidth in zip(row, self._widths):
swidth = str(rawwidth)
alignment = '<'
try:
width = int(swidth)
except ValueError:
alignment = swidth[0]
width = int(swidth[1:])
contents.append('{content!s:{alignment}{width}.{width}s}'.format(
content=cell,
alignment=alignment,
width=width))
row = ''
if dialect.left_border:
row = dialect.left_border
row += dialect.cell_delimiter.join(contents)
if dialect.right_border:
row += dialect.right_border
return row
def _rowdelim(self, delimiter):
dialect = self.dialect
delimcontents = list()
for rawwidth in self._widths:
swidth = str(rawwidth)
try:
width = int(swidth)
except ValueError:
width = int(swidth[1:])
delimcontents.append(delimiter * width)
delim = ''
if dialect.left_border:
delim = dialect.corner_border
delim += dialect.corner_border.join(delimcontents)
if dialect.right_border:
delim += dialect.corner_border
return delim
[docs] def writerow(self, row):
'''Write a single row out to :meth:`file`, respecting any delimiters and
header separators necessary.
:param row: An iterable representing the row to write
'''
dialect = self.dialect
if self.__wroteheader:
if dialect.header_delimiter and dialect.corner_border:
self._file.write(self._rowdelim(dialect.header_delimiter))
self._file.write(dialect.lineterminator)
elif self.__wroterow:
if dialect.row_delimiter and dialect.corner_border:
self._file.write(self._rowdelim(dialect.row_delimiter))
self._file.write(dialect.lineterminator)
self._file.write(self._row(row))
self._file.write(dialect.lineterminator)
self.__wroteheader = False
self.__wroterow = True
[docs] def writerows(self, rows):
'''Write a multiple rows out to :meth:`file`, respecting any delimiters
and header separators necessary.
:param rows: An iterable of iterables representing the rows to write
'''
for row in rows:
self.writerow(row)
[docs] def writetop(self):
'''Write the top of the table out to :meth:`file`.'''
dialect = self.dialect
self._file.write(self._rowdelim(dialect.top_border))
self._file.write(dialect.lineterminator)
[docs] def writebottom(self):
'''Write the bottom of the table out to :meth:`file`.'''
dialect = self.dialect
self._file.write(self._rowdelim(dialect.bottom_border))
self._file.write(dialect.lineterminator)
[docs]class DictWriter(object):
"""Fixed-table document writer, writing tables with predefined column-sizes
and names through dictionary rows passed in.
The :class:`texttables.Dialect` class is used to configure how this writes
tables. This is a simple convenience frontend to
:class:`texttables.fixed.writer`.
This works as a context manager, in which case :meth:`writetop` and
:meth:`writebottom` will be called automatically.
"""
def __init__(self, file, fieldnames, widths, dialect=None, **fmtparams):
"""
All the passed in construction parameters are passed to the
:class:`texttables.fixed.writer` constructor literally. All properties
and most methods also align directly as well.
"""
self._writer = writer(file, widths, dialect, **fmtparams)
self._fieldnames = fieldnames
def __enter__(self):
self._writer.__enter__()
return self
def __exit__(self, type, value, traceback):
self._writer.__exit__(type, value, traceback)
return self
@property
def file(self):
return self._writer.file
@property
def widths(self):
return self._writer.widths
@property
def dialect(self):
return self._writer.dialect
@dialect.setter
def dialect(self, value):
self._writer.dialect = value
@property
def fieldnames(self):
return self._fieldnames
@fieldnames.setter
def fieldnames(self, value):
self._fieldnames = value
[docs] def writerow(self, row):
'''Write a single row out to :meth:`file`, respecting any delimiters and
header separators necessary.
:param row: A dictionary representing the row to write
'''
self._writer.writerow(row[field] for field in self._fieldnames)
[docs] def writerows(self, rows):
'''Write multiple rows out to :meth:`file`, respecting any delimiters and
header separators necessary.
:param row: An iterable of dictionaries representing the rows to write
'''
for row in rows:
self.writerow(row)
[docs] def writetop(self):
self._writer.writetop()
[docs] def writebottom(self):
self._writer.writebottom()