Prophet
Jeremy's
notes

Sphinx (another) introduction tutorial§

I have been using Sphinx for documenting my Python code for a few months now so I have decided to make a guide on the most common commands that I use, to have them collected together. By the way, Sphinx is a pretty well documented tool, even though the documentation may seem complicated or disorienting, it is helpful if you give it the time it deserves. So this not pretend to be an alternative documentation, it is just a register of the most used features by me.

TL;DR

  • Start the documentation folder:

    sphinx-quickstart
    
  • Create the rst md files associated to your package (from sphinx folder):

    sphinx-apidoc -e -M -o source ..\python_package
    
  • Create html documentation:

    make html
    

Sphinx is not ReStructuredText§

When you are new at the business you may confuse this 2 things. Sphinx is a software that converts the docstrings in your code into beautiful package documentation. A markup language is a computer language that includes tags/flags around words or structures to tell the computer what they are, e.g. HTML, Latex, Markdown, ReStructuredText, … .

Sphinx supports 2 markup languages: Markdown and ReStructuredText. Even if Markdown is much more common nowadays and you are probably more used to it, I suggest you to give ReStructuredText a try. It have as many features as you can imagine and less limitations than Markdown. Being familiar with both won’t harm you.

Here I left some cool guides on ReStructuredText syntax, which I usually keep an eye on from time to time, but you know, Google will be there for you.

Project structure§

This is the minimal project structure that I have found most suitable to work with. It is pretty simple, and allow you to keep everything decently organized.

project_name
│   README.md
│   requirements.txt
│
├───docs
│
├───python_package
│
├───sphinx_docs
│   │   make.bat
│   │   Makefile
│   │   nocopy.txt
│   │
│   ├───build
│   │
│   └───source
│       │   conf.py
│       │   index.rst
│       │
│       ├───_static
│       │   │
│       │   └───css
│       │           mytouch.css
│       │
│       └───_templates
│
└───tests
  • docs: Docs folder for github pages. Hosting the content generated by Sphinx when doing make html.

  • python_package: Each python module must be inside this folder into a separate file (as usual). You can have multiple packages.

  • sphinx_docs: Sphinx documentation root directory.

    • build: Sphinx build directory. Each documentation filetype is generated in a separate folder automatically.

    • source: The source files used by Sphinx to create the package documentation.

      • _static: Static files used to give your personal touch when the documentation is compiled.

        • css: Personal html style sheets to be loaded after those used by Read the docs (or any other theme).

      • _templates: Templates used by Sphinx to create the documentation.

  • tests: Unitary tests to check that code changes doesn’t affect the current behaviour.

How to use it§

So lets suppose that you start a new python project, and you do it from console because it is how you must do it. These command sequence is console agnostic so it might work in any other operating system long as you have a version of Python with pip installed.

mkdir project_name
cd project_name
git init
mkdir sphinx_docs
cd sphinx_docs
pip install sphinx
sphinx-quickstart

After this it will ask you several questions, the option at the end between brackets is the default answer. I am going to help you just with the first:

Separate source and build directories (y/n) [n]: y

The rest is up to you. Once you have done this, the following have been created:

sphinx_docs
│   make.bat
│   Makefile
│
├───build
└───source
    │   conf.py
    │   index.rst
    │
    ├───_static
    └───_templates

Configuration§

We will get into make files later, for now you must know that the conf.py is the configuration file for Sphinx. Here I will list some features that I use to use.

# Add project's root folder and python_package to system path
sys.path.insert(0, os.path.abspath(os.path.join("..", "..")))
sys.path.insert(0, os.path.abspath(os.path.join("..", "..", "python_package")))

# Following extensions are extensively used by me
extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.doctest",
    "sphinx.ext.intersphinx",
    "sphinx.ext.mathjax",
    "sphinx.ext.viewcode",
    "sphinx_rtd_theme",
    "sphinx.ext.githubpages",
]

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "friendly"

# -- Options for HTML output -------------------------------------------------
add_module_names = False

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_title = ""  # This is added to the title, doesn't replace it (sh**t!)
html_short_title = "Awesome project"
html_theme = "sphinx_rtd_theme"
html_logo = os.path.join("_static", "my_logo.png")
html_favicon = os.path.join("_static", "my_icon.ico")
html_theme_options = {
    "canonical_url": "",
    "logo_only": True,
    "display_version": True,
    "prev_next_buttons_location": "both",
    "style_external_links": False,
    "style_nav_header_background": "#abcdef",
    # Toc options
    "collapse_navigation": False,
    "sticky_navigation": True,
    "navigation_depth": 4,
    "includehidden": False,
    "titles_only": True,
}
html_css_files = [
    os.path.join("css", "mytouch.css"),
]

With this configuration the documents will have a really beautiful aspect, which is all we care about.

Github-Pages§

Now let’s edit the make file so the HTML content will be copied to docs folder and the documentation will be almost immediately hosted in Github-Pages.

You should notice that we have added the extension sphinx.ext.githubpages in the configuration file. This extension is not the most useful, it creates a file named .nojekyll when building HTML documentation. The file is empty, so adding the extension and creating the file manually are just as complicated (not much). Now we will edit the make.bat (yes, Windows version, I expect to have the bash version for Mac and Unix soon) file so Sphinx will act the same but the contents when building html documentation, they will be copied to `docs` folder. You can make minimal changes to move instead of copy if you don’t want duplicated files. make.bat must be like:

Warning

I know almost nothing about how to create cmd scripts, so please, be merciful to me

@echo off

pushd %~dp0

REM Command file for Sphinx documentation

