Package: nicetable
nicetableA clean and elegant way to print text tables in Python with minimal boilerplate code.Built with modern Python (including type annotations) and has an extensive test suite. Requires Python 3.6 and up.Quickstart'NiceTable' object is printable. In its simplest form, you just pass your data object to the constructor:fromnicetable.nicetableimportNiceTableinput=[{"name":"Jones Green","height_cm":98.8,"shirt":"XL"},{"name":"Jill","height_cm":175,"birth_year":1956}]print(NiceTable(input))Output:+---------------+-------------+---------+--------------+
|  name         |  height_cm  |  shirt  |  birth_year  |
+---------------+-------------+---------+--------------+
|  Jones Green  |       98.8  |  XL     |        None  |
|  Jill         |      175.0  |  None   |        1956  |
+---------------+-------------+---------+--------------+Note that:The input is a list of dicts. A column was generated for each unique key in those dicts.String columns are by default left adjusted, and their column width is set automatically by the longest value.Numeric columns are nicely well-aligned by the digit to the right (see the height_cm column).You can specify a different layout as the second parameter and pass other   formatting options by name.You can also use a dot notation to specify column-level options (by column name or column position).For example, printing as a pipe-delimited CSV, or printing as a regular CSV, without an header line, when None values are printed as 'N/A' only for the 'shirt' column:fromnicetable.nicetableimportNiceTableinput=[{"name":"Jones Green","height_cm":98.8,"shirt":"XL"},{"name":"Jill","height_cm":175,"birth_year":1956}]print(NiceTable(input,'csv',sep_vertical='|'))print(NiceTable(input,'csv',header=False).set_col_options('shirt',none_string='N/A'))Output:name|height_cm|shirt|birth_year
Jones Green|167.8|XL|None
Jill|175|None|1956

Jones Green,167.8,XL,None
Jill,175,N/A,1956Working with different input types and column namesList of lists / List of tuplesThese inputs are interpreted as list of rows, each with a list / tuple of columns values.if youDO NOTspecify column names, they will be assigned automatically, as 'C001', 'C002' etc:fromnicetable.nicetableimportNiceTableinput=[[1],(1,2,3),[1,3,5,7,9]]print(NiceTable(input))Output:+--------+--------+--------+--------+--------+
|  c001  |  c002  |  c003  |  c004  |  c005  |
+--------+--------+--------+--------+--------+
|     1  |  None  |  None  |  None  |  None  |
|     1  |     2  |     3  |  None  |  None  |
|     1  |     3  |     5  |     7  |     9  |
+--------+--------+--------+--------+--------+If youDOspecify a list of column names, those will be used instead of the auto-generated names.The next example uses the functionNiceTable.builtin_layouts()that returns a list of lists:fromnicetable.nicetableimportNiceTableprint(NiceTable(NiceTable.builtin_layouts(),col_names=['Layout','Description']))Output:+-----------+------------------------------------------------------------------------------------------------------+
|  Layout   |  Description                                                                                         |
+-----------+------------------------------------------------------------------------------------------------------+
|  csv      |  comma-separated values with a one-line header.                                                      |
|  default  |  fixed-width table with data auto-alignment.                                                         |
|  grep     |  tab-separated values with no header. Great for CLI output, easily post-processed by cut, grep etc.  |
|  md       |  for tables inside Markdown(.md) files, using the GFM table extension. Ex: README.md on github.      |
|  tsv      |  tab-separated values with a one-line header.                                                        |
+-----------+------------------------------------------------------------------------------------------------------+List of dictsThis input is interpreted as list of rows, each with a dict of {column name : column value} pairs.If youDO NOTspecify column names, they will be collected from the input, as in the first example:fromnicetable.nicetableimportNiceTableinput=[{"name":"Jones Green","height_cm":98.8,"shirt":"XL"},{"name":"Jill","height_cm":175,"birth_year":1956}]print(NiceTable(input))Output:+---------------+-------------+---------+--------------+
|  name         |  height_cm  |  shirt  |  birth_year  |
+---------------+-------------+---------+--------------+
|  Jones Green  |       98.8  |  XL     |        None  |
|  Jill         |      175.0  |  None   |        1956  |
+---------------+-------------+---------+--------------+If youDOspecify a list of column names,ONLY THOSE COLUMNS WILL BE COLLECTED.For example, collecting only three columns, and setting a specific column order:fromnicetable.nicetableimportNiceTableinput=[{"name":"Jones Green","height_cm":98.8,"shirt":"XL"},{"name":"Jill","height_cm":175,"birth_year":1956}]print(NiceTable(input,col_names=['name','birth_year','height_cm']))Output:+---------------+--------------+-------------+
|  name         |  birth_year  |  height_cm  |
+---------------+--------------+-------------+
|  Jones Green  |        None  |       98.8  |
|  Jill         |        1956  |      175.0  |
+---------------+--------------+-------------+If you want to collect all columns, but provide them a new name, use therename_columns()function.fromnicetable.nicetableimportNiceTableinput=[{"name":"Jones Green","height_cm":98.8,"shirt":"XL"},{"name":"Jill","height_cm":175,"birth_year":1956}]print(NiceTable(input).rename_columns(['Name','Height(cm)','Shirt Size','Year of Birth']))Output:+---------------+--------------+--------------+-----------------+
|  Name         |  Height(cm)  |  Shirt Size  |  Year of Birth  |
+---------------+--------------+--------------+-----------------+
|  Jones Green  |        98.8  |  XL          |           None  |
|  Jill         |       175.0  |  None        |           1956  |
+---------------+--------------+--------------+-----------------+Fine-grained NiceTable controlInstead of creating a NiceTable object inside a print() statement, you can alternatively:Create a standalone NiceTable object, specifying a list of column names.Populate it iteratively with the append() function, passing a list, a tuple or a dict, representing a new row.Print it multiple times with different formatting.This example uses the stringNiceTable.SAMPLE_JSON, parses it as JSON, and chery-pick four columns:importjsonfromnicetable.nicetableimportNiceTableout=NiceTable(col_names=['Name','Type','Height(cm)','Weight(kg)'])forpokemoninjson.loads(NiceTable.SAMPLE_JSON):out.append([pokemon['name'],pokemon['type'],pokemon['height'],pokemon['weight']])print(out)out.layout='md'print(out)Output:+-------------+----------------+--------------+--------------+
|  Name       |  Type          |  Height(cm)  |  Weight(kg)  |
+-------------+----------------+--------------+--------------+
|  Bulbasaur  |  Grass/Poison  |          70  |       6.901  |
|  Pikachu    |  Electric      |          40  |       6.100  |
|  Mewtwo     |  Psychic       |         200  |     122.000  |
+-------------+----------------+--------------+--------------+

