# -*- coding: utf-8 -*- """Pretty-print tabular data.""" from __future__ import print_function from __future__ import unicode_literals from collections import namedtuple from platform import python_version_tuple import re import math if python_version_tuple() >= ("3", "3", "0"): from collections.abc import Iterable else: from collections import Iterable if python_version_tuple()[0] < "3": from itertools import izip_longest from functools import partial _none_type = type(None) _bool_type = bool _int_type = int _long_type = long # noqa _float_type = float _text_type = unicode # noqa _binary_type = str def _is_file(f): return hasattr(f, "read") else: from itertools import zip_longest as izip_longest from functools import reduce, partial _none_type = type(None) _bool_type = bool _int_type = int _long_type = int _float_type = float _text_type = str _binary_type = bytes basestring = str import io def _is_file(f): return isinstance(f, io.IOBase) try: import wcwidth # optional wide-character (CJK) support except ImportError: wcwidth = None try: from html import escape as htmlescape except ImportError: from cgi import escape as htmlescape __all__ = ["tabulate", "tabulate_formats", "simple_separated_format"] __version__ = "0.8.7" # minimum extra space in headers MIN_PADDING = 2 # Whether or not to preserve leading/trailing whitespace in data. PRESERVE_WHITESPACE = False _DEFAULT_FLOATFMT = "g" _DEFAULT_MISSINGVAL = "" # if True, enable wide-character (CJK) support WIDE_CHARS_MODE = wcwidth is not None Line = namedtuple("Line", ["begin", "hline", "sep", "end"]) DataRow = namedtuple("DataRow", ["begin", "sep", "end"]) # A table structure is suppposed to be: # # --- lineabove --------- # headerrow # --- linebelowheader --- # datarow # --- linebewteenrows --- # ... (more datarows) ... # --- linebewteenrows --- # last datarow # --- linebelow --------- # # TableFormat's line* elements can be # # - either None, if the element is not used, # - or a Line tuple, # - or a function: [col_widths], [col_alignments] -> string. # # TableFormat's *row elements can be # # - either None, if the element is not used, # - or a DataRow tuple, # - or a function: [cell_values], [col_widths], [col_alignments] -> string. # # padding (an integer) is the amount of white space around data values. # # with_header_hide: # # - either None, to display all table elements unconditionally, # - or a list of elements not to be displayed if the table has column headers. # TableFormat = namedtuple( "TableFormat", [ "lineabove", "linebelowheader", "linebetweenrows", "linebelow", "headerrow", "datarow", "padding", "with_header_hide", ], ) def _pipe_segment_with_colons(align, colwidth): """Return a segment of a horizontal line with optional colons which indicate column's alignment (as in `pipe` output format).""" w = colwidth if align in ["right", "decimal"]: return ("-" * (w - 1)) + ":" elif align == "center": return ":" + ("-" * (w - 2)) + ":" elif align == "left": return ":" + ("-" * (w - 1)) else: return "-" * w def _pipe_line_with_colons(colwidths, colaligns): """Return a horizontal line with optional colons to indicate column's alignment (as in `pipe` output format).""" if not colaligns: # e.g. printing an empty data frame (github issue #15) colaligns = [""] * len(colwidths) segments = [_pipe_segment_with_colons(a, w) for a, w in zip(colaligns, colwidths)] return "|" + "|".join(segments) + "|" def _mediawiki_row_with_attrs(separator, cell_values, colwidths, colaligns): alignment = { "left": "", "right": 'align="right"| ', "center": 'align="center"| ', "decimal": 'align="right"| ', } # hard-coded padding _around_ align attribute and value together # rather than padding parameter which affects only the value values_with_attrs = [ " " + alignment.get(a, "") + c + " " for c, a in zip(cell_values, colaligns) ] colsep = separator * 2 return (separator + colsep.join(values_with_attrs)).rstrip() def _textile_row_with_attrs(cell_values, colwidths, colaligns): cell_values[0] += " " alignment = {"left": "<.", "right": ">.", "center": "=.", "decimal": ">."} values = (alignment.get(a, "") + v for a, v in zip(colaligns, cell_values)) return "|" + "|".join(values) + "|" def _html_begin_table_without_header(colwidths_ignore, colaligns_ignore): # this table header will be suppressed if there is a header row return "