85.4 reStructuredText Style Docstrings
Right, let’s talk about reStructuredText docstrings. This is the granddaddy of Python docstring formats, the one that powered the ecosystem for years. It’s powerful, it’s expressive, and it can feel like you’re trying to assemble IKEA furniture with instructions written by a medieval scribe. But you need to know it, because Sphinx—the 800-pound gorilla of Python documentation generators—eats, sleeps, and breathes reStructuredText (or reST for short).
The core idea is simple: you write your docstring in plain text, but you sprinkle in little bits of reST markup to add structure, cross-references, and formatting. Sphinx then comes along, reads this markup, and turns it into beautiful HTML, PDF, or whatever else you fancy.
The Basic Anatomy of a reST Docstring
A good docstring explains what the function does, its parameters, what it returns, and any errors it might raise. reST gives us a conventional way to do this. Here’s the classic, no-frills example:
def calculate_velocity(distance, time):
"""
Calculate velocity given distance and time.
This is a straightforward calculation of the rate of motion.
:param distance: The distance traveled in meters.
:type distance: float
:param time: The time taken to travel the distance in seconds.
:type time: float
:returns: Velocity in meters per second.
:rtype: float
:raises ZeroDivisionError: If time is zero, because we can't be dividing by zero, come on.
"""
return distance / time
See those colons? That’s the classic reST field syntax. It’s a bit verbose (we’ll get to a nicer way shortly), but it’s unambiguous and Sphinx knows exactly how to parse it into nicely formatted parameter tables.
Embracing the Nicer Way: Napoleon and Google Style
Now, if you look at that :param distance: business and think, “This is a lot of typing for something so simple,” you’re not alone. The open-source community felt the same. Enter the napoleon extension for Sphinx.
Napoleon allows you to write docstrings in a more human-readable style—either Google-style or NumPy-style—and then it translates them into the standard reST fields for Sphinx under the hood. It’s the best of both worlds. The previous example becomes infinitely more pleasant to write and read:
def calculate_velocity(distance, time):
"""
Calculate velocity given distance and time.
This is a straightforward calculation of the rate of motion.
Args:
distance (float): The distance traveled in meters.
time (float): The time taken to travel the distance in seconds.
Returns:
float: Velocity in meters per second.
Raises:
ZeroDivisionError: If time is zero. You can't go back in time. Yet.
"""
return distance / time
This is objectively cleaner. You enable the napoleon extension in your Sphinx conf.py (extensions = ['sphinx.ext.napoleon']) and it just works. Always use Napoleon. Your fingers will thank you. The only reason to use the verbose :param: syntax directly is if you need some hyper-specific reST markup inside a parameter description.
Cross-Referencing: Your Docstring’s Superpower
This is where reST docstrings stop being a notepad and start being part of a living knowledge base. You can link to other classes, functions, and methods.
def initiate_warp_drive(mass, desired_velocity):
"""
Initiates the Alcubierre-type warp drive for FTL travel.
This function handles the complex energy calculations required. For the
actual spacetime metric manipulation, see :func:`~spacetime.metric.alcubierre`.
It is usually called by :class:`~starship.Bridge` after the captain gives the order.
Args:
mass (float): Mass of the object in kilograms. For entire starships, use
:class:`~starship.Starship.total_mass`.
desired_velocity (float): Desired velocity, in multiples of c.
Returns:
WarpField: An instance of the configured warp field.
Raises:
:exc:`~exceptions.EnergyRequiredError`: If the required energy exceeds
the output of the :class:`~power.matter_antimatter_reactor`.
"""
# ... implementation ...
The magic is in the roles, the bits like :func: and :class:. They create hyperlinks in your final documentation. The tilde (~) prefix is a pro move—it tells Sphinx to only display the final part of the path (e.g., alcubierre) instead of the full, often clunky, dotted path (spacetime.metric.alcubierre). Use it liberally to keep things readable.
Common Pitfalls and the One Big Gotcha
The number one mistake people make is inconsistent indentation. reST is fiercely pedantic about whitespace. Your docstring is a literal string. If you indent the lines of your Args list, you must do it consistently. Mixing spaces and tabs will cause Sphinx to have a meltdown and produce bizarre, unhelpful error messages.
Another pitfall is forgetting that you’re not writing Markdown. You can’t use **bold** or *italic* and expect it to work. reST uses its own, admittedly more clunky, syntax: **bold** and *italic*. It’s easy to forget and then wonder why your docs look wrong.
The biggest gotcha? reST docstrings are powerful, but they lock you into the Sphinx ecosystem. If you want to use a simpler, faster tool like MkDocs, you’ll need the mkdocstrings plugin to parse them, or you might be forced to convert everything to Markdown. It’s a commitment. But for large, complex projects, the payoff in interconnected, richly detailed documentation is often worth it. Just be prepared to wrestle with the syntax every now and then. It builds character.