|  Name       |  Type          |  Height(cm)  |  Weight(kg)  |
|-------------|----------------|--------------|--------------|
|  Bulbasaur  |  Grass/Poison  |          70  |       6.901  |
|  Pikachu    |  Electric      |          40  |       6.100  |
|  Mewtwo     |  Psychic       |         200  |     122.000  |Table-level settingsBelow is the list of the table-level settings, which you can use in the constructor, or set on an existing NiceTable object:SettingTypeDefaultDescriptionheaderbool1whether the table header will be printedheader_seplinebool1if the header is printed, whether a sepline will be printed after itheader_adjuststrleftadjust of the column names, one of: ['left', 'center', 'right', 'compact']sep_verticalstr|a vertical separator stringsep_horizontalstr-a horizontal separator stringsep_crossstr+a crossing separator string (where vertical and horizontal separators meet)border_topbool1whether the table top border will be printedborder_bottombool1whether the table bottom border will be printedborder_leftbool1whether the table left border will be printedborder_rightbool1whether the table right border will be printedcell_adjuststrautoadjust of the values, one of: ['auto', 'left', 'center', 'right', 'compact', 'strict_left', 'strict_center', 'strict_right']cell_spacingint2number of spaces to add to each side of a valuevalue_min_lenint1minimal string length of a value. Shorter values will be space-paddedvalue_max_lenint9999maximum string length of a valuevalue_too_long_policystrwraphandling of a string longer thanvalue_max_len, one of: ['truncate', 'wrap']value_newline_replacestrNoneif set, replace newlines in string value with thisvalue_none_stringstrNonestring representation of the None valuevalue_escape_typestrignorehandling ofsep_verticalinside a value, one of: ['remove', 'replace', 'prefix', 'ignore']value_escape_charstr\a string to replace or prefixsep_vertical, based onvalue_escape_typevalue_funcfunctionNonea function to pre-process the value before any other settings applyThe table above was generated fromNiceTable.FORMATTING_SETTINGS, using themdlayout:fromnicetable.nicetableimportNiceTableprint(NiceTable(NiceTable.FORMATTING_SETTINGS,'md',['Setting','Type','Default','Description']))Column-level settingsTheset_col_options()function sets allows you to set the following settings at the column-level:ParameterMeaningadjustoverrides the table-wide cell_adjustmax_lenoverrides the table-wide value_max_lennewline_replaceoverrides the table-wide value_newline_replacenone_stringoverrides the table-wide value_none_stringfuncoverrides the table-wide value_funcThis function accepts either a column name or a column position for the first parameter. For example:importjsonfromnicetable.nicetableimportNiceTableout=NiceTable(json.loads(NiceTable.SAMPLE_JSON))out.rename_columns(['ID','Name','Type','Height(cm)',' Weight(kg)'])# set the second column options by position (column positions starts from zero)out.set_col_options(1,adjust='center')# set the third column options by column nameout.set_col_options('Type',func=lambdax:x.lower()ifx!='Electric'elseNone,none_string='N/A')print(out)Output:+-------+-------------+----------------+--------------+---------------+
|  ID   |  Name       |  Type          |  Height(cm)  |   Weight(kg)  |
+-------+-------------+----------------+--------------+---------------+
|  001  |  Bulbasaur  |  grass/poison  |          70  |        6.901  |
|  025  |   Pikachu   |  N/A           |          40  |        6.100  |
|  150  |    Mewtwo   |  psychic       |         200  |      122.000  |
+-------+-------------+----------------+--------------+---------------+Cell adjustmentCell contents can be adjustedleft,centerorright, and are space-padded to the width of the longest value in the column (see also next section on wrapping).Alternatively, cell contents can be kept as-is withcompactadjustment, though it means that the table vertical lines will not align (this is used in some layouts such ascsv).The default adjustment isauto, meaning that numeric columns (those with only numbers or None values) are adjustedright, and non-numeric columns are adjustedleft.Numeric columns automatically well-aligned, meaning all their ones digit are printed in the same position.To print them as strings, add astrict_prefix to the adjust, likestrict_left. For example:+-----------------+-------------------+------------------+---------------+-----------------+----------------+
|  standard left  |  standard center  |  standard right  |  strict_left  |  strict_center  |  strict_right  |
+-----------------+-------------------+------------------+---------------+-----------------+----------------+
|    6.901        |        6.901      |           6.901  |  6.901        |      6.901      |         6.901  |
|    6.000        |        6.000      |           6.000  |  6            |        6        |             6  |
|    1.000        |        1.000      |           1.000  |  1            |        1        |             1  |
|  122.000        |      122.000      |         122.000  |  122          |       122       |           122  |
+-----------------+-------------------+------------------+---------------+-----------------+----------------+The example above uses long column names on purpose, otherwiseleft,centerandrightwould look the same,
as all the numbers in each column have the same fixed width (based on their longest column value).Text wrapping and newlinesNiceTablesupports handling long values and newlines in both column names and cell values.Text wrappingWhen a value is longer thanvalue_max_len, it handled by avalue_too_long_policypolicy.The default policy iswrap, which means the value will be broken to multiple lines everyvalue_max_lencharacters.Alternatively, specify thetruncatepolicy to have to values truncated.The following examples demonstrates the two policies:fromnicetable.nicetableimportNiceTableout=NiceTable(col_names=['Code','Product Description(Long)'])out.append([1,'Boeing 777. Batteries not included. May contain nuts.'])out.append([2,'Sack of sand'])print(out)out.value_max_len=19print(out)out.value_too_long_policy='truncate'print(out)Output:+--------+---------------------------------------------------------+
|  Code  |  Product Description(Long)                              |
+--------+---------------------------------------------------------+
|     1  |  Boeing 777. Batteries not included. May contain nuts.  |
|     2  |  Sack of sand                                           |
+--------+---------------------------------------------------------+

