Getting Along with Python

Document your project

Document your Python code using Python’s native tools and practices. Normally you should supply these three kinds of documentation:

  • docstrings to describe all your modules, functions and classes, particularly their overall functions and what properties they expect their arguments to have.
  • comments to explain mysteries in your code which aren’t already explained by naming things carefully, or in docstrings.
  • A Sphinx project which can be used to build your reference and narrative documentation.

Comments

Some people argue against comments because comments are sometimes badly written. Ideally, everyone would always write code which was so clear that it required no additional explanation. Then, six months after we wrote it, we would never have any problem figuring it out. But things are typically otherwise in reality. So it can be very useful to have comments in the code, to explain one thing or another to the people reading that code.

Here are some guidelines for making sure that your comments are more helpful and relevant than they are misleading or distracting.

  • Comments must be accurate. It’s better to have no comment, than a comment which is wrong. Verify statements before putting them in comments.
  • Keep comments updated. If you really can’t take the time to update a comment to ensure it is accurate, then it would be better to just remove it than to leave it misleadingly outdated. Comments no longer necessary to understand the code should be removed.
  • Don’t restate anything that is truly obvious from the code itself. Only for truly bad code is it necessary to explain WHAT is being done. It’s better to save comments for WHY something was done - when this is not obvious - or to document some critical assumption.
  • Remove dead code rather than commenting it out (if you are using version control, you can always check out that old code again later).

From these points it may sound like it’s better not to have comments at all! But there are situations where it’s much better to have them.

  • If you use a ‘magic constant’ that you can’t get rid of, and its name does not fully clarify the situation, there had better be a comment explaining why it was chosen.
  • If you do something that otherwise wouldn’t make sense, or that is essential but would be easy to mistake as broken or unnecessary, that’s probably code you should fix. But if you’ve done your best, clarify why it’s necessary rather than leaving readers confused.

You might also want to check out the PEP 8 section on comments. Here are a few stylistic notes on the use of comments in Python.

  • Don’t use strings as comments. Only use them for docstrings. If you are writing a comment, use #.
  • Avoid comments on the same line as code.
  • Indent comments at the same indent level as the code that they apply to, so it’s clear what they are about.

Docstrings

How are docstrings used?

  1. Read by humans in the source code.
  2. Used by help() and the pydoc utility to provide help on the stuff you wrote.
  3. Extracted by Sphinx autodoc so you can insert them into your documentation.

How to write good docstrings? For starters:

How to use docstrings

As a rule, put a triple-quoted string just inside the definitions of most functions, classes and methods. Here’s an example:

def f():
    """First line describing what the function is for.

    Subsequent lines of explanation. More explanation until we reach
    72 columns, then wrap to the next line.

    Another paragraph of explanation could be here if you wanted it.

    :returns:
        description of possible return values.
    """
    return 2

Breaking down the components of this docstring:

  • First line: what does the documented function do, why does it exist?
  • Then one empty line if there is any more to say.
  • Then one or more paragraphs on what the function does.
  • Then you can put in arg/return value type annotations, possibly in Sphinx RST format. For each one, explain your expectations for the arguments and what others should expect from this.

Methods and classes can be treated in essentially the same way.

Sphinx

Use Sphinx to build documentation in many different formats. It basically works as follows: you write files in a relatively simple format called reStructuredText, and Sphinx uses those to build documentation in any of several different formats (e.g. HTML), in the same general style used by many other Python projects as well as Python itself.

Here’s what Sphinx does for you:

  • Extracts docstrings from your source code to include in the generated docs.
  • Lets you integrate this automatically extracted stuff with whatever narrative documentation you write.
  • Can run doctests in your documentation (useful to avoid broken examples, which users find very frustrating).

To learn how to use Sphinx, check out the fine Sphinx tutorial. Most Python projects should also pay special attention to Sphinx’s autodoc extension, which does the job of extracting and reStructuredText markup from your docstrings to help you generate API documentation more easily. (It doesn’t fully automate the process, but such docs tend to be of pretty bad quality anyway.) You may also be interested in Sphinx’s doctest extension, to test snippets of code that are embedded in your documentation.

There are other documentation generators but Sphinx is the most standard and arguably the best; it’s used for Python itself and lends itself to writing properly explanatory, human-readable documentation rather than just an autogenerated catalog of functions and classes.

Hosting Docs

If you use Sphinx to build your docs, then you can use the very nice Read the Docs service to host your public Sphinx documentation for free. To learn how, check out the Read the Docs Tutorial.

If you want to self-host your Sphinx docs on the web, you can use any static file host, just deploy the html produced by make html.

Assuming your host can be configured to serve foo/index.html for requests of foo/, you can get clean URLs by using the ‘dirhtml’ build target for Sphinx (i.e.: make dirhtml); instead of foo.html, this generates foo/index.html.