Pages

Saturday, 30 November 2019

Convert Python docstrings to GitHub markdown readmes

This is suited for very small projects were read-the-docs is way overkill, however for most project that will be more suited — see how-to post on setting Sphinx for RTD for more
The Greek philosopher Epictetus said that a day reverse engineering a piece of code saves you half an hour reading the documentation. A maxim still valid to this day. Nevertheless, documenting code is important. With PyCharm and the push towards typehinting in Python writing docstrings is fairly simple. However, getting docstrings into the readme.md of GitHub is not straightforward the first time round. Hence, I wrote this simple guide to doing so.
Do note that for medium/big projects, using ReadTheDocs is recommended over this hack: ee the guide to setting up Sphinx for ReadTheDocs There are several tools available to convert docstrings to HTML pages. Some even have plug-ins that allow a streamlined readthedocs upload. For this purpose, we will use Sphinx.

Step 1: Sphinx-markdown-builder

Sphinx does not output markdown, but outputs HTML by default, so a Python module to allow it to do so needs installing: phinx-markdown-builder.

pip install sphinx sphinx-markdown-builder;

Run Sphinx

Most people trip up with Sphinx. It is very powerful and is tailored for large projects. As a consequence, the "autodoc" tool is far from automatic. In fact, we will not be using sphinx-autodoc, but sphinx-apidoc.

sphinx-apidoc -o Sphinx-docs . sphinx-apidoc --full -A 'Your name here';
cd Sphinx-docs;

Conf.py

In Sphinx there are two main files, the conf.py file and the index.rst file. The former holds how the project is parsed the latter how is the main menu.
To get it to work, fix the conf.py file, by either following the detailed instructions or just lazily copy paste the echo command below.

TL;DR

Copy paste this into the terminal:
echo " import os
import sys
sys.path.insert(0,os.path.abspath('../'))
def skip(app, what, name, obj,would_skip, options):
    if name in ( '__init__',):
        return False
    return would_skip
def setup(app):
    app.connect('autodoc-skip-member', skip)
" >> conf.py;

Manual

First uncomment the lines in the configuration file as these are otherwise commented out!

import os
import sys
sys.path.insert(0, os.path.abspath('../'))
Note the change to ../. This is because we are working in a direction within the project and not getting any conf.py mixed up.
One annoyance is that the magic methods ignored, while often this makes sense. Sometimes it does not. To override this add anywhere:

def skip(app, what, name, obj,would_skip, options):
    if name in ( '__init__',):
        return False
    return would_skip
def setup(app):
    app.connect('autodoc-skip-member', skip)

A thing to note: the docstrings ought to be written in ReStructuredText. If they are in markdown, you need to add a mod see this. ReStructuredText is actually a lot more powerful than Markdown, so it probably best sticking with that. One thing that it can do is repeat blocks for example.

Showtime

Now that the conf.py file is ready, it is show time.

make markdown;

The file is made and is in _build/markdown. I strongly suggest going to StackEdit or using the Markdown plugin for PyCharm to see that all is good. Actually, doing a few rounds of make html; to see all the formatting is good may be a better idea still. There will be a lot of formatting errors.
If you honestly don't care, then you are looking for these commands:

cd ..
cat _build/markdown/*.md >> README.md;
rm -r Sphinx-docs;

Concluding remarks

They say that a project is dead as soon as it is documented, but writing documentation as you go along is very handy. I think that running Sphinx, early on in a project is really handy as it let's you know what is going wrong in terms of formatting. Not to mention that no project is ever completed, only sidelined permanently...

No comments:

Post a Comment