+--------+-----------------------+
|  Code  |  Product Description  |
|        |  (Long)               |
+--------+-----------------------+
|     1  |  Boeing 777. Batteri  |
|        |  es not included. Ma  |
|        |  y contain nuts.      |
|     2  |  Sack of sand         |
+--------+-----------------------+

+--------+-----------------------+
|  Code  |  Product Description  |
+--------+-----------------------+
|     1  |  Boeing 777. Batteri  |
|     2  |  Sack of sand         |
+--------+-----------------------+NewlinesWhen newlines are encountered in a column name or a value, they by default cause the text to wrap.  Alternatively, you can ask that newlines will be replaced, by settingvalue_newline_replaceto an alternative string (default isNone).The following example first shows the default behavior, and than shows replacing newlines with the string\n:fromnicetable.nicetableimportNiceTableout=NiceTable(col_names=['Code','Product Description\n(Long)'])\.append([1,'Boeing 777\nBatteries not included.\nMay contain nuts.'])\.append([2,'Sack of sand'])print(out)out.value_newline_replace='\\n'print(out)Output:+--------+---------------------------+
|  Code  |  Product Description      |
|        |  (Long)                   |
+--------+---------------------------+
|     1  |  Boeing 777               |
|        |  Batteries not included.  |
|        |  May contain nuts.        |
|     2  |  Sack of sand             |
+--------+---------------------------+

