blob: d56b995069bfb87dce1fe5bad4592efe73a0eba0 [file] [log] [blame] [view]
Mike Frysinger3e7f3262020-08-11 19:49:261# Python Style Guidelines [go/cros-pystyle]
Kirtika Ruchandani91241162019-07-20 00:19:312
3[TOC]
4
5## Introduction
6
7You should follow this guide for all Python source code that is created as part
8of the Chromium OS project i.e. src/platform/\*.  You may not like some bits of
9it, but you still need to follow it.  In doing so, you ensure that others can
10work with your code.
11
12This guide may be ignored if you are working on code that is part of an open
13source project that already conforms to a different style i.e. generally Python
14code in src/third\_party.  Autotest is a great example of this - see the [other
15notable Python style guides](#Other-notable-Python-style-guides) below.
16
17See also the overall [Chromium coding style].
18
19## Official Style Guide
20
21The [Google Python Style guide] is the official Python style guide for
22Chromium OS original code.
23
24New projects should use that unmodified public Python Google style guide
25(4 space indent with methods\_with\_underscores). Historically, we adopted a
26style that was congruent with Google internal Python style guide (2 spaces with
27MethodsAsCamelCase). Individual older projects are in the process of migrating
28to the public style as time allows; there is no mandate to migrate.
29
30That is, for new projects, use:
31
32* [Indentation]: 4 spaces
33* [Naming]: Functions and Method names use lower\_with\_under()
34
35If that guide is silent on an issue, then you should fall back to [PEP-8].
36
37[PEP-257] is referenced by PEP-8.  If it isn't covered in the
38[Comments section], you can also fall back to PEP-257.
39
40## Other notable Python style guides
41
42There are a number of other Python style guidelines out there.
43
44* [The CODING STYLE file for autotest] - When working on autotest code (or
45 any autotest tests), you should ignore the Chromium OS style guidelines
46 (AKA this page) and use the autotest ones.
47
48## Describing arguments in docstrings
49
50[PEP-257] says that "The docstring for a function or method should summarize
51its behavior and document its arguments, return value(s), side effects,
52exceptions raised, and restrictions. However, it doesn't specify any details of
53what this should look like.
54
55Fortunately, the Google Python style guide provides a [reasonable syntax for
Mike Frysinger127243e2020-02-22 02:52:5456arguments and return values].  We will use that as our standard.
57
58The content of each subsection (e.g. `Args`, `Returns`, etc...) should use the
59same indentation level as the file. If the file is using 2 spaces, then the
60subsection content should use 2 space indents (as seen in the example below).
61If the file is using 4 space indents, then the subsection content should use
624 space indents as well. In both situations, use 4 space hanging indentation
63for long argument descriptions (as seen with `keys:` below).
64
65All docstring content should use full sentences (proper capitalization &
66periods), including argument descriptions (as seen below). This applies even
67when the docstring is a one-liner.
Kirtika Ruchandani91241162019-07-20 00:19:3168
Tom Hughesc3b2b192019-07-31 16:23:2969```python
Kirtika Ruchandani91241162019-07-20 00:19:3170def FetchBigtableRows(big_table, keys, other_silly_variable=None):
71 """Fetches rows from a Bigtable.
72
73 Retrieves rows pertaining to the given keys from the Table instance
74 represented by big_table. Silly things may happen if
75 other_silly_variable is not None.
76
77 Args:
78 big_table: An open Bigtable Table instance.
79 keys: A sequence of strings representing the key of each table row
80 to fetch.
81 other_silly_variable: Another optional variable, that has a much
82 longer name than the other args, and which does nothing.
83
84 Returns:
85 A dict mapping keys to the corresponding table row data
86 fetched. Each row is represented as a tuple of strings. For
87 example:
88
89 {'Serak': ('Rigel VII', 'Preparer'),
90 'Zim': ('Irk', 'Invader'),
91 'Lrrr': ('Omicron Persei 8', 'Emperor')}
92
93 If a key from the keys argument is missing from the dictionary,
94 then that row was not found in the table.
95
96 Raises:
97 IOError: An error occurred accessing the bigtable.Table object.
98 """
99 pass
100```
101
102Specifically:
103
104* **Required**  of all **public methods** to follow this convention.
105* **Encouraged** for all **private methods** to follow this conventionMay
106 use a one-line sentence describing the function and return value if it's
107 simple.
108* All arguments should be described in the `Args:` section, using the format
109 above.
110 * Ordering of descriptions should match order of parameters in function.
111 * For two-line descriptions, indent the 2nd line 4 spaces.
Mike Frysinger127243e2020-02-22 02:52:54112* For functions, if present, `Examples:`, `Args:`, `Returns:` (or `Yields:`
113 with generators), and `Raises:` should be in that order and come last in the
114 docstring, each separated by a blank line.
115* For classes, only `Examples:` and `Attributes:` are permitted. When the
116 the `__init__` method arguments line up with the class's attributes (e.g.,
117 the function does a lot of `self.foo = foo`), there is no need to duplicate
118 argument documentation in the `__init__` docstring. Putting information in
119 the class's attributes is preferred.
Kirtika Ruchandani91241162019-07-20 00:19:31120
121## Imports
122
123The following rules apply to imports (in addition to rules already talked about
124in [PEP-8]):
125
Kirtika Ruchandani91241162019-07-20 00:19:31126* Relative imports are forbidden ([PEP-8] only "highly discourages" them).
127 Where absolutely needed, the `from __future__ import absolute_import`
128 syntax should be used (see [PEP-328]).
129* Do not use `import *`. Always be explicit about what you are importing.
130  Using `import *` is useful for interactive Python but shouldn't be used in
131 checked-in code.
132
133## The shebang line
134
135All files that are meant to be executed should start with the line:
136
Tom Hughesc3b2b192019-07-31 16:23:29137```python
Kirtika Ruchandani91241162019-07-20 00:19:31138#!/usr/bin/env python2
139```
140
141If your system doesn't support that, it is sufficiently unusual for us to not
142worry about compatibility with it.  Most likely it will break in other fun ways.
143
144Files that are not meant to be executed should not contain a shebang line.
145
146## String formatting
147
Mike Frysinger40d5ecf2020-08-12 06:05:19148It is acceptable to mix f-strings & non-f-string formatting in a codebase,
149module, function, or any other grouping.
150Do not worry about having to pick only one style.
151
152### f-strings
153
154When writing Python 3.6+ code, prefer [f-strings].
155They aren't always the best fit which is why we say you should *prefer* rather
156than *always* use them.
157See the next section for non-f-string formatting.
158
159Writing readable f-strings is important, so try to avoid packing too much logic
160in the middle of the `{...}` sections.
161If you need a list comprehension or complicated computation, then it's usually
162better to pull it out into a temporary variable instead.
163Dynamic code in the middle of static strings can be easy to miss for readers.
164
165### % formatting
166
167The [Google style guide] says that the [% formatting] and `'{}'.format()` styles
168are both permitted.
169In CrOS we exclusively use [% formatting], so you should too to match.
170There is no real material difference between the two styles, so sticking with
171% for consistency is better.
172
173Changing either form to an f-string is always OK.
Kirtika Ruchandani91241162019-07-20 00:19:31174
Tom Hughesc3b2b192019-07-31 16:23:29175```python
Kirtika Ruchandani91241162019-07-20 00:19:31176x = 'name: %s; score: %d' % (name, n)
177x = 'name: %(name)s; score: %(score)d' % {'name': name, 'score': n}
178```
179
Mike Frysinger40d5ecf2020-08-12 06:05:19180### Logging formatting
181
182Keep in mind that for logging type functions, we don't format in-place.
Kirtika Ruchandani91241162019-07-20 00:19:31183Instead, we pass them as args to the function.
184
Tom Hughesc3b2b192019-07-31 16:23:29185```python
Kirtika Ruchandani91241162019-07-20 00:19:31186logging.info('name: %s; score: %d', name, n)
187```
188
189## Variables in Comprehensions & Generator Expressions
190
191When using comprehensions & generators, it's best if you stick to "throw away"
192variable names.  "x" tends to be the most common iterator.
193
Tom Hughesc3b2b192019-07-31 16:23:29194```python
Kirtika Ruchandani91241162019-07-20 00:19:31195some_var = [x for x in some_list if x]
196```
197
198While helping with readability (since these expressions are supposed to be kept
199"simple"), it also helps avoid the problem of variable scopes not being as tight
200as people expect.
201
Tom Hughesc3b2b192019-07-31 16:23:29202```python
Kirtika Ruchandani91241162019-07-20 00:19:31203# This throws an exception because the variable is undefined.
204print(x)
205
206some_var = [x for x in (1, 2)]
207# This displays "1" because |x| is now defined.
208print(x)
209
210string = 'some string'
211some_var = [string for string in ('a', 'b', 'c')]
212# This displays "c" because the earlier |string| value has been clobbered.
213print(string)
214```
215
216## TODOs
217
218It is OK to leave TODOs in code.  **Why?**  Encouraging people to at least
219document parts of code that need to be thought out more (or that are confusing)
220is better than leaving this code undocumented.
221
222See also the [Google style guide] here.
223
224All TODOs should be formatted like:
225
Tom Hughesc3b2b192019-07-31 16:23:29226```python
Kirtika Ruchandani91241162019-07-20 00:19:31227TODO(username): Revisit this code when the frob feature is done.
228```
229
230...where `username` is your chromium.org username. If you don't have a
231chromium.org username, please use an email address.
232
233Please **do not** use other forms of TODO (like `TBD`, `FIXME`, `XXX`, etc).
234This makes TODOs much harder for someone to find in the code.
235
236## pylint
237
238Python code written for Chromium OS should be "pylint clean".
239
240[Pylint] is a utility that analyzes Python code to look for bugs and for style
241violations.  We run it in Chromium OS because:
242
243* It can catch bugs at "compile time" that otherwise wouldn't show up until
244 runtime.  This can include typos and also places where you forgot to import
245 the required module.
246* It helps to ensure that everyone is following the style guidelines.
247
248Pylint is notoriously picky and sometimes warns about things that are really OK.
249Because of this, we:
250
251* Disable certain warnings in the Chromium OS pylintrc.
Andrew62b7d4c2019-11-08 17:26:54252* Don't consider it a problem to locally disable warnings parts of code.
Kirtika Ruchandani91241162019-07-20 00:19:31253 **NOTE**: There should be a high bar for disabling warnings.
254 Specifically, you should first think about whether there's a way to rewrite
255 the code to avoid the warning before disabling it.
256
257You can use `cros lint` so that we can be sure everyone is on the same version.
258This should work outside and inside the chroot.
259
260## Other Considerations
261
262### Unit tests
263
264All Python modules should have a corresponding unit test module called
265`<original name>_unittest.py` in the same directory.  We use a few related
266modules (they're available in the chroot as well):
267
268* [mock] (`import mock`) for testing mocks
269 * It has become the official Python mock library starting in 3.3
270 * [pymox] (`import mox`) is the old mock framework
271
272* You'll still see code using this as not all have been migrated to mock yet
273
274* [unittest] (`import unittest`) for unit test suites and methods for testing
275
Mike Frysingerd035a202019-09-04 16:07:43276### Main Modules {#main}
Kirtika Ruchandani91241162019-07-20 00:19:31277
Mike Frysingerd035a202019-09-04 16:07:43278* All main modules require a `main` method.  Use this boilerplate at the end:
279 ```python
280 if __name__ == '__main__':
281 sys.exit(main(sys.argv[1:]))
282 ```
283 * Having `main` accept the arguments instead of going through the implicit
284 global `sys.argv` state allows for better reuse & unittesting: other
285 modules are able to import your module and then invoke it with
286 `main(['foo', 'bar'])`.
287 * The `sys.exit` allows `main` to return values which makes it easy to
288 unittest, and easier to reason about behavior in general: you can always
289 use `return` and not worry if you're in a func that expects a `sys.exit`
290 call. If your func doesn't explicitly return, then you still get the
291 default behavior of `sys.exit(0)`.
292 * If you want a stable string for prefixing output, you shouldn't rely on
293 `sys.argv[0]` at all as the caller can set that to whatever it wants (it
294 is never guaranteed to be the basename of your script). You should set
295 a constant in your module, or you should base it off the filename.
296 ```python
297 # A constant string that is guaranteed to never change.
298 _NAME = 'foo'
299 # The filename which should be stable (respects symlink names).
300 _NAME = os.path.basename(__file__)
301 # The filename which derefs symlinks.
302 _NAME = os.path.basename(os.path.realpath(__file__))
303 ```
Kirtika Ruchandani91241162019-07-20 00:19:31304 * If your main func wants `argv[0]`, you should use `sys.argv[0]`
305 directly. If you're trying to print an error message, then usually you
306 want to use `argparse` (see below) and the `parser.error()` helper
307 instead.
308
309* Main modules should have a symlink without an extension that point to the
310 .py file.  All actual code should be in .py files.
311
312### Other
313
314* There should be one blank line between conditional blocks.
315* Do not use `GFlags` or `optparse` anymore -- stick to `argparse` everywhere.
316
Mike Frysinger9fc0fc02020-09-05 05:18:57317[Chromium coding style]: https://chromium.googlesource.com/chromium/src/+/HEAD/styleguide/styleguide.md
Kirtika Ruchandani91241162019-07-20 00:19:31318[Google Python Style guide]: https://github.com/google/styleguide/blob/gh-pages/pyguide.md
319[Indentation]: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#s3.4-indentation
320[Naming]: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#316-naming
321[PEP-8]: https://www.python.org/dev/peps/pep-0008/
322[Comments section]: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings
Mike Frysinger9fc0fc02020-09-05 05:18:57323[The CODING STYLE file for autotest]: https://chromium.googlesource.com/chromiumos/third_party/autotest/+/HEAD/docs/coding-style.md
Kirtika Ruchandani91241162019-07-20 00:19:31324[reasonable syntax for arguments and return values]: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings
325[section on imports in the Google Style Guide]: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#313-imports-formatting
326[PEP-8]: https://www.python.org/dev/peps/pep-0008/
327[PEP-257]: https://www.python.org/dev/peps/pep-0257/
328[PEP-328]: https://www.python.org/dev/peps/pep-0328/
Mike Frysinger40d5ecf2020-08-12 06:05:19329[f-strings]: https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals
Kirtika Ruchandani91241162019-07-20 00:19:31330[Google style guide]: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#310-strings
331[Google style guide]: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#312-todo-comments
332[mock]: https://docs.python.org/3/library/unittest.mock.html
333[pymox]: https://code.google.com/p/pymox/wiki/MoxDocumentation
334[unittest]: https://docs.python.org/library/unittest.html
335[Pylint]: https://github.com/PyCQA/pylint
Mike Frysinger40d5ecf2020-08-12 06:05:19336[% formatting]: https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting