Forked and adapted from the Google style guide. Many references to Google are deliberately left in to minimize merge conflicts.
Where Drake-specific rules contradict previous Google conventions, the text is retained for reference but otherwise marked with a strikethrough. The style is intentionally difficult to read in the normal course of perusal. Copying-and-pasting, or even simply highlighting the text, will facilitate reading if necessary. Rules specific to Drake are highlighted like this for clarity and easier maintainability.
A large number of sections are intentionally hidden, as they do not apply to Drake development. For all other aspects of code style, please see Drake's Code Style Guide documentation page.
Be sure to use the right style for module, function, method docstrings and inline comments.
In general, the semantic rules outlined below should be enforced by reviewers.
However, the formatting (e.g. indentations, lines, etc.) will be enforced by
automated lint tooling (e.g. pycodestyle
), and should not be remarked upon by
reviewers.
Python uses docstrings to document code. A docstring is a string that is the
first statement in a package, module, class or function. These strings can be
extracted automatically through the __doc__
member of the object and are used
by pydoc
.
(Try running pydoc
on your module to see how it looks.) Always use the
three-double-quote """
format for docstrings (per
PEP 257).
A docstring should be organized
as a summary line (one physical line not exceeding 80 characters) terminated by
a period, question mark, or exclamation point. When writing more (encouraged),
this must be followed by a blank line, followed by the rest of the docstring
starting at the same cursor position as the first quote of the first line.
Docstrings should conform to PEP 257 with one exception: summary lines are not
required (but are acceptable).
There
are more formatting guidelines for docstrings below.
pydrake
documentation uses Sphinx to generate documentation
(see Drake’s Documentation Generation Instructions),
and uses the Sphinx Napoleon plugin to process Google-style docstrings.
This guide itself does not have comprehensive documentation on all directives
available, but the Sphinx Napoleon documentation does:
sphinx.ext.napoleon
docs
Every file should contain license boilerplate. Choose the appropriate boilerplate for the license used by the project (for example, Apache 2.0, BSD, LGPL, GPL).
At present, Drake’s code (C++, Python, Skylark, etc.) does not explicitly declare its license.
Files should start with a docstring describing the contents and usage of the module.
In this section, “function” means a method, function, generator, or property.
A docstring is mandatory for every function that has one or more of the following properties:
A docstring should give enough information to write a call to the function without reading the function’s code. The docstring should describe the function’s calling syntax and its semantics, but generally not its implementation details, unless those details are relevant to how the function is to be used. For example, a function that mutates one of its arguments as a side effect should note that in its docstring. Otherwise, subtle but important details of a function’s implementation that are not relevant to the caller are better expressed as comments alongside the code than within the function’s docstring.
The docstring
may must
be descriptive-style ("""Fetches rows from a Bigtable."""
)
or imperative-style ("""Fetch rows from a Bigtable."""
), but the style should
be consistent within a file.
The docstring for a @property
data descriptor
should use the same style as the docstring for an attribute or a
function argument ("""The Bigtable path."""
,
rather than """Returns the Bigtable path."""
).
Certain aspects of a function should be documented in special sections, listed below. Each section begins with a heading line, which ends with a colon. All sections other than the heading should maintain a hanging indent of two or four spaces (be consistent within a file). These sections can be omitted in cases where the function’s name and signature are informative enough that it can be aptly described using a one-line docstring.
*foo
(variable length argument lists) and/or **bar
(arbitrary keyword arguments), they should be listed as *foo
and **bar
.
Obvious parameters do not need to be documented.
"""Returns row
from Bigtable as a tuple of strings."""
) and the opening sentence is
sufficient to describe the return value. Do not imitate older ‘NumPy style’
(example),
which frequently documented a tuple return value as if it were multiple
return values with individual names (never mentioning the tuple). Instead,
describe such a return value as: “Returns: A tuple (mat_a, mat_b), where
mat_a is …, and …”. The auxiliary names in the docstring need not
necessarily correspond to any internal names used in the function body (as
those are not part of the API). If the function uses yield
(is a
generator), the Yields:
section should document the object returned by
next()
, instead of the generator object itself that the call evaluates to.def fetch_smalltable_rows(
table_handle: smalltable.Table,
keys: Sequence[bytes | str],
require_all_keys: bool = False,
) -> Mapping[bytes, tuple[str, ...]]:
"""Fetches rows from a Smalltable.
Retrieves rows pertaining to the given keys from the Table instance
represented by table_handle. String keys will be UTF-8 encoded.
Args:
table_handle: An open smalltable.Table instance.
keys: A sequence of strings representing the key of each table
row to fetch. String keys will be UTF-8 encoded.
require_all_keys: If True only rows with values set for all keys will be
returned.
Returns:
A dict mapping keys to the corresponding table row data
fetched. Each row is represented as a tuple of strings. For
example:
{b'Serak': ('Rigel VII', 'Preparer'),
b'Zim': ('Irk', 'Invader'),
b'Lrrr': ('Omicron Persei 8', 'Emperor')}
Returned keys are always bytes. If a key from the keys argument is
missing from the dictionary, then that row was not found in the
table (and require_all_keys must have been False).
Raises:
IOError: An error occurred accessing the smalltable.
"""
Similarly, this variation on Args:
with a line break is also allowed:
def fetch_smalltable_rows(
table_handle: smalltable.Table,
keys: Sequence[bytes | str],
require_all_keys: bool = False,
) -> Mapping[bytes, tuple[str, ...]]:
"""Fetches rows from a Smalltable.
Retrieves rows pertaining to the given keys from the Table instance
represented by table_handle. String keys will be UTF-8 encoded.
Args:
table_handle:
An open smalltable.Table instance.
keys:
A sequence of strings representing the key of each table row to
fetch. String keys will be UTF-8 encoded.
require_all_keys:
If True only rows with values set for all keys will be returned.
Returns:
A dict mapping keys to the corresponding table row data
fetched. Each row is represented as a tuple of strings. For
example:
{b'Serak': ('Rigel VII', 'Preparer'),
b'Zim': ('Irk', 'Invader'),
b'Lrrr': ('Omicron Persei 8', 'Emperor')}
Returned keys are always bytes. If a key from the keys argument is
missing from the dictionary, then that row was not found in the
table (and require_all_keys must have been False).
Raises:
IOError: An error occurred accessing the smalltable.
"""
In the above example, note that the typing annotations on the function signature are not required in Drake.
A method that overrides a method from a base class does not need a docstring if
it is explicitly decorated with
@override
(from typing_extensions
or typing
modules), unless the overriding method’s
behavior materially refines the base method’s contract, or details need to be
provided (e.g., documenting additional side effects), in which case a docstring
with at least those differences is required on the overriding method.
from typing_extensions import override
class Parent:
def do_something(self):
"""Parent method, includes docstring."""
# Child class, method annotated with override.
class Child(Parent):
@override
def do_something(self):
pass
# Child class, but without @override decorator, a docstring is required.
class Child(Parent):
def do_something(self):
pass
# Docstring is trivial, @override is sufficient to indicate that docs can be
# found in the base class.
class Child(Parent):
@override
def do_something(self):
"""See base class."""
Classes should have a docstring below the class definition describing the class.
Public attributes, excluding properties,
should
may
be documented
here in an Attributes
section and follow the same formatting as a
function’s Args
section.
Attributes are not always documented for classes.
class SampleClass:
"""Summary of class here.
Longer class information...
Longer class information...
Attributes:
likes_spam: A boolean indicating if we like SPAM or not.
eggs: An integer count of the eggs we have laid.
"""
def __init__(self, likes_spam: bool = False):
"""Initializes the instance based on spam preference.
Args:
likes_spam: Defines if instance exhibits this preference.
"""
self.likes_spam = likes_spam
self.eggs = 0
@property
def butter_sticks(self) -> int:
"""The number of butter sticks we have."""
All class docstrings should start with a one-line summary that describes what
the class instance represents. This implies that subclasses of Exception
should also describe what the exception represents, and not the context in which
it might occur. The class docstring should not repeat unnecessary information,
such as that the class is a class.
# Yes:
class CheeseShopAddress:
"""The address of a cheese shop.
...
"""
class OutOfCheeseError(Exception):
"""No more cheese is available."""
# No:
class CheeseShopAddress:
"""Class that describes the address of a cheese shop.
...
"""
class OutOfCheeseError(Exception):
"""Raised when no more cheese is available."""
Pay attention to punctuation, spelling, and grammar; it is easier to read well-written comments than badly written ones.
Comments should be as readable as narrative text, with proper capitalization and punctuation. In many cases, complete sentences are more readable than sentence fragments. Shorter comments, such as comments at the end of a line of code, can sometimes be less formal, but you should be consistent with your style.
Although it can be frustrating to have a code reviewer point out that you are using a comma when you should be using a semicolon, it is very important that source code maintain a high level of clarity and readability. Proper punctuation, spelling, and grammar help with that goal.