Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Tiny python library with zero dependencies which generates formatted multiline tables in markdown
.
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()
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`. |
+--------------------------------------------------------------------------------------------------+
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.
markdown_table(data).set_params(row_sep = 'always').get_markdown()
+----------------------------------------+
| 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()
+----------------------------------------+
| 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()
| 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()
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 = 'left').get_markdown()
+------------------------------------------------------------+
| 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()
+------------------------------------------------------------+
| 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()
+------------------------------------------------------------+
|......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()
+------------------------------------------------------------+
|......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 = {"title": 2, "time": 4, "date": 3, "seats": 1}, padding_weight = {"title": "left", "time": "right", "date": "centerleft", "seats": "centerright"}).get_markdown()
+--------------------------------------------------+
| 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()
+-----------------------------------------------------------------------+
| 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 |
+-----------------------------------------------------------------------+
markdown_table(data).set_params(padding_width = 0, padding_weight = "centerleft", multiline = {"A": 25, "B": 12, "C": 9}).get_markdown()
+------------------------------------------------+
| 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())
+------------------------------------------------------------+
| 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()
+-----+
|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()
+-------------------------------+
| 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.
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.
+-----------------------------------------------+
| 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 | | |
+-----------------------------------------------+
FAQs
Package that generates markdown tables from a list of dicts
We found that py-markdown-table demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.