Code Monkey home page Code Monkey logo

py-markdown-table's Introduction

py-markdown-table

build codecov Downloads Downloads Downloads

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 dicts where the dicts 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.

Table with emoji in terminal

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  |         |                  |
+-----------------------------------------------+

py-markdown-table's People

Contributors

dependabot[bot] avatar hvalev avatar nigelm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

py-markdown-table's Issues

Padding around column seperator

Currently there is no whitespace around the column separator which is as far as I know totally conforming to the standard but (in my personal opinion) a little bit hard to read in the raw view (and I have never seen it that way).

e.g.

test = [{"A": "row1_A", "B": "row1_B", "C": "row1_C"},
        {"A": "row2_A", "B": "row2_B", "C": "row2_C"},
        {"A": "row3_A", "B": "row3_B", "C": "row3_C"}]

tabletest = mdT.markdownTable(test).setParams(row_sep="markdown", 
                                              padding_weight="right", 
                                              quote=False).getMarkdown()
print(tabletest)

Output:

|A     |B     |C     |
|------|------|------|
|row1_A|row1_B|row1_C|
|row2_A|row2_B|row2_C|
|row3_A|row3_B|row3_C|

I would prefer:

| A      | B      | C      |
| ------ | ------ | ------ |
| row1_A | row1_B | row1_C |
| row2_A | row2_B | row2_C |
| row3_A | row3_B | row3_C |

Do you accept a PR for this (maybe also as an optional setting) or do you want it to keep it the way it is?

Option for title case headers or custom headers

It would be nice have the table headers be capitalized via an option without needing to change all the dict keys to capitals. Additionally, it might be nice to allow custom headers different than the dict keys. One use case for this would be a boolean column where you may want to trail the column name with a question mark, for example. Another use case would be if you want spaces in the header.

`tests` folder is installed with the package

packages=find_packages(),

This line of code also finds the "tests" folder and it installs it as a package.

When developing in another repo and importing from my own tests folder it first finds your silently installed tests package before my folder.

As a workaround I needed to play with sys.path to first find my folder but that should not be the way of doing and wanted to fix the issue from its source root.

Add possibility to set the column alignment for the rendered cell

Currently it is not possible (as far as I understand it) to set the alignment of the rendered cell column wise.

This would require to "adjust" the second line of the markdown output with colons (see e.g. https://vuepressbook.com/tutorial/tutorial5.html#aligning-text-within-columns)

It would be nice to have a configuration option similar to the one of multiline where one can set the alignment per column name as dict[str, Literal["left","right","center"]] where the default is "left".

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.