if "%SPHINXBUILD%" == "" (
     set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build

if "%1" == "" goto help

%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
     echo.
     echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
     echo.installed, then set the SPHINXBUILD environment variable to point
     echo.to the full path of the 'sphinx-build' executable. Alternatively you
     echo.may add the Sphinx directory to PATH.
     echo.
     echo.If you don't have Sphinx installed, grab it from
     echo.http://sphinx-doc.org/
     exit /b 1
)

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
if "%1" == "html" goto copyfiles
goto end


:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%


:copyfiles
:: This only works for: root/whatevername/make.bat -> root/docs
for %%a in (%~dp0) do set SPHINXDIR=%%~dpa
for %%a in (%SPHINXDIR:~0,-1%) do set ROOTDIR=%%~dpa
xcopy /S /Q /Y %BUILDDIR%\%1 %ROOTDIR%\docs /EXCLUDE:nocopy.txt


:end
popd

In Github you must publish the contents of the docs folder. You will find it in the _Settings_ of your repository.

Documenting§

Now everything is setup, you should just begin writing cool docstrings in your python functions, classes and methods. Then ensure that you are located at sphinx_docs and run the following commands:

sphinx-apidoc -e -M -o source ..\python_package
make html

First one creates the .rst files associated to each module in your package. The second converts them in html files with the given theme an options.

We are not bureaucrats§

We did not write the documentation to seem as boring as possible, nor with the intention that no one, not even our loved ones, would dare to look at it, the contrary. So you must dedicate time to think about what your are writing and what is the best way to make it understandable. I can’t teach you how become more stylish and attractive at writing, that’s up to you. But I can teach you how to organize your documentation a bit better, and maybe later give you some style tips, they are not miracles, but could help you get rid of that mascot look from the Olympics in de 70s.

Use separate pages§

Sometimes, if you are working hard at job, the documentation of your classes may start to become too long. But you probably want to keep all your classes in the same python module because you think that is the way it makes sense to organize your code. You can tell Sphinx to use a different page for each one of your classes, just need to follow me a bit longer:

  • The Sphinx function used for this commitment is :autosummary:. It is mostly prepared to reach our goal, but as usual, some configuration is needed. Lets suppose that you have a module.rst file that looks like this:

python_package.module_name module
=================================

.. automodule:: python_package.module_name
    :show-inheritance:

.. currentmodule:: python_package.module_name

.. autosummary::
    :toctree: toctree_name_also_used_as_folder_where_files_will_be_stored

    class1
    class2
    class3

Doing this, when you automatically create your source that will be rendered later into html, each class listed under autosummary will have its own file, making your documentation much more readable. Of course I highly recommend to read the Sphinx extension documentation, I think that should be mandatory. As you might be guessing, the extension need to be append to our configuration and a few parameters that will make our documentation a bit better.

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    "sphinx_rtd_theme",
    "sphinx.ext.autodoc",
    "sphinx.ext.autosummary",
    "sphinx.ext.doctest",
    "sphinx.ext.intersphinx",
    "sphinx.ext.mathjax",
    "sphinx.ext.viewcode",
    "sphinx.ext.githubpages",
    "sphinx.ext.graphviz",
    "sphinx.ext.inheritance_diagram",
]

# Separate each class into a different document
autosummary_generate = True
autosummary_imported_members = True

autoclass_content = "class"  # Template used by Sphinx
autodoc_default_options = {
    "inherited_members": True,
    "undoc-members": True,
    "exclude-members": "__init__",
    "member-order": "groupwise",
}

graphviz_output_format = "svg"

Warning

As you can see I have added many other extensions related to diagrams, I am making UML diagrams using Graphviz and its dot extended language. I am still learning but I have found it lovely. That’s why I am not going to talk deeper about it

Some of the configurations that you are seeing are just useful for me, and how I write code (I document classes out of __init__ method, if you are here I suppose that you know the difference between a class and an instance). If you want to see your instance attributes documented, you need to include __init__ in the documentation.

To create your .rst files in the folder you can keep using the sphinx-apidoc command that I teach you above because we are using the recent autosummary_generate config value, telling Sphinx to refresh these files when creating the html.

Being more cool§

That’s all. If you have reached this point you have your documentation ready and hosted in Github Pages. From now on I will just collect some tips to make the documentation look better.

First of all I always add a css file that makes the standard ReadTheDocs theme a bit more beautiful.

p {
    text-align: justify;
}

dt {
    width: 100%;
}

.viewcode-link {
    margin-top: 4px;
    float: right;
}

.wy-menu-vertical li.toctree-l1.current>a,
.wy-menu-vertical li.toctree-l2.current>a,
.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current>a {
    background: #fcfcfc;
}

.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a {
    background-color: #cccccc54;
}

.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a:hover {
    background-color: #e3e3e3;
}

.toctree-wrapper.compound {
    display: none;
}

Next you should be thinking about the class template, that removes __dunder__ methods and makes the documentation cleaner, by the way it is a Jinja template, but I have struggled to find the variables of each class to which you have access. You must locate this in the templates location defined in conf.py

{{ fullname | escape | underline }}

.. currentmodule:: {{ module }}

.. autoclass:: {{ objname }}

{% block attributes %}
{% if attributes %}
.. rubric:: {{ _('Attributes') }}

.. autosummary::

{%- for item in attributes %}
    ~{{ name }}.{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}

{% block methods %}
{% if methods %}

{%- for item in methods %}
{% if not item.startswith('__') %}
.. automethod:: {{ name }}.{{ item }}
{% endif %}
{%- endfor %}

{% endif %}
{% endblock %}

Finally I use to edit minimal theme options, and use my own favicon, to make it look more personal.

Afterword§

I expect you to have enjoyed this travel!. Probably I would change some of the things said here in the near future, so don’t forget to come back soon, and if you want and have time you can send me some feedback.