Package that generates markdown tables from a list of dicts
Project description
py-markdown-table
Tiny python library with zero dependencies which generates formatted multiline tables in markdown
.
Basic Use
Install via pip as follows:
pip install py-markdown-table
Pass a list
of dict
s where the dict
s must have uniform keys which serve as column headers and the values are expanded to be rows. Simple example with no special formatting:
from py_markdown_table.markdown_table import markdown_table
data = [
{
"Product": "Smartphone",
"Brand": "Apple",
"Price": 999.99
},
{
"Product": "Laptop",
"Brand": "Dell",
"Price": 1299.99
}
]
markdown = markdown_table(data).get_markdown()
print(markdown)
+------------------------+
| Product |Brand| Price |
+----------+-----+-------+
|Smartphone|Apple| 999.99|
+----------+-----+-------+
| Laptop | Dell|1299.99|
+------------------------+
A more comprehensive example showcasing some of the formatting options:
from py_markdown_table.markdown_table import markdown_table
jokes_list = [
{
"joke1": "Why don't scientists trust atoms? Because they make up everything!",
"joke2": "Did you hear about the mathematician who's afraid of negative numbers? He will stop at nothing to avoid them!",
"joke3": "Why don't skeletons fight each other? They don't have the guts!"
},
{
"joke1": "What do you call a snowman with a six-pack? An abdominal snowman!",
"joke2": "Why don't eggs tell jokes? Because they might crack up!",
"joke3": "How does a penguin build its house? Igloos it together!"
}
]
markdown = markdown_table(jokes_list).set_params(padding_width = 3,
padding_weight = 'centerleft',
multiline = {'joke1': 30, 'joke2': 30, 'joke3': 30}
).get_markdown()
+--------------------------------------------------------------------------------------------------------------+
| joke1 | joke2 | joke3 |
+------------------------------------+------------------------------------+------------------------------------+
| Why don't scientists trust atoms? | Did you hear about the | Why don't skeletons fight each |
| Because they make up everything! | mathematician who's afraid of | other? They don't have the guts! |
| | negative numbers? He will stop at | |
| | nothing to avoid them! | |
+------------------------------------+------------------------------------+------------------------------------+
| What do you call a snowman with a | Why don't eggs tell jokes? | How does a penguin build its |
| six-pack? An abdominal snowman! | Because they might crack up! | house? Igloos it together! |
+--------------------------------------------------------------------------------------------------------------+
You can also use pandas dataframes by formatting them as follows:
from py_markdown_table.markdown_table import markdown_table
data = df.to_dict(orient='records')
markdown_table(data).get_markdown()
Advanced Use
To add parameters to how the markdown table is formatted, you can use the set_params()
function on a markdown_table
object, i.e. markdown_table(data).set_params(...).get_markdown()
, which allows you to pass the following keyword arguments:
+--------------------------------------------------------------------------------------------------+
| param | type | values | description |
+-----------------------+---------------------+-------------------+--------------------------------+
| row_sep | str | | Row separation strategy using |
| | | | `----` as pattern |
+-----------------------+---------------------+-------------------+--------------------------------+
| | | always | Separate each row |
+-----------------------+---------------------+-------------------+--------------------------------+
| | | topbottom | Separate the top (header) and |
| | | | bottom (last row) of the table |
+-----------------------+---------------------+-------------------+--------------------------------+
| | | markdown | Separate only header from body |
+-----------------------+---------------------+-------------------+--------------------------------+
| | | None | No row separators will be |
| | | | inserted |
+-----------------------+---------------------+-------------------+--------------------------------+
| padding_width | int or | | Allocate padding to all table |
| | dict<str,int> | | cells when passing an int or |
| | | | per-column when passing a dict |
+-----------------------+---------------------+-------------------+--------------------------------+
| padding_weight | str or | | Strategy for allocating |
| | dict<str,str> | | padding within table cells. |
| | | | Per-column when passing a dict |
+-----------------------+---------------------+-------------------+--------------------------------+
| | | left | Aligns the cell's contents to |
| | | | the end of the cell |
+-----------------------+---------------------+-------------------+--------------------------------+
| | | right | Aligns the cell's contents to |
| | | | the beginning of the cell |
+-----------------------+---------------------+-------------------+--------------------------------+
| | | centerleft | Centers cell's contents with |
| | | | extra padding allocated to the |
| | | | beginning of the cell |
+-----------------------+---------------------+-------------------+--------------------------------+
| | | centerright | Centers cell's contents with |
| | | | extra padding allocated to the |
| | | | end of the cell |
+-----------------------+---------------------+-------------------+--------------------------------+
| padding_char | str | | Single character used to fill |
| | | | padding with. Default is a |
| | | | blank space ` `. |
+-----------------------+---------------------+-------------------+--------------------------------+
| newline_char | str | | Character appended to each row |
| | | | to force a newline. Default is |
| | | | `\n` |
+-----------------------+---------------------+-------------------+--------------------------------+
| float_rounding | int | | Integer denoting the precision |
| | | | of cells of `floats` after the |
| | | | decimal point. Default is |
| | | | `None`. |
+-----------------------+---------------------+-------------------+--------------------------------+
| emoji_spacing | str | | Strategy for rendering emojis |
| | | | in tables. Currently only |
| | | | `mono` is supported for |
| | | | monospaced fonts. Default is |
| | | | `None` which disables special |
| | | | handling of emojis. |
+-----------------------+---------------------+-------------------+--------------------------------+
| multiline | dict<Any,int> | | Renders the table with |
| | | | predefined widths by passing a |
| | | | `dict` with `keys` being the |
| | | | column names (e.g. equivalent |
| | | | to those in the passed `data` |
| | | | variable) and `values` -- the |
| | | | `width` of each column as an |
| | | | integer. Note that the width |
| | | | of a column cannot be smaller |
| | | | than the longest contiguous |
| | | | string present in the data. |
+-----------------------+---------------------+-------------------+--------------------------------+
| multiline_strategy | str | | Strategy applied to rendering |
| | | | contents in multiple lines. |
| | | | Possible values are `rows`, |
| | | | `header` or `rows_and_header`. |
| | | | The default value is `rows`. |
+-----------------------+---------------------+-------------------+--------------------------------+
| | | rows | Splits only rows overfilling |
| | | | by the predefined column width |
| | | | as provided in the `multiline` |
| | | | variable |
+-----------------------+---------------------+-------------------+--------------------------------+
| | | header | Splits only the header |
| | | | overfilling by the predefined |
| | | | column width as provided in |
| | | | the `multiline` variable |
+-----------------------+---------------------+-------------------+--------------------------------+
| | | rows_and_header | Splits rows and header |
| | | | overfilling by the predefined |
| | | | column width as provided in |
| | | | the `multiline` variable |
+-----------------------+---------------------+-------------------+--------------------------------+
| multiline_delimiter | str | | Character that will be used to |
| | | | split a cell's contents into |
| | | | multiple rows. The default |
| | | | value is a blank space ` `. |
+-----------------------+---------------------+-------------------+--------------------------------+
| quote | bool | | Wraps the generated markdown |
| | | | table in block quotes |
| | | | ```table```. Default is |
| | | | `True`. |
+--------------------------------------------------------------------------------------------------+
Utils
The namespace py_markdown_table.utils
provides the functions count_emojis()
and find_longest_contiguous_strings()
. count_emojis()
detects emojis and their position in a given string, and find_longest_contiguous_strings()
finds the longest continuous strings present in the rows and/or columns of your input data. find_longest_contiguous_strings()
can be useful to figure out the minimal width of each column given a particular data.
Further Examples
Row separatation
markdown_table(data).set_params(row_sep = 'always').get_markdown()
see example
+----------------------------------------+
| title | time | date |seats|
+------------+-----------+---------+-----+
|Vrij Zwemmen|21:30-23:00|Wed 09.12|24/24|
+------------+-----------+---------+-----+
|Vrij Zwemmen|12:00-13:00|Thu 10.12|18/18|
+------------+-----------+---------+-----+
|Vrij zwemmen| 7:30-8:30 |Fri 11.12|18/18|
+------------+-----------+---------+-----+
|Vrij Zwemmen|13:15-14:15|Sat 12.12|18/18|
+----------------------------------------+
markdown_table(data).set_params(row_sep = 'topbottom').get_markdown()
see example
+----------------------------------------+
| title | time | date |seats|
|Vrij Zwemmen|21:30-23:00|Wed 09.12|24/24|
|Vrij Zwemmen|12:00-13:00|Thu 10.12|18/18|
|Vrij zwemmen| 7:30-8:30 |Fri 11.12|18/18|
|Vrij Zwemmen|13:15-14:15|Sat 12.12|18/18|
+----------------------------------------+
markdown_table(data).set_params(row_sep = 'markdown').get_markdown()
see example
| title | time | date |seats|
|------------|-----------|---------|-----|
|Vrij Zwemmen|21:30-23:00|Wed 09.12|24/24|
|Vrij Zwemmen|12:00-13:00|Thu 10.12|18/18|
|Vrij zwemmen| 7:30-8:30 |Fri 11.12|18/18|
|Vrij Zwemmen|13:15-14:15|Sat 12.12|18/18|
markdown_table(data).set_params(row_sep = 'markdown', quote = False).get_markdown()
see example
title | time | date | seats |
---|---|---|---|
Vrij Zwemmen | 21:30-23:00 | Wed 09.12 | 24/24 |
Vrij Zwemmen | 12:00-13:00 | Thu 10.12 | 18/18 |
Vrij zwemmen | 7:30-8:30 | Fri 11.12 | 18/18 |
Vrij Zwemmen | 13:15-14:15 | Sat 12.12 | 18/18 |
Padding, padding weight and padding char
markdown_table(data).set_params(row_sep = 'topbottom', padding_width = 5, padding_weight = 'left').get_markdown()
see example
+------------------------------------------------------------+
| title| time| date| seats|
| Vrij Zwemmen| 21:30-23:00| Wed 09.12| 24/24|
| Vrij Zwemmen| 12:00-13:00| Thu 10.12| 18/18|
| Vrij zwemmen| 7:30-8:30| Fri 11.12| 18/18|
| Vrij Zwemmen| 13:15-14:15| Sat 12.12| 18/18|
+------------------------------------------------------------+
markdown_table(data).set_params(row_sep = 'topbottom', padding_width = 5, padding_weight = 'centerright').get_markdown()
see example
+------------------------------------------------------------+
| title | time | date | seats |
| Vrij Zwemmen | 21:30-23:00 | Wed 09.12 | 24/24 |
| Vrij Zwemmen | 12:00-13:00 | Thu 10.12 | 18/18 |
| Vrij zwemmen | 7:30-8:30 | Fri 11.12 | 18/18 |
| Vrij Zwemmen | 13:15-14:15 | Sat 12.12 | 18/18 |
+------------------------------------------------------------+
markdown_table(data).set_params(row_sep = 'always', padding_width = 5, padding_weight = 'centerright', padding_char = '.').get_markdown()
see example
+------------------------------------------------------------+
|......title......|......time......|.....date.....|..seats...|
+-----------------+----------------+--------------+----------+
|..Vrij Zwemmen...|..21:30-23:00...|..Wed 09.12...|..24/24...|
+-----------------+----------------+--------------+----------+
|..Vrij Zwemmen...|..12:00-13:00...|..Thu 10.12...|..18/18...|
+-----------------+----------------+--------------+----------+
|..Vrij zwemmen...|...7:30-8:30....|..Fri 11.12...|..18/18...|
+-----------------+----------------+--------------+----------+
|..Vrij Zwemmen...|..13:15-14:15...|..Sat 12.12...|..18/18...|
+------------------------------------------------------------+
markdown_table(data).set_params(row_sep = 'always', padding_width = 5, padding_weight = 'centerright', padding_char = '.').get_markdown()
see example
+------------------------------------------------------------+
|......title......|......time......|.....date.....|..seats...|
+-----------------+----------------+--------------+----------+
|..Vrij Zwemmen...|..21:30-23:00...|..Wed 09.12...|..24/24...|
+-----------------+----------------+--------------+----------+
|..Vrij Zwemmen...|..12:00-13:00...|..Thu 10.12...|..18/18...|
+-----------------+----------------+--------------+----------+
|..Vrij zwemmen...|...7:30-8:30....|..Fri 11.12...|..18/18...|
+-----------------+----------------+--------------+----------+
|..Vrij Zwemmen...|..13:15-14:15...|..Sat 12.12...|..18/18...|
+------------------------------------------------------------+
Per-column padding and padding weight
markdown_table(data).set_params(row_sep = 'always', padding_width = {"title": 2, "time": 4, "date": 3, "seats": 1}, padding_weight = {"title": "left", "time": "right", "date": "centerleft", "seats": "centerright"}).get_markdown()
see example
+--------------------------------------------------+
| title|time | date |seats |
+--------------+---------------+------------+------+
| Vrij Zwemmen|21:30-23:00 | Wed 09.12 |24/24 |
+--------------+---------------+------------+------+
| Vrij Zwemmen|12:00-13:00 | Thu 10.12 |18/18 |
+--------------+---------------+------------+------+
| Vrij Zwemmen|7:30-8:30 | Fri 11.12 |18/18 |
+--------------+---------------+------------+------+
| Vrij Zwemmen|13:15-14:15 | Sat 12.12 |18/18 |
+--------------------------------------------------+
markdown_table(data).set_params(row_sep = 'always', padding_width = {"A": 2, "B": 4, "C": 3}, padding_weight = {"A": "left", "B": "right", "C": "centerleft"}).get_markdown()
see example
+-----------------------------------------------------------------------+
| A|B | C |
+-----------------------------+-------------------------------+---------+
| row1_A and additional stuff|row1_B | row1_C |
+-----------------------------+-------------------------------+---------+
| row2_A|row2_B and additional stuff | row2_C |
+-----------------------------+-------------------------------+---------+
| row3_A|row3_B | row3_C |
+-----------------------------------------------------------------------+
Multiline and emoji
markdown_table(data).set_params(padding_width = 0, padding_weight = "centerleft", multiline = {"A": 25, "B": 12, "C": 9}).get_markdown()
see example
+------------------------------------------------+
| A | B | C |
+-------------------------+------------+---------+
| row1_A and additional | row1_B | row1_C |
| stuff | | |
+-------------------------+------------+---------+
| row2_A | row2_B and | row2_C |
| | additional | |
| | stuff | |
+-------------------------+------------+---------+
| row3_A | row3_B | row3_C |
+------------------------------------------------+
markdown_table(data).set_params(padding_width = 2, padding_weight = "centerleft", multiline = {"A": 25, "B": 12, "C": 9}).get_markdown())
see example
+------------------------------------------------------------+
| A | B | C |
+-----------------------------+----------------+-------------+
| row1_A and additional stuff | row1_B | row1_C |
+-----------------------------+----------------+-------------+
| row2_A | row2_B and | row2_C |
| | additional | |
| | stuff | |
+-----------------------------+----------------+-------------+
| row3_A | row3_B | row3_C |
+------------------------------------------------------------+
markdown_table(data).set_params(row_sep = "always", multiline = {"those are multi rows": 5}, multiline_strategy = "rows_and_header").get_markdown()
see example
+-----+
|those|
| are |
|multi|
| rows|
+-----+
| yes |
| they|
| are |
+-----+
| no |
| they|
| are |
| not |
+-----+
markdown_table(data).set_params(row_sep = "topbottom", emoji_spacing = "mono", multiline = {"title that is maybe too long": 7, "time": 11, "date": 5, "seats": 5,}, multiline_strategy = "rows_and_header").get_markdown()
see example
*Note:* Github's markdown preview does not render emojis as two whole characters, hence the slight offsets in cells containing emojis.+-------------------------------+
| title | time | date|seats|
|that is| | | |
| maybe | | | |
| too | | | |
| long | | | |
| Vrij |21:30-23:00| 😊 |24/24|
|Zwemmen| | | |
| Vrij |12:00-13:00| Thu |18/18|
|Zwemmen| |10.12| |
| Vrij | 7:30-8:30 | Fri | 😊 |
|Zwemmen| |11.12|🌍 🎉|
| Vrij |13:15-14:15| Sat |20/20|
|Zwemmen| |12.12| |
| Vrij | 7:30-8:30 | Fri | asd |
|Zwemmen| |11.12| 😊-|
| | | | 🌍: |
| | | | 🎉 |
|Zwemmen|13:15-14:15| Sat |20/20|
| | |12.12| |
+-------------------------------+
Below is an example from a monospaced terminal, where the table is rendered correctly.
Benchmarks
The table below provide some benchmark results, evaluating the performance on data containing incrementally larger number of columns
, rows
, and characters in each table cell (i.e. cell_size
). You can benchmark it on your own system using the script contained within py_markdown_table/utils/benchmark.py
. Generally, reasonably-sized tables intended to be read by a human can be generated within a millisecond.
see benchmark
+-----------------------------------------------+
| parameters |Multiline| speed |
+------------------+---------+------------------+
| columns: 2 | False | 0.000000 ms |
| rows: 10 | | |
| cell_size: 5 | | |
+------------------+---------+------------------+
| columns: 4 | False | 0.000000 ms |
| rows: 40 | | |
| cell_size: 20 | | |
+------------------+---------+------------------+
| columns: 8 | False | 6.999756 ms |
| rows: 160 | | |
| cell_size: 80 | | |
+------------------+---------+------------------+
| columns: 16 | False | 1173.794678 ms |
| rows: 640 | | |
| cell_size: 320 | | |
+------------------+---------+------------------+
| columns: 2 | True | 0.000000 ms |
| rows: 10 | | |
| cell_size: 5 | | |
+------------------+---------+------------------+
| columns: 4 | True | 0.996338 ms |
| rows: 40 | | |
| cell_size: 20 | | |
+------------------+---------+------------------+
| columns: 8 | True | 16.038330 ms |
| rows: 160 | | |
| cell_size: 80 | | |
+------------------+---------+------------------+
| columns: 16 | True | 1448.473633 ms |
| rows: 640 | | |
| cell_size: 320 | | |
+-----------------------------------------------+
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file py_markdown_table-1.2.0.tar.gz
.
File metadata
- Download URL: py_markdown_table-1.2.0.tar.gz
- Upload date:
- Size: 15.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.8.4 CPython/3.12.1 Linux/6.5.0-1025-azure
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | bae30ca97dbbe144ffa6189796c8bed64595ea4caa4d42379e1158dd8d26c873 |
|
MD5 | dca1f8701780c6d43baedfbf62dab421 |
|
BLAKE2b-256 | 68f4c93684cc0db074ee5f9eb6a00e905f64a956d9035d9cc8cb96e837d663b5 |
File details
Details for the file py_markdown_table-1.2.0-py3-none-any.whl
.
File metadata
- Download URL: py_markdown_table-1.2.0-py3-none-any.whl
- Upload date:
- Size: 13.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.8.4 CPython/3.12.1 Linux/6.5.0-1025-azure
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 6965a0dd616af3cd1e4284af22027d251186308a3eaf5a16e25054acb30b4d56 |
|
MD5 | b1f790c260b991b02e0f9904daf98815 |
|
BLAKE2b-256 | d8e799010346d9dec761d830b9faefa05ae45059a89f766d127ae849cb53d19c |