Further Reading

  • https://samnicholls.net/2016/06/15/how-to-sphinx-readthedocs/
  • This post shows you good baseline layouts for different projects like CLI applications, standalone scripts, or packages.

Introduction

This post summarizes the sources I regularly refer to when starting a new Python package. I’ve done this a few times and had to find the relevant articles again each time, so I’m collecting all the information here for my next project.

Use an IDE

I’ve used Emacs Python-mode (elpy) for far too long. Use PyCharm, it’s way easier.

Virtual Environment

Use one. Here is how to do it in PyCharm.

Remember to pip freeze > requirements.txt.

Sources

Documentation

If you followed the file structure in the Hitchhiker’s guide linked above, you should have a docs folder. I’ve used Sphinx (project homepage) to write the documentation for previous projects and really like it.

Run sphinx-quickstart in your docs/ folder to quickly set up the initial files.

The conf.py must be edited in the following points:

  • To automatically set the version, set the version variable to version = __import__('pfc').__version__
  • Add "numpydoc" to the extensions list to be able to use NumPy style docstrings
  • If you want to include Jupyter Notebooks in your documentation (as “vignettes” or quickstart tutorials), add "nbsphinx" to the extensions list.
  • The top section “Path setup” is commented out. Uncomment the three lines and make the third one sys.path.insert(0, os.path.abspath('..')) to include your package’s root directory and make imports and autodoc work.

Docstrings

Sphinx can automatically create documentation based on your classes’ docstrings. Google for “sphinx autodoc automodule” to find out more.

My last project has a file modules.rst in the docs that just lists all modules’ classes; it looks like this:

Modules
=======


Config
------

.. automodule:: sng.Config
    :members:


Generator
---------

.. automodule:: sng.Generator
    :members:


Wordlists
---------

.. automodule:: sng.wordlists.wordlists
    :members:

These modules’ documentation gets autogenerated from the docstrings.

Docstring style

In my projects, Sphinx seems to automatically recognize Google style docstrings and NumPy style docstrings (official definition here). But there is an extension called Napoleon that does that, too. It might be useful to add that to the extensions list in some cases, but I don’t know.

I have used NumPy style docstrings before. An example from here looks like this:

class Generator:
    """Main class that holds the config, wordlist, and the trained model.

    Parameters
    ----------
    config : sng.Config, optional
        A Config instance specifying training and simulation parameters.
        If not supplied, a default configuration will be created.
    wordlist_file : str
        Path to a textfile holding the text corpus you want to use.
    wordlist : list of strings
        Alternatively to ``wordlist_file``, you can provide the already
        processed wordlist, a list of (ideally unique) strings.

    Attributes
    ----------
    config : sng.Config
        The Config object supplied, or a default object if none was supplied
        at initialization.
    wordlist : list of strings
        A processed list of unique words, each ending in a newline.
        This is the input to the neural network.

    Examples
    --------
    You can create a word generator like this::
        import sng
        cfg = sng.Config()
        # Folder for pre-installed wordlists:
        wordlist_folder = os.path.join(
            os.path.dirname(os.path.abspath(sng.__file__)), 'wordlists')
        sample_wordlist = os.path.join(wordlist_folder, 'latin.txt')
        # Create a Generator object with some wordlist:
        gen = sng.Generator(wordlist_file=sample_wordlist, config=cfg)
        # Train the model:
        gen.fit()
        # Get a few name suggestions:
        gen.simulate(n=5)
    """

    def __init__(self, config=Config(), wordlist_file=None, wordlist=None):

But it’s better to document the constructor arguments in the __init__ method instead of directly under the class, see here.