2025-02-07 00:47:33 +01:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""Format adapter for the tabulate module."""
|
|
|
|
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
|
|
|
from cli_helpers.utils import filter_dict_by_key
|
2025-02-07 00:49:46 +01:00
|
|
|
from cli_helpers.compat import Terminal256Formatter, Token, StringIO
|
2025-02-07 00:48:37 +01:00
|
|
|
from .preprocessors import (
|
|
|
|
convert_to_string,
|
|
|
|
truncate_string,
|
|
|
|
override_missing_value,
|
|
|
|
style_output,
|
|
|
|
HAS_PYGMENTS,
|
|
|
|
escape_newlines,
|
|
|
|
)
|
2025-02-07 00:47:33 +01:00
|
|
|
|
|
|
|
import tabulate
|
|
|
|
|
2025-02-07 00:48:37 +01:00
|
|
|
|
|
|
|
tabulate.MIN_PADDING = 0
|
|
|
|
|
|
|
|
tabulate._table_formats["psql_unicode"] = tabulate.TableFormat(
|
|
|
|
lineabove=tabulate.Line("┌", "─", "┬", "┐"),
|
|
|
|
linebelowheader=tabulate.Line("├", "─", "┼", "┤"),
|
|
|
|
linebetweenrows=None,
|
|
|
|
linebelow=tabulate.Line("└", "─", "┴", "┘"),
|
|
|
|
headerrow=tabulate.DataRow("│", "│", "│"),
|
|
|
|
datarow=tabulate.DataRow("│", "│", "│"),
|
|
|
|
padding=1,
|
|
|
|
with_header_hide=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
tabulate._table_formats["double"] = tabulate.TableFormat(
|
|
|
|
lineabove=tabulate.Line("╔", "═", "╦", "╗"),
|
|
|
|
linebelowheader=tabulate.Line("╠", "═", "╬", "╣"),
|
|
|
|
linebetweenrows=None,
|
|
|
|
linebelow=tabulate.Line("╚", "═", "╩", "╝"),
|
|
|
|
headerrow=tabulate.DataRow("║", "║", "║"),
|
|
|
|
datarow=tabulate.DataRow("║", "║", "║"),
|
|
|
|
padding=1,
|
|
|
|
with_header_hide=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
tabulate._table_formats["ascii"] = tabulate.TableFormat(
|
|
|
|
lineabove=tabulate.Line("+", "-", "+", "+"),
|
|
|
|
linebelowheader=tabulate.Line("+", "-", "+", "+"),
|
|
|
|
linebetweenrows=None,
|
|
|
|
linebelow=tabulate.Line("+", "-", "+", "+"),
|
|
|
|
headerrow=tabulate.DataRow("|", "|", "|"),
|
|
|
|
datarow=tabulate.DataRow("|", "|", "|"),
|
|
|
|
padding=1,
|
|
|
|
with_header_hide=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
# "minimal" is the same as "plain", but without headers
|
|
|
|
tabulate._table_formats["minimal"] = tabulate._table_formats["plain"]
|
|
|
|
|
|
|
|
supported_markup_formats = (
|
|
|
|
"mediawiki",
|
|
|
|
"html",
|
|
|
|
"latex",
|
|
|
|
"latex_booktabs",
|
|
|
|
"textile",
|
|
|
|
"moinmoin",
|
|
|
|
"jira",
|
|
|
|
)
|
|
|
|
supported_table_formats = (
|
|
|
|
"ascii",
|
|
|
|
"plain",
|
|
|
|
"simple",
|
|
|
|
"minimal",
|
|
|
|
"grid",
|
|
|
|
"fancy_grid",
|
|
|
|
"pipe",
|
|
|
|
"orgtbl",
|
|
|
|
"psql",
|
|
|
|
"psql_unicode",
|
|
|
|
"rst",
|
|
|
|
"github",
|
|
|
|
"double",
|
|
|
|
)
|
|
|
|
|
2025-02-07 00:47:33 +01:00
|
|
|
supported_formats = supported_markup_formats + supported_table_formats
|
|
|
|
|
2025-02-07 00:48:37 +01:00
|
|
|
default_kwargs = {"ascii": {"numalign": "left"}}
|
|
|
|
headless_formats = ("minimal",)
|
|
|
|
|
|
|
|
|
|
|
|
def get_preprocessors(format_name):
|
|
|
|
common_formatters = (
|
|
|
|
override_missing_value,
|
|
|
|
convert_to_string,
|
|
|
|
truncate_string,
|
|
|
|
style_output,
|
|
|
|
)
|
|
|
|
|
|
|
|
if tabulate.multiline_formats.get(format_name):
|
|
|
|
return common_formatters + (style_output_table(format_name),)
|
|
|
|
else:
|
|
|
|
return common_formatters + (escape_newlines, style_output_table(format_name))
|
2025-02-07 00:47:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
def style_output_table(format_name=""):
|
2025-02-07 00:48:37 +01:00
|
|
|
def style_output(
|
|
|
|
data,
|
|
|
|
headers,
|
|
|
|
style=None,
|
2025-02-07 00:49:46 +01:00
|
|
|
table_separator_token=Token.Output.TableSeparator,
|
|
|
|
**_,
|
2025-02-07 00:48:37 +01:00
|
|
|
):
|
2025-02-07 00:47:33 +01:00
|
|
|
"""Style the *table* a(e.g. bold, italic, and colors)
|
|
|
|
|
|
|
|
.. NOTE::
|
|
|
|
This requires the `Pygments <http://pygments.org/>`_ library to
|
|
|
|
be installed. You can install it with CLI Helpers as an extra::
|
|
|
|
$ pip install cli_helpers[styles]
|
|
|
|
|
|
|
|
Example usage::
|
|
|
|
|
|
|
|
from cli_helpers.tabular_output import tabulate_adapter
|
|
|
|
from pygments.style import Style
|
|
|
|
from pygments.token import Token
|
|
|
|
|
|
|
|
class YourStyle(Style):
|
|
|
|
default_style = ""
|
|
|
|
styles = {
|
|
|
|
Token.Output.TableSeparator: '#ansigray'
|
|
|
|
}
|
|
|
|
|
|
|
|
headers = ('First Name', 'Last Name')
|
|
|
|
data = [['Fred', 'Roberts'], ['George', 'Smith']]
|
|
|
|
style_output_table = tabulate_adapter.style_output_table('psql')
|
|
|
|
style_output_table(data, headers, style=CliStyle)
|
|
|
|
|
|
|
|
data, headers = style_output(data, headers, style=YourStyle)
|
|
|
|
output = tabulate_adapter.adapter(data, headers, style=YourStyle)
|
|
|
|
|
|
|
|
:param iterable data: An :term:`iterable` (e.g. list) of rows.
|
|
|
|
:param iterable headers: The column headers.
|
|
|
|
:param str/pygments.style.Style style: A Pygments style. You can `create
|
|
|
|
your own styles <https://pygments.org/docs/styles#creating-own-styles>`_.
|
|
|
|
:param str table_separator_token: The token type to be used for the table separator.
|
|
|
|
:return: data and headers.
|
|
|
|
:rtype: tuple
|
|
|
|
|
|
|
|
"""
|
|
|
|
if style and HAS_PYGMENTS and format_name in supported_table_formats:
|
|
|
|
formatter = Terminal256Formatter(style=style)
|
|
|
|
|
|
|
|
def style_field(token, field):
|
|
|
|
"""Get the styled text for a *field* using *token* type."""
|
|
|
|
s = StringIO()
|
|
|
|
formatter.format(((token, field),), s)
|
|
|
|
return s.getvalue()
|
|
|
|
|
|
|
|
def addColorInElt(elt):
|
|
|
|
if not elt:
|
|
|
|
return elt
|
|
|
|
if elt.__class__ == tabulate.Line:
|
2025-02-07 00:48:37 +01:00
|
|
|
return tabulate.Line(
|
|
|
|
*(style_field(table_separator_token, val) for val in elt)
|
|
|
|
)
|
2025-02-07 00:47:33 +01:00
|
|
|
if elt.__class__ == tabulate.DataRow:
|
2025-02-07 00:48:37 +01:00
|
|
|
return tabulate.DataRow(
|
|
|
|
*(style_field(table_separator_token, val) for val in elt)
|
|
|
|
)
|
2025-02-07 00:47:33 +01:00
|
|
|
return elt
|
|
|
|
|
|
|
|
srcfmt = tabulate._table_formats[format_name]
|
2025-02-07 00:48:37 +01:00
|
|
|
newfmt = tabulate.TableFormat(*(addColorInElt(val) for val in srcfmt))
|
2025-02-07 00:47:33 +01:00
|
|
|
tabulate._table_formats[format_name] = newfmt
|
|
|
|
|
|
|
|
return iter(data), headers
|
2025-02-07 00:48:37 +01:00
|
|
|
|
2025-02-07 00:47:33 +01:00
|
|
|
return style_output
|
|
|
|
|
2025-02-07 00:48:37 +01:00
|
|
|
|
|
|
|
def adapter(data, headers, table_format=None, preserve_whitespace=False, **kwargs):
|
2025-02-07 00:47:33 +01:00
|
|
|
"""Wrap tabulate inside a function for TabularOutputFormatter."""
|
2025-02-07 00:48:37 +01:00
|
|
|
keys = ("floatfmt", "numalign", "stralign", "showindex", "disable_numparse")
|
|
|
|
tkwargs = {"tablefmt": table_format}
|
2025-02-07 00:47:33 +01:00
|
|
|
tkwargs.update(filter_dict_by_key(kwargs, keys))
|
|
|
|
|
|
|
|
if table_format in supported_markup_formats:
|
|
|
|
tkwargs.update(numalign=None, stralign=None)
|
|
|
|
|
|
|
|
tabulate.PRESERVE_WHITESPACE = preserve_whitespace
|
|
|
|
|
2025-02-07 00:48:37 +01:00
|
|
|
tkwargs.update(default_kwargs.get(table_format, {}))
|
|
|
|
if table_format in headless_formats:
|
|
|
|
headers = []
|
|
|
|
return iter(tabulate.tabulate(data, headers, **tkwargs).split("\n"))
|