From my previous post, sphinx appears to be just another static site generator. But there’s much more to explore and do. Automation is where it’s at. A robust documentation pipeline is the goal of this delve into some of the powerful features in sphinx. Let’s go.

Code Documentation#

For these experiments I’m using code from a simple django-ninja CRUD project. I’ve documented the code and it will provide the necessary base for our testing.

I use the coc-pydocstring extension for coc.nvim in vim to generate “Google Docstring”. Full details of the plugin are available at the yaegassy / coc-pydocstring repository. The PEP 257 Docstring Conventions can be found on the python pep 257 webpage.

Getting the Project Setup#

For this post I’m doing a clean install. My basic setup is as follows:

# from my projects directory
# setup workspace
mkdir sphinx-next-level
cd sphinx-next-level
#setup virtual environment
python3.10 -m venv venv && source venv/bin/activate
source venv/bin/activate
python3 -m pip install --upgrade pip
# setup git
git init
# see note below
git add .gitignore
git commit -m"Add .gitignore in Initial Commit"

For a .gitignore for python/django/jupyter notebooks visit the toptotal website.

For this project I’m using a requirements.txt to manage the project dependencies. The key here is the dependencies. I’ll be adding them as the project develops. At the start it looks like this.

sphinx~=4.2.0

To install sphinx I run:

pip install -r requirements.txt

We know have access to the sphinx-quickbuild command.

sphinx-quickstart docs

A quick start interactive starts up. I choose to separate source and build directories in the root path.

  • Add your project name
  • Add your own name and email
  • Project release 0.1.0
  • Project language

Now is a good time to test.

cd docs
make html

No errors and we’re good to go. Let’s serve the pages and have a look.

cd build/html/
python http.server

All’s working. Now’s a good time to commit. (GTTC)

reStructuredText is one of the oldest and fully featured text markup standards. However markdown has grown in popularity and is used across many platforms. So I use markdown in Sphinx.

Hello .md Goodbye .rst#

I convert from .rst to markdown using rst-to-myst.

pip install rst-to-myst
rst2myst convert docs/**/*.rst # convert every file under docs
# It's possible to run the convert command with -R flag to
# replace the .rst file as opposed to creating .md files
# beside the original .rst files. But, caution.

I then delete the index.rst and edit conf.py. The myst_enable_extensions enable sphinx definition lists.

extensions = [
        "myst_parser",
]

myst_enable_extensions = [
    "deflist",
]

And install myst-parser.

pip install myst-parser
# test
cd docs/build/html/
python http.server

We are up and running and our files are in markdown.

GTTC.

Live Reload / Auto build#

Running and stoping the http.server over and over is tiresome.

Sphinx has a solution for that.

pip install sphinx-autobuild

This allows us to start the http server and let it run. When I make changes to the .md files it will automatically rebuild the site and refresh the browser. It’s similar to live reload in other apps. The server doesn’t update if you make changes to the .py files.

To start autobuild:

# from the root of your project
# command /inputDirectory /outputDirectory/
sphinx-autobuild docs/source/ docs/build/html/

Now when I modify the documentation the documentation site updates automatically.

I then remove the lines below from conf.py. These are used for the general index, the Python module index, and a standalone search page. I don’t need them for this project.

- {ref}`genindex`
- {ref}`modindex`
- {ref}`search`

Include a File#

To include the existing README.md of your project add the following to index.md. We use the include directive. Notice the opening and closing three back-tics.```

These open and close the directive. {“directive name”}

 ```{include} ../../README.md
 :relative-images:
```

Add a Warning#

To add a warning, we use the warning directive.

```{warning}
   This site is under heavy delvelopment.
```

For a list of directives the sphinx documentation pages are the place to go.

GTTC.

Are we there yet?#

To get a readout of the duration of your build add “sphinx.ext.duration” to conf.py extensions.

extensions = [
    ...
    "sphinx.ext.duration",
]

Look and Feel#

There are a lot of great themes to choose from. The Sphinx Themes Gallery has a large selection. I’m torn between furo and documatt. It depends on the project.

Install the theme:

pip install furo
# or
pip install sphinx_documatt_theme

Activate in conf.py by changing the selected theme.

html_theme = "furo"
# or
html_theme = "sphinx_documatt_theme"

GTTC.

Adding Usage Section#

Create a usage.md file in the source folder.

# Usage

## Installation

Installation instructions to go here #TODO

    pip install <libraryname>

If I build now, I will get a warning that usage.md is not in any toctree directive. Edit index.md.

	```{toctree}
	:caption: 'Contents:'
	:maxdepth: 2
	usage
	```

To add a domain independent or relative link to another document use:

For more information read {doc}`usage`

In this example I added it to index.md

To add a relative reference to a specific heading in a document there is a required extension.

In conf.py add:

extensions = [
	...
	"sphinx.ext.autosectionlabel",
]

And in our index.md:

For more installation instructions read {ref}`Installation`

Note: The reference heading should be unique.

GTTC.

Integrating Jupyter Notebooks#

Add the nbsphnix extension to conf.py

extensions = [
	...
	"nbsphinx",
]

If you don’t have pandoc installed you will receive an error when you rebuild.

To install pandoc the full instructions are on the pandoc official website.

For me on my MacOS, I use:

brew install pandoc

GTTC.

Documenting Code Manually#

To manually include documentation for a particular piece of code we use the {eval-rst} directive in our markdown.

```{eval-rst}
.. function:: example.get_attendee_names(kind=None)
     Return a list of attendees
    :param kind: Optional "kind" argument
	:type kind: list[str] or None
	:rtype: list[str]
```

This outputs the following:

example.get_attendee_names(kind=None)

Return a list of optional ingredients

Parameters

kind (list_[str] or_ None) – Optional “kind” argument

Return type

list[str]

Automate Code Documentation Import#

To automatically detect and create the documentation page for our code we need to add sphinx.ext.autodoc to the extensions list in conf.py.

extensions = [
		...
		"sphinx.ext.autodoc",
]

We then add a reference use autoformat to our markdown. In my case I added it to index.md.

```{eval-rst}
.. autofunction:: example.get_attendee_name
```

Every time the site documentation is built, sphinx reads the code docstring and includes it in the documentation. The documentation is automatically kept up to date with changes in the code documentation.

I document my code and autodoc semi-automatically does the rest. But there’s a better way. Which we’ll see soon.

Linking to the Specific Code docstring#

I use the {py:func} directive in my markdown to create a link to the source code documentation of a particular function.

{py:func}`example.get_attendee_name`

The {py:func} directive generates a relative link to the specific function on the documentation page. There are similar directives for classes {py:class}, modules {py:mod}, and methods {py:meth}. For a full list of cross-referencing python objects visit the sphinx documentation website.

Ok. That’s a lot.

It’s a good time to commit. (GTTC)

In the next post, I want to start answering some of my more burning questions.

How do I manage google docstring code comments?

How do I generate the docs for a complete code base?

How to I handle todo items in my code base docstring?

How do I add some flavour to my docs?

#source

Document Your Scientific Project With Markdown, Sphinx, and Read the Docs | PyData Global 2021

ReadtheDocs Migrate Restructured to Markdown

Sphinx Documentation

Markedly Structured Test