Modern Python Cookbook
上QQ阅读APP看书,第一时间看更新

Writing clear documentation strings with RST markup

How can we clearly document what a function does? Can we provide examples? Of course we can, and we really should. In the Including descriptions and documentation recipe in Chapter 2, Statements and Syntax, and in the Better RST markup in docstrings recipes, we looked at some essential documentation techniques. Those recipes introduced ReStructuredText (RST) for module docstrings.

We'll extend those techniques to write RST for function docstrings. When we use a tool such as Sphinx, the docstrings from our function will become elegant-looking documentation that describes what our function does.

Getting ready

In the Forcing keyword-only arguments with the * separator recipe, we looked at a function that had a large number of parameters and another function that had only two parameters.

Here's a slightly different version of one of those functions, Twc():

>>> def Twc(T, V):
...     """Wind Chill Temperature."""
...     if V < 4.8 or T > 10.0:
...         raise ValueError("V must be over 4.8 kph, T must be below 10°C")
...     return 13.12 + 0.6215*T - 11.37*V**0.16 + 0.3965*T*V**0.16

We need to annotate this function with some more complete documentation.

Ideally, we've got Sphinx installed to see the fruits of our labor. See http://www.sphinx-doc.org.

How to do it...

We'll generally write the following things (in this order) for a function description:

  • Synopsis
  • Description
  • Parameters
  • Returns
  • Exceptions
  • Test cases
  • Anything else that seems meaningful

Here's how we'll create nice documentation for a function. We can apply a similar method to a function, or even a module:

  1. Write the synopsis. A proper subject isn't required—we don't write This function computes...; we start with Computes.... There's no reason to overstate the context:
    def Twc(T, V):
        """Computes the wind chill temperature."""
    
  2. Write the description and provide details:
    def Twc(T, V):
        """Computes the wind chill temperature
     The wind-chill, :math:'T_{wc}', is based on
     air temperature, T, and wind speed, V.
        """
    

    In this case, we used a little block of typeset math in our description. The :math: interpreted text role uses LaTeX math typesetting. Sphinx can use MathJax or JSMath to do JavaScript math typesetting.

  3. Describe the parameters: For positional parameters, it's common to use :param name: description. Sphinx will tolerate a number of variations, but this is common. For parameters that must be keywords, it's common to use :key name: description.

    The word key instead of param shows that it's a keyword-only parameter:

    def Twc(T: float, V: float):
        """Computes the wind chill temperature
     The wind-chill, :math:'T_{wc}', is based on
     air temperature, T, and wind speed, V.
     :param T: Temperature in °C
     :param V: Wind Speed in kph
        """
    

    There are two ways to include type information:

    • Using Python 3 type hints
    • Using RST :type name: markup

      We generally don't use both techniques. Type hints seem to be a better idea than the RST :type: markup.

  4. Describe the return value using :returns::
    def Twc(T: float, V: float) -> float:
        """Computes the wind chill temperature
     The wind-chill, :math:'T_{wc}', is based on
     air temperature, T, and wind speed, V.
     :param T: Temperature in °C
     :param V: Wind Speed in kph
     :returns: Wind-Chill temperature in °C
        """
    

    There are two ways to include return type information:

    • Using Python 3 type hints
    • Using RST :rtype: markup

      We generally don't use both techniques. The RST :rtype: markup has been superseded by type hints.

  5. Identify the important exceptions that might be raised. Use the :raises exception: reason markup. There are several possible variations, but :raises exception: seems to be the most popular:
    def Twc(T: float, V: float) -> float:
        """Computes the wind chill temperature
     The wind-chill, :math:'T_{wc}', is based on
     air temperature, T, and wind speed, V.
     :param T: Temperature in °C
     :param V: Wind Speed in kph
     :returns: Wind-Chill temperature in °C
     :raises ValueError: for wind speeds under 4.8 kph or T above 10°C
        """
    
  6. Include a doctest test case, if possible:
    def Twc(T: float, V: float) -> float:
        """Computes the wind chill temperature
     The wind-chill, :math:'T_{wc}', is based on
     air temperature, T, and wind speed, V.
     :param T: Temperature in °C
     :param V: Wind Speed in kph
     :returns: Wind-Chill temperature in °C
     :raises ValueError: for wind speeds under 4.8 kph or T above 10°C
     >>> round(Twc(-10, 25), 1)
     -18.8
        """
    
  7. Write any additional notes and helpful information. We could add the following to the docstring:
    See https://en.wikipedia.org/wiki/Wind_chill
    ..  math::
        T_{wc}(T_a, V) = 13.12 + 0.6215 T_a - 11.37 V^{0.16} + 0.3965 
        T_a V^{0.16}
    

We've included a reference to a Wikipedia page that summarizes wind-chill calculations and has links to more detailed information. For more information, see https://web.archive.org/web/20130627223738/http://climate.weatheroffice.gc.ca/prods_servs/normals_documentation_e.html.

We've also included a math:: directive with the LaTeX formula that's used in the function. This will often typeset nicely, providing a very readable version of the code. Note that the LaTeX formula is indented four spaces inside the .. math:: directive.

How it works...

For more information on docstrings, see the Including descriptions and documentation recipe in Chapter 2, Statements and Syntax. While Sphinx is popular, it isn't the only tool that can create documentation from the docstring comments. The pydoc utility that's part of the Python Standard Library can also produce good-looking documentation from the docstring comments.

The Sphinx tool relies on the core features of RST processing in the docutils package. See https://pypi.python.org/pypi/docutils for more information.

The RST rules are relatively simple. Most of the additional features in this recipe leverage the interpreted text roles of RST. Each of our :param T:, :returns:, and :raises ValueError: constructs is a text role. The RST processor can use this information to decide on style and structure for the content. The style usually includes a distinctive font. The context might be an HTML definition list format.

There's more...

In many cases, we'll also need to include cross-references among functions and classes. For example, we might have a function that prepares a wind-chill table. This function might have documentation that includes a reference to the Twc() function.

Sphinx will generate these cross-references using a special :func: text role:

def wind_chill_table():
    """Uses :func:'Twc' to produce a wind-chill
 table for temperatures from -30°C to 10°C and
 wind speeds from 5kph to 50kph.
    """

We've used :func:'Twc' to cross-reference one function in the RST documentation for a different function. Sphinx will turn these into proper hyperlinks.

See also

  • See the Including descriptions and documentation and Writing better RST markup in docstrings recipes in Chapter 2, Statements and Syntax, for other recipes that show how RST works.