blob: cd7a5f9ca820759f73ad498cc900f0d8cb68930f [file] [log] [blame] [view]
Kirtika Ruchandani91241162019-07-20 00:19:311# Python Style Guidelines
2
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
56arguments and return values].  We will use that as our standard (adjusted to
57two-space indentation):
58
Tom Hughesc3b2b192019-07-31 16:23:2959```python
Kirtika Ruchandani91241162019-07-20 00:19:3160def FetchBigtableRows(big_table, keys, other_silly_variable=None):
61 """Fetches rows from a Bigtable.
62
63 Retrieves rows pertaining to the given keys from the Table instance
64 represented by big_table. Silly things may happen if
65 other_silly_variable is not None.
66
67 Args:
68 big_table: An open Bigtable Table instance.
69 keys: A sequence of strings representing the key of each table row
70 to fetch.
71 other_silly_variable: Another optional variable, that has a much
72 longer name than the other args, and which does nothing.
73
74 Returns:
75 A dict mapping keys to the corresponding table row data
76 fetched. Each row is represented as a tuple of strings. For
77 example:
78
79 {'Serak': ('Rigel VII', 'Preparer'),
80 'Zim': ('Irk', 'Invader'),
81 'Lrrr': ('Omicron Persei 8', 'Emperor')}
82
83 If a key from the keys argument is missing from the dictionary,
84 then that row was not found in the table.
85
86 Raises:
87 IOError: An error occurred accessing the bigtable.Table object.
88 """
89 pass
90```
91
92Specifically:
93
94* **Required**  of all **public methods** to follow this convention.
95* **Encouraged** for all **private methods** to follow this conventionMay
96 use a one-line sentence describing the function and return value if it's
97 simple.
98* All arguments should be described in the `Args:` section, using the format
99 above.
100 * Ordering of descriptions should match order of parameters in function.
101 * For two-line descriptions, indent the 2nd line 4 spaces.
102* If present, `Args:`, `Returns:`, and `Raises:` should be the last three
103 things in the docstring, separated by a blank line each.
104
105## Imports
106
107The following rules apply to imports (in addition to rules already talked about
108in [PEP-8]):
109
110* It is OK to import packages, modules, and things within a module. This is
111 mentioned solely because it contradicts the [section on imports in the
112 Google Style Guide] (which, remember, is not an authority for Chromium OS).
113 * Said another way, this is completely OK:
114 `from subprocess import Popen, PIPE`
115 * **NOTE**: Although not required, we still may want to encourage people
116 to only import packages and modules.
117* Relative imports are forbidden ([PEP-8] only "highly discourages" them).
118 Where absolutely needed, the `from __future__ import absolute_import`
119 syntax should be used (see [PEP-328]).
120* Do not use `import *`. Always be explicit about what you are importing.
121  Using `import *` is useful for interactive Python but shouldn't be used in
122 checked-in code.
123
124## The shebang line
125
126All files that are meant to be executed should start with the line:
127
Tom Hughesc3b2b192019-07-31 16:23:29128```python
Kirtika Ruchandani91241162019-07-20 00:19:31129#!/usr/bin/env python2
130```
131
132If your system doesn't support that, it is sufficiently unusual for us to not
133worry about compatibility with it.  Most likely it will break in other fun ways.
134
135Files that are not meant to be executed should not contain a shebang line.
136
137## String formatting
138
139The [Google style guide] says that the `'{}'.format()` style is permitted.
140That is true, but in CrOS we much more often/always use % instead. You should
141stick to using % in CrOS code to match existing codebases.
142
Tom Hughesc3b2b192019-07-31 16:23:29143```python
Kirtika Ruchandani91241162019-07-20 00:19:31144x = 'name: %s; score: %d' % (name, n)
145x = 'name: %(name)s; score: %(score)d' % {'name': name, 'score': n}
146```
147
148Also keep in mind that for logging type functions, we don't format in place.
149Instead, we pass them as args to the function.
150
Tom Hughesc3b2b192019-07-31 16:23:29151```python
Kirtika Ruchandani91241162019-07-20 00:19:31152logging.info('name: %s; score: %d', name, n)
153```
154
155## Variables in Comprehensions & Generator Expressions
156
157When using comprehensions & generators, it's best if you stick to "throw away"
158variable names.  "x" tends to be the most common iterator.
159
Tom Hughesc3b2b192019-07-31 16:23:29160```python
Kirtika Ruchandani91241162019-07-20 00:19:31161some_var = [x for x in some_list if x]
162```
163
164While helping with readability (since these expressions are supposed to be kept
165"simple"), it also helps avoid the problem of variable scopes not being as tight
166as people expect.
167
Tom Hughesc3b2b192019-07-31 16:23:29168```python
Kirtika Ruchandani91241162019-07-20 00:19:31169# This throws an exception because the variable is undefined.
170print(x)
171
172some_var = [x for x in (1, 2)]
173# This displays "1" because |x| is now defined.
174print(x)
175
176string = 'some string'
177some_var = [string for string in ('a', 'b', 'c')]
178# This displays "c" because the earlier |string| value has been clobbered.
179print(string)
180```
181
182## TODOs
183
184It is OK to leave TODOs in code.  **Why?**  Encouraging people to at least
185document parts of code that need to be thought out more (or that are confusing)
186is better than leaving this code undocumented.
187
188See also the [Google style guide] here.
189
190All TODOs should be formatted like:
191
Tom Hughesc3b2b192019-07-31 16:23:29192```python
Kirtika Ruchandani91241162019-07-20 00:19:31193TODO(username): Revisit this code when the frob feature is done.
194```
195
196...where `username` is your chromium.org username. If you don't have a
197chromium.org username, please use an email address.
198
199Please **do not** use other forms of TODO (like `TBD`, `FIXME`, `XXX`, etc).
200This makes TODOs much harder for someone to find in the code.
201
202## pylint
203
204Python code written for Chromium OS should be "pylint clean".
205
206[Pylint] is a utility that analyzes Python code to look for bugs and for style
207violations.  We run it in Chromium OS because:
208
209* It can catch bugs at "compile time" that otherwise wouldn't show up until
210 runtime.  This can include typos and also places where you forgot to import
211 the required module.
212* It helps to ensure that everyone is following the style guidelines.
213
214Pylint is notoriously picky and sometimes warns about things that are really OK.
215Because of this, we:
216
217* Disable certain warnings in the Chromium OS pylintrc.
218* Don't consider it an problem to locally disable warnings parts of code.
219 **NOTE**: There should be a high bar for disabling warnings.
220 Specifically, you should first think about whether there's a way to rewrite
221 the code to avoid the warning before disabling it.
222
223You can use `cros lint` so that we can be sure everyone is on the same version.
224This should work outside and inside the chroot.
225
226## Other Considerations
227
228### Unit tests
229
230All Python modules should have a corresponding unit test module called
231`<original name>_unittest.py` in the same directory.  We use a few related
232modules (they're available in the chroot as well):
233
234* [mock] (`import mock`) for testing mocks
235 * It has become the official Python mock library starting in 3.3
236 * [pymox] (`import mox`) is the old mock framework
237
238* You'll still see code using this as not all have been migrated to mock yet
239
240* [unittest] (`import unittest`) for unit test suites and methods for testing
241
Mike Frysingerd035a202019-09-04 16:07:43242### Main Modules {#main}
Kirtika Ruchandani91241162019-07-20 00:19:31243
Mike Frysingerd035a202019-09-04 16:07:43244* All main modules require a `main` method.  Use this boilerplate at the end:
245 ```python
246 if __name__ == '__main__':
247 sys.exit(main(sys.argv[1:]))
248 ```
249 * Having `main` accept the arguments instead of going through the implicit
250 global `sys.argv` state allows for better reuse & unittesting: other
251 modules are able to import your module and then invoke it with
252 `main(['foo', 'bar'])`.
253 * The `sys.exit` allows `main` to return values which makes it easy to
254 unittest, and easier to reason about behavior in general: you can always
255 use `return` and not worry if you're in a func that expects a `sys.exit`
256 call. If your func doesn't explicitly return, then you still get the
257 default behavior of `sys.exit(0)`.
258 * If you want a stable string for prefixing output, you shouldn't rely on
259 `sys.argv[0]` at all as the caller can set that to whatever it wants (it
260 is never guaranteed to be the basename of your script). You should set
261 a constant in your module, or you should base it off the filename.
262 ```python
263 # A constant string that is guaranteed to never change.
264 _NAME = 'foo'
265 # The filename which should be stable (respects symlink names).
266 _NAME = os.path.basename(__file__)
267 # The filename which derefs symlinks.
268 _NAME = os.path.basename(os.path.realpath(__file__))
269 ```
Kirtika Ruchandani91241162019-07-20 00:19:31270 * If your main func wants `argv[0]`, you should use `sys.argv[0]`
271 directly. If you're trying to print an error message, then usually you
272 want to use `argparse` (see below) and the `parser.error()` helper
273 instead.
274
275* Main modules should have a symlink without an extension that point to the
276 .py file.  All actual code should be in .py files.
277
278### Other
279
280* There should be one blank line between conditional blocks.
281* Do not use `GFlags` or `optparse` anymore -- stick to `argparse` everywhere.
282
283[Chromium coding style]: https://chromium.googlesource.com/chromium/src/+/master/styleguide/styleguide.md
284[Google Python Style guide]: https://github.com/google/styleguide/blob/gh-pages/pyguide.md
285[Indentation]: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#s3.4-indentation
286[Naming]: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#316-naming
287[PEP-8]: https://www.python.org/dev/peps/pep-0008/
288[Comments section]: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings
289[The CODING STYLE file for autotest]: https://chromium.googlesource.com/chromiumos/third_party/autotest/+/master/docs/coding-style.md
290[reasonable syntax for arguments and return values]: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings
291[section on imports in the Google Style Guide]: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#313-imports-formatting
292[PEP-8]: https://www.python.org/dev/peps/pep-0008/
293[PEP-257]: https://www.python.org/dev/peps/pep-0257/
294[PEP-328]: https://www.python.org/dev/peps/pep-0328/
295[Google style guide]: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#310-strings
296[Google style guide]: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#312-todo-comments
297[mock]: https://docs.python.org/3/library/unittest.mock.html
298[pymox]: https://code.google.com/p/pymox/wiki/MoxDocumentation
299[unittest]: https://docs.python.org/library/unittest.html
300[Pylint]: https://github.com/PyCQA/pylint