+--------+----------------------------------------------------------+
|  Code  |  Product Description\n(Long)                             |
+--------+----------------------------------------------------------+
|     1  |  Boeing 777\nBatteries not included.\nMay contain nuts.  |
|     2  |  Sack of sand                                            |
+--------+----------------------------------------------------------+EscapingThe values in different columns of the same row are separated by the vertical separator string (default is|, set by thesep_verticalproperty).What happens if the content of a cell contains that string? It might be irrelevant if the output is just viewed by a person, but it might matter if the string output will be processed by another program (for example, for theCSVlayout).There are four supported behaviors you can choose from, if the one set by the layout you picked is not appropriate:ignore: no special handling of the vertical separator in a a cell, it is printed as is.
This is the default escaping behavior.remove: the vertical separator is removed.This is set by thecsvlayout and its derivatives (tsvandgreplayouts).prefix: the vertical separator is prefixed by another string, controlled byvalue_escape_char.This is set by themdlayout, which uses\as a prefix.replace: the vertical separator is prefixed by another string, controlled byvalue_escape_char.Othersget_column(col)returns aListof the column values.Adding a custom layoutTo add a custom layout based on the existing options, you can inherit fromNiceTableand define your own layout function.The description of your function will be incorporated in thebuiltin_layouts()outputfromnicetable.nicetableimportNiceTableclassMyNiceTable(NiceTable):def_layout_as_winter_columns(self)->None:"""Table with a winter-themed separator. Quite Ugly."""self.sep_vertical='❄☂🌧☂❄'self.sep_cross='❄☂🌧☂❄'self.sep_horizontal='ˣ'print(MyNiceTable(MyNiceTable.builtin_layouts(),'winter_columns',['Layout','Description']))Output:❄☂🌧☂❄ˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣ❄☂🌧☂❄ˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣ❄☂🌧☂❄
❄☂🌧☂❄  Layout          ❄☂🌧☂❄  Description                                                                                         ❄☂🌧☂❄
❄☂🌧☂❄ˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣ❄☂🌧☂❄ˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣ❄☂🌧☂❄
❄☂🌧☂❄  csv             ❄☂🌧☂❄  comma-separated values with a one-line header.                                                      ❄☂🌧☂❄
❄☂🌧☂❄  default         ❄☂🌧☂❄  fixed-width table with data auto-alignment.                                                         ❄☂🌧☂❄
❄☂🌧☂❄  grep            ❄☂🌧☂❄  tab-separated values with no header. Great for CLI output, easily post-processed by cut, grep etc.  ❄☂🌧☂❄
❄☂🌧☂❄  md              ❄☂🌧☂❄  for tables inside Markdown(.md) files, using the GFM table extension. Ex: README.md on github.      ❄☂🌧☂❄
❄☂🌧☂❄  tsv             ❄☂🌧☂❄  tab-separated values with a one-line header.                                                        ❄☂🌧☂❄
❄☂🌧☂❄  winter_columns  ❄☂🌧☂❄  Table with a winter-themed separator. Quite Ugly.                                                   ❄☂🌧☂❄
❄☂🌧☂❄ˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣ❄☂🌧☂❄ˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣˣ❄☂🌧☂❄Note that the new layout and its description were added the output ofbuiltin_layouts()of the new class.