Last week, I built a command-line tool, csvtomd, to help me write documentation for my personal and work projects. Building Markdown tables by hand can be tedious and often involves lots of manual spacing tweaks to make the source code look just as good as the rendered table. With csvtomd, you can build a table in Excel or Numbers, export it as CSV, and convert the CSV to a Markdown table.

I wrote this tool in Python 3 and wanted to make it available to other developers. The best way to distribute Python 3 packages is to upload your package to the Python Package Index, or PyPI. Once a package is on PyPI, other developers can install your package with a simple command:

pip3 install csvtomd

Helpful Help

Here are a few of the articles that helped me put my package online:

The most useful tutorial I found was Sharing Your Labor of Love: PyPI Quick And Dirty by Hynek Schlawack. It's a great, simple introduction to getting your Python code ready for PyPI and uploading it as a standalone package.

PyPI and CLI Scripts

One of the hiccups I discovered while trying to install my CLI app was that all the tutorials I found involved creating packages for use in other Python scripts. I had to figure out how to set the entry point to my application so that I could run it properly from the command line.

My solution was to add this entry_points line to my

        'console_scripts': [
            'csvtomd = csvtomd:main'

To make this entry point work, I had to modify my script. Originally, my script's main module code—parsing arguments, running the conversion—was located outside a function and ran in the body of the main script. I had to move that code into a main() function.

For convenience, I added a runner for executing the script standalone:

if __name__ == '__main__':

PyPI and Markdown

Will McKenzie came up with a nifty solution for using Markdown READMEs in PyPI packages. Here's the problem:

  • PyPI uses ReStructuredText to render READMEs
  • GitHub uses Markdown to render READMEs

Will's solution was to do the following:

  1. Create a wrapper called
  2. Have convert (Markdown) to README.txt (ReStructuredText)
  3. Have read the README.txt file as its long_description property on PyPI

I had problems with the pyandoc library, so I took Will's idea and rebuilt it slightly differently into

import sys
import os
import subprocess
import pypandoc

with open('README.rst', 'w') as dest:
    long_description = pypandoc.convert('', 'rst')

args = ['python3', ''] + sys.argv[1:]

os.remove('README.rst') does the following:

  1. Converts to README.rst using pypandoc
  2. Runs python3 and passes along its own arguments
  3. Deletes README.rst after is done

Additionally, I added the following to

with open('README.rst') as f:
    long_description =


So now, instead of running python3 sdist, just run python3 sdist and you'll have a very pretty README on both GitHub and PyPI.