Python Style Guidelines

Introduction

You should follow this guide for all python source code that is created as part of the Chromium OS project i.e. src/platform/*. You may not like some bits of it, but you still need to follow it. In doing so, you ensure that others can work with your code.

This guide may be ignored if you are working on code that is part of an open source project that already conforms to a different style i.e. generally Python code in src/third_party. Autotest is a great example of this--see the other notable python style guides below.

See also overall Chromium coding style.

Official Style Guide

The Google Python Style guide is the official python style guide for Chromium OS original code. Note that we deviate from that guide in two ways (in order to match the internal Google Python Style guide):

  • Indentation: use 2, not 4, spaces
  • Naming: Functions and Method names use CapWords() style, not lower_with_under()
  • The only exception to this rule is the main() function -- we do not use Main()

If that guide is silent on an issue, then you should fall back to PEP-8.

PEP-257 is referenced by PEP-8. If it isn't covered in the Comments section, you can also fall back to PEP-257.

Other notable python style guides

There are a number of other python style guidelines out there.

  • The CODING STYLE file for autotest
    • When working on autotest code (or any autotest tests), you should ignore the Chromium OS style guidelines (AKA this page) and use the autotest ones.

Describing arguments in docstrings

PEP-257 says that "The docstring for a function or method should summarize its behavior and document its arguments, return value(s), side effects, exceptions raised, and restrictions. However, it doesn't specify any details of what this should look like.

Fortunately, the Google python style guide provides a reasonable syntax for arguments and return values. We will use that as our standard (adjusted to two-space indentation):

def FetchBigtableRows(big_table, keys, other_silly_variable=None):
  """Fetches rows from a Bigtable.
  Retrieves rows pertaining to the given keys from the Table instance
  represented by big_table.  Silly things may happen if
  other_silly_variable is not None.
  Args:
    big_table: An open Bigtable Table instance.
    keys: A sequence of strings representing the key of each table row
        to fetch.
    other_silly_variable: Another optional variable, that has a much
        longer name than the other args, and which does nothing.
  Returns:
    A dict mapping keys to the corresponding table row data
    fetched. Each row is represented as a tuple of strings. For
    example:
    {'Serak': ('Rigel VII', 'Preparer'),
     'Zim': ('Irk', 'Invader'),
     'Lrrr': ('Omicron Persei 8', 'Emperor')}
    If a key from the keys argument is missing from the dictionary,
    then that row was not found in the table.
  Raises:
    IOError: An error occurred accessing the bigtable.Table object.
  """
  pass

Specifically:

  • Required of all public methods to follow this convention.
  • Encouraged for all private methods to follow this convention. May use a one-line sentence describing the function and return value if it's simple.
  • All arguments should be described in the Args: section, using the format above.
    • Ordering of descriptions should match order of parameters in function.
    • For two-line descriptions, indent the 2nd line 4 spaces.
  • If present, Args:, Returns:, and Raises: should be the last three things in the docstring, separated by a blank line each.

Imports

The following rules apply to imports (in addition to rules already talked about in PEP-8):

  • It is OK to import packages, modules, and things within a module. This is mentioned solely because it contradicts the section on imports in the Google Style Guide (which, remember, is not an authority for Chromium OS).
    • Said another way, this is completely OK: from subprocess import Popen, PIPE
    • NOTE: Although not required, we still may want to encourage people to only import packages and modules.
  • Relative imports are forbidden (PEP-8 only “highly discourages” them). Where absolutely needed, the from __future__ import absolute_import syntax should be used (see PEP-328).
  • Do not use import *. Always be explicit about what you are importing. Using import * is useful for interactive python but shouldn't be used in checked in code.

The shebang line

All files that are meant to be executed should start with the line:

#!/usr/bin/python2

If your system doesn't support that, it is sufficiently unusual for us to not worry about compatibility with it. Most likely it will break in other fun ways.

Note that files that are not meant to be executed should not contain a shebang line.

String formatting

The Google style guide says that the ‘{}’.format() style is permitted. That is true, but in CrOS we much more often/always use % instead. You should stick to using % in CrOS code to match existing codebases.

x = 'name: %s; score: %d' % (name, n)
x = 'name: %(name)s; score: %(score)d' % {'name': name, 'score': n}

Also keep in mind that for logging type functions, we don't format in place. Instead, we pass them as args to the function.

logging.info('name: %s; score: %d', name, n)

TODOs

It is OK to leave TODOs in code. Why? Encouraging people to at least document parts of code that need to be thought out more (or that are confusing) is better than leaving this code undocumented. See also the Google style guide here.

All TODOs should be formatted like:

TODO(username): Revisit this code when the frob feature is done.

...where username is your chromium.org username. If you don't have a chromium.org username, please use an email address.

Please do not use other forms of TODO (like TBD, FIXME, XXX, etc). This makes TODOs much harder for someone to find in the code.

pylint

Python code written for Chromium OS should be “pylint clean”.

Pylint is a utility that analyzes python code to look for bugs and for style violations. We run it in Chromium OS because:

  • It can catch bugs at “compile time” that otherwise wouldn't show up until runtime. This can include typos and also places where you forgot to import the required module.
  • It helps to ensure that everyone is following the style guidelines.

Pylint is notoriously picky and sometimes warns about things that are really OK. Because of this, we:

  • Disable certain warnings in the Chromium OS pylintrc.
  • Don‘t consider it an problem to locally disable warnings parts of code. NOTE: There should be a high bar for disabling warnings. Specifically, you should first think about whether there’s a way to rewrite the code to avoid the warning before disabling it.

You can use cros lint so that we can be sure everyone is on the same version. This should work outside and inside the chroot.

Other Considerations

Unit tests

All python modules should have a corresponding unit test module called <original name>_unittest.py in the same directory. We use a few related modules (they're available in the chroot as well):

  • mock (import mock) for testing mocks
    • it has become the official Python mock library starting in 3.3
    • pymox (import mox) is the old mock framework
      • you'll still see code using this as not all have been migrated to mock yet
  • unittest (import unittest) for unit test suites and methods for testing

Main Modules

  • All main modules require a main method. Use if __name__ == 'main': main(sys.argv[1:]) to invoke (not on one line of course).
    • If your main func wants argv[0], you should use sys.argv[0] directly. If you're trying to print an error message, then usually you want to use argparse (see below) and the parser.error() helper instead.
  • Main modules should have a symlink without an extension that point to the .py file. All actual code should be in .py files.

Other

  • There should be one blank line between conditional blocks
  • We support use of argparse over GFlags or optparse.